pkinit_matching.c revision 159d09a20817016f09b3ea28d1bdada4a336bb91
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * COPYRIGHT (C) 2007
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * ALL RIGHTS RESERVED
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * Permission is granted to use, copy, create derivative works
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * and redistribute this software and such derivative works
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * for any purpose, so long as the name of The University of
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * Michigan is not used in any advertising or publicity
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * pertaining to the use of distribution of this software
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * without specific, written prior authorization. If the
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * above copyright notice or any other identification of the
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * University of Michigan is included in any copy of any
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * portion of this software, then the disclaimer below must
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * also be included.
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * SUCH DAMAGES.
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalantypedef struct _pkinit_cert_info pkinit_cert_info;
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalantypedef enum {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalanstatic char *
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* Solaris Kerberos: removed "break"s (lint) */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan default: return "INVALID";
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalantypedef enum {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalanstatic char *
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* Solaris Kerberos: removed "break"s (lint) */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan default: return "INVALID";
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalantypedef enum {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalanstatic char *
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* Solaris Kerberos: removed "break"s (lint) */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan default: return "INVALID";
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan const char *value;
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan { "<SUBJECT>", 9, kw_subject, kwvaltype_regexp },
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan const char *value;
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan unsigned int bitval;
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan { "digitalSignature", 16, PKINIT_KU_DIGITALSIGNATURE },
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan { "keyEncipherment", 15, PKINIT_KU_KEYENCIPHERMENT },
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan { "emailProtection", 15, PKINIT_EKU_EMAILPROTECTION },
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan/* Rule component */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalantypedef struct _rule_component {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan regex_t regexp; /* Compiled regular expression */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan char *regsrc; /* The regular expression source (for debugging) */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan unsigned int ku_bits;
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan unsigned int eku_bits;
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan/* Set rule components */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalantypedef struct _rule_set {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan/* ARGSUSED */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* Solaris Kerberos */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan/* ARGSUSED */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan unsigned int *bitptr;
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Missing or empty value for list keyword type %d\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Unknown list keyword type %d\n", __FUNCTION__, type);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Found value '%s', bitfield is now 0x%x\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Urecognized value '%s'\n", __FUNCTION__, value);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan const char **rule,
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan for (kw = matching_keywords; kw->value != NULL; kw++) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan if (strncmp(*rule, kw->value, kw->length) == 0) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Missing or invalid keyword in rule '%s'\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: found keyword '%s'\n", __FUNCTION__, kw->value);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * Before procesing the value for this keyword,
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * (compiling the regular expression or processing the list)
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * we need to find the end of it. That means parsing for the
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * beginning of the next keyword (or the end of the rule).
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* Possibly another keyword, check it out */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan for (nextkw = matching_keywords; nextkw->value != NULL; nextkw++) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan if (strncmp(nk, nextkw->value, nextkw->length) == 0) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* Found a keyword, nk points to the beginning */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan break; /* Need to break out of the while! */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Missing value for keyword '%s'\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: found value '%s'\n", __FUNCTION__, value);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan ret = regcomp(&rc->regexp, value, REG_EXTENDED);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan (void) regerror(ret, &rc->regexp, err_buf, sizeof(err_buf));
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Error compiling reg-exp '%s': %s\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan retval = parse_list_value(context, rc->kw_type, value, rc);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Error %d, parsing list values for keyword %s\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan/* ARGSUSED */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan const char *rule;
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* Solaris Kerberos */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* Solaris Kerberos */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan } else if (rule_in[0] == '|' && rule_in[1] == '|') {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan while (remaining > 0) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan if (rs->relation == relation_none && rs->num_crs > 1) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Assuming AND relation for multiple components in rule '%s'\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan ret = parse_rule_component(context, &rule, &remaining, &rc);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: After parse_rule_component, remaining %d, rule '%s'\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * Chain the new component on the end (order matters since
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * we can short-circuit an OR or an AND relation if an
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * earlier check passes
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan for (trc = rs->crs; trc != NULL && trc->next != NULL; trc = trc->next);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan/* ARGSUSED */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalanregexp_match(krb5_context context, rule_component *rc, char *value)
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: checking %s rule '%s' with value '%s'\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan __FUNCTION__, keyword2string(rc->kw_type), rc->regsrc, value);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: the result is%s a match\n", __FUNCTION__,
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan match = regexp_match(context, rc, md->subject_dn);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan match = regexp_match(context, rc, md->issuer_dn);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan match = regexp_match(context, rc, princ_string);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan if ((rc->eku_bits & md->eku_bits) == rc->eku_bits)
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: returning match = %d\n", __FUNCTION__, match);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * Returns match_found == 1 only if exactly one certificate matches
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * the given rule
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan/* ARGSUSED */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan if (match_found == NULL || matching_cert == NULL)
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: matching rule relation is %s with %d components\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan __FUNCTION__, relation2string(rs->relation), rs->num_crs);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * Loop through all the certs available and count
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * how many match the rule
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: subject: '%s'\n", __FUNCTION__, md->subject_dn);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: issuer: '%s'\n", __FUNCTION__, md->subject_dn);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan for (j = 0, p = md->sans[j]; p != NULL; p = md->sans[++j]) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: san: '%s'\n", __FUNCTION__, san_string);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan if (comp_match && rs->relation == relation_or) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: cert matches rule (OR relation)\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan if (!comp_match && rs->relation == relation_and) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: cert does not match rule (AND relation)\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: cert matches rule (AND relation)\n", __FUNCTION__);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: After checking %d certs, we found %d matches\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan __FUNCTION__, certs_checked, total_cert_matches);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalanfree_all_cert_matching_data(krb5_context context,
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan retval = crypto_cert_free_matching_data(context, md);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: crypto_cert_free_matching_data error %d, %s\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: crypto_cert_release error %d, %s\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalanobtain_all_cert_matching_data(krb5_context context,
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx,
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: crypto_cert_get_count error %d, %s\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: crypto_cert_get_count says there are %d certs\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan matchdata = calloc((size_t)cert_count + 1, sizeof(*matchdata));
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan retval = crypto_cert_iteration_begin(context, plg_cryptoctx, req_cryptoctx,
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: crypto_cert_iteration_begin returned %d, %s\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan for (i = 0; i < cert_count; i++) {
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan retval = crypto_cert_iteration_next(context, ih, &ch);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: We thought there were %d certs, but "
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan "crypto_cert_iteration_next stopped after %d?\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: crypto_cert_iteration_next error %d, %s\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan retval = crypto_cert_get_matching_data(context, ch, &matchdata[i]);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* Solaris Kerberos */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan (void) free_all_cert_matching_data(context, matchdata);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkinit_cert_matching_data *the_matching_cert = NULL;
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* If no matching rules, select the default cert and we're done */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan (void) pkinit_libdefault_strings(context, krb5_princ_realm(context, princ),
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: no matching rules found in config file\n", __FUNCTION__);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan retval = crypto_cert_select_default(context, plg_cryptoctx,
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* parse each rule line one at a time and check all the certs against it */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Processing rule '%s'\n", __FUNCTION__, rules[x]);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* Free rules from previous time through... */
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan retval = parse_rule_set(context, rules[x], &rs);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Ignoring invalid rule pkinit_cert_match = '%s'\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * Optimize so that we do not get cert info unless we have
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * valid rules to check. Once obtained, keep it around
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan * until we are done.
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan retval = obtain_all_cert_matching_data(context, plg_cryptoctx,
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Error %d obtaining certificate information\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan retval = check_all_certs(context, plg_cryptoctx, req_cryptoctx,
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Error %d, checking certs against rule '%s'\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: We have an exact match with rule '%s'\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: Selecting the matching cert!\n", __FUNCTION__);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan retval = crypto_cert_select(context, the_matching_cert);
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan pkiDebug("%s: crypto_cert_select error %d, %s\n",
159d09a20817016f09b3ea28d1bdada4a336bb91Mark Phalan /* Solaris Kerberos */