xref: /onnv-gate/usr/src/lib/krb5/plugins/preauth/pkinit/pkinit_matching.c (revision 12945:4a5c9854b161)
17934SMark.Phalan@Sun.COM /*
27934SMark.Phalan@Sun.COM  * COPYRIGHT (C) 2007
37934SMark.Phalan@Sun.COM  * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
47934SMark.Phalan@Sun.COM  * ALL RIGHTS RESERVED
57934SMark.Phalan@Sun.COM  *
67934SMark.Phalan@Sun.COM  * Permission is granted to use, copy, create derivative works
77934SMark.Phalan@Sun.COM  * and redistribute this software and such derivative works
87934SMark.Phalan@Sun.COM  * for any purpose, so long as the name of The University of
97934SMark.Phalan@Sun.COM  * Michigan is not used in any advertising or publicity
107934SMark.Phalan@Sun.COM  * pertaining to the use of distribution of this software
117934SMark.Phalan@Sun.COM  * without specific, written prior authorization.  If the
127934SMark.Phalan@Sun.COM  * above copyright notice or any other identification of the
137934SMark.Phalan@Sun.COM  * University of Michigan is included in any copy of any
147934SMark.Phalan@Sun.COM  * portion of this software, then the disclaimer below must
157934SMark.Phalan@Sun.COM  * also be included.
167934SMark.Phalan@Sun.COM  *
177934SMark.Phalan@Sun.COM  * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
187934SMark.Phalan@Sun.COM  * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
197934SMark.Phalan@Sun.COM  * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
207934SMark.Phalan@Sun.COM  * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
217934SMark.Phalan@Sun.COM  * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
227934SMark.Phalan@Sun.COM  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
237934SMark.Phalan@Sun.COM  * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
247934SMark.Phalan@Sun.COM  * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
257934SMark.Phalan@Sun.COM  * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
267934SMark.Phalan@Sun.COM  * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
277934SMark.Phalan@Sun.COM  * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
287934SMark.Phalan@Sun.COM  * SUCH DAMAGES.
297934SMark.Phalan@Sun.COM  */
307934SMark.Phalan@Sun.COM 
31*12945Swill.fiveash@oracle.com /*
32*12945Swill.fiveash@oracle.com  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
33*12945Swill.fiveash@oracle.com  */
34*12945Swill.fiveash@oracle.com 
357934SMark.Phalan@Sun.COM #include <errno.h>
367934SMark.Phalan@Sun.COM #include <string.h>
377934SMark.Phalan@Sun.COM #include <stdio.h>
387934SMark.Phalan@Sun.COM #include <stdlib.h>
397934SMark.Phalan@Sun.COM #include <unistd.h>
407934SMark.Phalan@Sun.COM #include <regex.h>
417934SMark.Phalan@Sun.COM #include <krb5.h>
427934SMark.Phalan@Sun.COM #include "pkinit.h"
437934SMark.Phalan@Sun.COM 
447934SMark.Phalan@Sun.COM typedef struct _pkinit_cert_info pkinit_cert_info;
457934SMark.Phalan@Sun.COM 
467934SMark.Phalan@Sun.COM typedef enum {
477934SMark.Phalan@Sun.COM     kw_undefined = 0,
487934SMark.Phalan@Sun.COM     kw_subject = 1,
497934SMark.Phalan@Sun.COM     kw_issuer = 2,
507934SMark.Phalan@Sun.COM     kw_san = 3,
517934SMark.Phalan@Sun.COM     kw_eku = 4,
527934SMark.Phalan@Sun.COM     kw_ku = 5
537934SMark.Phalan@Sun.COM } keyword_type;
547934SMark.Phalan@Sun.COM 
557934SMark.Phalan@Sun.COM static char *
keyword2string(unsigned int kw)567934SMark.Phalan@Sun.COM keyword2string(unsigned int kw)
577934SMark.Phalan@Sun.COM {
587934SMark.Phalan@Sun.COM     /* Solaris Kerberos: removed "break"s (lint) */
597934SMark.Phalan@Sun.COM     switch(kw) {
607934SMark.Phalan@Sun.COM     case kw_undefined: return "NONE";
617934SMark.Phalan@Sun.COM     case kw_subject: return "SUBJECT";
627934SMark.Phalan@Sun.COM     case kw_issuer: return "ISSUER";
637934SMark.Phalan@Sun.COM     case kw_san: return "SAN";
647934SMark.Phalan@Sun.COM     case kw_eku: return "EKU";
657934SMark.Phalan@Sun.COM     case kw_ku: return "KU";
667934SMark.Phalan@Sun.COM     default: return "INVALID";
677934SMark.Phalan@Sun.COM     }
687934SMark.Phalan@Sun.COM }
697934SMark.Phalan@Sun.COM typedef enum {
707934SMark.Phalan@Sun.COM     relation_none = 0,
717934SMark.Phalan@Sun.COM     relation_and = 1,
727934SMark.Phalan@Sun.COM     relation_or = 2
737934SMark.Phalan@Sun.COM } relation_type;
747934SMark.Phalan@Sun.COM 
757934SMark.Phalan@Sun.COM static char *
relation2string(unsigned int rel)767934SMark.Phalan@Sun.COM relation2string(unsigned int rel)
777934SMark.Phalan@Sun.COM {
787934SMark.Phalan@Sun.COM     /* Solaris Kerberos: removed "break"s (lint) */
797934SMark.Phalan@Sun.COM     switch(rel) {
807934SMark.Phalan@Sun.COM     case relation_none: return "NONE";
817934SMark.Phalan@Sun.COM     case relation_and: return "AND";
827934SMark.Phalan@Sun.COM     case relation_or: return "OR";
837934SMark.Phalan@Sun.COM     default: return "INVALID";
847934SMark.Phalan@Sun.COM     }
857934SMark.Phalan@Sun.COM }
867934SMark.Phalan@Sun.COM 
877934SMark.Phalan@Sun.COM typedef enum {
887934SMark.Phalan@Sun.COM     kwvaltype_undefined = 0,
897934SMark.Phalan@Sun.COM     kwvaltype_regexp = 1,
907934SMark.Phalan@Sun.COM     kwvaltype_list = 2
917934SMark.Phalan@Sun.COM } kw_value_type;
927934SMark.Phalan@Sun.COM 
937934SMark.Phalan@Sun.COM static char *
kwval2string(unsigned int kwval)947934SMark.Phalan@Sun.COM kwval2string(unsigned int kwval)
957934SMark.Phalan@Sun.COM {
967934SMark.Phalan@Sun.COM     /* Solaris Kerberos: removed "break"s (lint) */
977934SMark.Phalan@Sun.COM     switch(kwval) {
987934SMark.Phalan@Sun.COM     case kwvaltype_undefined: return "NONE";
997934SMark.Phalan@Sun.COM     case kwvaltype_regexp: return "REGEXP";
1007934SMark.Phalan@Sun.COM     case kwvaltype_list: return "LIST";
1017934SMark.Phalan@Sun.COM     default: return "INVALID";
1027934SMark.Phalan@Sun.COM     }
1037934SMark.Phalan@Sun.COM }
1047934SMark.Phalan@Sun.COM 
1057934SMark.Phalan@Sun.COM struct keyword_desc {
1067934SMark.Phalan@Sun.COM     const char *value;
1077934SMark.Phalan@Sun.COM     size_t length;
1087934SMark.Phalan@Sun.COM     keyword_type kwtype;
1097934SMark.Phalan@Sun.COM     kw_value_type kwvaltype;
1107934SMark.Phalan@Sun.COM } matching_keywords[] = {
1117934SMark.Phalan@Sun.COM     { "<KU>",	    4, kw_ku, kwvaltype_list },
1127934SMark.Phalan@Sun.COM     { "<EKU>",	    5, kw_eku, kwvaltype_list },
1137934SMark.Phalan@Sun.COM     { "<SAN>",	    5, kw_san, kwvaltype_regexp },
1147934SMark.Phalan@Sun.COM     { "<ISSUER>",   8, kw_issuer, kwvaltype_regexp },
1157934SMark.Phalan@Sun.COM     { "<SUBJECT>",  9, kw_subject, kwvaltype_regexp },
1167934SMark.Phalan@Sun.COM     { NULL, 0, kw_undefined, kwvaltype_undefined},
1177934SMark.Phalan@Sun.COM };
1187934SMark.Phalan@Sun.COM 
1197934SMark.Phalan@Sun.COM struct ku_desc {
1207934SMark.Phalan@Sun.COM     const char *value;
1217934SMark.Phalan@Sun.COM     size_t length;
1227934SMark.Phalan@Sun.COM     unsigned int bitval;
1237934SMark.Phalan@Sun.COM };
1247934SMark.Phalan@Sun.COM 
1257934SMark.Phalan@Sun.COM struct ku_desc ku_keywords[] = {
1267934SMark.Phalan@Sun.COM     { "digitalSignature",   16, PKINIT_KU_DIGITALSIGNATURE },
1277934SMark.Phalan@Sun.COM     { "keyEncipherment",    15, PKINIT_KU_KEYENCIPHERMENT },
1287934SMark.Phalan@Sun.COM     { NULL, 0, 0 },
1297934SMark.Phalan@Sun.COM };
1307934SMark.Phalan@Sun.COM 
1317934SMark.Phalan@Sun.COM struct ku_desc  eku_keywords[] = {
1327934SMark.Phalan@Sun.COM     { "pkinit",		    6,  PKINIT_EKU_PKINIT },
1337934SMark.Phalan@Sun.COM     { "msScLogin",	    9,  PKINIT_EKU_MSSCLOGIN },
1347934SMark.Phalan@Sun.COM     { "clientAuth",	    10, PKINIT_EKU_CLIENTAUTH },
1357934SMark.Phalan@Sun.COM     { "emailProtection",    15, PKINIT_EKU_EMAILPROTECTION },
1367934SMark.Phalan@Sun.COM     { NULL, 0, 0 },
1377934SMark.Phalan@Sun.COM };
1387934SMark.Phalan@Sun.COM 
1397934SMark.Phalan@Sun.COM /* Rule component */
1407934SMark.Phalan@Sun.COM typedef struct _rule_component {
1417934SMark.Phalan@Sun.COM     struct _rule_component *next;
1427934SMark.Phalan@Sun.COM     keyword_type kw_type;
1437934SMark.Phalan@Sun.COM     kw_value_type kwval_type;
1447934SMark.Phalan@Sun.COM     regex_t regexp;	    /* Compiled regular expression */
1457934SMark.Phalan@Sun.COM     char *regsrc;	    /* The regular expression source (for debugging) */
1467934SMark.Phalan@Sun.COM     unsigned int ku_bits;
1477934SMark.Phalan@Sun.COM     unsigned int eku_bits;
1487934SMark.Phalan@Sun.COM } rule_component;
1497934SMark.Phalan@Sun.COM 
1507934SMark.Phalan@Sun.COM /* Set rule components */
1517934SMark.Phalan@Sun.COM typedef struct _rule_set {
1527934SMark.Phalan@Sun.COM     relation_type relation;
1537934SMark.Phalan@Sun.COM     int num_crs;
1547934SMark.Phalan@Sun.COM     rule_component *crs;
1557934SMark.Phalan@Sun.COM } rule_set;
1567934SMark.Phalan@Sun.COM 
1577934SMark.Phalan@Sun.COM /* ARGSUSED */
1587934SMark.Phalan@Sun.COM static krb5_error_code
free_rule_component(krb5_context context,rule_component * rc)1597934SMark.Phalan@Sun.COM free_rule_component(krb5_context context,
1607934SMark.Phalan@Sun.COM 		    rule_component *rc)
1617934SMark.Phalan@Sun.COM {
1627934SMark.Phalan@Sun.COM     if (rc == NULL)
1637934SMark.Phalan@Sun.COM 	return 0;
1647934SMark.Phalan@Sun.COM 
1657934SMark.Phalan@Sun.COM     if (rc->kwval_type == kwvaltype_regexp) {
1667934SMark.Phalan@Sun.COM 	if (rc->regsrc)
1677934SMark.Phalan@Sun.COM 	    free(rc->regsrc);
1687934SMark.Phalan@Sun.COM 	regfree(&rc->regexp);
1697934SMark.Phalan@Sun.COM     }
1707934SMark.Phalan@Sun.COM     free(rc);
1717934SMark.Phalan@Sun.COM     return 0;
1727934SMark.Phalan@Sun.COM }
1737934SMark.Phalan@Sun.COM 
1747934SMark.Phalan@Sun.COM static krb5_error_code
free_rule_set(krb5_context context,rule_set * rs)1757934SMark.Phalan@Sun.COM free_rule_set(krb5_context context,
1767934SMark.Phalan@Sun.COM 	      rule_set *rs)
1777934SMark.Phalan@Sun.COM {
1787934SMark.Phalan@Sun.COM     rule_component *rc, *trc;
1797934SMark.Phalan@Sun.COM 
1807934SMark.Phalan@Sun.COM     if (rs == NULL)
1817934SMark.Phalan@Sun.COM 	return 0;
1827934SMark.Phalan@Sun.COM     for (rc = rs->crs; rc != NULL;) {
1837934SMark.Phalan@Sun.COM 	trc = rc->next;
1847934SMark.Phalan@Sun.COM 	/* Solaris Kerberos */
1857934SMark.Phalan@Sun.COM 	(void) free_rule_component(context, rc);
1867934SMark.Phalan@Sun.COM 	rc = trc;
1877934SMark.Phalan@Sun.COM     }
1887934SMark.Phalan@Sun.COM     free(rs);
1897934SMark.Phalan@Sun.COM     return 0;
1907934SMark.Phalan@Sun.COM }
1917934SMark.Phalan@Sun.COM 
1927934SMark.Phalan@Sun.COM /* ARGSUSED */
1937934SMark.Phalan@Sun.COM static krb5_error_code
parse_list_value(krb5_context context,keyword_type type,char * value,rule_component * rc)1947934SMark.Phalan@Sun.COM parse_list_value(krb5_context context,
1957934SMark.Phalan@Sun.COM 		 keyword_type type,
1967934SMark.Phalan@Sun.COM 		 char *value,
1977934SMark.Phalan@Sun.COM 		 rule_component *rc)
1987934SMark.Phalan@Sun.COM {
1997934SMark.Phalan@Sun.COM     krb5_error_code retval;
2007934SMark.Phalan@Sun.COM     char *comma;
2017934SMark.Phalan@Sun.COM     struct ku_desc *ku = NULL;
2027934SMark.Phalan@Sun.COM     int found;
2037934SMark.Phalan@Sun.COM     size_t len;
2047934SMark.Phalan@Sun.COM     unsigned int *bitptr;
2057934SMark.Phalan@Sun.COM 
2067934SMark.Phalan@Sun.COM 
2077934SMark.Phalan@Sun.COM     if (value == NULL || value[0] == '\0') {
2087934SMark.Phalan@Sun.COM 	pkiDebug("%s: Missing or empty value for list keyword type %d\n",
2097934SMark.Phalan@Sun.COM 		 __FUNCTION__, type);
2107934SMark.Phalan@Sun.COM 	retval = EINVAL;
2117934SMark.Phalan@Sun.COM 	goto out;
2127934SMark.Phalan@Sun.COM     }
2137934SMark.Phalan@Sun.COM 
2147934SMark.Phalan@Sun.COM     if (type == kw_eku) {
2157934SMark.Phalan@Sun.COM 	bitptr = &rc->eku_bits;
2167934SMark.Phalan@Sun.COM     } else if (type == kw_ku) {
2177934SMark.Phalan@Sun.COM 	bitptr = &rc->ku_bits;
2187934SMark.Phalan@Sun.COM     } else {
2197934SMark.Phalan@Sun.COM 	pkiDebug("%s: Unknown list keyword type %d\n", __FUNCTION__, type);
2207934SMark.Phalan@Sun.COM 	retval = EINVAL;
2217934SMark.Phalan@Sun.COM 	goto out;
2227934SMark.Phalan@Sun.COM     }
2237934SMark.Phalan@Sun.COM 
2247934SMark.Phalan@Sun.COM     do {
2257934SMark.Phalan@Sun.COM 	found = 0;
2267934SMark.Phalan@Sun.COM 	comma = strchr(value, ',');
2277934SMark.Phalan@Sun.COM 	if (comma != NULL)
2287934SMark.Phalan@Sun.COM 	    len = comma - value;
2297934SMark.Phalan@Sun.COM 	else
2307934SMark.Phalan@Sun.COM 	    len = strlen(value);
2317934SMark.Phalan@Sun.COM 
2327934SMark.Phalan@Sun.COM 	if (type == kw_eku) {
2337934SMark.Phalan@Sun.COM 	    ku = eku_keywords;
2347934SMark.Phalan@Sun.COM 	} else if (type == kw_ku) {
2357934SMark.Phalan@Sun.COM 	    ku = ku_keywords;
2367934SMark.Phalan@Sun.COM 	}
2377934SMark.Phalan@Sun.COM 
2387934SMark.Phalan@Sun.COM 	for (; ku->value != NULL; ku++) {
2397934SMark.Phalan@Sun.COM 	    if (strncasecmp(value, ku->value, len) == 0) {
2407934SMark.Phalan@Sun.COM 		*bitptr |= ku->bitval;
2417934SMark.Phalan@Sun.COM 		found = 1;
2427934SMark.Phalan@Sun.COM 		pkiDebug("%s: Found value '%s', bitfield is now 0x%x\n",
2437934SMark.Phalan@Sun.COM 			 __FUNCTION__, ku->value, *bitptr);
2447934SMark.Phalan@Sun.COM 		break;
2457934SMark.Phalan@Sun.COM 	    }
2467934SMark.Phalan@Sun.COM 	}
2477934SMark.Phalan@Sun.COM 	if (found) {
2487934SMark.Phalan@Sun.COM 	    value += ku->length;
2497934SMark.Phalan@Sun.COM 	    if (*value == ',')
2507934SMark.Phalan@Sun.COM 		value += 1;
2517934SMark.Phalan@Sun.COM 	} else {
2527934SMark.Phalan@Sun.COM 	    pkiDebug("%s: Urecognized value '%s'\n", __FUNCTION__, value);
2537934SMark.Phalan@Sun.COM 	    retval = EINVAL;
2547934SMark.Phalan@Sun.COM 	    goto out;
2557934SMark.Phalan@Sun.COM 	}
2567934SMark.Phalan@Sun.COM     } while (found && *value != '\0');
2577934SMark.Phalan@Sun.COM 
2587934SMark.Phalan@Sun.COM     retval = 0;
2597934SMark.Phalan@Sun.COM out:
2607934SMark.Phalan@Sun.COM     pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
2617934SMark.Phalan@Sun.COM     return retval;
2627934SMark.Phalan@Sun.COM }
2637934SMark.Phalan@Sun.COM 
2647934SMark.Phalan@Sun.COM static krb5_error_code
parse_rule_component(krb5_context context,const char ** rule,int * remaining,rule_component ** ret_rule)2657934SMark.Phalan@Sun.COM parse_rule_component(krb5_context context,
2667934SMark.Phalan@Sun.COM 		     const char **rule,
2677934SMark.Phalan@Sun.COM 		     int *remaining,
2687934SMark.Phalan@Sun.COM 		     rule_component **ret_rule)
2697934SMark.Phalan@Sun.COM {
2707934SMark.Phalan@Sun.COM     krb5_error_code retval;
2717934SMark.Phalan@Sun.COM     rule_component *rc = NULL;
2727934SMark.Phalan@Sun.COM     keyword_type kw_type;
2737934SMark.Phalan@Sun.COM     kw_value_type kwval_type;
2747934SMark.Phalan@Sun.COM     char err_buf[128];
2757934SMark.Phalan@Sun.COM     int ret;
2767934SMark.Phalan@Sun.COM     struct keyword_desc *kw, *nextkw;
2777934SMark.Phalan@Sun.COM     char *nk;
2787934SMark.Phalan@Sun.COM     int found_next_kw = 0;
2797934SMark.Phalan@Sun.COM     char *value = NULL;
2807934SMark.Phalan@Sun.COM     size_t len;
2817934SMark.Phalan@Sun.COM 
2827934SMark.Phalan@Sun.COM     for (kw = matching_keywords; kw->value != NULL; kw++) {
2837934SMark.Phalan@Sun.COM 	if (strncmp(*rule, kw->value, kw->length) == 0) {
2847934SMark.Phalan@Sun.COM 	    kw_type = kw->kwtype;
2857934SMark.Phalan@Sun.COM 	    kwval_type = kw->kwvaltype;
2867934SMark.Phalan@Sun.COM 	    *rule += kw->length;
2877934SMark.Phalan@Sun.COM 	    *remaining -= kw->length;
2887934SMark.Phalan@Sun.COM 	    break;
2897934SMark.Phalan@Sun.COM 	}
2907934SMark.Phalan@Sun.COM     }
2917934SMark.Phalan@Sun.COM     if (kw->value == NULL) {
2927934SMark.Phalan@Sun.COM 	pkiDebug("%s: Missing or invalid keyword in rule '%s'\n",
2937934SMark.Phalan@Sun.COM 		 __FUNCTION__, *rule);
2947934SMark.Phalan@Sun.COM 	retval = ENOENT;
2957934SMark.Phalan@Sun.COM 	goto out;
2967934SMark.Phalan@Sun.COM     }
2977934SMark.Phalan@Sun.COM 
2987934SMark.Phalan@Sun.COM     pkiDebug("%s: found keyword '%s'\n", __FUNCTION__, kw->value);
2997934SMark.Phalan@Sun.COM 
3007934SMark.Phalan@Sun.COM     rc = calloc(1, sizeof(*rc));
3017934SMark.Phalan@Sun.COM     if (rc == NULL) {
3027934SMark.Phalan@Sun.COM 	retval = ENOMEM;
3037934SMark.Phalan@Sun.COM 	goto out;
3047934SMark.Phalan@Sun.COM     }
3057934SMark.Phalan@Sun.COM     rc->next = NULL;
3067934SMark.Phalan@Sun.COM     rc->kw_type = kw_type;
3077934SMark.Phalan@Sun.COM     rc->kwval_type = kwval_type;
3087934SMark.Phalan@Sun.COM 
3097934SMark.Phalan@Sun.COM     /*
3107934SMark.Phalan@Sun.COM      * Before procesing the value for this keyword,
3117934SMark.Phalan@Sun.COM      * (compiling the regular expression or processing the list)
3127934SMark.Phalan@Sun.COM      * we need to find the end of it.  That means parsing for the
3137934SMark.Phalan@Sun.COM      * beginning of the next keyword (or the end of the rule).
3147934SMark.Phalan@Sun.COM      */
3157934SMark.Phalan@Sun.COM     nk = strchr(*rule, '<');
3167934SMark.Phalan@Sun.COM     while (nk != NULL) {
3177934SMark.Phalan@Sun.COM 	/* Possibly another keyword, check it out */
3187934SMark.Phalan@Sun.COM 	for (nextkw = matching_keywords; nextkw->value != NULL; nextkw++) {
3197934SMark.Phalan@Sun.COM 	    if (strncmp(nk, nextkw->value, nextkw->length) == 0) {
3207934SMark.Phalan@Sun.COM 		/* Found a keyword, nk points to the beginning */
3217934SMark.Phalan@Sun.COM 		found_next_kw = 1;
3227934SMark.Phalan@Sun.COM 		break;	/* Need to break out of the while! */
3237934SMark.Phalan@Sun.COM 	    }
3247934SMark.Phalan@Sun.COM 	}
3257934SMark.Phalan@Sun.COM 	if (!found_next_kw)
3267934SMark.Phalan@Sun.COM 	    nk = strchr(nk+1, '<');	/* keep looking */
3277934SMark.Phalan@Sun.COM 	else
3287934SMark.Phalan@Sun.COM 	    break;
3297934SMark.Phalan@Sun.COM     }
3307934SMark.Phalan@Sun.COM 
3317934SMark.Phalan@Sun.COM     if (nk != NULL && found_next_kw)
3327934SMark.Phalan@Sun.COM 	len = (nk - *rule);
3337934SMark.Phalan@Sun.COM     else
3347934SMark.Phalan@Sun.COM 	len = (*remaining);
3357934SMark.Phalan@Sun.COM 
3367934SMark.Phalan@Sun.COM     if (len == 0) {
3377934SMark.Phalan@Sun.COM 	pkiDebug("%s: Missing value for keyword '%s'\n",
3387934SMark.Phalan@Sun.COM 		 __FUNCTION__, kw->value);
3397934SMark.Phalan@Sun.COM 	retval = EINVAL;
3407934SMark.Phalan@Sun.COM 	goto out;
3417934SMark.Phalan@Sun.COM     }
3427934SMark.Phalan@Sun.COM 
3437934SMark.Phalan@Sun.COM     value = calloc(1, len+1);
3447934SMark.Phalan@Sun.COM     if (value == NULL) {
3457934SMark.Phalan@Sun.COM 	retval = ENOMEM;
3467934SMark.Phalan@Sun.COM 	goto out;
3477934SMark.Phalan@Sun.COM     }
3487934SMark.Phalan@Sun.COM     (void) memcpy(value, *rule, len);
3497934SMark.Phalan@Sun.COM     *remaining -= len;
3507934SMark.Phalan@Sun.COM     *rule += len;
3517934SMark.Phalan@Sun.COM     pkiDebug("%s: found value '%s'\n", __FUNCTION__, value);
3527934SMark.Phalan@Sun.COM 
3537934SMark.Phalan@Sun.COM     if (kw->kwvaltype == kwvaltype_regexp) {
3547934SMark.Phalan@Sun.COM 	ret = regcomp(&rc->regexp, value, REG_EXTENDED);
3557934SMark.Phalan@Sun.COM 	if (ret) {
3567934SMark.Phalan@Sun.COM 	    (void) regerror(ret, &rc->regexp, err_buf, sizeof(err_buf));
3577934SMark.Phalan@Sun.COM 	    pkiDebug("%s: Error compiling reg-exp '%s': %s\n",
3587934SMark.Phalan@Sun.COM 		     __FUNCTION__, value, err_buf);
3597934SMark.Phalan@Sun.COM 	    retval = ret;
3607934SMark.Phalan@Sun.COM 	    goto out;
3617934SMark.Phalan@Sun.COM 	}
3627934SMark.Phalan@Sun.COM 	rc->regsrc = strdup(value);
3637934SMark.Phalan@Sun.COM 	if (rc->regsrc == NULL) {
3647934SMark.Phalan@Sun.COM 	    retval = ENOMEM;
3657934SMark.Phalan@Sun.COM 	    goto out;
3667934SMark.Phalan@Sun.COM 	}
3677934SMark.Phalan@Sun.COM     } else if (kw->kwvaltype == kwvaltype_list) {
3687934SMark.Phalan@Sun.COM 	retval = parse_list_value(context, rc->kw_type, value, rc);
3697934SMark.Phalan@Sun.COM 	if (retval) {
3707934SMark.Phalan@Sun.COM 	    pkiDebug("%s: Error %d, parsing list values for keyword %s\n",
3717934SMark.Phalan@Sun.COM 		     __FUNCTION__, retval, kw->value);
3727934SMark.Phalan@Sun.COM 	    goto out;
3737934SMark.Phalan@Sun.COM 	}
3747934SMark.Phalan@Sun.COM     }
3757934SMark.Phalan@Sun.COM 
3767934SMark.Phalan@Sun.COM     *ret_rule = rc;
3777934SMark.Phalan@Sun.COM     retval = 0;
3787934SMark.Phalan@Sun.COM out:
3797934SMark.Phalan@Sun.COM     if (value != NULL)
3807934SMark.Phalan@Sun.COM 	free(value);
3817934SMark.Phalan@Sun.COM     if (retval && rc != NULL)
3827934SMark.Phalan@Sun.COM 	(void) free_rule_component(context, rc);
3837934SMark.Phalan@Sun.COM     pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
3847934SMark.Phalan@Sun.COM     return retval;
3857934SMark.Phalan@Sun.COM }
3867934SMark.Phalan@Sun.COM 
3877934SMark.Phalan@Sun.COM /* ARGSUSED */
3887934SMark.Phalan@Sun.COM static krb5_error_code
parse_rule_set(krb5_context context,const char * rule_in,rule_set ** out_rs)3897934SMark.Phalan@Sun.COM parse_rule_set(krb5_context context,
3907934SMark.Phalan@Sun.COM 	       const char *rule_in,
3917934SMark.Phalan@Sun.COM 	       rule_set **out_rs)
3927934SMark.Phalan@Sun.COM {
3937934SMark.Phalan@Sun.COM     const char *rule;
3947934SMark.Phalan@Sun.COM     /* Solaris Kerberos */
3957934SMark.Phalan@Sun.COM     int remaining;
3967934SMark.Phalan@Sun.COM     krb5_error_code ret, retval;
3977934SMark.Phalan@Sun.COM     rule_component *rc = NULL, *trc;
3987934SMark.Phalan@Sun.COM     rule_set *rs;
3997934SMark.Phalan@Sun.COM 
4007934SMark.Phalan@Sun.COM 
4017934SMark.Phalan@Sun.COM     if (rule_in == NULL)
4027934SMark.Phalan@Sun.COM 	return EINVAL;
4037934SMark.Phalan@Sun.COM     rule = rule_in;
4047934SMark.Phalan@Sun.COM     /* Solaris Kerberos */
4057934SMark.Phalan@Sun.COM     remaining = strlen(rule);
4067934SMark.Phalan@Sun.COM 
4077934SMark.Phalan@Sun.COM     rs = calloc(1, sizeof(*rs));
4087934SMark.Phalan@Sun.COM     if (rs == NULL) {
4097934SMark.Phalan@Sun.COM 	retval = ENOMEM;
4107934SMark.Phalan@Sun.COM 	goto cleanup;
4117934SMark.Phalan@Sun.COM     }
4127934SMark.Phalan@Sun.COM 
4137934SMark.Phalan@Sun.COM     rs->relation = relation_none;
4147934SMark.Phalan@Sun.COM     if (remaining > 1) {
4157934SMark.Phalan@Sun.COM 	if (rule[0] == '&' && rule[1] == '&') {
4167934SMark.Phalan@Sun.COM 	    rs->relation = relation_and;
4177934SMark.Phalan@Sun.COM 	    rule += 2;
4187934SMark.Phalan@Sun.COM 	    remaining -= 2;
4197934SMark.Phalan@Sun.COM 	} else if (rule_in[0] == '|' && rule_in[1] == '|') {
4207934SMark.Phalan@Sun.COM 	    rs->relation = relation_or;
4217934SMark.Phalan@Sun.COM 	    rule +=2;
4227934SMark.Phalan@Sun.COM 	    remaining -= 2;
4237934SMark.Phalan@Sun.COM 	}
4247934SMark.Phalan@Sun.COM     }
4257934SMark.Phalan@Sun.COM     rs->num_crs = 0;
4267934SMark.Phalan@Sun.COM     while (remaining > 0) {
4277934SMark.Phalan@Sun.COM 	if (rs->relation == relation_none && rs->num_crs > 1) {
4287934SMark.Phalan@Sun.COM 	    pkiDebug("%s: Assuming AND relation for multiple components in rule '%s'\n",
4297934SMark.Phalan@Sun.COM 		     __FUNCTION__, rule_in);
4307934SMark.Phalan@Sun.COM 	    rs->relation = relation_and;
4317934SMark.Phalan@Sun.COM 	}
4327934SMark.Phalan@Sun.COM 	ret = parse_rule_component(context, &rule, &remaining, &rc);
4337934SMark.Phalan@Sun.COM 	if (ret) {
4347934SMark.Phalan@Sun.COM 	    retval = ret;
4357934SMark.Phalan@Sun.COM 	    goto cleanup;
4367934SMark.Phalan@Sun.COM 	}
4377934SMark.Phalan@Sun.COM 	pkiDebug("%s: After parse_rule_component, remaining %d, rule '%s'\n",
4387934SMark.Phalan@Sun.COM 		 __FUNCTION__, remaining, rule);
4397934SMark.Phalan@Sun.COM 	rs->num_crs++;
4407934SMark.Phalan@Sun.COM 
4417934SMark.Phalan@Sun.COM 	/*
4427934SMark.Phalan@Sun.COM 	 * Chain the new component on the end (order matters since
4437934SMark.Phalan@Sun.COM 	 * we can short-circuit an OR or an AND relation if an
4447934SMark.Phalan@Sun.COM 	 * earlier check passes
4457934SMark.Phalan@Sun.COM 	 */
4467934SMark.Phalan@Sun.COM 	for (trc = rs->crs; trc != NULL && trc->next != NULL; trc = trc->next);
4477934SMark.Phalan@Sun.COM 	if (trc == NULL)
4487934SMark.Phalan@Sun.COM 	    rs->crs = rc;
4497934SMark.Phalan@Sun.COM 	else {
4507934SMark.Phalan@Sun.COM 	    trc->next = rc;
4517934SMark.Phalan@Sun.COM 	}
4527934SMark.Phalan@Sun.COM     }
4537934SMark.Phalan@Sun.COM 
4547934SMark.Phalan@Sun.COM     *out_rs = rs;
4557934SMark.Phalan@Sun.COM 
4567934SMark.Phalan@Sun.COM     retval = 0;
4577934SMark.Phalan@Sun.COM cleanup:
4587934SMark.Phalan@Sun.COM     if (retval && rs != NULL) {
4597934SMark.Phalan@Sun.COM 	(void) free_rule_set(context, rs);
4607934SMark.Phalan@Sun.COM     }
4617934SMark.Phalan@Sun.COM     pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
4627934SMark.Phalan@Sun.COM     return retval;
4637934SMark.Phalan@Sun.COM }
4647934SMark.Phalan@Sun.COM 
4657934SMark.Phalan@Sun.COM /* ARGSUSED */
4667934SMark.Phalan@Sun.COM static int
regexp_match(krb5_context context,rule_component * rc,char * value)4677934SMark.Phalan@Sun.COM regexp_match(krb5_context context, rule_component *rc, char *value)
4687934SMark.Phalan@Sun.COM {
4697934SMark.Phalan@Sun.COM     int code;
4707934SMark.Phalan@Sun.COM 
4717934SMark.Phalan@Sun.COM     pkiDebug("%s: checking %s rule '%s' with value '%s'\n",
4727934SMark.Phalan@Sun.COM 	     __FUNCTION__, keyword2string(rc->kw_type), rc->regsrc, value);
4737934SMark.Phalan@Sun.COM 
4747934SMark.Phalan@Sun.COM     code = regexec(&rc->regexp, value, 0, NULL, 0);
4757934SMark.Phalan@Sun.COM 
4767934SMark.Phalan@Sun.COM     pkiDebug("%s: the result is%s a match\n", __FUNCTION__,
4777934SMark.Phalan@Sun.COM 	     code == REG_NOMATCH ? " NOT" : "");
4787934SMark.Phalan@Sun.COM 
4797934SMark.Phalan@Sun.COM     return (code == 0 ? 1: 0);
4807934SMark.Phalan@Sun.COM }
4817934SMark.Phalan@Sun.COM 
4827934SMark.Phalan@Sun.COM static int
component_match(krb5_context context,rule_component * rc,pkinit_cert_matching_data * md)4837934SMark.Phalan@Sun.COM component_match(krb5_context context,
4847934SMark.Phalan@Sun.COM 		rule_component *rc,
4857934SMark.Phalan@Sun.COM 		pkinit_cert_matching_data *md)
4867934SMark.Phalan@Sun.COM {
4877934SMark.Phalan@Sun.COM     int match = 0;
4887934SMark.Phalan@Sun.COM     int i;
4897934SMark.Phalan@Sun.COM     krb5_principal p;
4907934SMark.Phalan@Sun.COM     char *princ_string;
4917934SMark.Phalan@Sun.COM 
4927934SMark.Phalan@Sun.COM     switch (rc->kwval_type) {
4937934SMark.Phalan@Sun.COM     case kwvaltype_regexp:
4947934SMark.Phalan@Sun.COM 	switch (rc->kw_type) {
4957934SMark.Phalan@Sun.COM 	case kw_subject:
4967934SMark.Phalan@Sun.COM 	    match = regexp_match(context, rc, md->subject_dn);
4977934SMark.Phalan@Sun.COM 	    break;
4987934SMark.Phalan@Sun.COM 	case kw_issuer:
4997934SMark.Phalan@Sun.COM 	    match = regexp_match(context, rc, md->issuer_dn);
5007934SMark.Phalan@Sun.COM 	    break;
5017934SMark.Phalan@Sun.COM 	case kw_san:
5027934SMark.Phalan@Sun.COM 	    if (md->sans == NULL)
5037934SMark.Phalan@Sun.COM 		break;
5047934SMark.Phalan@Sun.COM 	    for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) {
5057934SMark.Phalan@Sun.COM 		krb5_unparse_name(context, p, &princ_string);
5067934SMark.Phalan@Sun.COM 		match = regexp_match(context, rc, princ_string);
5077934SMark.Phalan@Sun.COM 		krb5_free_unparsed_name(context, princ_string);
5087934SMark.Phalan@Sun.COM 		if (match)
5097934SMark.Phalan@Sun.COM 		    break;
5107934SMark.Phalan@Sun.COM 	    }
5117934SMark.Phalan@Sun.COM 	    break;
5127934SMark.Phalan@Sun.COM 	default:
5137934SMark.Phalan@Sun.COM 	    pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
5147934SMark.Phalan@Sun.COM 		     __FUNCTION__, keyword2string(rc->kw_type),
5157934SMark.Phalan@Sun.COM 		     kwval2string(kwvaltype_regexp));
5167934SMark.Phalan@Sun.COM 	    break;
5177934SMark.Phalan@Sun.COM 	}
5187934SMark.Phalan@Sun.COM 	break;
5197934SMark.Phalan@Sun.COM     case kwvaltype_list:
5207934SMark.Phalan@Sun.COM 	switch(rc->kw_type) {
5217934SMark.Phalan@Sun.COM 	case kw_eku:
5227934SMark.Phalan@Sun.COM 	    pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
5237934SMark.Phalan@Sun.COM 		     __FUNCTION__, keyword2string(rc->kw_type),
5247934SMark.Phalan@Sun.COM 		     rc->eku_bits, md->eku_bits);
5257934SMark.Phalan@Sun.COM 	    if ((rc->eku_bits & md->eku_bits) == rc->eku_bits)
5267934SMark.Phalan@Sun.COM 		match = 1;
5277934SMark.Phalan@Sun.COM 	    break;
5287934SMark.Phalan@Sun.COM 	case kw_ku:
5297934SMark.Phalan@Sun.COM 	    pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
5307934SMark.Phalan@Sun.COM 		     __FUNCTION__, keyword2string(rc->kw_type),
5317934SMark.Phalan@Sun.COM 		     rc->ku_bits, md->ku_bits);
5327934SMark.Phalan@Sun.COM 	    if ((rc->ku_bits & md->ku_bits) == rc->ku_bits)
5337934SMark.Phalan@Sun.COM 		match = 1;
5347934SMark.Phalan@Sun.COM 	    break;
5357934SMark.Phalan@Sun.COM 	default:
5367934SMark.Phalan@Sun.COM 	    pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
5377934SMark.Phalan@Sun.COM 		     __FUNCTION__, keyword2string(rc->kw_type),
5387934SMark.Phalan@Sun.COM 		     kwval2string(kwvaltype_regexp));
5397934SMark.Phalan@Sun.COM 	    break;
5407934SMark.Phalan@Sun.COM 	}
5417934SMark.Phalan@Sun.COM 	break;
5427934SMark.Phalan@Sun.COM     default:
5437934SMark.Phalan@Sun.COM 	pkiDebug("%s: unknown keyword value type %d\n",
5447934SMark.Phalan@Sun.COM 		 __FUNCTION__, rc->kwval_type);
5457934SMark.Phalan@Sun.COM 	break;
5467934SMark.Phalan@Sun.COM     }
5477934SMark.Phalan@Sun.COM     pkiDebug("%s: returning match = %d\n", __FUNCTION__, match);
5487934SMark.Phalan@Sun.COM     return match;
5497934SMark.Phalan@Sun.COM }
5507934SMark.Phalan@Sun.COM /*
5517934SMark.Phalan@Sun.COM  * Returns match_found == 1 only if exactly one certificate matches
5527934SMark.Phalan@Sun.COM  * the given rule
5537934SMark.Phalan@Sun.COM  */
5547934SMark.Phalan@Sun.COM /* ARGSUSED */
5557934SMark.Phalan@Sun.COM 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)5567934SMark.Phalan@Sun.COM check_all_certs(krb5_context context,
5577934SMark.Phalan@Sun.COM 		pkinit_plg_crypto_context plg_cryptoctx,
5587934SMark.Phalan@Sun.COM 		pkinit_req_crypto_context req_cryptoctx,
5597934SMark.Phalan@Sun.COM 		pkinit_identity_crypto_context id_cryptoctx,
5607934SMark.Phalan@Sun.COM 		krb5_principal princ,
5617934SMark.Phalan@Sun.COM 		rule_set *rs,	/* rule to check */
5627934SMark.Phalan@Sun.COM 		pkinit_cert_matching_data **matchdata,
5637934SMark.Phalan@Sun.COM 		int *match_found,
5647934SMark.Phalan@Sun.COM 		pkinit_cert_matching_data **matching_cert)
5657934SMark.Phalan@Sun.COM {
5667934SMark.Phalan@Sun.COM     krb5_error_code retval;
5677934SMark.Phalan@Sun.COM     pkinit_cert_matching_data *md;
5687934SMark.Phalan@Sun.COM     int i;
5697934SMark.Phalan@Sun.COM     int comp_match = 0;
5707934SMark.Phalan@Sun.COM     int total_cert_matches = 0;
5717934SMark.Phalan@Sun.COM     rule_component *rc;
5727934SMark.Phalan@Sun.COM     int certs_checked = 0;
5737934SMark.Phalan@Sun.COM     pkinit_cert_matching_data *save_match = NULL;
5747934SMark.Phalan@Sun.COM 
5757934SMark.Phalan@Sun.COM     if (match_found == NULL || matching_cert == NULL)
5767934SMark.Phalan@Sun.COM 	return EINVAL;
5777934SMark.Phalan@Sun.COM 
5787934SMark.Phalan@Sun.COM     *matching_cert = NULL;
5797934SMark.Phalan@Sun.COM     *match_found = 0;
5807934SMark.Phalan@Sun.COM 
5817934SMark.Phalan@Sun.COM     pkiDebug("%s: matching rule relation is %s with %d components\n",
5827934SMark.Phalan@Sun.COM 	     __FUNCTION__, relation2string(rs->relation), rs->num_crs);
5837934SMark.Phalan@Sun.COM 
5847934SMark.Phalan@Sun.COM     /*
5857934SMark.Phalan@Sun.COM      * Loop through all the certs available and count
5867934SMark.Phalan@Sun.COM      * how many match the rule
5877934SMark.Phalan@Sun.COM      */
5887934SMark.Phalan@Sun.COM     for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
5897934SMark.Phalan@Sun.COM 	pkiDebug("%s: subject: '%s'\n", __FUNCTION__, md->subject_dn);
5907934SMark.Phalan@Sun.COM #if 0
5917934SMark.Phalan@Sun.COM 	pkiDebug("%s: issuer:  '%s'\n", __FUNCTION__, md->subject_dn);
5927934SMark.Phalan@Sun.COM 	for (j = 0, p = md->sans[j]; p != NULL; p = md->sans[++j]) {
5937934SMark.Phalan@Sun.COM 	    char *san_string;
5947934SMark.Phalan@Sun.COM 	    krb5_unparse_name(context, p, &san_string);
5957934SMark.Phalan@Sun.COM 	    pkiDebug("%s: san: '%s'\n", __FUNCTION__, san_string);
5967934SMark.Phalan@Sun.COM 	    krb5_free_unparsed_name(context, san_string);
5977934SMark.Phalan@Sun.COM 	}
5987934SMark.Phalan@Sun.COM #endif
5997934SMark.Phalan@Sun.COM 	certs_checked++;
6007934SMark.Phalan@Sun.COM 	for (rc = rs->crs; rc != NULL; rc = rc->next) {
6017934SMark.Phalan@Sun.COM 	    comp_match = component_match(context, rc, md);
6027934SMark.Phalan@Sun.COM 	    if (comp_match) {
6037934SMark.Phalan@Sun.COM 		pkiDebug("%s: match for keyword type %s\n",
6047934SMark.Phalan@Sun.COM 			 __FUNCTION__, keyword2string(rc->kw_type));
6057934SMark.Phalan@Sun.COM 	    }
6067934SMark.Phalan@Sun.COM 	    if (comp_match && rs->relation == relation_or) {
6077934SMark.Phalan@Sun.COM 		pkiDebug("%s: cert matches rule (OR relation)\n",
6087934SMark.Phalan@Sun.COM 			 __FUNCTION__);
6097934SMark.Phalan@Sun.COM 		total_cert_matches++;
6107934SMark.Phalan@Sun.COM 		save_match = md;
6117934SMark.Phalan@Sun.COM 		goto nextcert;
6127934SMark.Phalan@Sun.COM 	    }
6137934SMark.Phalan@Sun.COM 	    if (!comp_match && rs->relation == relation_and) {
6147934SMark.Phalan@Sun.COM 		pkiDebug("%s: cert does not match rule (AND relation)\n",
6157934SMark.Phalan@Sun.COM 			 __FUNCTION__);
6167934SMark.Phalan@Sun.COM 		goto nextcert;
6177934SMark.Phalan@Sun.COM 	    }
6187934SMark.Phalan@Sun.COM 	}
6197934SMark.Phalan@Sun.COM 	if (rc == NULL && comp_match) {
6207934SMark.Phalan@Sun.COM 	    pkiDebug("%s: cert matches rule (AND relation)\n", __FUNCTION__);
6217934SMark.Phalan@Sun.COM 	    total_cert_matches++;
6227934SMark.Phalan@Sun.COM 	    save_match = md;
6237934SMark.Phalan@Sun.COM 	}
6247934SMark.Phalan@Sun.COM nextcert:
6257934SMark.Phalan@Sun.COM 	continue;
6267934SMark.Phalan@Sun.COM     }
6277934SMark.Phalan@Sun.COM     pkiDebug("%s: After checking %d certs, we found %d matches\n",
6287934SMark.Phalan@Sun.COM 	     __FUNCTION__, certs_checked, total_cert_matches);
6297934SMark.Phalan@Sun.COM     if (total_cert_matches == 1) {
6307934SMark.Phalan@Sun.COM 	*match_found = 1;
6317934SMark.Phalan@Sun.COM 	*matching_cert = save_match;
6327934SMark.Phalan@Sun.COM     }
6337934SMark.Phalan@Sun.COM 
6347934SMark.Phalan@Sun.COM     retval = 0;
6357934SMark.Phalan@Sun.COM 
6367934SMark.Phalan@Sun.COM     pkiDebug("%s: returning %d, match_found %d\n",
6377934SMark.Phalan@Sun.COM 	     __FUNCTION__, retval, *match_found);
6387934SMark.Phalan@Sun.COM     return retval;
6397934SMark.Phalan@Sun.COM }
6407934SMark.Phalan@Sun.COM 
6417934SMark.Phalan@Sun.COM static krb5_error_code
free_all_cert_matching_data(krb5_context context,pkinit_cert_matching_data ** matchdata)6427934SMark.Phalan@Sun.COM free_all_cert_matching_data(krb5_context context,
6437934SMark.Phalan@Sun.COM 			    pkinit_cert_matching_data **matchdata)
6447934SMark.Phalan@Sun.COM {
6457934SMark.Phalan@Sun.COM     krb5_error_code retval;
6467934SMark.Phalan@Sun.COM     pkinit_cert_matching_data *md;
6477934SMark.Phalan@Sun.COM     int i;
6487934SMark.Phalan@Sun.COM 
6497934SMark.Phalan@Sun.COM     if (matchdata == NULL)
6507934SMark.Phalan@Sun.COM 	return EINVAL;
6517934SMark.Phalan@Sun.COM 
6527934SMark.Phalan@Sun.COM     for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
6537934SMark.Phalan@Sun.COM 	pkinit_cert_handle ch = md->ch;
6547934SMark.Phalan@Sun.COM 	retval = crypto_cert_free_matching_data(context, md);
6557934SMark.Phalan@Sun.COM 	if (retval) {
6567934SMark.Phalan@Sun.COM 	    pkiDebug("%s: crypto_cert_free_matching_data error %d, %s\n",
6577934SMark.Phalan@Sun.COM 		     __FUNCTION__, retval, error_message(retval));
6587934SMark.Phalan@Sun.COM 	    goto cleanup;
6597934SMark.Phalan@Sun.COM 	}
6607934SMark.Phalan@Sun.COM 	retval = crypto_cert_release(context, ch);
6617934SMark.Phalan@Sun.COM 	if (retval) {
6627934SMark.Phalan@Sun.COM 	    pkiDebug("%s: crypto_cert_release error %d, %s\n",
6637934SMark.Phalan@Sun.COM 		     __FUNCTION__, retval, error_message(retval));
6647934SMark.Phalan@Sun.COM 	    goto cleanup;
6657934SMark.Phalan@Sun.COM 	}
6667934SMark.Phalan@Sun.COM     }
6677934SMark.Phalan@Sun.COM     free(matchdata);
6687934SMark.Phalan@Sun.COM     retval = 0;
6697934SMark.Phalan@Sun.COM 
6707934SMark.Phalan@Sun.COM cleanup:
6717934SMark.Phalan@Sun.COM     return retval;
6727934SMark.Phalan@Sun.COM }
6737934SMark.Phalan@Sun.COM 
6747934SMark.Phalan@Sun.COM 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)6757934SMark.Phalan@Sun.COM obtain_all_cert_matching_data(krb5_context context,
6767934SMark.Phalan@Sun.COM 			      pkinit_plg_crypto_context plg_cryptoctx,
6777934SMark.Phalan@Sun.COM 			      pkinit_req_crypto_context req_cryptoctx,
6787934SMark.Phalan@Sun.COM 			      pkinit_identity_crypto_context id_cryptoctx,
6797934SMark.Phalan@Sun.COM 			      pkinit_cert_matching_data ***all_matching_data)
6807934SMark.Phalan@Sun.COM {
6817934SMark.Phalan@Sun.COM     krb5_error_code retval;
6827934SMark.Phalan@Sun.COM     int i, cert_count;
6837934SMark.Phalan@Sun.COM     pkinit_cert_iter_handle ih = NULL;
6847934SMark.Phalan@Sun.COM     pkinit_cert_handle ch;
6857934SMark.Phalan@Sun.COM     pkinit_cert_matching_data **matchdata = NULL;
6867934SMark.Phalan@Sun.COM 
6877934SMark.Phalan@Sun.COM     retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx,
6887934SMark.Phalan@Sun.COM 				   id_cryptoctx, &cert_count);
6897934SMark.Phalan@Sun.COM     if (retval) {
6907934SMark.Phalan@Sun.COM 	pkiDebug("%s: crypto_cert_get_count error %d, %s\n",
6917934SMark.Phalan@Sun.COM 		 __FUNCTION__, retval, error_message(retval));
6927934SMark.Phalan@Sun.COM 	goto cleanup;
6937934SMark.Phalan@Sun.COM     }
6947934SMark.Phalan@Sun.COM 
6957934SMark.Phalan@Sun.COM     pkiDebug("%s: crypto_cert_get_count says there are %d certs\n",
6967934SMark.Phalan@Sun.COM 	      __FUNCTION__, cert_count);
6977934SMark.Phalan@Sun.COM 
6987934SMark.Phalan@Sun.COM     matchdata = calloc((size_t)cert_count + 1, sizeof(*matchdata));
6997934SMark.Phalan@Sun.COM     if (matchdata == NULL)
7007934SMark.Phalan@Sun.COM 	return ENOMEM;
7017934SMark.Phalan@Sun.COM 
7027934SMark.Phalan@Sun.COM     retval = crypto_cert_iteration_begin(context, plg_cryptoctx, req_cryptoctx,
7037934SMark.Phalan@Sun.COM 					 id_cryptoctx, &ih);
7047934SMark.Phalan@Sun.COM     if (retval) {
7057934SMark.Phalan@Sun.COM 	pkiDebug("%s: crypto_cert_iteration_begin returned %d, %s\n",
7067934SMark.Phalan@Sun.COM 		 __FUNCTION__, retval, error_message(retval));
7077934SMark.Phalan@Sun.COM 	goto cleanup;
7087934SMark.Phalan@Sun.COM     }
7097934SMark.Phalan@Sun.COM 
7107934SMark.Phalan@Sun.COM     for (i = 0; i < cert_count; i++) {
7117934SMark.Phalan@Sun.COM 	retval = crypto_cert_iteration_next(context, ih, &ch);
7127934SMark.Phalan@Sun.COM 	if (retval) {
7137934SMark.Phalan@Sun.COM 	    if (retval == PKINIT_ITER_NO_MORE)
7147934SMark.Phalan@Sun.COM 		pkiDebug("%s: We thought there were %d certs, but "
7157934SMark.Phalan@Sun.COM 			 "crypto_cert_iteration_next stopped after %d?\n",
7167934SMark.Phalan@Sun.COM 			 __FUNCTION__, cert_count, i);
7177934SMark.Phalan@Sun.COM 	    else
7187934SMark.Phalan@Sun.COM 		pkiDebug("%s: crypto_cert_iteration_next error %d, %s\n",
7197934SMark.Phalan@Sun.COM 			 __FUNCTION__, retval, error_message(retval));
7207934SMark.Phalan@Sun.COM 	    goto cleanup;
7217934SMark.Phalan@Sun.COM 	}
7227934SMark.Phalan@Sun.COM 
7237934SMark.Phalan@Sun.COM 	retval = crypto_cert_get_matching_data(context, ch, &matchdata[i]);
7247934SMark.Phalan@Sun.COM 	if (retval) {
7257934SMark.Phalan@Sun.COM 	    pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n",
7267934SMark.Phalan@Sun.COM 		     __FUNCTION__, retval, error_message(retval));
7277934SMark.Phalan@Sun.COM 	    goto cleanup;
7287934SMark.Phalan@Sun.COM 	}
7297934SMark.Phalan@Sun.COM 
7307934SMark.Phalan@Sun.COM     }
7317934SMark.Phalan@Sun.COM 
7327934SMark.Phalan@Sun.COM     *all_matching_data = matchdata;
7337934SMark.Phalan@Sun.COM     retval = 0;
7347934SMark.Phalan@Sun.COM cleanup:
7357934SMark.Phalan@Sun.COM     if (ih != NULL)
7367934SMark.Phalan@Sun.COM 	/* Solaris Kerberos */
7377934SMark.Phalan@Sun.COM 	(void) crypto_cert_iteration_end(context, ih);
7387934SMark.Phalan@Sun.COM     if (retval) {
7397934SMark.Phalan@Sun.COM 	if (matchdata != NULL)
7407934SMark.Phalan@Sun.COM 	    (void) free_all_cert_matching_data(context, matchdata);
7417934SMark.Phalan@Sun.COM     }
7427934SMark.Phalan@Sun.COM     pkiDebug("%s: returning %d, certinfo %p\n",
7437934SMark.Phalan@Sun.COM 	     __FUNCTION__, retval, *all_matching_data);
7447934SMark.Phalan@Sun.COM     return retval;
7457934SMark.Phalan@Sun.COM }
7467934SMark.Phalan@Sun.COM 
7477934SMark.Phalan@Sun.COM 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)7487934SMark.Phalan@Sun.COM pkinit_cert_matching(krb5_context context,
7497934SMark.Phalan@Sun.COM 		     pkinit_plg_crypto_context plg_cryptoctx,
7507934SMark.Phalan@Sun.COM 		     pkinit_req_crypto_context req_cryptoctx,
7517934SMark.Phalan@Sun.COM 		     pkinit_identity_crypto_context id_cryptoctx,
752*12945Swill.fiveash@oracle.com 		     krb5_principal princ,
753*12945Swill.fiveash@oracle.com                      krb5_boolean do_select)
7547934SMark.Phalan@Sun.COM {
7557934SMark.Phalan@Sun.COM 
7567934SMark.Phalan@Sun.COM     krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
7577934SMark.Phalan@Sun.COM     int x;
7587934SMark.Phalan@Sun.COM     char **rules = NULL;
7597934SMark.Phalan@Sun.COM     rule_set *rs = NULL;
7607934SMark.Phalan@Sun.COM     int match_found = 0;
7617934SMark.Phalan@Sun.COM     pkinit_cert_matching_data **matchdata = NULL;
7627934SMark.Phalan@Sun.COM     pkinit_cert_matching_data *the_matching_cert = NULL;
7637934SMark.Phalan@Sun.COM 
7647934SMark.Phalan@Sun.COM     /* If no matching rules, select the default cert and we're done */
7657934SMark.Phalan@Sun.COM     (void) pkinit_libdefault_strings(context, krb5_princ_realm(context, princ),
7667934SMark.Phalan@Sun.COM 			      "pkinit_cert_match", &rules);
7677934SMark.Phalan@Sun.COM     if (rules == NULL) {
7687934SMark.Phalan@Sun.COM 	pkiDebug("%s: no matching rules found in config file\n", __FUNCTION__);
769*12945Swill.fiveash@oracle.com         if (do_select == TRUE) {
770*12945Swill.fiveash@oracle.com             retval = crypto_cert_select_default(context, plg_cryptoctx,
771*12945Swill.fiveash@oracle.com                                                 req_cryptoctx, id_cryptoctx);
772*12945Swill.fiveash@oracle.com         } else
773*12945Swill.fiveash@oracle.com             retval = 0;
7747934SMark.Phalan@Sun.COM 	goto cleanup;
7757934SMark.Phalan@Sun.COM     }
7767934SMark.Phalan@Sun.COM 
7777934SMark.Phalan@Sun.COM     /* parse each rule line one at a time and check all the certs against it */
7787934SMark.Phalan@Sun.COM     for (x = 0; rules[x] != NULL; x++) {
7797934SMark.Phalan@Sun.COM 	pkiDebug("%s: Processing rule '%s'\n", __FUNCTION__, rules[x]);
7807934SMark.Phalan@Sun.COM 
7817934SMark.Phalan@Sun.COM 	/* Free rules from previous time through... */
7827934SMark.Phalan@Sun.COM 	if (rs != NULL) {
7837934SMark.Phalan@Sun.COM 	    (void) free_rule_set(context, rs);
7847934SMark.Phalan@Sun.COM 	    rs = NULL;
7857934SMark.Phalan@Sun.COM 	}
7867934SMark.Phalan@Sun.COM 	retval = parse_rule_set(context, rules[x], &rs);
7877934SMark.Phalan@Sun.COM 	if (retval) {
7887934SMark.Phalan@Sun.COM 	    if (retval == EINVAL) {
7897934SMark.Phalan@Sun.COM 		pkiDebug("%s: Ignoring invalid rule pkinit_cert_match = '%s'\n",
7907934SMark.Phalan@Sun.COM 			 __FUNCTION__, rules[x]);
7917934SMark.Phalan@Sun.COM 		continue;
7927934SMark.Phalan@Sun.COM 	    }
7937934SMark.Phalan@Sun.COM 	    goto cleanup;
7947934SMark.Phalan@Sun.COM 	}
7957934SMark.Phalan@Sun.COM 
7967934SMark.Phalan@Sun.COM 	/*
7977934SMark.Phalan@Sun.COM 	 * Optimize so that we do not get cert info unless we have
7987934SMark.Phalan@Sun.COM 	 * valid rules to check.  Once obtained, keep it around
7997934SMark.Phalan@Sun.COM 	 * until we are done.
8007934SMark.Phalan@Sun.COM 	 */
8017934SMark.Phalan@Sun.COM 	if (matchdata == NULL) {
8027934SMark.Phalan@Sun.COM 	    retval = obtain_all_cert_matching_data(context, plg_cryptoctx,
8037934SMark.Phalan@Sun.COM 						   req_cryptoctx, id_cryptoctx,
8047934SMark.Phalan@Sun.COM 						   &matchdata);
8057934SMark.Phalan@Sun.COM 	    if (retval || matchdata == NULL) {
8067934SMark.Phalan@Sun.COM 		pkiDebug("%s: Error %d obtaining certificate information\n",
8077934SMark.Phalan@Sun.COM 			 __FUNCTION__, retval);
8087934SMark.Phalan@Sun.COM 		retval = ENOENT;
8097934SMark.Phalan@Sun.COM 		goto cleanup;
8107934SMark.Phalan@Sun.COM 	    }
8117934SMark.Phalan@Sun.COM 	}
8127934SMark.Phalan@Sun.COM 
8137934SMark.Phalan@Sun.COM 	retval = check_all_certs(context, plg_cryptoctx, req_cryptoctx,
8147934SMark.Phalan@Sun.COM 				 id_cryptoctx, princ, rs, matchdata,
8157934SMark.Phalan@Sun.COM 				 &match_found, &the_matching_cert);
8167934SMark.Phalan@Sun.COM 	if (retval) {
8177934SMark.Phalan@Sun.COM 	    pkiDebug("%s: Error %d, checking certs against rule '%s'\n",
8187934SMark.Phalan@Sun.COM 		     __FUNCTION__, retval, rules[x]);
8197934SMark.Phalan@Sun.COM 	    goto cleanup;
8207934SMark.Phalan@Sun.COM 	}
8217934SMark.Phalan@Sun.COM 	if (match_found) {
8227934SMark.Phalan@Sun.COM 	    pkiDebug("%s: We have an exact match with rule '%s'\n",
8237934SMark.Phalan@Sun.COM 		     __FUNCTION__, rules[x]);
8247934SMark.Phalan@Sun.COM 	    break;
8257934SMark.Phalan@Sun.COM 	}
8267934SMark.Phalan@Sun.COM     }
8277934SMark.Phalan@Sun.COM 
8287934SMark.Phalan@Sun.COM     if (match_found && the_matching_cert != NULL) {
829*12945Swill.fiveash@oracle.com         if (do_select == TRUE) {
830*12945Swill.fiveash@oracle.com             pkiDebug("%s: Selecting the matching cert!\n", __FUNCTION__);
831*12945Swill.fiveash@oracle.com             retval = crypto_cert_select(context, the_matching_cert);
832*12945Swill.fiveash@oracle.com             if (retval) {
833*12945Swill.fiveash@oracle.com                 pkiDebug("%s: crypto_cert_select error %d, %s\n",
834*12945Swill.fiveash@oracle.com                          __FUNCTION__, retval, error_message(retval));
835*12945Swill.fiveash@oracle.com                 goto cleanup;
836*12945Swill.fiveash@oracle.com             }
837*12945Swill.fiveash@oracle.com         }
8387934SMark.Phalan@Sun.COM     } else {
8397934SMark.Phalan@Sun.COM 	retval = ENOENT;    /* XXX */
8407934SMark.Phalan@Sun.COM 	goto cleanup;
8417934SMark.Phalan@Sun.COM     }
8427934SMark.Phalan@Sun.COM 
8437934SMark.Phalan@Sun.COM     retval = 0;
8447934SMark.Phalan@Sun.COM cleanup:
8457934SMark.Phalan@Sun.COM     if (rules != NULL)
8467934SMark.Phalan@Sun.COM 	profile_free_list(rules);
8477934SMark.Phalan@Sun.COM     if (rs != NULL)
8487934SMark.Phalan@Sun.COM 	/* Solaris Kerberos */
8497934SMark.Phalan@Sun.COM 	(void) free_rule_set(context, rs);
8507934SMark.Phalan@Sun.COM     if (matchdata != NULL)
8517934SMark.Phalan@Sun.COM 	(void) free_all_cert_matching_data(context, matchdata);
8527934SMark.Phalan@Sun.COM     return retval;
8537934SMark.Phalan@Sun.COM }
854