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.
183 lines
5.1 KiB
Go
183 lines
5.1 KiB
Go
// Package pam provides a wrapper for the PAM application API.
|
|
package pam
|
|
|
|
/*
|
|
#include "transaction.h"
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"runtime/cgo"
|
|
"unsafe"
|
|
)
|
|
|
|
// ModuleTransaction is an interface that a pam module transaction
|
|
// should implement.
|
|
type ModuleTransaction interface {
|
|
SetItem(Item, string) error
|
|
GetItem(Item) (string, error)
|
|
PutEnv(nameVal string) error
|
|
GetEnv(name string) string
|
|
GetEnvList() (map[string]string, error)
|
|
GetUser(prompt string) (string, error)
|
|
SetData(key string, data any) error
|
|
GetData(key string) (any, error)
|
|
}
|
|
|
|
// ModuleHandlerFunc is a function type used by the ModuleHandler.
|
|
type ModuleHandlerFunc func(ModuleTransaction, Flags, []string) error
|
|
|
|
// ModuleTransaction is the module-side handle for a PAM transaction.
|
|
type moduleTransaction struct {
|
|
transactionBase
|
|
}
|
|
|
|
// ModuleHandler is an interface for objects that can be used to create
|
|
// PAM modules from go.
|
|
type ModuleHandler interface {
|
|
AcctMgmt(ModuleTransaction, Flags, []string) error
|
|
Authenticate(ModuleTransaction, Flags, []string) error
|
|
ChangeAuthTok(ModuleTransaction, Flags, []string) error
|
|
CloseSession(ModuleTransaction, Flags, []string) error
|
|
OpenSession(ModuleTransaction, Flags, []string) error
|
|
SetCred(ModuleTransaction, Flags, []string) error
|
|
}
|
|
|
|
// ModuleTransactionInvoker is an interface that a pam module transaction
|
|
// should implement to redirect requests from C handlers to go,
|
|
type ModuleTransactionInvoker interface {
|
|
ModuleTransaction
|
|
InvokeHandler(handler ModuleHandlerFunc, flags Flags, args []string) error
|
|
}
|
|
|
|
// NewModuleTransactionInvoker allows initializing a transaction invoker from
|
|
// the module side.
|
|
func NewModuleTransactionInvoker(handle NativeHandle) ModuleTransactionInvoker {
|
|
return &moduleTransaction{transactionBase{handle: handle}}
|
|
}
|
|
|
|
func (m *moduleTransaction) InvokeHandler(handler ModuleHandlerFunc,
|
|
flags Flags, args []string) error {
|
|
invoker := func() error {
|
|
if handler == nil {
|
|
return ErrIgnore
|
|
}
|
|
err := handler(m, flags, args)
|
|
if err != nil {
|
|
service, _ := m.GetItem(Service)
|
|
|
|
var pamErr Error
|
|
if !errors.As(err, &pamErr) {
|
|
err = ErrSystem
|
|
}
|
|
|
|
if pamErr == ErrIgnore || service == "" {
|
|
return err
|
|
}
|
|
|
|
return fmt.Errorf("%s failed: %w", service, err)
|
|
}
|
|
return nil
|
|
}
|
|
err := invoker()
|
|
if errors.Is(err, Error(0)) {
|
|
err = nil
|
|
}
|
|
var status int32
|
|
if err != nil {
|
|
status = int32(ErrSystem)
|
|
|
|
var pamErr Error
|
|
if errors.As(err, &pamErr) {
|
|
status = int32(pamErr)
|
|
}
|
|
}
|
|
m.lastStatus.Store(status)
|
|
return err
|
|
}
|
|
|
|
type moduleTransactionIface interface {
|
|
getUser(outUser **C.char, prompt *C.char) C.int
|
|
setData(key *C.char, handle C.uintptr_t) C.int
|
|
getData(key *C.char, outHandle *C.uintptr_t) C.int
|
|
}
|
|
|
|
func (m *moduleTransaction) getUser(outUser **C.char, prompt *C.char) C.int {
|
|
return C.pam_get_user(m.handle, outUser, prompt)
|
|
}
|
|
|
|
// getUserImpl is the default implementation for GetUser, but kept as private so
|
|
// that can be used to test the pam package
|
|
func (m *moduleTransaction) getUserImpl(iface moduleTransactionIface,
|
|
prompt string) (string, error) {
|
|
var user *C.char
|
|
var cPrompt = C.CString(prompt)
|
|
defer C.free(unsafe.Pointer(cPrompt))
|
|
err := m.handlePamStatus(iface.getUser(&user, cPrompt))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return C.GoString(user), nil
|
|
}
|
|
|
|
// GetUser is similar to GetItem(User), but it would start a conversation if
|
|
// no user is currently set in PAM.
|
|
func (m *moduleTransaction) GetUser(prompt string) (string, error) {
|
|
return m.getUserImpl(m, prompt)
|
|
}
|
|
|
|
// SetData allows to save any value in the module data that is preserved
|
|
// during the whole time the module is loaded.
|
|
func (m *moduleTransaction) SetData(key string, data any) error {
|
|
return m.setDataImpl(m, key, data)
|
|
}
|
|
|
|
func (m *moduleTransaction) setData(key *C.char, handle C.uintptr_t) C.int {
|
|
return C.set_data(m.handle, key, handle)
|
|
}
|
|
|
|
// setDataImpl is the implementation for SetData for testing purposes.
|
|
func (m *moduleTransaction) setDataImpl(iface moduleTransactionIface,
|
|
key string, data any) error {
|
|
var cKey = C.CString(key)
|
|
defer C.free(unsafe.Pointer(cKey))
|
|
var handle cgo.Handle
|
|
if data != nil {
|
|
handle = cgo.NewHandle(data)
|
|
}
|
|
return m.handlePamStatus(iface.setData(cKey, C.uintptr_t(handle)))
|
|
}
|
|
|
|
//export _go_pam_data_cleanup
|
|
func _go_pam_data_cleanup(h NativeHandle, handle C.uintptr_t, status C.int) {
|
|
cgo.Handle(handle).Delete()
|
|
}
|
|
|
|
// GetData allows to get any value from the module data saved using SetData
|
|
// that is preserved across the whole time the module is loaded.
|
|
func (m *moduleTransaction) GetData(key string) (any, error) {
|
|
return m.getDataImpl(m, key)
|
|
}
|
|
|
|
func (m *moduleTransaction) getData(key *C.char, outHandle *C.uintptr_t) C.int {
|
|
return C.get_data(m.handle, key, outHandle)
|
|
}
|
|
|
|
// getDataImpl is the implementation for GetData for testing purposes.
|
|
func (m *moduleTransaction) getDataImpl(iface moduleTransactionIface,
|
|
key string) (any, error) {
|
|
var cKey = C.CString(key)
|
|
defer C.free(unsafe.Pointer(cKey))
|
|
var handle C.uintptr_t
|
|
if err := m.handlePamStatus(iface.getData(cKey, &handle)); err != nil {
|
|
return nil, err
|
|
}
|
|
if goHandle := cgo.Handle(handle); goHandle != cgo.Handle(0) {
|
|
return goHandle.Value(), nil
|
|
}
|
|
|
|
return nil, m.handlePamStatus(C.int(ErrNoModuleData))
|
|
}
|