diff --git a/cmd/pam-moduler/tests/integration-tester-module/integration-tester-module.go b/cmd/pam-moduler/tests/integration-tester-module/integration-tester-module.go index 915c6b66..5066da6b 100644 --- a/cmd/pam-moduler/tests/integration-tester-module/integration-tester-module.go +++ b/cmd/pam-moduler/tests/integration-tester-module/integration-tester-module.go @@ -59,6 +59,9 @@ func (m *integrationTesterModule) handleRequest(authReq *authRequest, r *Request case SerializableStringConvRequest: args = append(args, reflect.ValueOf( pam.NewStringConvRequest(v.Style, v.Request))) + case SerializableBinaryConvRequest: + args = append(args, reflect.ValueOf( + pam.NewBinaryConvRequestFromBytes(v.Request))) default: if arg == nil { args = append(args, reflect.Zero(method.Type().In(i))) @@ -75,6 +78,13 @@ func (m *integrationTesterModule) handleRequest(authReq *authRequest, r *Request case pam.StringConvResponse: res.ActionArgs = append(res.ActionArgs, SerializableStringConvResponse{value.Style(), value.Response()}) + case pam.BinaryConvResponse: + data, err := value.Decode(utils.TestBinaryDataDecoder) + if err != nil { + return nil, err + } + res.ActionArgs = append(res.ActionArgs, + SerializableBinaryConvResponse{data}) case pam.Status: authReq.lastError = value res.ActionArgs = append(res.ActionArgs, value) diff --git a/cmd/pam-moduler/tests/integration-tester-module/integration-tester-module_test.go b/cmd/pam-moduler/tests/integration-tester-module/integration-tester-module_test.go index 3030e22f..41d3f29f 100644 --- a/cmd/pam-moduler/tests/integration-tester-module/integration-tester-module_test.go +++ b/cmd/pam-moduler/tests/integration-tester-module/integration-tester-module_test.go @@ -72,6 +72,19 @@ func ensureEnv(tx *pam.Transaction, variable string, expected string) error { } } +func (r *Request) toBytes(t *testing.T) []byte { + if bytes, err := r.GOB(); err != nil { + t.Fatalf("error: %v", err) + return nil + } else { + return bytes + } +} + +func (r *Request) toTransactionData(t *testing.T) []byte { + return utils.TestBinaryDataEncoder(r.toBytes(t)) +} + func Test_Moduler_IntegrationTesterModule(t *testing.T) { if !pam.CheckPamHasStartConfdir() { t.Skip("this requires PAM with Conf dir support") @@ -854,6 +867,107 @@ func Test_Moduler_IntegrationTesterModule(t *testing.T) { }, }, }, + "start-conv-binary": { + expectedStatus: pam.Success, + credentials: utils.NewBinaryTransactionWithData([]byte( + "\x00This is a binary data request\xC5\x00\xffYes it is!"), + []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}), + checkedRequests: []checkedRequest{ + { + r: NewRequest("StartConv", SerializableBinaryConvRequest{ + utils.TestBinaryDataEncoder( + []byte("\x00This is a binary data request\xC5\x00\xffYes it is!")), + }), + exp: []interface{}{SerializableBinaryConvResponse{ + []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}, + }, nil}, + }, + { + r: NewRequest("StartBinaryConv", + utils.TestBinaryDataEncoder( + []byte("\x00This is a binary data request\xC5\x00\xffYes it is!"))), + exp: []interface{}{SerializableBinaryConvResponse{ + []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}, + }, nil}, + }, + }, + }, + "start-conv-binary-handle-failure-passed-data-mismatch": { + expectedStatus: pam.ConvErr, + credentials: utils.NewBinaryTransactionWithData([]byte( + "\x00This is a binary data request\xC5\x00\xffYes it is!"), + []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}), + checkedRequests: []checkedRequest{ + { + r: NewRequest("StartConv", SerializableBinaryConvRequest{ + (&Request{"Not the expected binary data", nil}).toTransactionData(t), + }), + exp: []interface{}{nil, pam.ConvErr}, + }, + { + r: NewRequest("StartBinaryConv", + (&Request{"Not the expected binary data", nil}).toTransactionData(t)), + exp: []interface{}{nil, pam.ConvErr}, + }, + }, + }, + "start-conv-binary-handle-failure-returned-data-mismatch": { + expectedStatus: pam.ConvErr, + credentials: utils.NewBinaryTransactionWithRandomData(100, + []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}), + checkedRequests: []checkedRequest{ + { + r: NewRequest("StartConv", SerializableBinaryConvRequest{ + (&Request{"Wrong binary data", nil}).toTransactionData(t), + }), + exp: []interface{}{nil, pam.ConvErr}, + }, + { + r: NewRequest("StartBinaryConv", + (&Request{"Wrong binary data", nil}).toTransactionData(t)), + exp: []interface{}{nil, pam.ConvErr}, + }, + }, + }, + "start-conv-binary-in-nil": { + expectedStatus: pam.Success, + credentials: utils.NewBinaryTransactionWithData(nil, + (&Request{"Binary data", []interface{}{true, 123, 0.5, "yay!"}}).toBytes(t)), + checkedRequests: []checkedRequest{ + { + r: NewRequest("StartConv", SerializableBinaryConvRequest{}), + exp: []interface{}{SerializableBinaryConvResponse{ + (&Request{"Binary data", []interface{}{true, 123, 0.5, "yay!"}}).toBytes(t), + }, nil}, + }, + { + r: NewRequest("StartBinaryConv", nil), + exp: []interface{}{SerializableBinaryConvResponse{ + (&Request{"Binary data", []interface{}{true, 123, 0.5, "yay!"}}).toBytes(t), + }, nil}, + }, + }, + }, + "start-conv-binary-out-nil": { + expectedStatus: pam.Success, + credentials: utils.NewBinaryTransactionWithData([]byte( + "\x00This is a binary data request\xC5\x00\xffGimme nil!"), nil), + checkedRequests: []checkedRequest{ + { + r: NewRequest("StartConv", SerializableBinaryConvRequest{ + utils.TestBinaryDataEncoder( + []byte("\x00This is a binary data request\xC5\x00\xffGimme nil!")), + }), + exp: []interface{}{SerializableBinaryConvResponse{}, nil}, + }, + { + r: NewRequest("StartBinaryConv", + utils.TestBinaryDataEncoder( + []byte("\x00This is a binary data request\xC5\x00\xffGimme nil!"))), + exp: []interface{}{SerializableBinaryConvResponse{}, nil}, + }, + }, + }, } for name, tc := range tests { @@ -1119,6 +1233,15 @@ func Test_Moduler_IntegrationTesterModule_Authenticate(t *testing.T) { exp: []interface{}{nil, pam.SystemErr}, }}, }, + "StartConv-Binary": { + expectedStatus: pam.SystemErr, + checkedRequests: []checkedRequest{{ + r: NewRequest("StartConv", SerializableBinaryConvRequest{ + []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}, + }), + exp: []interface{}{nil, pam.SystemErr}, + }}, + }, } for name, tc := range tests { diff --git a/cmd/pam-moduler/tests/integration-tester-module/serialization.go b/cmd/pam-moduler/tests/integration-tester-module/serialization.go index 78d734a0..13c870e7 100644 --- a/cmd/pam-moduler/tests/integration-tester-module/serialization.go +++ b/cmd/pam-moduler/tests/integration-tester-module/serialization.go @@ -30,6 +30,14 @@ type SerializableStringConvResponse struct { Response string } +type SerializableBinaryConvRequest struct { + Request []byte +} + +type SerializableBinaryConvResponse struct { + Response []byte +} + func init() { gob.Register(map[string]string{}) gob.Register(Request{}) @@ -43,5 +51,9 @@ func init() { SerializableStringConvRequest{}) gob.RegisterName("main.SerializableStringConvResponse", SerializableStringConvResponse{}) + gob.RegisterName("main.SerializableBinaryConvRequest", + SerializableBinaryConvRequest{}) + gob.RegisterName("main.SerializableBinaryConvResponse", + SerializableBinaryConvResponse{}) gob.Register(utils.SerializableError{}) } diff --git a/cmd/pam-moduler/tests/internal/utils/test-utils.go b/cmd/pam-moduler/tests/internal/utils/test-utils.go index d7a39962..3b7f1f75 100644 --- a/cmd/pam-moduler/tests/internal/utils/test-utils.go +++ b/cmd/pam-moduler/tests/internal/utils/test-utils.go @@ -1,7 +1,14 @@ package utils +//#include +import "C" + import ( + "crypto/rand" + "encoding/binary" "fmt" + "reflect" + "unsafe" "github.com/msteinert/pam" ) @@ -148,3 +155,80 @@ func (c Credentials) RespondPAM(s pam.Style, msg string) (string, error) { return "", pam.NewTransactionError( &SerializableError{fmt.Sprintf("unhandled style: %v", s)}, pam.ConvErr) } + +type BinaryTransaction struct { + data []byte + ExpectedNull bool + ReturnedData []byte +} + +func TestBinaryDataEncoder(bytes []byte) []byte { + if len(bytes) > 0xff { + panic("Binary transaction size not supported") + } + + if bytes == nil { + return bytes + } + + data := make([]byte, 0, len(bytes)+1) + data = append(data, byte(len(bytes))) + data = append(data, bytes...) + return data +} + +func TestBinaryDataDecoder(ptr pam.BinaryPointer) ([]byte, error) { + if ptr == nil { + return nil, nil + } + + length := uint8(*((*C.uint8_t)(ptr))) + return C.GoBytes(unsafe.Pointer(ptr), C.int(length+1))[1:], nil +} + +func NewBinaryTransactionWithData(data []byte, retData []byte) BinaryTransaction { + t := BinaryTransaction{ReturnedData: retData} + t.data = TestBinaryDataEncoder(data) + t.ExpectedNull = data == nil + return t +} + +func NewBinaryTransactionWithRandomData(size uint8, retData []byte) BinaryTransaction { + t := BinaryTransaction{ReturnedData: retData} + randomData := make([]byte, size) + if err := binary.Read(rand.Reader, binary.LittleEndian, &randomData); err != nil { + panic(err) + } + + t.data = TestBinaryDataEncoder(randomData) + return t +} + +func (b BinaryTransaction) Data() []byte { + return b.data +} + +func (b BinaryTransaction) RespondPAM(s pam.Style, msg string) (string, error) { + return "", pam.NewTransactionError( + &SerializableError{"unexpected non-binary request"}, pam.ConvErr) +} + +func (b BinaryTransaction) RespondPAMBinary(ptr pam.BinaryPointer) ([]byte, error) { + if ptr == nil && !b.ExpectedNull { + return nil, pam.NewTransactionError( + &SerializableError{"unexpected null binary data"}, pam.ConvErr) + } else if ptr == nil { + return TestBinaryDataEncoder(b.ReturnedData), nil + } + + bytes, _ := TestBinaryDataDecoder(ptr) + if !reflect.DeepEqual(bytes, b.data[1:]) { + return nil, pam.NewTransactionError( + &SerializableError{ + fmt.Sprintf("data mismatch %v vs %v", bytes, b.data[1:]), + }, + pam.ConvErr) + } + + return TestBinaryDataEncoder(b.ReturnedData), nil +} diff --git a/module-transaction-mock.go b/module-transaction-mock.go index a702e641..e2b5b8b1 100644 --- a/module-transaction-mock.go +++ b/module-transaction-mock.go @@ -10,7 +10,9 @@ void init_pam_conv(struct pam_conv *conv, uintptr_t appdata); */ import "C" import ( + "errors" "fmt" + "reflect" "runtime" "runtime/cgo" "testing" @@ -131,8 +133,11 @@ type mockConversationHandler struct { PromptEchoOff string TextInfo string ErrorMsg string + Binary []byte ExpectedMessage string ExpectedMessagesByStyle map[Style]string + ExpectedNil bool + ExpectedBinary []byte CheckEmptyMessage bool ExpectedStyle Style CheckZeroStyle bool @@ -175,3 +180,45 @@ func (c mockConversationHandler) RespondPAM(s Style, msg string) (string, error) return "", NewTransactionError( fmt.Errorf("unhandled style: %v", s), ConvErr) } + +func testBinaryDataEncoder(bytes []byte) []byte { + if len(bytes) > 0xff { + panic("Binary transaction size not supported") + } + + if bytes == nil { + return bytes + } + + data := make([]byte, 0, len(bytes)+1) + data = append(data, byte(len(bytes))) + data = append(data, bytes...) + return data +} + +func testBinaryDataDecoder(ptr BinaryPointer) ([]byte, error) { + if ptr == nil { + return nil, nil + } + + length := uint8(*((*C.uint8_t)(ptr))) + return C.GoBytes(unsafe.Pointer(ptr), C.int(length+1))[1:], nil +} + +func (m mockConversationHandler) RespondPAMBinary(ptr BinaryPointer) ([]byte, error) { + if ptr == nil && !m.ExpectedNil { + return nil, NewTransactionError( + errors.New("unexpected null binary data"), ConvErr) + } else if ptr == nil { + return testBinaryDataEncoder(m.Binary), nil + } + + bytes, _ := testBinaryDataDecoder(ptr) + if !reflect.DeepEqual(bytes, m.ExpectedBinary) { + return nil, NewTransactionError( + fmt.Errorf("data mismatch %v vs %v", bytes, m.ExpectedBinary), + ConvErr) + } + + return testBinaryDataEncoder(m.Binary), nil +} diff --git a/module-transaction.go b/module-transaction.go index 5731a5af..e5fff009 100644 --- a/module-transaction.go +++ b/module-transaction.go @@ -18,6 +18,7 @@ import "C" import ( "errors" "fmt" + "runtime" "runtime/cgo" "sync/atomic" "unsafe" @@ -242,6 +243,74 @@ func (s stringConvResponse) Response() string { return s.response } +// BinaryConvRequest is a ConvRequest for performing binary conversations. +type BinaryConvRequest struct { + ptr BinaryPointer +} + +// NewBinaryConvRequest creates a new BinaryConvRequest. +func NewBinaryConvRequest(ptr BinaryPointer) BinaryConvRequest { + return BinaryConvRequest{ptr} +} + +// NewBinaryConvRequest creates a new BinaryConvRequest from an array +// pointer, the lifetime of the data is bound to the object. +func NewBinaryConvRequestFromBytes(bytes []byte) BinaryConvRequest { + m := BinaryConvRequest{} + if bytes != nil { + m.ptr = BinaryPointer(C.CBytes(bytes)) + runtime.SetFinalizer(&m, func(*BinaryConvRequest) { + defer C.free(unsafe.Pointer(m.ptr)) + m.ptr = nil + }) + } + return m +} + +// Style returns the response style for the request, so always BinaryPrompt. +func (b BinaryConvRequest) Style() Style { + return BinaryPrompt +} + +// Pointer returns the conversation style of the StringConvRequest. +func (b BinaryConvRequest) Pointer() BinaryPointer { + return b.ptr +} + +type BinaryDecoder func(BinaryPointer) ([]byte, error) + +// BinaryConvResponse is a subtype of ConvResponse used for binary +// conversation responses. +type BinaryConvResponse interface { + ConvResponse + Data() BinaryPointer + Decode(BinaryDecoder) ([]byte, error) +} + +type binaryConvResponse struct { + ptr BinaryPointer +} + +// Style returns the response style for the response, so always BinaryPrompt. +func (b binaryConvResponse) Style() Style { + return BinaryPrompt +} + +// Data returns the response native pointer, it's up to the protocol to parse +// it accordingly. +func (b binaryConvResponse) Data() BinaryPointer { + return b.ptr +} + +// Decode decodes the binary data using the provided decoder function. +func (b binaryConvResponse) Decode(decoder BinaryDecoder) ( + []byte, error) { + if decoder == nil { + return nil, errors.New("nil decoder provided") + } + return decoder(b.ptr) +} + // StartStringConv starts a text-based conversation using the provided style // and prompt. func (m *ModuleTransaction) StartStringConv(style Style, prompt string) ( @@ -273,6 +342,24 @@ func (m *ModuleTransaction) StartStringConvf(style Style, format string, args .. return m.StartStringConv(style, fmt.Sprintf(format, args...)) } +// StartBinaryConv starts a binary conversation using the provided bytes. +func (m *ModuleTransaction) StartBinaryConv(bytes []byte) ( + BinaryConvResponse, error) { + return m.startBinaryConvImpl(m, bytes) +} + +func (m *ModuleTransaction) startBinaryConvImpl(iface moduleTransactionIface, + bytes []byte) ( + BinaryConvResponse, error) { + res, err := m.startConvImpl(iface, NewBinaryConvRequestFromBytes(bytes)) + if err != nil { + return nil, err + } + + binaryRes := res.(BinaryConvResponse) + return binaryRes, nil +} + // StartConv initiates a PAM conversation using the provided ConvRequest. func (m *ModuleTransaction) StartConv(req ConvRequest) ( ConvResponse, error) { @@ -332,6 +419,14 @@ func (m *ModuleTransaction) startConvMultiImpl(iface moduleTransactionIface, case StringConvRequest: cBytes = unsafe.Pointer(C.CString(r.Prompt())) defer C.free(unsafe.Pointer(cBytes)) + case BinaryConvRequest: + if !CheckPamHasBinaryProtocol() { + return nil, NewTransactionError( + fmt.Errorf( + "binary protocol is not supported"), + ConvErr) + } + cBytes = unsafe.Pointer(r.Pointer()) default: return nil, NewTransactionError( fmt.Errorf( @@ -374,6 +469,14 @@ func (m *ModuleTransaction) startConvMultiImpl(iface moduleTransactionIface, style: msgStyle, response: C.GoString(resp.resp), }) + case BinaryPrompt: + // Let's steal the resp ownership in this case + bcr := binaryConvResponse{BinaryPointer(resp.resp)} + responses[i].resp = nil + runtime.SetFinalizer(&bcr, func(bcr *binaryConvResponse) { + C.free(unsafe.Pointer(bcr.ptr)) + }) + goReplies = append(goReplies, bcr) default: return nil, NewTransactionError( fmt.Errorf( diff --git a/module-transaction_test.go b/module-transaction_test.go index 751f9682..887b4276 100644 --- a/module-transaction_test.go +++ b/module-transaction_test.go @@ -99,6 +99,8 @@ func Test_NewNullModuleTransaction(t *testing.T) { return mt.StartConvMulti([]ConvRequest{ NewStringConvRequest(TextInfo, "a prompt"), NewStringConvRequest(ErrorMsg, "another prompt"), + NewBinaryConvRequest(BinaryPointer(&mt)), + NewBinaryConvRequestFromBytes([]byte("These are bytes!")), }) }, }, @@ -592,31 +594,226 @@ func Test_MockModuleTransaction(t *testing.T) { }, "StartConvMulti-all-types": { expectedRet: Success, - expectedValue: []ConvResponse{ - stringConvResponse{TextInfo, "nice to see you, Go!"}, - stringConvResponse{ErrorMsg, "ops, sorry..."}, - stringConvResponse{PromptEchoOn, "here's my public data"}, - stringConvResponse{PromptEchoOff, "here's my private data"}, + expectedValue: []any{ + []ConvResponse{ + stringConvResponse{TextInfo, "nice to see you, Go!"}, + stringConvResponse{ErrorMsg, "ops, sorry..."}, + stringConvResponse{PromptEchoOn, "here's my public data"}, + stringConvResponse{PromptEchoOff, "here's my private data"}, + }, + [][]byte{ + {0x01, 0x02, 0x03, 0x05, 0x00, 0x99}, + }, }, conversationHandler: mockConversationHandler{ TextInfo: "nice to see you, Go!", ErrorMsg: "ops, sorry...", PromptEchoOn: "here's my public data", PromptEchoOff: "here's my private data", + Binary: []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}, ExpectedMessagesByStyle: map[Style]string{ TextInfo: "hello PAM!", ErrorMsg: "This is wrong, PAM!", PromptEchoOn: "Give me your non-private infos", PromptEchoOff: "Give me your private secrets", }, + ExpectedBinary: []byte("\x00This is a binary data request\xC5\x00\xffYes it is!"), }, testFunc: func(mock *mockModuleTransaction) (any, error) { - return mt.startConvMultiImpl(mock, []ConvRequest{ + requests := []ConvRequest{ NewStringConvRequest(TextInfo, "hello PAM!"), NewStringConvRequest(ErrorMsg, "This is wrong, PAM!"), NewStringConvRequest(PromptEchoOn, "Give me your non-private infos"), NewStringConvRequest(PromptEchoOff, "Give me your private secrets"), - }) + NewBinaryConvRequestFromBytes( + testBinaryDataEncoder([]byte("\x00This is a binary data request\xC5\x00\xffYes it is!"))), + } + + if data, err := mt.startConvMultiImpl(mock, requests); err != nil { + return data, err + } else { + stringResponses := []ConvResponse{} + binaryResponses := [][]byte{} + for i, r := range data { + if r.Style() != requests[i].Style() { + mock.T.Fatalf("unexpected style %v vs %v", + r.Style(), requests[i].Style()) + } + + switch rt := r.(type) { + case BinaryConvResponse: + decoded, err := rt.Decode(testBinaryDataDecoder) + if err != nil { + return data, err + } + binaryResponses = append(binaryResponses, decoded) + case StringConvResponse: + stringResponses = append(stringResponses, r) + default: + mock.T.Fatalf("unexpected value %v", rt) + } + } + return []any{ + stringResponses, + binaryResponses, + }, err + } + }, + }, + "StartConv-Binary": { + expectedRet: Success, + expectedValue: []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}, + conversationHandler: mockConversationHandler{ + ExpectedStyle: BinaryPrompt, + ExpectedBinary: []byte("\x00This is a binary data request\xC5\x00\xffYes it is!"), + Binary: []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}, + }, + testFunc: func(mock *mockModuleTransaction) (any, error) { + bytes := testBinaryDataEncoder([]byte( + "\x00This is a binary data request\xC5\x00\xffYes it is!")) + if data, err := mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(bytes)); err != nil { + return data, err + } else { + return data.(BinaryConvResponse).Decode(testBinaryDataDecoder) + } + }, + }, + "StartConv-Binary-expected-data-mismatch": { + expectedRet: ConvErr, + expectedValue: nil, + conversationHandler: mockConversationHandler{ + ExpectedStyle: BinaryPrompt, + ExpectedBinary: []byte("\x00This is not the expected data!"), + Binary: []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}, + }, + testFunc: func(mock *mockModuleTransaction) (any, error) { + bytes := testBinaryDataEncoder([]byte( + "\x00This is a binary data request\xC5\x00\xffYes it is!")) + return mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(bytes)) + }, + }, + "StartConv-Binary-unexpected-nil": { + expectedRet: ConvErr, + expectedValue: nil, + conversationHandler: mockConversationHandler{ + ExpectedStyle: BinaryPrompt, + ExpectedBinary: []byte("\x00This should not be nil"), + Binary: []byte("\x1ASome binary Dat\xaa"), + }, + testFunc: func(mock *mockModuleTransaction) (any, error) { + return mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(nil)) + }, + }, + "StartConv-Binary-expected-nil": { + expectedRet: Success, + expectedValue: []byte("\x1ASome binary Dat\xaa"), + conversationHandler: mockConversationHandler{ + ExpectedStyle: BinaryPrompt, + ExpectedNil: true, + ExpectedBinary: []byte("\x00This should not be nil"), + Binary: []byte("\x1ASome binary Dat\xaa"), + }, + testFunc: func(mock *mockModuleTransaction) (any, error) { + if data, err := mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(nil)); err != nil { + return data, err + } else { + return data.(BinaryConvResponse).Decode(testBinaryDataDecoder) + } + }, + }, + "StartConv-Binary-returns-nil": { + expectedRet: Success, + expectedValue: BinaryPointer(nil), + conversationHandler: mockConversationHandler{ + ExpectedStyle: BinaryPrompt, + ExpectedBinary: []byte("\x1ASome binary Dat\xaa"), + Binary: nil, + }, + testFunc: func(mock *mockModuleTransaction) (any, error) { + bytes := testBinaryDataEncoder([]byte("\x1ASome binary Dat\xaa")) + if data, err := mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(bytes)); err != nil { + return data, err + } else { + return data.(BinaryConvResponse).Data(), err + } + }, + }, + "StartBinaryConv": { + expectedRet: Success, + expectedValue: []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}, + conversationHandler: mockConversationHandler{ + ExpectedStyle: BinaryPrompt, + ExpectedBinary: []byte("\x00This is a binary data request\xC5\x00\xffYes it is!"), + Binary: []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}, + }, + testFunc: func(mock *mockModuleTransaction) (any, error) { + bytes := testBinaryDataEncoder([]byte( + "\x00This is a binary data request\xC5\x00\xffYes it is!")) + if data, err := mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(bytes)); err != nil { + return data, err + } else { + return data.(BinaryConvResponse).Decode(testBinaryDataDecoder) + } + }, + }, + "StartBinaryConv-expected-data-mismatch": { + expectedRet: ConvErr, + expectedValue: nil, + conversationHandler: mockConversationHandler{ + ExpectedStyle: BinaryPrompt, + ExpectedBinary: []byte("\x00This is not the expected data!"), + Binary: []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}, + }, + testFunc: func(mock *mockModuleTransaction) (any, error) { + bytes := testBinaryDataEncoder([]byte( + "\x00This is a binary data request\xC5\x00\xffYes it is!")) + return mt.startBinaryConvImpl(mock, bytes) + }, + }, + "StartBinaryConv-unexpected-nil": { + expectedRet: ConvErr, + expectedValue: nil, + conversationHandler: mockConversationHandler{ + ExpectedStyle: BinaryPrompt, + ExpectedBinary: []byte("\x00This should not be nil"), + Binary: []byte("\x1ASome binary Dat\xaa"), + }, + testFunc: func(mock *mockModuleTransaction) (any, error) { + return mt.startBinaryConvImpl(mock, nil) + }, + }, + "StartBinaryConv-expected-nil": { + expectedRet: Success, + expectedValue: []byte("\x1ASome binary Dat\xaa"), + conversationHandler: mockConversationHandler{ + ExpectedStyle: BinaryPrompt, + ExpectedNil: true, + ExpectedBinary: []byte("\x00This should not be nil"), + Binary: []byte("\x1ASome binary Dat\xaa"), + }, + testFunc: func(mock *mockModuleTransaction) (any, error) { + if data, err := mt.startBinaryConvImpl(mock, nil); err != nil { + return data, err + } else { + return data.Decode(testBinaryDataDecoder) + } + }, + }, + "StartBinaryConv-returns-nil": { + expectedRet: Success, + expectedValue: BinaryPointer(nil), + conversationHandler: mockConversationHandler{ + ExpectedStyle: BinaryPrompt, + ExpectedBinary: []byte("\x1ASome binary Dat\xaa"), + Binary: nil, + }, + testFunc: func(mock *mockModuleTransaction) (any, error) { + bytes := testBinaryDataEncoder([]byte("\x1ASome binary Dat\xaa")) + if data, err := mt.startBinaryConvImpl(mock, bytes); err != nil { + return data, err + } else { + return data.Data(), err + } }, }, } diff --git a/transaction.c b/transaction.c index 5c8c33c4..842ce8f1 100644 --- a/transaction.c +++ b/transaction.c @@ -30,7 +30,10 @@ int cb_pam_conv(int num_msg, PAM_CONST struct pam_message **msg, struct pam_resp error: for (size_t i = 0; i < num_msg; ++i) { if ((*resp)[i].resp) { - memset((*resp)[i].resp, 0, strlen((*resp)[i].resp)); +#ifdef PAM_BINARY_PROMPT + if (msg[i]->msg_style != PAM_BINARY_PROMPT) +#endif + memset((*resp)[i].resp, 0, strlen((*resp)[i].resp)); free((*resp)[i].resp); } }