transaction: Use cgo.Handle to pass callback data to PAM

Go provides a nicer way to handle Go structs lifetime when they
are passed to C now, so use this instead of a custom
implementation that requires to store them in a map
This commit is contained in:
Marco Trevisan (Treviño)
2023-09-19 20:04:06 +02:00
parent 5253f659f3
commit 78ffef4acd
4 changed files with 13 additions and 82 deletions

View File

@@ -1,39 +0,0 @@
package pam
import "sync"
var cb struct {
sync.Mutex
m map[int]interface{}
c int
}
func init() {
cb.m = make(map[int]interface{})
}
func cbAdd(v interface{}) int {
cb.Lock()
defer cb.Unlock()
cb.c++
cb.m[cb.c] = v
return cb.c
}
func cbGet(c int) interface{} {
cb.Lock()
defer cb.Unlock()
if v, ok := cb.m[c]; ok {
return v
}
panic("Callback pointer not found")
}
func cbDelete(c int) {
cb.Lock()
defer cb.Unlock()
if _, ok := cb.m[c]; !ok {
panic("Callback pointer not found")
}
delete(cb.m, c)
}

View File

@@ -1,33 +0,0 @@
package pam
import (
"reflect"
"testing"
)
func TestCallback_001(t *testing.T) {
c := cbAdd(TestCallback_001)
v := cbGet(c)
if reflect.TypeOf(v) != reflect.TypeOf(TestCallback_001) {
t.Error("Received unexpected value")
}
cbDelete(c)
}
func TestCallback_002(t *testing.T) {
defer func() {
recover()
}()
c := cbAdd(TestCallback_002)
cbGet(c + 1)
t.Error("Expected a panic")
}
func TestCallback_003(t *testing.T) {
defer func() {
recover()
}()
c := cbAdd(TestCallback_003)
cbDelete(c + 1)
t.Error("Expected a panic")
}

View File

@@ -1,5 +1,6 @@
#include "_cgo_export.h" #include "_cgo_export.h"
#include <security/pam_appl.h> #include <security/pam_appl.h>
#include <stdint.h>
#include <string.h> #include <string.h>
#ifdef __sun #ifdef __sun
@@ -25,7 +26,7 @@ int cb_pam_conv(
struct cbPAMConv_return result = cbPAMConv( struct cbPAMConv_return result = cbPAMConv(
msg[i]->msg_style, msg[i]->msg_style,
(char *)msg[i]->msg, (char *)msg[i]->msg,
(long)appdata_ptr); (uintptr_t)appdata_ptr);
if (result.r1 != PAM_SUCCESS) { if (result.r1 != PAM_SUCCESS) {
goto error; goto error;
} }
@@ -45,10 +46,10 @@ error:
return PAM_CONV_ERR; return PAM_CONV_ERR;
} }
void init_pam_conv(struct pam_conv *conv, long c) void init_pam_conv(struct pam_conv *conv, uintptr_t appdata)
{ {
conv->conv = cb_pam_conv; conv->conv = cb_pam_conv;
conv->appdata_ptr = (void *)c; conv->appdata_ptr = (void *)appdata;
} }
// pam_start_confdir is a recent PAM api to declare a confdir (mostly for tests) // pam_start_confdir is a recent PAM api to declare a confdir (mostly for tests)

View File

@@ -3,9 +3,10 @@ package pam
//#include <security/pam_appl.h> //#include <security/pam_appl.h>
//#include <stdlib.h> //#include <stdlib.h>
//#include <stdint.h>
//#cgo CFLAGS: -Wall -std=c99 //#cgo CFLAGS: -Wall -std=c99
//#cgo LDFLAGS: -lpam //#cgo LDFLAGS: -lpam
//void init_pam_conv(struct pam_conv *conv, long c); //void init_pam_conv(struct pam_conv *conv, uintptr_t);
//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 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); //int check_pam_start_confdir(void);
import "C" import "C"
@@ -13,6 +14,7 @@ import "C"
import ( import (
"errors" "errors"
"runtime" "runtime"
"runtime/cgo"
"strings" "strings"
"unsafe" "unsafe"
) )
@@ -56,10 +58,10 @@ func (f ConversationFunc) RespondPAM(s Style, msg string) (string, error) {
// cbPAMConv is a wrapper for the conversation callback function. // cbPAMConv is a wrapper for the conversation callback function.
//export cbPAMConv //export cbPAMConv
func cbPAMConv(s C.int, msg *C.char, c int) (*C.char, C.int) { func cbPAMConv(s C.int, msg *C.char, c C.uintptr_t) (*C.char, C.int) {
var r string var r string
var err error var err error
v := cbGet(c) v := cgo.Handle(c).Value()
switch cb := v.(type) { switch cb := v.(type) {
case ConversationHandler: case ConversationHandler:
r, err = cb.RespondPAM(Style(s), C.GoString(msg)) r, err = cb.RespondPAM(Style(s), C.GoString(msg))
@@ -75,14 +77,14 @@ type Transaction struct {
handle *C.pam_handle_t handle *C.pam_handle_t
conv *C.struct_pam_conv conv *C.struct_pam_conv
status C.int status C.int
c int c cgo.Handle
} }
// transactionFinalizer cleans up the PAM handle and deletes the callback // transactionFinalizer cleans up the PAM handle and deletes the callback
// function. // function.
func transactionFinalizer(t *Transaction) { func transactionFinalizer(t *Transaction) {
C.pam_end(t.handle, t.status) C.pam_end(t.handle, t.status)
cbDelete(t.c) t.c.Delete()
} }
// Start initiates a new PAM transaction. Service is treated identically to // Start initiates a new PAM transaction. Service is treated identically to
@@ -117,9 +119,9 @@ func StartConfDir(service, user string, handler ConversationHandler, confDir str
func start(service, user string, handler ConversationHandler, confDir string) (*Transaction, error) { func start(service, user string, handler ConversationHandler, confDir string) (*Transaction, error) {
t := &Transaction{ t := &Transaction{
conv: &C.struct_pam_conv{}, conv: &C.struct_pam_conv{},
c: cbAdd(handler), c: cgo.NewHandle(handler),
} }
C.init_pam_conv(t.conv, C.long(t.c)) C.init_pam_conv(t.conv, C.uintptr_t(t.c))
runtime.SetFinalizer(t, transactionFinalizer) runtime.SetFinalizer(t, transactionFinalizer)
s := C.CString(service) s := C.CString(service)
defer C.free(unsafe.Pointer(s)) defer C.free(unsafe.Pointer(s))