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