xref: /illumos-gate/usr/src/lib/krb5/plugins/preauth/pkinit/pkinit_matching.c (revision 9164a50bf932130cbb5097a16f6986873ce0e6e5)
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 <unistd.h>
40 #include <regex.h>
41 #include <krb5.h>
42 #include "pkinit.h"
43 
44 typedef struct _pkinit_cert_info pkinit_cert_info;
45 
46 typedef enum {
47     kw_undefined = 0,
48     kw_subject = 1,
49     kw_issuer = 2,
50     kw_san = 3,
51     kw_eku = 4,
52     kw_ku = 5
53 } keyword_type;
54 
55 static char *
56 keyword2string(unsigned int kw)
57 {
58     /* Solaris Kerberos: removed "break"s (lint) */
59     switch(kw) {
60     case kw_undefined: return "NONE";
61     case kw_subject: return "SUBJECT";
62     case kw_issuer: return "ISSUER";
63     case kw_san: return "SAN";
64     case kw_eku: return "EKU";
65     case kw_ku: return "KU";
66     default: return "INVALID";
67     }
68 }
69 typedef enum {
70     relation_none = 0,
71     relation_and = 1,
72     relation_or = 2
73 } relation_type;
74 
75 static char *
76 relation2string(unsigned int rel)
77 {
78     /* Solaris Kerberos: removed "break"s (lint) */
79     switch(rel) {
80     case relation_none: return "NONE";
81     case relation_and: return "AND";
82     case relation_or: return "OR";
83     default: return "INVALID";
84     }
85 }
86 
87 typedef enum {
88     kwvaltype_undefined = 0,
89     kwvaltype_regexp = 1,
90     kwvaltype_list = 2
91 } kw_value_type;
92 
93 static char *
94 kwval2string(unsigned int kwval)
95 {
96     /* Solaris Kerberos: removed "break"s (lint) */
97     switch(kwval) {
98     case kwvaltype_undefined: return "NONE";
99     case kwvaltype_regexp: return "REGEXP";
100     case kwvaltype_list: return "LIST";
101     default: return "INVALID";
102     }
103 }
104 
105 struct keyword_desc {
106     const char *value;
107     size_t length;
108     keyword_type kwtype;
109     kw_value_type kwvaltype;
110 } matching_keywords[] = {
111     { "<KU>",	    4, kw_ku, kwvaltype_list },
112     { "<EKU>",	    5, kw_eku, kwvaltype_list },
113     { "<SAN>",	    5, kw_san, kwvaltype_regexp },
114     { "<ISSUER>",   8, kw_issuer, kwvaltype_regexp },
115     { "<SUBJECT>",  9, kw_subject, kwvaltype_regexp },
116     { NULL, 0, kw_undefined, kwvaltype_undefined},
117 };
118 
119 struct ku_desc {
120     const char *value;
121     size_t length;
122     unsigned int bitval;
123 };
124 
125 struct ku_desc ku_keywords[] = {
126     { "digitalSignature",   16, PKINIT_KU_DIGITALSIGNATURE },
127     { "keyEncipherment",    15, PKINIT_KU_KEYENCIPHERMENT },
128     { NULL, 0, 0 },
129 };
130 
131 struct ku_desc  eku_keywords[] = {
132     { "pkinit",		    6,  PKINIT_EKU_PKINIT },
133     { "msScLogin",	    9,  PKINIT_EKU_MSSCLOGIN },
134     { "clientAuth",	    10, PKINIT_EKU_CLIENTAUTH },
135     { "emailProtection",    15, PKINIT_EKU_EMAILPROTECTION },
136     { NULL, 0, 0 },
137 };
138 
139 /* Rule component */
140 typedef struct _rule_component {
141     struct _rule_component *next;
142     keyword_type kw_type;
143     kw_value_type kwval_type;
144     regex_t regexp;	    /* Compiled regular expression */
145     char *regsrc;	    /* The regular expression source (for debugging) */
146     unsigned int ku_bits;
147     unsigned int eku_bits;
148 } rule_component;
149 
150 /* Set rule components */
151 typedef struct _rule_set {
152     relation_type relation;
153     int num_crs;
154     rule_component *crs;
155 } rule_set;
156 
157 /* ARGSUSED */
158 static krb5_error_code
159 free_rule_component(krb5_context context,
160 		    rule_component *rc)
161 {
162     if (rc == NULL)
163 	return 0;
164 
165     if (rc->kwval_type == kwvaltype_regexp) {
166 	if (rc->regsrc)
167 	    free(rc->regsrc);
168 	regfree(&rc->regexp);
169     }
170     free(rc);
171     return 0;
172 }
173 
174 static krb5_error_code
175 free_rule_set(krb5_context context,
176 	      rule_set *rs)
177 {
178     rule_component *rc, *trc;
179 
180     if (rs == NULL)
181 	return 0;
182     for (rc = rs->crs; rc != NULL;) {
183 	trc = rc->next;
184 	/* Solaris Kerberos */
185 	(void) free_rule_component(context, rc);
186 	rc = trc;
187     }
188     free(rs);
189     return 0;
190 }
191 
192 /* ARGSUSED */
193 static krb5_error_code
194 parse_list_value(krb5_context context,
195 		 keyword_type type,
196 		 char *value,
197 		 rule_component *rc)
198 {
199     krb5_error_code retval;
200     char *comma;
201     struct ku_desc *ku = NULL;
202     int found;
203     size_t len;
204     unsigned int *bitptr;
205 
206 
207     if (value == NULL || value[0] == '\0') {
208 	pkiDebug("%s: Missing or empty value for list keyword type %d\n",
209 		 __FUNCTION__, type);
210 	retval = EINVAL;
211 	goto out;
212     }
213 
214     if (type == kw_eku) {
215 	bitptr = &rc->eku_bits;
216     } else if (type == kw_ku) {
217 	bitptr = &rc->ku_bits;
218     } else {
219 	pkiDebug("%s: Unknown list keyword type %d\n", __FUNCTION__, type);
220 	retval = EINVAL;
221 	goto out;
222     }
223 
224     do {
225 	found = 0;
226 	comma = strchr(value, ',');
227 	if (comma != NULL)
228 	    len = comma - value;
229 	else
230 	    len = strlen(value);
231 
232 	if (type == kw_eku) {
233 	    ku = eku_keywords;
234 	} else if (type == kw_ku) {
235 	    ku = ku_keywords;
236 	}
237 
238 	for (; ku->value != NULL; ku++) {
239 	    if (strncasecmp(value, ku->value, len) == 0) {
240 		*bitptr |= ku->bitval;
241 		found = 1;
242 		pkiDebug("%s: Found value '%s', bitfield is now 0x%x\n",
243 			 __FUNCTION__, ku->value, *bitptr);
244 		break;
245 	    }
246 	}
247 	if (found) {
248 	    value += ku->length;
249 	    if (*value == ',')
250 		value += 1;
251 	} else {
252 	    pkiDebug("%s: Urecognized value '%s'\n", __FUNCTION__, value);
253 	    retval = EINVAL;
254 	    goto out;
255 	}
256     } while (found && *value != '\0');
257 
258     retval = 0;
259 out:
260     pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
261     return retval;
262 }
263 
264 static krb5_error_code
265 parse_rule_component(krb5_context context,
266 		     const char **rule,
267 		     int *remaining,
268 		     rule_component **ret_rule)
269 {
270     krb5_error_code retval;
271     rule_component *rc = NULL;
272     keyword_type kw_type;
273     kw_value_type kwval_type;
274     char err_buf[128];
275     int ret;
276     struct keyword_desc *kw, *nextkw;
277     char *nk;
278     int found_next_kw = 0;
279     char *value = NULL;
280     size_t len;
281 
282     for (kw = matching_keywords; kw->value != NULL; kw++) {
283 	if (strncmp(*rule, kw->value, kw->length) == 0) {
284 	    kw_type = kw->kwtype;
285 	    kwval_type = kw->kwvaltype;
286 	    *rule += kw->length;
287 	    *remaining -= kw->length;
288 	    break;
289 	}
290     }
291     if (kw->value == NULL) {
292 	pkiDebug("%s: Missing or invalid keyword in rule '%s'\n",
293 		 __FUNCTION__, *rule);
294 	retval = ENOENT;
295 	goto out;
296     }
297 
298     pkiDebug("%s: found keyword '%s'\n", __FUNCTION__, kw->value);
299 
300     rc = calloc(1, sizeof(*rc));
301     if (rc == NULL) {
302 	retval = ENOMEM;
303 	goto out;
304     }
305     rc->next = NULL;
306     rc->kw_type = kw_type;
307     rc->kwval_type = kwval_type;
308 
309     /*
310      * Before procesing the value for this keyword,
311      * (compiling the regular expression or processing the list)
312      * we need to find the end of it.  That means parsing for the
313      * beginning of the next keyword (or the end of the rule).
314      */
315     nk = strchr(*rule, '<');
316     while (nk != NULL) {
317 	/* Possibly another keyword, check it out */
318 	for (nextkw = matching_keywords; nextkw->value != NULL; nextkw++) {
319 	    if (strncmp(nk, nextkw->value, nextkw->length) == 0) {
320 		/* Found a keyword, nk points to the beginning */
321 		found_next_kw = 1;
322 		break;	/* Need to break out of the while! */
323 	    }
324 	}
325 	if (!found_next_kw)
326 	    nk = strchr(nk+1, '<');	/* keep looking */
327 	else
328 	    break;
329     }
330 
331     if (nk != NULL && found_next_kw)
332 	len = (nk - *rule);
333     else
334 	len = (*remaining);
335 
336     if (len == 0) {
337 	pkiDebug("%s: Missing value for keyword '%s'\n",
338 		 __FUNCTION__, kw->value);
339 	retval = EINVAL;
340 	goto out;
341     }
342 
343     value = calloc(1, len+1);
344     if (value == NULL) {
345 	retval = ENOMEM;
346 	goto out;
347     }
348     (void) memcpy(value, *rule, len);
349     *remaining -= len;
350     *rule += len;
351     pkiDebug("%s: found value '%s'\n", __FUNCTION__, value);
352 
353     if (kw->kwvaltype == kwvaltype_regexp) {
354 	ret = regcomp(&rc->regexp, value, REG_EXTENDED);
355 	if (ret) {
356 	    (void) regerror(ret, &rc->regexp, err_buf, sizeof(err_buf));
357 	    pkiDebug("%s: Error compiling reg-exp '%s': %s\n",
358 		     __FUNCTION__, value, err_buf);
359 	    retval = ret;
360 	    goto out;
361 	}
362 	rc->regsrc = strdup(value);
363 	if (rc->regsrc == NULL) {
364 	    retval = ENOMEM;
365 	    goto out;
366 	}
367     } else if (kw->kwvaltype == kwvaltype_list) {
368 	retval = parse_list_value(context, rc->kw_type, value, rc);
369 	if (retval) {
370 	    pkiDebug("%s: Error %d, parsing list values for keyword %s\n",
371 		     __FUNCTION__, retval, kw->value);
372 	    goto out;
373 	}
374     }
375 
376     *ret_rule = rc;
377     retval = 0;
378 out:
379     if (value != NULL)
380 	free(value);
381     if (retval && rc != NULL)
382 	(void) free_rule_component(context, rc);
383     pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
384     return retval;
385 }
386 
387 /* ARGSUSED */
388 static krb5_error_code
389 parse_rule_set(krb5_context context,
390 	       const char *rule_in,
391 	       rule_set **out_rs)
392 {
393     const char *rule;
394     /* Solaris Kerberos */
395     int remaining;
396     krb5_error_code ret, retval;
397     rule_component *rc = NULL, *trc;
398     rule_set *rs;
399 
400 
401     if (rule_in == NULL)
402 	return EINVAL;
403     rule = rule_in;
404     /* Solaris Kerberos */
405     remaining = strlen(rule);
406 
407     rs = calloc(1, sizeof(*rs));
408     if (rs == NULL) {
409 	retval = ENOMEM;
410 	goto cleanup;
411     }
412 
413     rs->relation = relation_none;
414     if (remaining > 1) {
415 	if (rule[0] == '&' && rule[1] == '&') {
416 	    rs->relation = relation_and;
417 	    rule += 2;
418 	    remaining -= 2;
419 	} else if (rule_in[0] == '|' && rule_in[1] == '|') {
420 	    rs->relation = relation_or;
421 	    rule +=2;
422 	    remaining -= 2;
423 	}
424     }
425     rs->num_crs = 0;
426     while (remaining > 0) {
427 	if (rs->relation == relation_none && rs->num_crs > 1) {
428 	    pkiDebug("%s: Assuming AND relation for multiple components in rule '%s'\n",
429 		     __FUNCTION__, rule_in);
430 	    rs->relation = relation_and;
431 	}
432 	ret = parse_rule_component(context, &rule, &remaining, &rc);
433 	if (ret) {
434 	    retval = ret;
435 	    goto cleanup;
436 	}
437 	pkiDebug("%s: After parse_rule_component, remaining %d, rule '%s'\n",
438 		 __FUNCTION__, remaining, rule);
439 	rs->num_crs++;
440 
441 	/*
442 	 * Chain the new component on the end (order matters since
443 	 * we can short-circuit an OR or an AND relation if an
444 	 * earlier check passes
445 	 */
446 	for (trc = rs->crs; trc != NULL && trc->next != NULL; trc = trc->next);
447 	if (trc == NULL)
448 	    rs->crs = rc;
449 	else {
450 	    trc->next = rc;
451 	}
452     }
453 
454     *out_rs = rs;
455 
456     retval = 0;
457 cleanup:
458     if (retval && rs != NULL) {
459 	(void) free_rule_set(context, rs);
460     }
461     pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
462     return retval;
463 }
464 
465 /* ARGSUSED */
466 static int
467 regexp_match(krb5_context context, rule_component *rc, char *value)
468 {
469     int code;
470 
471     pkiDebug("%s: checking %s rule '%s' with value '%s'\n",
472 	     __FUNCTION__, keyword2string(rc->kw_type), rc->regsrc, value);
473 
474     code = regexec(&rc->regexp, value, 0, NULL, 0);
475 
476     pkiDebug("%s: the result is%s a match\n", __FUNCTION__,
477 	     code == REG_NOMATCH ? " NOT" : "");
478 
479     return (code == 0 ? 1: 0);
480 }
481 
482 static int
483 component_match(krb5_context context,
484 		rule_component *rc,
485 		pkinit_cert_matching_data *md)
486 {
487     int match = 0;
488     int i;
489     krb5_principal p;
490     char *princ_string;
491 
492     switch (rc->kwval_type) {
493     case kwvaltype_regexp:
494 	switch (rc->kw_type) {
495 	case kw_subject:
496 	    match = regexp_match(context, rc, md->subject_dn);
497 	    break;
498 	case kw_issuer:
499 	    match = regexp_match(context, rc, md->issuer_dn);
500 	    break;
501 	case kw_san:
502 	    if (md->sans == NULL)
503 		break;
504 	    for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) {
505 		krb5_unparse_name(context, p, &princ_string);
506 		match = regexp_match(context, rc, princ_string);
507 		krb5_free_unparsed_name(context, princ_string);
508 		if (match)
509 		    break;
510 	    }
511 	    break;
512 	default:
513 	    pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
514 		     __FUNCTION__, keyword2string(rc->kw_type),
515 		     kwval2string(kwvaltype_regexp));
516 	    break;
517 	}
518 	break;
519     case kwvaltype_list:
520 	switch(rc->kw_type) {
521 	case kw_eku:
522 	    pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
523 		     __FUNCTION__, keyword2string(rc->kw_type),
524 		     rc->eku_bits, md->eku_bits);
525 	    if ((rc->eku_bits & md->eku_bits) == rc->eku_bits)
526 		match = 1;
527 	    break;
528 	case kw_ku:
529 	    pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
530 		     __FUNCTION__, keyword2string(rc->kw_type),
531 		     rc->ku_bits, md->ku_bits);
532 	    if ((rc->ku_bits & md->ku_bits) == rc->ku_bits)
533 		match = 1;
534 	    break;
535 	default:
536 	    pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
537 		     __FUNCTION__, keyword2string(rc->kw_type),
538 		     kwval2string(kwvaltype_regexp));
539 	    break;
540 	}
541 	break;
542     default:
543 	pkiDebug("%s: unknown keyword value type %d\n",
544 		 __FUNCTION__, rc->kwval_type);
545 	break;
546     }
547     pkiDebug("%s: returning match = %d\n", __FUNCTION__, match);
548     return match;
549 }
550 /*
551  * Returns match_found == 1 only if exactly one certificate matches
552  * the given rule
553  */
554 /* ARGSUSED */
555 static krb5_error_code
556 check_all_certs(krb5_context context,
557 		pkinit_plg_crypto_context plg_cryptoctx,
558 		pkinit_req_crypto_context req_cryptoctx,
559 		pkinit_identity_crypto_context id_cryptoctx,
560 		krb5_principal princ,
561 		rule_set *rs,	/* rule to check */
562 		pkinit_cert_matching_data **matchdata,
563 		int *match_found,
564 		pkinit_cert_matching_data **matching_cert)
565 {
566     krb5_error_code retval;
567     pkinit_cert_matching_data *md;
568     int i;
569     int comp_match = 0;
570     int total_cert_matches = 0;
571     rule_component *rc;
572     int certs_checked = 0;
573     pkinit_cert_matching_data *save_match = NULL;
574 
575     if (match_found == NULL || matching_cert == NULL)
576 	return EINVAL;
577 
578     *matching_cert = NULL;
579     *match_found = 0;
580 
581     pkiDebug("%s: matching rule relation is %s with %d components\n",
582 	     __FUNCTION__, relation2string(rs->relation), rs->num_crs);
583 
584     /*
585      * Loop through all the certs available and count
586      * how many match the rule
587      */
588     for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
589 	pkiDebug("%s: subject: '%s'\n", __FUNCTION__, md->subject_dn);
590 #if 0
591 	pkiDebug("%s: issuer:  '%s'\n", __FUNCTION__, md->subject_dn);
592 	for (j = 0, p = md->sans[j]; p != NULL; p = md->sans[++j]) {
593 	    char *san_string;
594 	    krb5_unparse_name(context, p, &san_string);
595 	    pkiDebug("%s: san: '%s'\n", __FUNCTION__, san_string);
596 	    krb5_free_unparsed_name(context, san_string);
597 	}
598 #endif
599 	certs_checked++;
600 	for (rc = rs->crs; rc != NULL; rc = rc->next) {
601 	    comp_match = component_match(context, rc, md);
602 	    if (comp_match) {
603 		pkiDebug("%s: match for keyword type %s\n",
604 			 __FUNCTION__, keyword2string(rc->kw_type));
605 	    }
606 	    if (comp_match && rs->relation == relation_or) {
607 		pkiDebug("%s: cert matches rule (OR relation)\n",
608 			 __FUNCTION__);
609 		total_cert_matches++;
610 		save_match = md;
611 		goto nextcert;
612 	    }
613 	    if (!comp_match && rs->relation == relation_and) {
614 		pkiDebug("%s: cert does not match rule (AND relation)\n",
615 			 __FUNCTION__);
616 		goto nextcert;
617 	    }
618 	}
619 	if (rc == NULL && comp_match) {
620 	    pkiDebug("%s: cert matches rule (AND relation)\n", __FUNCTION__);
621 	    total_cert_matches++;
622 	    save_match = md;
623 	}
624 nextcert:
625 	continue;
626     }
627     pkiDebug("%s: After checking %d certs, we found %d matches\n",
628 	     __FUNCTION__, certs_checked, total_cert_matches);
629     if (total_cert_matches == 1) {
630 	*match_found = 1;
631 	*matching_cert = save_match;
632     }
633 
634     retval = 0;
635 
636     pkiDebug("%s: returning %d, match_found %d\n",
637 	     __FUNCTION__, retval, *match_found);
638     return retval;
639 }
640 
641 static krb5_error_code
642 free_all_cert_matching_data(krb5_context context,
643 			    pkinit_cert_matching_data **matchdata)
644 {
645     krb5_error_code retval;
646     pkinit_cert_matching_data *md;
647     int i;
648 
649     if (matchdata == NULL)
650 	return EINVAL;
651 
652     for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
653 	pkinit_cert_handle ch = md->ch;
654 	retval = crypto_cert_free_matching_data(context, md);
655 	if (retval) {
656 	    pkiDebug("%s: crypto_cert_free_matching_data error %d, %s\n",
657 		     __FUNCTION__, retval, error_message(retval));
658 	    goto cleanup;
659 	}
660 	retval = crypto_cert_release(context, ch);
661 	if (retval) {
662 	    pkiDebug("%s: crypto_cert_release error %d, %s\n",
663 		     __FUNCTION__, retval, error_message(retval));
664 	    goto cleanup;
665 	}
666     }
667     free(matchdata);
668     retval = 0;
669 
670 cleanup:
671     return retval;
672 }
673 
674 static krb5_error_code
675 obtain_all_cert_matching_data(krb5_context context,
676 			      pkinit_plg_crypto_context plg_cryptoctx,
677 			      pkinit_req_crypto_context req_cryptoctx,
678 			      pkinit_identity_crypto_context id_cryptoctx,
679 			      pkinit_cert_matching_data ***all_matching_data)
680 {
681     krb5_error_code retval;
682     int i, cert_count;
683     pkinit_cert_iter_handle ih = NULL;
684     pkinit_cert_handle ch;
685     pkinit_cert_matching_data **matchdata = NULL;
686 
687     retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx,
688 				   id_cryptoctx, &cert_count);
689     if (retval) {
690 	pkiDebug("%s: crypto_cert_get_count error %d, %s\n",
691 		 __FUNCTION__, retval, error_message(retval));
692 	goto cleanup;
693     }
694 
695     pkiDebug("%s: crypto_cert_get_count says there are %d certs\n",
696 	      __FUNCTION__, cert_count);
697 
698     matchdata = calloc((size_t)cert_count + 1, sizeof(*matchdata));
699     if (matchdata == NULL)
700 	return ENOMEM;
701 
702     retval = crypto_cert_iteration_begin(context, plg_cryptoctx, req_cryptoctx,
703 					 id_cryptoctx, &ih);
704     if (retval) {
705 	pkiDebug("%s: crypto_cert_iteration_begin returned %d, %s\n",
706 		 __FUNCTION__, retval, error_message(retval));
707 	goto cleanup;
708     }
709 
710     for (i = 0; i < cert_count; i++) {
711 	retval = crypto_cert_iteration_next(context, ih, &ch);
712 	if (retval) {
713 	    if (retval == PKINIT_ITER_NO_MORE)
714 		pkiDebug("%s: We thought there were %d certs, but "
715 			 "crypto_cert_iteration_next stopped after %d?\n",
716 			 __FUNCTION__, cert_count, i);
717 	    else
718 		pkiDebug("%s: crypto_cert_iteration_next error %d, %s\n",
719 			 __FUNCTION__, retval, error_message(retval));
720 	    goto cleanup;
721 	}
722 
723 	retval = crypto_cert_get_matching_data(context, ch, &matchdata[i]);
724 	if (retval) {
725 	    pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n",
726 		     __FUNCTION__, retval, error_message(retval));
727 	    goto cleanup;
728 	}
729 
730     }
731 
732     *all_matching_data = matchdata;
733     retval = 0;
734 cleanup:
735     if (ih != NULL)
736 	/* Solaris Kerberos */
737 	(void) crypto_cert_iteration_end(context, ih);
738     if (retval) {
739 	if (matchdata != NULL)
740 	    (void) free_all_cert_matching_data(context, matchdata);
741     }
742     pkiDebug("%s: returning %d, certinfo %p\n",
743 	     __FUNCTION__, retval, *all_matching_data);
744     return retval;
745 }
746 
747 krb5_error_code
748 pkinit_cert_matching(krb5_context context,
749 		     pkinit_plg_crypto_context plg_cryptoctx,
750 		     pkinit_req_crypto_context req_cryptoctx,
751 		     pkinit_identity_crypto_context id_cryptoctx,
752 		     krb5_principal princ,
753                      krb5_boolean do_select)
754 {
755 
756     krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
757     int x;
758     char **rules = NULL;
759     rule_set *rs = NULL;
760     int match_found = 0;
761     pkinit_cert_matching_data **matchdata = NULL;
762     pkinit_cert_matching_data *the_matching_cert = NULL;
763 
764     /* If no matching rules, select the default cert and we're done */
765     (void) pkinit_libdefault_strings(context, krb5_princ_realm(context, princ),
766 			      "pkinit_cert_match", &rules);
767     if (rules == NULL) {
768 	pkiDebug("%s: no matching rules found in config file\n", __FUNCTION__);
769         if (do_select == TRUE) {
770             retval = crypto_cert_select_default(context, plg_cryptoctx,
771                                                 req_cryptoctx, id_cryptoctx);
772         } else
773             retval = 0;
774 	goto cleanup;
775     }
776 
777     /* parse each rule line one at a time and check all the certs against it */
778     for (x = 0; rules[x] != NULL; x++) {
779 	pkiDebug("%s: Processing rule '%s'\n", __FUNCTION__, rules[x]);
780 
781 	/* Free rules from previous time through... */
782 	if (rs != NULL) {
783 	    (void) free_rule_set(context, rs);
784 	    rs = NULL;
785 	}
786 	retval = parse_rule_set(context, rules[x], &rs);
787 	if (retval) {
788 	    if (retval == EINVAL) {
789 		pkiDebug("%s: Ignoring invalid rule pkinit_cert_match = '%s'\n",
790 			 __FUNCTION__, rules[x]);
791 		continue;
792 	    }
793 	    goto cleanup;
794 	}
795 
796 	/*
797 	 * Optimize so that we do not get cert info unless we have
798 	 * valid rules to check.  Once obtained, keep it around
799 	 * until we are done.
800 	 */
801 	if (matchdata == NULL) {
802 	    retval = obtain_all_cert_matching_data(context, plg_cryptoctx,
803 						   req_cryptoctx, id_cryptoctx,
804 						   &matchdata);
805 	    if (retval || matchdata == NULL) {
806 		pkiDebug("%s: Error %d obtaining certificate information\n",
807 			 __FUNCTION__, retval);
808 		retval = ENOENT;
809 		goto cleanup;
810 	    }
811 	}
812 
813 	retval = check_all_certs(context, plg_cryptoctx, req_cryptoctx,
814 				 id_cryptoctx, princ, rs, matchdata,
815 				 &match_found, &the_matching_cert);
816 	if (retval) {
817 	    pkiDebug("%s: Error %d, checking certs against rule '%s'\n",
818 		     __FUNCTION__, retval, rules[x]);
819 	    goto cleanup;
820 	}
821 	if (match_found) {
822 	    pkiDebug("%s: We have an exact match with rule '%s'\n",
823 		     __FUNCTION__, rules[x]);
824 	    break;
825 	}
826     }
827 
828     if (match_found && the_matching_cert != NULL) {
829         if (do_select == TRUE) {
830             pkiDebug("%s: Selecting the matching cert!\n", __FUNCTION__);
831             retval = crypto_cert_select(context, the_matching_cert);
832             if (retval) {
833                 pkiDebug("%s: crypto_cert_select error %d, %s\n",
834                          __FUNCTION__, retval, error_message(retval));
835                 goto cleanup;
836             }
837         }
838     } else {
839 	retval = ENOENT;    /* XXX */
840 	goto cleanup;
841     }
842 
843     retval = 0;
844 cleanup:
845     if (rules != NULL)
846 	profile_free_list(rules);
847     if (rs != NULL)
848 	/* Solaris Kerberos */
849 	(void) free_rule_set(context, rs);
850     if (matchdata != NULL)
851 	(void) free_all_cert_matching_data(context, matchdata);
852     return retval;
853 }
854