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