Merge pull request #9 from 3v1n0/binary-protocol

transaction: Add support for Binary conversation
This commit is contained in:
Mike Steinert
2023-09-22 09:16:15 -05:00
committed by GitHub

View File

@@ -1,11 +1,21 @@
// Package pam provides a wrapper for the PAM application API. // Package pam provides a wrapper for the PAM application API.
package pam package pam
//#cgo CFLAGS: -Wall -std=c99
//#cgo LDFLAGS: -lpam
//
//#include <security/pam_appl.h> //#include <security/pam_appl.h>
//#include <stdlib.h> //#include <stdlib.h>
//#include <stdint.h> //#include <stdint.h>
//#cgo CFLAGS: -Wall -std=c99 //
//#cgo LDFLAGS: -lpam //#ifdef PAM_BINARY_PROMPT
//#define BINARY_PROMPT_IS_SUPPORTED 1
//#else
//#include <limits.h>
//#define PAM_BINARY_PROMPT INT_MAX
//#define BINARY_PROMPT_IS_SUPPORTED 0
//#endif
//
//void init_pam_conv(struct pam_conv *conv, uintptr_t); //void init_pam_conv(struct pam_conv *conv, uintptr_t);
//int pam_start_confdir(const char *service_name, const char *user, const struct pam_conv *pam_conversation, const char *confdir, pam_handle_t **pamh) __attribute__ ((weak)); //int pam_start_confdir(const char *service_name, const char *user, const struct pam_conv *pam_conversation, const char *confdir, pam_handle_t **pamh) __attribute__ ((weak));
//int check_pam_start_confdir(void); //int check_pam_start_confdir(void);
@@ -36,6 +46,9 @@ const (
// TextInfo indicates the conversation handler should display some // TextInfo indicates the conversation handler should display some
// text. // text.
TextInfo = C.PAM_TEXT_INFO TextInfo = C.PAM_TEXT_INFO
// BinaryPrompt indicates the conversation handler that should implement
// the private binary protocol
BinaryPrompt = C.PAM_BINARY_PROMPT
) )
// ConversationHandler is an interface for objects that can be used as // ConversationHandler is an interface for objects that can be used as
@@ -47,6 +60,23 @@ type ConversationHandler interface {
RespondPAM(Style, string) (string, error) RespondPAM(Style, string) (string, error)
} }
// BinaryPointer exposes the type used for the data in a binary conversation
// it represents a pointer to data that is produced by the module and that
// must be parsed depending on the protocol in use
type BinaryPointer unsafe.Pointer
// BinaryConversationHandler is an interface for objects that can be used as
// conversation callbacks during PAM authentication if binary protocol is going
// to be supported.
type BinaryConversationHandler interface {
ConversationHandler
// RespondPAMBinary receives a pointer to the binary message. It's up to
// the receiver to parse it according to the protocol specifications.
// The function can return a byte array that will passed as pointer back
// to the module.
RespondPAMBinary(BinaryPointer) ([]byte, error)
}
// ConversationFunc is an adapter to allow the use of ordinary functions as // ConversationFunc is an adapter to allow the use of ordinary functions as
// conversation callbacks. // conversation callbacks.
type ConversationFunc func(Style, string) (string, error) type ConversationFunc func(Style, string) (string, error)
@@ -57,14 +87,29 @@ func (f ConversationFunc) RespondPAM(s Style, msg string) (string, error) {
} }
// cbPAMConv is a wrapper for the conversation callback function. // cbPAMConv is a wrapper for the conversation callback function.
//
//export cbPAMConv //export cbPAMConv
func cbPAMConv(s C.int, msg *C.char, c C.uintptr_t) (*C.char, C.int) { func cbPAMConv(s C.int, msg *C.char, c C.uintptr_t) (*C.char, C.int) {
var r string var r string
var err error var err error
v := cgo.Handle(c).Value() v := cgo.Handle(c).Value()
style := Style(s)
switch cb := v.(type) { switch cb := v.(type) {
case BinaryConversationHandler:
if style == BinaryPrompt {
bytes, err := cb.RespondPAMBinary(BinaryPointer(msg))
if err != nil {
return nil, C.PAM_CONV_ERR
}
return (*C.char)(C.CBytes(bytes)), C.PAM_SUCCESS
} else {
r, err = cb.RespondPAM(style, C.GoString(msg))
}
case ConversationHandler: case ConversationHandler:
r, err = cb.RespondPAM(Style(s), C.GoString(msg)) if style == BinaryPrompt {
return nil, C.PAM_AUTHINFO_UNAVAIL
}
r, err = cb.RespondPAM(style, C.GoString(msg))
} }
if err != nil { if err != nil {
return nil, C.PAM_CONV_ERR return nil, C.PAM_CONV_ERR
@@ -117,6 +162,12 @@ func StartConfDir(service, user string, handler ConversationHandler, confDir str
} }
func start(service, user string, handler ConversationHandler, confDir string) (*Transaction, error) { func start(service, user string, handler ConversationHandler, confDir string) (*Transaction, error) {
switch handler.(type) {
case BinaryConversationHandler:
if !CheckPamHasBinaryProtocol() {
return nil, errors.New("BinaryConversationHandler() was used, but it is not supported by this platform")
}
}
t := &Transaction{ t := &Transaction{
conv: &C.struct_pam_conv{}, conv: &C.struct_pam_conv{},
c: cgo.NewHandle(handler), c: cgo.NewHandle(handler),
@@ -339,3 +390,8 @@ func (t *Transaction) GetEnvList() (map[string]string, error) {
func CheckPamHasStartConfdir() bool { func CheckPamHasStartConfdir() bool {
return C.check_pam_start_confdir() == 0 return C.check_pam_start_confdir() == 0
} }
// CheckPamHasBinaryProtocol return if pam on system supports PAM_BINARY_PROMPT
func CheckPamHasBinaryProtocol() bool {
return C.BINARY_PROMPT_IS_SUPPORTED != 0
}