1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * COPYRIGHT (C) 2007
4 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
5 * ALL RIGHTS RESERVED
6 *
7 * Permission is granted to use, copy, create derivative works
8 * and redistribute this software and such derivative works
9 * for any purpose, so long as the name of The University of
10 * Michigan is not used in any advertising or publicity
11 * pertaining to the use of distribution of this software
12 * without specific, written prior authorization. If the
13 * above copyright notice or any other identification of the
14 * University of Michigan is included in any copy of any
15 * portion of this software, then the disclaimer below must
16 * also be included.
17 *
18 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
19 * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
20 * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
21 * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
22 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
24 * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
25 * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
26 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
27 * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
28 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGES.
30 */
31
32 #include "pkinit.h"
33 #include <dirent.h>
34
35 static void
free_list(char ** list)36 free_list(char **list)
37 {
38 int i;
39
40 if (list == NULL)
41 return;
42
43 for (i = 0; list[i] != NULL; i++)
44 free(list[i]);
45 free(list);
46 }
47
48 static krb5_error_code
copy_list(char *** dst,char ** src)49 copy_list(char ***dst, char **src)
50 {
51 int i;
52 char **newlist;
53
54 if (dst == NULL)
55 return EINVAL;
56 *dst = NULL;
57
58 if (src == NULL)
59 return 0;
60
61 for (i = 0; src[i] != NULL; i++);
62
63 newlist = calloc(1, (i + 1) * sizeof(*newlist));
64 if (newlist == NULL)
65 return ENOMEM;
66
67 for (i = 0; src[i] != NULL; i++) {
68 newlist[i] = strdup(src[i]);
69 if (newlist[i] == NULL)
70 goto cleanup;
71 }
72 newlist[i] = NULL;
73 *dst = newlist;
74 return 0;
75 cleanup:
76 free_list(newlist);
77 return ENOMEM;
78 }
79
80 char *
idtype2string(int idtype)81 idtype2string(int idtype)
82 {
83 switch(idtype) {
84 case IDTYPE_FILE: return "FILE"; break;
85 case IDTYPE_DIR: return "DIR"; break;
86 case IDTYPE_PKCS11: return "PKCS11"; break;
87 case IDTYPE_PKCS12: return "PKCS12"; break;
88 case IDTYPE_ENVVAR: return "ENV"; break;
89 default: return "INVALID"; break;
90 }
91 }
92
93 char *
catype2string(int catype)94 catype2string(int catype)
95 {
96 switch(catype) {
97 case CATYPE_ANCHORS: return "ANCHORS"; break;
98 case CATYPE_INTERMEDIATES: return "INTERMEDIATES"; break;
99 case CATYPE_CRLS: return "CRLS"; break;
100 default: return "INVALID"; break;
101 }
102 }
103
104 krb5_error_code
pkinit_init_identity_opts(pkinit_identity_opts ** idopts)105 pkinit_init_identity_opts(pkinit_identity_opts **idopts)
106 {
107 pkinit_identity_opts *opts = NULL;
108
109 *idopts = NULL;
110 opts = calloc(1, sizeof(pkinit_identity_opts));
111 if (opts == NULL)
112 return ENOMEM;
113
114 opts->identity = NULL;
115 opts->anchors = NULL;
116 opts->intermediates = NULL;
117 opts->crls = NULL;
118
119 opts->cert_filename = NULL;
120 opts->key_filename = NULL;
121 #ifndef WITHOUT_PKCS11
122 opts->p11_module_name = NULL;
123 opts->slotid = PK_NOSLOT;
124 opts->token_label = NULL;
125 opts->cert_id_string = NULL;
126 opts->cert_label = NULL;
127 #endif
128
129 *idopts = opts;
130
131 return 0;
132 }
133
134 krb5_error_code
pkinit_dup_identity_opts(pkinit_identity_opts * src_opts,pkinit_identity_opts ** dest_opts)135 pkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
136 pkinit_identity_opts **dest_opts)
137 {
138 pkinit_identity_opts *newopts;
139 krb5_error_code retval;
140
141 *dest_opts = NULL;
142 retval = pkinit_init_identity_opts(&newopts);
143 if (retval)
144 return retval;
145
146 retval = ENOMEM;
147
148 if (src_opts->identity != NULL) {
149 newopts->identity = strdup(src_opts->identity);
150 if (newopts->identity == NULL)
151 goto cleanup;
152 }
153
154 retval = copy_list(&newopts->anchors, src_opts->anchors);
155 if (retval)
156 goto cleanup;
157
158 retval = copy_list(&newopts->intermediates,src_opts->intermediates);
159 if (retval)
160 goto cleanup;
161
162 retval = copy_list(&newopts->crls, src_opts->crls);
163 if (retval)
164 goto cleanup;
165
166 if (src_opts->cert_filename != NULL) {
167 newopts->cert_filename = strdup(src_opts->cert_filename);
168 if (newopts->cert_filename == NULL)
169 goto cleanup;
170 }
171
172 if (src_opts->key_filename != NULL) {
173 newopts->key_filename = strdup(src_opts->key_filename);
174 if (newopts->key_filename == NULL)
175 goto cleanup;
176 }
177
178 #ifndef WITHOUT_PKCS11
179 if (src_opts->p11_module_name != NULL) {
180 newopts->p11_module_name = strdup(src_opts->p11_module_name);
181 if (newopts->p11_module_name == NULL)
182 goto cleanup;
183 }
184
185 newopts->slotid = src_opts->slotid;
186
187 if (src_opts->token_label != NULL) {
188 newopts->token_label = strdup(src_opts->token_label);
189 if (newopts->token_label == NULL)
190 goto cleanup;
191 }
192
193 if (src_opts->cert_id_string != NULL) {
194 newopts->cert_id_string = strdup(src_opts->cert_id_string);
195 if (newopts->cert_id_string == NULL)
196 goto cleanup;
197 }
198
199 if (src_opts->cert_label != NULL) {
200 newopts->cert_label = strdup(src_opts->cert_label);
201 if (newopts->cert_label == NULL)
202 goto cleanup;
203 }
204 #endif
205
206
207 *dest_opts = newopts;
208 return 0;
209 cleanup:
210 pkinit_fini_identity_opts(newopts);
211 return retval;
212 }
213
214 void
pkinit_fini_identity_opts(pkinit_identity_opts * idopts)215 pkinit_fini_identity_opts(pkinit_identity_opts *idopts)
216 {
217 if (idopts == NULL)
218 return;
219
220 if (idopts->identity != NULL)
221 free(idopts->identity);
222 free_list(idopts->anchors);
223 free_list(idopts->intermediates);
224 free_list(idopts->crls);
225 free_list(idopts->identity_alt);
226
227 free(idopts->cert_filename);
228 free(idopts->key_filename);
229 #ifndef WITHOUT_PKCS11
230 free(idopts->p11_module_name);
231 free(idopts->token_label);
232 free(idopts->cert_id_string);
233 free(idopts->cert_label);
234 #endif
235 free(idopts);
236 }
237
238 #ifndef WITHOUT_PKCS11
239 static krb5_error_code
parse_pkcs11_options(krb5_context context,pkinit_identity_opts * idopts,const char * residual)240 parse_pkcs11_options(krb5_context context,
241 pkinit_identity_opts *idopts,
242 const char *residual)
243 {
244 char *s, *cp, *vp, *save;
245 krb5_error_code retval = ENOMEM;
246
247 if (residual == NULL || residual[0] == '\0')
248 return 0;
249
250 /* Split string into attr=value substrings */
251 s = strdup(residual);
252 if (s == NULL)
253 return retval;
254
255 for (cp = strtok_r(s, ":", &save); cp; cp = strtok_r(NULL, ":", &save)) {
256 vp = strchr(cp, '=');
257
258 /* If there is no "=", this is a pkcs11 module name */
259 if (vp == NULL) {
260 free(idopts->p11_module_name);
261 idopts->p11_module_name = strdup(cp);
262 if (idopts->p11_module_name == NULL)
263 goto cleanup;
264 continue;
265 }
266 *vp++ = '\0';
267 if (!strcmp(cp, "module_name")) {
268 free(idopts->p11_module_name);
269 idopts->p11_module_name = strdup(vp);
270 if (idopts->p11_module_name == NULL)
271 goto cleanup;
272 } else if (!strcmp(cp, "slotid")) {
273 long slotid = strtol(vp, NULL, 10);
274 if ((slotid == LONG_MIN || slotid == LONG_MAX) && errno != 0) {
275 retval = EINVAL;
276 goto cleanup;
277 }
278 if ((long) (int) slotid != slotid) {
279 retval = EINVAL;
280 goto cleanup;
281 }
282 idopts->slotid = slotid;
283 } else if (!strcmp(cp, "token")) {
284 free(idopts->token_label);
285 idopts->token_label = strdup(vp);
286 if (idopts->token_label == NULL)
287 goto cleanup;
288 } else if (!strcmp(cp, "certid")) {
289 free(idopts->cert_id_string);
290 idopts->cert_id_string = strdup(vp);
291 if (idopts->cert_id_string == NULL)
292 goto cleanup;
293 } else if (!strcmp(cp, "certlabel")) {
294 free(idopts->cert_label);
295 idopts->cert_label = strdup(vp);
296 if (idopts->cert_label == NULL)
297 goto cleanup;
298 }
299 }
300 retval = 0;
301 cleanup:
302 free(s);
303 return retval;
304 }
305 #endif
306
307 static krb5_error_code
parse_fs_options(krb5_context context,pkinit_identity_opts * idopts,const char * residual)308 parse_fs_options(krb5_context context,
309 pkinit_identity_opts *idopts,
310 const char *residual)
311 {
312 char *certname, *keyname, *save;
313 char *copy = NULL, *cert_filename = NULL, *key_filename = NULL;
314 krb5_error_code retval = ENOMEM;
315
316 if (residual == NULL || residual[0] == '\0' || residual[0] == ',')
317 return EINVAL;
318
319 copy = strdup(residual);
320 if (copy == NULL)
321 goto cleanup;
322
323 certname = strtok_r(copy, ",", &save);
324 if (certname == NULL)
325 goto cleanup;
326 keyname = strtok_r(NULL, ",", &save);
327
328 cert_filename = strdup(certname);
329 if (cert_filename == NULL)
330 goto cleanup;
331
332 key_filename = strdup((keyname != NULL) ? keyname : certname);
333 if (key_filename == NULL)
334 goto cleanup;
335
336 free(idopts->cert_filename);
337 free(idopts->key_filename);
338 idopts->cert_filename = cert_filename;
339 idopts->key_filename = key_filename;
340 cert_filename = key_filename = NULL;
341 retval = 0;
342
343 cleanup:
344 free(copy);
345 free(cert_filename);
346 free(key_filename);
347 return retval;
348 }
349
350 static krb5_error_code
parse_pkcs12_options(krb5_context context,pkinit_identity_opts * idopts,const char * residual)351 parse_pkcs12_options(krb5_context context,
352 pkinit_identity_opts *idopts,
353 const char *residual)
354 {
355 krb5_error_code retval = ENOMEM;
356
357 if (residual == NULL || residual[0] == '\0')
358 return 0;
359
360 free(idopts->cert_filename);
361 idopts->cert_filename = strdup(residual);
362 if (idopts->cert_filename == NULL)
363 goto cleanup;
364
365 free(idopts->key_filename);
366 idopts->key_filename = strdup(residual);
367 if (idopts->key_filename == NULL)
368 goto cleanup;
369
370 pkiDebug("%s: cert_filename '%s' key_filename '%s'\n",
371 __FUNCTION__, idopts->cert_filename,
372 idopts->key_filename);
373 retval = 0;
374 cleanup:
375 return retval;
376 }
377
378 static krb5_error_code
process_option_identity(krb5_context context,pkinit_plg_crypto_context plg_cryptoctx,pkinit_req_crypto_context req_cryptoctx,pkinit_identity_opts * idopts,pkinit_identity_crypto_context id_cryptoctx,krb5_principal princ,const char * value)379 process_option_identity(krb5_context context,
380 pkinit_plg_crypto_context plg_cryptoctx,
381 pkinit_req_crypto_context req_cryptoctx,
382 pkinit_identity_opts *idopts,
383 pkinit_identity_crypto_context id_cryptoctx,
384 krb5_principal princ, const char *value)
385 {
386 const char *residual;
387 int idtype;
388 krb5_error_code retval = 0;
389
390 TRACE_PKINIT_IDENTITY_OPTION(context, value);
391 if (value == NULL)
392 return EINVAL;
393
394 residual = strchr(value, ':');
395 if (residual != NULL) {
396 unsigned int typelen;
397 residual++; /* skip past colon */
398 typelen = residual - value;
399 if (strncmp(value, "FILE:", typelen) == 0) {
400 idtype = IDTYPE_FILE;
401 #ifndef WITHOUT_PKCS11
402 } else if (strncmp(value, "PKCS11:", typelen) == 0) {
403 idtype = IDTYPE_PKCS11;
404 #endif
405 } else if (strncmp(value, "PKCS12:", typelen) == 0) {
406 idtype = IDTYPE_PKCS12;
407 } else if (strncmp(value, "DIR:", typelen) == 0) {
408 idtype = IDTYPE_DIR;
409 } else if (strncmp(value, "ENV:", typelen) == 0) {
410 idtype = IDTYPE_ENVVAR;
411 } else {
412 pkiDebug("%s: Unsupported type while processing '%s'\n",
413 __FUNCTION__, value);
414 krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
415 _("Unsupported type while processing "
416 "'%s'\n"), value);
417 return KRB5_PREAUTH_FAILED;
418 }
419 } else {
420 idtype = IDTYPE_FILE;
421 residual = value;
422 }
423
424 idopts->idtype = idtype;
425 pkiDebug("%s: idtype is %s\n", __FUNCTION__, idtype2string(idopts->idtype));
426 switch (idtype) {
427 case IDTYPE_ENVVAR:
428 return process_option_identity(context, plg_cryptoctx, req_cryptoctx,
429 idopts, id_cryptoctx, princ,
430 secure_getenv(residual));
431 break;
432 case IDTYPE_FILE:
433 retval = parse_fs_options(context, idopts, residual);
434 break;
435 case IDTYPE_PKCS12:
436 retval = parse_pkcs12_options(context, idopts, residual);
437 break;
438 #ifndef WITHOUT_PKCS11
439 case IDTYPE_PKCS11:
440 retval = parse_pkcs11_options(context, idopts, residual);
441 break;
442 #endif
443 case IDTYPE_DIR:
444 free(idopts->cert_filename);
445 idopts->cert_filename = strdup(residual);
446 if (idopts->cert_filename == NULL)
447 retval = ENOMEM;
448 break;
449 default:
450 krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
451 _("Internal error parsing "
452 "X509_user_identity\n"));
453 retval = EINVAL;
454 break;
455 }
456 if (retval)
457 return retval;
458
459 retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx, idopts,
460 id_cryptoctx, princ, TRUE);
461 if (retval)
462 return retval;
463
464 crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx);
465 return 0;
466 }
467
468 static krb5_error_code
process_option_ca_crl(krb5_context context,pkinit_plg_crypto_context plg_cryptoctx,pkinit_req_crypto_context req_cryptoctx,pkinit_identity_opts * idopts,pkinit_identity_crypto_context id_cryptoctx,const char * value,int catype)469 process_option_ca_crl(krb5_context context,
470 pkinit_plg_crypto_context plg_cryptoctx,
471 pkinit_req_crypto_context req_cryptoctx,
472 pkinit_identity_opts *idopts,
473 pkinit_identity_crypto_context id_cryptoctx,
474 const char *value,
475 int catype)
476 {
477 char *residual;
478 unsigned int typelen;
479 int idtype;
480
481 pkiDebug("%s: processing catype %s, value '%s'\n",
482 __FUNCTION__, catype2string(catype), value);
483 residual = strchr(value, ':');
484 if (residual == NULL) {
485 pkiDebug("No type given for '%s'\n", value);
486 return EINVAL;
487 }
488 residual++; /* skip past colon */
489 typelen = residual - value;
490 if (strncmp(value, "FILE:", typelen) == 0) {
491 idtype = IDTYPE_FILE;
492 } else if (strncmp(value, "DIR:", typelen) == 0) {
493 idtype = IDTYPE_DIR;
494 } else {
495 return ENOTSUP;
496 }
497 return crypto_load_cas_and_crls(context,
498 plg_cryptoctx,
499 req_cryptoctx,
500 idopts, id_cryptoctx,
501 idtype, catype, residual);
502 }
503
504 /*
505 * Load any identity information which doesn't require us to ask a controlling
506 * user any questions, and record the names of anything else which would
507 * require us to ask questions.
508 */
509 krb5_error_code
pkinit_identity_initialize(krb5_context context,pkinit_plg_crypto_context plg_cryptoctx,pkinit_req_crypto_context req_cryptoctx,pkinit_identity_opts * idopts,pkinit_identity_crypto_context id_cryptoctx,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,krb5_principal princ)510 pkinit_identity_initialize(krb5_context context,
511 pkinit_plg_crypto_context plg_cryptoctx,
512 pkinit_req_crypto_context req_cryptoctx,
513 pkinit_identity_opts *idopts,
514 pkinit_identity_crypto_context id_cryptoctx,
515 krb5_clpreauth_callbacks cb,
516 krb5_clpreauth_rock rock,
517 krb5_principal princ)
518 {
519 krb5_error_code retval = EINVAL;
520 int i;
521
522 pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx);
523 if (!(princ &&
524 krb5_principal_compare_any_realm(context, princ,
525 krb5_anonymous_principal()))) {
526 if (idopts == NULL || id_cryptoctx == NULL)
527 goto errout;
528
529 /*
530 * If identity was specified, use that. (For the kdc, this
531 * is specified as pkinit_identity in the kdc.conf. For users,
532 * this is specified on the command line via X509_user_identity.)
533 * If a user did not specify identity on the command line,
534 * then we will try alternatives which may have been specified
535 * in the config file.
536 */
537 if (idopts->identity != NULL) {
538 retval = process_option_identity(context, plg_cryptoctx,
539 req_cryptoctx, idopts,
540 id_cryptoctx, princ,
541 idopts->identity);
542 } else if (idopts->identity_alt != NULL) {
543 for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++) {
544 retval = process_option_identity(context, plg_cryptoctx,
545 req_cryptoctx, idopts,
546 id_cryptoctx, princ,
547 idopts->identity_alt[i]);
548 }
549 } else {
550 retval = KRB5_PREAUTH_FAILED;
551 krb5_set_error_message(context, retval,
552 _("No user identity options specified"));
553 pkiDebug("%s: no user identity options specified\n", __FUNCTION__);
554 goto errout;
555 }
556 } else {
557 /* We're the anonymous principal. */
558 retval = 0;
559 }
560
561 errout:
562 return retval;
563 }
564
565 /*
566 * Load identity information, including that which requires us to ask a
567 * controlling user any questions. If we have PIN/password values which
568 * correspond to a given identity, use that, otherwise, if one is available,
569 * we'll use the prompter callback.
570 */
571 krb5_error_code
pkinit_identity_prompt(krb5_context context,pkinit_plg_crypto_context plg_cryptoctx,pkinit_req_crypto_context req_cryptoctx,pkinit_identity_opts * idopts,pkinit_identity_crypto_context id_cryptoctx,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,int do_matching,krb5_principal princ)572 pkinit_identity_prompt(krb5_context context,
573 pkinit_plg_crypto_context plg_cryptoctx,
574 pkinit_req_crypto_context req_cryptoctx,
575 pkinit_identity_opts *idopts,
576 pkinit_identity_crypto_context id_cryptoctx,
577 krb5_clpreauth_callbacks cb,
578 krb5_clpreauth_rock rock,
579 int do_matching,
580 krb5_principal princ)
581 {
582 krb5_error_code retval = 0;
583 const char *signer_identity;
584 krb5_boolean valid;
585 int i;
586
587 pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx);
588 if (!(princ &&
589 krb5_principal_compare_any_realm(context, princ,
590 krb5_anonymous_principal()))) {
591 retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx,
592 idopts, id_cryptoctx, princ, FALSE);
593 if (retval)
594 goto errout;
595
596 if (do_matching) {
597 /*
598 * Try to select exactly one certificate based on matching
599 * criteria. Typical used for clients.
600 */
601 retval = pkinit_cert_matching(context, plg_cryptoctx,
602 req_cryptoctx, id_cryptoctx, princ);
603 if (retval) {
604 crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
605 id_cryptoctx);
606 goto errout;
607 }
608 } else {
609 /*
610 * Tell crypto code to use the "default" identity. Typically used
611 * for KDCs.
612 */
613 retval = crypto_cert_select_default(context, plg_cryptoctx,
614 req_cryptoctx, id_cryptoctx);
615 if (retval) {
616 crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
617 id_cryptoctx);
618 goto errout;
619 }
620 }
621
622 if (rock != NULL && cb != NULL && retval == 0) {
623 /* Save the signer identity if we're the client. */
624 if (crypto_retrieve_signer_identity(context, id_cryptoctx,
625 &signer_identity) == 0) {
626 cb->set_cc_config(context, rock, "X509_user_identity",
627 signer_identity);
628 }
629 }
630
631 retval = crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
632 id_cryptoctx);
633 if (retval)
634 goto errout;
635 } /* Not anonymous principal */
636
637 /* Require at least one successful anchor if any are specified. */
638 valid = FALSE;
639 for (i = 0; idopts->anchors != NULL && idopts->anchors[i] != NULL; i++) {
640 retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx,
641 idopts, id_cryptoctx,
642 idopts->anchors[i], CATYPE_ANCHORS);
643 if (!retval)
644 valid = TRUE;
645 }
646 if (retval && !valid)
647 goto errout;
648 krb5_clear_error_message(context);
649 retval = 0;
650
651 /* Require at least one successful intermediate if any are specified. */
652 valid = FALSE;
653 for (i = 0; idopts->intermediates != NULL
654 && idopts->intermediates[i] != NULL; i++) {
655 retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx,
656 idopts, id_cryptoctx,
657 idopts->intermediates[i],
658 CATYPE_INTERMEDIATES);
659 if (!retval)
660 valid = TRUE;
661 }
662 if (retval && !valid)
663 goto errout;
664 krb5_clear_error_message(context);
665 retval = 0;
666
667 for (i = 0; idopts->crls != NULL && idopts->crls[i] != NULL; i++) {
668 retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx,
669 idopts, id_cryptoctx, idopts->crls[i],
670 CATYPE_CRLS);
671 if (retval)
672 goto errout;
673 }
674
675 errout:
676 return retval;
677 }
678
679 /*
680 * Create an entry in the passed-in list for the named identity, optionally
681 * with the specified token flag value and/or supplied password, replacing any
682 * existing entry with the same identity name.
683 */
684 krb5_error_code
pkinit_set_deferred_id(pkinit_deferred_id ** identities,const char * identity,unsigned long ck_flags,const char * password)685 pkinit_set_deferred_id(pkinit_deferred_id **identities,
686 const char *identity, unsigned long ck_flags,
687 const char *password)
688 {
689 int i;
690 pkinit_deferred_id *out = NULL, *ids;
691 char *tmp;
692
693 /* Search for an entry that's already in the list. */
694 ids = *identities;
695 for (i = 0; ids != NULL && ids[i] != NULL; i++) {
696 if (strcmp(ids[i]->identity, identity) == 0) {
697 /* Replace its password value, then we're done. */
698 tmp = password ? strdup(password) : NULL;
699 if (password != NULL && tmp == NULL)
700 return ENOMEM;
701 ids[i]->ck_flags = ck_flags;
702 free(ids[i]->password);
703 ids[i]->password = tmp;
704 return 0;
705 }
706 }
707
708 /* Resize the list. */
709 out = realloc(ids, sizeof(*ids) * (i + 2));
710 if (out == NULL)
711 goto oom;
712 *identities = out;
713
714 /* Allocate the new final entry. */
715 out[i] = malloc(sizeof(*(out[i])));
716 if (out[i] == NULL)
717 goto oom;
718
719 /* Populate the new entry. */
720 out[i]->magic = PKINIT_DEFERRED_ID_MAGIC;
721 out[i]->identity = strdup(identity);
722 if (out[i]->identity == NULL)
723 goto oom;
724
725 out[i]->ck_flags = ck_flags;
726 out[i]->password = password ? strdup(password) : NULL;
727 if (password != NULL && out[i]->password == NULL)
728 goto oom;
729
730 /* Terminate the list. */
731 out[i + 1] = NULL;
732 return 0;
733
734 oom:
735 if (out != NULL && out[i] != NULL) {
736 free(out[i]->identity);
737 free(out[i]);
738 out[i] = NULL;
739 }
740 return ENOMEM;
741 }
742
743 /*
744 * Return a password which we've associated with the named identity, if we've
745 * stored one. Otherwise return NULL.
746 */
747 const char *
pkinit_find_deferred_id(pkinit_deferred_id * identities,const char * identity)748 pkinit_find_deferred_id(pkinit_deferred_id *identities,
749 const char *identity)
750 {
751 int i;
752
753 for (i = 0; identities != NULL && identities[i] != NULL; i++) {
754 if (strcmp(identities[i]->identity, identity) == 0)
755 return identities[i]->password;
756 }
757 return NULL;
758 }
759
760 /*
761 * Return the flags associated with the specified identity, or 0 if we don't
762 * have such an identity.
763 */
764 unsigned long
pkinit_get_deferred_id_flags(pkinit_deferred_id * identities,const char * identity)765 pkinit_get_deferred_id_flags(pkinit_deferred_id *identities,
766 const char *identity)
767 {
768 int i;
769
770 for (i = 0; identities != NULL && identities[i] != NULL; i++) {
771 if (strcmp(identities[i]->identity, identity) == 0)
772 return identities[i]->ck_flags;
773 }
774 return 0;
775 }
776
777 /*
778 * Free a deferred_id list.
779 */
780 void
pkinit_free_deferred_ids(pkinit_deferred_id * identities)781 pkinit_free_deferred_ids(pkinit_deferred_id *identities)
782 {
783 int i;
784
785 for (i = 0; identities != NULL && identities[i] != NULL; i++) {
786 free(identities[i]->identity);
787 free(identities[i]->password);
788 free(identities[i]);
789 }
790 free(identities);
791 }
792