module-transaction: Add support for binary conversations
A module can now initiate a binary conversation decoding the native pointer value as it wants. Added tests to verify the main cases
This commit is contained in:
@@ -60,6 +60,9 @@ func (m *integrationTesterModule) handleRequest(authReq *authRequest, r *Request
|
|||||||
case SerializableStringConvRequest:
|
case SerializableStringConvRequest:
|
||||||
args = append(args, reflect.ValueOf(
|
args = append(args, reflect.ValueOf(
|
||||||
pam.NewStringConvRequest(v.Style, v.Request)))
|
pam.NewStringConvRequest(v.Style, v.Request)))
|
||||||
|
case SerializableBinaryConvRequest:
|
||||||
|
args = append(args, reflect.ValueOf(
|
||||||
|
pam.NewBinaryConvRequestFromBytes(v.Request)))
|
||||||
default:
|
default:
|
||||||
if arg == nil {
|
if arg == nil {
|
||||||
args = append(args, reflect.Zero(method.Type().In(i)))
|
args = append(args, reflect.Zero(method.Type().In(i)))
|
||||||
@@ -76,6 +79,12 @@ func (m *integrationTesterModule) handleRequest(authReq *authRequest, r *Request
|
|||||||
case pam.StringConvResponse:
|
case pam.StringConvResponse:
|
||||||
res.ActionArgs = append(res.ActionArgs,
|
res.ActionArgs = append(res.ActionArgs,
|
||||||
SerializableStringConvResponse{value.Style(), value.Response()})
|
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.Error:
|
case pam.Error:
|
||||||
authReq.lastError = value
|
authReq.lastError = value
|
||||||
res.ActionArgs = append(res.ActionArgs, value)
|
res.ActionArgs = append(res.ActionArgs, value)
|
||||||
|
|||||||
@@ -71,6 +71,21 @@ func ensureEnv(tx *pam.Transaction, variable string, expected string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Request) toBytes(t *testing.T) []byte {
|
||||||
|
t.Helper()
|
||||||
|
bytes, err := r.GOB()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Request) toTransactionData(t *testing.T) []byte {
|
||||||
|
t.Helper()
|
||||||
|
return utils.TestBinaryDataEncoder(r.toBytes(t))
|
||||||
|
}
|
||||||
|
|
||||||
func Test_Moduler_IntegrationTesterModule(t *testing.T) {
|
func Test_Moduler_IntegrationTesterModule(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
if !pam.CheckPamHasStartConfdir() {
|
if !pam.CheckPamHasStartConfdir() {
|
||||||
@@ -818,6 +833,104 @@ func Test_Moduler_IntegrationTesterModule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"start-conv-binary": {
|
||||||
|
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": {
|
||||||
|
expectedError: pam.ErrConv,
|
||||||
|
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.ErrConv},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
r: NewRequest("StartBinaryConv",
|
||||||
|
(&Request{"Not the expected binary data", nil}).toTransactionData(t)),
|
||||||
|
exp: []interface{}{nil, pam.ErrConv},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"start-conv-binary-handle-failure-returned-data-mismatch": {
|
||||||
|
expectedError: pam.ErrConv,
|
||||||
|
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.ErrConv},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
r: NewRequest("StartBinaryConv",
|
||||||
|
(&Request{"Wrong binary data", nil}).toTransactionData(t)),
|
||||||
|
exp: []interface{}{nil, pam.ErrConv},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"start-conv-binary-in-nil": {
|
||||||
|
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": {
|
||||||
|
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 {
|
for name, tc := range tests {
|
||||||
@@ -831,6 +944,13 @@ func Test_Moduler_IntegrationTesterModule(t *testing.T) {
|
|||||||
Args: []string{socketPath}},
|
Args: []string{socketPath}},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
switch tc.credentials.(type) {
|
||||||
|
case pam.BinaryConversationHandler:
|
||||||
|
if !pam.CheckPamHasBinaryProtocol() {
|
||||||
|
t.Skip("Binary protocol is not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tx, err := pam.StartConfDir(name, tc.user, tc.credentials, ts.WorkDir())
|
tx, err := pam.StartConfDir(name, tc.user, tc.credentials, ts.WorkDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("start #error: %v", err)
|
t.Fatalf("start #error: %v", err)
|
||||||
@@ -1085,6 +1205,15 @@ func Test_Moduler_IntegrationTesterModule_Authenticate(t *testing.T) {
|
|||||||
exp: []interface{}{nil, pam.ErrSystem},
|
exp: []interface{}{nil, pam.ErrSystem},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
"StartConv-Binary": {
|
||||||
|
expectedError: pam.ErrSystem,
|
||||||
|
checkedRequests: []checkedRequest{{
|
||||||
|
r: NewRequest("StartConv", SerializableBinaryConvRequest{
|
||||||
|
[]byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99},
|
||||||
|
}),
|
||||||
|
exp: []interface{}{nil, pam.ErrSystem},
|
||||||
|
}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range tests {
|
for name, tc := range tests {
|
||||||
|
|||||||
@@ -36,6 +36,16 @@ type SerializableStringConvResponse struct {
|
|||||||
Response string
|
Response string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SerializableBinaryConvRequest is a serializable binary request.
|
||||||
|
type SerializableBinaryConvRequest struct {
|
||||||
|
Request []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializableBinaryConvResponse is a serializable binary response.
|
||||||
|
type SerializableBinaryConvResponse struct {
|
||||||
|
Response []byte
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
gob.Register(map[string]string{})
|
gob.Register(map[string]string{})
|
||||||
gob.Register(Request{})
|
gob.Register(Request{})
|
||||||
@@ -49,5 +59,9 @@ func init() {
|
|||||||
SerializableStringConvRequest{})
|
SerializableStringConvRequest{})
|
||||||
gob.RegisterName("main.SerializableStringConvResponse",
|
gob.RegisterName("main.SerializableStringConvResponse",
|
||||||
SerializableStringConvResponse{})
|
SerializableStringConvResponse{})
|
||||||
|
gob.RegisterName("main.SerializableBinaryConvRequest",
|
||||||
|
SerializableBinaryConvRequest{})
|
||||||
|
gob.RegisterName("main.SerializableBinaryConvResponse",
|
||||||
|
SerializableBinaryConvResponse{})
|
||||||
gob.Register(utils.SerializableError{})
|
gob.Register(utils.SerializableError{})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
// Package utils contains the internal test utils
|
// Package utils contains the internal test utils
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
|
//#include <stdint.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/msteinert/pam/v2"
|
"github.com/msteinert/pam/v2"
|
||||||
)
|
)
|
||||||
@@ -167,3 +174,90 @@ func (c Credentials) RespondPAM(s pam.Style, msg string) (string, error) {
|
|||||||
return "", errors.Join(pam.ErrConv,
|
return "", errors.Join(pam.ErrConv,
|
||||||
&SerializableError{fmt.Sprintf("unhandled style: %v", s)})
|
&SerializableError{fmt.Sprintf("unhandled style: %v", s)})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BinaryTransaction represents a binary PAM transaction handler struct.
|
||||||
|
type BinaryTransaction struct {
|
||||||
|
data []byte
|
||||||
|
ExpectedNull bool
|
||||||
|
ReturnedData []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBinaryDataEncoder encodes a test binary data.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBinaryDataDecoder decodes a test binary data.
|
||||||
|
func TestBinaryDataDecoder(ptr pam.BinaryPointer) ([]byte, error) {
|
||||||
|
if ptr == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
length := uint8(*((*C.uint8_t)(ptr)))
|
||||||
|
if length == 0 {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
return C.GoBytes(unsafe.Pointer(ptr), C.int(length+1))[1:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBinaryTransactionWithData creates a new [pam.BinaryTransaction] from bytes.
|
||||||
|
func NewBinaryTransactionWithData(data []byte, retData []byte) BinaryTransaction {
|
||||||
|
t := BinaryTransaction{ReturnedData: retData}
|
||||||
|
t.data = TestBinaryDataEncoder(data)
|
||||||
|
t.ExpectedNull = data == nil
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBinaryTransactionWithRandomData creates a new [pam.BinaryTransaction] with random data.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data returns the bytes of the transaction.
|
||||||
|
func (b BinaryTransaction) Data() []byte {
|
||||||
|
return b.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// RespondPAM (not) handles the PAM string conversations.
|
||||||
|
func (b BinaryTransaction) RespondPAM(s pam.Style, msg string) (string, error) {
|
||||||
|
return "", errors.Join(pam.ErrConv,
|
||||||
|
&SerializableError{"unexpected non-binary request"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RespondPAMBinary handles the PAM binary conversations.
|
||||||
|
func (b BinaryTransaction) RespondPAMBinary(ptr pam.BinaryPointer) ([]byte, error) {
|
||||||
|
if ptr == nil && !b.ExpectedNull {
|
||||||
|
return nil, errors.Join(pam.ErrConv,
|
||||||
|
&SerializableError{"unexpected null binary data"})
|
||||||
|
} else if ptr == nil {
|
||||||
|
return TestBinaryDataEncoder(b.ReturnedData), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, _ := TestBinaryDataDecoder(ptr)
|
||||||
|
if !reflect.DeepEqual(bytes, b.data[1:]) {
|
||||||
|
return nil, errors.Join(pam.ErrConv,
|
||||||
|
&SerializableError{
|
||||||
|
fmt.Sprintf("data mismatch %#v vs %#v", bytes, b.data[1:]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return TestBinaryDataEncoder(b.ReturnedData), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import "C"
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/cgo"
|
"runtime/cgo"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -40,10 +41,12 @@ type mockModuleTransaction struct {
|
|||||||
ConversationHandler ConversationHandler
|
ConversationHandler ConversationHandler
|
||||||
moduleData map[string]uintptr
|
moduleData map[string]uintptr
|
||||||
allocatedData []unsafe.Pointer
|
allocatedData []unsafe.Pointer
|
||||||
|
binaryProtocol bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMockModuleTransaction(m *mockModuleTransaction) *mockModuleTransaction {
|
func newMockModuleTransaction(m *mockModuleTransaction) *mockModuleTransaction {
|
||||||
m.moduleData = make(map[string]uintptr)
|
m.moduleData = make(map[string]uintptr)
|
||||||
|
m.binaryProtocol = true
|
||||||
runtime.SetFinalizer(m, func(m *mockModuleTransaction) {
|
runtime.SetFinalizer(m, func(m *mockModuleTransaction) {
|
||||||
for _, ptr := range m.allocatedData {
|
for _, ptr := range m.allocatedData {
|
||||||
C.free(ptr)
|
C.free(ptr)
|
||||||
@@ -126,14 +129,21 @@ func (m *mockModuleTransaction) getConv() (*C.struct_pam_conv, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockModuleTransaction) hasBinaryProtocol() bool {
|
||||||
|
return m.binaryProtocol
|
||||||
|
}
|
||||||
|
|
||||||
type mockConversationHandler struct {
|
type mockConversationHandler struct {
|
||||||
User string
|
User string
|
||||||
PromptEchoOn string
|
PromptEchoOn string
|
||||||
PromptEchoOff string
|
PromptEchoOff string
|
||||||
TextInfo string
|
TextInfo string
|
||||||
ErrorMsg string
|
ErrorMsg string
|
||||||
|
Binary []byte
|
||||||
ExpectedMessage string
|
ExpectedMessage string
|
||||||
ExpectedMessagesByStyle map[Style]string
|
ExpectedMessagesByStyle map[Style]string
|
||||||
|
ExpectedNil bool
|
||||||
|
ExpectedBinary []byte
|
||||||
CheckEmptyMessage bool
|
CheckEmptyMessage bool
|
||||||
ExpectedStyle Style
|
ExpectedStyle Style
|
||||||
CheckZeroStyle bool
|
CheckZeroStyle bool
|
||||||
@@ -178,3 +188,46 @@ func (c mockConversationHandler) RespondPAM(s Style, msg string) (string, error)
|
|||||||
|
|
||||||
return "", fmt.Errorf("%w: unhandled style: %v", ErrConv, s)
|
return "", fmt.Errorf("%w: unhandled style: %v", ErrConv, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)))
|
||||||
|
if length == 0 {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
return C.GoBytes(unsafe.Pointer(ptr), C.int(length+1))[1:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c mockConversationHandler) RespondPAMBinary(ptr BinaryPointer) ([]byte, error) {
|
||||||
|
if ptr == nil && !c.ExpectedNil {
|
||||||
|
return nil, fmt.Errorf("%w: unexpected null binary data", ErrConv)
|
||||||
|
} else if ptr == nil {
|
||||||
|
return testBinaryDataEncoder(c.Binary), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, _ := testBinaryDataDecoder(ptr)
|
||||||
|
if !reflect.DeepEqual(bytes, c.ExpectedBinary) {
|
||||||
|
return nil, fmt.Errorf("%w: data mismatch %#v vs %#v",
|
||||||
|
ErrConv, bytes, c.ExpectedBinary)
|
||||||
|
}
|
||||||
|
|
||||||
|
return testBinaryDataEncoder(c.Binary), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ import "C"
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime"
|
||||||
"runtime/cgo"
|
"runtime/cgo"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,6 +32,7 @@ type ModuleTransaction interface {
|
|||||||
StartStringConv(style Style, prompt string) (StringConvResponse, error)
|
StartStringConv(style Style, prompt string) (StringConvResponse, error)
|
||||||
StartStringConvf(style Style, format string, args ...interface{}) (
|
StartStringConvf(style Style, format string, args ...interface{}) (
|
||||||
StringConvResponse, error)
|
StringConvResponse, error)
|
||||||
|
StartBinaryConv([]byte) (BinaryConvResponse, error)
|
||||||
StartConv(ConvRequest) (ConvResponse, error)
|
StartConv(ConvRequest) (ConvResponse, error)
|
||||||
StartConvMulti([]ConvRequest) ([]ConvResponse, error)
|
StartConvMulti([]ConvRequest) ([]ConvResponse, error)
|
||||||
}
|
}
|
||||||
@@ -110,6 +114,7 @@ type moduleTransactionIface interface {
|
|||||||
setData(key *C.char, handle C.uintptr_t) C.int
|
setData(key *C.char, handle C.uintptr_t) C.int
|
||||||
getData(key *C.char, outHandle *C.uintptr_t) C.int
|
getData(key *C.char, outHandle *C.uintptr_t) C.int
|
||||||
getConv() (*C.struct_pam_conv, error)
|
getConv() (*C.struct_pam_conv, error)
|
||||||
|
hasBinaryProtocol() bool
|
||||||
startConv(conv *C.struct_pam_conv, nMsg C.int,
|
startConv(conv *C.struct_pam_conv, nMsg C.int,
|
||||||
messages **C.struct_pam_message,
|
messages **C.struct_pam_message,
|
||||||
outResponses **C.struct_pam_response) C.int
|
outResponses **C.struct_pam_response) C.int
|
||||||
@@ -261,6 +266,143 @@ func (s stringConvResponse) Response() string {
|
|||||||
return s.response
|
return s.response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BinaryFinalizer is a type of function that can be used to release
|
||||||
|
// the binary when it's not required anymore
|
||||||
|
type BinaryFinalizer func(BinaryPointer)
|
||||||
|
|
||||||
|
// BinaryConvRequester is the interface that binary ConvRequests should
|
||||||
|
// implement
|
||||||
|
type BinaryConvRequester interface {
|
||||||
|
ConvRequest
|
||||||
|
Pointer() BinaryPointer
|
||||||
|
CreateResponse(BinaryPointer) BinaryConvResponse
|
||||||
|
Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryConvRequest is a ConvRequest for performing binary conversations.
|
||||||
|
type BinaryConvRequest struct {
|
||||||
|
ptr atomic.Uintptr
|
||||||
|
finalizer BinaryFinalizer
|
||||||
|
responseFinalizer BinaryFinalizer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBinaryConvRequestFull creates a new BinaryConvRequest with finalizer
|
||||||
|
// for response BinaryResponse.
|
||||||
|
func NewBinaryConvRequestFull(ptr BinaryPointer, finalizer BinaryFinalizer,
|
||||||
|
responseFinalizer BinaryFinalizer) *BinaryConvRequest {
|
||||||
|
b := &BinaryConvRequest{finalizer: finalizer, responseFinalizer: responseFinalizer}
|
||||||
|
b.ptr.Store(uintptr(ptr))
|
||||||
|
if ptr == nil || finalizer == nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ownership of the data here is temporary
|
||||||
|
runtime.SetFinalizer(b, func(b *BinaryConvRequest) { b.Release() })
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBinaryConvRequest creates a new BinaryConvRequest
|
||||||
|
func NewBinaryConvRequest(ptr BinaryPointer, finalizer BinaryFinalizer) *BinaryConvRequest {
|
||||||
|
return NewBinaryConvRequestFull(ptr, finalizer, finalizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBinaryConvRequestFromBytes creates a new BinaryConvRequest from an array
|
||||||
|
// of bytes.
|
||||||
|
func NewBinaryConvRequestFromBytes(bytes []byte) *BinaryConvRequest {
|
||||||
|
if bytes == nil {
|
||||||
|
return &BinaryConvRequest{}
|
||||||
|
}
|
||||||
|
return NewBinaryConvRequest(BinaryPointer(C.CBytes(bytes)),
|
||||||
|
func(ptr BinaryPointer) { C.free(unsafe.Pointer(ptr)) })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
ptr := b.ptr.Load()
|
||||||
|
return *(*BinaryPointer)(unsafe.Pointer(&ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResponse creates a new BinaryConvResponse from the request
|
||||||
|
func (b *BinaryConvRequest) CreateResponse(ptr BinaryPointer) BinaryConvResponse {
|
||||||
|
bcr := &binaryConvResponse{ptr, b.responseFinalizer, &sync.Mutex{}}
|
||||||
|
runtime.SetFinalizer(bcr, func(bcr *binaryConvResponse) {
|
||||||
|
bcr.Release()
|
||||||
|
})
|
||||||
|
return bcr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release releases the resources allocated by the request
|
||||||
|
func (b *BinaryConvRequest) Release() {
|
||||||
|
ptr := b.ptr.Swap(0)
|
||||||
|
if b.finalizer != nil {
|
||||||
|
b.finalizer(*(*BinaryPointer)(unsafe.Pointer(&ptr)))
|
||||||
|
runtime.SetFinalizer(b, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryDecoder is a function type for decode the a binary pointer data into
|
||||||
|
// bytes
|
||||||
|
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)
|
||||||
|
Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
type binaryConvResponse struct {
|
||||||
|
ptr BinaryPointer
|
||||||
|
finalizer BinaryFinalizer
|
||||||
|
mutex *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
b.mutex.Lock()
|
||||||
|
defer b.mutex.Unlock()
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
b.mutex.Lock()
|
||||||
|
defer b.mutex.Unlock()
|
||||||
|
return decoder(b.ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release releases the binary conversation response data.
|
||||||
|
// This is also automatically via a finalizer, but applications may control
|
||||||
|
// this explicitly deferring execution of this.
|
||||||
|
func (b *binaryConvResponse) Release() {
|
||||||
|
b.mutex.Lock()
|
||||||
|
defer b.mutex.Unlock()
|
||||||
|
ptr := b.ptr
|
||||||
|
b.ptr = nil
|
||||||
|
if b.finalizer != nil {
|
||||||
|
b.finalizer(ptr)
|
||||||
|
} else {
|
||||||
|
C.free(unsafe.Pointer(ptr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// StartStringConv starts a text-based conversation using the provided style
|
// StartStringConv starts a text-based conversation using the provided style
|
||||||
// and prompt.
|
// and prompt.
|
||||||
func (m *moduleTransaction) StartStringConv(style Style, prompt string) (
|
func (m *moduleTransaction) StartStringConv(style Style, prompt string) (
|
||||||
@@ -291,6 +433,29 @@ func (m *moduleTransaction) StartStringConvf(style Style, format string, args ..
|
|||||||
return m.StartStringConv(style, fmt.Sprintf(format, args...))
|
return m.StartStringConv(style, fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasBinaryProtocol checks if binary protocol is supported.
|
||||||
|
func (m *moduleTransaction) hasBinaryProtocol() bool {
|
||||||
|
return CheckPamHasBinaryProtocol()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
// StartConv initiates a PAM conversation using the provided ConvRequest.
|
||||||
func (m *moduleTransaction) StartConv(req ConvRequest) (
|
func (m *moduleTransaction) StartConv(req ConvRequest) (
|
||||||
ConvResponse, error) {
|
ConvResponse, error) {
|
||||||
@@ -360,14 +525,21 @@ func (m *moduleTransaction) startConvMultiImpl(iface moduleTransactionIface,
|
|||||||
case StringConvRequest:
|
case StringConvRequest:
|
||||||
cBytes = unsafe.Pointer(C.CString(r.Prompt()))
|
cBytes = unsafe.Pointer(C.CString(r.Prompt()))
|
||||||
defer C.free(cBytes)
|
defer C.free(cBytes)
|
||||||
|
case BinaryConvRequester:
|
||||||
|
if !iface.hasBinaryProtocol() {
|
||||||
|
return nil, errors.New("%w: binary protocol is not supported")
|
||||||
|
}
|
||||||
|
cBytes = unsafe.Pointer(r.Pointer())
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported conversation type %#v", r)
|
return nil, fmt.Errorf("unsupported conversation type %#v", r)
|
||||||
}
|
}
|
||||||
|
|
||||||
goMsgs[i] = &C.struct_pam_message{
|
cMessage := (*C.struct_pam_message)(C.calloc(1,
|
||||||
msg_style: C.int(req.Style()),
|
(C.size_t)(unsafe.Sizeof(*goMsgs[i]))))
|
||||||
msg: (*C.char)(cBytes),
|
defer C.free(unsafe.Pointer(cMessage))
|
||||||
}
|
cMessage.msg_style = C.int(req.Style())
|
||||||
|
cMessage.msg = (*C.char)(cBytes)
|
||||||
|
goMsgs[i] = cMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
var cResponses *C.struct_pam_response
|
var cResponses *C.struct_pam_response
|
||||||
@@ -378,15 +550,26 @@ func (m *moduleTransaction) startConvMultiImpl(iface moduleTransactionIface,
|
|||||||
|
|
||||||
goResponses := unsafe.Slice(cResponses, len(requests))
|
goResponses := unsafe.Slice(cResponses, len(requests))
|
||||||
defer func() {
|
defer func() {
|
||||||
for _, resp := range goResponses {
|
for i, resp := range goResponses {
|
||||||
C.free(unsafe.Pointer(resp.resp))
|
if resp.resp == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch req := requests[i].(type) {
|
||||||
|
case BinaryConvRequester:
|
||||||
|
// In the binary prompt case, we need to rely on the provided
|
||||||
|
// finalizer to release the response, so let's create a new one.
|
||||||
|
req.CreateResponse(BinaryPointer(resp.resp)).Release()
|
||||||
|
default:
|
||||||
|
C.free(unsafe.Pointer(resp.resp))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
C.free(unsafe.Pointer(cResponses))
|
C.free(unsafe.Pointer(cResponses))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
responses = make([]ConvResponse, 0, len(requests))
|
responses = make([]ConvResponse, 0, len(requests))
|
||||||
for i, resp := range goResponses {
|
for i, resp := range goResponses {
|
||||||
msgStyle := requests[i].Style()
|
request := requests[i]
|
||||||
|
msgStyle := request.Style()
|
||||||
switch msgStyle {
|
switch msgStyle {
|
||||||
case PromptEchoOff:
|
case PromptEchoOff:
|
||||||
fallthrough
|
fallthrough
|
||||||
@@ -399,6 +582,13 @@ func (m *moduleTransaction) startConvMultiImpl(iface moduleTransactionIface,
|
|||||||
style: msgStyle,
|
style: msgStyle,
|
||||||
response: C.GoString(resp.resp),
|
response: C.GoString(resp.resp),
|
||||||
})
|
})
|
||||||
|
case BinaryPrompt:
|
||||||
|
// Let's steal the resp ownership here, so that the request
|
||||||
|
// finalizer won't act on it.
|
||||||
|
bcr, _ := request.(BinaryConvRequester)
|
||||||
|
resp := bcr.CreateResponse(BinaryPointer(resp.resp))
|
||||||
|
goResponses[i].resp = nil
|
||||||
|
responses = append(responses, resp)
|
||||||
default:
|
default:
|
||||||
return nil,
|
return nil,
|
||||||
fmt.Errorf("unsupported conversation type %v", msgStyle)
|
fmt.Errorf("unsupported conversation type %v", msgStyle)
|
||||||
|
|||||||
@@ -123,6 +123,11 @@ func Test_NewNullModuleTransaction(t *testing.T) {
|
|||||||
return mt.StartConvMulti([]ConvRequest{
|
return mt.StartConvMulti([]ConvRequest{
|
||||||
NewStringConvRequest(TextInfo, "a prompt"),
|
NewStringConvRequest(TextInfo, "a prompt"),
|
||||||
NewStringConvRequest(ErrorMsg, "another prompt"),
|
NewStringConvRequest(ErrorMsg, "another prompt"),
|
||||||
|
NewBinaryConvRequest(BinaryPointer(&mt), nil),
|
||||||
|
NewBinaryConvRequestFromBytes([]byte("These are bytes!")),
|
||||||
|
NewBinaryConvRequestFromBytes([]byte{}),
|
||||||
|
NewBinaryConvRequestFromBytes(nil),
|
||||||
|
NewBinaryConvRequest(nil, nil),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -620,31 +625,272 @@ func Test_MockModuleTransaction(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"StartConvMulti-all-types": {
|
"StartConvMulti-all-types": {
|
||||||
expectedValue: []ConvResponse{
|
expectedValue: []any{
|
||||||
stringConvResponse{TextInfo, "nice to see you, Go!"},
|
[]ConvResponse{
|
||||||
stringConvResponse{ErrorMsg, "ops, sorry..."},
|
stringConvResponse{TextInfo, "nice to see you, Go!"},
|
||||||
stringConvResponse{PromptEchoOn, "here's my public data"},
|
stringConvResponse{ErrorMsg, "ops, sorry..."},
|
||||||
stringConvResponse{PromptEchoOff, "here's my private data"},
|
stringConvResponse{PromptEchoOn, "here's my public data"},
|
||||||
|
stringConvResponse{PromptEchoOff, "here's my private data"},
|
||||||
|
},
|
||||||
|
[][]byte{
|
||||||
|
{0x01, 0x02, 0x03, 0x05, 0x00, 0x99},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
conversationHandler: mockConversationHandler{
|
conversationHandler: mockConversationHandler{
|
||||||
TextInfo: "nice to see you, Go!",
|
TextInfo: "nice to see you, Go!",
|
||||||
ErrorMsg: "ops, sorry...",
|
ErrorMsg: "ops, sorry...",
|
||||||
PromptEchoOn: "here's my public data",
|
PromptEchoOn: "here's my public data",
|
||||||
PromptEchoOff: "here's my private data",
|
PromptEchoOff: "here's my private data",
|
||||||
|
Binary: []byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99},
|
||||||
ExpectedMessagesByStyle: map[Style]string{
|
ExpectedMessagesByStyle: map[Style]string{
|
||||||
TextInfo: "hello PAM!",
|
TextInfo: "hello PAM!",
|
||||||
ErrorMsg: "This is wrong, PAM!",
|
ErrorMsg: "This is wrong, PAM!",
|
||||||
PromptEchoOn: "Give me your non-private infos",
|
PromptEchoOn: "Give me your non-private infos",
|
||||||
PromptEchoOff: "Give me your private secrets",
|
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) {
|
testFunc: func(mock *mockModuleTransaction) (any, error) {
|
||||||
return mt.startConvMultiImpl(mock, []ConvRequest{
|
requests := []ConvRequest{
|
||||||
NewStringConvRequest(TextInfo, "hello PAM!"),
|
NewStringConvRequest(TextInfo, "hello PAM!"),
|
||||||
NewStringConvRequest(ErrorMsg, "This is wrong, PAM!"),
|
NewStringConvRequest(ErrorMsg, "This is wrong, PAM!"),
|
||||||
NewStringConvRequest(PromptEchoOn, "Give me your non-private infos"),
|
NewStringConvRequest(PromptEchoOn, "Give me your non-private infos"),
|
||||||
NewStringConvRequest(PromptEchoOff, "Give me your private secrets"),
|
NewStringConvRequest(PromptEchoOff, "Give me your private secrets"),
|
||||||
})
|
NewBinaryConvRequestFromBytes(
|
||||||
|
testBinaryDataEncoder([]byte("\x00This is a binary data request\xC5\x00\xffYes it is!"))),
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := mt.startConvMultiImpl(mock, requests)
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"StartConvMulti-all-types-some-failing": {
|
||||||
|
expectedError: ErrConv,
|
||||||
|
expectedValue: []ConvResponse(nil),
|
||||||
|
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",
|
||||||
|
Style(0xfaaf): "This will fail",
|
||||||
|
},
|
||||||
|
ExpectedBinary: []byte("\x00This is a binary data request\xC5\x00\xffYes it is!"),
|
||||||
|
IgnoreUnknownStyle: true,
|
||||||
|
},
|
||||||
|
testFunc: func(mock *mockModuleTransaction) (any, error) {
|
||||||
|
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"),
|
||||||
|
NewStringConvRequest(Style(0xfaaf), "This will fail"),
|
||||||
|
NewBinaryConvRequestFromBytes(
|
||||||
|
testBinaryDataEncoder([]byte("\x00This is a binary data request\xC5\x00\xffYes it is!"))),
|
||||||
|
}
|
||||||
|
|
||||||
|
return mt.startConvMultiImpl(mock, requests)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"StartConv-Binary-unsupported": {
|
||||||
|
expectedValue: nil,
|
||||||
|
expectedError: ErrConv,
|
||||||
|
conversationHandler: mockConversationHandler{
|
||||||
|
ExpectedStyle: BinaryPrompt,
|
||||||
|
ExpectedBinary: []byte("\x00This is a binary data request\xC5\x00\xffYes it is!"),
|
||||||
|
},
|
||||||
|
testFunc: func(mock *mockModuleTransaction) (any, error) {
|
||||||
|
mock.binaryProtocol = false
|
||||||
|
bytes := testBinaryDataEncoder([]byte(
|
||||||
|
"\x00This is a binary data request\xC5\x00\xffYes it is!"))
|
||||||
|
return mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(bytes))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"StartConv-Binary": {
|
||||||
|
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!"))
|
||||||
|
data, err := mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(bytes))
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
bcr, _ := data.(BinaryConvResponse)
|
||||||
|
return bcr.Decode(testBinaryDataDecoder)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"StartConv-Binary-expected-data-mismatch": {
|
||||||
|
expectedError: ErrConv,
|
||||||
|
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": {
|
||||||
|
expectedError: ErrConv,
|
||||||
|
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": {
|
||||||
|
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) {
|
||||||
|
data, err := mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(nil))
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
bcr, _ := data.(BinaryConvResponse)
|
||||||
|
return bcr.Decode(testBinaryDataDecoder)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"StartConv-Binary-returns-nil": {
|
||||||
|
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"))
|
||||||
|
data, err := mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(bytes))
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
bcr, _ := data.(BinaryConvResponse)
|
||||||
|
return bcr.Data(), err
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"StartBinaryConv": {
|
||||||
|
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!"))
|
||||||
|
data, err := mt.startConvImpl(mock, NewBinaryConvRequestFromBytes(bytes))
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
bcr, _ := data.(BinaryConvResponse)
|
||||||
|
return bcr.Decode(testBinaryDataDecoder)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"StartBinaryConv-expected-data-mismatch": {
|
||||||
|
expectedError: ErrConv,
|
||||||
|
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": {
|
||||||
|
expectedError: ErrConv,
|
||||||
|
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": {
|
||||||
|
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) {
|
||||||
|
data, err := mt.startBinaryConvImpl(mock, nil)
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
return data.Decode(testBinaryDataDecoder)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"StartBinaryConv-returns-nil": {
|
||||||
|
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"))
|
||||||
|
data, err := mt.startBinaryConvImpl(mock, bytes)
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
return data.Data(), err
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,10 @@ static inline int cb_pam_conv(int num_msg, PAM_CONST struct pam_message **msg, s
|
|||||||
error:
|
error:
|
||||||
for (size_t i = 0; i < num_msg; ++i) {
|
for (size_t i = 0; i < num_msg; ++i) {
|
||||||
if ((*resp)[i].resp) {
|
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);
|
free((*resp)[i].resp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user