Skip to content

Commit 149df1f

Browse files
committed
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
1 parent a7026fc commit 149df1f

File tree

8 files changed

+588
-8
lines changed

8 files changed

+588
-8
lines changed

cmd/pam-moduler/tests/integration-tester-module/integration-tester-module.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ func (m *integrationTesterModule) handleRequest(authReq *authRequest, r *Request
5959
case SerializableStringConvRequest:
6060
args = append(args, reflect.ValueOf(
6161
pam.NewStringConvRequest(v.Style, v.Request)))
62+
case SerializableBinaryConvRequest:
63+
args = append(args, reflect.ValueOf(
64+
pam.NewBinaryConvRequestFromBytes(v.Request)))
6265
default:
6366
if arg == nil {
6467
args = append(args, reflect.Zero(method.Type().In(i)))
@@ -75,6 +78,13 @@ func (m *integrationTesterModule) handleRequest(authReq *authRequest, r *Request
7578
case pam.StringConvResponse:
7679
res.ActionArgs = append(res.ActionArgs,
7780
SerializableStringConvResponse{value.Style(), value.Response()})
81+
case pam.BinaryConvResponse:
82+
data, err := value.Decode(utils.TestBinaryDataDecoder)
83+
if err != nil {
84+
return nil, err
85+
}
86+
res.ActionArgs = append(res.ActionArgs,
87+
SerializableBinaryConvResponse{data})
7888
case pam.Status:
7989
authReq.lastError = value
8090
res.ActionArgs = append(res.ActionArgs, value)

cmd/pam-moduler/tests/integration-tester-module/integration-tester-module_test.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,19 @@ func ensureEnv(tx *pam.Transaction, variable string, expected string) error {
7272
}
7373
}
7474

75+
func (r *Request) toBytes(t *testing.T) []byte {
76+
if bytes, err := r.GOB(); err != nil {
77+
t.Fatalf("error: %v", err)
78+
return nil
79+
} else {
80+
return bytes
81+
}
82+
}
83+
84+
func (r *Request) toTransactionData(t *testing.T) []byte {
85+
return utils.TestBinaryDataEncoder(r.toBytes(t))
86+
}
87+
7588
func Test_Moduler_IntegrationTesterModule(t *testing.T) {
7689
if !pam.CheckPamHasStartConfdir() {
7790
t.Skip("this requires PAM with Conf dir support")
@@ -854,6 +867,107 @@ func Test_Moduler_IntegrationTesterModule(t *testing.T) {
854867
},
855868
},
856869
},
870+
"start-conv-binary": {
871+
expectedStatus: pam.Success,
872+
credentials: utils.NewBinaryTransactionWithData([]byte(
873+
"\x00This is a binary data request\xC5\x00\xffYes it is!"),
874+
[]byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}),
875+
checkedRequests: []checkedRequest{
876+
{
877+
r: NewRequest("StartConv", SerializableBinaryConvRequest{
878+
utils.TestBinaryDataEncoder(
879+
[]byte("\x00This is a binary data request\xC5\x00\xffYes it is!")),
880+
}),
881+
exp: []interface{}{SerializableBinaryConvResponse{
882+
[]byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99},
883+
}, nil},
884+
},
885+
{
886+
r: NewRequest("StartBinaryConv",
887+
utils.TestBinaryDataEncoder(
888+
[]byte("\x00This is a binary data request\xC5\x00\xffYes it is!"))),
889+
exp: []interface{}{SerializableBinaryConvResponse{
890+
[]byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99},
891+
}, nil},
892+
},
893+
},
894+
},
895+
"start-conv-binary-handle-failure-passed-data-mismatch": {
896+
expectedStatus: pam.ConvErr,
897+
credentials: utils.NewBinaryTransactionWithData([]byte(
898+
"\x00This is a binary data request\xC5\x00\xffYes it is!"),
899+
[]byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}),
900+
checkedRequests: []checkedRequest{
901+
{
902+
r: NewRequest("StartConv", SerializableBinaryConvRequest{
903+
(&Request{"Not the expected binary data", nil}).toTransactionData(t),
904+
}),
905+
exp: []interface{}{nil, pam.ConvErr},
906+
},
907+
{
908+
r: NewRequest("StartBinaryConv",
909+
(&Request{"Not the expected binary data", nil}).toTransactionData(t)),
910+
exp: []interface{}{nil, pam.ConvErr},
911+
},
912+
},
913+
},
914+
"start-conv-binary-handle-failure-returned-data-mismatch": {
915+
expectedStatus: pam.ConvErr,
916+
credentials: utils.NewBinaryTransactionWithRandomData(100,
917+
[]byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99}),
918+
checkedRequests: []checkedRequest{
919+
{
920+
r: NewRequest("StartConv", SerializableBinaryConvRequest{
921+
(&Request{"Wrong binary data", nil}).toTransactionData(t),
922+
}),
923+
exp: []interface{}{nil, pam.ConvErr},
924+
},
925+
{
926+
r: NewRequest("StartBinaryConv",
927+
(&Request{"Wrong binary data", nil}).toTransactionData(t)),
928+
exp: []interface{}{nil, pam.ConvErr},
929+
},
930+
},
931+
},
932+
"start-conv-binary-in-nil": {
933+
expectedStatus: pam.Success,
934+
credentials: utils.NewBinaryTransactionWithData(nil,
935+
(&Request{"Binary data", []interface{}{true, 123, 0.5, "yay!"}}).toBytes(t)),
936+
checkedRequests: []checkedRequest{
937+
{
938+
r: NewRequest("StartConv", SerializableBinaryConvRequest{}),
939+
exp: []interface{}{SerializableBinaryConvResponse{
940+
(&Request{"Binary data", []interface{}{true, 123, 0.5, "yay!"}}).toBytes(t),
941+
}, nil},
942+
},
943+
{
944+
r: NewRequest("StartBinaryConv", nil),
945+
exp: []interface{}{SerializableBinaryConvResponse{
946+
(&Request{"Binary data", []interface{}{true, 123, 0.5, "yay!"}}).toBytes(t),
947+
}, nil},
948+
},
949+
},
950+
},
951+
"start-conv-binary-out-nil": {
952+
expectedStatus: pam.Success,
953+
credentials: utils.NewBinaryTransactionWithData([]byte(
954+
"\x00This is a binary data request\xC5\x00\xffGimme nil!"), nil),
955+
checkedRequests: []checkedRequest{
956+
{
957+
r: NewRequest("StartConv", SerializableBinaryConvRequest{
958+
utils.TestBinaryDataEncoder(
959+
[]byte("\x00This is a binary data request\xC5\x00\xffGimme nil!")),
960+
}),
961+
exp: []interface{}{SerializableBinaryConvResponse{}, nil},
962+
},
963+
{
964+
r: NewRequest("StartBinaryConv",
965+
utils.TestBinaryDataEncoder(
966+
[]byte("\x00This is a binary data request\xC5\x00\xffGimme nil!"))),
967+
exp: []interface{}{SerializableBinaryConvResponse{}, nil},
968+
},
969+
},
970+
},
857971
}
858972

859973
for name, tc := range tests {
@@ -1119,6 +1233,15 @@ func Test_Moduler_IntegrationTesterModule_Authenticate(t *testing.T) {
11191233
exp: []interface{}{nil, pam.SystemErr},
11201234
}},
11211235
},
1236+
"StartConv-Binary": {
1237+
expectedStatus: pam.SystemErr,
1238+
checkedRequests: []checkedRequest{{
1239+
r: NewRequest("StartConv", SerializableBinaryConvRequest{
1240+
[]byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99},
1241+
}),
1242+
exp: []interface{}{nil, pam.SystemErr},
1243+
}},
1244+
},
11221245
}
11231246

11241247
for name, tc := range tests {

cmd/pam-moduler/tests/integration-tester-module/serialization.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ type SerializableStringConvResponse struct {
3030
Response string
3131
}
3232

33+
type SerializableBinaryConvRequest struct {
34+
Request []byte
35+
}
36+
37+
type SerializableBinaryConvResponse struct {
38+
Response []byte
39+
}
40+
3341
func init() {
3442
gob.Register(map[string]string{})
3543
gob.Register(Request{})
@@ -43,5 +51,9 @@ func init() {
4351
SerializableStringConvRequest{})
4452
gob.RegisterName("main.SerializableStringConvResponse",
4553
SerializableStringConvResponse{})
54+
gob.RegisterName("main.SerializableBinaryConvRequest",
55+
SerializableBinaryConvRequest{})
56+
gob.RegisterName("main.SerializableBinaryConvResponse",
57+
SerializableBinaryConvResponse{})
4658
gob.Register(utils.SerializableError{})
4759
}

cmd/pam-moduler/tests/internal/utils/test-utils.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
package utils
22

3+
//#include <stdint.h>
4+
import "C"
5+
36
import (
7+
"crypto/rand"
8+
"encoding/binary"
49
"fmt"
10+
"reflect"
11+
"unsafe"
512

613
"github.com/msteinert/pam"
714
)
@@ -148,3 +155,80 @@ func (c Credentials) RespondPAM(s pam.Style, msg string) (string, error) {
148155
return "", pam.NewTransactionError(
149156
&SerializableError{fmt.Sprintf("unhandled style: %v", s)}, pam.ConvErr)
150157
}
158+
159+
type BinaryTransaction struct {
160+
data []byte
161+
ExpectedNull bool
162+
ReturnedData []byte
163+
}
164+
165+
func TestBinaryDataEncoder(bytes []byte) []byte {
166+
if len(bytes) > 0xff {
167+
panic("Binary transaction size not supported")
168+
}
169+
170+
if bytes == nil {
171+
return bytes
172+
}
173+
174+
data := make([]byte, 0, len(bytes)+1)
175+
data = append(data, byte(len(bytes)))
176+
data = append(data, bytes...)
177+
return data
178+
}
179+
180+
func TestBinaryDataDecoder(ptr pam.BinaryPointer) ([]byte, error) {
181+
if ptr == nil {
182+
return nil, nil
183+
}
184+
185+
length := uint8(*((*C.uint8_t)(ptr)))
186+
return C.GoBytes(unsafe.Pointer(ptr), C.int(length+1))[1:], nil
187+
}
188+
189+
func NewBinaryTransactionWithData(data []byte, retData []byte) BinaryTransaction {
190+
t := BinaryTransaction{ReturnedData: retData}
191+
t.data = TestBinaryDataEncoder(data)
192+
t.ExpectedNull = data == nil
193+
return t
194+
}
195+
196+
func NewBinaryTransactionWithRandomData(size uint8, retData []byte) BinaryTransaction {
197+
t := BinaryTransaction{ReturnedData: retData}
198+
randomData := make([]byte, size)
199+
if err := binary.Read(rand.Reader, binary.LittleEndian, &randomData); err != nil {
200+
panic(err)
201+
}
202+
203+
t.data = TestBinaryDataEncoder(randomData)
204+
return t
205+
}
206+
207+
func (b BinaryTransaction) Data() []byte {
208+
return b.data
209+
}
210+
211+
func (b BinaryTransaction) RespondPAM(s pam.Style, msg string) (string, error) {
212+
return "", pam.NewTransactionError(
213+
&SerializableError{"unexpected non-binary request"}, pam.ConvErr)
214+
}
215+
216+
func (b BinaryTransaction) RespondPAMBinary(ptr pam.BinaryPointer) ([]byte, error) {
217+
if ptr == nil && !b.ExpectedNull {
218+
return nil, pam.NewTransactionError(
219+
&SerializableError{"unexpected null binary data"}, pam.ConvErr)
220+
} else if ptr == nil {
221+
return TestBinaryDataEncoder(b.ReturnedData), nil
222+
}
223+
224+
bytes, _ := TestBinaryDataDecoder(ptr)
225+
if !reflect.DeepEqual(bytes, b.data[1:]) {
226+
return nil, pam.NewTransactionError(
227+
&SerializableError{
228+
fmt.Sprintf("data mismatch %v vs %v", bytes, b.data[1:]),
229+
},
230+
pam.ConvErr)
231+
}
232+
233+
return TestBinaryDataEncoder(b.ReturnedData), nil
234+
}

module-transaction-mock.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ void init_pam_conv(struct pam_conv *conv, uintptr_t appdata);
1010
*/
1111
import "C"
1212
import (
13+
"errors"
1314
"fmt"
15+
"reflect"
1416
"runtime"
1517
"runtime/cgo"
1618
"testing"
@@ -131,8 +133,11 @@ type mockConversationHandler struct {
131133
PromptEchoOff string
132134
TextInfo string
133135
ErrorMsg string
136+
Binary []byte
134137
ExpectedMessage string
135138
ExpectedMessagesByStyle map[Style]string
139+
ExpectedNil bool
140+
ExpectedBinary []byte
136141
CheckEmptyMessage bool
137142
ExpectedStyle Style
138143
CheckZeroStyle bool
@@ -175,3 +180,45 @@ func (c mockConversationHandler) RespondPAM(s Style, msg string) (string, error)
175180
return "", NewTransactionError(
176181
fmt.Errorf("unhandled style: %v", s), ConvErr)
177182
}
183+
184+
func testBinaryDataEncoder(bytes []byte) []byte {
185+
if len(bytes) > 0xff {
186+
panic("Binary transaction size not supported")
187+
}
188+
189+
if bytes == nil {
190+
return bytes
191+
}
192+
193+
data := make([]byte, 0, len(bytes)+1)
194+
data = append(data, byte(len(bytes)))
195+
data = append(data, bytes...)
196+
return data
197+
}
198+
199+
func testBinaryDataDecoder(ptr BinaryPointer) ([]byte, error) {
200+
if ptr == nil {
201+
return nil, nil
202+
}
203+
204+
length := uint8(*((*C.uint8_t)(ptr)))
205+
return C.GoBytes(unsafe.Pointer(ptr), C.int(length+1))[1:], nil
206+
}
207+
208+
func (m mockConversationHandler) RespondPAMBinary(ptr BinaryPointer) ([]byte, error) {
209+
if ptr == nil && !m.ExpectedNil {
210+
return nil, NewTransactionError(
211+
errors.New("unexpected null binary data"), ConvErr)
212+
} else if ptr == nil {
213+
return testBinaryDataEncoder(m.Binary), nil
214+
}
215+
216+
bytes, _ := testBinaryDataDecoder(ptr)
217+
if !reflect.DeepEqual(bytes, m.ExpectedBinary) {
218+
return nil, NewTransactionError(
219+
fmt.Errorf("data mismatch %v vs %v", bytes, m.ExpectedBinary),
220+
ConvErr)
221+
}
222+
223+
return testBinaryDataEncoder(m.Binary), nil
224+
}

0 commit comments

Comments
 (0)