wscript: separate embedded_heimdal from system_heimdal
[Samba.git] / third_party / pam_wrapper / libpamtest.c
blob4474736d688bd0fe9652cac1ba909fbdf05f502e
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)
71 int rv;
72 pam_handle_t *ph;
73 struct pam_conv conv;
74 size_t tcindex;
75 struct pam_testcase *tc = NULL;
76 bool call_pam_end = true;
78 conv.conv = conv_fn;
79 conv.appdata_ptr = conv_userdata;
81 if (test_cases == NULL) {
82 return PAMTEST_ERR_INTERNAL;
85 rv = pam_start(service, user, &conv, &ph);
86 if (rv != PAM_SUCCESS) {
87 return PAMTEST_ERR_START;
90 for (tcindex = 0; tcindex < num_test_cases; tcindex++) {
91 tc = &test_cases[tcindex];
93 rv = run_test_case(ph, tc);
94 if (rv == PAMTEST_ERR_KEEPHANDLE) {
95 call_pam_end = false;
96 continue;
97 } else if (rv != PAMTEST_ERR_OK) {
98 return PAMTEST_ERR_INTERNAL;
101 if (tc->op_rv != tc->expected_rv) {
102 break;
106 if (call_pam_end == true && tc != NULL) {
107 rv = pam_end(ph, tc->op_rv);
108 if (rv != PAM_SUCCESS) {
109 return PAMTEST_ERR_END;
113 if (tcindex < num_test_cases) {
114 return PAMTEST_ERR_CASE;
117 return PAMTEST_ERR_OK;
120 void pamtest_free_env(char **envlist)
122 size_t i;
124 if (envlist == NULL) {
125 return;
128 for (i = 0; envlist[i] != NULL; i++) {
129 free(envlist[i]);
131 free(envlist);
134 const struct pam_testcase *
135 _pamtest_failed_case(struct pam_testcase *test_cases,
136 size_t num_test_cases)
138 size_t tcindex;
140 for (tcindex = 0; tcindex < num_test_cases; tcindex++) {
141 const struct pam_testcase *tc = &test_cases[tcindex];
143 if (tc->expected_rv != tc->op_rv) {
144 return tc;
148 /* Nothing failed */
149 return NULL;
152 const char *pamtest_strerror(enum pamtest_err perr)
154 switch (perr) {
155 case PAMTEST_ERR_OK:
156 return "Success";
157 case PAMTEST_ERR_START:
158 return "pam_start failed()";
159 case PAMTEST_ERR_CASE:
160 return "Unexpected testcase result";
161 case PAMTEST_ERR_OP:
162 return "Could not run a test case";
163 case PAMTEST_ERR_END:
164 return "pam_end failed()";
165 case PAMTEST_ERR_KEEPHANDLE:
166 /* Fallthrough */
167 case PAMTEST_ERR_INTERNAL:
168 return "Internal libpamtest error";
171 return "Unknown";
174 struct pamtest_conv_ctx {
175 struct pamtest_conv_data *data;
177 size_t echo_off_idx;
178 size_t echo_on_idx;
179 size_t err_idx;
180 size_t info_idx;
183 static int add_to_reply(struct pam_response *reply, const char *str)
185 size_t len;
187 len = strlen(str) + 1;
189 reply->resp = calloc(len, sizeof(char));
190 if (reply->resp == NULL) {
191 return PAM_BUF_ERR;
194 memcpy(reply->resp, str, len);
195 return PAM_SUCCESS;
198 static void free_reply(struct pam_response *reply, int num_msg)
200 int i;
202 if (reply == NULL) {
203 return;
206 for (i = 0; i < num_msg; i++) {
207 free(reply[i].resp);
209 free(reply);
212 static int pamtest_simple_conv(int num_msg,
213 const struct pam_message **msgm,
214 struct pam_response **response,
215 void *appdata_ptr)
217 int i = 0;
218 int ret;
219 struct pam_response *reply = NULL;
220 const char *prompt;
221 struct pamtest_conv_ctx *cctx = (struct pamtest_conv_ctx *)appdata_ptr;
223 if (cctx == NULL) {
224 return PAM_CONV_ERR;
227 if (response) {
228 reply = (struct pam_response *) calloc(num_msg,
229 sizeof(struct pam_response));
230 if (reply == NULL) {
231 return PAM_CONV_ERR;
235 for (i=0; i < num_msg; i++) {
236 switch (msgm[i]->msg_style) {
237 case PAM_PROMPT_ECHO_OFF:
238 prompt = (const char *) \
239 cctx->data->in_echo_off[cctx->echo_off_idx];
241 if (reply != NULL) {
242 if (prompt != NULL) {
243 ret = add_to_reply(&reply[i], prompt);
244 if (ret != PAM_SUCCESS) {
245 free_reply(reply, num_msg);
246 return ret;
251 cctx->echo_off_idx++;
252 break;
253 case PAM_PROMPT_ECHO_ON:
254 prompt = (const char *) \
255 cctx->data->in_echo_on[cctx->echo_on_idx];
256 if (prompt == NULL) {
257 free_reply(reply, num_msg);
258 return PAM_CONV_ERR;
261 if (reply != NULL) {
262 if (prompt != NULL) {
263 ret = add_to_reply(&reply[i], prompt);
264 if (ret != PAM_SUCCESS) {
265 free_reply(reply, num_msg);
266 return ret;
271 cctx->echo_on_idx++;
272 break;
273 case PAM_ERROR_MSG:
274 if (reply != NULL) {
275 ret = add_to_reply(&reply[i], msgm[i]->msg);
276 if (ret != PAM_SUCCESS) {
277 free_reply(reply, num_msg);
278 return ret;
282 if (cctx->data->out_err != NULL) {
283 memcpy(cctx->data->out_err[cctx->err_idx],
284 msgm[i]->msg,
285 MIN(strlen(msgm[i]->msg),
286 PAM_MAX_MSG_SIZE));
287 cctx->err_idx++;
289 break;
290 case PAM_TEXT_INFO:
291 if (reply != NULL) {
292 ret = add_to_reply(&reply[i], msgm[i]->msg);
293 if (ret != PAM_SUCCESS) {
294 free_reply(reply, num_msg);
295 return ret;
299 if (cctx->data->out_info != NULL) {
300 memcpy(cctx->data->out_info[cctx->info_idx],
301 msgm[i]->msg,
302 MIN(strlen(msgm[i]->msg),
303 PAM_MAX_MSG_SIZE));
304 cctx->info_idx++;
306 break;
307 default:
308 continue;
312 if (response != NULL) {
313 *response = reply;
314 } else {
315 free(reply);
318 return PAM_SUCCESS;
321 enum pamtest_err _pamtest(const char *service,
322 const char *user,
323 struct pamtest_conv_data *conv_data,
324 struct pam_testcase test_cases[],
325 size_t num_test_cases)
327 struct pamtest_conv_ctx cctx = {
328 .data = conv_data,
331 return _pamtest_conv(service, user,
332 pamtest_simple_conv,
333 &cctx,
334 test_cases,
335 num_test_cases);