pdb_samba_dsdb: implement PDB_CAP_TRUSTED_DOMAINS_EX related functions
[Samba.git] / third_party / pam_wrapper / libpamtest.c
blobc0ab41dac8ec88ae222beebcc744da9e26f6efe2
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 <stdlib.h>
20 #include <string.h>
21 #include <stdbool.h>
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;
34 case PAMTEST_SETCRED:
35 tc->op_rv = pam_setcred(ph, tc->flags);
36 return PAMTEST_ERR_OK;
37 case PAMTEST_ACCOUNT:
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:
53 tc->case_out.ph = ph;
54 return PAMTEST_ERR_KEEPHANDLE;
55 default:
56 return PAMTEST_ERR_OP;
59 return PAMTEST_ERR_OP;
62 enum pamtest_err _pamtest_conv(const char *service,
63 const char *user,
64 pam_conv_fn conv_fn,
65 void *conv_userdata,
66 struct pam_testcase test_cases[],
67 size_t num_test_cases)
69 int rv;
70 pam_handle_t *ph;
71 struct pam_conv conv;
72 size_t tcindex;
73 struct pam_testcase *tc = NULL;
74 bool call_pam_end = true;
76 conv.conv = conv_fn;
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) {
93 call_pam_end = false;
94 continue;
95 } else if (rv != PAMTEST_ERR_OK) {
96 return PAMTEST_ERR_INTERNAL;
99 if (tc->op_rv != tc->expected_rv) {
100 break;
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)
120 size_t i;
122 if (envlist == NULL) {
123 return;
126 for (i = 0; envlist[i] != NULL; i++) {
127 free(envlist[i]);
129 free(envlist);
132 const struct pam_testcase *
133 _pamtest_failed_case(struct pam_testcase *test_cases,
134 size_t num_test_cases)
136 size_t tcindex;
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) {
142 return tc;
146 /* Nothing failed */
147 return NULL;
150 const char *pamtest_strerror(enum pamtest_err perr)
152 switch (perr) {
153 case PAMTEST_ERR_OK:
154 return "Success";
155 case PAMTEST_ERR_START:
156 return "pam_start failed()";
157 case PAMTEST_ERR_CASE:
158 return "Unexpected testcase result";
159 case PAMTEST_ERR_OP:
160 return "Could not run a test case";
161 case PAMTEST_ERR_END:
162 return "pam_end failed()";
163 case PAMTEST_ERR_KEEPHANDLE:
164 /* Fallthrough */
165 case PAMTEST_ERR_INTERNAL:
166 return "Internal libpamtest error";
169 return "Unknown";
172 struct pamtest_conv_ctx {
173 struct pamtest_conv_data *data;
175 size_t echo_off_idx;
176 size_t echo_on_idx;
177 size_t err_idx;
178 size_t info_idx;
181 static int add_to_reply(struct pam_response *reply, const char *str)
183 size_t len;
185 len = strlen(str) + 1;
187 reply->resp = calloc(len, sizeof(char));
188 if (reply->resp == NULL) {
189 return PAM_BUF_ERR;
192 memcpy(reply->resp, str, len);
193 return PAM_SUCCESS;
196 static void free_reply(struct pam_response *reply, int num_msg)
198 int i;
200 if (reply == NULL) {
201 return;
204 for (i = 0; i < num_msg; i++) {
205 free(reply[i].resp);
207 free(reply);
210 static int pamtest_simple_conv(int num_msg,
211 const struct pam_message **msgm,
212 struct pam_response **response,
213 void *appdata_ptr)
215 int i, ri = 0;
216 int ret;
217 struct pam_response *reply = NULL;
218 const char *prompt;
219 struct pamtest_conv_ctx *cctx = \
220 (struct pamtest_conv_ctx *) appdata_ptr;
222 if (cctx == NULL) {
223 return PAM_CONV_ERR;
226 if (response) {
227 reply = (struct pam_response *) calloc(num_msg,
228 sizeof(struct pam_response));
229 if (reply == NULL) {
230 return PAM_CONV_ERR;
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];
240 if (reply != NULL) {
241 if (prompt != NULL) {
242 ret = add_to_reply(&reply[ri], prompt);
243 if (ret != PAM_SUCCESS) {
244 free_reply(reply, num_msg);
245 return ret;
247 } else {
248 reply[ri].resp = NULL;
250 ri++;
253 cctx->echo_off_idx++;
254 break;
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);
260 return PAM_CONV_ERR;
263 if (reply != NULL) {
264 if (prompt != NULL) {
265 ret = add_to_reply(&reply[ri], prompt);
266 if (ret != PAM_SUCCESS) {
267 free_reply(reply, num_msg);
268 return ret;
271 ri++;
274 cctx->echo_on_idx++;
275 break;
276 case PAM_ERROR_MSG:
277 if (cctx->data->out_err != NULL) {
278 memcpy(cctx->data->out_err[cctx->err_idx],
279 msgm[i]->msg,
280 MIN(strlen(msgm[i]->msg),
281 PAM_MAX_MSG_SIZE));
282 cctx->err_idx++;
284 break;
285 case PAM_TEXT_INFO:
286 if (cctx->data->out_info != NULL) {
287 memcpy(cctx->data->out_info[cctx->info_idx],
288 msgm[i]->msg,
289 MIN(strlen(msgm[i]->msg),
290 PAM_MAX_MSG_SIZE));
291 cctx->info_idx++;
293 break;
294 default:
295 continue;
299 if (response && ri > 0) {
300 *response = reply;
301 } else {
302 free(reply);
305 return PAM_SUCCESS;
308 enum pamtest_err _pamtest(const char *service,
309 const char *user,
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 = {
315 .data = conv_data,
318 return _pamtest_conv(service, user,
319 pamtest_simple_conv,
320 &cctx,
321 test_cases,
322 num_test_cases);