xref: /freebsd/crypto/krb5/src/plugins/preauth/pkinit/pkinit_matching.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * COPYRIGHT (C) 2007
4  * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
5  * ALL RIGHTS RESERVED
6  *
7  * Permission is granted to use, copy, create derivative works
8  * and redistribute this software and such derivative works
9  * for any purpose, so long as the name of The University of
10  * Michigan is not used in any advertising or publicity
11  * pertaining to the use of distribution of this software
12  * without specific, written prior authorization.  If the
13  * above copyright notice or any other identification of the
14  * University of Michigan is included in any copy of any
15  * portion of this software, then the disclaimer below must
16  * also be included.
17  *
18  * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
19  * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
20  * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
21  * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
22  * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
23  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
24  * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
25  * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
26  * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
27  * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
28  * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGES.
30  */
31 
32 #include <errno.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include "pkinit.h"
37 #include "k5-regex.h"
38 
39 typedef struct _pkinit_cert_info pkinit_cert_info;
40 
41 typedef enum {
42     kw_undefined = 0,
43     kw_subject = 1,
44     kw_issuer = 2,
45     kw_san = 3,
46     kw_eku = 4,
47     kw_ku = 5
48 } keyword_type;
49 
50 static char *
keyword2string(unsigned int kw)51 keyword2string(unsigned int kw)
52 {
53     switch(kw) {
54     case kw_undefined: return "NONE"; break;
55     case kw_subject: return "SUBJECT"; break;
56     case kw_issuer: return "ISSUER"; break;
57     case kw_san: return "SAN"; break;
58     case kw_eku: return "EKU"; break;
59     case kw_ku: return "KU"; break;
60     default: return "INVALID"; break;
61     }
62 }
63 typedef enum {
64     relation_none = 0,
65     relation_and = 1,
66     relation_or = 2
67 } relation_type;
68 
69 static char *
relation2string(unsigned int rel)70 relation2string(unsigned int rel)
71 {
72     switch(rel) {
73     case relation_none: return "NONE"; break;
74     case relation_and: return "AND"; break;
75     case relation_or: return "OR"; break;
76     default: return "INVALID"; break;
77     }
78 }
79 
80 typedef enum {
81     kwvaltype_undefined = 0,
82     kwvaltype_regexp = 1,
83     kwvaltype_list = 2
84 } kw_value_type;
85 
86 static char *
kwval2string(unsigned int kwval)87 kwval2string(unsigned int kwval)
88 {
89     switch(kwval) {
90     case kwvaltype_undefined: return "NONE"; break;
91     case kwvaltype_regexp: return "REGEXP"; break;
92     case kwvaltype_list: return "LIST"; break;
93     default: return "INVALID"; break;
94     }
95 }
96 
97 struct keyword_desc {
98     const char *value;
99     size_t length;
100     keyword_type kwtype;
101     kw_value_type kwvaltype;
102 } matching_keywords[] = {
103     { "<KU>",       4, kw_ku, kwvaltype_list },
104     { "<EKU>",      5, kw_eku, kwvaltype_list },
105     { "<SAN>",      5, kw_san, kwvaltype_regexp },
106     { "<ISSUER>",   8, kw_issuer, kwvaltype_regexp },
107     { "<SUBJECT>",  9, kw_subject, kwvaltype_regexp },
108     { NULL, 0, kw_undefined, kwvaltype_undefined},
109 };
110 
111 struct ku_desc {
112     const char *value;
113     size_t length;
114     unsigned int bitval;
115 };
116 
117 struct ku_desc ku_keywords[] = {
118     { "digitalSignature",   16, PKINIT_KU_DIGITALSIGNATURE },
119     { "keyEncipherment",    15, PKINIT_KU_KEYENCIPHERMENT },
120     { NULL, 0, 0 },
121 };
122 
123 struct ku_desc  eku_keywords[] = {
124     { "pkinit",             6,  PKINIT_EKU_PKINIT },
125     { "msScLogin",          9,  PKINIT_EKU_MSSCLOGIN },
126     { "clientAuth",         10, PKINIT_EKU_CLIENTAUTH },
127     { "emailProtection",    15, PKINIT_EKU_EMAILPROTECTION },
128     { NULL, 0, 0 },
129 };
130 
131 /* Rule component */
132 typedef struct _rule_component {
133     struct _rule_component *next;
134     keyword_type kw_type;
135     kw_value_type kwval_type;
136     regex_t regexp;         /* Compiled regular expression */
137     char *regsrc;           /* The regular expression source (for debugging) */
138     unsigned int ku_bits;
139     unsigned int eku_bits;
140 } rule_component;
141 
142 /* Set rule components */
143 typedef struct _rule_set {
144     relation_type relation;
145     int num_crs;
146     rule_component *crs;
147 } rule_set;
148 
149 static krb5_error_code
free_rule_component(krb5_context context,rule_component * rc)150 free_rule_component(krb5_context context,
151                     rule_component *rc)
152 {
153     if (rc == NULL)
154         return 0;
155 
156     if (rc->kwval_type == kwvaltype_regexp) {
157         free(rc->regsrc);
158         regfree(&rc->regexp);
159     }
160     free(rc);
161     return 0;
162 }
163 
164 static krb5_error_code
free_rule_set(krb5_context context,rule_set * rs)165 free_rule_set(krb5_context context,
166               rule_set *rs)
167 {
168     rule_component *rc, *trc;
169 
170     if (rs == NULL)
171         return 0;
172     for (rc = rs->crs; rc != NULL;) {
173         trc = rc->next;
174         free_rule_component(context, rc);
175         rc = trc;
176     }
177     free(rs);
178     return 0;
179 }
180 
181 static krb5_error_code
parse_list_value(krb5_context context,keyword_type type,char * value,rule_component * rc)182 parse_list_value(krb5_context context,
183                  keyword_type type,
184                  char *value,
185                  rule_component *rc)
186 {
187     krb5_error_code retval;
188     char *comma;
189     struct ku_desc *ku = NULL;
190     int found;
191     size_t len;
192     unsigned int *bitptr;
193 
194 
195     if (value == NULL || value[0] == '\0') {
196         pkiDebug("%s: Missing or empty value for list keyword type %d\n",
197                  __FUNCTION__, type);
198         retval = EINVAL;
199         goto out;
200     }
201 
202     if (type == kw_eku) {
203         bitptr = &rc->eku_bits;
204     } else if (type == kw_ku) {
205         bitptr = &rc->ku_bits;
206     } else {
207         pkiDebug("%s: Unknown list keyword type %d\n", __FUNCTION__, type);
208         retval = EINVAL;
209         goto out;
210     }
211 
212     do {
213         found = 0;
214         comma = strchr(value, ',');
215         if (comma != NULL)
216             len = comma - value;
217         else
218             len = strlen(value);
219 
220         if (type == kw_eku) {
221             ku = eku_keywords;
222         } else if (type == kw_ku) {
223             ku = ku_keywords;
224         }
225 
226         for (; ku->value != NULL; ku++) {
227             if (strncasecmp(value, ku->value, len) == 0) {
228                 *bitptr |= ku->bitval;
229                 found = 1;
230                 pkiDebug("%s: Found value '%s', bitfield is now 0x%x\n",
231                          __FUNCTION__, ku->value, *bitptr);
232                 break;
233             }
234         }
235         if (found) {
236             value += ku->length;
237             if (*value == ',')
238                 value += 1;
239         } else {
240             pkiDebug("%s: Urecognized value '%s'\n", __FUNCTION__, value);
241             retval = EINVAL;
242             goto out;
243         }
244     } while (found && *value != '\0');
245 
246     retval = 0;
247 out:
248     pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
249     return retval;
250 }
251 
252 static krb5_error_code
parse_rule_component(krb5_context context,const char ** rule,int * remaining,rule_component ** ret_rule)253 parse_rule_component(krb5_context context,
254                      const char **rule,
255                      int *remaining,
256                      rule_component **ret_rule)
257 {
258     krb5_error_code retval;
259     rule_component *rc = NULL;
260     keyword_type kw_type;
261     kw_value_type kwval_type;
262     char err_buf[128];
263     int ret;
264     struct keyword_desc *kw, *nextkw;
265     char *nk;
266     int found_next_kw = 0;
267     char *value = NULL;
268     size_t len;
269 
270     for (kw = matching_keywords; kw->value != NULL; kw++) {
271         if (strncmp(*rule, kw->value, kw->length) == 0) {
272             kw_type = kw->kwtype;
273             kwval_type = kw->kwvaltype;
274             *rule += kw->length;
275             *remaining -= kw->length;
276             break;
277         }
278     }
279     if (kw->value == NULL) {
280         pkiDebug("%s: Missing or invalid keyword in rule '%s'\n",
281                  __FUNCTION__, *rule);
282         retval = ENOENT;
283         goto out;
284     }
285 
286     pkiDebug("%s: found keyword '%s'\n", __FUNCTION__, kw->value);
287 
288     rc = calloc(1, sizeof(*rc));
289     if (rc == NULL) {
290         retval = ENOMEM;
291         goto out;
292     }
293     rc->next = NULL;
294     rc->kw_type = kw_type;
295     rc->kwval_type = kwval_type;
296 
297     /*
298      * Before processing the value for this keyword,
299      * (compiling the regular expression or processing the list)
300      * we need to find the end of it.  That means parsing for the
301      * beginning of the next keyword (or the end of the rule).
302      */
303     nk = strchr(*rule, '<');
304     while (nk != NULL) {
305         /* Possibly another keyword, check it out */
306         for (nextkw = matching_keywords; nextkw->value != NULL; nextkw++) {
307             if (strncmp(nk, nextkw->value, nextkw->length) == 0) {
308                 /* Found a keyword, nk points to the beginning */
309                 found_next_kw = 1;
310                 break;  /* Need to break out of the while! */
311             }
312         }
313         if (!found_next_kw)
314             nk = strchr(nk+1, '<');     /* keep looking */
315         else
316             break;
317     }
318 
319     if (nk != NULL && found_next_kw)
320         len = (nk - *rule);
321     else
322         len = (*remaining);
323 
324     if (len == 0) {
325         pkiDebug("%s: Missing value for keyword '%s'\n",
326                  __FUNCTION__, kw->value);
327         retval = EINVAL;
328         goto out;
329     }
330 
331     value = calloc(1, len+1);
332     if (value == NULL) {
333         retval = ENOMEM;
334         goto out;
335     }
336     memcpy(value, *rule, len);
337     *remaining -= len;
338     *rule += len;
339     pkiDebug("%s: found value '%s'\n", __FUNCTION__, value);
340 
341     if (kw->kwvaltype == kwvaltype_regexp) {
342         ret = regcomp(&rc->regexp, value, REG_EXTENDED);
343         if (ret) {
344             regerror(ret, &rc->regexp, err_buf, sizeof(err_buf));
345             pkiDebug("%s: Error compiling reg-exp '%s': %s\n",
346                      __FUNCTION__, value, err_buf);
347             retval = ret;
348             goto out;
349         }
350         rc->regsrc = strdup(value);
351         if (rc->regsrc == NULL) {
352             retval = ENOMEM;
353             goto out;
354         }
355     } else if (kw->kwvaltype == kwvaltype_list) {
356         retval = parse_list_value(context, rc->kw_type, value, rc);
357         if (retval) {
358             pkiDebug("%s: Error %d, parsing list values for keyword %s\n",
359                      __FUNCTION__, retval, kw->value);
360             goto out;
361         }
362     }
363 
364     *ret_rule = rc;
365     retval = 0;
366 out:
367     free(value);
368     if (retval && rc != NULL)
369         free_rule_component(context, rc);
370     pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
371     return retval;
372 }
373 
374 static krb5_error_code
parse_rule_set(krb5_context context,const char * rule_in,rule_set ** out_rs)375 parse_rule_set(krb5_context context,
376                const char *rule_in,
377                rule_set **out_rs)
378 {
379     const char *rule;
380     int remaining;
381     krb5_error_code ret, retval;
382     rule_component *rc = NULL, *trc;
383     rule_set *rs;
384 
385 
386     if (rule_in == NULL)
387         return EINVAL;
388     rule = rule_in;
389     remaining = strlen(rule);
390 
391     rs = calloc(1, sizeof(*rs));
392     if (rs == NULL) {
393         retval = ENOMEM;
394         goto cleanup;
395     }
396 
397     rs->relation = relation_none;
398     if (remaining > 1) {
399         if (rule[0] == '&' && rule[1] == '&') {
400             rs->relation = relation_and;
401             rule += 2;
402             remaining -= 2;
403         } else if (rule_in[0] == '|' && rule_in[1] == '|') {
404             rs->relation = relation_or;
405             rule +=2;
406             remaining -= 2;
407         }
408     }
409     rs->num_crs = 0;
410     while (remaining > 0) {
411         if (rs->relation == relation_none && rs->num_crs > 0) {
412             pkiDebug("%s: Assuming AND relation for multiple components in rule '%s'\n",
413                      __FUNCTION__, rule_in);
414             rs->relation = relation_and;
415         }
416         ret = parse_rule_component(context, &rule, &remaining, &rc);
417         if (ret) {
418             retval = ret;
419             goto cleanup;
420         }
421         pkiDebug("%s: After parse_rule_component, remaining %d, rule '%s'\n",
422                  __FUNCTION__, remaining, rule);
423         rs->num_crs++;
424 
425         /*
426          * Chain the new component on the end (order matters since
427          * we can short-circuit an OR or an AND relation if an
428          * earlier check passes
429          */
430         for (trc = rs->crs; trc != NULL && trc->next != NULL; trc = trc->next);
431         if (trc == NULL)
432             rs->crs = rc;
433         else {
434             trc->next = rc;
435         }
436     }
437 
438     *out_rs = rs;
439 
440     retval = 0;
441 cleanup:
442     if (retval && rs != NULL) {
443         free_rule_set(context, rs);
444     }
445     pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
446     return retval;
447 }
448 
449 static int
regexp_match(krb5_context context,rule_component * rc,char * value,int idx)450 regexp_match(krb5_context context, rule_component *rc, char *value, int idx)
451 {
452     int code;
453 
454     code = regexec(&rc->regexp, value, 0, NULL, 0);
455 
456     if (code == 0) {
457         TRACE_PKINIT_REGEXP_MATCH(context, keyword2string(rc->kw_type),
458                                   rc->regsrc, value, idx);
459     } else {
460         TRACE_PKINIT_REGEXP_NOMATCH(context, keyword2string(rc->kw_type),
461                                     rc->regsrc, value, idx);
462     }
463 
464     return (code == 0 ? 1: 0);
465 }
466 
467 static int
component_match(krb5_context context,rule_component * rc,pkinit_cert_matching_data * md,int idx)468 component_match(krb5_context context, rule_component *rc,
469                 pkinit_cert_matching_data *md, int idx)
470 {
471     int match = 0;
472     int i;
473     char *princ_string;
474 
475     switch (rc->kwval_type) {
476     case kwvaltype_regexp:
477         switch (rc->kw_type) {
478         case kw_subject:
479             match = regexp_match(context, rc, md->subject_dn, idx);
480             break;
481         case kw_issuer:
482             match = regexp_match(context, rc, md->issuer_dn, idx);
483             break;
484         case kw_san:
485             for (i = 0; md->sans != NULL && md->sans[i] != NULL; i++) {
486                 krb5_unparse_name(context, md->sans[i], &princ_string);
487                 match = regexp_match(context, rc, princ_string, idx);
488                 krb5_free_unparsed_name(context, princ_string);
489                 if (match)
490                     break;
491             }
492             for (i = 0; md->upns != NULL && md->upns[i] != NULL; i++) {
493                 match = regexp_match(context, rc, md->upns[i], idx);
494                 if (match)
495                     break;
496             }
497             break;
498         default:
499             pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
500                      __FUNCTION__, keyword2string(rc->kw_type),
501                      kwval2string(kwvaltype_regexp));
502             break;
503         }
504         break;
505     case kwvaltype_list:
506         switch(rc->kw_type) {
507         case kw_eku:
508             pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
509                      __FUNCTION__, keyword2string(rc->kw_type),
510                      rc->eku_bits, md->eku_bits);
511             if ((rc->eku_bits & md->eku_bits) == rc->eku_bits)
512                 match = 1;
513             break;
514         case kw_ku:
515             pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
516                      __FUNCTION__, keyword2string(rc->kw_type),
517                      rc->ku_bits, md->ku_bits);
518             if ((rc->ku_bits & md->ku_bits) == rc->ku_bits)
519                 match = 1;
520             break;
521         default:
522             pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
523                      __FUNCTION__, keyword2string(rc->kw_type),
524                      kwval2string(kwvaltype_regexp));
525             break;
526         }
527         break;
528     default:
529         pkiDebug("%s: unknown keyword value type %d\n",
530                  __FUNCTION__, rc->kwval_type);
531         break;
532     }
533     pkiDebug("%s: returning match = %d\n", __FUNCTION__, match);
534     return match;
535 }
536 /*
537  * Returns match_found == 1 only if exactly one certificate matches
538  * the given rule
539  */
540 static krb5_error_code
check_all_certs(krb5_context context,pkinit_plg_crypto_context plg_cryptoctx,pkinit_req_crypto_context req_cryptoctx,pkinit_identity_crypto_context id_cryptoctx,krb5_principal princ,rule_set * rs,pkinit_cert_matching_data ** matchdata,int * match_found,size_t * match_index)541 check_all_certs(krb5_context context,
542                 pkinit_plg_crypto_context plg_cryptoctx,
543                 pkinit_req_crypto_context req_cryptoctx,
544                 pkinit_identity_crypto_context id_cryptoctx,
545                 krb5_principal princ,
546                 rule_set *rs,   /* rule to check */
547                 pkinit_cert_matching_data **matchdata,
548                 int *match_found,
549                 size_t *match_index)
550 {
551     krb5_error_code retval;
552     pkinit_cert_matching_data *md;
553     int i;
554     int comp_match = 0;
555     int total_cert_matches = 0;
556     rule_component *rc;
557     int certs_checked = 0;
558     size_t save_index = 0;
559 
560     if (match_found == NULL || match_index == NULL)
561         return EINVAL;
562 
563     *match_index = 0;
564     *match_found = 0;
565 
566     pkiDebug("%s: matching rule relation is %s with %d components\n",
567              __FUNCTION__, relation2string(rs->relation), rs->num_crs);
568 
569     /*
570      * Loop through all the certs available and count
571      * how many match the rule
572      */
573     for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
574         pkiDebug("%s: subject: '%s'\n", __FUNCTION__, md->subject_dn);
575         certs_checked++;
576         for (rc = rs->crs; rc != NULL; rc = rc->next) {
577             comp_match = component_match(context, rc, md, i);
578             if (comp_match) {
579                 pkiDebug("%s: match for keyword type %s\n",
580                          __FUNCTION__, keyword2string(rc->kw_type));
581             }
582             if (comp_match && rs->relation == relation_or) {
583                 pkiDebug("%s: cert matches rule (OR relation)\n",
584                          __FUNCTION__);
585                 total_cert_matches++;
586                 save_index = i;
587                 goto nextcert;
588             }
589             if (!comp_match && rs->relation == relation_and) {
590                 pkiDebug("%s: cert does not match rule (AND relation)\n",
591                          __FUNCTION__);
592                 goto nextcert;
593             }
594         }
595         if (rc == NULL && comp_match) {
596             pkiDebug("%s: cert matches rule (AND relation)\n", __FUNCTION__);
597             total_cert_matches++;
598             save_index = i;
599         }
600     nextcert:
601         continue;
602     }
603     TRACE_PKINIT_CERT_NUM_MATCHING(context, certs_checked, total_cert_matches);
604     if (total_cert_matches == 1) {
605         *match_found = 1;
606         *match_index = save_index;
607     }
608 
609     retval = 0;
610 
611     pkiDebug("%s: returning %d, match_found %d\n",
612              __FUNCTION__, retval, *match_found);
613     return retval;
614 }
615 
616 krb5_error_code
pkinit_cert_matching(krb5_context context,pkinit_plg_crypto_context plg_cryptoctx,pkinit_req_crypto_context req_cryptoctx,pkinit_identity_crypto_context id_cryptoctx,krb5_principal princ)617 pkinit_cert_matching(krb5_context context,
618                      pkinit_plg_crypto_context plg_cryptoctx,
619                      pkinit_req_crypto_context req_cryptoctx,
620                      pkinit_identity_crypto_context id_cryptoctx,
621                      krb5_principal princ)
622 {
623 
624     krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
625     int x;
626     char **rules = NULL;
627     rule_set *rs = NULL;
628     int match_found = 0;
629     pkinit_cert_matching_data **matchdata = NULL;
630     size_t match_index = 0;
631 
632     /* If no matching rules, select the default cert and we're done */
633     pkinit_libdefault_strings(context, krb5_princ_realm(context, princ),
634                               KRB5_CONF_PKINIT_CERT_MATCH, &rules);
635     if (rules == NULL) {
636         pkiDebug("%s: no matching rules found in config file\n", __FUNCTION__);
637         retval = crypto_cert_select_default(context, plg_cryptoctx,
638                                             req_cryptoctx, id_cryptoctx);
639         goto cleanup;
640     }
641 
642     /* parse each rule line one at a time and check all the certs against it */
643     for (x = 0; rules[x] != NULL; x++) {
644         TRACE_PKINIT_CERT_RULE(context, rules[x]);
645 
646         /* Free rules from previous time through... */
647         if (rs != NULL) {
648             free_rule_set(context, rs);
649             rs = NULL;
650         }
651         retval = parse_rule_set(context, rules[x], &rs);
652         if (retval) {
653             if (retval == EINVAL) {
654                 TRACE_PKINIT_CERT_RULE_INVALID(context, rules[x]);
655                 continue;
656             }
657             goto cleanup;
658         }
659 
660         /*
661          * Optimize so that we do not get cert info unless we have
662          * valid rules to check.  Once obtained, keep it around
663          * until we are done.
664          */
665         if (matchdata == NULL) {
666             retval = crypto_cert_get_matching_data(context, plg_cryptoctx,
667                                                    req_cryptoctx, id_cryptoctx,
668                                                    &matchdata);
669             if (retval || matchdata == NULL) {
670                 pkiDebug("%s: Error %d obtaining certificate information\n",
671                          __FUNCTION__, retval);
672                 retval = ENOENT;
673                 goto cleanup;
674             }
675         }
676 
677         retval = check_all_certs(context, plg_cryptoctx, req_cryptoctx,
678                                  id_cryptoctx, princ, rs, matchdata,
679                                  &match_found, &match_index);
680         if (retval) {
681             pkiDebug("%s: Error %d, checking certs against rule '%s'\n",
682                      __FUNCTION__, retval, rules[x]);
683             goto cleanup;
684         }
685         if (match_found) {
686             pkiDebug("%s: We have an exact match with rule '%s'\n",
687                      __FUNCTION__, rules[x]);
688             break;
689         }
690     }
691 
692     if (match_found) {
693         pkiDebug("%s: Selecting the matching cert!\n", __FUNCTION__);
694         retval = crypto_cert_select(context, id_cryptoctx, match_index);
695         if (retval) {
696             pkiDebug("%s: crypto_cert_select error %d, %s\n",
697                      __FUNCTION__, retval, error_message(retval));
698             goto cleanup;
699         }
700     } else {
701         TRACE_PKINIT_NO_MATCHING_CERT(context);
702         retval = ENOENT;    /* XXX */
703         goto cleanup;
704     }
705 
706     retval = 0;
707 
708 cleanup:
709     profile_free_list(rules);
710     free_rule_set(context, rs);
711     crypto_cert_free_matching_data_list(context, matchdata);
712     return retval;
713 }
714 
715 krb5_error_code
pkinit_client_cert_match(krb5_context context,pkinit_plg_crypto_context plgctx,pkinit_req_crypto_context reqctx,const char * match_rule,krb5_boolean * matched)716 pkinit_client_cert_match(krb5_context context,
717                          pkinit_plg_crypto_context plgctx,
718                          pkinit_req_crypto_context reqctx,
719                          const char *match_rule,
720                          krb5_boolean *matched)
721 {
722     krb5_error_code ret;
723     pkinit_cert_matching_data *md = NULL;
724     rule_component *rc = NULL;
725     int comp_match = 0;
726     rule_set *rs = NULL;
727 
728     *matched = FALSE;
729     ret = parse_rule_set(context, match_rule, &rs);
730     if (ret)
731         goto cleanup;
732 
733     ret = crypto_req_cert_matching_data(context, plgctx, reqctx, &md);
734     if (ret)
735         goto cleanup;
736 
737     for (rc = rs->crs; rc != NULL; rc = rc->next) {
738         comp_match = component_match(context, rc, md, 0);
739         if ((comp_match && rs->relation == relation_or) ||
740             (!comp_match && rs->relation == relation_and)) {
741             break;
742         }
743     }
744     *matched = comp_match;
745 
746 cleanup:
747     free_rule_set(context, rs);
748     crypto_cert_free_matching_data(context, md);
749     return ret;
750 }
751