static krb5_error_code
free_all_cert_matching_data(krb5_context context,
			    pkinit_cert_matching_data **matchdata)
{
    krb5_error_code retval;
    pkinit_cert_matching_data *md;
    int i;

    if (matchdata == NULL)
	return EINVAL;

    for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
	pkinit_cert_handle ch = md->ch;
	retval = crypto_cert_free_matching_data(context, md);
	if (retval) {
	    pkiDebug("%s: crypto_cert_free_matching_data error %d, %s\n",
		     __FUNCTION__, retval, error_message(retval));
	    goto cleanup;
	}
	retval = crypto_cert_release(context, ch);
	if (retval) {
	    pkiDebug("%s: crypto_cert_release error %d, %s\n",
		     __FUNCTION__, retval, error_message(retval));
	    goto cleanup;
	}
    }
    free(matchdata);
    retval = 0;

cleanup:
    return retval;
}
static krb5_error_code
process_option_ca_crl(krb5_context context,
                      pkinit_plg_crypto_context plg_cryptoctx,
                      pkinit_req_crypto_context req_cryptoctx,
                      pkinit_identity_opts *idopts,
                      pkinit_identity_crypto_context id_cryptoctx,
                      const char *value,
                      int catype)
{
    char *residual;
    unsigned int typelen;
    int idtype;

    pkiDebug("%s: processing catype %s, value '%s'\n",
             __FUNCTION__, catype2string(catype), value);
    residual = strchr(value, ':');
    if (residual == NULL) {
        pkiDebug("No type given for '%s'\n", value);
        return EINVAL;
    }
    residual++; /* skip past colon */
    typelen = residual - value;
    if (strncmp(value, "FILE:", typelen) == 0) {
        idtype = IDTYPE_FILE;
    } else if (strncmp(value, "DIR:", typelen) == 0) {
        idtype = IDTYPE_DIR;
    } else {
        return ENOTSUP;
    }
    return crypto_load_cas_and_crls(context,
                                    plg_cryptoctx,
                                    req_cryptoctx,
                                    idopts, id_cryptoctx,
                                    idtype, catype, residual);
}
Exemple #3
0
static krb5_error_code
verify_client_eku(krb5_context context,
		  pkinit_kdc_context plgctx,
		  pkinit_kdc_req_context reqctx,
		  int *eku_accepted)
{
    krb5_error_code retval;

    *eku_accepted = 0;

    if (plgctx->opts->require_eku == 0) {
	pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
	*eku_accepted = 1;
	retval = 0;
	goto out;
    }

    retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
				   reqctx->cryptoctx, plgctx->idctx,
				   0, /* kdc cert */
				   plgctx->opts->accept_secondary_eku,
				   eku_accepted);
    if (retval) {
	pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
		 __FUNCTION__, retval, error_message(retval));
	goto out;
    }

out:
    pkiDebug("%s: returning retval %d, eku_accepted %d\n",
	     __FUNCTION__, retval, *eku_accepted);
    return retval;
}
Exemple #4
0
static int
pkinit_server_plugin_init_realm(krb5_context context, const char *realmname,
				pkinit_kdc_context *pplgctx)
{
    krb5_error_code retval = ENOMEM;
    pkinit_kdc_context plgctx = NULL;

    *pplgctx = NULL;

    plgctx = (pkinit_kdc_context) calloc(1, sizeof(*plgctx));
    if (plgctx == NULL)
	goto errout;

    pkiDebug("%s: initializing context at %p for realm '%s'\n",
	     __FUNCTION__, plgctx, realmname);
    memset(plgctx, 0, sizeof(*plgctx));
    plgctx->magic = PKINIT_CTX_MAGIC;

    plgctx->realmname = strdup(realmname);
    if (plgctx->realmname == NULL)
	goto errout;
    plgctx->realmname_len = strlen(plgctx->realmname);

    retval = pkinit_init_plg_crypto(&plgctx->cryptoctx);
    if (retval)
	goto errout;

    retval = pkinit_init_plg_opts(&plgctx->opts);
    if (retval)
	goto errout;

    retval = pkinit_init_identity_crypto(&plgctx->idctx);
    if (retval)
	goto errout;

    retval = pkinit_init_identity_opts(&plgctx->idopts);
    if (retval)
	goto errout;

    retval = pkinit_init_kdc_profile(context, plgctx);
    if (retval)
	goto errout;

    retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL,
					plgctx->idopts, plgctx->idctx, 0, NULL);
    if (retval)
	goto errout;

    pkiDebug("%s: returning context at %p for realm '%s'\n",
	     __FUNCTION__, plgctx, realmname);
    *pplgctx = plgctx;
    retval = 0;

errout:
    if (retval)
	pkinit_server_plugin_fini_realm(context, plgctx);

    return retval;
}
/*
 * Obtain signing cert for specified principal. On successful return,
 * caller must eventually release the cert with krb5_pkinit_release_cert().
 */
krb5_error_code krb5_pkinit_get_client_cert(
    const char                  *principal,     /* full principal string */
    krb5_pkinit_signing_cert_t  *client_cert)
{
    CFDataRef issuerSerial = NULL;
    CSSM_DATA issuerSerialData;
    SecIdentityRef idRef = NULL;
    OSStatus ortn;
    CFDictionaryRef theDict = NULL;
    CFStringRef cfPrinc = NULL;
    krb5_error_code ourRtn = 0;

    if(principal == NULL) {
        return KRB5_PRINC_NOMATCH;
    }

    /* Is there a stored preference for PKINIT certs for this user? */
    ortn = pkinit_get_pref_dict(&theDict);
    if(ortn) {
        return KRB5_PRINC_NOMATCH;
    }

    /* Entry in the dictionary for specified principal? */
    cfPrinc = CFStringCreateWithCString(NULL, principal,
                                        kCFStringEncodingASCII);
    issuerSerial = (CFDataRef)CFDictionaryGetValue(theDict, cfPrinc);
    CFRelease(cfPrinc);
    if(issuerSerial == NULL) {
        pkiDebug("krb5_pkinit_get_client_cert: no identity found\n");
        ourRtn = KRB5_PRINC_NOMATCH;
        goto errOut;
    }
    if(CFGetTypeID(issuerSerial) != CFDataGetTypeID()) {
        pkiDebug("krb5_pkinit_get_client_cert: bad kPkinitClientCertKey value\n");
        ourRtn = KRB5_PRINC_NOMATCH;
        goto errOut;
    }

    issuerSerialData.Data = (uint8 *)CFDataGetBytePtr(issuerSerial);
    issuerSerialData.Length = CFDataGetLength(issuerSerial);

    /* find a cert with that issuer/serial number in default search list */
    ortn = pkinit_search_ident(NULL, CSSM_KEYUSE_SIGN | CSSM_KEYUSE_ENCRYPT,
                               &issuerSerialData, &idRef);
    if(ortn) {
        pkiDebug("krb5_pkinit_get_client_cert: no identity found!\n");
        pkiCssmErr("pkinit_search_ident", ortn);
        ourRtn = KRB5_PRINC_NOMATCH;
    }
    else {
        *client_cert = (krb5_pkinit_signing_cert_t)idRef;
    }
errOut:
    if(theDict) {
        CFRelease(theDict);
    }
    return ourRtn;
}
static int
regexp_match(krb5_context context, rule_component *rc, char *value)
{
    int code;

    pkiDebug("%s: checking %s rule '%s' with value '%s'\n",
	     __FUNCTION__, keyword2string(rc->kw_type), rc->regsrc, value);

    code = regexec(&rc->regexp, value, 0, NULL, 0);

    pkiDebug("%s: the result is%s a match\n", __FUNCTION__,
	     code == REG_NOMATCH ? " NOT" : "");

    return (code == 0 ? 1: 0);
}
Exemple #7
0
static krb5_error_code
pkinit_init_kdc_req_context(krb5_context context, void **ctx)
{
    krb5_error_code retval = ENOMEM;
    pkinit_kdc_req_context reqctx = NULL;

    reqctx = (pkinit_kdc_req_context)malloc(sizeof(*reqctx));
    if (reqctx == NULL)
	return retval;
    memset(reqctx, 0, sizeof(*reqctx));
    reqctx->magic = PKINIT_CTX_MAGIC;

    retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
    if (retval)
	goto cleanup;
    reqctx->rcv_auth_pack = NULL;
    reqctx->rcv_auth_pack9 = NULL;

    pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
    *ctx = reqctx;
    retval = 0;
cleanup:
    if (retval)
	pkinit_fini_kdc_req_context(context, reqctx);

    return retval;
}
Exemple #8
0
static krb5_error_code
pkinit_server_get_edata(krb5_context context,
			krb5_kdc_req * request,
			struct _krb5_db_entry_new * client,
			struct _krb5_db_entry_new * server,
			preauth_get_entry_data_proc server_get_entry_data,
			void *pa_plugin_context,
			krb5_pa_data * data)
{
    krb5_error_code retval = 0;
    pkinit_kdc_context plgctx = NULL;

    pkiDebug("pkinit_server_get_edata: entered!\n");

    /*
     * If we don't have a realm context for the given realm,
     * don't tell the client that we support pkinit! 
     */
    plgctx = pkinit_find_realm_context(context, pa_plugin_context,
				       request->server);
    if (plgctx == NULL)
	retval = EINVAL;

    return retval;
}
static krb5_error_code
pkinit_server_get_edata(krb5_context context,
                        krb5_kdc_req * request,
                        struct _krb5_db_entry_new * client,
                        struct _krb5_db_entry_new * server,
                        preauth_get_entry_data_proc server_get_entry_data,
                        void *pa_plugin_context,
                        krb5_pa_data * data)
{
    krb5_error_code retval = 0;
    pkinit_kdc_context plgctx = NULL;
    krb5_keyblock *armor_key = NULL;

    pkiDebug("pkinit_server_get_edata: entered!\n");

    /* Remove (along with armor_key) when FAST PKINIT is settled. */
    retval = fast_kdc_get_armor_key(context, server_get_entry_data, request,
                                    client, &armor_key);
    if (retval == 0 && armor_key != NULL) {
        /* Don't advertise PKINIT if the client used FAST. */
        krb5_free_keyblock(context, armor_key);
        return EINVAL;
    }

    /*
     * If we don't have a realm context for the given realm,
     * don't tell the client that we support pkinit!
     */
    plgctx = pkinit_find_realm_context(context, pa_plugin_context,
                                       request->server);
    if (plgctx == NULL)
        retval = EINVAL;

    return retval;
}
static krb5_error_code
parse_pkcs12_options(krb5_context context,
                     pkinit_identity_opts *idopts,
                     const char *residual)
{
    krb5_error_code retval = ENOMEM;

    if (residual == NULL || residual[0] == '\0')
        return 0;

    idopts->cert_filename = strdup(residual);
    if (idopts->cert_filename == NULL)
        goto cleanup;

    idopts->key_filename = strdup(residual);
    if (idopts->key_filename == NULL)
        goto cleanup;

    pkiDebug("%s: cert_filename '%s' key_filename '%s'\n",
             __FUNCTION__, idopts->cert_filename,
             idopts->key_filename);
    retval = 0;
cleanup:
    return retval;
}
Exemple #11
0
static int
pkinit_server_plugin_init(krb5_context context, void **blob,
			  const char **realmnames)
{
    krb5_error_code retval = ENOMEM;
    pkinit_kdc_context plgctx, *realm_contexts = NULL;
    int i, j;
    size_t numrealms;

    retval = pkinit_accessor_init();
    if (retval)
	return retval;

    /* Determine how many realms we may need to support */
    for (i = 0; realmnames[i] != NULL; i++) {};
    numrealms = i;

    realm_contexts = (pkinit_kdc_context *)
			calloc(numrealms+1, sizeof(pkinit_kdc_context));
    if (realm_contexts == NULL)
	return ENOMEM;

    for (i = 0, j = 0; i < numrealms; i++) {
	pkiDebug("%s: processing realm '%s'\n", __FUNCTION__, realmnames[i]);
	retval = pkinit_server_plugin_init_realm(context, realmnames[i], &plgctx);
	if (retval == 0 && plgctx != NULL)
	    realm_contexts[j++] = plgctx;
    }

    if (j == 0) {
	retval = EINVAL;
	krb5_set_error_message(context, retval, "No realms configured "
			       "correctly for pkinit support");
	goto errout;
    }

    *blob = realm_contexts;
    retval = 0;
    pkiDebug("%s: returning context at %p\n", __FUNCTION__, realm_contexts);

errout:
    if (retval)
	pkinit_server_plugin_fini(context, realm_contexts);

    return retval;
}
/*
 * Obtain the CFDictionary representing this user's PKINIT client cert prefs, if it
 * exists. Returns noErr or errSecItemNotFound as appropriate.
 */
static OSStatus pkinit_get_pref_dict(
    CFDictionaryRef *dict)
{
    CFDictionaryRef theDict;
    theDict = (CFDictionaryRef)CFPreferencesCopyValue(CFSTR(kPkinitClientCertKey),
                                                      CFSTR(kPkinitClientCertApp), kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
    if(theDict == NULL) {
        pkiDebug("pkinit_get_pref_dict: no kPkinitClientCertKey\n");
        return errSecItemNotFound;
    }
    if(CFGetTypeID(theDict) != CFDictionaryGetTypeID()) {
        pkiDebug("pkinit_get_pref_dict: bad kPkinitClientCertKey pref\n");
        CFRelease(theDict);
        return errSecItemNotFound;
    }
    *dict = theDict;
    return noErr;
}
Exemple #13
0
static void
pkinit_fini_kdc_req_context(krb5_context context, void *ctx)
{
    pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx;

    if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) {
	pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx);
	return;
    }
    pkiDebug("%s: freeing   reqctx at %p\n", __FUNCTION__, reqctx);

    pkinit_fini_req_crypto(reqctx->cryptoctx);
    if (reqctx->rcv_auth_pack != NULL)
	free_krb5_auth_pack(&reqctx->rcv_auth_pack);
    if (reqctx->rcv_auth_pack9 != NULL)
	free_krb5_auth_pack_draft9(context, &reqctx->rcv_auth_pack9);

    free(reqctx);
}
Exemple #14
0
static krb5_error_code
pkinit_create_edata(krb5_context context,
		    pkinit_plg_crypto_context plg_cryptoctx,
		    pkinit_req_crypto_context req_cryptoctx,
		    pkinit_identity_crypto_context id_cryptoctx,
		    pkinit_plg_opts *opts,
		    krb5_error_code err_code,
		    krb5_data **e_data)
{
    krb5_error_code retval = KRB5KRB_ERR_GENERIC;

    pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n",
	     err_code, error_message(err_code));
    switch(err_code) {
	case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
	    retval = pkinit_create_td_trusted_certifiers(context,
		plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
	    break;
	case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
	    retval = pkinit_create_td_dh_parameters(context, plg_cryptoctx,
		req_cryptoctx, id_cryptoctx, opts, e_data);
	    break;
	case KRB5KDC_ERR_INVALID_CERTIFICATE:
	case KRB5KDC_ERR_REVOKED_CERTIFICATE:
	    retval = pkinit_create_td_invalid_certificate(context,
		plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
	    break;
	default:
	    pkiDebug("no edata needed for error %d (%s)\n",
		     err_code, error_message(err_code));
	    retval = 0;
	    goto cleanup;
    }

cleanup:

    return retval;
}
Exemple #15
0
static void
pkinit_server_plugin_fini(krb5_context context, void *blob)
{
    pkinit_kdc_context *realm_contexts = blob;
    int i;

    if (realm_contexts == NULL)
	return;

    for (i = 0; realm_contexts[i] != NULL; i++) {
	pkinit_server_plugin_fini_realm(context, realm_contexts[i]);
    }
    pkiDebug("%s: freeing   context at %p\n", __FUNCTION__, realm_contexts);
    free(realm_contexts);
}
Exemple #16
0
static pkinit_kdc_context
pkinit_find_realm_context(krb5_context context, void *pa_plugin_context,
			  krb5_principal princ)
{
    int i;
    pkinit_kdc_context *realm_contexts = pa_plugin_context;

    if (pa_plugin_context == NULL)
	return NULL;

    for (i = 0; realm_contexts[i] != NULL; i++) {
	pkinit_kdc_context p = realm_contexts[i];

	if ((p->realmname_len == princ->realm.length) &&
	    (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) {
	    pkiDebug("%s: returning context at %p for realm '%s'\n",
		     __FUNCTION__, p, p->realmname);
	    return p;
	}
    }
    pkiDebug("%s: unable to find realm context for realm '%.*s'\n",
	     __FUNCTION__, princ->realm.length, princ->realm.data);
    return NULL;
}
Exemple #17
0
/*
 * Create a CMS message: either encrypted (EnvelopedData), signed 
 * (SignedData), or both (EnvelopedData(SignedData(content)).
 *
 * The message is signed iff signing_cert is non-NULL.
 * The message is encrypted iff recip_cert is non-NULL.
 *
 * The content_type argument specifies to the eContentType
 * for a SignedData's EncapsulatedContentInfo. 
 */
krb5_error_code krb5int_pkinit_create_cms_msg(
    const krb5_data		*content,	/* Content */
    krb5_pkinit_signing_cert_t	signing_cert,	/* optional: signed by this cert */
    const krb5_data		*recip_cert,	/* optional: encrypted with this cert */
    krb5int_cms_content_type	content_type,   /* OID for EncapsulatedData */
    krb5_ui_4			num_cms_types,	/* optional, unused here */
    const krb5int_algorithm_id	*cms_types,	/* optional, unused here */
    krb5_data			*content_info)  /* contents mallocd and RETURNED */
{
    krb5_error_code krtn;
    OSStatus ortn;
    SecCertificateRef sec_recip = NULL;
    CFDataRef cf_content = NULL;
    const CSSM_OID *eContentOid = NULL;
    
    if((signing_cert == NULL) && (recip_cert == NULL)) {
	/* must have one or the other */
	pkiDebug("krb5int_pkinit_create_cms_msg: no signer or recipient\n");
	return KRB5_CRYPTO_INTERNAL;
    }
    
    /* 
     * Optional signer cert. Note signing_cert, if present, is 
     * a SecIdentityRef. 
     */
    if(recip_cert) {
	if(pkiKrb5DataToSecCert(recip_cert, &sec_recip)) {
	    krtn = ASN1_BAD_FORMAT;
	    goto errOut;
	}
    }
    
    /* optional eContentType */
    if(signing_cert) {
	switch(content_type) {
	    case ECT_PkAuthData:
		eContentOid = &_CSSMOID_PKINIT_AUTH_DATA;
		break;
	    case ECT_PkReplyKeyKata:
		eContentOid = &_CSSMOID_PKINIT_RKEY_DATA;
		break;
	    case ECT_Data:
		/* the only standard/default case we allow */
		break;
	    default:
		/* others: no can do */
		pkiDebug("krb5int_pkinit_create_cms_msg: bad contentType\n");
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	}
    }
    
    /* GO */
    ortn = CMSEncode((SecIdentityRef)signing_cert, sec_recip,
	eContentOid, 
	FALSE,		/* detachedContent */
	kCMSAttrNone,	/* no signed attributes that I know of */
	content->data, content->length,
	&cf_content);
    if(ortn) {
	pkiCssmErr("CMSEncode", ortn);
	krtn = KRB5_CRYPTO_INTERNAL;
	goto errOut;
    }
    krtn = pkiCfDataToKrb5Data(cf_content, content_info);
errOut:
    CFRELEASE(sec_recip);
    CFRELEASE(cf_content);
    return krtn;
}
Exemple #18
0
static krb5_error_code
pkinit_server_return_padata(krb5_context context,
			    krb5_pa_data * padata,
			    struct _krb5_db_entry_new * client,
			    krb5_data *req_pkt,
			    krb5_kdc_req * request,
			    krb5_kdc_rep * reply,
			    struct _krb5_key_data * client_key,
			    krb5_keyblock * encrypting_key,
			    krb5_pa_data ** send_pa,
			    preauth_get_entry_data_proc server_get_entry_data,
			    void *pa_plugin_context,
			    void **pa_request_context)
{
    krb5_error_code retval = 0;
    krb5_data scratch = {0, 0, NULL};
    krb5_pa_pk_as_req *reqp = NULL;
    krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
    int i = 0;

    unsigned char *subjectPublicKey = NULL;
    unsigned char *dh_pubkey = NULL, *server_key = NULL;
    unsigned int subjectPublicKey_len = 0;
    unsigned int server_key_len = 0, dh_pubkey_len = 0;

    krb5_kdc_dh_key_info dhkey_info;
    krb5_data *encoded_dhkey_info = NULL;
    krb5_pa_pk_as_rep *rep = NULL;
    krb5_pa_pk_as_rep_draft9 *rep9 = NULL;
    krb5_data *out_data = NULL;

    krb5_enctype enctype = -1;

    krb5_reply_key_pack *key_pack = NULL;
    krb5_reply_key_pack_draft9 *key_pack9 = NULL;
    krb5_data *encoded_key_pack = NULL;
    unsigned int num_types;
    krb5_cksumtype *cksum_types = NULL;

    pkinit_kdc_context plgctx;
    pkinit_kdc_req_context reqctx;

    int fixed_keypack = 0;

    *send_pa = NULL;
    if (padata == NULL || padata->length <= 0 || padata->contents == NULL)
	return 0;

    if (pa_request_context == NULL || *pa_request_context == NULL) {
	pkiDebug("missing request context \n");
	return EINVAL;
    }
    
    plgctx = pkinit_find_realm_context(context, pa_plugin_context,
				       request->server);
    if (plgctx == NULL) {
	pkiDebug("Unable to locate correct realm context\n");
	return ENOENT;
    }

    pkiDebug("pkinit_return_padata: entered!\n");
    reqctx = (pkinit_kdc_req_context)*pa_request_context;

    if (encrypting_key->contents) {
	free(encrypting_key->contents);
	encrypting_key->length = 0;
	encrypting_key->contents = NULL;
    }

    for(i = 0; i < request->nktypes; i++) {
	enctype = request->ktype[i];
	if (!krb5_c_valid_enctype(enctype))
	    continue;
	else {
	    pkiDebug("KDC picked etype = %d\n", enctype);
	    break;
	}
    }

    if (i == request->nktypes) {
	retval = KRB5KDC_ERR_ETYPE_NOSUPP;
	goto cleanup;
    }

    switch((int)reqctx->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    init_krb5_pa_pk_as_rep(&rep);
	    if (rep == NULL) {
		retval = ENOMEM;
		goto cleanup;
	    }
	    /* let's assume it's RSA. we'll reset it to DH if needed */
	    rep->choice = choice_pa_pk_as_rep_encKeyPack;
	    break;
	case KRB5_PADATA_PK_AS_REP_OLD:
	case KRB5_PADATA_PK_AS_REQ_OLD:
	    init_krb5_pa_pk_as_rep_draft9(&rep9);
	    if (rep9 == NULL) {
		retval = ENOMEM;
		goto cleanup;
	    }
	    rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
	    break;
	default:
	    retval = KRB5KDC_ERR_PREAUTH_FAILED;
	    goto cleanup;
    }

    if (reqctx->rcv_auth_pack != NULL &&
	    reqctx->rcv_auth_pack->clientPublicValue != NULL) {
	subjectPublicKey =
	    reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data;
	subjectPublicKey_len =
	    reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length;
	rep->choice = choice_pa_pk_as_rep_dhInfo;
    } else if (reqctx->rcv_auth_pack9 != NULL &&
		reqctx->rcv_auth_pack9->clientPublicValue != NULL) {
	subjectPublicKey =
	    reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data;
	subjectPublicKey_len =
	    reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length;
	rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData;
    }

    /* if this DH, then process finish computing DH key */
    if (rep != NULL && (rep->choice == choice_pa_pk_as_rep_dhInfo ||
	    rep->choice == choice_pa_pk_as_rep_draft9_dhSignedData)) {
	pkiDebug("received DH key delivery AS REQ\n");
	retval = server_process_dh(context, plgctx->cryptoctx,
	    reqctx->cryptoctx, plgctx->idctx, subjectPublicKey,
	    subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len, 
	    &server_key, &server_key_len);
	if (retval) {
	    pkiDebug("failed to process/create dh paramters\n");
	    goto cleanup;
	}
    }
	
    if ((rep9 != NULL &&
	    rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) ||
	(rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) {
	retval = pkinit_octetstring2key(context, enctype, server_key,
					server_key_len, encrypting_key);
	if (retval) {
	    pkiDebug("pkinit_octetstring2key failed: %s\n",
		     error_message(retval));
	    goto cleanup;
	}

	dhkey_info.subjectPublicKey.length = dh_pubkey_len;
	dhkey_info.subjectPublicKey.data = dh_pubkey;
	dhkey_info.nonce = request->nonce;
	dhkey_info.dhKeyExpiration = 0;

	retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info,
						   &encoded_dhkey_info);
	if (retval) {
	    pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
	    goto cleanup;
	}
#ifdef DEBUG_ASN1
	print_buffer_bin((unsigned char *)encoded_dhkey_info->data,
			 encoded_dhkey_info->length,
			 "/tmp/kdc_dh_key_info");
#endif

	switch ((int)padata->pa_type) {
	    case KRB5_PADATA_PK_AS_REQ:
		retval = cms_signeddata_create(context, plgctx->cryptoctx,
		    reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1,
		    (unsigned char *)encoded_dhkey_info->data,
		    encoded_dhkey_info->length,
		    &rep->u.dh_Info.dhSignedData.data,
		    &rep->u.dh_Info.dhSignedData.length);
		if (retval) {
		    pkiDebug("failed to create pkcs7 signed data\n");
		    goto cleanup;
		}
		break;
	    case KRB5_PADATA_PK_AS_REP_OLD:
	    case KRB5_PADATA_PK_AS_REQ_OLD:
		retval = cms_signeddata_create(context, plgctx->cryptoctx,
		    reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1,
		    (unsigned char *)encoded_dhkey_info->data,
		    encoded_dhkey_info->length,
		    &rep9->u.dhSignedData.data,
		    &rep9->u.dhSignedData.length);
		if (retval) {
		    pkiDebug("failed to create pkcs7 signed data\n");
		    goto cleanup;
		}
		break;
	}
    } else {
	pkiDebug("received RSA key delivery AS REQ\n");

	retval = krb5_c_make_random_key(context, enctype, encrypting_key);
	if (retval) {
	    pkiDebug("unable to make a session key\n");
	    goto cleanup;
	}

	/* check if PA_TYPE of 132 is present which means the client is
	 * requesting that a checksum is send back instead of the nonce
	 */
	for (i = 0; request->padata[i] != NULL; i++) {
	    pkiDebug("%s: Checking pa_type 0x%08x\n",
		     __FUNCTION__, request->padata[i]->pa_type);
	    if (request->padata[i]->pa_type == 132)
		fixed_keypack = 1;
	}
	pkiDebug("%s: return checksum instead of nonce = %d\n",
		 __FUNCTION__, fixed_keypack);

	/* if this is an RFC reply or draft9 client requested a checksum 
	 * in the reply instead of the nonce, create an RFC-style keypack
	 */
	if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) {
	    init_krb5_reply_key_pack(&key_pack);
	    if (key_pack == NULL) {
		retval = ENOMEM;
		goto cleanup;
	    }
	    /* retrieve checksums for a given enctype of the reply key */
	    retval = krb5_c_keyed_checksum_types(context,
		encrypting_key->enctype, &num_types, &cksum_types);
	    if (retval)
		goto cleanup;

	    /* pick the first of acceptable enctypes for the checksum */
	    retval = krb5_c_make_checksum(context, cksum_types[0],
		    encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
		    req_pkt, &key_pack->asChecksum);
	    if (retval) {
		pkiDebug("unable to calculate AS REQ checksum\n");
		goto cleanup;
	    }
#ifdef DEBUG_CKSUM
	    pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length);
	    print_buffer(req_pkt->data, req_pkt->length);
	    pkiDebug("checksum size = %d\n", key_pack->asChecksum.length);
	    print_buffer(key_pack->asChecksum.contents, 
			 key_pack->asChecksum.length);
	    pkiDebug("encrypting key (%d)\n", encrypting_key->length);
	    print_buffer(encrypting_key->contents, encrypting_key->length);
#endif

	    krb5_copy_keyblock_contents(context, encrypting_key,
					&key_pack->replyKey);

	    retval = k5int_encode_krb5_reply_key_pack(key_pack,
						      &encoded_key_pack);
	    if (retval) {
		pkiDebug("failed to encode reply_key_pack\n");
		goto cleanup;
	    }
	}

	switch ((int)padata->pa_type) {
	    case KRB5_PADATA_PK_AS_REQ:
		rep->choice = choice_pa_pk_as_rep_encKeyPack;
		retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
		    reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, 
		    (unsigned char *)encoded_key_pack->data,
		    encoded_key_pack->length,
		    &rep->u.encKeyPack.data, &rep->u.encKeyPack.length);
		break;
	    case KRB5_PADATA_PK_AS_REP_OLD:
	    case KRB5_PADATA_PK_AS_REQ_OLD:
		/* if the request is from the broken draft9 client that
		 * expects back a nonce, create it now 
		 */
		if (!fixed_keypack) {
		    init_krb5_reply_key_pack_draft9(&key_pack9);
		    if (key_pack9 == NULL) {
			retval = ENOMEM;
			goto cleanup;
		    }
		    key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce;
		    krb5_copy_keyblock_contents(context, encrypting_key,
						&key_pack9->replyKey);

		    retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9,
							   &encoded_key_pack);
		    if (retval) {
			pkiDebug("failed to encode reply_key_pack\n");
			goto cleanup;
		    }
		} 

		rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
		retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
		    reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, 
		    (unsigned char *)encoded_key_pack->data,
		    encoded_key_pack->length,
		    &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length);
		break;
	}
	if (retval) {
	    pkiDebug("failed to create pkcs7 enveloped data: %s\n",
		     error_message(retval));
	    goto cleanup;
	}
#ifdef DEBUG_ASN1
	print_buffer_bin((unsigned char *)encoded_key_pack->data,
			 encoded_key_pack->length,
			 "/tmp/kdc_key_pack");
	switch ((int)padata->pa_type) {
	    case KRB5_PADATA_PK_AS_REQ:
		print_buffer_bin(rep->u.encKeyPack.data,
				 rep->u.encKeyPack.length,
				 "/tmp/kdc_enc_key_pack");
		break;
	    case KRB5_PADATA_PK_AS_REP_OLD:
	    case KRB5_PADATA_PK_AS_REQ_OLD:
		print_buffer_bin(rep9->u.encKeyPack.data,
				 rep9->u.encKeyPack.length,
				 "/tmp/kdc_enc_key_pack");
		break;
	}
#endif
    }

    switch ((int)padata->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);
	    break;
	case KRB5_PADATA_PK_AS_REP_OLD:
	case KRB5_PADATA_PK_AS_REQ_OLD:
	    retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data);
	    break;
    }
    if (retval) {
	pkiDebug("failed to encode AS_REP\n");
	goto cleanup;
    }
#ifdef DEBUG_ASN1
    if (out_data != NULL)
	print_buffer_bin((unsigned char *)out_data->data, out_data->length,
			 "/tmp/kdc_as_rep");
#endif

    *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
    if (*send_pa == NULL) {
	retval = ENOMEM;
	free(out_data->data);
	free(out_data);
	out_data = NULL;
	goto cleanup;
    }
    (*send_pa)->magic = KV5M_PA_DATA;
    switch ((int)padata->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;
	    break;
	case KRB5_PADATA_PK_AS_REQ_OLD:
	case KRB5_PADATA_PK_AS_REP_OLD:
	    (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
	    break;
    }
    (*send_pa)->length = out_data->length;
    (*send_pa)->contents = (krb5_octet *) out_data->data;


  cleanup:
    pkinit_fini_kdc_req_context(context, reqctx);
    if (scratch.data != NULL)
	free(scratch.data);
    if (out_data != NULL)
	free(out_data);
    if (encoded_dhkey_info != NULL)
	krb5_free_data(context, encoded_dhkey_info);
    if (encoded_key_pack != NULL)
	krb5_free_data(context, encoded_key_pack);
    if (dh_pubkey != NULL)
	free(dh_pubkey);
    if (server_key != NULL)
	free(server_key);
    if (cksum_types != NULL)
	free(cksum_types);

    switch ((int)padata->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    free_krb5_pa_pk_as_req(&reqp);
	    free_krb5_pa_pk_as_rep(&rep);
	    free_krb5_reply_key_pack(&key_pack);
	    break;
	case KRB5_PADATA_PK_AS_REP_OLD:
	case KRB5_PADATA_PK_AS_REQ_OLD:
	    free_krb5_pa_pk_as_req_draft9(&reqp9);
	    free_krb5_pa_pk_as_rep_draft9(&rep9);
	    if (!fixed_keypack)
		free_krb5_reply_key_pack_draft9(&key_pack9);
	    else
		free_krb5_reply_key_pack(&key_pack);
	    break;
    }

    if (retval)
	pkiDebug("pkinit_verify_padata failure");

    return retval;
}
Exemple #19
0
static krb5_error_code
pkinit_server_verify_padata(krb5_context context,
			    struct _krb5_db_entry_new * client,
			    krb5_data *req_pkt,
			    krb5_kdc_req * request,
			    krb5_enc_tkt_part * enc_tkt_reply,
			    krb5_pa_data * data,
			    preauth_get_entry_data_proc server_get_entry_data,
			    void *pa_plugin_context,
			    void **pa_request_context,
			    krb5_data **e_data,
			    krb5_authdata ***authz_data)
{
    krb5_error_code retval = 0;	
    krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL};
    krb5_data *encoded_pkinit_authz_data = NULL;
    krb5_pa_pk_as_req *reqp = NULL;
    krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
    krb5_auth_pack *auth_pack = NULL;
    krb5_auth_pack_draft9 *auth_pack9 = NULL;
    pkinit_kdc_context plgctx = NULL;
    pkinit_kdc_req_context reqctx;
    krb5_preauthtype pa_type;
    krb5_checksum cksum = {0, 0, 0, NULL};
    krb5_data *der_req = NULL;
    int valid_eku = 0, valid_san = 0;
    krb5_authdata **my_authz_data = NULL, *pkinit_authz_data = NULL;
    krb5_kdc_req *tmp_as_req = NULL;
    krb5_data k5data;

    pkiDebug("pkinit_verify_padata: entered!\n");
    if (data == NULL || data->length <= 0 || data->contents == NULL)
	return 0;

    if (pa_plugin_context == NULL || e_data == NULL)
	return EINVAL;

    plgctx = pkinit_find_realm_context(context, pa_plugin_context,
				       request->server);
    if (plgctx == NULL)
	return 0;

#ifdef DEBUG_ASN1
    print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");
#endif
    /* create a per-request context */
    retval = pkinit_init_kdc_req_context(context, (void **)&reqctx);
    if (retval)
	goto cleanup;
    reqctx->pa_type = data->pa_type;

    PADATA_TO_KRB5DATA(data, &k5data);

    switch ((int)data->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
	    pa_type = (int)data->pa_type;
	    retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
	    if (retval) {
		pkiDebug("decode_krb5_pa_pk_as_req failed\n");
		goto cleanup;
	    }
#ifdef DEBUG_ASN1
	    print_buffer_bin(reqp->signedAuthPack.data,
			     reqp->signedAuthPack.length,
			     "/tmp/kdc_signed_data");
#endif
	    retval = cms_signeddata_verify(context, plgctx->cryptoctx,
		reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT,
		plgctx->opts->require_crl_checking,
		reqp->signedAuthPack.data, reqp->signedAuthPack.length,
		&authp_data.data, &authp_data.length, &krb5_authz.data, 
		&krb5_authz.length);
	    break;
	case KRB5_PADATA_PK_AS_REP_OLD:
	case KRB5_PADATA_PK_AS_REQ_OLD:
	    pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
	    pa_type = KRB5_PADATA_PK_AS_REQ_OLD;
	    retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9);
	    if (retval) {
		pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
		goto cleanup;
	    }
#ifdef DEBUG_ASN1
	    print_buffer_bin(reqp9->signedAuthPack.data,
			     reqp9->signedAuthPack.length,
			     "/tmp/kdc_signed_data_draft9");
#endif

	    retval = cms_signeddata_verify(context, plgctx->cryptoctx,
		reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9,
		plgctx->opts->require_crl_checking,
		reqp9->signedAuthPack.data, reqp9->signedAuthPack.length,
		&authp_data.data, &authp_data.length, &krb5_authz.data, 
		&krb5_authz.length);
	    break;
	default:
	    pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
	    retval = EINVAL;
	    goto cleanup;
    }
    if (retval) {
	pkiDebug("pkcs7_signeddata_verify failed\n");
	goto cleanup;
    }

    retval = verify_client_san(context, plgctx, reqctx, request->client,
			       &valid_san);
    if (retval)
	goto cleanup;
    if (!valid_san) {
	pkiDebug("%s: did not find an acceptable SAN in user certificate\n",
		 __FUNCTION__);
	retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
	goto cleanup;
    }
    retval = verify_client_eku(context, plgctx, reqctx, &valid_eku);
    if (retval)
	goto cleanup;

    if (!valid_eku) {
	pkiDebug("%s: did not find an acceptable EKU in user certificate\n",
		 __FUNCTION__);
	retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
	goto cleanup;
    }

#ifdef DEBUG_ASN1
    print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");
#endif

    OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);
    switch ((int)data->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);
	    if (retval) {
		pkiDebug("failed to decode krb5_auth_pack\n");
		goto cleanup;
	    }

	    /* check dh parameters */
	    if (auth_pack->clientPublicValue != NULL) {	
		retval = server_check_dh(context, plgctx->cryptoctx,
		    reqctx->cryptoctx, plgctx->idctx,
		    &auth_pack->clientPublicValue->algorithm.parameters,
		    plgctx->opts->dh_min_bits);

		if (retval) {
		    pkiDebug("bad dh parameters\n");
		    goto cleanup;
		}
	    }
	    /*
	     * The KDC may have modified the request after decoding it.
	     * We need to compute the checksum on the data that
	     * came from the client.  Therefore, we use the original
	     * packet contents.
	     */
	    retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req); 
	    if (retval) {
		pkiDebug("decode_krb5_as_req returned %d\n", (int)retval);
		goto cleanup;
	    }
		
	    retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req);
	    if (retval) {
		pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
		goto cleanup;
	    }
	    retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL,
					  0, der_req, &cksum);
	    if (retval) {
		pkiDebug("unable to calculate AS REQ checksum\n");
		goto cleanup;
	    }
	    if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length ||
		memcmp(cksum.contents,
		       auth_pack->pkAuthenticator.paChecksum.contents,
		       cksum.length)) {
		pkiDebug("failed to match the checksum\n");
#ifdef DEBUG_CKSUM
		pkiDebug("calculating checksum on buf size (%d)\n",
			 req_pkt->length);
		print_buffer(req_pkt->data, req_pkt->length);
		pkiDebug("received checksum type=%d size=%d ",
			auth_pack->pkAuthenticator.paChecksum.checksum_type,
			auth_pack->pkAuthenticator.paChecksum.length);
		print_buffer(auth_pack->pkAuthenticator.paChecksum.contents,
			     auth_pack->pkAuthenticator.paChecksum.length);
		pkiDebug("expected checksum type=%d size=%d ",
			 cksum.checksum_type, cksum.length);
		print_buffer(cksum.contents, cksum.length);
#endif

		retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
		goto cleanup;
	    }

	    /* check if kdcPkId present and match KDC's subjectIdentifier */
	    if (reqp->kdcPkId.data != NULL) {
		int valid_kdcPkId = 0;
		retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,
		    reqctx->cryptoctx, plgctx->idctx,
		    reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId);
		if (retval)
		    goto cleanup;
		if (!valid_kdcPkId)
		    pkiDebug("kdcPkId in AS_REQ does not match KDC's cert"
			     "RFC says to ignore and proceed\n");

	    }
	    /* remember the decoded auth_pack for verify_padata routine */
	    reqctx->rcv_auth_pack = auth_pack;
	    auth_pack = NULL;
	    break;
	case KRB5_PADATA_PK_AS_REP_OLD:
	case KRB5_PADATA_PK_AS_REQ_OLD:
	    retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9);
	    if (retval) {
		pkiDebug("failed to decode krb5_auth_pack_draft9\n");
		goto cleanup;
	    }
	    if (auth_pack9->clientPublicValue != NULL) {	
		retval = server_check_dh(context, plgctx->cryptoctx,
		    reqctx->cryptoctx, plgctx->idctx,
		    &auth_pack9->clientPublicValue->algorithm.parameters,
		    plgctx->opts->dh_min_bits);

		if (retval) {
		    pkiDebug("bad dh parameters\n");
		    goto cleanup;
		}
	    }
	    /* remember the decoded auth_pack for verify_padata routine */
	    reqctx->rcv_auth_pack9 = auth_pack9;
	    auth_pack9 = NULL;
	    break;
    }

    /* return authorization data to be included in the ticket */
    switch ((int)data->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    my_authz_data = malloc(2 * sizeof(*my_authz_data));
	    if (my_authz_data == NULL) {
		retval = ENOMEM;
		pkiDebug("Couldn't allocate krb5_authdata ptr array\n");
		goto cleanup;
	    }
	    my_authz_data[1] = NULL;
	    my_authz_data[0] = malloc(sizeof(krb5_authdata));
	    if (my_authz_data[0] == NULL) {
		retval = ENOMEM;
		pkiDebug("Couldn't allocate krb5_authdata\n");
		free(my_authz_data);
		goto cleanup;
	    }
	    /* AD-INITIAL-VERIFIED-CAS must be wrapped in AD-IF-RELEVANT */
	    my_authz_data[0]->magic = KV5M_AUTHDATA;
	    my_authz_data[0]->ad_type = KRB5_AUTHDATA_IF_RELEVANT;

	    /* create an internal AD-INITIAL-VERIFIED-CAS data */
	    pkinit_authz_data = malloc(sizeof(krb5_authdata));
	    if (pkinit_authz_data == NULL) {
		retval = ENOMEM;
		pkiDebug("Couldn't allocate krb5_authdata\n");
		free(my_authz_data[0]);
		free(my_authz_data);
		goto cleanup;
	    }
	    pkinit_authz_data->ad_type = KRB5_AUTHDATA_INITIAL_VERIFIED_CAS;
	    /* content of this ad-type contains the certification
	       path with which the client certificate was validated
	     */
	    pkinit_authz_data->contents = krb5_authz.data;
	    pkinit_authz_data->length = krb5_authz.length;
	    retval = k5int_encode_krb5_authdata_elt(pkinit_authz_data, 
			    &encoded_pkinit_authz_data);
#ifdef DEBUG_ASN1
	    print_buffer_bin((unsigned char *)encoded_pkinit_authz_data->data,
			     encoded_pkinit_authz_data->length,
			     "/tmp/kdc_pkinit_authz_data");
#endif
	    free(pkinit_authz_data);
	    if (retval) {
		pkiDebug("k5int_encode_krb5_authdata_elt failed\n");
		free(my_authz_data[0]);
		free(my_authz_data);
		goto cleanup;
	    }

	    my_authz_data[0]->contents =
			    (krb5_octet *) encoded_pkinit_authz_data->data;
	    my_authz_data[0]->length = encoded_pkinit_authz_data->length;
	    *authz_data = my_authz_data;
	    pkiDebug("Returning %d bytes of authorization data\n", 
		     krb5_authz.length);
	    encoded_pkinit_authz_data->data = NULL; /* Don't free during cleanup*/
	    free(encoded_pkinit_authz_data);
	    break;
	default: 
	    *authz_data = NULL;
    }
    /* remember to set the PREAUTH flag in the reply */
    enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
    *pa_request_context = reqctx;
    reqctx = NULL;

  cleanup:
    if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
	pkiDebug("pkinit_verify_padata failed: creating e-data\n");
	if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,
		plgctx->idctx, plgctx->opts, retval, e_data))
	    pkiDebug("pkinit_create_edata failed\n");
    }

    switch ((int)data->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    free_krb5_pa_pk_as_req(&reqp);
	    if (cksum.contents != NULL)
		free(cksum.contents);
	    if (der_req != NULL)
		 krb5_free_data(context, der_req);
	    break;
	case KRB5_PADATA_PK_AS_REP_OLD:
	case KRB5_PADATA_PK_AS_REQ_OLD:
	    free_krb5_pa_pk_as_req_draft9(&reqp9);
    }
    if (tmp_as_req != NULL)
	k5int_krb5_free_kdc_req(context, tmp_as_req); 
    if (authp_data.data != NULL)
	free(authp_data.data);
    if (krb5_authz.data != NULL)
	free(krb5_authz.data);
    if (reqctx != NULL)
	pkinit_fini_kdc_req_context(context, reqctx);
    if (auth_pack != NULL)
	free_krb5_auth_pack(&auth_pack);
    if (auth_pack9 != NULL)
	free_krb5_auth_pack_draft9(context, &auth_pack9);

    return retval;
}
static krb5_error_code
parse_rule_component(krb5_context context,
		     const char **rule,
		     int *remaining,
		     rule_component **ret_rule)
{
    krb5_error_code retval;
    rule_component *rc = NULL;
    keyword_type kw_type;
    kw_value_type kwval_type;
    char err_buf[128];
    int ret;
    struct keyword_desc *kw, *nextkw;
    char *nk;
    int found_next_kw = 0;
    char *value = NULL;
    size_t len;

    for (kw = matching_keywords; kw->value != NULL; kw++) {
	if (strncmp(*rule, kw->value, kw->length) == 0) {
	    kw_type = kw->kwtype;
	    kwval_type = kw->kwvaltype;
	    *rule += kw->length;
	    *remaining -= kw->length;
	    break;
	}
    }
    if (kw->value == NULL) {
	pkiDebug("%s: Missing or invalid keyword in rule '%s'\n",
		 __FUNCTION__, *rule);
	retval = ENOENT;
	goto out;
    }

    pkiDebug("%s: found keyword '%s'\n", __FUNCTION__, kw->value);

    rc = calloc(1, sizeof(*rc));
    if (rc == NULL) {
	retval = ENOMEM;
	goto out;
    }
    rc->next = NULL;
    rc->kw_type = kw_type;
    rc->kwval_type = kwval_type;

    /*
     * Before procesing the value for this keyword,
     * (compiling the regular expression or processing the list)
     * we need to find the end of it.  That means parsing for the
     * beginning of the next keyword (or the end of the rule).
     */
    nk = strchr(*rule, '<');
    while (nk != NULL) {
	/* Possibly another keyword, check it out */
	for (nextkw = matching_keywords; nextkw->value != NULL; nextkw++) {
	    if (strncmp(nk, nextkw->value, nextkw->length) == 0) {
		/* Found a keyword, nk points to the beginning */
		found_next_kw = 1;
		break;	/* Need to break out of the while! */
	    }
	}
	if (!found_next_kw)
	    nk = strchr(nk+1, '<');	/* keep looking */
	else
	    break;
    }

    if (nk != NULL && found_next_kw)
	len = (nk - *rule);
    else
	len = (*remaining);

    if (len == 0 && kw->kwvaltype != kwvaltype_principal) {
	pkiDebug("%s: Missing value for keyword '%s'\n",
		 __FUNCTION__, kw->value);
	retval = EINVAL;
	goto out;
    } else {
        value = calloc(1, len+1);
        if (value == NULL) {
            retval = ENOMEM;
            goto out;
        }
    }

    memcpy(value, *rule, len);
    *remaining -= len;
    *rule += len;
    pkiDebug("%s: found value '%s'\n", __FUNCTION__, value);

    if (kw->kwvaltype == kwvaltype_regexp) {
	ret = regcomp(&rc->regexp, value, REG_EXTENDED);
	if (ret) {
	    regerror(ret, &rc->regexp, err_buf, sizeof(err_buf));
	    pkiDebug("%s: Error compiling reg-exp '%s': %s\n",
		     __FUNCTION__, value, err_buf);
	    retval = ret;
	    goto out;
	}
	rc->regsrc = strdup(value);
	if (rc->regsrc == NULL) {
	    retval = ENOMEM;
	    goto out;
	}
    } else if (kw->kwvaltype == kwvaltype_list) {
	retval = parse_list_value(context, rc->kw_type, value, rc);
	if (retval) {
	    pkiDebug("%s: Error %d, parsing list values for keyword %s\n",
		     __FUNCTION__, retval, kw->value);
	    goto out;
	}
    }

    *ret_rule = rc;
    retval = 0;
out:
    if (value != NULL)
	free(value);
    if (retval && rc != NULL)
	free_rule_component(context, rc);
    pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
    return retval;
}
Exemple #21
0
static krb5_error_code
verify_client_san(krb5_context context,
		  pkinit_kdc_context plgctx,
		  pkinit_kdc_req_context reqctx,
		  krb5_principal client,
		  int *valid_san)
{
    krb5_error_code retval;
    krb5_principal *princs = NULL;
    krb5_principal *upns = NULL;
    int i;
#ifdef DEBUG_SAN_INFO
    char *client_string = NULL, *san_string;
#endif
    
    retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
				       reqctx->cryptoctx, plgctx->idctx,
				       &princs,
				       plgctx->opts->allow_upn ? &upns : NULL,
				       NULL);
    if (retval) {
	pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
	retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
	goto out;
    }
    /* XXX Verify this is consistent with client side XXX */
#if 0
    retval = call_san_checking_plugins(context, plgctx, reqctx, princs,
				       upns, NULL, &plugin_decision, &ignore);
    pkiDebug("%s: call_san_checking_plugins() returned retval %d\n",
	     __FUNCTION__);
    if (retval) {
	retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
	goto cleanup;
    }
    pkiDebug("%s: call_san_checking_plugins() returned decision %d\n",
	     __FUNCTION__, plugin_decision);
    if (plugin_decision != NO_DECISION) {
	retval = plugin_decision;
	goto out;
    }
#endif

#ifdef DEBUG_SAN_INFO
    krb5_unparse_name(context, client, &client_string);
#endif
    pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
    for (i = 0; princs != NULL && princs[i] != NULL; i++) {
#ifdef DEBUG_SAN_INFO
	krb5_unparse_name(context, princs[i], &san_string);
	pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n",
		 __FUNCTION__, client_string, san_string);
	krb5_free_unparsed_name(context, san_string);
#endif
	if (krb5_principal_compare(context, princs[i], client)) {
	    pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
	    *valid_san = 1;
	    retval = 0;
	    goto out;
	}
    }
    pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
    /*
     * XXX if cert has names but none match, should we
     * be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here?
     */

    if (upns == NULL) {
	pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n",
		 __FUNCTION__);
	retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
	goto out;
    }

    pkiDebug("%s: Checking upn sans\n", __FUNCTION__);
    for (i = 0; upns[i] != NULL; i++) {
#ifdef DEBUG_SAN_INFO
	krb5_unparse_name(context, upns[i], &san_string);
	pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n",
		 __FUNCTION__, client_string, san_string);
	krb5_free_unparsed_name(context, san_string);
#endif
	if (krb5_principal_compare(context, upns[i], client)) {
	    pkiDebug("%s: upn san match found\n", __FUNCTION__);
	    *valid_san = 1;
	    retval = 0;
	    goto out;
	}
    }
    pkiDebug("%s: no upn san match found\n", __FUNCTION__);

    /* We found no match */
    if (princs != NULL || upns != NULL) {
	*valid_san = 0;
	/* XXX ??? If there was one or more name in the cert, but
	 * none matched the client name, then return mismatch? */
	retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
    }
    retval = 0;

out:
    if (princs != NULL) {
	for (i = 0; princs[i] != NULL; i++)
	    krb5_free_principal(context, princs[i]);
	free(princs);
    }
    if (upns != NULL) {
	for (i = 0; upns[i] != NULL; i++)
	    krb5_free_principal(context, upns[i]);
	free(upns);
    }
#ifdef DEBUG_SAN_INFO
    if (client_string != NULL)
	krb5_free_unparsed_name(context, client_string);
#endif
    pkiDebug("%s: returning retval %d, valid_san %d\n",
	     __FUNCTION__, retval, *valid_san);
    return retval;
}
static krb5_error_code
parse_rule_set(krb5_context context,
	       const char *rule_in,
	       rule_set **out_rs)
{
    const char *rule;
    int remaining, totlen;
    krb5_error_code ret, retval;
    rule_component *rc = NULL, *trc;
    rule_set *rs;


    if (rule_in == NULL)
	return EINVAL;
    rule = rule_in;
    totlen = remaining = strlen(rule);

    rs = calloc(1, sizeof(*rs));
    if (rs == NULL) {
	retval = ENOMEM;
	goto cleanup;
    }

    rs->relation = relation_none;
    if (remaining > 1) {
	if (rule[0] == '&' && rule[1] == '&') {
	    rs->relation = relation_and;
	    rule += 2;
	    remaining -= 2;
	} else if (rule_in[0] == '|' && rule_in[1] == '|') {
	    rs->relation = relation_or;
	    rule +=2;
	    remaining -= 2;
	}
    }
    rs->num_crs = 0;
    while (remaining > 0) {
	if (rs->relation == relation_none && rs->num_crs > 1) {
	    pkiDebug("%s: Assuming AND relation for multiple components in rule '%s'\n",
		     __FUNCTION__, rule_in);
	    rs->relation = relation_and;
	}
	ret = parse_rule_component(context, &rule, &remaining, &rc);
	if (ret) {
	    retval = ret;
	    goto cleanup;
	}
	pkiDebug("%s: After parse_rule_component, remaining %d, rule '%s'\n",
		 __FUNCTION__, remaining, rule);
	rs->num_crs++;

	/*
	 * Chain the new component on the end (order matters since
	 * we can short-circuit an OR or an AND relation if an
	 * earlier check passes
	 */
	for (trc = rs->crs; trc != NULL && trc->next != NULL; trc = trc->next);
	if (trc == NULL)
	    rs->crs = rc;
	else {
	    trc->next = rc;
	}
    }

    *out_rs = rs;

    retval = 0;
cleanup:
    if (retval && rs != NULL) {
	free_rule_set(context, rs);
    }
    pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
    return retval;
}
static int
component_match(krb5_context context,
		rule_component *rc,
		pkinit_cert_matching_data *md,
		krb5_principal princ)
{
    int match = 0;
    int i;
    krb5_principal p;
    char *princ_string;

    switch (rc->kwval_type) {
    case kwvaltype_regexp:
	switch (rc->kw_type) {
	case kw_subject:
	    match = regexp_match(context, rc, md->subject_dn);
	    break;
	case kw_issuer:
	    match = regexp_match(context, rc, md->issuer_dn);
	    break;
	case kw_san:
	    if (md->sans == NULL)
		break;
	    for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) {
		krb5_unparse_name(context, p, &princ_string);
		match = regexp_match(context, rc, princ_string);
		krb5_free_unparsed_name(context, princ_string);
		if (match)
		    break;
	    }
	    break;
	default:
	    pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
		     __FUNCTION__, keyword2string(rc->kw_type),
		     kwval2string(kwvaltype_regexp));
	    break;
	}
	break;
    case kwvaltype_list:
	switch(rc->kw_type) {
	case kw_eku:
	    pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
		     __FUNCTION__, keyword2string(rc->kw_type),
		     rc->eku_bits, md->eku_bits);
	    if ((rc->eku_bits & md->eku_bits) == rc->eku_bits)
		match = 1;
	    break;
	case kw_ku:
	    pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
		     __FUNCTION__, keyword2string(rc->kw_type),
		     rc->ku_bits, md->ku_bits);
	    if ((rc->ku_bits & md->ku_bits) == rc->ku_bits)
		match = 1;
	    break;
	default:
	    pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
		     __FUNCTION__, keyword2string(rc->kw_type),
		     kwval2string(kwvaltype_regexp));
	    break;
	}
	break;
    case kwvaltype_principal:
        if (md->sans == NULL)
            break;
#ifdef DEBUG
        krb5_unparse_name(context, princ, &princ_string);
#endif
        for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) {
#ifdef DEBUG
            char *san_string;

            krb5_unparse_name(context, p, &san_string);
            pkiDebug("%s: comparing principal '%s' with cert SAN '%s'\n",
                    __FUNCTION__, princ_string, san_string);
#endif
            if (krb5_principal_compare_flags(context, p, princ,
                        KRB5_PRINCIPAL_COMPARE_CASEFOLD)) {
                match = 1;
                break;
            }
            if (match)
                break;
        }
	break;
    default:
	pkiDebug("%s: unknown keyword value type %d\n",
		 __FUNCTION__, rc->kwval_type);
	break;
    }
    pkiDebug("%s: returning match = %d\n", __FUNCTION__, match);
    return match;
}
/*
 * Returns match_found == 1 only if exactly one certificate matches
 * the given rule
 */
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,	/* rule to check */
		pkinit_cert_matching_data **matchdata,
		int *match_found,
		pkinit_cert_matching_data **matching_cert)
{
    krb5_error_code retval;
    pkinit_cert_matching_data *md;
    int i;
    int comp_match = 0;
    int total_cert_matches = 0;
    rule_component *rc;
    int certs_checked = 0;
    pkinit_cert_matching_data *save_match = NULL;
    char *princ_string = NULL;
    char *princ_realm = NULL;

    if (match_found == NULL || matching_cert == NULL)
	return EINVAL;

    *matching_cert = NULL;
    *match_found = 0;

    pkiDebug("%s: matching rule relation is %s with %d components\n",
	     __FUNCTION__, relation2string(rs->relation), rs->num_crs);

    if (princ)
    {
        retval = krb5_unparse_name(context, princ, &princ_string);
        if (retval || princ_string == NULL) {
            return EINVAL;
        }

        princ_realm = strchr(princ_string, '@');

        if (princ_realm == NULL) {
            krb5_free_unparsed_name(context, princ_string);
            return EINVAL;
        }

        *princ_realm++ = '\0';
    }

    /*
     * Loop through all the certs available and count
     * how many match the rule
     */
    for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
	pkiDebug("%s: subject: '%s'\n", __FUNCTION__, md->subject_dn);
	pkiDebug("%s: issuer:  '%s'\n", __FUNCTION__, md->issuer_dn);

	certs_checked++;
	for (rc = rs->crs; rc != NULL; rc = rc->next) {
	    comp_match = component_match(context, rc, md,
                    princ);
	    if (comp_match) {
		pkiDebug("%s: match for keyword type %s\n",
			 __FUNCTION__, keyword2string(rc->kw_type));
	    }
	    if (comp_match && rs->relation == relation_or) {
		pkiDebug("%s: cert matches rule (OR relation)\n",
			 __FUNCTION__);
		total_cert_matches++;
		save_match = md;
		goto nextcert;
	    }
	    if (!comp_match && rs->relation == relation_and) {
		pkiDebug("%s: cert does not match rule (AND relation)\n",
			 __FUNCTION__);
		goto nextcert;
	    }
	}
	if (rc == NULL && comp_match) {
	    pkiDebug("%s: cert matches rule (AND relation)\n", __FUNCTION__);
	    total_cert_matches++;
	    save_match = md;
	}
nextcert:
	continue;
    }

    if (princ_string) {
        krb5_free_unparsed_name(context, princ_string);
    }

    pkiDebug("%s: After checking %d certs, we found %d matches\n",
	     __FUNCTION__, certs_checked, total_cert_matches);
    if (total_cert_matches == 1) {
	*match_found = 1;	
	*matching_cert = save_match;
    }

    retval = 0;

    pkiDebug("%s: returning %d, match_found %d\n",
	     __FUNCTION__, retval, *match_found);
    return retval;
}
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)
{
    krb5_error_code retval;
    int i, cert_count;
    pkinit_cert_iter_handle ih = NULL;
    pkinit_cert_handle ch;
    pkinit_cert_matching_data **matchdata = NULL;

    retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx,
				   id_cryptoctx, &cert_count);
    if (retval) {
	pkiDebug("%s: crypto_cert_get_count error %d, %s\n",
		 __FUNCTION__, retval, error_message(retval));
	goto cleanup;
    }

    pkiDebug("%s: crypto_cert_get_count says there are %d certs\n",
	      __FUNCTION__, cert_count);

    matchdata = calloc((size_t)cert_count + 1, sizeof(*matchdata));
    if (matchdata == NULL)
	return ENOMEM;

    retval = crypto_cert_iteration_begin(context, plg_cryptoctx, req_cryptoctx,
					 id_cryptoctx, &ih);
    if (retval) {
	pkiDebug("%s: crypto_cert_iteration_begin returned %d, %s\n",
		 __FUNCTION__, retval, error_message(retval));
	goto cleanup;
    }

    for (i = 0; i < cert_count; i++) {
	retval = crypto_cert_iteration_next(context, ih, &ch);
	if (retval) {
	    if (retval == PKINIT_ITER_NO_MORE)
		pkiDebug("%s: We thought there were %d certs, but "
			 "crypto_cert_iteration_next stopped after %d?\n",
			 __FUNCTION__, cert_count, i);
	    else
		pkiDebug("%s: crypto_cert_iteration_next error %d, %s\n",
			 __FUNCTION__, retval, error_message(retval));
	    goto cleanup;
	}

	retval = crypto_cert_get_matching_data(context, ch, &matchdata[i]);
	if (retval) {
	    pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n",
		     __FUNCTION__, retval, error_message(retval));
	    goto cleanup;
	}

    }

    *all_matching_data = matchdata;
    retval = 0;
cleanup:
    if (ih != NULL)
	crypto_cert_iteration_end(context, ih);
    if (retval) {
	if (matchdata != NULL)
	    free_all_cert_matching_data(context, matchdata);
    }
    pkiDebug("%s: returning %d, certinfo %p\n",
	     __FUNCTION__, retval, *all_matching_data);
    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;
}
Exemple #27
0
static krb5_error_code
pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
{
    krb5_error_code retval;
    char *eku_string = NULL;

    pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);
    retval = pkinit_kdcdefault_string(context, plgctx->realmname,
				      "pkinit_identity",
				      &plgctx->idopts->identity);
    if (retval != 0 || NULL == plgctx->idopts->identity) {
	retval = EINVAL;
	krb5_set_error_message(context, retval,
			       "No pkinit_identity supplied for realm %s",
			       plgctx->realmname);
	goto errout;
    }

    retval = pkinit_kdcdefault_strings(context, plgctx->realmname,
				       "pkinit_anchors",
				       &plgctx->idopts->anchors);
    if (retval != 0 || NULL == plgctx->idopts->anchors) {
	retval = EINVAL;
	krb5_set_error_message(context, retval,
			       "No pkinit_anchors supplied for realm %s",
			       plgctx->realmname);
	goto errout;
    }

    pkinit_kdcdefault_strings(context, plgctx->realmname,
			      "pkinit_pool",
			      &plgctx->idopts->intermediates);

    pkinit_kdcdefault_strings(context, plgctx->realmname,
			      "pkinit_revoke",
			      &plgctx->idopts->crls);

    pkinit_kdcdefault_string(context, plgctx->realmname,
			     "pkinit_kdc_ocsp",
			     &plgctx->idopts->ocsp);

    pkinit_kdcdefault_string(context, plgctx->realmname,
			     "pkinit_mappings_file",
			     &plgctx->idopts->dn_mapping_file);

    pkinit_kdcdefault_integer(context, plgctx->realmname,
			      "pkinit_dh_min_bits",
			      PKINIT_DEFAULT_DH_MIN_BITS,
			      &plgctx->opts->dh_min_bits);
    if (plgctx->opts->dh_min_bits < 1024) {
	pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, "
		 "using default value (%d) instead\n", __FUNCTION__,
		 plgctx->opts->dh_min_bits, PKINIT_DEFAULT_DH_MIN_BITS);
	plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
    }

    pkinit_kdcdefault_boolean(context, plgctx->realmname,
			      "pkinit_allow_upn",
			      0, &plgctx->opts->allow_upn);

    pkinit_kdcdefault_boolean(context, plgctx->realmname,
			      "pkinit_require_crl_checking",
			      0, &plgctx->opts->require_crl_checking);

    pkinit_kdcdefault_string(context, plgctx->realmname,
			     "pkinit_eku_checking",
			     &eku_string);
    if (eku_string != NULL) {
	if (strcasecmp(eku_string, "kpClientAuth") == 0) {
	    plgctx->opts->require_eku = 1;
	    plgctx->opts->accept_secondary_eku = 0;
	} else if (strcasecmp(eku_string, "scLogin") == 0) {
	    plgctx->opts->require_eku = 1;
	    plgctx->opts->accept_secondary_eku = 1;
	} else if (strcasecmp(eku_string, "none") == 0) {
	    plgctx->opts->require_eku = 0;
	    plgctx->opts->accept_secondary_eku = 0;
	} else {
	    pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
		     __FUNCTION__, eku_string);
	}
	free(eku_string);
    }


    return 0;
errout:
    pkinit_fini_kdc_profile(context, plgctx);
    return retval;
}
Exemple #28
0
/*
 * Parse a ContentInfo as best we can. All return fields are optional.
 * If signer_cert_status is NULL on entry, NO signature or cert evaluation 
 * will be performed. 
 */
krb5_error_code krb5int_pkinit_parse_cms_msg(
    const krb5_data	    *content_info,
    krb5_pkinit_cert_db_t   cert_db,		/* may be required for SignedData */
    krb5_boolean	    is_client_msg,	/* TRUE : msg is from client */
    krb5_boolean	    *is_signed,		/* RETURNED */
    krb5_boolean	    *is_encrypted,	/* RETURNED */
    krb5_data		    *raw_data,		/* RETURNED */
    krb5int_cms_content_type *inner_content_type,/* Returned, ContentType of */
						/*    EncapsulatedData */
    krb5_data		    *signer_cert,	/* RETURNED */
    krb5int_cert_sig_status *signer_cert_status,/* RETURNED */
    unsigned		    *num_all_certs,	/* size of *all_certs RETURNED */
    krb5_data		    **all_certs)	/* entire cert chain RETURNED */
{
    SecPolicySearchRef policy_search = NULL;
    SecPolicyRef policy = NULL;
    OSStatus ortn;
    krb5_error_code krtn = 0;
    CMSDecoderRef decoder = NULL;
    size_t num_signers;
    CMSSignerStatus signer_status;
    OSStatus cert_verify_status;
    CFArrayRef cf_all_certs = NULL;
    int msg_is_signed = 0;
    
    if(content_info == NULL) {
	pkiDebug("krb5int_pkinit_parse_cms_msg: no ContentInfo\n");
	return KRB5_CRYPTO_INTERNAL;
    }
    
    ortn = CMSDecoderCreate(&decoder);
    if(ortn) {
	return ENOMEM;
    }
    ortn = CMSDecoderUpdateMessage(decoder, content_info->data, content_info->length);
    if(ortn) {
	/* no verify yet, must be bad message */
	krtn = KRB5_PARSE_MALFORMED;
	goto errOut;
    }
    ortn = CMSDecoderFinalizeMessage(decoder);
    if(ortn) {
	pkiCssmErr("CMSDecoderFinalizeMessage", ortn);
	krtn = KRB5_PARSE_MALFORMED;
	goto errOut;
    }

    /* expect zero or one signers */
    ortn = CMSDecoderGetNumSigners(decoder, &num_signers);
    switch(num_signers) {
	case 0:
	    msg_is_signed = 0;
	    break;
	case 1:
	    msg_is_signed = 1;
	    break;
	default:
	    krtn = KRB5_PARSE_MALFORMED;
	    goto errOut;
    }

    /*
     * We need a cert verify policy even if we're not actually evaluating 
     * the cert due to requirements in libsecurity_smime.
     */
    ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
	is_client_msg ? &CSSMOID_APPLE_TP_PKINIT_CLIENT : &CSSMOID_APPLE_TP_PKINIT_SERVER, 
	NULL, &policy_search);
    if(ortn) {
	pkiCssmErr("SecPolicySearchCreate", ortn);
	krtn = KRB5_CRYPTO_INTERNAL;
	goto errOut;
    }
    ortn = SecPolicySearchCopyNext(policy_search, &policy);
    if(ortn) {
	pkiCssmErr("SecPolicySearchCopyNext", ortn);
	krtn = KRB5_CRYPTO_INTERNAL;
	goto errOut;
    }
    
    /* get some basic status that doesn't need heavyweight evaluation */
    if(msg_is_signed) {
	if(is_signed) {
	    *is_signed = TRUE;
	}
	if(inner_content_type) {
	    CSSM_OID ec_oid = {0, NULL};
	    CFDataRef ec_data = NULL;
	    
	    krb5int_cms_content_type ctype;
	    
	    ortn = CMSDecoderCopyEncapsulatedContentType(decoder, &ec_data);
	    if(ortn || (ec_data == NULL)) {
		pkiCssmErr("CMSDecoderCopyEncapsulatedContentType", ortn);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    ec_oid.Data = (uint8 *)CFDataGetBytePtr(ec_data);
	    ec_oid.Length = CFDataGetLength(ec_data);
	    if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_Data)) {
		ctype = ECT_Data;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_SignedData)) {
		ctype = ECT_SignedData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_EnvelopedData)) {
		ctype = ECT_EnvelopedData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_EncryptedData)) {
		ctype = ECT_EncryptedData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &_CSSMOID_PKINIT_AUTH_DATA)) {
		ctype = ECT_PkAuthData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &_CSSMOID_PKINIT_RKEY_DATA)) {
		ctype = ECT_PkReplyKeyKata;
	    }
	    else {
		ctype = ECT_Other;
	    }
	    *inner_content_type = ctype;
	    CFRelease(ec_data);
	}
	
	/* 
	 * Get SignedData's certs if the caller wants them
	 */
	if(all_certs) {	    
	    ortn = CMSDecoderCopyAllCerts(decoder, &cf_all_certs);
	    if(ortn) {
		pkiCssmErr("CMSDecoderCopyAllCerts", ortn);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    krtn = pkiCertArrayToKrb5Data(cf_all_certs, num_all_certs, all_certs);
	    if(krtn) {
		goto errOut;
	    }
	}
	
	/* optional signer cert */
	if(signer_cert) {
	    SecCertificateRef sec_signer_cert = NULL;
	    CSSM_DATA cert_data;

	    ortn = CMSDecoderCopySignerCert(decoder, 0, &sec_signer_cert);
	    if(ortn) {
		/* should never happen if it's signed */
		pkiCssmErr("CMSDecoderCopySignerStatus", ortn);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    ortn = SecCertificateGetData(sec_signer_cert, &cert_data);
	    if(ortn) {
		pkiCssmErr("SecCertificateGetData", ortn);
		CFRelease(sec_signer_cert);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    krtn = pkiDataToKrb5Data(cert_data.Data, cert_data.Length, signer_cert);
	    CFRelease(sec_signer_cert);
	    if(krtn) {
		goto errOut;
	    }
	}
    }
    else {
	/* not signed */
	if(is_signed) {
	    *is_signed = FALSE;
	}
	if(inner_content_type) {
	    *inner_content_type = ECT_Other;
	}
	if(signer_cert) {
	    signer_cert->data = NULL;
	    signer_cert->length = 0;
	}
	if(signer_cert_status) {
	    *signer_cert_status = pki_not_signed;
	}
	if(num_all_certs) {
	    *num_all_certs = 0;
	}
	if(all_certs) {
	    *all_certs = NULL;
	}
    }
    if(is_encrypted) {
	Boolean bencr;
	ortn = CMSDecoderIsContentEncrypted(decoder, &bencr);
	if(ortn) {
	    pkiCssmErr("CMSDecoderCopySignerStatus", ortn);
	    krtn = KRB5_CRYPTO_INTERNAL;
	    goto errOut;
	}
	*is_encrypted = bencr ? TRUE : FALSE;
    }
    
    /* 
     * Verify signature and cert. The actual verify operation is optional,
     * per our signer_cert_status argument, but we do this anyway if we need
     * to get the signer cert.
     */
    if((signer_cert_status != NULL) || (signer_cert != NULL)) {
	
	ortn = CMSDecoderCopySignerStatus(decoder, 
	    0,					    /* signerIndex */
	    policy,
	    signer_cert_status ? TRUE : FALSE,	    /* evaluateSecTrust */
	    &signer_status,
	    NULL,				    /* secTrust - not needed */
	    &cert_verify_status);
	if(ortn) {
	    /* gross error - subsequent processing impossible */
	    pkiCssmErr("CMSDecoderCopySignerStatus", ortn);
	    krtn = KRB5_PARSE_MALFORMED;
	    goto errOut;
	}
    }
    /* obtain & return status */
    if(signer_cert_status) {
	*signer_cert_status = pkiInferSigStatus(signer_status, cert_verify_status);
    }
    
    /* finally, the payload */
    if(raw_data) {
	CFDataRef cf_content = NULL;
	
	ortn = CMSDecoderCopyContent(decoder, &cf_content);
	if(ortn) {
	    pkiCssmErr("CMSDecoderCopyContent", ortn);
	    krtn = KRB5_PARSE_MALFORMED;
	    goto errOut;
	}
	krtn = pkiCfDataToKrb5Data(cf_content, raw_data);
	CFRELEASE(cf_content);
    }
errOut:
    CFRELEASE(policy_search);
    CFRELEASE(policy);
    CFRELEASE(cf_all_certs);
    CFRELEASE(decoder);
    return krtn;
}
static krb5_error_code
pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
{
    krb5_error_code retval;
    char *eku_string = NULL;

    pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);
    retval = pkinit_kdcdefault_string(context, plgctx->realmname,
                                      KRB5_CONF_PKINIT_IDENTITY,
                                      &plgctx->idopts->identity);
    if (retval != 0 || NULL == plgctx->idopts->identity) {
        retval = EINVAL;
        krb5_set_error_message(context, retval,
                               "No pkinit_identity supplied for realm %s",
                               plgctx->realmname);
        goto errout;
    }

    retval = pkinit_kdcdefault_strings(context, plgctx->realmname,
                                       KRB5_CONF_PKINIT_ANCHORS,
                                       &plgctx->idopts->anchors);
    if (retval != 0 || NULL == plgctx->idopts->anchors) {
        retval = EINVAL;
        krb5_set_error_message(context, retval,
                               "No pkinit_anchors supplied for realm %s",
                               plgctx->realmname);
        goto errout;
    }

    pkinit_kdcdefault_strings(context, plgctx->realmname,
                              KRB5_CONF_PKINIT_POOL,
                              &plgctx->idopts->intermediates);

    pkinit_kdcdefault_strings(context, plgctx->realmname,
                              KRB5_CONF_PKINIT_REVOKE,
                              &plgctx->idopts->crls);

    pkinit_kdcdefault_string(context, plgctx->realmname,
                             KRB5_CONF_PKINIT_KDC_OCSP,
                             &plgctx->idopts->ocsp);

    pkinit_kdcdefault_string(context, plgctx->realmname,
                             KRB5_CONF_PKINIT_MAPPING_FILE,
                             &plgctx->idopts->dn_mapping_file);

    pkinit_kdcdefault_integer(context, plgctx->realmname,
                              KRB5_CONF_PKINIT_DH_MIN_BITS,
                              PKINIT_DEFAULT_DH_MIN_BITS,
                              &plgctx->opts->dh_min_bits);
    if (plgctx->opts->dh_min_bits < PKINIT_DEFAULT_DH_MIN_BITS) {
        pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, "
                 "using default value (%d) instead\n", __FUNCTION__,
                 plgctx->opts->dh_min_bits, PKINIT_DEFAULT_DH_MIN_BITS);
        plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
    }

    pkinit_kdcdefault_boolean(context, plgctx->realmname,
                              KRB5_CONF_PKINIT_ALLOW_UPN,
                              0, &plgctx->opts->allow_upn);

    pkinit_kdcdefault_boolean(context, plgctx->realmname,
                              KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING,
                              0, &plgctx->opts->require_crl_checking);

    pkinit_kdcdefault_string(context, plgctx->realmname,
                             KRB5_CONF_PKINIT_EKU_CHECKING,
                             &eku_string);
    if (eku_string != NULL) {
        if (strcasecmp(eku_string, "kpClientAuth") == 0) {
            plgctx->opts->require_eku = 1;
            plgctx->opts->accept_secondary_eku = 0;
        } else if (strcasecmp(eku_string, "scLogin") == 0) {
            plgctx->opts->require_eku = 1;
            plgctx->opts->accept_secondary_eku = 1;
        } else if (strcasecmp(eku_string, "none") == 0) {
            plgctx->opts->require_eku = 0;
            plgctx->opts->accept_secondary_eku = 0;
        } else {
            pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
                     __FUNCTION__, eku_string);
        }
        free(eku_string);
    }


    return 0;
errout:
    pkinit_fini_kdc_profile(context, plgctx);
    return retval;
}
static krb5_error_code
pkinit_server_verify_padata(krb5_context context,
                            struct _krb5_db_entry_new * client,
                            krb5_data *req_pkt,
                            krb5_kdc_req * request,
                            krb5_enc_tkt_part * enc_tkt_reply,
                            krb5_pa_data * data,
                            preauth_get_entry_data_proc server_get_entry_data,
                            void *pa_plugin_context,
                            void **pa_request_context,
                            krb5_data **e_data,
                            krb5_authdata ***authz_data)
{
    krb5_error_code retval = 0;
    krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL};
    krb5_pa_pk_as_req *reqp = NULL;
    krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
    krb5_auth_pack *auth_pack = NULL;
    krb5_auth_pack_draft9 *auth_pack9 = NULL;
    pkinit_kdc_context plgctx = NULL;
    pkinit_kdc_req_context reqctx;
    krb5_preauthtype pa_type;
    krb5_checksum cksum = {0, 0, 0, NULL};
    krb5_data *der_req = NULL;
    int valid_eku = 0, valid_san = 0;
    krb5_kdc_req *tmp_as_req = NULL;
    krb5_data k5data;
    int is_signed = 1;
    krb5_keyblock *armor_key;

    pkiDebug("pkinit_verify_padata: entered!\n");
    if (data == NULL || data->length <= 0 || data->contents == NULL)
        return 0;

    /* Remove (along with armor_key) when FAST PKINIT is settled. */
    retval = fast_kdc_get_armor_key(context, server_get_entry_data, request,
                                    client, &armor_key);
    if (retval == 0 && armor_key != NULL) {
        /* Don't allow PKINIT if the client used FAST. */
        krb5_free_keyblock(context, armor_key);
        return EINVAL;
    }

    if (pa_plugin_context == NULL || e_data == NULL)
        return EINVAL;

    plgctx = pkinit_find_realm_context(context, pa_plugin_context,
                                       request->server);
    if (plgctx == NULL)
        return 0;

#ifdef DEBUG_ASN1
    print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");
#endif
    /* create a per-request context */
    retval = pkinit_init_kdc_req_context(context, (void **)&reqctx);
    if (retval)
        goto cleanup;
    reqctx->pa_type = data->pa_type;

    PADATA_TO_KRB5DATA(data, &k5data);

    switch ((int)data->pa_type) {
    case KRB5_PADATA_PK_AS_REQ:
        pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
        pa_type = (int)data->pa_type;
        retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
        if (retval) {
            pkiDebug("decode_krb5_pa_pk_as_req failed\n");
            goto cleanup;
        }
#ifdef DEBUG_ASN1
        print_buffer_bin(reqp->signedAuthPack.data,
                         reqp->signedAuthPack.length,
                         "/tmp/kdc_signed_data");
#endif
        retval = cms_signeddata_verify(context, plgctx->cryptoctx,
                                       reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT,
                                       plgctx->opts->require_crl_checking,
                                       reqp->signedAuthPack.data, reqp->signedAuthPack.length,
                                       &authp_data.data, &authp_data.length, &krb5_authz.data,
                                       &krb5_authz.length, &is_signed);
        break;
    case KRB5_PADATA_PK_AS_REP_OLD:
    case KRB5_PADATA_PK_AS_REQ_OLD:
        pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
        pa_type = KRB5_PADATA_PK_AS_REQ_OLD;
        retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9);
        if (retval) {
            pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
            goto cleanup;
        }
#ifdef DEBUG_ASN1
        print_buffer_bin(reqp9->signedAuthPack.data,
                         reqp9->signedAuthPack.length,
                         "/tmp/kdc_signed_data_draft9");
#endif

        retval = cms_signeddata_verify(context, plgctx->cryptoctx,
                                       reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9,
                                       plgctx->opts->require_crl_checking,
                                       reqp9->signedAuthPack.data, reqp9->signedAuthPack.length,
                                       &authp_data.data, &authp_data.length, &krb5_authz.data,
                                       &krb5_authz.length, NULL);
        break;
    default:
        pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
        retval = EINVAL;
        goto cleanup;
    }
    if (retval) {
        pkiDebug("pkcs7_signeddata_verify failed\n");
        goto cleanup;
    }
    if (is_signed) {

        retval = verify_client_san(context, plgctx, reqctx, request->client,
                                   &valid_san);
        if (retval)
            goto cleanup;
        if (!valid_san) {
            pkiDebug("%s: did not find an acceptable SAN in user "
                     "certificate\n", __FUNCTION__);
            retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
            goto cleanup;
        }
        retval = verify_client_eku(context, plgctx, reqctx, &valid_eku);
        if (retval)
            goto cleanup;

        if (!valid_eku) {
            pkiDebug("%s: did not find an acceptable EKU in user "
                     "certificate\n", __FUNCTION__);
            retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
            goto cleanup;
        }
    } else { /* !is_signed */
        if (!krb5_principal_compare(context, request->client,
                                    krb5_anonymous_principal())) {
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            krb5_set_error_message(context, retval, "Pkinit request not "
                                   "signed, but client not anonymous.");
            goto cleanup;
        }
    }
#ifdef DEBUG_ASN1
    print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");
#endif

    OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);
    switch ((int)data->pa_type) {
    case KRB5_PADATA_PK_AS_REQ:
        retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);
        if (retval) {
            pkiDebug("failed to decode krb5_auth_pack\n");
            goto cleanup;
        }

        /* check dh parameters */
        if (auth_pack->clientPublicValue != NULL) {
            retval = server_check_dh(context, plgctx->cryptoctx,
                                     reqctx->cryptoctx, plgctx->idctx,
                                     &auth_pack->clientPublicValue->algorithm.parameters,
                                     plgctx->opts->dh_min_bits);

            if (retval) {
                pkiDebug("bad dh parameters\n");
                goto cleanup;
            }
        } else if (!is_signed) {
            /*Anonymous pkinit requires DH*/
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            krb5_set_error_message(context, retval, "Anonymous pkinit without DH public value not supported.");
            goto cleanup;
        }
        /*
         * The KDC may have modified the request after decoding it.
         * We need to compute the checksum on the data that
         * came from the client.  Therefore, we use the original
         * packet contents.
         */
        retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req);
        if (retval) {
            pkiDebug("decode_krb5_as_req returned %d\n", (int)retval);
            goto cleanup;
        }

        retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req);
        if (retval) {
            pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
            goto cleanup;
        }
        retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL,
                                      0, der_req, &cksum);
        if (retval) {
            pkiDebug("unable to calculate AS REQ checksum\n");
            goto cleanup;
        }
        if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length ||
            memcmp(cksum.contents,
                   auth_pack->pkAuthenticator.paChecksum.contents,
                   cksum.length)) {
            pkiDebug("failed to match the checksum\n");
#ifdef DEBUG_CKSUM
            pkiDebug("calculating checksum on buf size (%d)\n",
                     req_pkt->length);
            print_buffer(req_pkt->data, req_pkt->length);
            pkiDebug("received checksum type=%d size=%d ",
                     auth_pack->pkAuthenticator.paChecksum.checksum_type,
                     auth_pack->pkAuthenticator.paChecksum.length);
            print_buffer(auth_pack->pkAuthenticator.paChecksum.contents,
                         auth_pack->pkAuthenticator.paChecksum.length);
            pkiDebug("expected checksum type=%d size=%d ",
                     cksum.checksum_type, cksum.length);
            print_buffer(cksum.contents, cksum.length);
#endif

            retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
            goto cleanup;
        }

        /* check if kdcPkId present and match KDC's subjectIdentifier */
        if (reqp->kdcPkId.data != NULL) {
            int valid_kdcPkId = 0;
            retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,
                                           reqctx->cryptoctx, plgctx->idctx,
                                           reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId);
            if (retval)
                goto cleanup;
            if (!valid_kdcPkId)
                pkiDebug("kdcPkId in AS_REQ does not match KDC's cert"
                         "RFC says to ignore and proceed\n");

        }
        /* remember the decoded auth_pack for verify_padata routine */
        reqctx->rcv_auth_pack = auth_pack;
        auth_pack = NULL;
        break;
    case KRB5_PADATA_PK_AS_REP_OLD:
    case KRB5_PADATA_PK_AS_REQ_OLD:
        retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9);
        if (retval) {
            pkiDebug("failed to decode krb5_auth_pack_draft9\n");
            goto cleanup;
        }
        if (auth_pack9->clientPublicValue != NULL) {
            retval = server_check_dh(context, plgctx->cryptoctx,
                                     reqctx->cryptoctx, plgctx->idctx,
                                     &auth_pack9->clientPublicValue->algorithm.parameters,
                                     plgctx->opts->dh_min_bits);

            if (retval) {
                pkiDebug("bad dh parameters\n");
                goto cleanup;
            }
        }
        /* remember the decoded auth_pack for verify_padata routine */
        reqctx->rcv_auth_pack9 = auth_pack9;
        auth_pack9 = NULL;
        break;
    }

    /*
     * This code used to generate ad-initial-verified-cas authorization data.
     * However that has been removed until the ad-kdc-issued discussion can
     * happen in the working group.  Dec 2009
     */
    /* return authorization data to be included in the ticket */
    switch ((int)data->pa_type) {
    default:
        *authz_data = NULL;
    }
    /* remember to set the PREAUTH flag in the reply */
    enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
    *pa_request_context = reqctx;
    reqctx = NULL;

cleanup:
    if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
        pkiDebug("pkinit_verify_padata failed: creating e-data\n");
        if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,
                                plgctx->idctx, plgctx->opts, retval, e_data))
            pkiDebug("pkinit_create_edata failed\n");
    }

    switch ((int)data->pa_type) {
    case KRB5_PADATA_PK_AS_REQ:
        free_krb5_pa_pk_as_req(&reqp);
        free(cksum.contents);
        if (der_req != NULL)
            krb5_free_data(context, der_req);
        break;
    case KRB5_PADATA_PK_AS_REP_OLD:
    case KRB5_PADATA_PK_AS_REQ_OLD:
        free_krb5_pa_pk_as_req_draft9(&reqp9);
    }
    if (tmp_as_req != NULL)
        k5int_krb5_free_kdc_req(context, tmp_as_req);
    free(authp_data.data);
    free(krb5_authz.data);
    if (reqctx != NULL)
        pkinit_fini_kdc_req_context(context, reqctx);
    if (auth_pack != NULL)
        free_krb5_auth_pack(&auth_pack);
    if (auth_pack9 != NULL)
        free_krb5_auth_pack_draft9(context, &auth_pack9);

    return retval;
}