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