2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * COPYRIGHT (C) 2007
2N/A * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
2N/A * ALL RIGHTS RESERVED
2N/A *
2N/A * Permission is granted to use, copy, create derivative works
2N/A * and redistribute this software and such derivative works
2N/A * for any purpose, so long as the name of The University of
2N/A * Michigan is not used in any advertising or publicity
2N/A * pertaining to the use of distribution of this software
2N/A * without specific, written prior authorization. If the
2N/A * above copyright notice or any other identification of the
2N/A * University of Michigan is included in any copy of any
2N/A * portion of this software, then the disclaimer below must
2N/A * also be included.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
2N/A * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
2N/A * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
2N/A * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
2N/A * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
2N/A * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
2N/A * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
2N/A * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
2N/A * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
2N/A * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
2N/A * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
2N/A * SUCH DAMAGES.
2N/A */
2N/A
2N/A#include <errno.h>
2N/A#include <string.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <regex.h>
2N/A#include "pkinit.h"
2N/A
2N/Atypedef struct _pkinit_cert_info pkinit_cert_info;
2N/A
2N/Atypedef enum {
2N/A kw_undefined = 0,
2N/A kw_subject = 1,
2N/A kw_issuer = 2,
2N/A kw_san = 3,
2N/A kw_eku = 4,
2N/A kw_ku = 5
2N/A} keyword_type;
2N/A
2N/Astatic char *
2N/Akeyword2string(unsigned int kw)
2N/A{
2N/A switch(kw) {
2N/A case kw_undefined: return "NONE"; break;
2N/A case kw_subject: return "SUBJECT"; break;
2N/A case kw_issuer: return "ISSUER"; break;
2N/A case kw_san: return "SAN"; break;
2N/A case kw_eku: return "EKU"; break;
2N/A case kw_ku: return "KU"; break;
2N/A default: return "INVALID"; break;
2N/A }
2N/A}
2N/Atypedef enum {
2N/A relation_none = 0,
2N/A relation_and = 1,
2N/A relation_or = 2
2N/A} relation_type;
2N/A
2N/Astatic char *
2N/Arelation2string(unsigned int rel)
2N/A{
2N/A switch(rel) {
2N/A case relation_none: return "NONE"; break;
2N/A case relation_and: return "AND"; break;
2N/A case relation_or: return "OR"; break;
2N/A default: return "INVALID"; break;
2N/A }
2N/A}
2N/A
2N/Atypedef enum {
2N/A kwvaltype_undefined = 0,
2N/A kwvaltype_regexp = 1,
2N/A kwvaltype_list = 2
2N/A} kw_value_type;
2N/A
2N/Astatic char *
2N/Akwval2string(unsigned int kwval)
2N/A{
2N/A switch(kwval) {
2N/A case kwvaltype_undefined: return "NONE"; break;
2N/A case kwvaltype_regexp: return "REGEXP"; break;
2N/A case kwvaltype_list: return "LIST"; break;
2N/A default: return "INVALID"; break;
2N/A }
2N/A}
2N/A
2N/Astruct keyword_desc {
2N/A const char *value;
2N/A size_t length;
2N/A keyword_type kwtype;
2N/A kw_value_type kwvaltype;
2N/A} matching_keywords[] = {
2N/A { "<KU>", 4, kw_ku, kwvaltype_list },
2N/A { "<EKU>", 5, kw_eku, kwvaltype_list },
2N/A { "<SAN>", 5, kw_san, kwvaltype_regexp },
2N/A { "<ISSUER>", 8, kw_issuer, kwvaltype_regexp },
2N/A { "<SUBJECT>", 9, kw_subject, kwvaltype_regexp },
2N/A { NULL, 0, kw_undefined, kwvaltype_undefined},
2N/A};
2N/A
2N/Astruct ku_desc {
2N/A const char *value;
2N/A size_t length;
2N/A unsigned int bitval;
2N/A};
2N/A
2N/Astruct ku_desc ku_keywords[] = {
2N/A { "digitalSignature", 16, PKINIT_KU_DIGITALSIGNATURE },
2N/A { "keyEncipherment", 15, PKINIT_KU_KEYENCIPHERMENT },
2N/A { NULL, 0, 0 },
2N/A};
2N/A
2N/Astruct ku_desc eku_keywords[] = {
2N/A { "pkinit", 6, PKINIT_EKU_PKINIT },
2N/A { "msScLogin", 9, PKINIT_EKU_MSSCLOGIN },
2N/A { "clientAuth", 10, PKINIT_EKU_CLIENTAUTH },
2N/A { "emailProtection", 15, PKINIT_EKU_EMAILPROTECTION },
2N/A { NULL, 0, 0 },
2N/A};
2N/A
2N/A/* Rule component */
2N/Atypedef struct _rule_component {
2N/A struct _rule_component *next;
2N/A keyword_type kw_type;
2N/A kw_value_type kwval_type;
2N/A regex_t regexp; /* Compiled regular expression */
2N/A char *regsrc; /* The regular expression source (for debugging) */
2N/A unsigned int ku_bits;
2N/A unsigned int eku_bits;
2N/A} rule_component;
2N/A
2N/A/* Set rule components */
2N/Atypedef struct _rule_set {
2N/A relation_type relation;
2N/A int num_crs;
2N/A rule_component *crs;
2N/A} rule_set;
2N/A
2N/Astatic krb5_error_code
2N/Afree_rule_component(krb5_context context,
2N/A rule_component *rc)
2N/A{
2N/A if (rc == NULL)
2N/A return 0;
2N/A
2N/A if (rc->kwval_type == kwvaltype_regexp) {
2N/A free(rc->regsrc);
2N/A regfree(&rc->regexp);
2N/A }
2N/A free(rc);
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Afree_rule_set(krb5_context context,
2N/A rule_set *rs)
2N/A{
2N/A rule_component *rc, *trc;
2N/A
2N/A if (rs == NULL)
2N/A return 0;
2N/A for (rc = rs->crs; rc != NULL;) {
2N/A trc = rc->next;
2N/A free_rule_component(context, rc);
2N/A rc = trc;
2N/A }
2N/A free(rs);
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Aparse_list_value(krb5_context context,
2N/A keyword_type type,
2N/A char *value,
2N/A rule_component *rc)
2N/A{
2N/A krb5_error_code retval;
2N/A char *comma;
2N/A struct ku_desc *ku = NULL;
2N/A int found;
2N/A size_t len;
2N/A unsigned int *bitptr;
2N/A
2N/A
2N/A if (value == NULL || value[0] == '\0') {
2N/A pkiDebug("%s: Missing or empty value for list keyword type %d\n",
2N/A __FUNCTION__, type);
2N/A retval = EINVAL;
2N/A goto out;
2N/A }
2N/A
2N/A if (type == kw_eku) {
2N/A bitptr = &rc->eku_bits;
2N/A } else if (type == kw_ku) {
2N/A bitptr = &rc->ku_bits;
2N/A } else {
2N/A pkiDebug("%s: Unknown list keyword type %d\n", __FUNCTION__, type);
2N/A retval = EINVAL;
2N/A goto out;
2N/A }
2N/A
2N/A do {
2N/A found = 0;
2N/A comma = strchr(value, ',');
2N/A if (comma != NULL)
2N/A len = comma - value;
2N/A else
2N/A len = strlen(value);
2N/A
2N/A if (type == kw_eku) {
2N/A ku = eku_keywords;
2N/A } else if (type == kw_ku) {
2N/A ku = ku_keywords;
2N/A }
2N/A
2N/A for (; ku->value != NULL; ku++) {
2N/A if (strncasecmp(value, ku->value, len) == 0) {
2N/A *bitptr |= ku->bitval;
2N/A found = 1;
2N/A pkiDebug("%s: Found value '%s', bitfield is now 0x%x\n",
2N/A __FUNCTION__, ku->value, *bitptr);
2N/A break;
2N/A }
2N/A }
2N/A if (found) {
2N/A value += ku->length;
2N/A if (*value == ',')
2N/A value += 1;
2N/A } else {
2N/A pkiDebug("%s: Urecognized value '%s'\n", __FUNCTION__, value);
2N/A retval = EINVAL;
2N/A goto out;
2N/A }
2N/A } while (found && *value != '\0');
2N/A
2N/A retval = 0;
2N/Aout:
2N/A pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
2N/A return retval;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Aparse_rule_component(krb5_context context,
2N/A const char **rule,
2N/A int *remaining,
2N/A rule_component **ret_rule)
2N/A{
2N/A krb5_error_code retval;
2N/A rule_component *rc = NULL;
2N/A keyword_type kw_type;
2N/A kw_value_type kwval_type;
2N/A char err_buf[128];
2N/A int ret;
2N/A struct keyword_desc *kw, *nextkw;
2N/A char *nk;
2N/A int found_next_kw = 0;
2N/A char *value = NULL;
2N/A size_t len;
2N/A
2N/A for (kw = matching_keywords; kw->value != NULL; kw++) {
2N/A if (strncmp(*rule, kw->value, kw->length) == 0) {
2N/A kw_type = kw->kwtype;
2N/A kwval_type = kw->kwvaltype;
2N/A *rule += kw->length;
2N/A *remaining -= kw->length;
2N/A break;
2N/A }
2N/A }
2N/A if (kw->value == NULL) {
2N/A pkiDebug("%s: Missing or invalid keyword in rule '%s'\n",
2N/A __FUNCTION__, *rule);
2N/A retval = ENOENT;
2N/A goto out;
2N/A }
2N/A
2N/A pkiDebug("%s: found keyword '%s'\n", __FUNCTION__, kw->value);
2N/A
2N/A rc = calloc(1, sizeof(*rc));
2N/A if (rc == NULL) {
2N/A retval = ENOMEM;
2N/A goto out;
2N/A }
2N/A rc->next = NULL;
2N/A rc->kw_type = kw_type;
2N/A rc->kwval_type = kwval_type;
2N/A
2N/A /*
2N/A * Before procesing the value for this keyword,
2N/A * (compiling the regular expression or processing the list)
2N/A * we need to find the end of it. That means parsing for the
2N/A * beginning of the next keyword (or the end of the rule).
2N/A */
2N/A nk = strchr(*rule, '<');
2N/A while (nk != NULL) {
2N/A /* Possibly another keyword, check it out */
2N/A for (nextkw = matching_keywords; nextkw->value != NULL; nextkw++) {
2N/A if (strncmp(nk, nextkw->value, nextkw->length) == 0) {
2N/A /* Found a keyword, nk points to the beginning */
2N/A found_next_kw = 1;
2N/A break; /* Need to break out of the while! */
2N/A }
2N/A }
2N/A if (!found_next_kw)
2N/A nk = strchr(nk+1, '<'); /* keep looking */
2N/A else
2N/A break;
2N/A }
2N/A
2N/A if (nk != NULL && found_next_kw)
2N/A len = (nk - *rule);
2N/A else
2N/A len = (*remaining);
2N/A
2N/A if (len == 0) {
2N/A pkiDebug("%s: Missing value for keyword '%s'\n",
2N/A __FUNCTION__, kw->value);
2N/A retval = EINVAL;
2N/A goto out;
2N/A }
2N/A
2N/A value = calloc(1, len+1);
2N/A if (value == NULL) {
2N/A retval = ENOMEM;
2N/A goto out;
2N/A }
2N/A memcpy(value, *rule, len);
2N/A *remaining -= len;
2N/A *rule += len;
2N/A pkiDebug("%s: found value '%s'\n", __FUNCTION__, value);
2N/A
2N/A if (kw->kwvaltype == kwvaltype_regexp) {
2N/A ret = regcomp(&rc->regexp, value, REG_EXTENDED);
2N/A if (ret) {
2N/A regerror(ret, &rc->regexp, err_buf, sizeof(err_buf));
2N/A pkiDebug("%s: Error compiling reg-exp '%s': %s\n",
2N/A __FUNCTION__, value, err_buf);
2N/A retval = ret;
2N/A goto out;
2N/A }
2N/A rc->regsrc = strdup(value);
2N/A if (rc->regsrc == NULL) {
2N/A retval = ENOMEM;
2N/A goto out;
2N/A }
2N/A } else if (kw->kwvaltype == kwvaltype_list) {
2N/A retval = parse_list_value(context, rc->kw_type, value, rc);
2N/A if (retval) {
2N/A pkiDebug("%s: Error %d, parsing list values for keyword %s\n",
2N/A __FUNCTION__, retval, kw->value);
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A *ret_rule = rc;
2N/A retval = 0;
2N/Aout:
2N/A free(value);
2N/A if (retval && rc != NULL)
2N/A free_rule_component(context, rc);
2N/A pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
2N/A return retval;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Aparse_rule_set(krb5_context context,
2N/A const char *rule_in,
2N/A rule_set **out_rs)
2N/A{
2N/A const char *rule;
2N/A int remaining, totlen;
2N/A krb5_error_code ret, retval;
2N/A rule_component *rc = NULL, *trc;
2N/A rule_set *rs;
2N/A
2N/A
2N/A if (rule_in == NULL)
2N/A return EINVAL;
2N/A rule = rule_in;
2N/A totlen = remaining = strlen(rule);
2N/A
2N/A rs = calloc(1, sizeof(*rs));
2N/A if (rs == NULL) {
2N/A retval = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A rs->relation = relation_none;
2N/A if (remaining > 1) {
2N/A if (rule[0] == '&' && rule[1] == '&') {
2N/A rs->relation = relation_and;
2N/A rule += 2;
2N/A remaining -= 2;
2N/A } else if (rule_in[0] == '|' && rule_in[1] == '|') {
2N/A rs->relation = relation_or;
2N/A rule +=2;
2N/A remaining -= 2;
2N/A }
2N/A }
2N/A rs->num_crs = 0;
2N/A while (remaining > 0) {
2N/A if (rs->relation == relation_none && rs->num_crs > 1) {
2N/A pkiDebug("%s: Assuming AND relation for multiple components in rule '%s'\n",
2N/A __FUNCTION__, rule_in);
2N/A rs->relation = relation_and;
2N/A }
2N/A ret = parse_rule_component(context, &rule, &remaining, &rc);
2N/A if (ret) {
2N/A retval = ret;
2N/A goto cleanup;
2N/A }
2N/A pkiDebug("%s: After parse_rule_component, remaining %d, rule '%s'\n",
2N/A __FUNCTION__, remaining, rule);
2N/A rs->num_crs++;
2N/A
2N/A /*
2N/A * Chain the new component on the end (order matters since
2N/A * we can short-circuit an OR or an AND relation if an
2N/A * earlier check passes
2N/A */
2N/A for (trc = rs->crs; trc != NULL && trc->next != NULL; trc = trc->next);
2N/A if (trc == NULL)
2N/A rs->crs = rc;
2N/A else {
2N/A trc->next = rc;
2N/A }
2N/A }
2N/A
2N/A *out_rs = rs;
2N/A
2N/A retval = 0;
2N/Acleanup:
2N/A if (retval && rs != NULL) {
2N/A free_rule_set(context, rs);
2N/A }
2N/A pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
2N/A return retval;
2N/A}
2N/A
2N/Astatic int
2N/Aregexp_match(krb5_context context, rule_component *rc, char *value)
2N/A{
2N/A int code;
2N/A
2N/A pkiDebug("%s: checking %s rule '%s' with value '%s'\n",
2N/A __FUNCTION__, keyword2string(rc->kw_type), rc->regsrc, value);
2N/A
2N/A code = regexec(&rc->regexp, value, 0, NULL, 0);
2N/A
2N/A pkiDebug("%s: the result is%s a match\n", __FUNCTION__,
2N/A code == REG_NOMATCH ? " NOT" : "");
2N/A
2N/A return (code == 0 ? 1: 0);
2N/A}
2N/A
2N/Astatic int
2N/Acomponent_match(krb5_context context,
2N/A rule_component *rc,
2N/A pkinit_cert_matching_data *md)
2N/A{
2N/A int match = 0;
2N/A int i;
2N/A krb5_principal p;
2N/A char *princ_string;
2N/A
2N/A switch (rc->kwval_type) {
2N/A case kwvaltype_regexp:
2N/A switch (rc->kw_type) {
2N/A case kw_subject:
2N/A match = regexp_match(context, rc, md->subject_dn);
2N/A break;
2N/A case kw_issuer:
2N/A match = regexp_match(context, rc, md->issuer_dn);
2N/A break;
2N/A case kw_san:
2N/A if (md->sans == NULL)
2N/A break;
2N/A for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) {
2N/A krb5_unparse_name(context, p, &princ_string);
2N/A match = regexp_match(context, rc, princ_string);
2N/A krb5_free_unparsed_name(context, princ_string);
2N/A if (match)
2N/A break;
2N/A }
2N/A break;
2N/A default:
2N/A pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
2N/A __FUNCTION__, keyword2string(rc->kw_type),
2N/A kwval2string(kwvaltype_regexp));
2N/A break;
2N/A }
2N/A break;
2N/A case kwvaltype_list:
2N/A switch(rc->kw_type) {
2N/A case kw_eku:
2N/A pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
2N/A __FUNCTION__, keyword2string(rc->kw_type),
2N/A rc->eku_bits, md->eku_bits);
2N/A if ((rc->eku_bits & md->eku_bits) == rc->eku_bits)
2N/A match = 1;
2N/A break;
2N/A case kw_ku:
2N/A pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
2N/A __FUNCTION__, keyword2string(rc->kw_type),
2N/A rc->ku_bits, md->ku_bits);
2N/A if ((rc->ku_bits & md->ku_bits) == rc->ku_bits)
2N/A match = 1;
2N/A break;
2N/A default:
2N/A pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
2N/A __FUNCTION__, keyword2string(rc->kw_type),
2N/A kwval2string(kwvaltype_regexp));
2N/A break;
2N/A }
2N/A break;
2N/A default:
2N/A pkiDebug("%s: unknown keyword value type %d\n",
2N/A __FUNCTION__, rc->kwval_type);
2N/A break;
2N/A }
2N/A pkiDebug("%s: returning match = %d\n", __FUNCTION__, match);
2N/A return match;
2N/A}
2N/A/*
2N/A * Returns match_found == 1 only if exactly one certificate matches
2N/A * the given rule
2N/A */
2N/Astatic krb5_error_code
2N/Acheck_all_certs(krb5_context context,
2N/A pkinit_plg_crypto_context plg_cryptoctx,
2N/A pkinit_req_crypto_context req_cryptoctx,
2N/A pkinit_identity_crypto_context id_cryptoctx,
2N/A krb5_principal princ,
2N/A rule_set *rs, /* rule to check */
2N/A pkinit_cert_matching_data **matchdata,
2N/A int *match_found,
2N/A pkinit_cert_matching_data **matching_cert)
2N/A{
2N/A krb5_error_code retval;
2N/A pkinit_cert_matching_data *md;
2N/A int i;
2N/A int comp_match = 0;
2N/A int total_cert_matches = 0;
2N/A rule_component *rc;
2N/A int certs_checked = 0;
2N/A pkinit_cert_matching_data *save_match = NULL;
2N/A
2N/A if (match_found == NULL || matching_cert == NULL)
2N/A return EINVAL;
2N/A
2N/A *matching_cert = NULL;
2N/A *match_found = 0;
2N/A
2N/A pkiDebug("%s: matching rule relation is %s with %d components\n",
2N/A __FUNCTION__, relation2string(rs->relation), rs->num_crs);
2N/A
2N/A /*
2N/A * Loop through all the certs available and count
2N/A * how many match the rule
2N/A */
2N/A for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
2N/A pkiDebug("%s: subject: '%s'\n", __FUNCTION__, md->subject_dn);
2N/A#if 0
2N/A pkiDebug("%s: issuer: '%s'\n", __FUNCTION__, md->subject_dn);
2N/A for (j = 0, p = md->sans[j]; p != NULL; p = md->sans[++j]) {
2N/A char *san_string;
2N/A krb5_unparse_name(context, p, &san_string);
2N/A pkiDebug("%s: san: '%s'\n", __FUNCTION__, san_string);
2N/A krb5_free_unparsed_name(context, san_string);
2N/A }
2N/A#endif
2N/A certs_checked++;
2N/A for (rc = rs->crs; rc != NULL; rc = rc->next) {
2N/A comp_match = component_match(context, rc, md);
2N/A if (comp_match) {
2N/A pkiDebug("%s: match for keyword type %s\n",
2N/A __FUNCTION__, keyword2string(rc->kw_type));
2N/A }
2N/A if (comp_match && rs->relation == relation_or) {
2N/A pkiDebug("%s: cert matches rule (OR relation)\n",
2N/A __FUNCTION__);
2N/A total_cert_matches++;
2N/A save_match = md;
2N/A goto nextcert;
2N/A }
2N/A if (!comp_match && rs->relation == relation_and) {
2N/A pkiDebug("%s: cert does not match rule (AND relation)\n",
2N/A __FUNCTION__);
2N/A goto nextcert;
2N/A }
2N/A }
2N/A if (rc == NULL && comp_match) {
2N/A pkiDebug("%s: cert matches rule (AND relation)\n", __FUNCTION__);
2N/A total_cert_matches++;
2N/A save_match = md;
2N/A }
2N/A nextcert:
2N/A continue;
2N/A }
2N/A pkiDebug("%s: After checking %d certs, we found %d matches\n",
2N/A __FUNCTION__, certs_checked, total_cert_matches);
2N/A if (total_cert_matches == 1) {
2N/A *match_found = 1;
2N/A *matching_cert = save_match;
2N/A }
2N/A
2N/A retval = 0;
2N/A
2N/A pkiDebug("%s: returning %d, match_found %d\n",
2N/A __FUNCTION__, retval, *match_found);
2N/A return retval;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Afree_all_cert_matching_data(krb5_context context,
2N/A pkinit_cert_matching_data **matchdata)
2N/A{
2N/A krb5_error_code retval;
2N/A pkinit_cert_matching_data *md;
2N/A int i;
2N/A
2N/A if (matchdata == NULL)
2N/A return EINVAL;
2N/A
2N/A for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
2N/A pkinit_cert_handle ch = md->ch;
2N/A retval = crypto_cert_free_matching_data(context, md);
2N/A if (retval) {
2N/A pkiDebug("%s: crypto_cert_free_matching_data error %d, %s\n",
2N/A __FUNCTION__, retval, error_message(retval));
2N/A goto cleanup;
2N/A }
2N/A retval = crypto_cert_release(context, ch);
2N/A if (retval) {
2N/A pkiDebug("%s: crypto_cert_release error %d, %s\n",
2N/A __FUNCTION__, retval, error_message(retval));
2N/A goto cleanup;
2N/A }
2N/A }
2N/A free(matchdata);
2N/A retval = 0;
2N/A
2N/Acleanup:
2N/A return retval;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Aobtain_all_cert_matching_data(krb5_context context,
2N/A pkinit_plg_crypto_context plg_cryptoctx,
2N/A pkinit_req_crypto_context req_cryptoctx,
2N/A pkinit_identity_crypto_context id_cryptoctx,
2N/A pkinit_cert_matching_data ***all_matching_data)
2N/A{
2N/A krb5_error_code retval;
2N/A int i, cert_count;
2N/A pkinit_cert_iter_handle ih = NULL;
2N/A pkinit_cert_handle ch;
2N/A pkinit_cert_matching_data **matchdata = NULL;
2N/A
2N/A retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx,
2N/A id_cryptoctx, &cert_count);
2N/A if (retval) {
2N/A pkiDebug("%s: crypto_cert_get_count error %d, %s\n",
2N/A __FUNCTION__, retval, error_message(retval));
2N/A goto cleanup;
2N/A }
2N/A
2N/A pkiDebug("%s: crypto_cert_get_count says there are %d certs\n",
2N/A __FUNCTION__, cert_count);
2N/A
2N/A matchdata = calloc((size_t)cert_count + 1, sizeof(*matchdata));
2N/A if (matchdata == NULL)
2N/A return ENOMEM;
2N/A
2N/A retval = crypto_cert_iteration_begin(context, plg_cryptoctx, req_cryptoctx,
2N/A id_cryptoctx, &ih);
2N/A if (retval) {
2N/A pkiDebug("%s: crypto_cert_iteration_begin returned %d, %s\n",
2N/A __FUNCTION__, retval, error_message(retval));
2N/A goto cleanup;
2N/A }
2N/A
2N/A for (i = 0; i < cert_count; i++) {
2N/A retval = crypto_cert_iteration_next(context, ih, &ch);
2N/A if (retval) {
2N/A if (retval == PKINIT_ITER_NO_MORE)
2N/A pkiDebug("%s: We thought there were %d certs, but "
2N/A "crypto_cert_iteration_next stopped after %d?\n",
2N/A __FUNCTION__, cert_count, i);
2N/A else
2N/A pkiDebug("%s: crypto_cert_iteration_next error %d, %s\n",
2N/A __FUNCTION__, retval, error_message(retval));
2N/A goto cleanup;
2N/A }
2N/A
2N/A retval = crypto_cert_get_matching_data(context, ch, &matchdata[i]);
2N/A if (retval) {
2N/A pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n",
2N/A __FUNCTION__, retval, error_message(retval));
2N/A goto cleanup;
2N/A }
2N/A
2N/A }
2N/A
2N/A *all_matching_data = matchdata;
2N/A retval = 0;
2N/Acleanup:
2N/A if (ih != NULL)
2N/A crypto_cert_iteration_end(context, ih);
2N/A if (retval) {
2N/A if (matchdata != NULL)
2N/A free_all_cert_matching_data(context, matchdata);
2N/A }
2N/A pkiDebug("%s: returning %d, certinfo %p\n",
2N/A __FUNCTION__, retval, *all_matching_data);
2N/A return retval;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Apkinit_cert_matching(krb5_context context,
2N/A pkinit_plg_crypto_context plg_cryptoctx,
2N/A pkinit_req_crypto_context req_cryptoctx,
2N/A pkinit_identity_crypto_context id_cryptoctx,
2N/A krb5_principal princ,
2N/A krb5_boolean do_select) /* Solaris Kerberos */
2N/A
2N/A{
2N/A
2N/A krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
2N/A int x;
2N/A char **rules = NULL;
2N/A rule_set *rs = NULL;
2N/A int match_found = 0;
2N/A pkinit_cert_matching_data **matchdata = NULL;
2N/A pkinit_cert_matching_data *the_matching_cert = NULL;
2N/A
2N/A /* If no matching rules, select the default cert and we're done */
2N/A pkinit_libdefault_strings(context, krb5_princ_realm(context, princ),
2N/A KRB5_CONF_PKINIT_CERT_MATCH, &rules);
2N/A if (rules == NULL) {
2N/A pkiDebug("%s: no matching rules found in config file\n", __FUNCTION__);
2N/A if (do_select == TRUE) {
2N/A retval = crypto_cert_select_default(context, plg_cryptoctx,
2N/A req_cryptoctx, id_cryptoctx);
2N/A } else
2N/A retval = 0;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* parse each rule line one at a time and check all the certs against it */
2N/A for (x = 0; rules[x] != NULL; x++) {
2N/A pkiDebug("%s: Processing rule '%s'\n", __FUNCTION__, rules[x]);
2N/A
2N/A /* Free rules from previous time through... */
2N/A if (rs != NULL) {
2N/A free_rule_set(context, rs);
2N/A rs = NULL;
2N/A }
2N/A retval = parse_rule_set(context, rules[x], &rs);
2N/A if (retval) {
2N/A if (retval == EINVAL) {
2N/A pkiDebug("%s: Ignoring invalid rule pkinit_cert_match = '%s'\n",
2N/A __FUNCTION__, rules[x]);
2N/A continue;
2N/A }
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Optimize so that we do not get cert info unless we have
2N/A * valid rules to check. Once obtained, keep it around
2N/A * until we are done.
2N/A */
2N/A if (matchdata == NULL) {
2N/A retval = obtain_all_cert_matching_data(context, plg_cryptoctx,
2N/A req_cryptoctx, id_cryptoctx,
2N/A &matchdata);
2N/A if (retval || matchdata == NULL) {
2N/A pkiDebug("%s: Error %d obtaining certificate information\n",
2N/A __FUNCTION__, retval);
2N/A retval = ENOENT;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A retval = check_all_certs(context, plg_cryptoctx, req_cryptoctx,
2N/A id_cryptoctx, princ, rs, matchdata,
2N/A &match_found, &the_matching_cert);
2N/A if (retval) {
2N/A pkiDebug("%s: Error %d, checking certs against rule '%s'\n",
2N/A __FUNCTION__, retval, rules[x]);
2N/A goto cleanup;
2N/A }
2N/A if (match_found) {
2N/A pkiDebug("%s: We have an exact match with rule '%s'\n",
2N/A __FUNCTION__, rules[x]);
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (match_found && the_matching_cert != NULL) {
2N/A if (do_select == TRUE) {
2N/A pkiDebug("%s: Selecting the matching cert!\n", __FUNCTION__);
2N/A retval = crypto_cert_select(context, the_matching_cert);
2N/A if (retval) {
2N/A pkiDebug("%s: crypto_cert_select error %d, %s\n",
2N/A __FUNCTION__, retval, error_message(retval));
2N/A goto cleanup;
2N/A }
2N/A }
2N/A } else {
2N/A retval = ENOENT; /* XXX */
2N/A goto cleanup;
2N/A }
2N/A
2N/A retval = 0;
2N/Acleanup:
2N/A if (rules != NULL)
2N/A profile_free_list(rules);
2N/A if (rs != NULL)
2N/A free_rule_set(context, rs);
2N/A if (matchdata != NULL)
2N/A free_all_cert_matching_data(context, matchdata);
2N/A return retval;
2N/A}