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); }
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; }
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); }
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; }
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; }
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; }
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); }
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; }
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); }
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; }
/* * 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; }
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; }
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; }
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; }
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; }
/* * 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; }