Update interface

This commit is contained in:
Michael Steinert
2015-03-27 18:59:29 -05:00
parent a6dbe3c2bf
commit d67bb86e54
9 changed files with 385 additions and 427 deletions

1
example/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/example

62
example/example.go Normal file
View File

@@ -0,0 +1,62 @@
// This is a fake login implementation. It uses whatever default
// PAM service configuration is available on the system, and tries
// to authenticate any user. This should cause PAM to ask its
// conversation handler for a username and password, in sequence.
//
// This application will handle those requests by displaying the
// PAM-provided prompt and sending back the first line of stdin input
// it can read for each.
//
// Keep in mind that unless run as root (or setuid root), the only
// user's authentication that can succeed is that of the process owner.
//
// It's not a real login for several reasons:
//
// It doesn't switch users.
// It's not a real login.
//
// It does however demonstrate a simple but powerful use of PAM.
package main
import (
"bufio"
"code.google.com/p/gopass"
"errors"
"fmt"
"github.com/msteinert/pam"
"log"
"os"
)
func main() {
t, err := pam.StartFunc("", "", func(s pam.Style, msg string) (string, error) {
switch s {
case pam.PromptEchoOff:
return gopass.GetPass(msg)
case pam.PromptEchoOn:
fmt.Print(msg)
bio := bufio.NewReader(os.Stdin)
input, err := bio.ReadString('\n')
if err != nil {
return "", err
}
return input[:len(input)-1], nil
case pam.ErrorMsg:
log.Print(msg)
return "", nil
case pam.TextInfo:
fmt.Println(msg)
return "", nil
}
return "", errors.New("Unrecognized message style")
})
if err != nil {
log.Fatalf("Start: %s", err.Error())
}
err = t.Authenticate(0)
if err != nil {
log.Fatalf("Authenticate: %s", err.Error())
}
log.Print("Authentication succeeded!")
}

View File

@@ -1,66 +0,0 @@
#include <security/pam_appl.h>
#include <stdlib.h>
#include <string.h>
#include "_cgo_export.h"
/* Simplification of pam_get_item to remove type ambiguity. Will never
be called (promise) with a type that returns anything other than a string */
get_item_result pam_get_item_string(pam_handle_t *handle, int type) {
get_item_result result;
result.status = pam_get_item(handle, type, (const void **)&result.str);
return result;
}
/* The universal conversation callback for gopam transactions. appdata_ptr
is always taken as a raw pointer to a Go-side pam.conversation object.
In order to avoid nightmareish unsafe pointer math all over the Go
implementation, this universal callback deals with memory allocation of
response buffers, as well as unpacking and repackaging incoming messages
and responses, calling a Go-side callback that handles each one on an
individual basis. */
int gopam_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
{
int i, ok = 1;
struct pam_response *responses = (struct pam_response*)malloc(sizeof(struct pam_response) * num_msg);
memset(responses, 0, sizeof(struct pam_response) * num_msg);
for(i = 1; i < num_msg; ++i) {
const struct pam_message *m = msg[i];
struct goPAMConv_return result = goPAMConv(m->msg_style, (char*)m->msg, appdata_ptr);
if(result.r1 == PAM_SUCCESS)
responses[i].resp = result.r0;
else {
ok = 0;
break;
}
}
if(ok) {
*resp = responses;
return PAM_SUCCESS;
}
/* In the case of failure PAM will never see these responses. The
resp strings that have been allocated by Go-side C.CString calls
must be freed lest we leak them. */
for(i = 0; i < num_msg; ++i)
if(responses[i].resp != NULL)
free(responses[i].resp);
free(responses);
return PAM_CONV_ERR;
}
/* This allocates a new pam_conv struct and fills in its fields:
The conv function pointer always points to the universal gopam_conv.
The appdata_ptr will be set to the incoming void* argument, which
is always a Go-side *pam.conversation whose handler was given
to pam.Start(). */
struct pam_conv* make_gopam_conv(void *goconv)
{
struct pam_conv* conv = (struct pam_conv*)malloc(sizeof(struct pam_conv));
conv->conv = gopam_conv;
conv->appdata_ptr = goconv;
return conv;
}

View File

@@ -1,11 +0,0 @@
#include <security/pam_appl.h>
#include <stdlib.h>
typedef struct {
const char *str;
int status;
} get_item_result;
get_item_result pam_get_item_string(pam_handle_t *handle, int type);
struct pam_conv* make_gopam_conv(void *goconv);

197
pam.go
View File

@@ -1,197 +0,0 @@
// Package pam provides a wrapper for the application layer of the
// Pluggable Authentication Modules library.
package pam
import (
//#include "golang-pam.h"
//#cgo LDFLAGS: -lpam
"C"
"unsafe"
"strings"
)
// Objects implementing the ConversationHandler interface can
// be registered as conversation callbacks to be used during
// PAM authentication. RespondPAM receives a message style
// (one of PROMPT_ECHO_OFF, PROMPT_ECHO_ON, ERROR_MSG, or
// TEXT_INFO) and a message string. It is expected to return
// a response string and a bool indicating success or failure.
type ConversationHandler interface {
RespondPAM(msg_style int, msg string) (string,bool)
}
// ResponseFunc is an adapter to allow the use of ordinary
// functions as conversation callbacks. ResponseFunc(f) is
// a ConversationHandler that calls f, where f must have
// the signature func(int,string)(string,bool).
type ResponseFunc func(int,string) (string,bool)
func (f ResponseFunc) RespondPAM(style int, msg string) (string,bool) {
return f(style,msg)
}
// Internal conversation structure
type conversation struct {
handler ConversationHandler
cconv *C.struct_pam_conv
}
// Cosntructs 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 {
conv := &conversation{}
conv.handler = handler
conv.cconv = C.make_gopam_conv(unsafe.Pointer(conv))
return conv
}
//export goPAMConv
// 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.
func goPAMConv(msg_style C.int, msg *C.char, appdata unsafe.Pointer) (*C.char,int) {
conv := (*conversation)(appdata)
resp,ok := conv.handler.RespondPAM(int(msg_style), C.GoString(msg))
if ok {
return C.CString(resp),SUCCESS
}
return nil,CONV_ERR
}
// Transaction is the application's handle for a single PAM transaction.
type Transaction struct {
handle *C.pam_handle_t
conv *conversation
}
// Start initiates a new PAM transaction. serviceName is treated identically
// to how pam_start internally treats it. The same applies to user, except that
// the empty string is passed to PAM as nil; therefore the empty string should be
// used to signal that no username is being provided.
//
// All application calls to PAM begin with Start(). The returned *Transaction
// provides an interface to the remainder of the API.
//
// The returned status int may be ABORT, BUF_ERR, SUCCESS, or SYSTEM_ERR, as per
// the official PAM documentation.
func Start(serviceName, user string, handler ConversationHandler) (*Transaction,int) {
t := &Transaction{}
t.conv = newConversation(handler)
var status C.int
if len(user) == 0 {
status = C.pam_start(C.CString(serviceName), nil, t.conv.cconv, &t.handle)
} else {
status = C.pam_start(C.CString(serviceName), C.CString(user), t.conv.cconv, &t.handle)
}
if status != SUCCESS {
C.free(unsafe.Pointer(t.conv.cconv))
return nil,int(status)
}
return t, int(status)
}
// Ends a PAM transaction. From Linux-PAM documentation: "The [status] argument
// should be set to the value returned by the last PAM library call."
//
// This may return SUCCESS, or SYSTEM_ERR.
//
// This *must* be called on any Transaction successfully returned by Start() or
// you will leak memory.
func (t *Transaction) End(status int) {
C.pam_end(t.handle, C.int(status))
C.free(unsafe.Pointer(t.conv.cconv))
}
// Sets a PAM informational item. Legal values of itemType are listed here (excluding Linux extensions):
//
// http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/adg-interface-by-app-expected.html#adg-pam_set_item
//
// the CONV item type is also not supported in order to simplify the Go API (and due to
// the fact that it is completely unnecessary).
func (t *Transaction) SetItem(itemType int, item string) int {
if itemType == CONV { return BAD_ITEM }
cs := unsafe.Pointer(C.CString(item))
defer C.free(cs)
return int(C.pam_set_item(t.handle, C.int(itemType), cs))
}
// Gets a PAM item. Legal values of itemType are as specified by the documentation of SetItem.
func (t *Transaction) GetItem(itemType int) (string,int) {
if itemType == CONV { return "",BAD_ITEM }
result := C.pam_get_item_string(t.handle, C.int(itemType))
return C.GoString(result.str),int(result.status)
}
// Error returns a PAM error string from a PAM error code
func (t *Transaction) Error(errnum int) string {
return C.GoString(C.pam_strerror(t.handle, C.int(errnum)))
}
// pam_authenticate
func (t *Transaction) Authenticate(flags int) int {
return int(C.pam_authenticate(t.handle, C.int(flags)))
}
// pam_setcred
func (t* Transaction) SetCred(flags int) int {
return int(C.pam_setcred(t.handle, C.int(flags)))
}
// pam_acctmgmt
func (t* Transaction) AcctMgmt(flags int) int {
return int(C.pam_acct_mgmt(t.handle, C.int(flags)))
}
// pam_chauthtok
func (t* Transaction) ChangeAuthTok(flags int) int {
return int(C.pam_chauthtok(t.handle, C.int(flags)))
}
// pam_open_session
func (t* Transaction) OpenSession(flags int) int {
return int(C.pam_open_session(t.handle, C.int(flags)))
}
// pam_close_session
func (t* Transaction) CloseSession(flags int) int {
return int(C.pam_close_session(t.handle, C.int(flags)))
}
// pam_putenv
func (t* Transaction) PutEnv(nameval string) int {
cs := C.CString(nameval)
defer C.free(unsafe.Pointer(cs))
return int(C.pam_putenv(t.handle, cs))
}
// pam_getenv. Returns an additional argument indicating
// the actual existence of the given environment variable.
func (t* Transaction) GetEnv(name string) (string,bool) {
cs := C.CString(name)
defer C.free(unsafe.Pointer(cs))
value := C.pam_getenv(t.handle, cs)
if value != nil {
return C.GoString(value),true
}
return "",false
}
// GetEnvList internally calls pam_getenvlist and then uses some very
// dangerous code to pull out the returned environment data and mash
// it into a map[string]string. This call may be safe, but it hasn't
// been tested on enough platforms/architectures/PAM-implementations to
// be sure.
func (t* Transaction) GetEnvList() map[string]string {
env := make(map[string]string)
list := (uintptr)(unsafe.Pointer(C.pam_getenvlist(t.handle)))
for *(*uintptr)(unsafe.Pointer(list)) != 0 {
entry := *(*uintptr)(unsafe.Pointer(list))
nameval := C.GoString((*C.char)(unsafe.Pointer(entry)))
chunks := strings.SplitN(nameval, "=", 2)
env[chunks[0]] = chunks[1]
list += (uintptr)(unsafe.Sizeof(list))
}
return env
}

View File

@@ -1,90 +0,0 @@
#include <security/pam_appl.h>
enum {
$SUCCESS = PAM_SUCCESS,
$OPEN_ERR = PAM_OPEN_ERR,
$SYMBOL_ERR = PAM_SYMBOL_ERR,
$SERVICE_ERR = PAM_SERVICE_ERR,
$SYSTEM_ERR = PAM_SYSTEM_ERR,
$BUF_ERR = PAM_BUF_ERR,
$PERM_DENIED = PAM_PERM_DENIED,
$AUTH_ERR = PAM_AUTH_ERR,
$CRED_INSUFFICIENT = PAM_CRED_INSUFFICIENT,
$AUTHINFO_UNAVAIL = PAM_AUTHINFO_UNAVAIL,
$USER_UNKNOWN = PAM_USER_UNKNOWN,
$MAXTRIES = PAM_MAXTRIES,
$NEW_AUTHOTK_REQD = PAM_NEW_AUTHTOK_REQD,
$ACCT_EXPIRED = PAM_ACCT_EXPIRED,
$SESSION_ERR = PAM_SESSION_ERR,
$CRED_UNAVAIL = PAM_CRED_UNAVAIL,
$CRED_EXPIRED = PAM_CRED_EXPIRED,
$CRED_ERR = PAM_CRED_ERR,
$NO_MODULE_DATA = PAM_NO_MODULE_DATA,
$CONV_ERR = PAM_CONV_ERR,
$AUTHTOK_ERR = PAM_AUTHTOK_ERR,
$AUTHTOK_RECOVERY_ERR = PAM_AUTHTOK_RECOVERY_ERR,
$AUTHTOK_LOCK_BUSY = PAM_AUTHTOK_LOCK_BUSY,
$AUTHTOK_DISABLE_AGING = PAM_AUTHTOK_DISABLE_AGING,
$TRY_AGAIN = PAM_TRY_AGAIN,
$IGNORE = PAM_IGNORE,
$ABORT = PAM_ABORT,
$AUTHTOK_EXPIRED = PAM_AUTHTOK_EXPIRED,
$MODULE_UNKNOWN = PAM_MODULE_UNKNOWN,
#if !defined(PAM_BAD_ITEM)
$BAD_ITEM = PAM_SYMBOL_ERR,
#else
$BAD_ITEM = PAM_BAD_ITEM,
#endif
#if !defined(PAM_CONV_AGAIN)
$CONV_AGAIN = PAM_SYMBOL_ERR,
#else
$CONV_AGAIN = PAM_CONV_AGAIN,
#endif
#if !defined(PAM_INCOMPLETE)
$INCOMPLETE = PAM_SYMBOL_ERR
#else
$INCOMPLETE = PAM_INCOMPLETE
#endif
};
enum {
$SILENT = PAM_SILENT,
$DISALLOW_NULL_AUTHTOK = PAM_DISALLOW_NULL_AUTHTOK,
$ESTABLISH_CRED = PAM_ESTABLISH_CRED,
$DELETE_CRED = PAM_DELETE_CRED,
$REINITIALIZE_CRED = PAM_REINITIALIZE_CRED,
$REFRESH_CRED = PAM_REFRESH_CRED,
$CHANGE_EXPIRED_AUTHTOK = PAM_CHANGE_EXPIRED_AUTHTOK
};
enum {
$SERVICE = PAM_SERVICE,
$USER = PAM_USER,
$TTY = PAM_TTY,
$RHOST = PAM_RHOST,
$CONV = PAM_CONV,
$AUTHTOK = PAM_AUTHTOK,
$OLDAUTHTOK = PAM_OLDAUTHTOK,
$RUSER = PAM_RUSER,
$USER_PROMPT = PAM_USER_PROMPT,
/* Linux-PAM extensions. Leaving these out, for now...
$FAIL_DELAY = PAM_FAIL_DELAY,
$XDISPLAY = PAM_XDISPLAY,
$XAUTHDATA = PAM_XAUTHDATA,
$AUTHTOK_TYPE = PAM_AUTHTOK_TYPE
*/
};
enum {
$PROMPT_ECHO_OFF = PAM_PROMPT_ECHO_OFF,
$PROMPT_ECHO_ON = PAM_PROMPT_ECHO_ON,
$ERROR_MSG = PAM_ERROR_MSG,
$TEXT_INFO = PAM_TEXT_INFO
};

View File

@@ -1,63 +0,0 @@
// godefs -g pam pamdefs.c
// MACHINE GENERATED - DO NOT EDIT.
package pam
// Constants
const (
SUCCESS = 0
OPEN_ERR = 0x1
SYMBOL_ERR = 0x2
SERVICE_ERR = 0x3
SYSTEM_ERR = 0x4
BUF_ERR = 0x5
PERM_DENIED = 0x6
AUTH_ERR = 0x7
CRED_INSUFFICIENT = 0x8
AUTHINFO_UNAVAIL = 0x9
USER_UNKNOWN = 0xa
MAXTRIES = 0xb
NEW_AUTHOTK_REQD = 0xc
ACCT_EXPIRED = 0xd
SESSION_ERR = 0xe
CRED_UNAVAIL = 0xf
CRED_EXPIRED = 0x10
CRED_ERR = 0x11
NO_MODULE_DATA = 0x12
CONV_ERR = 0x13
AUTHTOK_ERR = 0x14
AUTHTOK_RECOVERY_ERR = 0x15
AUTHTOK_LOCK_BUSY = 0x16
AUTHTOK_DISABLE_AGING = 0x17
TRY_AGAIN = 0x18
IGNORE = 0x19
ABORT = 0x1a
AUTHTOK_EXPIRED = 0x1b
MODULE_UNKNOWN = 0x1c
BAD_ITEM = 0x1d
CONV_AGAIN = 0x1e
INCOMPLETE = 0x1f
SILENT = 0x8000
DISALLOW_NULL_AUTHTOK = 0x1
ESTABLISH_CRED = 0x2
DELETE_CRED = 0x4
REINITIALIZE_CRED = 0x8
REFRESH_CRED = 0x10
CHANGE_EXPIRED_AUTHTOK = 0x20
SERVICE = 0x1
USER = 0x2
TTY = 0x3
RHOST = 0x4
CONV = 0x5
AUTHTOK = 0x6
OLDAUTHTOK = 0x7
RUSER = 0x8
USER_PROMPT = 0x9
PROMPT_ECHO_OFF = 0x1
PROMPT_ECHO_ON = 0x2
ERROR_MSG = 0x3
TEXT_INFO = 0x4
)
// Types

42
transaction.c Normal file
View File

@@ -0,0 +1,42 @@
#include "_cgo_export.h"
#include <security/pam_appl.h>
int cb_pam_conv(
int num_msg,
const struct pam_message **msg,
struct pam_response **resp,
void *appdata_ptr)
{
*resp = calloc(num_msg, sizeof **resp);
if (!*resp) {
return PAM_BUF_ERR;
}
for (size_t i = 0; i < num_msg; ++i) {
const struct pam_message *m = msg[i];
struct cbPAMConv_return result =
cbPAMConv(m->msg_style, (char *)m->msg, appdata_ptr);
if (result.r1 != PAM_SUCCESS) {
goto error;
}
(*resp)[i].resp = result.r0;
}
return PAM_SUCCESS;
error:
for (size_t i = 0; i < num_msg; ++i) {
free((*resp)[i].resp);
}
free(*resp);
*resp = NULL;
return PAM_CONV_ERR;
}
struct pam_conv *make_pam_conv(void *appdata_ptr)
{
struct pam_conv* conv = malloc(sizeof *conv);
if (!conv) {
return NULL;
}
conv->conv = cb_pam_conv;
conv->appdata_ptr = appdata_ptr;
return conv;
}

280
transaction.go Normal file
View File

@@ -0,0 +1,280 @@
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"
)
type Style int
const (
PromptEchoOff Style = C.PAM_PROMPT_ECHO_OFF
PromptEchoOn = C.PAM_PROMPT_ECHO_ON
ErrorMsg = C.PAM_ERROR_MSG
TextInfo = C.PAM_TEXT_INFO
)
// Objects implementing the ConversationHandler interface can
// be registered as conversation callbacks to be used during
// PAM authentication. RespondPAM receives a message style
// (one of PROMPT_ECHO_OFF, PROMPT_ECHO_ON, ERROR_MSG, or
// TEXT_INFO) and a message string. It is expected to return
// a response string and a bool indicating success or failure.
type ConversationHandler interface {
RespondPAM(Style, string) (string, error)
}
// ConversationFunc is an adapter to allow the use of ordinary
// functions as conversation callbacks. ConversationFunc(f) is
// a ConversationHandler that calls f, where f must have
// the signature func(int,string)(string,bool).
type ConversationFunc func(Style, string) (string, error)
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 single PAM transaction.
type Transaction struct {
handle *C.pam_handle_t
conv *Conversation
status C.int
}
// Ends a PAM transaction. From Linux-PAM documentation: "The [status] argument
// should be set to the value returned by the last PAM library call."
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(). The returned *Transaction
// provides an interface to the remainder of the API.
//
// The returned status int may be ABORT, BUF_ERR, SUCCESS, or SYSTEM_ERR, as per
// the official PAM documentation.
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(s))
}
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
}
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)))
}
type Item int
const (
Service Item = C.PAM_SERVICE
User = C.PAM_USER
Tty = C.PAM_TTY
Rhost = C.PAM_RHOST
Authtok = C.PAM_AUTHTOK
Oldauthtok = C.PAM_OLDAUTHTOK
Ruser = C.PAM_RUSER
UserPrompt = C.PAM_USER_PROMPT
)
// Sets a PAM informational item. Legal values of i are listed here
// (excluding Linux extensions):
//
// http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/adg-interface-by-app-expected.html#adg-pam_set_item
//
// PAM_CONV is not supported in order to simplify the Go API
// (and due to the fact that it is completely unnecessary).
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
}
// Gets a PAM item. Legal values of itemType are as specified by the
// documentation of SetItem.
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
}
type Flags int
const (
Silent Flags = C.PAM_SILENT
DisallowNullAuthtok = C.PAM_DISALLOW_NULL_AUTHTOK
EstablishCred = C.PAM_ESTABLISH_CRED
DeleteCred = C.PAM_DELETE_CRED
ReinitializeCred = C.PAM_REINITIALIZE_CRED
RefreshCred = C.PAM_REFRESH_CRED
ChangeExpiredAuthtok = C.PAM_CHANGE_EXPIRED_AUTHTOK
)
// pam_authenticate
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
}
// pam_setcred
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
}
// pam_acctmgmt
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
}
// pam_chauthtok
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
}
// pam_open_session
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
}
// pam_close_session
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
}
// pam_putenv
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
}
// pam_getenv
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)
}
// GetEnvList internally calls pam_getenvlist and then uses some very
// dangerous code to pull out the returned environment data and mash
// it into a map[string]string. This call may be safe, but it hasn't
// been tested on enough platforms/architectures/PAM-implementations to
// be sure.
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
}
list := (uintptr)(unsafe.Pointer(p))
for *(*uintptr)(unsafe.Pointer(list)) != 0 {
entry := *(*uintptr)(unsafe.Pointer(list))
nameval := C.GoString((*C.char)(unsafe.Pointer(entry)))
chunks := strings.SplitN(nameval, "=", 2)
if len(chunks) == 2 {
env[chunks[0]] = chunks[1]
list += (uintptr)(unsafe.Sizeof(list))
}
}
return env, nil
}