passdb: Use getline(3) to read our old machine sid
[Samba.git] / third_party / pam_wrapper / libpamtest.c
blob6033d5a5b7e92b292459cadef1d4436a42c84093
1 /*
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/>.
19 #include "config.h"
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdbool.h>
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;
36 case PAMTEST_SETCRED:
37 tc->op_rv = pam_setcred(ph, tc->flags);
38 return PAMTEST_ERR_OK;
39 case PAMTEST_ACCOUNT:
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:
55 tc->case_out.ph = ph;
56 return PAMTEST_ERR_KEEPHANDLE;
57 default:
58 return PAMTEST_ERR_OP;
61 return PAMTEST_ERR_OP;
64 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)
72 int rv;
73 pam_handle_t *ph;
74 struct pam_conv conv;
75 size_t tcindex;
76 struct pam_testcase *tc = NULL;
77 bool call_pam_end = true;
79 conv.conv = conv_fn;
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;
91 } else {
92 ph = pam_handle;
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;
101 continue;
102 } else if (rv != PAMTEST_ERR_OK) {
103 return PAMTEST_ERR_INTERNAL;
106 if (tc->op_rv != tc->expected_rv) {
107 break;
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)
127 size_t i;
129 if (envlist == NULL) {
130 return;
133 for (i = 0; envlist[i] != NULL; i++) {
134 free(envlist[i]);
136 free(envlist);
139 const struct pam_testcase *
140 _pamtest_failed_case(struct pam_testcase *test_cases,
141 size_t num_test_cases)
143 size_t tcindex;
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) {
149 return tc;
153 /* Nothing failed */
154 return NULL;
157 const char *pamtest_strerror(enum pamtest_err perr)
159 switch (perr) {
160 case PAMTEST_ERR_OK:
161 return "Success";
162 case PAMTEST_ERR_START:
163 return "pam_start failed()";
164 case PAMTEST_ERR_CASE:
165 return "Unexpected testcase result";
166 case PAMTEST_ERR_OP:
167 return "Could not run a test case";
168 case PAMTEST_ERR_END:
169 return "pam_end failed()";
170 case PAMTEST_ERR_KEEPHANDLE:
171 /* Fallthrough */
172 case PAMTEST_ERR_INTERNAL:
173 return "Internal libpamtest error";
176 return "Unknown";
179 struct pamtest_conv_ctx {
180 struct pamtest_conv_data *data;
182 size_t echo_off_idx;
183 size_t echo_on_idx;
184 size_t err_idx;
185 size_t info_idx;
188 static int add_to_reply(struct pam_response *reply, const char *str)
190 size_t len;
192 len = strlen(str) + 1;
194 reply->resp = calloc(len, sizeof(char));
195 if (reply->resp == NULL) {
196 return PAM_BUF_ERR;
199 memcpy(reply->resp, str, len);
200 return PAM_SUCCESS;
203 static void free_reply(struct pam_response *reply, int num_msg)
205 int i;
207 if (reply == NULL) {
208 return;
211 for (i = 0; i < num_msg; i++) {
212 free(reply[i].resp);
214 free(reply);
217 static int pamtest_simple_conv(int num_msg,
218 const struct pam_message **msgm,
219 struct pam_response **response,
220 void *appdata_ptr)
222 int i = 0;
223 int ret;
224 struct pam_response *reply = NULL;
225 const char *prompt;
226 struct pamtest_conv_ctx *cctx = (struct pamtest_conv_ctx *)appdata_ptr;
228 if (cctx == NULL) {
229 return PAM_CONV_ERR;
232 if (response) {
233 reply = (struct pam_response *) calloc(num_msg,
234 sizeof(struct pam_response));
235 if (reply == NULL) {
236 return PAM_CONV_ERR;
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];
246 if (reply != NULL) {
247 if (prompt != NULL) {
248 ret = add_to_reply(&reply[i], prompt);
249 if (ret != PAM_SUCCESS) {
250 free_reply(reply, num_msg);
251 return ret;
256 cctx->echo_off_idx++;
257 break;
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);
263 return PAM_CONV_ERR;
266 if (reply != NULL) {
267 if (prompt != NULL) {
268 ret = add_to_reply(&reply[i], prompt);
269 if (ret != PAM_SUCCESS) {
270 free_reply(reply, num_msg);
271 return ret;
276 cctx->echo_on_idx++;
277 break;
278 case PAM_ERROR_MSG:
279 if (reply != NULL) {
280 ret = add_to_reply(&reply[i], msgm[i]->msg);
281 if (ret != PAM_SUCCESS) {
282 free_reply(reply, num_msg);
283 return ret;
287 if (cctx->data->out_err != NULL) {
288 memcpy(cctx->data->out_err[cctx->err_idx],
289 msgm[i]->msg,
290 MIN(strlen(msgm[i]->msg),
291 PAM_MAX_MSG_SIZE));
292 cctx->err_idx++;
294 break;
295 case PAM_TEXT_INFO:
296 if (reply != NULL) {
297 ret = add_to_reply(&reply[i], msgm[i]->msg);
298 if (ret != PAM_SUCCESS) {
299 free_reply(reply, num_msg);
300 return ret;
304 if (cctx->data->out_info != NULL) {
305 memcpy(cctx->data->out_info[cctx->info_idx],
306 msgm[i]->msg,
307 MIN(strlen(msgm[i]->msg),
308 PAM_MAX_MSG_SIZE));
309 cctx->info_idx++;
311 break;
312 default:
313 continue;
317 if (response != NULL) {
318 *response = reply;
319 } else {
320 free(reply);
323 return PAM_SUCCESS;
326 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)
333 struct pamtest_conv_ctx cctx = {
334 .data = conv_data,
337 return _pamtest_conv(service, user,
338 pamtest_simple_conv,
339 &cctx,
340 test_cases,
341 num_test_cases,
342 pam_handle);