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