krb5_error_code
pkinit_libdefault_string(krb5_context context, const krb5_data *realm,
			 const char *option, char **ret_value)
{
    krb5_error_code retval;
    char **values = NULL;

    retval = pkinit_libdefault_strings(context, realm, option, &values);
    if (retval)
	return retval;

    if (values[0] == NULL) {
	retval = ENOENT;
    } else {
	*ret_value = strdup(values[0]);
	if (*ret_value == NULL)
	    retval = ENOMEM;
    }

    profile_free_list(values);
    return retval;
}
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_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
    int x;
    char **rules = NULL;
    rule_set *rs = NULL;
    int match_found = 0;
    pkinit_cert_matching_data **matchdata = NULL;
    pkinit_cert_matching_data *the_matching_cert = NULL;

    /* If no matching rules, select the default cert and we're done */
    pkinit_libdefault_strings(context, krb5_princ_realm(context, princ),
			      KRB5_CONF_PKINIT_CERT_MATCH, &rules);
    if (rules == NULL) {
	pkiDebug("%s: no matching rules found in config file\n", __FUNCTION__);
	retval = crypto_cert_select_default(context, plg_cryptoctx,
					    req_cryptoctx, id_cryptoctx);
	goto cleanup;
    }

    /* parse each rule line one at a time and check all the certs against it */
    for (x = 0; rules[x] != NULL; x++) {
	pkiDebug("%s: Processing rule '%s'\n", __FUNCTION__, rules[x]);

	/* Free rules from previous time through... */
	if (rs != NULL) {
	    free_rule_set(context, rs);
	    rs = NULL;
	}
	retval = parse_rule_set(context, rules[x], &rs);
	if (retval) {
	    if (retval == EINVAL) {
		pkiDebug("%s: Ignoring invalid rule pkinit_cert_match = '%s'\n",
			 __FUNCTION__, rules[x]); 
		continue;
	    }
	    goto cleanup;
	}

	/*
	 * Optimize so that we do not get cert info unless we have
	 * valid rules to check.  Once obtained, keep it around
	 * until we are done.
	 */
	if (matchdata == NULL) {
	    retval = obtain_all_cert_matching_data(context, plg_cryptoctx,
						   req_cryptoctx, id_cryptoctx,
						   &matchdata);
	    if (retval || matchdata == NULL) {
		pkiDebug("%s: Error %d obtaining certificate information\n",
			 __FUNCTION__, retval);
		retval = ENOENT;
		goto cleanup;
	    }
	}

	retval = check_all_certs(context, plg_cryptoctx, req_cryptoctx,
				 id_cryptoctx, princ, rs, matchdata,
				 &match_found, &the_matching_cert);
	if (retval) {
	    pkiDebug("%s: Error %d, checking certs against rule '%s'\n",
		     __FUNCTION__, retval, rules[x]);
	    goto cleanup;
	}
	if (match_found) {
	    pkiDebug("%s: We have an exact match with rule '%s'\n",
		     __FUNCTION__, rules[x]);
	    break;
	}
    }

    if (match_found && the_matching_cert != NULL) {
	pkiDebug("%s: Selecting the matching cert!\n", __FUNCTION__);
	retval = crypto_cert_select(context, the_matching_cert);
	if (retval) {
	    pkiDebug("%s: crypto_cert_select error %d, %s\n",
		     __FUNCTION__, retval, error_message(retval));
	    goto cleanup;
	}
    } else {
	retval = ENOENT;    /* XXX */
	goto cleanup;
    }

    retval = 0;
cleanup:
    if (rules != NULL)
	profile_free_list(rules);
    if (rs != NULL)
	free_rule_set(context, rs);
    if (matchdata != NULL)
	free_all_cert_matching_data(context, matchdata);
    return retval;
}