module-transaction: Add support for setting/getting module data

Module data is data associated with a module handle that is available
for the whole module loading time so it can be used also during
different operations.

We use cgo handles to preserve the life of the go objects so any value
can be associated with a pam transaction.
This commit is contained in:
Marco Trevisan (Treviño)
2023-10-03 14:37:28 +02:00
parent 449b2672b9
commit 7a073f5ba0
6 changed files with 309 additions and 8 deletions

View File

@@ -6,6 +6,7 @@ package pam
#cgo CFLAGS: -Wall -std=c99
#include <stdint.h>
#include <stdlib.h>
#include <security/pam_modules.h>
*/
import "C"
@@ -19,6 +20,7 @@ import (
type mockModuleTransactionExpectations struct {
UserPrompt string
DataKey string
}
type mockModuleTransactionReturnedData struct {
@@ -33,14 +35,19 @@ type mockModuleTransaction struct {
Expectations mockModuleTransactionExpectations
RetData mockModuleTransactionReturnedData
ConversationHandler ConversationHandler
moduleData map[string]uintptr
allocatedData []unsafe.Pointer
}
func newMockModuleTransaction(m *mockModuleTransaction) *mockModuleTransaction {
m.moduleData = make(map[string]uintptr)
runtime.SetFinalizer(m, func(m *mockModuleTransaction) {
for _, ptr := range m.allocatedData {
C.free(ptr)
}
for _, handle := range m.moduleData {
_go_pam_data_cleanup(nil, C.uintptr_t(handle), C.PAM_DATA_SILENT)
}
})
return m
}
@@ -76,6 +83,33 @@ func (m *mockModuleTransaction) getUser(outUser **C.char, prompt *C.char) C.int
return C.int(m.RetData.Status)
}
func (m *mockModuleTransaction) getData(key *C.char, outHandle *C.uintptr_t) C.int {
goKey := C.GoString(key)
if m.Expectations.DataKey != "" && goKey != m.Expectations.DataKey {
m.T.Fatalf("data key mismatch: %#v vs %#v", goKey, m.Expectations.DataKey)
}
if handle, ok := m.moduleData[goKey]; ok {
*outHandle = C.uintptr_t(handle)
} else {
*outHandle = 0
}
return C.int(m.RetData.Status)
}
func (m *mockModuleTransaction) setData(key *C.char, handle C.uintptr_t) C.int {
goKey := C.GoString(key)
if m.Expectations.DataKey != "" && goKey != m.Expectations.DataKey {
m.T.Fatalf("data key mismatch: %#v vs %#v", goKey, m.Expectations.DataKey)
}
if oldHandle, ok := m.moduleData[goKey]; ok {
_go_pam_data_cleanup(nil, C.uintptr_t(oldHandle), C.PAM_DATA_REPLACE)
}
if handle != 0 {
m.moduleData[goKey] = uintptr(handle)
}
return C.int(m.RetData.Status)
}
type mockConversationHandler struct {
User string
ExpectedMessage string