Line data Source code
1 : /*
2 : * Copyright (c) 2015 Andreas Schneider <asn@samba.org>
3 : * Copyright (c) 2015 Jakub Hrozek <jakub.hrozek@posteo.se>
4 : *
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.
9 : *
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.
14 : *
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/>.
17 : */
18 :
19 : #include "config.h"
20 :
21 : #include <stdlib.h>
22 : #include <string.h>
23 : #include <stdbool.h>
24 :
25 : #include "libpamtest.h"
26 :
27 : #define MIN(a,b) ((a) < (b) ? (a) : (b))
28 :
29 256 : static enum pamtest_err run_test_case(pam_handle_t *ph,
30 : struct pam_testcase *tc)
31 : {
32 256 : switch (tc->pam_operation) {
33 238 : case PAMTEST_AUTHENTICATE:
34 238 : tc->op_rv = pam_authenticate(ph, tc->flags);
35 238 : return PAMTEST_ERR_OK;
36 2 : case PAMTEST_SETCRED:
37 2 : tc->op_rv = pam_setcred(ph, tc->flags);
38 2 : return PAMTEST_ERR_OK;
39 0 : case PAMTEST_ACCOUNT:
40 0 : tc->op_rv = pam_acct_mgmt(ph, tc->flags);
41 0 : return PAMTEST_ERR_OK;
42 0 : case PAMTEST_OPEN_SESSION:
43 0 : tc->op_rv = pam_open_session(ph, tc->flags);
44 0 : return PAMTEST_ERR_OK;
45 0 : case PAMTEST_CLOSE_SESSION:
46 0 : tc->op_rv = pam_close_session(ph, tc->flags);
47 0 : return PAMTEST_ERR_OK;
48 12 : case PAMTEST_CHAUTHTOK:
49 12 : tc->op_rv = pam_chauthtok(ph, tc->flags);
50 12 : return PAMTEST_ERR_OK;
51 2 : case PAMTEST_GETENVLIST:
52 2 : tc->case_out.envlist = pam_getenvlist(ph);
53 2 : return PAMTEST_ERR_OK;
54 2 : case PAMTEST_KEEPHANDLE:
55 2 : tc->case_out.ph = ph;
56 2 : return PAMTEST_ERR_KEEPHANDLE;
57 0 : default:
58 0 : return PAMTEST_ERR_OP;
59 : }
60 :
61 : return PAMTEST_ERR_OP;
62 : }
63 :
64 252 : enum pamtest_err _pamtest_conv(const char *service,
65 : const char *user,
66 : pam_conv_fn conv_fn,
67 : void *conv_userdata,
68 : struct pam_testcase test_cases[],
69 : size_t num_test_cases,
70 : pam_handle_t *pam_handle)
71 : {
72 : int rv;
73 : pam_handle_t *ph;
74 : struct pam_conv conv;
75 : size_t tcindex;
76 252 : struct pam_testcase *tc = NULL;
77 252 : bool call_pam_end = true;
78 :
79 252 : conv.conv = conv_fn;
80 252 : conv.appdata_ptr = conv_userdata;
81 :
82 252 : if (test_cases == NULL) {
83 0 : return PAMTEST_ERR_INTERNAL;
84 : }
85 :
86 252 : if (pam_handle == NULL) {
87 250 : rv = pam_start(service, user, &conv, &ph);
88 250 : if (rv != PAM_SUCCESS) {
89 0 : return PAMTEST_ERR_START;
90 : }
91 : } else {
92 2 : ph = pam_handle;
93 : }
94 :
95 508 : for (tcindex = 0; tcindex < num_test_cases; tcindex++) {
96 256 : tc = &test_cases[tcindex];
97 :
98 256 : rv = run_test_case(ph, tc);
99 256 : if (rv == PAMTEST_ERR_KEEPHANDLE) {
100 2 : call_pam_end = false;
101 2 : continue;
102 254 : } else if (rv != PAMTEST_ERR_OK) {
103 0 : return PAMTEST_ERR_INTERNAL;
104 : }
105 :
106 254 : if (tc->op_rv != tc->expected_rv) {
107 0 : break;
108 : }
109 : }
110 :
111 252 : if (call_pam_end == true && tc != NULL) {
112 250 : rv = pam_end(ph, tc->op_rv);
113 250 : if (rv != PAM_SUCCESS) {
114 0 : return PAMTEST_ERR_END;
115 : }
116 : }
117 :
118 252 : if (tcindex < num_test_cases) {
119 0 : return PAMTEST_ERR_CASE;
120 : }
121 :
122 252 : return PAMTEST_ERR_OK;
123 : }
124 :
125 0 : void pamtest_free_env(char **envlist)
126 : {
127 : size_t i;
128 :
129 0 : if (envlist == NULL) {
130 0 : return;
131 : }
132 :
133 0 : for (i = 0; envlist[i] != NULL; i++) {
134 0 : free(envlist[i]);
135 : }
136 0 : free(envlist);
137 : }
138 :
139 : const struct pam_testcase *
140 0 : _pamtest_failed_case(struct pam_testcase *test_cases,
141 : size_t num_test_cases)
142 : {
143 : size_t tcindex;
144 :
145 0 : for (tcindex = 0; tcindex < num_test_cases; tcindex++) {
146 0 : const struct pam_testcase *tc = &test_cases[tcindex];
147 :
148 0 : if (tc->expected_rv != tc->op_rv) {
149 0 : return tc;
150 : }
151 : }
152 :
153 : /* Nothing failed */
154 0 : return NULL;
155 : }
156 :
157 0 : const char *pamtest_strerror(enum pamtest_err perr)
158 : {
159 0 : switch (perr) {
160 0 : case PAMTEST_ERR_OK:
161 0 : return "Success";
162 0 : case PAMTEST_ERR_START:
163 0 : return "pam_start failed()";
164 0 : case PAMTEST_ERR_CASE:
165 0 : return "Unexpected testcase result";
166 0 : case PAMTEST_ERR_OP:
167 0 : return "Could not run a test case";
168 0 : case PAMTEST_ERR_END:
169 0 : return "pam_end failed()";
170 0 : case PAMTEST_ERR_KEEPHANDLE:
171 : /* Fallthrough */
172 : case PAMTEST_ERR_INTERNAL:
173 0 : return "Internal libpamtest error";
174 : }
175 :
176 0 : return "Unknown";
177 : }
178 :
179 : struct pamtest_conv_ctx {
180 : struct pamtest_conv_data *data;
181 :
182 : size_t echo_off_idx;
183 : size_t echo_on_idx;
184 : size_t err_idx;
185 : size_t info_idx;
186 : };
187 :
188 326 : static int add_to_reply(struct pam_response *reply, const char *str)
189 : {
190 : size_t len;
191 :
192 326 : len = strlen(str) + 1;
193 :
194 326 : reply->resp = calloc(len, sizeof(char));
195 326 : if (reply->resp == NULL) {
196 0 : return PAM_BUF_ERR;
197 : }
198 :
199 326 : memcpy(reply->resp, str, len);
200 326 : return PAM_SUCCESS;
201 : }
202 :
203 0 : static void free_reply(struct pam_response *reply, int num_msg)
204 : {
205 : int i;
206 :
207 0 : if (reply == NULL) {
208 0 : return;
209 : }
210 :
211 0 : for (i = 0; i < num_msg; i++) {
212 0 : free(reply[i].resp);
213 : }
214 0 : free(reply);
215 : }
216 :
217 310 : static int pamtest_simple_conv(int num_msg,
218 : const struct pam_message **msgm,
219 : struct pam_response **response,
220 : void *appdata_ptr)
221 : {
222 310 : int i = 0;
223 : int ret;
224 310 : struct pam_response *reply = NULL;
225 : const char *prompt;
226 310 : struct pamtest_conv_ctx *cctx = (struct pamtest_conv_ctx *)appdata_ptr;
227 :
228 310 : if (cctx == NULL) {
229 0 : return PAM_CONV_ERR;
230 : }
231 :
232 310 : if (response) {
233 310 : reply = (struct pam_response *) calloc(num_msg,
234 : sizeof(struct pam_response));
235 310 : if (reply == NULL) {
236 0 : return PAM_CONV_ERR;
237 : }
238 : }
239 :
240 636 : for (i=0; i < num_msg; i++) {
241 326 : switch (msgm[i]->msg_style) {
242 258 : case PAM_PROMPT_ECHO_OFF:
243 258 : prompt = (const char *) \
244 258 : cctx->data->in_echo_off[cctx->echo_off_idx];
245 :
246 258 : if (reply != NULL) {
247 258 : if (prompt != NULL) {
248 258 : ret = add_to_reply(&reply[i], prompt);
249 258 : if (ret != PAM_SUCCESS) {
250 0 : free_reply(reply, num_msg);
251 0 : return ret;
252 : }
253 : }
254 : }
255 :
256 258 : cctx->echo_off_idx++;
257 258 : break;
258 0 : case PAM_PROMPT_ECHO_ON:
259 0 : prompt = (const char *) \
260 0 : cctx->data->in_echo_on[cctx->echo_on_idx];
261 0 : if (prompt == NULL) {
262 0 : free_reply(reply, num_msg);
263 0 : return PAM_CONV_ERR;
264 : }
265 :
266 0 : if (reply != NULL) {
267 0 : if (prompt != NULL) {
268 0 : ret = add_to_reply(&reply[i], prompt);
269 0 : if (ret != PAM_SUCCESS) {
270 0 : free_reply(reply, num_msg);
271 0 : return ret;
272 : }
273 : }
274 : }
275 :
276 0 : cctx->echo_on_idx++;
277 0 : break;
278 52 : case PAM_ERROR_MSG:
279 52 : if (reply != NULL) {
280 52 : ret = add_to_reply(&reply[i], msgm[i]->msg);
281 52 : if (ret != PAM_SUCCESS) {
282 0 : free_reply(reply, num_msg);
283 0 : return ret;
284 : }
285 : }
286 :
287 52 : if (cctx->data->out_err != NULL) {
288 0 : memcpy(cctx->data->out_err[cctx->err_idx],
289 52 : msgm[i]->msg,
290 52 : MIN(strlen(msgm[i]->msg),
291 : PAM_MAX_MSG_SIZE));
292 52 : cctx->err_idx++;
293 : }
294 52 : break;
295 16 : case PAM_TEXT_INFO:
296 16 : if (reply != NULL) {
297 16 : ret = add_to_reply(&reply[i], msgm[i]->msg);
298 16 : if (ret != PAM_SUCCESS) {
299 0 : free_reply(reply, num_msg);
300 0 : return ret;
301 : }
302 : }
303 :
304 16 : if (cctx->data->out_info != NULL) {
305 0 : memcpy(cctx->data->out_info[cctx->info_idx],
306 16 : msgm[i]->msg,
307 16 : MIN(strlen(msgm[i]->msg),
308 : PAM_MAX_MSG_SIZE));
309 16 : cctx->info_idx++;
310 : }
311 16 : break;
312 0 : default:
313 0 : continue;
314 : }
315 : }
316 :
317 310 : if (response != NULL) {
318 310 : *response = reply;
319 : } else {
320 0 : free(reply);
321 : }
322 :
323 310 : return PAM_SUCCESS;
324 : }
325 :
326 252 : enum pamtest_err _pamtest(const char *service,
327 : const char *user,
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)
332 : {
333 252 : struct pamtest_conv_ctx cctx = {
334 : .data = conv_data,
335 : };
336 :
337 252 : return _pamtest_conv(service, user,
338 : pamtest_simple_conv,
339 : &cctx,
340 : test_cases,
341 : num_test_cases,
342 : pam_handle);
343 : }
|