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,