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