Skip to content

Commit

Permalink
transaction: Add support for using raw binary pointers conversation h…
Browse files Browse the repository at this point in the history
…andler

This requires the allocating function to provide a binary pointer that
will be free'd by the conversation handlers finalizers.

This is for a more advanced usage scenario where the binary conversion
may be handled manually.
  • Loading branch information
3v1n0 committed Nov 27, 2023
1 parent df7e464 commit 0e05609
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 0 deletions.
40 changes: 40 additions & 0 deletions app-transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ type BinaryConversationHandler interface {
RespondPAMBinary(BinaryPointer) ([]byte, error)
}

// BinaryPointerConversationHandler is an interface for objects that can be used as
// conversation callbacks during PAM authentication if binary protocol is going
// to be supported.
type BinaryPointerConversationHandler 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 must return a pointer that is allocated via malloc or
// similar, as it's expected to be free'd by the conversation handler.
RespondPAMBinary(BinaryPointer) (BinaryPointer, error)
}

// ConversationFunc is an adapter to allow the use of ordinary functions as
// conversation callbacks.
type ConversationFunc func(Style, string) (string, error)
Expand All @@ -61,6 +73,20 @@ func (f BinaryConversationFunc) RespondPAM(Style, string) (string, error) {
return "", ErrConv
}

// BinaryPointerConversationFunc is an adapter to allow the use of ordinary
// functions as binary pointer (only) conversation callbacks.
type BinaryPointerConversationFunc func(BinaryPointer) (BinaryPointer, error)

// RespondPAMBinary is a conversation callback adapter.
func (f BinaryPointerConversationFunc) RespondPAMBinary(ptr BinaryPointer) (BinaryPointer, error) {
return f(ptr)
}

// RespondPAM is a dummy conversation callback adapter.
func (f BinaryPointerConversationFunc) RespondPAM(Style, string) (string, error) {
return "", ErrConv
}

// _go_pam_conv_handler is a C wrapper for the conversation callback function.
//
//export _go_pam_conv_handler
Expand Down Expand Up @@ -91,6 +117,15 @@ func pamConvHandler(style Style, msg *C.char, handler ConversationHandler) (*C.c
return (*C.char)(C.CBytes(bytes)), success
}
handler = cb
case BinaryPointerConversationHandler:
if style == BinaryPrompt {
ptr, err := cb.RespondPAMBinary(BinaryPointer(msg))
if err != nil {
return nil, ErrConv.toC()
}
return (*C.char)(ptr), success
}
handler = cb
case ConversationHandler:
if style == BinaryPrompt {
return nil, ErrConv.toC()
Expand Down Expand Up @@ -172,6 +207,11 @@ func start(service, user string, handler ConversationHandler, confDir string) (*
return nil, NewTransactionError(ErrSystem,
errors.New("BinaryConversationHandler() was used, but it is not supported by this platform"))
}
case BinaryPointerConversationHandler:
if !CheckPamHasBinaryProtocol() {
return nil, NewTransactionError(ErrSystem,
errors.New("BinaryPointerConversationHandler() was used, but it is not supported by this platform"))
}
}
t := &Transaction{
conv: &C.struct_pam_conv{},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,10 @@ func Test_Moduler_IntegrationTesterModule(t *testing.T) {
if !pam.CheckPamHasBinaryProtocol() {
t.Skip("Binary protocol is not supported")
}
case pam.BinaryPointerConversationHandler:
if !pam.CheckPamHasBinaryProtocol() {
t.Skip("Binary protocol is not supported")
}
}

tx, err := pam.StartConfDir(name, tc.user, tc.credentials, ts.WorkDir())
Expand Down
139 changes: 139 additions & 0 deletions module-transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,145 @@ func testMockModuleTransaction(t *testing.T, mt *moduleTransaction) {
return mt.startConvImpl(mock, NewStringConvRequest(TextInfo, "prompt"))
},
},
"StartConv-Binary-with-PointerConvFunc": {
expectedValue: []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x95},
conversationHandler: BinaryPointerConversationFunc(func(ptr BinaryPointer) (BinaryPointer, error) {
bytes, _ := testBinaryDataDecoder(ptr)
expectedBinary := []byte(
"\x00This is a binary data request\xC5\x00\xffYes it is! From bytes pointer.")
if !reflect.DeepEqual(bytes, expectedBinary) {
return nil, NewTransactionError(ErrConv,
fmt.Errorf("data mismatch %#v vs %#v", bytes, expectedBinary))
}
return allocateCBytes(testBinaryDataEncoder([]byte{
0x01, 0x02, 0x03, 0x05, 0x00, 0x95})), nil
}),
testFunc: func(mock *mockModuleTransaction) (any, error) {
bytes := testBinaryDataEncoder([]byte(
"\x00This is a binary data request\xC5\x00\xffYes it is! From bytes pointer."))
data, err := mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(bytes))
if err != nil {
return data, err
}
resp, _ := data.(BinaryConvResponse)
return resp.Decode(testBinaryDataDecoder)
},
},
"StartConv-Binary-with-PointerConvFunc-and-allocated-data": {
expectedValue: []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x95},
conversationHandler: BinaryPointerConversationFunc(func(ptr BinaryPointer) (BinaryPointer, error) {
bytes, _ := testBinaryDataDecoder(ptr)
expectedBinary := []byte(
"\x00This is a binary data request\xC5\x00\xffYes it is! From pointer...")
if !reflect.DeepEqual(bytes, expectedBinary) {
return nil, NewTransactionError(ErrConv,
fmt.Errorf("data mismatch %#v vs %#v", bytes, expectedBinary))
}
return allocateCBytes(testBinaryDataEncoder([]byte{
0x01, 0x02, 0x03, 0x05, 0x00, 0x95})), nil
}),
testFunc: func(mock *mockModuleTransaction) (any, error) {
bytes := testBinaryDataEncoder([]byte(
"\x00This is a binary data request\xC5\x00\xffYes it is! From pointer..."))
data, err := mt.startConvImpl(mock,
NewBinaryConvRequest(allocateCBytes(bytes), binaryPointerCBytesFinalizer))
if err != nil {
return data, err
}
resp, _ := data.(BinaryConvResponse)
return resp.Decode(testBinaryDataDecoder)
},
},
"StartConv-Binary-with-PointerConvFunc-and-allocated-data-erroring": {
expectedValue: nil,
expectedError: ErrConv,
conversationHandler: BinaryPointerConversationFunc(func(ptr BinaryPointer) (BinaryPointer, error) {
bytes, _ := testBinaryDataDecoder(ptr)
expectedBinary := []byte(
"\x00This is a binary data request\xC5\x00\xffYes it is! From pointer...")
if !reflect.DeepEqual(bytes, expectedBinary) {
return nil, NewTransactionError(ErrConv,
fmt.Errorf("data mismatch %#v vs %#v", bytes, expectedBinary))
}
return allocateCBytes(testBinaryDataEncoder([]byte{
0x01, 0x02, 0x03, 0x05, 0x00, 0x95})), ErrConv
}),
testFunc: func(mock *mockModuleTransaction) (any, error) {
bytes := testBinaryDataEncoder([]byte(
"\x00This is a binary data request\xC5\x00\xffYes it is! From pointer..."))
data, err := mt.startConvImpl(mock,
NewBinaryConvRequest(allocateCBytes(bytes), binaryPointerCBytesFinalizer))
if err != nil {
return data, err
}
resp, _ := data.(BinaryConvResponse)
return resp.Decode(testBinaryDataDecoder)
},
},
"StartConv-Binary-with-PointerConvFunc-empty": {
expectedValue: []byte{},
conversationHandler: BinaryPointerConversationFunc(func(ptr BinaryPointer) (BinaryPointer, error) {
bytes, _ := testBinaryDataDecoder(ptr)
expectedBinary := []byte(
"\x00This is an empty binary data request\xC5\x00\xffYes it is!")
if !reflect.DeepEqual(bytes, expectedBinary) {
return nil, NewTransactionError(ErrConv,
fmt.Errorf("data mismatch %#v vs %#v", bytes, expectedBinary))
}
return allocateCBytes(testBinaryDataEncoder([]byte{})), nil
}),
testFunc: func(mock *mockModuleTransaction) (any, error) {
bytes := testBinaryDataEncoder([]byte(
"\x00This is an empty binary data request\xC5\x00\xffYes it is!"))
data, err := mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(bytes))
if err != nil {
return data, err
}
resp, _ := data.(BinaryConvResponse)
return resp.Decode(testBinaryDataDecoder)
},
},
"StartConv-Binary-with-PointerConvFunc-nil": {
expectedValue: []byte(nil),
conversationHandler: BinaryPointerConversationFunc(func(ptr BinaryPointer) (BinaryPointer, error) {
bytes, _ := testBinaryDataDecoder(ptr)
expectedBinary := []byte(
"\x00This is a nil binary data request\xC5\x00\xffYes it is!")
if !reflect.DeepEqual(bytes, expectedBinary) {
return nil, NewTransactionError(ErrConv,
fmt.Errorf("data mismatch %#v vs %#v", bytes, expectedBinary))
}
return nil, nil
}),
testFunc: func(mock *mockModuleTransaction) (any, error) {
bytes := testBinaryDataEncoder([]byte(
"\x00This is a nil binary data request\xC5\x00\xffYes it is!"))
data, err := mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(bytes))
if err != nil {
return data, err
}
resp, _ := data.(BinaryConvResponse)
return resp.Decode(testBinaryDataDecoder)
},
},
"StartConv-Binary-with-PointerConvFunc-error": {
expectedError: ErrConv,
conversationHandler: BinaryPointerConversationFunc(func(ptr BinaryPointer) (BinaryPointer, error) {
return nil, errors.New("got an error")
}),
testFunc: func(mock *mockModuleTransaction) (any, error) {
return mt.startConvImpl(mock, NewBinaryConvRequestFromBytes([]byte{}))
},
},
"StartConv-String-with-ConvPointerBinaryFunc": {
expectedError: ErrConv,
conversationHandler: BinaryPointerConversationFunc(func(ptr BinaryPointer) (BinaryPointer, error) {
return nil, nil
}),
testFunc: func(mock *mockModuleTransaction) (any, error) {
return mt.startConvImpl(mock, NewStringConvRequest(TextInfo, "prompt"))
},
},
}

for name, tc := range tests {
Expand Down
11 changes: 11 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
package pam

/*
#include <stdlib.h>
#ifdef __SANITIZE_ADDRESS__
#include <sanitizer/lsan_interface.h>
#endif
Expand All @@ -20,6 +22,7 @@ import (
"os"
"runtime"
"time"
"unsafe"
)

func maybeDoLeakCheck() {
Expand All @@ -29,3 +32,11 @@ func maybeDoLeakCheck() {
C.maybe_do_leak_check()
}
}

func allocateCBytes(bytes []byte) BinaryPointer {
return BinaryPointer(C.CBytes(bytes))
}

func binaryPointerCBytesFinalizer(ptr BinaryPointer) {
C.free(unsafe.Pointer(ptr))
}

0 comments on commit 0e05609

Please sign in to comment.