2 * Copyright (c) 2015 Andreas Schneider <asn@samba.org>
3 * Copyright (c) 2015 Jakub Hrozek <jakub.hrozek@posteo.se>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "libpamtest.h"
27 #define MIN(a,b) ((a) < (b) ? (a) : (b))
29 static enum pamtest_err
run_test_case(pam_handle_t
*ph
,
30 struct pam_testcase
*tc
)
32 switch (tc
->pam_operation
) {
33 case PAMTEST_AUTHENTICATE
:
34 tc
->op_rv
= pam_authenticate(ph
, tc
->flags
);
35 return PAMTEST_ERR_OK
;
37 tc
->op_rv
= pam_setcred(ph
, tc
->flags
);
38 return PAMTEST_ERR_OK
;
40 tc
->op_rv
= pam_acct_mgmt(ph
, tc
->flags
);
41 return PAMTEST_ERR_OK
;
42 case PAMTEST_OPEN_SESSION
:
43 tc
->op_rv
= pam_open_session(ph
, tc
->flags
);
44 return PAMTEST_ERR_OK
;
45 case PAMTEST_CLOSE_SESSION
:
46 tc
->op_rv
= pam_close_session(ph
, tc
->flags
);
47 return PAMTEST_ERR_OK
;
48 case PAMTEST_CHAUTHTOK
:
49 tc
->op_rv
= pam_chauthtok(ph
, tc
->flags
);
50 return PAMTEST_ERR_OK
;
51 case PAMTEST_GETENVLIST
:
52 tc
->case_out
.envlist
= pam_getenvlist(ph
);
53 return PAMTEST_ERR_OK
;
54 case PAMTEST_KEEPHANDLE
:
56 return PAMTEST_ERR_KEEPHANDLE
;
58 return PAMTEST_ERR_OP
;
61 return PAMTEST_ERR_OP
;
64 enum pamtest_err
_pamtest_conv(const char *service
,
68 struct pam_testcase test_cases
[],
69 size_t num_test_cases
)
75 struct pam_testcase
*tc
= NULL
;
76 bool call_pam_end
= true;
79 conv
.appdata_ptr
= conv_userdata
;
81 if (test_cases
== NULL
) {
82 return PAMTEST_ERR_INTERNAL
;
85 rv
= pam_start(service
, user
, &conv
, &ph
);
86 if (rv
!= PAM_SUCCESS
) {
87 return PAMTEST_ERR_START
;
90 for (tcindex
= 0; tcindex
< num_test_cases
; tcindex
++) {
91 tc
= &test_cases
[tcindex
];
93 rv
= run_test_case(ph
, tc
);
94 if (rv
== PAMTEST_ERR_KEEPHANDLE
) {
97 } else if (rv
!= PAMTEST_ERR_OK
) {
98 return PAMTEST_ERR_INTERNAL
;
101 if (tc
->op_rv
!= tc
->expected_rv
) {
106 if (call_pam_end
== true && tc
!= NULL
) {
107 rv
= pam_end(ph
, tc
->op_rv
);
108 if (rv
!= PAM_SUCCESS
) {
109 return PAMTEST_ERR_END
;
113 if (tcindex
< num_test_cases
) {
114 return PAMTEST_ERR_CASE
;
117 return PAMTEST_ERR_OK
;
120 void pamtest_free_env(char **envlist
)
124 if (envlist
== NULL
) {
128 for (i
= 0; envlist
[i
] != NULL
; i
++) {
134 const struct pam_testcase
*
135 _pamtest_failed_case(struct pam_testcase
*test_cases
,
136 size_t num_test_cases
)
140 for (tcindex
= 0; tcindex
< num_test_cases
; tcindex
++) {
141 const struct pam_testcase
*tc
= &test_cases
[tcindex
];
143 if (tc
->expected_rv
!= tc
->op_rv
) {
152 const char *pamtest_strerror(enum pamtest_err perr
)
157 case PAMTEST_ERR_START
:
158 return "pam_start failed()";
159 case PAMTEST_ERR_CASE
:
160 return "Unexpected testcase result";
162 return "Could not run a test case";
163 case PAMTEST_ERR_END
:
164 return "pam_end failed()";
165 case PAMTEST_ERR_KEEPHANDLE
:
167 case PAMTEST_ERR_INTERNAL
:
168 return "Internal libpamtest error";
174 struct pamtest_conv_ctx
{
175 struct pamtest_conv_data
*data
;
183 static int add_to_reply(struct pam_response
*reply
, const char *str
)
187 len
= strlen(str
) + 1;
189 reply
->resp
= calloc(len
, sizeof(char));
190 if (reply
->resp
== NULL
) {
194 memcpy(reply
->resp
, str
, len
);
198 static void free_reply(struct pam_response
*reply
, int num_msg
)
206 for (i
= 0; i
< num_msg
; i
++) {
212 static int pamtest_simple_conv(int num_msg
,
213 const struct pam_message
**msgm
,
214 struct pam_response
**response
,
219 struct pam_response
*reply
= NULL
;
221 struct pamtest_conv_ctx
*cctx
= (struct pamtest_conv_ctx
*)appdata_ptr
;
228 reply
= (struct pam_response
*) calloc(num_msg
,
229 sizeof(struct pam_response
));
235 for (i
=0; i
< num_msg
; i
++) {
236 switch (msgm
[i
]->msg_style
) {
237 case PAM_PROMPT_ECHO_OFF
:
238 prompt
= (const char *) \
239 cctx
->data
->in_echo_off
[cctx
->echo_off_idx
];
242 if (prompt
!= NULL
) {
243 ret
= add_to_reply(&reply
[i
], prompt
);
244 if (ret
!= PAM_SUCCESS
) {
245 free_reply(reply
, num_msg
);
251 cctx
->echo_off_idx
++;
253 case PAM_PROMPT_ECHO_ON
:
254 prompt
= (const char *) \
255 cctx
->data
->in_echo_on
[cctx
->echo_on_idx
];
256 if (prompt
== NULL
) {
257 free_reply(reply
, num_msg
);
262 if (prompt
!= NULL
) {
263 ret
= add_to_reply(&reply
[i
], prompt
);
264 if (ret
!= PAM_SUCCESS
) {
265 free_reply(reply
, num_msg
);
275 ret
= add_to_reply(&reply
[i
], msgm
[i
]->msg
);
276 if (ret
!= PAM_SUCCESS
) {
277 free_reply(reply
, num_msg
);
282 if (cctx
->data
->out_err
!= NULL
) {
283 memcpy(cctx
->data
->out_err
[cctx
->err_idx
],
285 MIN(strlen(msgm
[i
]->msg
),
292 ret
= add_to_reply(&reply
[i
], msgm
[i
]->msg
);
293 if (ret
!= PAM_SUCCESS
) {
294 free_reply(reply
, num_msg
);
299 if (cctx
->data
->out_info
!= NULL
) {
300 memcpy(cctx
->data
->out_info
[cctx
->info_idx
],
302 MIN(strlen(msgm
[i
]->msg
),
312 if (response
!= NULL
) {
321 enum pamtest_err
_pamtest(const char *service
,
323 struct pamtest_conv_data
*conv_data
,
324 struct pam_testcase test_cases
[],
325 size_t num_test_cases
)
327 struct pamtest_conv_ctx cctx
= {
331 return _pamtest_conv(service
, user
,