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