import less(1)
[unleashed/tickless.git] / usr / src / lib / krb5 / plugins / preauth / pkinit / pkinit_identity.c
blob030dd40ff7f9e886e8ec951d173498a30a13f5b6
1 /*
2 * COPYRIGHT (C) 2007
3 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
4 * ALL RIGHTS RESERVED
6 * Permission is granted to use, copy, create derivative works
7 * and redistribute this software and such derivative works
8 * for any purpose, so long as the name of The University of
9 * Michigan is not used in any advertising or publicity
10 * pertaining to the use of distribution of this software
11 * without specific, written prior authorization. If the
12 * above copyright notice or any other identification of the
13 * University of Michigan is included in any copy of any
14 * portion of this software, then the disclaimer below must
15 * also be included.
17 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
18 * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
19 * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
20 * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
21 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
23 * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
24 * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
25 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
26 * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
27 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGES.
32 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
35 #include <errno.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <dlfcn.h>
40 #include <unistd.h>
41 #include <dirent.h>
43 #include <libintl.h>
45 #include "pkinit.h"
47 static void
48 free_list(char **list)
50 int i;
52 if (list == NULL)
53 return;
55 for (i = 0; list[i] != NULL; i++)
56 free(list[i]);
57 free(list);
60 static krb5_error_code
61 copy_list(char ***dst, char **src)
63 int i;
64 char **newlist;
66 if (dst == NULL)
67 return EINVAL;
68 *dst = NULL;
70 if (src == NULL)
71 return 0;
73 for (i = 0; src[i] != NULL; i++);
75 newlist = calloc(1, (i + 1) * sizeof(*newlist));
76 if (newlist == NULL)
77 return ENOMEM;
79 for (i = 0; src[i] != NULL; i++) {
80 newlist[i] = strdup(src[i]);
81 if (newlist[i] == NULL)
82 goto cleanup;
84 newlist[i] = NULL;
85 *dst = newlist;
86 return 0;
87 cleanup:
88 free_list(newlist);
89 return ENOMEM;
92 char *
93 idtype2string(int idtype)
95 /* Solaris Kerberos: Removed "break"s (lint) */
96 switch(idtype) {
97 case IDTYPE_FILE: return "FILE";
98 case IDTYPE_DIR: return "DIR";
99 case IDTYPE_PKCS11: return "PKCS11";
100 case IDTYPE_PKCS12: return "PKCS12";
101 case IDTYPE_ENVVAR: return "ENV";
102 default: return "INVALID";
106 char *
107 catype2string(int catype)
109 /* Solaris Kerberos: Removed "break"s (lint) */
110 switch(catype) {
111 case CATYPE_ANCHORS: return "ANCHORS";
112 case CATYPE_INTERMEDIATES: return "INTERMEDIATES";
113 case CATYPE_CRLS: return "CRLS";
114 default: return "INVALID";
118 krb5_error_code
119 pkinit_init_identity_opts(pkinit_identity_opts **idopts)
121 pkinit_identity_opts *opts = NULL;
123 *idopts = NULL;
124 opts = (pkinit_identity_opts *) calloc(1, sizeof(pkinit_identity_opts));
125 if (opts == NULL)
126 return ENOMEM;
128 opts->identity = NULL;
129 opts->anchors = NULL;
130 opts->intermediates = NULL;
131 opts->crls = NULL;
132 opts->ocsp = NULL;
133 opts->dn_mapping_file = NULL;
135 opts->cert_filename = NULL;
136 opts->key_filename = NULL;
137 #ifndef WITHOUT_PKCS11
138 opts->p11_module_name = NULL;
139 opts->slotid = PK_NOSLOT;
140 opts->token_label = NULL;
141 opts->cert_id_string = NULL;
142 opts->cert_label = NULL;
143 opts->PIN = NULL;
144 #endif
146 *idopts = opts;
148 return 0;
151 krb5_error_code
152 pkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
153 pkinit_identity_opts **dest_opts)
155 pkinit_identity_opts *newopts;
156 krb5_error_code retval;
158 *dest_opts = NULL;
159 retval = pkinit_init_identity_opts(&newopts);
160 if (retval)
161 return retval;
163 retval = ENOMEM;
165 if (src_opts->identity != NULL) {
166 newopts->identity = strdup(src_opts->identity);
167 if (newopts->identity == NULL)
168 goto cleanup;
171 retval = copy_list(&newopts->anchors, src_opts->anchors);
172 if (retval)
173 goto cleanup;
175 retval = copy_list(&newopts->intermediates,src_opts->intermediates);
176 if (retval)
177 goto cleanup;
179 retval = copy_list(&newopts->crls, src_opts->crls);
180 if (retval)
181 goto cleanup;
183 if (src_opts->ocsp != NULL) {
184 newopts->ocsp = strdup(src_opts->ocsp);
185 if (newopts->ocsp == NULL)
186 goto cleanup;
189 if (src_opts->cert_filename != NULL) {
190 newopts->cert_filename = strdup(src_opts->cert_filename);
191 if (newopts->cert_filename == NULL)
192 goto cleanup;
195 if (src_opts->key_filename != NULL) {
196 newopts->key_filename = strdup(src_opts->key_filename);
197 if (newopts->key_filename == NULL)
198 goto cleanup;
201 #ifndef WITHOUT_PKCS11
202 if (src_opts->p11_module_name != NULL) {
203 newopts->p11_module_name = strdup(src_opts->p11_module_name);
204 if (newopts->p11_module_name == NULL)
205 goto cleanup;
208 newopts->slotid = src_opts->slotid;
210 if (src_opts->token_label != NULL) {
211 newopts->token_label = strdup(src_opts->token_label);
212 if (newopts->token_label == NULL)
213 goto cleanup;
216 if (src_opts->cert_id_string != NULL) {
217 newopts->cert_id_string = strdup(src_opts->cert_id_string);
218 if (newopts->cert_id_string == NULL)
219 goto cleanup;
222 if (src_opts->cert_label != NULL) {
223 newopts->cert_label = strdup(src_opts->cert_label);
224 if (newopts->cert_label == NULL)
225 goto cleanup;
227 if (src_opts->PIN != NULL) {
228 newopts->PIN = strdup(src_opts->PIN);
229 if (newopts->PIN == NULL)
230 goto cleanup;
232 #endif
235 *dest_opts = newopts;
236 return 0;
237 cleanup:
238 pkinit_fini_identity_opts(newopts);
239 return retval;
242 void
243 pkinit_fini_identity_opts(pkinit_identity_opts *idopts)
245 if (idopts == NULL)
246 return;
248 free(idopts->identity);
249 free_list(idopts->anchors);
250 free_list(idopts->intermediates);
251 free_list(idopts->crls);
252 free_list(idopts->identity_alt);
254 free(idopts->cert_filename);
255 free(idopts->key_filename);
256 #ifndef WITHOUT_PKCS11
257 free(idopts->p11_module_name);
258 free(idopts->token_label);
259 free(idopts->cert_id_string);
260 free(idopts->cert_label);
261 if (idopts->PIN != NULL) {
262 (void) memset(idopts->PIN, 0, strlen(idopts->PIN));
263 free(idopts->PIN);
265 #endif
266 free(idopts);
269 #ifndef WITHOUT_PKCS11
270 /* ARGSUSED */
271 static krb5_error_code
272 parse_pkcs11_options(krb5_context context,
273 pkinit_identity_opts *idopts,
274 const char *residual)
276 char *s, *cp, *vp;
277 krb5_error_code retval = ENOMEM;
279 if (residual == NULL || residual[0] == '\0')
280 return 0;
282 /* Split string into attr=value substrings */
283 s = strdup(residual);
284 if (s == NULL)
285 return retval;
287 for ((cp = strtok(s, ":")); cp; (cp = strtok(NULL, ":"))) {
288 vp = strchr(cp, '=');
290 /* If there is no "=", this is a pkcs11 module name */
291 if (vp == NULL) {
292 free(idopts->p11_module_name);
293 idopts->p11_module_name = strdup(cp);
294 if (idopts->p11_module_name == NULL)
295 goto cleanup;
296 continue;
298 *vp++ = '\0';
299 if (!strcmp(cp, "module_name")) {
300 free(idopts->p11_module_name);
301 idopts->p11_module_name = strdup(vp);
302 if (idopts->p11_module_name == NULL)
303 goto cleanup;
304 } else if (!strcmp(cp, "slotid")) {
305 long slotid = strtol(vp, NULL, 10);
306 if ((slotid == LONG_MIN || slotid == LONG_MAX) && errno != 0) {
307 retval = EINVAL;
308 goto cleanup;
310 if ((long) (int) slotid != slotid) {
311 retval = EINVAL;
312 goto cleanup;
314 idopts->slotid = slotid;
315 } else if (!strcmp(cp, "token")) {
316 free(idopts->token_label);
317 idopts->token_label = strdup(vp);
318 if (idopts->token_label == NULL)
319 goto cleanup;
320 } else if (!strcmp(cp, "certid")) {
321 free(idopts->cert_id_string);
322 idopts->cert_id_string = strdup(vp);
323 if (idopts->cert_id_string == NULL)
324 goto cleanup;
325 } else if (!strcmp(cp, "certlabel")) {
326 free(idopts->cert_label);
327 idopts->cert_label = strdup(vp);
328 if (idopts->cert_label == NULL)
329 goto cleanup;
332 retval = 0;
333 cleanup:
334 free(s);
335 return retval;
337 #endif
339 /* ARGSUSED */
340 static krb5_error_code
341 parse_fs_options(krb5_context context,
342 pkinit_identity_opts *idopts,
343 const char *residual)
345 char *certname, *keyname;
346 krb5_error_code retval = ENOMEM;
348 if (residual == NULL || residual[0] == '\0')
349 return 0;
351 certname = strdup(residual);
352 if (certname == NULL)
353 goto cleanup;
355 certname = strtok(certname, ",");
356 keyname = strtok(NULL, ",");
358 idopts->cert_filename = strdup(certname);
359 if (idopts->cert_filename == NULL)
360 goto cleanup;
362 idopts->key_filename = strdup(keyname ? keyname : certname);
363 if (idopts->key_filename == NULL)
364 goto cleanup;
366 retval = 0;
367 cleanup:
368 free(certname);
369 return retval;
372 /* ARGSUSED */
373 static krb5_error_code
374 parse_pkcs12_options(krb5_context context,
375 pkinit_identity_opts *idopts,
376 const char *residual)
378 krb5_error_code retval = ENOMEM;
380 if (residual == NULL || residual[0] == '\0')
381 return 0;
383 idopts->cert_filename = strdup(residual);
384 if (idopts->cert_filename == NULL)
385 goto cleanup;
387 idopts->key_filename = strdup(residual);
388 if (idopts->key_filename == NULL)
389 goto cleanup;
391 pkiDebug("%s: cert_filename '%s' key_filename '%s'\n",
392 __FUNCTION__, idopts->cert_filename,
393 idopts->key_filename);
394 retval = 0;
395 cleanup:
396 return retval;
399 static krb5_error_code
400 process_option_identity(krb5_context context,
401 pkinit_plg_crypto_context plg_cryptoctx,
402 pkinit_req_crypto_context req_cryptoctx,
403 pkinit_identity_opts *idopts,
404 pkinit_identity_crypto_context id_cryptoctx,
405 const char *value)
407 const char *residual;
408 int idtype;
409 krb5_error_code retval = 0;
411 pkiDebug("%s: processing value '%s'\n",
412 __FUNCTION__, value ? value : "NULL");
413 if (value == NULL)
414 return EINVAL;
416 residual = strchr(value, ':');
417 if (residual != NULL) {
418 unsigned int typelen;
419 residual++; /* skip past colon */
420 typelen = residual - value;
421 if (strncmp(value, "FILE:", typelen) == 0) {
422 idtype = IDTYPE_FILE;
423 #ifndef WITHOUT_PKCS11
424 } else if (strncmp(value, "PKCS11:", typelen) == 0) {
425 idtype = IDTYPE_PKCS11;
426 #endif
427 } else if (strncmp(value, "PKCS12:", typelen) == 0) {
428 idtype = IDTYPE_PKCS12;
429 } else if (strncmp(value, "DIR:", typelen) == 0) {
430 idtype = IDTYPE_DIR;
431 } else if (strncmp(value, "ENV:", typelen) == 0) {
432 idtype = IDTYPE_ENVVAR;
433 } else {
434 pkiDebug("%s: Unsupported type while processing '%s'\n",
435 __FUNCTION__, value);
436 krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
437 "Unsupported type while processing '%s'\n",
438 value);
439 return KRB5_PREAUTH_FAILED;
441 } else {
442 idtype = IDTYPE_FILE;
443 residual = value;
446 idopts->idtype = idtype;
447 pkiDebug("%s: idtype is %s\n", __FUNCTION__, idtype2string(idopts->idtype));
448 switch (idtype) {
449 case IDTYPE_ENVVAR: {
450 /* Solaris Kerberos: Improved error messages */
451 char *envvar = getenv(residual);
452 if (envvar == NULL) {
453 krb5_set_error_message(context, EINVAL,
454 gettext("failed to find environmental variable \'%s\'"),
455 residual);
456 return EINVAL;
458 return process_option_identity(context, plg_cryptoctx,
459 req_cryptoctx, idopts, id_cryptoctx,
460 envvar);
461 /* Solaris Kerberos: not reached */
463 case IDTYPE_FILE:
464 retval = parse_fs_options(context, idopts, residual);
465 break;
466 case IDTYPE_PKCS12:
467 retval = parse_pkcs12_options(context, idopts, residual);
468 break;
469 #ifndef WITHOUT_PKCS11
470 case IDTYPE_PKCS11:
471 retval = parse_pkcs11_options(context, idopts, residual);
472 break;
473 #endif
474 case IDTYPE_DIR:
475 idopts->cert_filename = strdup(residual);
476 if (idopts->cert_filename == NULL)
477 retval = ENOMEM;
478 break;
479 default:
480 krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
481 "Internal error parsing X509_user_identity\n");
482 retval = EINVAL;
483 break;
485 return retval;
488 static krb5_error_code
489 process_option_ca_crl(krb5_context context,
490 pkinit_plg_crypto_context plg_cryptoctx,
491 pkinit_req_crypto_context req_cryptoctx,
492 pkinit_identity_opts *idopts,
493 pkinit_identity_crypto_context id_cryptoctx,
494 const char *value,
495 int catype)
497 char *residual;
498 unsigned int typelen;
499 int idtype;
501 pkiDebug("%s: processing catype %s, value '%s'\n",
502 __FUNCTION__, catype2string(catype), value);
503 residual = strchr(value, ':');
504 if (residual == NULL) {
505 pkiDebug("No type given for '%s'\n", value);
506 return EINVAL;
508 residual++; /* skip past colon */
509 typelen = residual - value;
510 if (strncmp(value, "FILE:", typelen) == 0) {
511 idtype = IDTYPE_FILE;
512 } else if (strncmp(value, "DIR:", typelen) == 0) {
513 idtype = IDTYPE_DIR;
514 } else {
515 return ENOTSUP;
517 return crypto_load_cas_and_crls(context,
518 plg_cryptoctx,
519 req_cryptoctx,
520 idopts, id_cryptoctx,
521 idtype, catype, residual);
524 static krb5_error_code
525 pkinit_identity_process_option(krb5_context context,
526 pkinit_plg_crypto_context plg_cryptoctx,
527 pkinit_req_crypto_context req_cryptoctx,
528 pkinit_identity_opts *idopts,
529 pkinit_identity_crypto_context id_cryptoctx,
530 int attr,
531 const char *value)
533 krb5_error_code retval = 0;
535 switch (attr) {
536 case PKINIT_ID_OPT_USER_IDENTITY:
537 retval = process_option_identity(context, plg_cryptoctx,
538 req_cryptoctx, idopts,
539 id_cryptoctx, value);
540 break;
541 case PKINIT_ID_OPT_ANCHOR_CAS:
542 retval = process_option_ca_crl(context, plg_cryptoctx,
543 req_cryptoctx, idopts,
544 id_cryptoctx, value,
545 CATYPE_ANCHORS);
546 break;
547 case PKINIT_ID_OPT_INTERMEDIATE_CAS:
548 retval = process_option_ca_crl(context, plg_cryptoctx,
549 req_cryptoctx, idopts,
550 id_cryptoctx,
551 value, CATYPE_INTERMEDIATES);
552 break;
553 case PKINIT_ID_OPT_CRLS:
554 retval = process_option_ca_crl(context, plg_cryptoctx,
555 req_cryptoctx, idopts,
556 id_cryptoctx,
557 value, CATYPE_CRLS);
558 break;
559 case PKINIT_ID_OPT_OCSP:
560 retval = ENOTSUP;
561 break;
562 default:
563 retval = EINVAL;
564 break;
566 return retval;
569 krb5_error_code
570 pkinit_identity_initialize(krb5_context context,
571 pkinit_plg_crypto_context plg_cryptoctx,
572 pkinit_req_crypto_context req_cryptoctx,
573 pkinit_identity_opts *idopts,
574 pkinit_identity_crypto_context id_cryptoctx,
575 int do_matching,
576 krb5_principal princ)
578 krb5_error_code retval = EINVAL;
579 int i;
581 pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx);
582 if (idopts == NULL || id_cryptoctx == NULL)
583 goto errout;
586 * If identity was specified, use that. (For the kdc, this
587 * is specified as pkinit_identity in the kdc.conf. For users,
588 * this is specified on the command line via X509_user_identity.)
589 * If a user did not specify identity on the command line,
590 * then we will try alternatives which may have been specified
591 * in the config file.
593 if (idopts->identity != NULL) {
594 retval = pkinit_identity_process_option(context, plg_cryptoctx,
595 req_cryptoctx, idopts,
596 id_cryptoctx,
597 PKINIT_ID_OPT_USER_IDENTITY,
598 idopts->identity);
599 } else if (idopts->identity_alt != NULL) {
600 for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++)
601 retval = pkinit_identity_process_option(context, plg_cryptoctx,
602 req_cryptoctx, idopts,
603 id_cryptoctx,
604 PKINIT_ID_OPT_USER_IDENTITY,
605 idopts->identity_alt[i]);
606 } else {
607 pkiDebug("%s: no user identity options specified\n", __FUNCTION__);
608 goto errout;
610 if (retval)
611 goto errout;
613 retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx,
614 idopts, id_cryptoctx, princ, do_matching);
615 if (retval)
616 goto errout;
618 if (do_matching) {
619 retval = pkinit_cert_matching(context, plg_cryptoctx, req_cryptoctx,
620 id_cryptoctx, princ, TRUE);
621 if (retval) {
622 pkiDebug("%s: No matching certificate found\n", __FUNCTION__);
623 (void) crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
624 id_cryptoctx);
625 goto errout;
627 } else {
628 /* Tell crypto code to use the "default" */
629 retval = crypto_cert_select_default(context, plg_cryptoctx,
630 req_cryptoctx, id_cryptoctx);
631 if (retval) {
632 pkiDebug("%s: Failed while selecting default certificate\n",
633 __FUNCTION__);
634 (void) crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
635 id_cryptoctx);
636 goto errout;
640 retval = crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
641 id_cryptoctx);
642 if (retval)
643 goto errout;
645 for (i = 0; idopts->anchors != NULL && idopts->anchors[i] != NULL; i++) {
646 retval = pkinit_identity_process_option(context, plg_cryptoctx,
647 req_cryptoctx, idopts,
648 id_cryptoctx,
649 PKINIT_ID_OPT_ANCHOR_CAS,
650 idopts->anchors[i]);
651 if (retval)
652 goto errout;
654 for (i = 0; idopts->intermediates != NULL
655 && idopts->intermediates[i] != NULL; i++) {
656 retval = pkinit_identity_process_option(context, plg_cryptoctx,
657 req_cryptoctx, idopts,
658 id_cryptoctx,
659 PKINIT_ID_OPT_INTERMEDIATE_CAS,
660 idopts->intermediates[i]);
661 if (retval)
662 goto errout;
664 for (i = 0; idopts->crls != NULL && idopts->crls[i] != NULL; i++) {
665 retval = pkinit_identity_process_option(context, plg_cryptoctx,
666 req_cryptoctx, idopts,
667 id_cryptoctx,
668 PKINIT_ID_OPT_CRLS,
669 idopts->crls[i]);
670 if (retval)
671 goto errout;
673 if (idopts->ocsp != NULL) {
674 retval = pkinit_identity_process_option(context, plg_cryptoctx,
675 req_cryptoctx, idopts,
676 id_cryptoctx,
677 PKINIT_ID_OPT_OCSP,
678 idopts->ocsp);
679 if (retval)
680 goto errout;
683 errout:
684 return retval;