Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/plugins/preauth/pkinit/pkinit_matching.c
34923 views
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 *
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 *
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 *
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
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
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
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
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
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
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
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
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
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
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
752