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