| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 | package pam//#include <security/pam_appl.h>//#include <stdlib.h>//#cgo CFLAGS: -Wall -std=c99//#cgo LDFLAGS: -lpam//struct pam_conv *make_pam_conv(void *);import "C"import (	"runtime"	"strings"	"unsafe")// Style is the type of message that the conversation handler should display.type Style int// Coversation handler style types.const (	// PromptEchoOff indicates the conversation handler should obtain a	// string without echoing any text.	PromptEchoOff Style = C.PAM_PROMPT_ECHO_OFF	// PromptEchoOn indicates the conversation handler should obtain a	// string while echoing text.	PromptEchoOn = C.PAM_PROMPT_ECHO_ON	// ErrorMsg indicates the conversation handler should display an	// error message.	ErrorMsg = C.PAM_ERROR_MSG	// TextInfo indicates the conversation handler should display some	// text.	TextInfo = C.PAM_TEXT_INFO)// ConversationHandler is an interface for objects that can be used as// conversation callbacks during PAM authentication.type ConversationHandler interface {	// RespondPAM receives a message style and a message string. If the	// message Style is PromptEchoOff or PromptEchoOn then the function	// should return a response string.	RespondPAM(Style, string) (string, error)}// ConversationFunc is an adapter to allow the use of ordinary functions as// conversation callbacks.type ConversationFunc func(Style, string) (string, error)// RespondPAM is a conversation callback adapter.func (f ConversationFunc) RespondPAM(s Style, msg string) (string, error) {	return f(s, msg)}// Internal conversation structuretype conversation struct {	handler ConversationHandler	conv    *C.struct_pam_conv}// Constructs a new conversation object with a given handler and a newly// allocated pam_conv struct that uses this object as its appdata_ptr.func newConversation(handler ConversationHandler) (*conversation, C.int) {	c := &conversation{}	c.handler = handler	c.conv = C.make_pam_conv(unsafe.Pointer(c))	if c.conv == nil {		return nil, C.PAM_BUF_ERR	}	return c, C.PAM_SUCCESS}// Go-side function for processing a single conversational message. Ultimately// this calls the associated ConversationHandler's ResponsePAM callback with data// coming in from a C-side call.//export cbPAMConvfunc cbPAMConv(s C.int, msg *C.char, appdata unsafe.Pointer) (*C.char, C.int) {	c := (*conversation)(appdata)	r, err := c.handler.RespondPAM(Style(s), C.GoString(msg))	if err != nil {		return nil, C.PAM_CONV_ERR	}	return C.CString(r), C.PAM_SUCCESS}// Transaction is the application's handle for a PAM transaction.type Transaction struct {	handle *C.pam_handle_t	conv   *conversation	status C.int}// Finalize a PAM transaction.func transactionFinalizer(t *Transaction) {	C.pam_end(t.handle, t.status)	C.free(unsafe.Pointer(t.conv.conv))}// 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// transaction provides an interface to the remainder of the API.func Start(service, user string, handler ConversationHandler) (*Transaction, error) {	t := &Transaction{}	t.conv, t.status = newConversation(handler)	if t.status != C.PAM_SUCCESS {		return nil, t	}	s := C.CString(service)	defer C.free(unsafe.Pointer(s))	var u *C.char	if len(user) != 0 {		u = C.CString(user)		defer C.free(unsafe.Pointer(u))	}	t.status = C.pam_start(s, u, t.conv.conv, &t.handle)	if t.status != C.PAM_SUCCESS {		C.free(unsafe.Pointer(t.conv.conv))		return nil, t	}	runtime.SetFinalizer(t, transactionFinalizer)	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)))}// Item is a an PAM information type.type Item int// PAM Item types.const (	// Service is the name which identifies the PAM stack.	Service Item = C.PAM_SERVICE	// User identifies the username identity used by a service.	User = C.PAM_USER	// Tty is the terminal name.	Tty = C.PAM_TTY	// Rhost is the requesting host name.	Rhost = C.PAM_RHOST	// Authtok is the currently active authentication token.	Authtok = C.PAM_AUTHTOK	// Oldauthtok is the old authentication token.	Oldauthtok = C.PAM_OLDAUTHTOK	// Ruser is the requesting user name.	Ruser = C.PAM_RUSER	// UserPrompt is the string use to prompt for a username.	UserPrompt = C.PAM_USER_PROMPT)// SetItem sets a PAM information item.func (t *Transaction) SetItem(i Item, item string) error {	cs := unsafe.Pointer(C.CString(item))	defer C.free(cs)	t.status = C.pam_set_item(t.handle, C.int(i), cs)	if t.status != C.PAM_SUCCESS {		return t	}	return nil}// GetItem retrieves a PAM information item.func (t *Transaction) GetItem(i Item) (string, error) {	var s unsafe.Pointer	t.status = C.pam_get_item(t.handle, C.int(i), &s)	if t.status != C.PAM_SUCCESS {		return "", t	}	return C.GoString((*C.char)(s)), nil}// Flags are inputs to various PAM functions than be combined with a bitwise// or. Refer to the official PAM documentation for which flags are accepted// by which functions.type Flags int// PAM Flag types.const (	// Silent indicates that no messages should be emitted.	Silent Flags = C.PAM_SILENT	// DisallowNullAuthtok indicates that authorization should fail	// if the user does not have a registered authentication token.	DisallowNullAuthtok = C.PAM_DISALLOW_NULL_AUTHTOK	// EstablishCred indicates that credentials should be established	// for the user.	EstablishCred = C.PAM_ESTABLISH_CRED	// DeleteCred inidicates that credentials should be deleted.	DeleteCred = C.PAM_DELETE_CRED	// ReinitializeCred indicates that credentials should be fully	// reinitialized.	ReinitializeCred = C.PAM_REINITIALIZE_CRED	// RefreshCred indicates that the lifetime of existing credentials	// should be extended.	RefreshCred = C.PAM_REFRESH_CRED	// ChangeExpiredAuthtok indicates that the authentication token	// should be changed if it has expired.	ChangeExpiredAuthtok = C.PAM_CHANGE_EXPIRED_AUTHTOK)// Authenticate is used to authenticate the user.//// Valid flags: Silent, DisallowNullAuthtokfunc (t *Transaction) Authenticate(f Flags) error {	t.status = C.pam_authenticate(t.handle, C.int(f))	if t.status != C.PAM_SUCCESS {		return t	}	return nil}// SetCred is used to establish, maintain and delete the credentials of a// user.//// Valid flags: EstablishCred, DeleteCred, ReinitializeCred, RefreshCredfunc (t *Transaction) SetCred(f Flags) error {	t.status = C.pam_setcred(t.handle, C.int(f))	if t.status != C.PAM_SUCCESS {		return t	}	return nil}// AcctMgmt is used to determine if the user's account is valid.//// Valid flags: Silent, DisallowNullAuthtokfunc (t *Transaction) AcctMgmt(f Flags) error {	t.status = C.pam_acct_mgmt(t.handle, C.int(f))	if t.status != C.PAM_SUCCESS {		return t	}	return nil}// ChangeAuthTok is used to change the authentication token.//// Valid flags: Silent, ChangeExpiredAuthtokfunc (t *Transaction) ChangeAuthTok(f Flags) error {	t.status = C.pam_chauthtok(t.handle, C.int(f))	if t.status != C.PAM_SUCCESS {		return t	}	return nil}// OpenSession sets up a user session for an authenticated user.//// Valid flags: Slientfunc (t *Transaction) OpenSession(f Flags) error {	t.status = C.pam_open_session(t.handle, C.int(f))	if t.status != C.PAM_SUCCESS {		return t	}	return nil}// CloseSession closes a previously opened session.//// Valid flags: Silentfunc (t *Transaction) CloseSession(f Flags) error {	t.status = C.pam_close_session(t.handle, C.int(f))	if t.status != C.PAM_SUCCESS {		return t	}	return nil}// PutEnv adds or changes the value of PAM environment variables.//// NAME=value will set a variable to a value.// NAME= will set a variable to an empty value.// NAME (without an "=") will delete a variable.func (t *Transaction) PutEnv(nameval string) error {	cs := C.CString(nameval)	defer C.free(unsafe.Pointer(cs))	t.status = C.pam_putenv(t.handle, cs)	if t.status != C.PAM_SUCCESS {		return t	}	return nil}// GetEnv is used to retrieve a PAM environment variable.func (t *Transaction) GetEnv(name string) string {	cs := C.CString(name)	defer C.free(unsafe.Pointer(cs))	value := C.pam_getenv(t.handle, cs)	if value == nil {		return ""	}	return C.GoString(value)}func next(p **C.char) **C.char {	return (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(p)))}// GetEnvList returns a copy of the PAM environment as a map.func (t *Transaction) GetEnvList() (map[string]string, error) {	env := make(map[string]string)	p := C.pam_getenvlist(t.handle)	if p == nil {		t.status = C.PAM_BUF_ERR		return nil, t	}	for q := p; *q != nil; q = next(q) {		chunks := strings.SplitN(C.GoString(*q), "=", 2)		if len(chunks) == 2 {			env[chunks[0]] = chunks[1]		}		C.free(unsafe.Pointer(*q))	}	C.free(unsafe.Pointer(p))	return env, nil}
 |