xref: /freebsd/crypto/krb5/src/plugins/preauth/pkinit/pkinit_identity.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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