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 structure
- type 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 cbPAMConv
- func 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, DisallowNullAuthtok
- func (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, RefreshCred
- func (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, DisallowNullAuthtok
- func (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, ChangeExpiredAuthtok
- func (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: Slient
- func (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: Silent
- func (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
- }
|