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/>.
23 #include "libpamtest.h"
25 #define MIN(a,b) ((a) < (b) ? (a) : (b))
27 static enum pamtest_err
run_test_case(pam_handle_t
*ph
,
28 struct pam_testcase
*tc
)
30 switch (tc
->pam_operation
) {
31 case PAMTEST_AUTHENTICATE
:
32 tc
->op_rv
= pam_authenticate(ph
, tc
->flags
);
33 return PAMTEST_ERR_OK
;
35 tc
->op_rv
= pam_setcred(ph
, tc
->flags
);
36 return PAMTEST_ERR_OK
;
38 tc
->op_rv
= pam_acct_mgmt(ph
, tc
->flags
);
39 return PAMTEST_ERR_OK
;
40 case PAMTEST_OPEN_SESSION
:
41 tc
->op_rv
= pam_open_session(ph
, tc
->flags
);
42 return PAMTEST_ERR_OK
;
43 case PAMTEST_CLOSE_SESSION
:
44 tc
->op_rv
= pam_close_session(ph
, tc
->flags
);
45 return PAMTEST_ERR_OK
;
46 case PAMTEST_CHAUTHTOK
:
47 tc
->op_rv
= pam_chauthtok(ph
, tc
->flags
);
48 return PAMTEST_ERR_OK
;
49 case PAMTEST_GETENVLIST
:
50 tc
->case_out
.envlist
= pam_getenvlist(ph
);
51 return PAMTEST_ERR_OK
;
52 case PAMTEST_KEEPHANDLE
:
54 return PAMTEST_ERR_KEEPHANDLE
;
56 return PAMTEST_ERR_OP
;
59 return PAMTEST_ERR_OP
;
62 enum pamtest_err
_pamtest_conv(const char *service
,
66 struct pam_testcase test_cases
[],
67 size_t num_test_cases
)
73 struct pam_testcase
*tc
= NULL
;
74 bool call_pam_end
= true;
77 conv
.appdata_ptr
= conv_userdata
;
79 if (test_cases
== NULL
) {
80 return PAMTEST_ERR_INTERNAL
;
83 rv
= pam_start(service
, user
, &conv
, &ph
);
84 if (rv
!= PAM_SUCCESS
) {
85 return PAMTEST_ERR_START
;
88 for (tcindex
= 0; tcindex
< num_test_cases
; tcindex
++) {
89 tc
= &test_cases
[tcindex
];
91 rv
= run_test_case(ph
, tc
);
92 if (rv
== PAMTEST_ERR_KEEPHANDLE
) {
95 } else if (rv
!= PAMTEST_ERR_OK
) {
96 return PAMTEST_ERR_INTERNAL
;
99 if (tc
->op_rv
!= tc
->expected_rv
) {
104 if (call_pam_end
== true && tc
!= NULL
) {
105 rv
= pam_end(ph
, tc
->op_rv
);
106 if (rv
!= PAM_SUCCESS
) {
107 return PAMTEST_ERR_END
;
111 if (tcindex
< num_test_cases
) {
112 return PAMTEST_ERR_CASE
;
115 return PAMTEST_ERR_OK
;
118 void pamtest_free_env(char **envlist
)
122 if (envlist
== NULL
) {
126 for (i
= 0; envlist
[i
] != NULL
; i
++) {
132 const struct pam_testcase
*
133 _pamtest_failed_case(struct pam_testcase
*test_cases
,
134 size_t num_test_cases
)
138 for (tcindex
= 0; tcindex
< num_test_cases
; tcindex
++) {
139 const struct pam_testcase
*tc
= &test_cases
[tcindex
];
141 if (tc
->expected_rv
!= tc
->op_rv
) {
150 const char *pamtest_strerror(enum pamtest_err perr
)
155 case PAMTEST_ERR_START
:
156 return "pam_start failed()";
157 case PAMTEST_ERR_CASE
:
158 return "Unexpected testcase result";
160 return "Could not run a test case";
161 case PAMTEST_ERR_END
:
162 return "pam_end failed()";
163 case PAMTEST_ERR_KEEPHANDLE
:
165 case PAMTEST_ERR_INTERNAL
:
166 return "Internal libpamtest error";
172 struct pamtest_conv_ctx
{
173 struct pamtest_conv_data
*data
;
181 static int add_to_reply(struct pam_response
*reply
, const char *str
)
185 len
= strlen(str
) + 1;
187 reply
->resp
= calloc(len
, sizeof(char));
188 if (reply
->resp
== NULL
) {
192 memcpy(reply
->resp
, str
, len
);
196 static void free_reply(struct pam_response
*reply
, int num_msg
)
204 for (i
= 0; i
< num_msg
; i
++) {
210 static int pamtest_simple_conv(int num_msg
,
211 const struct pam_message
**msgm
,
212 struct pam_response
**response
,
217 struct pam_response
*reply
= NULL
;
219 struct pamtest_conv_ctx
*cctx
= \
220 (struct pamtest_conv_ctx
*) appdata_ptr
;
227 reply
= (struct pam_response
*) calloc(num_msg
,
228 sizeof(struct pam_response
));
234 for (i
=0; i
< num_msg
; i
++) {
235 switch (msgm
[i
]->msg_style
) {
236 case PAM_PROMPT_ECHO_OFF
:
237 prompt
= (const char *) \
238 cctx
->data
->in_echo_off
[cctx
->echo_off_idx
];
241 if (prompt
!= NULL
) {
242 ret
= add_to_reply(&reply
[ri
], prompt
);
243 if (ret
!= PAM_SUCCESS
) {
244 free_reply(reply
, num_msg
);
248 reply
[ri
].resp
= NULL
;
253 cctx
->echo_off_idx
++;
255 case PAM_PROMPT_ECHO_ON
:
256 prompt
= (const char *) \
257 cctx
->data
->in_echo_on
[cctx
->echo_on_idx
];
258 if (prompt
== NULL
) {
259 free_reply(reply
, num_msg
);
264 if (prompt
!= NULL
) {
265 ret
= add_to_reply(&reply
[ri
], prompt
);
266 if (ret
!= PAM_SUCCESS
) {
267 free_reply(reply
, num_msg
);
277 if (cctx
->data
->out_err
!= NULL
) {
278 memcpy(cctx
->data
->out_err
[cctx
->err_idx
],
280 MIN(strlen(msgm
[i
]->msg
),
286 if (cctx
->data
->out_info
!= NULL
) {
287 memcpy(cctx
->data
->out_info
[cctx
->info_idx
],
289 MIN(strlen(msgm
[i
]->msg
),
299 if (response
&& ri
> 0) {
308 enum pamtest_err
_pamtest(const char *service
,
310 struct pamtest_conv_data
*conv_data
,
311 struct pam_testcase test_cases
[],
312 size_t num_test_cases
)
314 struct pamtest_conv_ctx cctx
= {
318 return _pamtest_conv(service
, user
,