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
,
70 pam_handle_t
*pam_handle
)
76 struct pam_testcase
*tc
= NULL
;
77 bool call_pam_end
= true;
80 conv
.appdata_ptr
= conv_userdata
;
82 if (test_cases
== NULL
) {
83 return PAMTEST_ERR_INTERNAL
;
86 if (pam_handle
== NULL
) {
87 rv
= pam_start(service
, user
, &conv
, &ph
);
88 if (rv
!= PAM_SUCCESS
) {
89 return PAMTEST_ERR_START
;
95 for (tcindex
= 0; tcindex
< num_test_cases
; tcindex
++) {
96 tc
= &test_cases
[tcindex
];
98 rv
= run_test_case(ph
, tc
);
99 if (rv
== PAMTEST_ERR_KEEPHANDLE
) {
100 call_pam_end
= false;
102 } else if (rv
!= PAMTEST_ERR_OK
) {
103 return PAMTEST_ERR_INTERNAL
;
106 if (tc
->op_rv
!= tc
->expected_rv
) {
111 if (call_pam_end
== true && tc
!= NULL
) {
112 rv
= pam_end(ph
, tc
->op_rv
);
113 if (rv
!= PAM_SUCCESS
) {
114 return PAMTEST_ERR_END
;
118 if (tcindex
< num_test_cases
) {
119 return PAMTEST_ERR_CASE
;
122 return PAMTEST_ERR_OK
;
125 void pamtest_free_env(char **envlist
)
129 if (envlist
== NULL
) {
133 for (i
= 0; envlist
[i
] != NULL
; i
++) {
139 const struct pam_testcase
*
140 _pamtest_failed_case(struct pam_testcase
*test_cases
,
141 size_t num_test_cases
)
145 for (tcindex
= 0; tcindex
< num_test_cases
; tcindex
++) {
146 const struct pam_testcase
*tc
= &test_cases
[tcindex
];
148 if (tc
->expected_rv
!= tc
->op_rv
) {
157 const char *pamtest_strerror(enum pamtest_err perr
)
162 case PAMTEST_ERR_START
:
163 return "pam_start failed()";
164 case PAMTEST_ERR_CASE
:
165 return "Unexpected testcase result";
167 return "Could not run a test case";
168 case PAMTEST_ERR_END
:
169 return "pam_end failed()";
170 case PAMTEST_ERR_KEEPHANDLE
:
172 case PAMTEST_ERR_INTERNAL
:
173 return "Internal libpamtest error";
179 struct pamtest_conv_ctx
{
180 struct pamtest_conv_data
*data
;
188 static int add_to_reply(struct pam_response
*reply
, const char *str
)
192 len
= strlen(str
) + 1;
194 reply
->resp
= calloc(len
, sizeof(char));
195 if (reply
->resp
== NULL
) {
199 memcpy(reply
->resp
, str
, len
);
203 static void free_reply(struct pam_response
*reply
, int num_msg
)
211 for (i
= 0; i
< num_msg
; i
++) {
217 static int pamtest_simple_conv(int num_msg
,
218 const struct pam_message
**msgm
,
219 struct pam_response
**response
,
224 struct pam_response
*reply
= NULL
;
226 struct pamtest_conv_ctx
*cctx
= (struct pamtest_conv_ctx
*)appdata_ptr
;
233 reply
= (struct pam_response
*) calloc(num_msg
,
234 sizeof(struct pam_response
));
240 for (i
=0; i
< num_msg
; i
++) {
241 switch (msgm
[i
]->msg_style
) {
242 case PAM_PROMPT_ECHO_OFF
:
243 prompt
= (const char *) \
244 cctx
->data
->in_echo_off
[cctx
->echo_off_idx
];
247 if (prompt
!= NULL
) {
248 ret
= add_to_reply(&reply
[i
], prompt
);
249 if (ret
!= PAM_SUCCESS
) {
250 free_reply(reply
, num_msg
);
256 cctx
->echo_off_idx
++;
258 case PAM_PROMPT_ECHO_ON
:
259 prompt
= (const char *) \
260 cctx
->data
->in_echo_on
[cctx
->echo_on_idx
];
261 if (prompt
== NULL
) {
262 free_reply(reply
, num_msg
);
267 if (prompt
!= NULL
) {
268 ret
= add_to_reply(&reply
[i
], prompt
);
269 if (ret
!= PAM_SUCCESS
) {
270 free_reply(reply
, num_msg
);
280 ret
= add_to_reply(&reply
[i
], msgm
[i
]->msg
);
281 if (ret
!= PAM_SUCCESS
) {
282 free_reply(reply
, num_msg
);
287 if (cctx
->data
->out_err
!= NULL
) {
288 memcpy(cctx
->data
->out_err
[cctx
->err_idx
],
290 MIN(strlen(msgm
[i
]->msg
),
297 ret
= add_to_reply(&reply
[i
], msgm
[i
]->msg
);
298 if (ret
!= PAM_SUCCESS
) {
299 free_reply(reply
, num_msg
);
304 if (cctx
->data
->out_info
!= NULL
) {
305 memcpy(cctx
->data
->out_info
[cctx
->info_idx
],
307 MIN(strlen(msgm
[i
]->msg
),
317 if (response
!= NULL
) {
326 enum pamtest_err
_pamtest(const char *service
,
328 struct pamtest_conv_data
*conv_data
,
329 struct pam_testcase test_cases
[],
330 size_t num_test_cases
,
331 pam_handle_t
*pam_handle
)
333 struct pamtest_conv_ctx cctx
= {
337 return _pamtest_conv(service
, user
,