static int certBag_parser(hx509_context context, struct hx509_collector *c, const void *data, size_t length, const PKCS12_Attributes *attrs) { heim_octet_string os; hx509_cert cert; PKCS12_CertBag cb; int ret; ret = decode_PKCS12_CertBag(data, length, &cb, NULL); if (ret) return ret; if (der_heim_oid_cmp(&asn1_oid_id_pkcs_9_at_certTypes_x509, &cb.certType)) { free_PKCS12_CertBag(&cb); return 0; } ret = decode_PKCS12_OctetString(cb.certValue.data, cb.certValue.length, &os, NULL); free_PKCS12_CertBag(&cb); if (ret) return ret; ret = hx509_cert_init_data(context, os.data, os.length, &cert); der_free_octet_string(&os); if (ret) return ret; ret = _hx509_collector_certs_add(context, c, cert); if (ret) { hx509_cert_free(cert); return ret; } { const PKCS12_Attribute *attr; const heim_oid *oids[] = { &asn1_oid_id_pkcs_9_at_localKeyId, &asn1_oid_id_pkcs_9_at_friendlyName }; size_t i; for (i = 0; i < sizeof(oids)/sizeof(oids[0]); i++) { const heim_oid *oid = oids[i]; attr = find_attribute(attrs, oid); if (attr) _hx509_set_cert_attribute(context, cert, oid, &attr->attrValues); } } hx509_cert_free(cert); return 0; }
static int any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs) { int ret; size_t i; if (sd->certificates == NULL) return 0; for (i = 0; i < sd->certificates->len; i++) { hx509_cert c; ret = hx509_cert_init_data(context, sd->certificates->val[i].data, sd->certificates->val[i].length, &c); if (ret) return ret; ret = hx509_certs_add(context, certs, c); hx509_cert_free(c); if (ret) return ret; } return 0; }
static int any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs) { int ret; size_t i; if (sd->certificates == NULL) return 0; for (i = 0; i < sd->certificates->len; i++) { heim_error_t error; hx509_cert c; c = hx509_cert_init_data(context, sd->certificates->val[i].data, sd->certificates->val[i].length, &error); if (c == NULL) { ret = heim_error_get_code(error); heim_release(error); return ret; } ret = hx509_certs_add(context, certs, c); hx509_cert_free(c); if (ret) return ret; } return 0; }
static int match_localkeyid(hx509_context context, struct private_key *value, hx509_certs certs) { hx509_cert cert; hx509_query q; int ret; if (value->localKeyId.length == 0) { hx509_set_error_string(context, 0, HX509_LOCAL_ATTRIBUTE_MISSING, "No local key attribute on private key"); return HX509_LOCAL_ATTRIBUTE_MISSING; } _hx509_query_clear(&q); q.match |= HX509_QUERY_MATCH_LOCAL_KEY_ID; q.local_key_id = &value->localKeyId; ret = hx509_certs_find(context, certs, &q, &cert); if (ret == 0) { if (value->private_key) _hx509_cert_assign_key(cert, value->private_key); hx509_cert_free(cert); } return ret; }
int hx509_certs_iter_f(hx509_context context, hx509_certs certs, int (*func)(hx509_context, void *, hx509_cert), void *ctx) { hx509_cursor cursor; hx509_cert c; int ret; ret = hx509_certs_start_seq(context, certs, &cursor); if (ret) return ret; while (1) { ret = hx509_certs_next_cert(context, certs, cursor, &c); if (ret) break; if (c == NULL) { ret = 0; break; } ret = (*func)(context, ctx, c); hx509_cert_free(c); if (ret) break; } hx509_certs_end_seq(context, certs, cursor); return ret; }
void _kdc_pk_free_client_param(krb5_context context, pk_client_params *cp) { if (cp == NULL) return; if (cp->cert) hx509_cert_free(cp->cert); if (cp->verify_ctx) hx509_verify_destroy_ctx(cp->verify_ctx); if (cp->keyex == USE_DH) { if (cp->u.dh.key) DH_free(cp->u.dh.key); if (cp->u.dh.public_key) BN_free(cp->u.dh.public_key); } if (cp->keyex == USE_ECDH) _kdc_pk_free_client_ec_param(context, cp->u.ecdh.key, cp->u.ecdh.public_key); krb5_free_keyblock_contents(context, &cp->reply_key); if (cp->dh_group_name) free(cp->dh_group_name); if (cp->peer) hx509_peer_info_free(cp->peer); if (cp->client_anchors) hx509_certs_free(&cp->client_anchors); memset(cp, 0, sizeof(*cp)); free(cp); }
/* * Certs used for this protocol have a GUID in the issuer_uniq_id field. * This function fetch it. */ static struct GUID *get_cert_guid(struct torture_context *tctx, TALLOC_CTX *mem_ctx, uint8_t *cert_data, uint32_t cert_len) { hx509_context hctx; hx509_cert cert; heim_bit_string subjectuniqid; DATA_BLOB data; int hret; uint32_t size; struct GUID *guid = talloc_zero(mem_ctx, struct GUID); NTSTATUS status; hx509_context_init(&hctx); hret = hx509_cert_init_data(hctx, cert_data, cert_len, &cert); if (hret) { torture_comment(tctx, "error while loading the cert\n"); hx509_context_free(&hctx); return NULL; } hret = hx509_cert_get_issuer_unique_id(hctx, cert, &subjectuniqid); if (hret) { torture_comment(tctx, "error while getting the issuer_uniq_id\n"); hx509_cert_free(cert); hx509_context_free(&hctx); return NULL; } /* The subjectuniqid is a bit string, * which means that the real size has to be divided by 8 * to have the number of bytes */ hx509_cert_free(cert); hx509_context_free(&hctx); size = subjectuniqid.length / 8; data = data_blob_const(subjectuniqid.data, size); status = GUID_from_data_blob(&data, guid); der_free_bit_string(&subjectuniqid); if (!NT_STATUS_IS_OK(status)) { return NULL; } return guid; }
static void free_ocsp(struct revoke_ocsp *ocsp) { free(ocsp->path); free_OCSPBasicOCSPResponse(&ocsp->ocsp); hx509_certs_free(&ocsp->certs); hx509_cert_free(ocsp->signer); }
static int match_keys(hx509_context context, struct private_key *value, hx509_certs certs) { hx509_cursor cursor; hx509_cert c; int ret, found = HX509_CERT_NOT_FOUND; if (value->private_key == NULL) { hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING, "No private key to compare with"); return HX509_PRIVATE_KEY_MISSING; } ret = hx509_certs_start_seq(context, certs, &cursor); if (ret) return ret; c = NULL; while (1) { ret = hx509_certs_next_cert(context, certs, cursor, &c); if (ret) break; if (c == NULL) break; if (_hx509_cert_private_key(c)) { hx509_cert_free(c); continue; } ret = _hx509_match_keys(c, value->private_key); if (ret) { _hx509_cert_assign_key(c, value->private_key); hx509_cert_free(c); found = 0; break; } hx509_cert_free(c); } hx509_certs_end_seq(context, certs, cursor); if (found) hx509_clear_error_string(context); return found; }
static int collect_cert(hx509_context context, struct p11_module *p, struct p11_slot *slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, void *ptr, CK_ATTRIBUTE *query, int num_query) { struct hx509_collector *collector = ptr; hx509_cert cert; int ret; if ((CK_LONG)query[0].ulValueLen == -1 || (CK_LONG)query[1].ulValueLen == -1) { return 0; } ret = hx509_cert_init_data(context, query[1].pValue, query[1].ulValueLen, &cert); if (ret) return ret; if (p->ref == 0) _hx509_abort("pkcs11 ref == 0 on alloc"); p->ref++; if (p->ref == UINT_MAX) _hx509_abort("pkcs11 ref to high"); _hx509_cert_set_release(cert, p11_cert_release, p); { heim_octet_string data; data.data = query[0].pValue; data.length = query[0].ulValueLen; _hx509_set_cert_attribute(context, cert, oid_id_pkcs_9_at_localKeyId(), &data); } if ((CK_LONG)query[2].ulValueLen != -1) { char *str; asprintf(&str, "%.*s", (int)query[2].ulValueLen, (char *)query[2].pValue); if (str) { hx509_cert_set_friendly_name(cert, str); free(str); } } ret = _hx509_collector_certs_add(context, collector, cert); hx509_cert_free(cert); return ret; }
int hx509_peer_info_set_cert(hx509_peer_info peer, hx509_cert cert) { if (peer->cert) hx509_cert_free(peer->cert); peer->cert = hx509_cert_ref(cert); return 0; }
void hx509_peer_info_free(hx509_peer_info peer) { if (peer == NULL) return; if (peer->cert) hx509_cert_free(peer->cert); free_cms_alg(peer); memset(peer, 0, sizeof(*peer)); free(peer); }
/* * Encrypt a blob with the private key of the certificate * passed as a parameter. */ static DATA_BLOB *encrypt_blob_pk(struct torture_context *tctx, TALLOC_CTX *mem_ctx, uint8_t *cert_data, uint32_t cert_len, DATA_BLOB *to_encrypt) { hx509_context hctx; hx509_cert cert; heim_octet_string secretdata; heim_octet_string encrypted; heim_oid encryption_oid; DATA_BLOB *blob; int hret; hx509_context_init(&hctx); hret = hx509_cert_init_data(hctx, cert_data, cert_len, &cert); if (hret) { torture_comment(tctx, "error while loading the cert\n"); hx509_context_free(&hctx); return NULL; } secretdata.data = to_encrypt->data; secretdata.length = to_encrypt->length; hret = hx509_cert_public_encrypt(hctx, &secretdata, cert, &encryption_oid, &encrypted); hx509_cert_free(cert); hx509_context_free(&hctx); if (hret) { torture_comment(tctx, "error while encrypting\n"); return NULL; } blob = talloc_zero(mem_ctx, DATA_BLOB); if (blob == NULL) { der_free_oid(&encryption_oid); der_free_octet_string(&encrypted); return NULL; } *blob = data_blob_talloc(blob, encrypted.data, encrypted.length); der_free_octet_string(&encrypted); der_free_oid(&encryption_oid); if (blob->data == NULL) { return NULL; } return blob; }
static int test_compare(hx509_context context) { int ret; hx509_cert c1, c2, c3; int l0, l1, l2, l3; /* check transative properties of name compare function */ ret = hx509_cert_init_data(context, certdata1, sizeof(certdata1) - 1, &c1); if (ret) return 1; ret = hx509_cert_init_data(context, certdata2, sizeof(certdata2) - 1, &c2); if (ret) return 1; ret = hx509_cert_init_data(context, certdata3, sizeof(certdata3) - 1, &c3); if (ret) return 1; ret = compare_subject(c1, c1, &l0); if (ret) return 1; ret = compare_subject(c1, c2, &l1); if (ret) return 1; ret = compare_subject(c1, c3, &l2); if (ret) return 1; ret = compare_subject(c2, c3, &l3); if (ret) return 1; if (l0 != 0) return 1; if (l2 < l1) return 1; if (l3 < l2) return 1; if (l3 < l1) return 1; hx509_cert_free(c1); hx509_cert_free(c2); hx509_cert_free(c3); return 0; }
static int mem_free(hx509_certs certs, void *data) { struct mem_data *mem = data; unsigned long i; for (i = 0; i < mem->certs.len; i++) hx509_cert_free(mem->certs.val[i]); free(mem->certs.val); for (i = 0; mem->keys && mem->keys[i]; i++) hx509_private_key_free(&mem->keys[i]); free(mem->keys); free(mem->name); free(mem); return 0; }
static int parse_certificate(hx509_context context, const char *fn, struct hx509_collector *c, const hx509_pem_header *headers, const void *data, size_t len) { hx509_cert cert; int ret; ret = hx509_cert_init_data(context, data, len, &cert); if (ret) return ret; ret = _hx509_collector_certs_add(context, c, cert); hx509_cert_free(cert); return ret; }
int hx509_certs_find(hx509_context context, hx509_certs certs, const hx509_query *q, hx509_cert *r) { hx509_cursor cursor; hx509_cert c; int ret; *r = NULL; _hx509_query_statistic(context, 0, q); if (certs->ops->query) return (*certs->ops->query)(context, certs, certs->ops_data, q, r); ret = hx509_certs_start_seq(context, certs, &cursor); if (ret) return ret; c = NULL; while (1) { ret = hx509_certs_next_cert(context, certs, cursor, &c); if (ret) break; if (c == NULL) break; if (_hx509_query_match_cert(context, q, c)) { *r = c; break; } hx509_cert_free(c); } hx509_certs_end_seq(context, certs, cursor); if (ret) return ret; if (c == NULL) { hx509_clear_error_string(context); return HX509_CERT_NOT_FOUND; } return 0; }
void _kdc_pk_free_client_param(krb5_context context, pk_client_params *client_params) { if (client_params->cert) hx509_cert_free(client_params->cert); if (client_params->dh) DH_free(client_params->dh); if (client_params->dh_public_key) BN_free(client_params->dh_public_key); krb5_free_keyblock_contents(context, &client_params->reply_key); if (client_params->dh_group_name) free(client_params->dh_group_name); if (client_params->peer) hx509_peer_info_free(client_params->peer); if (client_params->client_anchors) hx509_certs_free(&client_params->client_anchors); memset(client_params, 0, sizeof(*client_params)); free(client_params); }
static int parse_certificate(hx509_context context, const char *fn, struct hx509_collector *c, const hx509_pem_header *headers, const void *data, size_t len, const AlgorithmIdentifier *ai) { heim_error_t error = NULL; hx509_cert cert; int ret; cert = hx509_cert_init_data(context, data, len, &error); if (cert == NULL) { ret = heim_error_get_code(error); heim_release(error); return ret; } ret = _hx509_collector_certs_add(context, c, cert); hx509_cert_free(cert); return ret; }
static int keychain_iter_start(hx509_context context, hx509_certs certs, void *data, void **cursor) { #ifndef __APPLE_TARGET_EMBEDDED__ struct ks_keychain *ctx = data; #endif struct iter *iter; iter = calloc(1, sizeof(*iter)); if (iter == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } #ifndef __APPLE_TARGET_EMBEDDED__ if (ctx->anchors) { CFArrayRef anchors; int ret; int i; ret = hx509_certs_init(context, "MEMORY:ks-file-create", 0, NULL, &iter->certs); if (ret) { free(iter); return ret; } ret = SecTrustCopyAnchorCertificates(&anchors); if (ret != 0) { hx509_certs_free(&iter->certs); free(iter); hx509_set_error_string(context, 0, ENOMEM, "Can't get trust anchors from Keychain"); return ENOMEM; } for (i = 0; i < CFArrayGetCount(anchors); i++) { SecCertificateRef cr; hx509_cert cert; CFDataRef dataref; cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i); dataref = SecCertificateCopyData(cr); if (dataref == NULL) continue; ret = hx509_cert_init_data(context, CFDataGetBytePtr(dataref), CFDataGetLength(dataref), &cert); CFRelease(dataref); if (ret) continue; ret = hx509_certs_add(context, iter->certs, cert); hx509_cert_free(cert); } CFRelease(anchors); if (ret != 0) { hx509_certs_free(&iter->certs); free(iter); hx509_set_error_string(context, 0, ret, "Failed to add cert"); return ret; } } if (iter->certs) { int ret; ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor); if (ret) { hx509_certs_free(&iter->certs); free(iter); return ret; } } else #endif { OSStatus ret; const void *keys[] = { kSecClass, kSecReturnRef, kSecMatchLimit }; const void *values[] = { kSecClassCertificate, kCFBooleanTrue, kSecMatchLimitAll }; CFDictionaryRef secQuery; secQuery = CFDictionaryCreate(NULL, keys, values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); ret = SecItemCopyMatching(secQuery, (CFTypeRef *)&iter->search); CFRelease(secQuery); if (ret) { free(iter); return ENOMEM; } } *cursor = iter; return 0; }
OM_uint32 _gss_iakerb_acquire_cred_ext(OM_uint32 * minor_status, const gss_name_t desired_name, gss_const_OID credential_type, const void *credential_data, OM_uint32 time_req, gss_const_OID desired_mech, gss_cred_usage_t cred_usage, gss_cred_id_t * output_cred_handle) { krb5_context context; gsskrb5_cred handle; krb5_error_code ret; krb5_creds cred; gss_buffer_t credential_buffer = NULL; #ifdef PKINIT hx509_cert cert = NULL; #endif memset(&cred, 0, sizeof(cred)); if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) return GSS_S_FAILURE; GSSAPI_KRB5_INIT_STATUS(&context, status); /* pick up the credential */ if (gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { credential_buffer = (gss_buffer_t)credential_data; if (credential_buffer->length + 1 < credential_buffer->length) return GSS_S_FAILURE; #ifdef PKINIT } else if (gss_oid_equal(credential_type, GSS_C_CRED_CERTIFICATE)) { cert = (hx509_cert)credential_data; } else if (gss_oid_equal(credential_type, GSS_C_CRED_SecIdentity)) { ret = hx509_cert_init_SecFramework(context->hx509ctx, rk_UNCONST(credential_data), &cert); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } #endif } else { *minor_status = KRB5_NOCREDS_SUPPLIED; return GSS_S_FAILURE; } if (desired_name == GSS_C_NO_NAME) return GSS_S_FAILURE; handle = calloc(1, sizeof(*handle)); if (handle == NULL) return (GSS_S_FAILURE); HEIMDAL_MUTEX_init(&handle->cred_id_mutex); handle->usage = GSS_C_INITIATE; { krb5_principal princ = (krb5_principal)desired_name; ret = krb5_copy_principal(context, princ, &handle->principal); if (ret) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); free(handle); *minor_status = ret; return GSS_S_FAILURE; } } if (credential_buffer) { handle->password = malloc(credential_buffer->length + 1); if (handle->password == NULL) { krb5_free_principal(context, handle->principal); HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); free(handle); *minor_status = ENOMEM; return GSS_S_FAILURE; } memcpy(handle->password, credential_buffer->value, credential_buffer->length); handle->password[credential_buffer->length] = '\0'; } #ifdef PKINIT if (cert) handle->cert = heim_retain(cert); #endif handle->keytab = NULL; handle->ccache = NULL; handle->endtime = INT_MAX; /* * Lets overwrite the same credentials if we already have it */ ret = krb5_cc_cache_match(context, handle->principal, &handle->ccache); if (ret) { ret = krb5_cc_new_unique(context, krb5_cc_type_api, NULL, &handle->ccache); if (ret) goto out; } ret = krb5_cc_initialize(context, handle->ccache, handle->principal); if (ret) goto out; { krb5_data data; krb5_data_zero(&data); krb5_cc_set_config(context, handle->ccache, NULL, "iakerb", &data); } if (handle->password) { krb5_data pw; pw.data = handle->password; pw.length = strlen(handle->password); ret = krb5_cc_set_config(context, handle->ccache, NULL, "password", &pw); if (ret) goto out; } #ifdef PKINIT if (handle->cert) { krb5_data pd; ret = hx509_cert_get_persistent(handle->cert, &pd); if (ret) goto out; ret = krb5_cc_set_config(context, handle->ccache, NULL, "certificate-ref", &pd); der_free_octet_string(&pd); if (ret) goto out; } #endif *output_cred_handle = (gss_cred_id_t) handle; *minor_status = 0; return GSS_S_COMPLETE; out: krb5_free_principal(context, handle->principal); if (handle->password) { memset(handle->password, 0, strlen(handle->password)); free(handle->password); } #ifdef PKINIT if (handle->cert) hx509_cert_free(handle->cert); #endif if (handle->ccache) krb5_cc_destroy(context, handle->ccache); HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); free(handle); *minor_status = ret; return GSS_S_FAILURE; }
static krb5_error_code pk_mk_pa_reply_enckey(krb5_context context, krb5_kdc_configuration *config, pk_client_params *cp, const KDC_REQ *req, const krb5_data *req_buffer, krb5_keyblock *reply_key, ContentInfo *content_info, hx509_cert *kdc_cert) { const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL; krb5_error_code ret; krb5_data buf, signed_data; size_t size = 0; int do_win2k = 0; krb5_data_zero(&buf); krb5_data_zero(&signed_data); *kdc_cert = NULL; /* * If the message client is a win2k-type but it send pa data * 09-binding it expects a IETF (checksum) reply so there can be * no replay attacks. */ switch (cp->type) { case PKINIT_WIN2K: { int i = 0; if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL && config->pkinit_require_binding == 0) { do_win2k = 1; } sdAlg = &asn1_oid_id_pkcs7_data; evAlg = &asn1_oid_id_pkcs7_data; envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc; break; } case PKINIT_27: sdAlg = &asn1_oid_id_pkrkeydata; evAlg = &asn1_oid_id_pkcs7_signedData; break; default: krb5_abortx(context, "internal pkinit error"); } if (do_win2k) { ReplyKeyPack_Win2k kp; memset(&kp, 0, sizeof(kp)); ret = copy_EncryptionKey(reply_key, &kp.replyKey); if (ret) { krb5_clear_error_message(context); goto out; } kp.nonce = cp->nonce; ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k, buf.data, buf.length, &kp, &size,ret); free_ReplyKeyPack_Win2k(&kp); } else { krb5_crypto ascrypto; ReplyKeyPack kp; memset(&kp, 0, sizeof(kp)); ret = copy_EncryptionKey(reply_key, &kp.replyKey); if (ret) { krb5_clear_error_message(context); goto out; } ret = krb5_crypto_init(context, reply_key, 0, &ascrypto); if (ret) { krb5_clear_error_message(context); goto out; } ret = krb5_create_checksum(context, ascrypto, 6, 0, req_buffer->data, req_buffer->length, &kp.asChecksum); if (ret) { krb5_clear_error_message(context); goto out; } ret = krb5_crypto_destroy(context, ascrypto); if (ret) { krb5_clear_error_message(context); goto out; } ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret); free_ReplyKeyPack(&kp); } if (ret) { krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack " "failed (%d)", ret); goto out; } if (buf.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); { hx509_query *q; hx509_cert cert; ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) goto out; hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); if (config->pkinit_kdc_friendly_name) hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret) goto out; ret = hx509_cms_create_signed_1(context->hx509ctx, 0, sdAlg, buf.data, buf.length, NULL, cert, cp->peer, cp->client_anchors, kdc_identity->certpool, &signed_data); *kdc_cert = cert; } krb5_data_free(&buf); if (ret) goto out; if (cp->type == PKINIT_WIN2K) { ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &signed_data, &buf); if (ret) goto out; krb5_data_free(&signed_data); signed_data = buf; } ret = hx509_cms_envelope_1(context->hx509ctx, HX509_CMS_EV_NO_KU_CHECK, cp->cert, signed_data.data, signed_data.length, envelopedAlg, evAlg, &buf); if (ret) goto out; ret = _krb5_pk_mk_ContentInfo(context, &buf, &asn1_oid_id_pkcs7_envelopedData, content_info); out: if (ret && *kdc_cert) { hx509_cert_free(*kdc_cert); *kdc_cert = NULL; } krb5_data_free(&buf); krb5_data_free(&signed_data); return ret; }
static krb5_error_code pk_mk_pa_reply_dh(krb5_context context, krb5_kdc_configuration *config, pk_client_params *cp, ContentInfo *content_info, hx509_cert *kdc_cert) { KDCDHKeyInfo dh_info; krb5_data signed_data, buf; ContentInfo contentinfo; krb5_error_code ret; hx509_cert cert; hx509_query *q; size_t size = 0; memset(&contentinfo, 0, sizeof(contentinfo)); memset(&dh_info, 0, sizeof(dh_info)); krb5_data_zero(&signed_data); krb5_data_zero(&buf); *kdc_cert = NULL; if (cp->keyex == USE_DH) { DH *kdc_dh = cp->u.dh.key; heim_integer i; ret = BN_to_integer(context, kdc_dh->pub_key, &i); if (ret) return ret; ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret); der_free_heim_integer(&i); if (ret) { krb5_set_error_message(context, ret, "ASN.1 encoding of " "DHPublicKey failed (%d)", ret); return ret; } if (buf.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); dh_info.subjectPublicKey.length = buf.length * 8; dh_info.subjectPublicKey.data = buf.data; krb5_data_zero(&buf); } else if (cp->keyex == USE_ECDH) { unsigned char *p; ret = _kdc_serialize_ecdh_key(context, cp->u.ecdh.key, &p, &dh_info.subjectPublicKey.length); dh_info.subjectPublicKey.data = p; if (ret) goto out; } else krb5_abortx(context, "no keyex selected ?"); dh_info.nonce = cp->nonce; ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size, ret); if (ret) { krb5_set_error_message(context, ret, "ASN.1 encoding of " "KdcDHKeyInfo failed (%d)", ret); goto out; } if (buf.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); /* * Create the SignedData structure and sign the KdcDHKeyInfo * filled in above */ ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) goto out; hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); if (config->pkinit_kdc_friendly_name) hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret) goto out; ret = hx509_cms_create_signed_1(context->hx509ctx, 0, &asn1_oid_id_pkdhkeydata, buf.data, buf.length, NULL, cert, cp->peer, cp->client_anchors, kdc_identity->certpool, &signed_data); if (ret) { kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret); goto out; } *kdc_cert = cert; ret = _krb5_pk_mk_ContentInfo(context, &signed_data, &asn1_oid_id_pkcs7_signedData, content_info); if (ret) goto out; out: if (ret && *kdc_cert) { hx509_cert_free(*kdc_cert); *kdc_cert = NULL; } krb5_data_free(&buf); krb5_data_free(&signed_data); free_KDCDHKeyInfo(&dh_info); return ret; }
krb5_error_code krb5_kdc_pk_initialize(krb5_context context, krb5_kdc_configuration *config, const char *user_id, const char *anchors, char **pool, char **revoke_list) { const char *file; char *fn = NULL; krb5_error_code ret; file = krb5_config_get_string(context, NULL, "libdefaults", "moduli", NULL); ret = _krb5_parse_moduli(context, file, &moduli); if (ret) krb5_err(context, 1, ret, "PKINIT: failed to load modidi file"); principal_mappings.len = 0; principal_mappings.val = NULL; ret = _krb5_pk_load_id(context, &kdc_identity, user_id, anchors, pool, revoke_list, NULL, NULL, NULL); if (ret) { krb5_warn(context, ret, "PKINIT: "); config->enable_pkinit = 0; return ret; } { hx509_query *q; hx509_cert cert; ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { krb5_warnx(context, "PKINIT: out of memory"); return ENOMEM; } hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); if (config->pkinit_kdc_friendly_name) hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret == 0) { if (hx509_cert_check_eku(context->hx509ctx, cert, &asn1_oid_id_pkkdcekuoid, 0)) { hx509_name name; char *str; ret = hx509_cert_get_subject(cert, &name); if (ret == 0) { hx509_name_to_string(name, &str); krb5_warnx(context, "WARNING Found KDC certificate (%s)" "is missing the PK-INIT KDC EKU, this is bad for " "interoperability.", str); hx509_name_free(&name); free(str); } } hx509_cert_free(cert); } else krb5_warnx(context, "PKINIT: failed to find a signing " "certifiate with a public key"); } if (krb5_config_get_bool_default(context, NULL, FALSE, "kdc", "pkinit_allow_proxy_certificate", NULL)) config->pkinit_allow_proxy_certs = 1; file = krb5_config_get_string(context, NULL, "kdc", "pkinit_mappings_file", NULL); if (file == NULL) { int aret; aret = asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context)); if (aret == -1) { krb5_warnx(context, "PKINIT: out of memory"); return ENOMEM; } file = fn; } load_mappings(context, file); if (fn) free(fn); return 0; }
krb5_error_code _kdc_pk_rd_padata(krb5_context context, krb5_kdc_configuration *config, const KDC_REQ *req, const PA_DATA *pa, hdb_entry_ex *client, pk_client_params **ret_params) { pk_client_params *cp; krb5_error_code ret; heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL }; krb5_data eContent = { 0, NULL }; krb5_data signed_content = { 0, NULL }; const char *type = "unknown type"; hx509_certs trust_anchors; int have_data = 0; const HDB_Ext_PKINIT_cert *pc; *ret_params = NULL; if (!config->enable_pkinit) { kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled"); krb5_clear_error_message(context); return 0; } cp = calloc(1, sizeof(*cp)); if (cp == NULL) { krb5_clear_error_message(context); ret = ENOMEM; goto out; } ret = hx509_certs_init(context->hx509ctx, "MEMORY:trust-anchors", 0, NULL, &trust_anchors); if (ret) { krb5_set_error_message(context, ret, "failed to create trust anchors"); goto out; } ret = hx509_certs_merge(context->hx509ctx, trust_anchors, kdc_identity->anchors); if (ret) { hx509_certs_free(&trust_anchors); krb5_set_error_message(context, ret, "failed to create verify context"); goto out; } /* Add any registered certificates for this client as trust anchors */ ret = hdb_entry_get_pkinit_cert(&client->entry, &pc); if (ret == 0 && pc != NULL) { hx509_cert cert; unsigned int i; for (i = 0; i < pc->len; i++) { cert = hx509_cert_init_data(context->hx509ctx, pc->val[i].cert.data, pc->val[i].cert.length, NULL); if (cert == NULL) continue; hx509_certs_add(context->hx509ctx, trust_anchors, cert); hx509_cert_free(cert); } } ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx); if (ret) { hx509_certs_free(&trust_anchors); krb5_set_error_message(context, ret, "failed to create verify context"); goto out; } hx509_verify_set_time(cp->verify_ctx, kdc_time); hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors); hx509_certs_free(&trust_anchors); if (config->pkinit_allow_proxy_certs) hx509_verify_set_proxy_certificate(cp->verify_ctx, 1); if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { PA_PK_AS_REQ_Win2k r; type = "PK-INIT-Win2k"; if (_kdc_is_anon_request(&req->req_body)) { ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; krb5_set_error_message(context, ret, "Anon not supported in RSA mode"); goto out; } ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data, pa->padata_value.length, &r, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode " "PK-AS-REQ-Win2k: %d", ret); goto out; } ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack, &contentInfoOid, &signed_content, &have_data); free_PA_PK_AS_REQ_Win2k(&r); if (ret) { krb5_set_error_message(context, ret, "Can't unwrap ContentInfo(win): %d", ret); goto out; } } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { PA_PK_AS_REQ r; type = "PK-INIT-IETF"; ret = decode_PA_PK_AS_REQ(pa->padata_value.data, pa->padata_value.length, &r, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode PK-AS-REQ: %d", ret); goto out; } /* XXX look at r.kdcPkId */ if (r.trustedCertifiers) { ExternalPrincipalIdentifiers *edi = r.trustedCertifiers; unsigned int i, maxedi; ret = hx509_certs_init(context->hx509ctx, "MEMORY:client-anchors", 0, NULL, &cp->client_anchors); if (ret) { krb5_set_error_message(context, ret, "Can't allocate client anchors: %d", ret); goto out; } /* * If the client sent more then 10 EDI, don't bother * looking more then 10 of performance reasons. */ maxedi = edi->len; if (maxedi > 10) maxedi = 10; for (i = 0; i < maxedi; i++) { IssuerAndSerialNumber iasn; hx509_query *q; hx509_cert cert; size_t size; if (edi->val[i].issuerAndSerialNumber == NULL) continue; ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { krb5_set_error_message(context, ret, "Failed to allocate hx509_query"); goto out; } ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data, edi->val[i].issuerAndSerialNumber->length, &iasn, &size); if (ret) { hx509_query_free(context->hx509ctx, q); continue; } ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber); free_IssuerAndSerialNumber(&iasn); if (ret) { hx509_query_free(context->hx509ctx, q); continue; } ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret) continue; hx509_certs_add(context->hx509ctx, cp->client_anchors, cert); hx509_cert_free(cert); } } ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack, &contentInfoOid, &signed_content, &have_data); free_PA_PK_AS_REQ(&r); if (ret) { krb5_set_error_message(context, ret, "Can't unwrap ContentInfo: %d", ret); goto out; } } else { krb5_clear_error_message(context); ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; goto out; } ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData); if (ret != 0) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "PK-AS-REQ-Win2k invalid content type oid"); goto out; } if (!have_data) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "PK-AS-REQ-Win2k no signed auth pack"); goto out; } { hx509_certs signer_certs; int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */ if (_kdc_is_anon_request(&req->req_body)) flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER; ret = hx509_cms_verify_signed(context->hx509ctx, cp->verify_ctx, flags, signed_content.data, signed_content.length, NULL, kdc_identity->certpool, &eContentType, &eContent, &signer_certs); if (ret) { char *s = hx509_get_error_string(context->hx509ctx, ret); krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d", s, ret); free(s); goto out; } if (signer_certs) { ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &cp->cert); hx509_certs_free(&signer_certs); } if (ret) goto out; } /* Signature is correct, now verify the signed message */ if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 && der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0) { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "got wrong oid for pkauthdata"); goto out; } if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { AuthPack_Win2k ap; ret = decode_AuthPack_Win2k(eContent.data, eContent.length, &ap, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode AuthPack: %d", ret); goto out; } ret = pk_check_pkauthenticator_win2k(context, &ap.pkAuthenticator, req); if (ret) { free_AuthPack_Win2k(&ap); goto out; } cp->type = PKINIT_WIN2K; cp->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "DH not supported for windows"); goto out; } free_AuthPack_Win2k(&ap); } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { AuthPack ap; ret = decode_AuthPack(eContent.data, eContent.length, &ap, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode AuthPack: %d", ret); free_AuthPack(&ap); goto out; } if (_kdc_is_anon_request(&req->req_body) && ap.clientPublicValue == NULL) { free_AuthPack(&ap); ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; krb5_set_error_message(context, ret, "Anon not supported in RSA mode"); goto out; } ret = pk_check_pkauthenticator(context, &ap.pkAuthenticator, req); if (ret) { free_AuthPack(&ap); goto out; } cp->type = PKINIT_27; cp->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) { cp->keyex = USE_DH; ret = get_dh_param(context, config, ap.clientPublicValue, cp); } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) { cp->keyex = USE_ECDH; ret = _kdc_get_ecdh_param(context, config, ap.clientPublicValue, &cp->u.ecdh.public_key); } else { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism"); } if (ret) { free_AuthPack(&ap); goto out; } } else cp->keyex = USE_RSA; ret = hx509_peer_info_alloc(context->hx509ctx, &cp->peer); if (ret) { free_AuthPack(&ap); goto out; } if (ap.supportedCMSTypes) { ret = hx509_peer_info_set_cms_algs(context->hx509ctx, cp->peer, ap.supportedCMSTypes->val, ap.supportedCMSTypes->len); if (ret) { free_AuthPack(&ap); goto out; } } else { /* assume old client */ hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_crypto_des_rsdi_ede3_cbc()); hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_signature_rsa_with_sha1()); hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_signature_sha1()); } free_AuthPack(&ap); } else krb5_abortx(context, "internal pkinit error"); kdc_log(context, config, 0, "PK-INIT request of type %s", type); out: if (ret) krb5_warn(context, ret, "PKINIT"); if (signed_content.data) free(signed_content.data); krb5_data_free(&eContent); der_free_oid(&eContentType); der_free_oid(&contentInfoOid); if (ret) { _kdc_pk_free_client_param(context, cp); } else *ret_params = cp; return ret; }
krb5_error_code _kdc_pk_check_client(krb5_context context, krb5_kdc_configuration *config, HDB *clientdb, hdb_entry_ex *client, pk_client_params *cp, char **subject_name) { const HDB_Ext_PKINIT_acl *acl; const HDB_Ext_PKINIT_cert *pc; krb5_error_code ret; hx509_name name; size_t i; if (cp->cert == NULL) { *subject_name = strdup("anonymous client client"); if (*subject_name == NULL) return ENOMEM; return 0; } ret = hx509_cert_get_base_subject(context->hx509ctx, cp->cert, &name); if (ret) return ret; ret = hx509_name_to_string(name, subject_name); hx509_name_free(&name); if (ret) return ret; kdc_log(context, config, 0, "Trying to authorize PK-INIT subject DN %s", *subject_name); ret = hdb_entry_get_pkinit_cert(&client->entry, &pc); if (ret == 0 && pc) { hx509_cert cert; size_t j; for (j = 0; j < pc->len; j++) { cert = hx509_cert_init_data(context->hx509ctx, pc->val[j].cert.data, pc->val[j].cert.length, NULL); if (cert == NULL) continue; ret = hx509_cert_cmp(cert, cp->cert); hx509_cert_free(cert); if (ret == 0) { kdc_log(context, config, 5, "Found matching PK-INIT cert in hdb"); return 0; } } } if (config->pkinit_princ_in_cert) { ret = match_rfc_san(context, config, context->hx509ctx, cp->cert, client->entry.principal); if (ret == 0) { kdc_log(context, config, 5, "Found matching PK-INIT SAN in certificate"); return 0; } ret = match_ms_upn_san(context, config, context->hx509ctx, cp->cert, clientdb, client); if (ret == 0) { kdc_log(context, config, 5, "Found matching MS UPN SAN in certificate"); return 0; } } ret = hdb_entry_get_pkinit_acl(&client->entry, &acl); if (ret == 0 && acl != NULL) { /* * Cheat here and compare the generated name with the string * and not the reverse. */ for (i = 0; i < acl->len; i++) { if (strcmp(*subject_name, acl->val[0].subject) != 0) continue; /* Don't support isser and anchor checking right now */ if (acl->val[0].issuer) continue; if (acl->val[0].anchor) continue; kdc_log(context, config, 5, "Found matching PK-INIT database ACL"); return 0; } } for (i = 0; i < principal_mappings.len; i++) { krb5_boolean b; b = krb5_principal_compare(context, client->entry.principal, principal_mappings.val[i].principal); if (b == FALSE) continue; if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0) continue; kdc_log(context, config, 5, "Found matching PK-INIT FILE ACL"); return 0; } ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; krb5_set_error_message(context, ret, "PKINIT no matching principals for %s", *subject_name); kdc_log(context, config, 5, "PKINIT no matching principals for %s", *subject_name); free(*subject_name); *subject_name = NULL; return ret; }
static int keychain_iter_start(hx509_context context, hx509_certs certs, void *data, void **cursor) { struct ks_keychain *ctx = data; struct iter *iter; iter = calloc(1, sizeof(*iter)); if (iter == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } if (ctx->anchors) { CFArrayRef anchors; int ret; int i; ret = hx509_certs_init(context, "MEMORY:ks-file-create", 0, NULL, &iter->certs); if (ret) { free(iter); return ret; } ret = SecTrustCopyAnchorCertificates(&anchors); if (ret != 0) { hx509_certs_free(&iter->certs); free(iter); hx509_set_error_string(context, 0, ENOMEM, "Can't get trust anchors from Keychain"); return ENOMEM; } for (i = 0; i < CFArrayGetCount(anchors); i++) { SecCertificateRef cr; hx509_cert cert; CSSM_DATA cssm; cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i); SecCertificateGetData(cr, &cssm); ret = hx509_cert_init_data(context, cssm.Data, cssm.Length, &cert); if (ret) continue; ret = hx509_certs_add(context, iter->certs, cert); hx509_cert_free(cert); } CFRelease(anchors); } if (iter->certs) { int ret; ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor); if (ret) { hx509_certs_free(&iter->certs); free(iter); return ret; } } else { OSStatus ret; ret = SecKeychainSearchCreateFromAttributes(ctx->keychain, kSecCertificateItemClass, NULL, &iter->searchRef); if (ret) { free(iter); hx509_set_error_string(context, 0, ret, "Failed to start search for attributes"); return ENOMEM; } } *cursor = iter; return 0; }
krb5_error_code _kdc_pk_mk_pa_reply(krb5_context context, krb5_kdc_configuration *config, pk_client_params *cp, const hdb_entry_ex *client, krb5_enctype sessionetype, const KDC_REQ *req, const krb5_data *req_buffer, krb5_keyblock *reply_key, krb5_keyblock *sessionkey, METHOD_DATA *md) { krb5_error_code ret; void *buf = NULL; size_t len = 0, size = 0; krb5_enctype enctype; int pa_type; hx509_cert kdc_cert = NULL; size_t i; if (!config->enable_pkinit) { krb5_clear_error_message(context); return 0; } if (req->req_body.etype.len > 0) { for (i = 0; i < req->req_body.etype.len; i++) if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0) break; if (req->req_body.etype.len <= i) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "No valid enctype available from client"); goto out; } enctype = req->req_body.etype.val[i]; } else enctype = ETYPE_DES3_CBC_SHA1; if (cp->type == PKINIT_27) { PA_PK_AS_REP rep; const char *type, *other = ""; memset(&rep, 0, sizeof(rep)); pa_type = KRB5_PADATA_PK_AS_REP; if (cp->keyex == USE_RSA) { ContentInfo info; type = "enckey"; rep.element = choice_PA_PK_AS_REP_encKeyPack; ret = krb5_generate_random_keyblock(context, enctype, &cp->reply_key); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } ret = pk_mk_pa_reply_enckey(context, config, cp, req, req_buffer, &cp->reply_key, &info, &kdc_cert); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data, rep.u.encKeyPack.length, &info, &size, ret); free_ContentInfo(&info); if (ret) { krb5_set_error_message(context, ret, "encoding of Key ContentInfo " "failed %d", ret); free_PA_PK_AS_REP(&rep); goto out; } if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); ret = krb5_generate_random_keyblock(context, sessionetype, sessionkey); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } } else { ContentInfo info; switch (cp->keyex) { case USE_DH: type = "dh"; break; case USE_ECDH: type = "ecdh"; break; default: krb5_abortx(context, "unknown keyex"); break; } if (cp->dh_group_name) other = cp->dh_group_name; rep.element = choice_PA_PK_AS_REP_dhInfo; ret = generate_dh_keyblock(context, cp, enctype); if (ret) return ret; ret = pk_mk_pa_reply_dh(context, config, cp, &info, &kdc_cert); if (ret) { free_PA_PK_AS_REP(&rep); krb5_set_error_message(context, ret, "create pa-reply-dh " "failed %d", ret); goto out; } ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data, rep.u.dhInfo.dhSignedData.length, &info, &size, ret); free_ContentInfo(&info); if (ret) { krb5_set_error_message(context, ret, "encoding of Key ContentInfo " "failed %d", ret); free_PA_PK_AS_REP(&rep); goto out; } if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); /* generate the session key using the method from RFC6112 */ { krb5_keyblock kdc_contribution_key; krb5_crypto reply_crypto; krb5_crypto kdccont_crypto; krb5_data p1 = { strlen("PKINIT"), "PKINIT"}; krb5_data p2 = { strlen("KEYEXCHANGE"), "KEYEXCHANGE"}; void *kckdata; size_t kcklen; EncryptedData kx; void *kxdata; size_t kxlen; ret = krb5_generate_random_keyblock(context, sessionetype, &kdc_contribution_key); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } ret = krb5_crypto_init(context, &cp->reply_key, enctype, &reply_crypto); if (ret) { krb5_free_keyblock_contents(context, &kdc_contribution_key); free_PA_PK_AS_REP(&rep); goto out; } ret = krb5_crypto_init(context, &kdc_contribution_key, sessionetype, &kdccont_crypto); if (ret) { krb5_crypto_destroy(context, reply_crypto); krb5_free_keyblock_contents(context, &kdc_contribution_key); free_PA_PK_AS_REP(&rep); goto out; } /* KRB-FX-CF2 */ ret = krb5_crypto_fx_cf2(context, kdccont_crypto, reply_crypto, &p1, &p2, sessionetype, sessionkey); krb5_crypto_destroy(context, kdccont_crypto); if (ret) { krb5_crypto_destroy(context, reply_crypto); krb5_free_keyblock_contents(context, &kdc_contribution_key); free_PA_PK_AS_REP(&rep); goto out; } ASN1_MALLOC_ENCODE(EncryptionKey, kckdata, kcklen, &kdc_contribution_key, &size, ret); krb5_free_keyblock_contents(context, &kdc_contribution_key); if (ret) { krb5_set_error_message(context, ret, "encoding of PKINIT-KX Key failed %d", ret); krb5_crypto_destroy(context, reply_crypto); free_PA_PK_AS_REP(&rep); goto out; } if (kcklen != size) krb5_abortx(context, "Internal ASN.1 encoder error"); ret = krb5_encrypt_EncryptedData(context, reply_crypto, KRB5_KU_PA_PKINIT_KX, kckdata, kcklen, 0, &kx); krb5_crypto_destroy(context, reply_crypto); free(kckdata); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } ASN1_MALLOC_ENCODE(EncryptedData, kxdata, kxlen, &kx, &size, ret); free_EncryptedData(&kx); if (ret) { krb5_set_error_message(context, ret, "encoding of PKINIT-KX failed %d", ret); free_PA_PK_AS_REP(&rep); goto out; } if (kxlen != size) krb5_abortx(context, "Internal ASN.1 encoder error"); /* Add PA-PKINIT-KX */ ret = krb5_padata_add(context, md, KRB5_PADATA_PKINIT_KX, kxdata, kxlen); if (ret) { krb5_set_error_message(context, ret, "Failed adding PKINIT-KX %d", ret); free(buf); goto out; } } } #define use_btmm_with_enckey 0 if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) { PA_PK_AS_REP_BTMM btmm; heim_any any; any.data = rep.u.encKeyPack.data; any.length = rep.u.encKeyPack.length; btmm.dhSignedData = NULL; btmm.encKeyPack = &any; ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret); } else { ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret); } free_PA_PK_AS_REP(&rep); if (ret) { krb5_set_error_message(context, ret, "encode PA-PK-AS-REP failed %d", ret); goto out; } if (len != size) krb5_abortx(context, "Internal ASN.1 encoder error"); kdc_log(context, config, 0, "PK-INIT using %s %s", type, other); } else if (cp->type == PKINIT_WIN2K) { PA_PK_AS_REP_Win2k rep; ContentInfo info; if (cp->keyex != USE_RSA) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "Windows PK-INIT doesn't support DH"); goto out; } memset(&rep, 0, sizeof(rep)); pa_type = KRB5_PADATA_PK_AS_REP_19; rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack; ret = krb5_generate_random_keyblock(context, enctype, &cp->reply_key); if (ret) { free_PA_PK_AS_REP_Win2k(&rep); goto out; } ret = pk_mk_pa_reply_enckey(context, config, cp, req, req_buffer, &cp->reply_key, &info, &kdc_cert); if (ret) { free_PA_PK_AS_REP_Win2k(&rep); goto out; } ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data, rep.u.encKeyPack.length, &info, &size, ret); free_ContentInfo(&info); if (ret) { krb5_set_error_message(context, ret, "encoding of Key ContentInfo " "failed %d", ret); free_PA_PK_AS_REP_Win2k(&rep); goto out; } if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret); free_PA_PK_AS_REP_Win2k(&rep); if (ret) { krb5_set_error_message(context, ret, "encode PA-PK-AS-REP-Win2k failed %d", ret); goto out; } if (len != size) krb5_abortx(context, "Internal ASN.1 encoder error"); ret = krb5_generate_random_keyblock(context, sessionetype, sessionkey); if (ret) { free(buf); goto out; } } else krb5_abortx(context, "PK-INIT internal error"); ret = krb5_padata_add(context, md, pa_type, buf, len); if (ret) { krb5_set_error_message(context, ret, "Failed adding PA-PK-AS-REP %d", ret); free(buf); goto out; } if (config->pkinit_kdc_ocsp_file) { if (ocsp.expire == 0 && ocsp.next_update > kdc_time) { struct stat sb; int fd; krb5_data_free(&ocsp.data); ocsp.expire = 0; ocsp.next_update = kdc_time + 60 * 5; fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY); if (fd < 0) { kdc_log(context, config, 0, "PK-INIT failed to open ocsp data file %d", errno); goto out_ocsp; } ret = fstat(fd, &sb); if (ret) { ret = errno; close(fd); kdc_log(context, config, 0, "PK-INIT failed to stat ocsp data %d", ret); goto out_ocsp; } ret = krb5_data_alloc(&ocsp.data, sb.st_size); if (ret) { close(fd); kdc_log(context, config, 0, "PK-INIT failed to stat ocsp data %d", ret); goto out_ocsp; } ocsp.data.length = sb.st_size; ret = read(fd, ocsp.data.data, sb.st_size); close(fd); if (ret != sb.st_size) { kdc_log(context, config, 0, "PK-INIT failed to read ocsp data %d", errno); goto out_ocsp; } ret = hx509_ocsp_verify(context->hx509ctx, kdc_time, kdc_cert, 0, ocsp.data.data, ocsp.data.length, &ocsp.expire); if (ret) { kdc_log(context, config, 0, "PK-INIT failed to verify ocsp data %d", ret); krb5_data_free(&ocsp.data); ocsp.expire = 0; } else if (ocsp.expire > 180) { ocsp.expire -= 180; /* refetch the ocsp before it expire */ ocsp.next_update = ocsp.expire; } else { ocsp.next_update = kdc_time; } out_ocsp: ret = 0; } if (ocsp.expire != 0 && ocsp.expire > kdc_time) { ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PK_OCSP_RESPONSE, ocsp.data.data, ocsp.data.length); if (ret) { krb5_set_error_message(context, ret, "Failed adding OCSP response %d", ret); goto out; } } } out: if (kdc_cert) hx509_cert_free(kdc_cert); if (ret == 0) ret = krb5_copy_keyblock_contents(context, &cp->reply_key, reply_key); return ret; }
int hx509_cms_verify_signed(hx509_context context, hx509_verify_ctx ctx, unsigned int flags, const void *data, size_t length, const heim_octet_string *signedContent, hx509_certs pool, heim_oid *contentType, heim_octet_string *content, hx509_certs *signer_certs) { SignerInfo *signer_info; hx509_cert cert = NULL; hx509_certs certs = NULL; SignedData sd; size_t size; int ret, found_valid_sig; size_t i; *signer_certs = NULL; content->data = NULL; content->length = 0; contentType->length = 0; contentType->components = NULL; memset(&sd, 0, sizeof(sd)); ret = decode_SignedData(data, length, &sd, &size); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode SignedData"); goto out; } if (sd.encapContentInfo.eContent == NULL && signedContent == NULL) { ret = HX509_CMS_NO_DATA_AVAILABLE; hx509_set_error_string(context, 0, ret, "No content data in SignedData"); goto out; } if (sd.encapContentInfo.eContent && signedContent) { ret = HX509_CMS_NO_DATA_AVAILABLE; hx509_set_error_string(context, 0, ret, "Both external and internal SignedData"); goto out; } if (sd.encapContentInfo.eContent) ret = der_copy_octet_string(sd.encapContentInfo.eContent, content); else ret = der_copy_octet_string(signedContent, content); if (ret) { hx509_set_error_string(context, 0, ret, "malloc: out of memory"); goto out; } ret = hx509_certs_init(context, "MEMORY:cms-cert-buffer", 0, NULL, &certs); if (ret) goto out; ret = hx509_certs_init(context, "MEMORY:cms-signer-certs", 0, NULL, signer_certs); if (ret) goto out; /* XXX Check CMS version */ ret = any_to_certs(context, &sd, certs); if (ret) goto out; if (pool) { ret = hx509_certs_merge(context, certs, pool); if (ret) goto out; } for (found_valid_sig = 0, i = 0; i < sd.signerInfos.len; i++) { heim_octet_string signed_data; const heim_oid *match_oid; heim_oid decode_oid; signer_info = &sd.signerInfos.val[i]; match_oid = NULL; if (signer_info->signature.length == 0) { ret = HX509_CMS_MISSING_SIGNER_DATA; hx509_set_error_string(context, 0, ret, "SignerInfo %d in SignedData " "missing sigature", i); continue; } ret = find_CMSIdentifier(context, &signer_info->sid, certs, _hx509_verify_get_time(ctx), &cert, HX509_QUERY_KU_DIGITALSIGNATURE); if (ret) { /** * If HX509_CMS_VS_NO_KU_CHECK is set, allow more liberal * search for matching certificates by not considering * KeyUsage bits on the certificates. */ if ((flags & HX509_CMS_VS_NO_KU_CHECK) == 0) continue; ret = find_CMSIdentifier(context, &signer_info->sid, certs, _hx509_verify_get_time(ctx), &cert, 0); if (ret) continue; } if (signer_info->signedAttrs) { const Attribute *attr; CMSAttributes sa; heim_octet_string os; sa.val = signer_info->signedAttrs->val; sa.len = signer_info->signedAttrs->len; /* verify that sigature exists */ attr = find_attribute(&sa, &asn1_oid_id_pkcs9_messageDigest); if (attr == NULL) { ret = HX509_CRYPTO_SIGNATURE_MISSING; hx509_set_error_string(context, 0, ret, "SignerInfo have signed attributes " "but messageDigest (signature) " "is missing"); goto next_sigature; } if (attr->value.len != 1) { ret = HX509_CRYPTO_SIGNATURE_MISSING; hx509_set_error_string(context, 0, ret, "SignerInfo have more then one " "messageDigest (signature)"); goto next_sigature; } ret = decode_MessageDigest(attr->value.val[0].data, attr->value.val[0].length, &os, &size); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode " "messageDigest (signature)"); goto next_sigature; } ret = _hx509_verify_signature(context, NULL, &signer_info->digestAlgorithm, content, &os); der_free_octet_string(&os); if (ret) { hx509_set_error_string(context, HX509_ERROR_APPEND, ret, "Failed to verify messageDigest"); goto next_sigature; } /* * Fetch content oid inside signedAttrs or set it to * id-pkcs7-data. */ attr = find_attribute(&sa, &asn1_oid_id_pkcs9_contentType); if (attr == NULL) { match_oid = &asn1_oid_id_pkcs7_data; } else { if (attr->value.len != 1) { ret = HX509_CMS_DATA_OID_MISMATCH; hx509_set_error_string(context, 0, ret, "More then one oid in signedAttrs"); goto next_sigature; } ret = decode_ContentType(attr->value.val[0].data, attr->value.val[0].length, &decode_oid, &size); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode " "oid in signedAttrs"); goto next_sigature; } match_oid = &decode_oid; } ASN1_MALLOC_ENCODE(CMSAttributes, signed_data.data, signed_data.length, &sa, &size, ret); if (ret) { if (match_oid == &decode_oid) der_free_oid(&decode_oid); hx509_clear_error_string(context); goto next_sigature; } if (size != signed_data.length) _hx509_abort("internal ASN.1 encoder error"); } else { signed_data.data = content->data; signed_data.length = content->length; match_oid = &asn1_oid_id_pkcs7_data; } /** * If HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH, allow * encapContentInfo mismatch with the oid in signedAttributes * (or if no signedAttributes where use, pkcs7-data oid). * This is only needed to work with broken CMS implementations * that doesn't follow CMS signedAttributes rules. */ if (der_heim_oid_cmp(match_oid, &sd.encapContentInfo.eContentType) && (flags & HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH) == 0) { ret = HX509_CMS_DATA_OID_MISMATCH; hx509_set_error_string(context, 0, ret, "Oid in message mismatch from the expected"); } if (match_oid == &decode_oid) der_free_oid(&decode_oid); if (ret == 0) { ret = hx509_verify_signature(context, cert, &signer_info->signatureAlgorithm, &signed_data, &signer_info->signature); if (ret) hx509_set_error_string(context, HX509_ERROR_APPEND, ret, "Failed to verify signature in " "CMS SignedData"); } if (signer_info->signedAttrs) free(signed_data.data); if (ret) goto next_sigature; /** * If HX509_CMS_VS_NO_VALIDATE flags is set, do not verify the * signing certificates and leave that up to the caller. */ if ((flags & HX509_CMS_VS_NO_VALIDATE) == 0) { ret = hx509_verify_path(context, ctx, cert, certs); if (ret) goto next_sigature; } ret = hx509_certs_add(context, *signer_certs, cert); if (ret) goto next_sigature; found_valid_sig++; next_sigature: if (cert) hx509_cert_free(cert); cert = NULL; } /** * If HX509_CMS_VS_ALLOW_ZERO_SIGNER is set, allow empty * SignerInfo (no signatures). If SignedData have no signatures, * the function will return 0 with signer_certs set to NULL. Zero * signers is allowed by the standard, but since its only useful * in corner cases, it make into a flag that the caller have to * turn on. */ if (sd.signerInfos.len == 0 && (flags & HX509_CMS_VS_ALLOW_ZERO_SIGNER)) { if (*signer_certs) hx509_certs_free(signer_certs); } else if (found_valid_sig == 0) { if (ret == 0) { ret = HX509_CMS_SIGNER_NOT_FOUND; hx509_set_error_string(context, 0, ret, "No signers where found"); } goto out; } ret = der_copy_oid(&sd.encapContentInfo.eContentType, contentType); if (ret) { hx509_clear_error_string(context); goto out; } out: free_SignedData(&sd); if (certs) hx509_certs_free(&certs); if (ret) { if (content->data) der_free_octet_string(content); if (*signer_certs) hx509_certs_free(signer_certs); der_free_oid(contentType); der_free_octet_string(content); } return ret; }
int hx509_cms_unenvelope(hx509_context context, hx509_certs certs, int flags, const void *data, size_t length, const heim_octet_string *encryptedContent, time_t time_now, heim_oid *contentType, heim_octet_string *content) { heim_octet_string key; EnvelopedData ed; hx509_cert cert; AlgorithmIdentifier *ai; const heim_octet_string *enccontent; heim_octet_string *params, params_data; heim_octet_string ivec; size_t size; int ret, matched = 0, findflags = 0; size_t i; memset(&key, 0, sizeof(key)); memset(&ed, 0, sizeof(ed)); memset(&ivec, 0, sizeof(ivec)); memset(content, 0, sizeof(*content)); memset(contentType, 0, sizeof(*contentType)); if ((flags & HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT) == 0) findflags |= HX509_QUERY_KU_ENCIPHERMENT; ret = decode_EnvelopedData(data, length, &ed, &size); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode EnvelopedData"); return ret; } if (ed.recipientInfos.len == 0) { ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE; hx509_set_error_string(context, 0, ret, "No recipient info in enveloped data"); goto out; } enccontent = ed.encryptedContentInfo.encryptedContent; if (enccontent == NULL) { if (encryptedContent == NULL) { ret = HX509_CMS_NO_DATA_AVAILABLE; hx509_set_error_string(context, 0, ret, "Content missing from encrypted data"); goto out; } enccontent = encryptedContent; } else if (encryptedContent != NULL) { ret = HX509_CMS_NO_DATA_AVAILABLE; hx509_set_error_string(context, 0, ret, "Both internal and external encrypted data"); goto out; } cert = NULL; for (i = 0; i < ed.recipientInfos.len; i++) { KeyTransRecipientInfo *ri; char *str; int ret2; ri = &ed.recipientInfos.val[i]; ret = find_CMSIdentifier(context, &ri->rid, certs, time_now, &cert, HX509_QUERY_PRIVATE_KEY|findflags); if (ret) continue; matched = 1; /* found a matching certificate, let decrypt */ ret = _hx509_cert_private_decrypt(context, &ri->encryptedKey, &ri->keyEncryptionAlgorithm.algorithm, cert, &key); hx509_cert_free(cert); if (ret == 0) break; /* succuessfully decrypted cert */ cert = NULL; ret2 = unparse_CMSIdentifier(context, &ri->rid, &str); if (ret2 == 0) { hx509_set_error_string(context, HX509_ERROR_APPEND, ret, "Failed to decrypt with %s", str); free(str); } } if (!matched) { ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE; hx509_set_error_string(context, 0, ret, "No private key matched any certificate"); goto out; } if (cert == NULL) { ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE; hx509_set_error_string(context, HX509_ERROR_APPEND, ret, "No private key decrypted the transfer key"); goto out; } ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy EnvelopedData content oid"); goto out; } ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm; if (ai->parameters) { params_data.data = ai->parameters->data; params_data.length = ai->parameters->length; params = ¶ms_data; } else params = NULL; { hx509_crypto crypto; ret = hx509_crypto_init(context, NULL, &ai->algorithm, &crypto); if (ret) goto out; if (flags & HX509_CMS_UE_ALLOW_WEAK) hx509_crypto_allow_weak(crypto); if (params) { ret = hx509_crypto_set_params(context, crypto, params, &ivec); if (ret) { hx509_crypto_destroy(crypto); goto out; } } ret = hx509_crypto_set_key_data(crypto, key.data, key.length); if (ret) { hx509_crypto_destroy(crypto); hx509_set_error_string(context, 0, ret, "Failed to set key for decryption " "of EnvelopedData"); goto out; } ret = hx509_crypto_decrypt(crypto, enccontent->data, enccontent->length, ivec.length ? &ivec : NULL, content); hx509_crypto_destroy(crypto); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decrypt EnvelopedData"); goto out; } } out: free_EnvelopedData(&ed); der_free_octet_string(&key); if (ivec.length) der_free_octet_string(&ivec); if (ret) { der_free_oid(contentType); der_free_octet_string(content); } return ret; }