Initial commit
This commit is contained in:
16
pam/Makefile
Normal file
16
pam/Makefile
Normal file
@@ -0,0 +1,16 @@
|
||||
include $(GOROOT)/src/Make.inc
|
||||
TARG=pam
|
||||
GOFILES:=pamdefs.go
|
||||
CGOFILES:=pam.go
|
||||
CGO_LDFLAGS:=-lpam
|
||||
CGO_OFILES:=gopam.o
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
|
||||
DOLLAR:="$"
|
||||
|
||||
pamdefs.go: pamdefs.c
|
||||
godefs `echo -n $(CGO_FLAGS) | sed 's/\(^ ^$(DOLLAR)]*\)/-f \1/g'` -g pam pamdefs.c > pamdefs.go
|
||||
gofmt -w pamdefs.go
|
||||
|
||||
|
||||
68
pam/gopam.c
Normal file
68
pam/gopam.c
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <security/_pam_types.h>
|
||||
#include <security/pam_appl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "gopam.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 = 0; 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;
|
||||
}
|
||||
|
||||
11
pam/gopam.h
Normal file
11
pam/gopam.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#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);
|
||||
|
||||
185
pam/pam.go
Normal file
185
pam/pam.go
Normal file
@@ -0,0 +1,185 @@
|
||||
// Package pam provides a wrapper for the application layer of the
|
||||
// Pluggable Authentication Modules library.
|
||||
package pam
|
||||
|
||||
import (
|
||||
//#include "gopam.h"
|
||||
"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
|
||||
}
|
||||
|
||||
// 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{}
|
||||
conv := newConversation(handler)
|
||||
var status C.int
|
||||
if len(user) == 0 {
|
||||
status = C.pam_start(C.CString(serviceName), nil, conv.cconv, &t.handle)
|
||||
} else {
|
||||
status = C.pam_start(C.CString(serviceName), C.CString(user), conv.cconv, &t.handle)
|
||||
}
|
||||
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.
|
||||
func (t *Transaction) End(status int) {
|
||||
C.pam_end(t.handle, C.int(status))
|
||||
}
|
||||
|
||||
// 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.Split(nameval, "=", 2)
|
||||
env[chunks[0]] = chunks[1]
|
||||
list += (uintptr)(unsafe.Sizeof(list))
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
74
pam/pamdefs.c
Normal file
74
pam/pamdefs.c
Normal file
@@ -0,0 +1,74 @@
|
||||
#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,
|
||||
$BAD_ITEM = PAM_BAD_ITEM,
|
||||
$CONV_AGAIN = PAM_CONV_AGAIN,
|
||||
$INCOMPLETE = PAM_INCOMPLETE
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
63
pam/pamdefs.go
Normal file
63
pam/pamdefs.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user