2023-09-25 18:52:56 +02:00
|
|
|
// Package pam provides a wrapper for the PAM application API.
|
|
|
|
|
package pam
|
|
|
|
|
|
2023-09-29 15:13:43 +02:00
|
|
|
/*
|
2023-10-03 14:37:28 +02:00
|
|
|
#include "transaction.h"
|
2023-09-29 15:13:43 +02:00
|
|
|
*/
|
2023-09-25 23:08:08 +02:00
|
|
|
import "C"
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2023-10-03 14:37:28 +02:00
|
|
|
"runtime/cgo"
|
2023-09-29 15:13:43 +02:00
|
|
|
"unsafe"
|
2023-09-25 23:08:08 +02:00
|
|
|
)
|
|
|
|
|
|
2023-09-25 18:52:56 +02:00
|
|
|
// 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)
|
2023-09-29 15:13:43 +02:00
|
|
|
GetUser(prompt string) (string, error)
|
2023-10-03 14:37:28 +02:00
|
|
|
SetData(key string, data any) error
|
|
|
|
|
GetData(key string) (any, error)
|
2023-09-25 18:52:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
2023-09-25 19:31:46 +02:00
|
|
|
type moduleTransaction struct {
|
|
|
|
|
transactionBase
|
|
|
|
|
}
|
2023-09-25 18:52:56 +02:00
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
}
|
2023-09-25 19:31:46 +02:00
|
|
|
|
2023-09-25 23:08:08 +02:00
|
|
|
// 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
|
2023-09-25 19:31:46 +02:00
|
|
|
// the module side.
|
2023-09-25 23:08:08 +02:00
|
|
|
func NewModuleTransactionInvoker(handle NativeHandle) ModuleTransactionInvoker {
|
2023-09-25 19:31:46 +02:00
|
|
|
return &moduleTransaction{transactionBase{handle: handle}}
|
|
|
|
|
}
|
2023-09-25 23:08:08 +02:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
2023-09-29 15:13:43 +02:00
|
|
|
|
|
|
|
|
type moduleTransactionIface interface {
|
|
|
|
|
getUser(outUser **C.char, prompt *C.char) C.int
|
2023-10-03 14:37:28 +02:00
|
|
|
setData(key *C.char, handle C.uintptr_t) C.int
|
|
|
|
|
getData(key *C.char, outHandle *C.uintptr_t) C.int
|
2023-09-29 15:13:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
2023-10-03 14:37:28 +02:00
|
|
|
|
|
|
|
|
// 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))
|
|
|
|
|
}
|