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