Merge pull request #5 from didrocks/start_confdir

Allow to define confdir
This commit is contained in:
Mike Steinert
2022-09-17 16:16:24 -05:00
committed by GitHub
4 changed files with 95 additions and 7 deletions

2
my-service Normal file
View File

@@ -0,0 +1,2 @@
# Custom stack to always permit, independent of the user name/pass
auth required pam_permit.so

View File

@@ -50,3 +50,12 @@ void init_pam_conv(struct pam_conv *conv, long c)
conv->conv = cb_pam_conv;
conv->appdata_ptr = (void *)c;
}
// pam_start_confdir is a recent PAM api to declare a confdir (mostly for tests)
// weaken the linking dependency to detect if its present.
int pam_start_confdir(const char *service_name, const char *user, const struct pam_conv *pam_conversation, const char *confdir, pam_handle_t **pamh) __attribute__ ((weak));
int check_pam_start_confdir(void) {
if (pam_start_confdir == NULL)
return 1;
return 0;
}

View File

@@ -6,9 +6,12 @@ package pam
//#cgo CFLAGS: -Wall -std=c99
//#cgo LDFLAGS: -lpam
//void init_pam_conv(struct pam_conv *conv, long c);
//int pam_start_confdir(const char *service_name, const char *user, const struct pam_conv *pam_conversation, const char *confdir, pam_handle_t **pamh) __attribute__ ((weak));
//int check_pam_start_confdir(void);
import "C"
import (
"errors"
"runtime"
"strings"
"unsafe"
@@ -85,9 +88,33 @@ func transactionFinalizer(t *Transaction) {
// Start initiates a new PAM transaction. Service is treated identically to
// how pam_start treats it internally.
//
// All application calls to PAM begin with Start (or StartFunc). The returned
// All application calls to PAM begin with Start*. The returned
// transaction provides an interface to the remainder of the API.
func Start(service, user string, handler ConversationHandler) (*Transaction, error) {
return start(service, user, handler, "")
}
// StartFunc registers the handler func as a conversation handler.
func StartFunc(service, user string, handler func(Style, string) (string, error)) (*Transaction, error) {
return Start(service, user, ConversationFunc(handler))
}
// StartConfDir initiates a new PAM transaction. Service is treated identically to
// how pam_start treats it internally.
// confdir allows to define where all pam services are defined. This is used to provide
// custom paths for tests.
//
// All application calls to PAM begin with Start*. The returned
// transaction provides an interface to the remainder of the API.
func StartConfDir(service, user string, handler ConversationHandler, confDir string) (*Transaction, error) {
if !CheckPamHasStartConfdir() {
return nil, errors.New("StartConfDir() was used, but the pam version on the system is not recent enough")
}
return start(service, user, handler, confDir)
}
func start(service, user string, handler ConversationHandler, confDir string) (*Transaction, error) {
t := &Transaction{
conv: &C.struct_pam_conv{},
c: cbAdd(handler),
@@ -101,18 +128,19 @@ func Start(service, user string, handler ConversationHandler) (*Transaction, err
u = C.CString(user)
defer C.free(unsafe.Pointer(u))
}
if confDir == "" {
t.status = C.pam_start(s, u, t.conv, &t.handle)
} else {
c := C.CString(confDir)
defer C.free(unsafe.Pointer(c))
t.status = C.pam_start_confdir(s, u, t.conv, c, &t.handle)
}
if t.status != C.PAM_SUCCESS {
return nil, t
}
return t, nil
}
// StartFunc registers the handler func as a conversation handler.
func StartFunc(service, user string, handler func(Style, string) (string, error)) (*Transaction, error) {
return Start(service, user, ConversationFunc(handler))
}
func (t *Transaction) Error() string {
return C.GoString(C.pam_strerror(t.handle, C.int(t.status)))
}
@@ -304,3 +332,8 @@ func (t *Transaction) GetEnvList() (map[string]string, error) {
C.free(unsafe.Pointer(p))
return env, nil
}
// CheckPamHasStartConfdir return if pam on system supports pam_system_confdir
func CheckPamHasStartConfdir() bool {
return C.check_pam_start_confdir() == 0
}

View File

@@ -166,6 +166,50 @@ func TestPAM_007(t *testing.T) {
}
}
func TestPAM_ConfDir(t *testing.T) {
u, _ := user.Current()
if u.Uid != "0" {
t.Skip("run this test as root")
}
c := Credentials{
// the custom service always permits even with wrong password.
Password: "wrongsecret",
}
tx, err := StartConfDir("my-service", "test", c, ".")
if !CheckPamHasStartConfdir() {
if err == nil {
t.Fatalf("start should have errored out as pam_start_confdir is not available: %v", err)
}
// nothing else we do, we don't support it.
return
}
if err != nil {
t.Fatalf("start #error: %v", err)
}
err = tx.Authenticate(0)
if err != nil {
t.Fatalf("authenticate #error: %v", err)
}
}
func TestPAM_ConfDir_FailNoServiceOrUnsupported(t *testing.T) {
u, _ := user.Current()
if u.Uid != "0" {
t.Skip("run this test as root")
}
c := Credentials{
Password: "secret",
}
_, err := StartConfDir("does-not-exists", "test", c, ".")
if err == nil {
t.Fatalf("authenticate #expected an error")
}
s := err.Error()
if len(s) == 0 {
t.Fatalf("error #expected an error message")
}
}
func TestItem(t *testing.T) {
tx, _ := StartFunc("passwd", "test", func(s Style, msg string) (string, error) {
return "", nil