Line data Source code
1 : /*
2 : SSSD
3 :
4 : Unit tests - exercise the krb5 child
5 :
6 : Authors:
7 : Jakub Hrozek <jhrozek@redhat.com>
8 :
9 : Copyright (C) 2012 Red Hat
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include <stdio.h>
26 : #include <stdlib.h>
27 : #include <talloc.h>
28 : #include <popt.h>
29 : #include <errno.h>
30 : #include <unistd.h>
31 : #include <limits.h>
32 :
33 : #include "util/util.h"
34 : #include "src/tools/tools_util.h"
35 :
36 : /* Interfaces being tested */
37 : #include "providers/krb5/krb5_auth.h"
38 : #include "providers/krb5/krb5_common.h"
39 : #include "providers/krb5/krb5_utils.h"
40 : #include "providers/krb5/krb5_ccache.h"
41 :
42 : extern struct dp_option default_krb5_opts[];
43 :
44 : static krb5_context krb5_error_ctx;
45 : #define KRB5_CHILD_TEST_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
46 :
47 : #define CHECK_KRET_L(kret, err, label) do { \
48 : if (kret) { \
49 : KRB5_CHILD_TEST_DEBUG(SSSDBG_OP_FAILURE, kret); \
50 : goto label; \
51 : } \
52 : } while(0) \
53 :
54 : struct krb5_child_test_ctx {
55 : struct tevent_context *ev;
56 : struct krb5child_req *kr;
57 :
58 : bool done;
59 : errno_t child_ret;
60 :
61 : uint8_t *buf;
62 : ssize_t len;
63 : struct krb5_child_response *res;
64 : };
65 :
66 : static errno_t
67 0 : setup_krb5_child_test(TALLOC_CTX *mem_ctx, struct krb5_child_test_ctx **_ctx)
68 : {
69 : struct krb5_child_test_ctx *ctx;
70 :
71 0 : ctx = talloc_zero(mem_ctx, struct krb5_child_test_ctx);
72 0 : if (!ctx) return ENOMEM;
73 :
74 0 : ctx->ev = tevent_context_init(ctx);
75 0 : if (ctx->ev == NULL) {
76 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tevent context\n");
77 0 : talloc_free(ctx);
78 0 : return EFAULT;
79 : }
80 :
81 0 : *_ctx = ctx;
82 0 : return EOK;
83 : }
84 :
85 0 : int re_destructor(void *memctx)
86 : {
87 0 : struct krb5_ctx *ctx = (struct krb5_ctx *) memctx;
88 :
89 0 : if (ctx->illegal_path_re) {
90 0 : pcre_free(ctx->illegal_path_re);
91 0 : ctx->illegal_path_re = NULL;
92 : }
93 0 : return 0;
94 : }
95 :
96 : static struct krb5_ctx *
97 0 : create_dummy_krb5_ctx(TALLOC_CTX *mem_ctx, const char *realm)
98 : {
99 : struct krb5_ctx *krb5_ctx;
100 : const char *errstr;
101 : int errval;
102 : int errpos;
103 : int i;
104 : errno_t ret;
105 :
106 0 : krb5_ctx = talloc_zero(mem_ctx, struct krb5_ctx);
107 0 : if (!krb5_ctx) return NULL;
108 :
109 0 : krb5_ctx->illegal_path_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
110 : &errval, &errstr, &errpos, NULL);
111 0 : if (krb5_ctx->illegal_path_re == NULL) {
112 0 : DEBUG(SSSDBG_OP_FAILURE,
113 : "Invalid Regular Expression pattern at position %d. "
114 : "(Error: %d [%s])\n", errpos, errval, errstr);
115 0 : goto fail;
116 : }
117 0 : talloc_set_destructor((TALLOC_CTX *) krb5_ctx, re_destructor);
118 :
119 : /* Kerberos options */
120 0 : krb5_ctx->opts = talloc_zero_array(krb5_ctx, struct dp_option, KRB5_OPTS);
121 0 : if (!krb5_ctx->opts) goto fail;
122 0 : for (i = 0; i < KRB5_OPTS; i++) {
123 0 : krb5_ctx->opts[i].opt_name = default_krb5_opts[i].opt_name;
124 0 : krb5_ctx->opts[i].type = default_krb5_opts[i].type;
125 0 : krb5_ctx->opts[i].def_val = default_krb5_opts[i].def_val;
126 0 : switch (krb5_ctx->opts[i].type) {
127 : case DP_OPT_STRING:
128 0 : ret = dp_opt_set_string(krb5_ctx->opts, i,
129 : default_krb5_opts[i].def_val.string);
130 0 : break;
131 : case DP_OPT_BLOB:
132 0 : ret = dp_opt_set_blob(krb5_ctx->opts, i,
133 : default_krb5_opts[i].def_val.blob);
134 0 : break;
135 : case DP_OPT_NUMBER:
136 0 : ret = dp_opt_set_int(krb5_ctx->opts, i,
137 : default_krb5_opts[i].def_val.number);
138 0 : break;
139 : case DP_OPT_BOOL:
140 0 : ret = dp_opt_set_bool(krb5_ctx->opts, i,
141 : default_krb5_opts[i].def_val.boolean);
142 0 : break;
143 : }
144 0 : if (ret) goto fail;
145 : }
146 :
147 0 : ret = dp_opt_set_string(krb5_ctx->opts, KRB5_REALM, realm);
148 0 : if (ret) goto fail;
149 :
150 0 : return krb5_ctx;
151 :
152 : fail:
153 0 : talloc_free(krb5_ctx);
154 0 : return NULL;
155 : }
156 :
157 : static struct pam_data *
158 0 : create_dummy_pam_data(TALLOC_CTX *mem_ctx, const char *user,
159 : const char *password)
160 : {
161 : struct pam_data *pd;
162 : const char *authtok;
163 : size_t authtok_len;
164 : errno_t ret;
165 :
166 0 : pd = create_pam_data(mem_ctx);
167 0 : if (!pd) goto fail;
168 :
169 0 : pd->cmd = SSS_PAM_AUTHENTICATE;
170 0 : pd->user = talloc_strdup(pd, user);
171 0 : if (!pd->user) goto fail;
172 :
173 0 : ret = sss_authtok_set_password(pd->authtok, password, 0);
174 0 : if (ret) goto fail;
175 :
176 0 : (void)sss_authtok_get_password(pd->authtok, &authtok, &authtok_len);
177 0 : DEBUG(SSSDBG_FUNC_DATA, "Authtok [%s] len [%d]\n",
178 : authtok, (int)authtok_len);
179 :
180 0 : return pd;
181 :
182 : fail:
183 0 : talloc_free(pd);
184 0 : return NULL;
185 : }
186 :
187 : static struct krb5child_req *
188 0 : create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
189 : const char *password, const char *realm,
190 : const char *ccname, const char *ccname_template,
191 : int timeout)
192 : {
193 : struct krb5child_req *kr;
194 : struct passwd *pwd;
195 : errno_t ret;
196 :
197 : /* The top level child request */
198 0 : kr = talloc_zero(mem_ctx, struct krb5child_req);
199 0 : if (!kr) return NULL;
200 :
201 0 : pwd = getpwnam(user);
202 0 : if (!pwd) {
203 0 : DEBUG(SSSDBG_FATAL_FAILURE,
204 : "Cannot get info on user [%s]\n", user);
205 0 : goto fail;
206 : }
207 :
208 0 : kr->uid = pwd->pw_uid;
209 0 : kr->gid = pwd->pw_gid;
210 :
211 : /* The Kerberos context */
212 0 : kr->krb5_ctx = create_dummy_krb5_ctx(kr, realm);
213 : /* PAM Data structure */
214 0 : kr->pd = create_dummy_pam_data(kr, user, password);
215 :
216 0 : ret = krb5_get_simple_upn(kr, kr->krb5_ctx, NULL, kr->pd->user, NULL,
217 : &kr->upn);
218 0 : if (ret != EOK) {
219 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_get_simple_upn failed.\n");
220 0 : goto fail;
221 : }
222 :
223 : /* Override options with what was provided by the user */
224 0 : if (ccname_template) {
225 0 : ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL,
226 : ccname_template);
227 0 : if (ret != EOK) goto fail;
228 : }
229 :
230 0 : if (timeout) {
231 0 : ret = dp_opt_set_int(kr->krb5_ctx->opts, KRB5_AUTH_TIMEOUT, timeout);
232 0 : if (ret != EOK) {
233 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set value for krb5_auth_timeout\n");
234 0 : goto fail;
235 : }
236 : }
237 :
238 0 : if (!ccname) {
239 0 : kr->ccname = expand_ccname_template(kr, kr,
240 0 : dp_opt_get_cstring(kr->krb5_ctx->opts,
241 : KRB5_CCNAME_TMPL),
242 0 : kr->krb5_ctx->illegal_path_re, true, true);
243 0 : if (!kr->ccname) goto fail;
244 :
245 0 : DEBUG(SSSDBG_FUNC_DATA, "ccname [%s] uid [%llu] gid [%llu]\n",
246 : kr->ccname, (unsigned long long) kr->uid,
247 : (unsigned long long) kr->gid);
248 : } else {
249 0 : kr->ccname = talloc_strdup(kr, ccname);
250 : }
251 0 : if (!kr->ccname) goto fail;
252 :
253 0 : DEBUG(SSSDBG_FUNC_DATA, "ccname [%s] uid [%u] gid [%u]\n",
254 : kr->ccname, kr->uid, kr->gid);
255 :
256 0 : ret = sss_krb5_precreate_ccache(kr->ccname,
257 : kr->uid, kr->gid);
258 0 : if (ret != EOK) {
259 0 : DEBUG(SSSDBG_OP_FAILURE, "create_ccache_dir failed.\n");
260 0 : goto fail;
261 : }
262 :
263 0 : return kr;
264 :
265 : fail:
266 0 : talloc_free(kr);
267 0 : return NULL;
268 : }
269 :
270 : static void
271 0 : child_done(struct tevent_req *req)
272 : {
273 0 : struct krb5_child_test_ctx *ctx = tevent_req_callback_data(req,
274 : struct krb5_child_test_ctx);
275 : errno_t ret;
276 :
277 0 : ret = handle_child_recv(req, ctx, &ctx->buf, &ctx->len);
278 0 : talloc_free(req);
279 0 : ctx->done = true;
280 0 : ctx->child_ret = ret;
281 0 : }
282 :
283 : static void
284 0 : printtime(krb5_timestamp ts)
285 : {
286 : krb5_error_code kret;
287 : char timestring[BUFSIZ];
288 0 : char fill = '\0';
289 :
290 : #ifdef HAVE_KRB5_TIMESTAMP_TO_SFSTRING
291 0 : kret = krb5_timestamp_to_sfstring(ts, timestring, BUFSIZ, &fill);
292 0 : if (kret) {
293 0 : KRB5_CHILD_TEST_DEBUG(SSSDBG_OP_FAILURE, kret);
294 : }
295 0 : printf("%s", timestring);
296 : #else
297 : printf("%s", ctime(&ts));
298 : #endif /* HAVE_KRB5_TIMESTAMP_TO_SFSTRING */
299 0 : }
300 :
301 : static void
302 0 : print_creds(krb5_context kcontext, krb5_creds *cred, const char *defname)
303 : {
304 : krb5_error_code kret;
305 0 : char *name = NULL;
306 0 : char *sname = NULL;
307 :
308 0 : kret = krb5_unparse_name(kcontext, cred->client, &name);
309 0 : CHECK_KRET_L(kret, EIO, done);
310 :
311 0 : kret = krb5_unparse_name(kcontext, cred->server, &sname);
312 0 : CHECK_KRET_L(kret, EIO, done);
313 :
314 0 : if (!cred->times.starttime) {
315 0 : cred->times.starttime = cred->times.authtime;
316 : }
317 :
318 :
319 0 : printf("\t\t%s\n", sname);
320 0 : printf("\t\tValid from\t"); printtime(cred->times.starttime);
321 0 : printf("\n\t\tValid until\t"); printtime(cred->times.endtime);
322 0 : printf("\n");
323 :
324 0 : if (strcmp(name, defname)) {
325 0 : printf("\t\tfor client %s", name);
326 : }
327 :
328 : done:
329 0 : krb5_free_unparsed_name(kcontext, name);
330 0 : krb5_free_unparsed_name(kcontext, sname);
331 0 : }
332 :
333 : static errno_t
334 0 : print_ccache(const char *cc)
335 : {
336 : krb5_cc_cursor cur;
337 0 : krb5_ccache cache = NULL;
338 : krb5_error_code kret;
339 0 : krb5_context kcontext = NULL;
340 0 : krb5_principal_data *princ = NULL;
341 : krb5_creds creds;
342 0 : char *defname = NULL;
343 0 : int i = 1;
344 0 : errno_t ret = EIO;
345 :
346 0 : kret = krb5_init_context(&kcontext);
347 0 : CHECK_KRET_L(kret, EIO, done);
348 :
349 0 : kret = krb5_cc_resolve(kcontext, cc, &cache);
350 0 : CHECK_KRET_L(kret, EIO, done);
351 :
352 0 : kret = krb5_cc_get_principal(kcontext, cache, &princ);
353 0 : CHECK_KRET_L(kret, EIO, done);
354 :
355 0 : kret = krb5_unparse_name(kcontext, princ, &defname);
356 0 : CHECK_KRET_L(kret, EIO, done);
357 :
358 0 : printf("\nTicket cache: %s:%s\nDefault principal: %s\n\n",
359 : krb5_cc_get_type(kcontext, cache),
360 : krb5_cc_get_name(kcontext, cache), defname);
361 :
362 0 : kret = krb5_cc_start_seq_get(kcontext, cache, &cur);
363 0 : CHECK_KRET_L(kret, EIO, done);
364 :
365 0 : while (!(kret = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) {
366 0 : printf("Ticket #%d:\n", i);
367 0 : print_creds(kcontext, &creds, defname);
368 0 : krb5_free_cred_contents(kcontext, &creds);
369 : }
370 :
371 0 : kret = krb5_cc_end_seq_get(kcontext, cache, &cur);
372 0 : CHECK_KRET_L(kret, EIO, done);
373 :
374 0 : ret = EOK;
375 : done:
376 0 : krb5_cc_close(kcontext, cache);
377 0 : krb5_free_unparsed_name(kcontext, defname);
378 0 : krb5_free_principal(kcontext, princ);
379 0 : krb5_free_context(kcontext);
380 0 : return ret;
381 : }
382 :
383 : int
384 0 : main(int argc, const char *argv[])
385 : {
386 : int opt;
387 : errno_t ret;
388 0 : struct krb5_child_test_ctx *ctx = NULL;
389 : struct tevent_req *req;
390 :
391 0 : int pc_debug = 0;
392 0 : int pc_timeout = 0;
393 0 : const char *pc_user = NULL;;
394 0 : const char *pc_passwd = NULL;;
395 0 : const char *pc_realm = NULL;;
396 0 : const char *pc_ccname = NULL;;
397 0 : const char *pc_ccname_tp = NULL;;
398 0 : char *password = NULL;
399 0 : bool rm_ccache = true;
400 :
401 : poptContext pc;
402 0 : struct poptOption long_options[] = {
403 : POPT_AUTOHELP
404 : { "debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_debug, 0,
405 : "The debug level to run with", NULL },
406 : { "user", 'u', POPT_ARG_STRING, &pc_user, 0,
407 : "The user to log in as", NULL },
408 : { "password", 'w', POPT_ARG_STRING, &pc_passwd, 0,
409 : "The authtok to use", NULL },
410 : { "ask-password", 'W', POPT_ARG_NONE, NULL, 'W',
411 : "Ask interactively for authtok", NULL },
412 : { "ccname", 'c', POPT_ARG_STRING, &pc_ccname, 0,
413 : "Force usage of a certain credential cache", NULL },
414 : { "ccname-template", 't', POPT_ARG_STRING, &pc_ccname_tp, 0,
415 : "Specify the credential cache template", NULL },
416 : { "realm", 'r', POPT_ARG_STRING, &pc_realm, 0,
417 : "The Kerberos realm to use", NULL },
418 : { "keep-ccache", 'k', POPT_ARG_NONE, NULL, 'k',
419 : "Do not delete the ccache when the tool finishes", NULL },
420 : { "timeout", '\0', POPT_ARG_INT, &pc_timeout, 0,
421 : "The timeout for the child, in seconds", NULL },
422 : POPT_TABLEEND
423 : };
424 :
425 0 : debug_prg_name = argv[0];
426 0 : pc = poptGetContext(NULL, argc, argv, long_options, 0);
427 :
428 0 : while ((opt = poptGetNextOpt(pc)) > 0) {
429 0 : switch(opt) {
430 : case 'W':
431 0 : errno = 0;
432 0 : password = getpass("Enter password:");
433 0 : if (!password) {
434 0 : return 1;
435 : }
436 0 : break;
437 : case 'k':
438 0 : rm_ccache = false;
439 0 : break;
440 : default:
441 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Unexpected option\n");
442 0 : return 1;
443 : }
444 : }
445 :
446 0 : DEBUG_CLI_INIT(pc_debug);
447 :
448 0 : if (opt != -1) {
449 0 : poptPrintUsage(pc, stderr, 0);
450 0 : fprintf(stderr, "%s", poptStrerror(opt));
451 0 : return 1;
452 : }
453 :
454 0 : if (!pc_user) {
455 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Please specify the user\n");
456 0 : poptPrintUsage(pc, stderr, 0);
457 0 : return 1;
458 : }
459 :
460 0 : if (!pc_realm) {
461 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Please specify the realm\n");
462 0 : poptPrintUsage(pc, stderr, 0);
463 0 : return 1;
464 : }
465 :
466 0 : if (!password && !pc_passwd) {
467 0 : DEBUG(SSSDBG_FATAL_FAILURE,
468 : "Password was not provided or asked for\n");
469 0 : poptPrintUsage(pc, stderr, 0);
470 0 : return 1;
471 : }
472 :
473 0 : if (pc_ccname && pc_ccname_tp) {
474 0 : DEBUG(SSSDBG_MINOR_FAILURE,
475 : "Both ccname and ccname template specified, "
476 : "will prefer ccname\n");
477 : }
478 :
479 0 : ret = setup_krb5_child_test(NULL, &ctx);
480 0 : if (ret != EOK) {
481 0 : poptPrintUsage(pc, stderr, 0);
482 0 : fprintf(stderr, "%s", poptStrerror(opt));
483 0 : return 3;
484 : }
485 :
486 0 : ctx->kr = create_dummy_req(ctx, pc_user, password ? password : pc_passwd,
487 : pc_realm, pc_ccname, pc_ccname_tp, pc_timeout);
488 0 : if (!ctx->kr) {
489 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Cannot create Kerberos request\n");
490 0 : ret = 4;
491 0 : goto done;
492 : }
493 :
494 0 : req = handle_child_send(ctx, ctx->ev, ctx->kr);
495 0 : if (!req) {
496 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Cannot create child request\n");
497 0 : ret = 4;
498 0 : goto done;
499 : }
500 0 : tevent_req_set_callback(req, child_done, ctx);
501 :
502 0 : while (ctx->done == false) {
503 0 : tevent_loop_once(ctx->ev);
504 : }
505 :
506 0 : printf("Child returned %d\n", ctx->child_ret);
507 :
508 0 : ret = parse_krb5_child_response(ctx, ctx->buf, ctx->len,
509 0 : ctx->kr->pd, 0, &ctx->res);
510 0 : if (ret != EOK) {
511 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Could not parse child response\n");
512 0 : ret = 5;
513 0 : goto done;
514 : }
515 :
516 0 : if (!ctx->res->ccname) {
517 0 : fprintf(stderr, "No ccname returned\n");
518 0 : ret = 6;
519 0 : goto done;
520 : }
521 :
522 0 : print_ccache(ctx->res->ccname);
523 :
524 0 : ret = 0;
525 : done:
526 0 : if (rm_ccache && ctx->res
527 0 : && ctx->res->ccname
528 0 : && ctx->kr) {
529 0 : sss_krb5_cc_destroy(ctx->res->ccname, ctx->kr->uid, ctx->kr->gid);
530 : }
531 0 : free(password);
532 0 : talloc_free(ctx);
533 0 : poptFreeContext(pc);
534 0 : return ret;
535 : }
|