Blame src/plugins/preauth/pkinit/pkinit_matching.c

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