Skip to content

Commit bceb1f8

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 df8cada commit bceb1f8

File tree

8 files changed

+584
-8
lines changed

8 files changed

+584
-8
lines changed

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

Lines changed: 21 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)))
@@ -83,6 +86,24 @@ func (m *integrationTesterModule) handleRequest(authReq *authRequest, r *Request
8386
} else {
8487
res.ActionArgs = append(res.ActionArgs, nil)
8588
}
89+
case pam.BinaryConvResponse:
90+
data, err := value.Decode(utils.TestBinaryDataDecoder)
91+
if err != nil {
92+
return nil, err
93+
}
94+
res.ActionArgs = append(res.ActionArgs,
95+
SerializableBinaryConvResponse{data})
96+
case *pam.BinaryConvResponse:
97+
if value != nil {
98+
data, err := value.Decode(utils.TestBinaryDataDecoder)
99+
if err != nil {
100+
return nil, err
101+
}
102+
res.ActionArgs = append(res.ActionArgs,
103+
SerializableBinaryConvResponse{data})
104+
} else {
105+
res.ActionArgs = append(res.ActionArgs, nil)
106+
}
86107
case pam.ReturnType:
87108
authReq.lastError = value
88109
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 {
@@ -1157,6 +1271,15 @@ func Test_Moduler_IntegrationTesterModule_Authenticate(t *testing.T) {
11571271
exp: []interface{}{nil, pam.SystemErr},
11581272
}},
11591273
},
1274+
"StartConv-Binary": {
1275+
expectedStatus: pam.SystemErr,
1276+
checkedRequests: []checkedRequest{{
1277+
r: NewRequest("StartConv", SerializableBinaryConvRequest{
1278+
[]byte{0x01, 0x02, 0x03, 0x05, 0x00, 0x99},
1279+
}),
1280+
exp: []interface{}{nil, pam.SystemErr},
1281+
}},
1282+
},
11601283
}
11611284

11621285
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
@@ -29,6 +29,14 @@ type SerializableStringConvResponse struct {
2929
Response string
3030
}
3131

32+
type SerializableBinaryConvRequest struct {
33+
Request []byte
34+
}
35+
36+
type SerializableBinaryConvResponse struct {
37+
Response []byte
38+
}
39+
3240
func init() {
3341
gob.Register(map[string]string{})
3442
gob.Register(Request{})
@@ -42,5 +50,9 @@ func init() {
4250
SerializableStringConvRequest{})
4351
gob.RegisterName("main.SerializableStringConvResponse",
4452
SerializableStringConvResponse{})
53+
gob.RegisterName("main.SerializableBinaryConvRequest",
54+
SerializableBinaryConvRequest{})
55+
gob.RegisterName("main.SerializableBinaryConvResponse",
56+
SerializableBinaryConvResponse{})
4557
gob.Register(utils.SerializableError{})
4658
}

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"
@@ -135,8 +137,11 @@ type mockConversationHandler struct {
135137
PromptEchoOff string
136138
TextInfo string
137139
ErrorMsg string
140+
Binary []byte
138141
ExpectedMessage string
139142
ExpectedMessagesByStyle map[Style]string
143+
ExpectedNil bool
144+
ExpectedBinary []byte
140145
CheckEmptyMessage bool
141146
ExpectedStyle Style
142147
CheckZeroStyle bool
@@ -179,3 +184,45 @@ func (c mockConversationHandler) RespondPAM(s Style, msg string) (string, error)
179184
return "", NewTransactionError(
180185
fmt.Errorf("unhandled style: %v", s), ConvErr)
181186
}
187+
188+
func testBinaryDataEncoder(bytes []byte) []byte {
189+
if len(bytes) > 0xff {
190+
panic("Binary transaction size not supported")
191+
}
192+
193+
if bytes == nil {
194+
return bytes
195+
}
196+
197+
data := make([]byte, 0, len(bytes)+1)
198+
data = append(data, byte(len(bytes)))
199+
data = append(data, bytes...)
200+
return data
201+
}
202+
203+
func testBinaryDataDecoder(ptr BinaryPointer) ([]byte, error) {
204+
if ptr == nil {
205+
return nil, nil
206+
}
207+
208+
length := uint8(*((*C.uint8_t)(ptr)))
209+
return C.GoBytes(unsafe.Pointer(ptr), C.int(length+1))[1:], nil
210+
}
211+
212+
func (m mockConversationHandler) RespondPAMBinary(ptr BinaryPointer) ([]byte, error) {
213+
if ptr == nil && !m.ExpectedNil {
214+
return nil, NewTransactionError(
215+
errors.New("unexpected null binary data"), ConvErr)
216+
} else if ptr == nil {
217+
return testBinaryDataEncoder(m.Binary), nil
218+
}
219+
220+
bytes, _ := testBinaryDataDecoder(ptr)
221+
if !reflect.DeepEqual(bytes, m.ExpectedBinary) {
222+
return nil, NewTransactionError(
223+
fmt.Errorf("data mismatch %v vs %v", bytes, m.ExpectedBinary),
224+
ConvErr)
225+
}
226+
227+
return testBinaryDataEncoder(m.Binary), nil
228+
}

0 commit comments

Comments
 (0)