Added ldap and pam authentication
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
//go:build pam
|
||||
|
||||
// Package pamauth authenticates a username/password pair against the host's PAM
|
||||
// stack (/etc/pam.d/<service>). It is the backend for uopi's built-in HTTP Basic
|
||||
// authentication: because the uopi host is typically already an SSSD/LDAP client,
|
||||
// validating through PAM reuses the exact same login path as `login`/`ssh`
|
||||
// (pam_sss → the site directory) without uopi needing any directory schema.
|
||||
//
|
||||
// This file is the real implementation, compiled only with the `pam` build tag
|
||||
// (which also requires cgo + libpam). The default fully-static CGO_ENABLED=0
|
||||
// build uses stub.go instead, where Authenticate reports PAM is unavailable.
|
||||
package pamauth
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lpam
|
||||
#include <security/pam_appl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// uopiPamConv answers every password-style PAM prompt with the password passed
|
||||
// through appdata_ptr. Informational/error messages get a NULL response. The PAM
|
||||
// library takes ownership of the returned responses and frees them.
|
||||
static int uopiPamConv(int num_msg, const struct pam_message **msg,
|
||||
struct pam_response **resp, void *appdata_ptr) {
|
||||
if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG) {
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
struct pam_response *r = calloc((size_t)num_msg, sizeof(struct pam_response));
|
||||
if (r == NULL) {
|
||||
return PAM_BUF_ERR;
|
||||
}
|
||||
for (int i = 0; i < num_msg; i++) {
|
||||
int style = msg[i]->msg_style;
|
||||
if (style == PAM_PROMPT_ECHO_OFF || style == PAM_PROMPT_ECHO_ON) {
|
||||
r[i].resp = strdup((const char *)appdata_ptr);
|
||||
if (r[i].resp == NULL) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
free(r[j].resp);
|
||||
}
|
||||
free(r);
|
||||
return PAM_BUF_ERR;
|
||||
}
|
||||
}
|
||||
r[i].resp_retcode = 0;
|
||||
}
|
||||
*resp = r;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
// uopiPamAuth runs authentication + account management for service/user using
|
||||
// pass. Returns PAM_SUCCESS or the failing PAM error code.
|
||||
static int uopiPamAuth(const char *service, const char *user, char *pass) {
|
||||
struct pam_conv conv;
|
||||
conv.conv = uopiPamConv;
|
||||
conv.appdata_ptr = (void *)pass;
|
||||
|
||||
pam_handle_t *pamh = NULL;
|
||||
int ret = pam_start(service, user, &conv, &pamh);
|
||||
if (ret != PAM_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
ret = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK);
|
||||
if (ret == PAM_SUCCESS) {
|
||||
ret = pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK);
|
||||
}
|
||||
pam_end(pamh, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// uopiStrerror maps a PAM error code to a human-readable string. Linux-PAM
|
||||
// ignores the handle, so NULL is fine after pam_end.
|
||||
static const char *uopiStrerror(int code) {
|
||||
return pam_strerror(NULL, code);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Available reports whether this build includes PAM support. True here.
|
||||
const Available = true
|
||||
|
||||
// ErrUnavailable is returned by Authenticate in builds without PAM support. It
|
||||
// is declared in both build variants so callers can compare against it.
|
||||
var ErrUnavailable = errors.New("pamauth: PAM support not compiled in")
|
||||
|
||||
// Authenticate verifies username/password against the named PAM service
|
||||
// (/etc/pam.d/<service>). It returns nil on success or an error describing the
|
||||
// PAM failure. It is safe for concurrent use.
|
||||
func Authenticate(service, username, password string) error {
|
||||
cService := C.CString(service)
|
||||
cUser := C.CString(username)
|
||||
cPass := C.CString(password)
|
||||
defer C.free(unsafe.Pointer(cService))
|
||||
defer C.free(unsafe.Pointer(cUser))
|
||||
defer C.free(unsafe.Pointer(cPass))
|
||||
|
||||
if ret := C.uopiPamAuth(cService, cUser, cPass); ret != C.PAM_SUCCESS {
|
||||
return fmt.Errorf("pam: %s", C.GoString(C.uopiStrerror(ret)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user