int i2d_DHparams(DH *dh, unsigned char **pp) { DHParameter data; size_t size = 0; int ret; memset(&data, 0, sizeof(data)); if (bn2heim_int(dh->p, &data.prime) || bn2heim_int(dh->g, &data.base)) { free_DHParameter(&data); return -1; } if (pp == NULL) { size = length_DHParameter(&data); free_DHParameter(&data); } else { void *p; size_t len; ASN1_MALLOC_ENCODE(DHParameter, p, len, &data, &size, ret); free_DHParameter(&data); if (ret) return -1; if (len != size) { abort(); } memcpy((char *)*pp, p, size); free(p); *pp += size; } return (int)size; }
static krb5_error_code encode_uvinfo(krb5_context context, krb5_const_principal p, krb5_data *data) { KRB5PrincipalName pn; krb5_error_code ret; size_t size = 0; pn.principalName = p->name; pn.realm = p->realm; ASN1_MALLOC_ENCODE(KRB5PrincipalName, data->data, data->length, &pn, &size, ret); if (ret) { krb5_data_zero(data); krb5_set_error_message(context, ret, N_("Failed to encode KRB5PrincipalName", "")); return ret; } if (data->length != size) krb5_abortx(context, "asn1 compiler internal error"); return 0; }
int hx509_cms_wrap_ContentInfo(const heim_oid *oid, const heim_octet_string *buf, heim_octet_string *res) { ContentInfo ci; size_t size; int ret; memset(res, 0, sizeof(*res)); memset(&ci, 0, sizeof(ci)); ret = der_copy_oid(oid, &ci.contentType); if (ret) return ret; if (buf) { ALLOC(ci.content, 1); if (ci.content == NULL) { free_ContentInfo(&ci); return ENOMEM; } ci.content->data = malloc(buf->length); if (ci.content->data == NULL) { free_ContentInfo(&ci); return ENOMEM; } memcpy(ci.content->data, buf->data, buf->length); ci.content->length = buf->length; } ASN1_MALLOC_ENCODE(ContentInfo, res->data, res->length, &ci, &size, ret); free_ContentInfo(&ci); if (ret) return ret; if (res->length != size) _hx509_abort("internal ASN.1 encoder error"); return 0; }
static void add_pkinit_acl(krb5_context contextp, kadm5_principal_ent_rec *princ, struct getarg_strings *strings) { krb5_error_code ret; HDB_extension ext; krb5_data buf; size_t size = 0; int i; memset(&ext, 0, sizeof(ext)); ext.mandatory = FALSE; ext.data.element = choice_HDB_extension_data_pkinit_acl; ext.data.u.aliases.case_insensitive = 0; if (strings->num_strings == 1 && strings->strings[0][0] == '\0') { ext.data.u.pkinit_acl.val = NULL; ext.data.u.pkinit_acl.len = 0; } else { ext.data.u.pkinit_acl.val = calloc(strings->num_strings, sizeof(ext.data.u.pkinit_acl.val[0])); ext.data.u.pkinit_acl.len = strings->num_strings; for (i = 0; i < strings->num_strings; i++) { ext.data.u.pkinit_acl.val[i].subject = estrdup(strings->strings[i]); } } ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length, &ext, &size, ret); free_HDB_extension(&ext); if (ret) abort(); if (buf.length != size) abort(); add_tl(princ, KRB5_TL_EXTENSION, &buf); }
krb5_error_code KRB5_LIB_FUNCTION krb5_build_ap_req (krb5_context context, krb5_enctype enctype, krb5_creds *cred, krb5_flags ap_options, krb5_data authenticator, krb5_data *retdata) { krb5_error_code ret = 0; AP_REQ ap; Ticket t; size_t len; ap.pvno = 5; ap.msg_type = krb_ap_req; memset(&ap.ap_options, 0, sizeof(ap.ap_options)); ap.ap_options.use_session_key = (ap_options & AP_OPTS_USE_SESSION_KEY) > 0; ap.ap_options.mutual_required = (ap_options & AP_OPTS_MUTUAL_REQUIRED) > 0; ap.ticket.tkt_vno = 5; copy_Realm(&cred->server->realm, &ap.ticket.realm); copy_PrincipalName(&cred->server->name, &ap.ticket.sname); decode_Ticket(cred->ticket.data, cred->ticket.length, &t, &len); copy_EncryptedData(&t.enc_part, &ap.ticket.enc_part); free_Ticket(&t); ap.authenticator.etype = enctype; ap.authenticator.kvno = NULL; ap.authenticator.cipher = authenticator; ASN1_MALLOC_ENCODE(AP_REQ, retdata->data, retdata->length, &ap, &len, ret); if(ret == 0 && retdata->length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); free_AP_REQ(&ap); return ret; }
int i2d_RSAPublicKey(RSA *rsa, unsigned char **pp) { RSAPublicKey data; size_t size; int ret; memset(&data, 0, sizeof(data)); if (_hc_BN_to_integer(rsa->n, &data.modulus) || _hc_BN_to_integer(rsa->e, &data.publicExponent)) { free_RSAPublicKey(&data); return -1; } if (pp == NULL) { size = length_RSAPublicKey(&data); free_RSAPublicKey(&data); } else { void *p; size_t len; ASN1_MALLOC_ENCODE(RSAPublicKey, p, len, &data, &size, ret); free_RSAPublicKey(&data); if (ret) return -1; if (len != size) abort(); memcpy(*pp, p, size); free(p); *pp += size; } return size; }
static krb5_error_code get_pa_etype_info(krb5_context context, krb5_kdc_configuration *config, METHOD_DATA *md, Key *ckey) { krb5_error_code ret = 0; ETYPE_INFO pa; unsigned char *buf; size_t len; pa.len = 1; pa.val = calloc(1, sizeof(pa.val[0])); if(pa.val == NULL) return ENOMEM; ret = make_etype_info_entry(context, &pa.val[0], ckey); if (ret) { free_ETYPE_INFO(&pa); return ret; } ASN1_MALLOC_ENCODE(ETYPE_INFO, buf, len, &pa, &len, ret); free_ETYPE_INFO(&pa); if(ret) return ret; ret = realloc_method_data(md); if(ret) { free(buf); return ret; } md->val[md->len - 1].padata_type = KRB5_PADATA_ETYPE_INFO; md->val[md->len - 1].padata_value.length = len; md->val[md->len - 1].padata_value.data = buf; return 0; }
krb5_error_code _kdc_encode_reply(krb5_context context, krb5_kdc_configuration *config, KDC_REP *rep, const EncTicketPart *et, EncKDCRepPart *ek, krb5_enctype etype, int skvno, const EncryptionKey *skey, int ckvno, const EncryptionKey *reply_key, int rk_is_subkey, const char **e_text, krb5_data *reply) { unsigned char *buf; size_t buf_size; size_t len = 0; krb5_error_code ret; krb5_crypto crypto; ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size, et, &len, ret); if(ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Failed to encode ticket: %s", msg); krb5_free_error_message(context, msg); return ret; } if(buf_size != len) { free(buf); kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; return KRB5KRB_ERR_GENERIC; } ret = krb5_crypto_init(context, skey, etype, &crypto); if (ret) { const char *msg; free(buf); msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg); krb5_free_error_message(context, msg); return ret; } ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_TICKET, buf, len, skvno, &rep->ticket.enc_part); free(buf); krb5_crypto_destroy(context, crypto); if(ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Failed to encrypt data: %s", msg); krb5_free_error_message(context, msg); return ret; } if(rep->msg_type == krb_as_rep && !config->encode_as_rep_as_tgs_rep) ASN1_MALLOC_ENCODE(EncASRepPart, buf, buf_size, ek, &len, ret); else ASN1_MALLOC_ENCODE(EncTGSRepPart, buf, buf_size, ek, &len, ret); if(ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Failed to encode KDC-REP: %s", msg); krb5_free_error_message(context, msg); return ret; } if(buf_size != len) { free(buf); kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; return KRB5KRB_ERR_GENERIC; } ret = krb5_crypto_init(context, reply_key, 0, &crypto); if (ret) { const char *msg = krb5_get_error_message(context, ret); free(buf); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg); krb5_free_error_message(context, msg); return ret; } if(rep->msg_type == krb_as_rep) { krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_AS_REP_ENC_PART, buf, len, ckvno, &rep->enc_part); free(buf); ASN1_MALLOC_ENCODE(AS_REP, buf, buf_size, rep, &len, ret); } else { krb5_encrypt_EncryptedData(context, crypto, rk_is_subkey ? KRB5_KU_TGS_REP_ENC_PART_SUB_KEY : KRB5_KU_TGS_REP_ENC_PART_SESSION, buf, len, ckvno, &rep->enc_part); free(buf); ASN1_MALLOC_ENCODE(TGS_REP, buf, buf_size, rep, &len, ret); } krb5_crypto_destroy(context, crypto); if(ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Failed to encode KDC-REP: %s", msg); krb5_free_error_message(context, msg); return ret; } if(buf_size != len) { free(buf); kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; return KRB5KRB_ERR_GENERIC; } reply->data = buf; reply->length = buf_size; return 0; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_cred(krb5_context context, krb5_auth_context auth_context, krb5_data *in_data, krb5_creds ***ret_creds, krb5_replay_data *outdata) { krb5_error_code ret; size_t len; KRB_CRED cred; EncKrbCredPart enc_krb_cred_part; krb5_data enc_krb_cred_part_data; krb5_crypto crypto; int i; memset(&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part)); if ((auth_context->flags & (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && outdata == NULL) return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */ *ret_creds = NULL; ret = decode_KRB_CRED(in_data->data, in_data->length, &cred, &len); if(ret) { krb5_clear_error_message(context); return ret; } if (cred.pvno != 5) { ret = KRB5KRB_AP_ERR_BADVERSION; krb5_clear_error_message (context); goto out; } if (cred.msg_type != krb_cred) { ret = KRB5KRB_AP_ERR_MSG_TYPE; krb5_clear_error_message (context); goto out; } if (cred.enc_part.etype == ETYPE_NULL) { /* DK: MIT GSS-API Compatibility */ enc_krb_cred_part_data.length = cred.enc_part.cipher.length; enc_krb_cred_part_data.data = cred.enc_part.cipher.data; } else { /* Try both subkey and session key. * * RFC4120 claims we should use the session key, but Heimdal * before 0.8 used the remote subkey if it was send in the * auth_context. */ if (auth_context->remote_subkey) { ret = krb5_crypto_init(context, auth_context->remote_subkey, 0, &crypto); if (ret) goto out; ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_KRB_CRED, &cred.enc_part, &enc_krb_cred_part_data); krb5_crypto_destroy(context, crypto); } /* * If there was not subkey, or we failed using subkey, * retry using the session key */ if (auth_context->remote_subkey == NULL || ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) { ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto); if (ret) goto out; ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_KRB_CRED, &cred.enc_part, &enc_krb_cred_part_data); krb5_crypto_destroy(context, crypto); } if (ret) goto out; } ret = decode_EncKrbCredPart(enc_krb_cred_part_data.data, enc_krb_cred_part_data.length, &enc_krb_cred_part, &len); if (enc_krb_cred_part_data.data != cred.enc_part.cipher.data) krb5_data_free(&enc_krb_cred_part_data); if (ret) { krb5_set_error_message(context, ret, N_("Failed to decode " "encrypte credential part", "")); goto out; } /* check sender address */ if (enc_krb_cred_part.s_address && auth_context->remote_address && auth_context->remote_port) { krb5_address *a; ret = krb5_make_addrport (context, &a, auth_context->remote_address, auth_context->remote_port); if (ret) goto out; ret = compare_addrs(context, a, enc_krb_cred_part.s_address, N_("sender address is wrong " "in received creds", "")); krb5_free_address(context, a); free(a); if(ret) goto out; } /* check receiver address */ if (enc_krb_cred_part.r_address && auth_context->local_address) { if(auth_context->local_port && enc_krb_cred_part.r_address->addr_type == KRB5_ADDRESS_ADDRPORT) { krb5_address *a; ret = krb5_make_addrport (context, &a, auth_context->local_address, auth_context->local_port); if (ret) goto out; ret = compare_addrs(context, a, enc_krb_cred_part.r_address, N_("receiver address is wrong " "in received creds", "")); krb5_free_address(context, a); free(a); if(ret) goto out; } else { ret = compare_addrs(context, auth_context->local_address, enc_krb_cred_part.r_address, N_("receiver address is wrong " "in received creds", "")); if(ret) goto out; } } /* check timestamp */ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_timestamp sec; krb5_timeofday (context, &sec); if (enc_krb_cred_part.timestamp == NULL || enc_krb_cred_part.usec == NULL || abs(*enc_krb_cred_part.timestamp - sec) > context->max_skew) { krb5_clear_error_message (context); ret = KRB5KRB_AP_ERR_SKEW; goto out; } } if ((auth_context->flags & (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) { /* if these fields are not present in the cred-part, silently return zero */ memset(outdata, 0, sizeof(*outdata)); if(enc_krb_cred_part.timestamp) outdata->timestamp = *enc_krb_cred_part.timestamp; if(enc_krb_cred_part.usec) outdata->usec = *enc_krb_cred_part.usec; if(enc_krb_cred_part.nonce) outdata->seq = *enc_krb_cred_part.nonce; } /* Convert to NULL terminated list of creds */ *ret_creds = calloc(enc_krb_cred_part.ticket_info.len + 1, sizeof(**ret_creds)); if (*ret_creds == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } for (i = 0; i < enc_krb_cred_part.ticket_info.len; ++i) { KrbCredInfo *kci = &enc_krb_cred_part.ticket_info.val[i]; krb5_creds *creds; creds = calloc(1, sizeof(*creds)); if(creds == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length, &cred.tickets.val[i], &len, ret); if (ret) { free(creds); goto out; } if(creds->ticket.length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); copy_EncryptionKey (&kci->key, &creds->session); if (kci->prealm && kci->pname) _krb5_principalname2krb5_principal (context, &creds->client, *kci->pname, *kci->prealm); if (kci->flags) creds->flags.b = *kci->flags; if (kci->authtime) creds->times.authtime = *kci->authtime; if (kci->starttime) creds->times.starttime = *kci->starttime; if (kci->endtime) creds->times.endtime = *kci->endtime; if (kci->renew_till) creds->times.renew_till = *kci->renew_till; if (kci->srealm && kci->sname) _krb5_principalname2krb5_principal (context, &creds->server, *kci->sname, *kci->srealm); if (kci->caddr) krb5_copy_addresses (context, kci->caddr, &creds->addresses); (*ret_creds)[i] = creds; } (*ret_creds)[i] = NULL; free_KRB_CRED (&cred); free_EncKrbCredPart(&enc_krb_cred_part); return 0; out: free_EncKrbCredPart(&enc_krb_cred_part); free_KRB_CRED (&cred); if(*ret_creds) { for(i = 0; (*ret_creds)[i]; i++) krb5_free_creds(context, (*ret_creds)[i]); free(*ret_creds); *ret_creds = NULL; } return ret; }
static int ca_sign(hx509_context context, hx509_ca_tbs tbs, hx509_private_key signer, const AuthorityKeyIdentifier *ai, const Name *issuername, hx509_cert *certificate) { heim_octet_string data; Certificate c; TBSCertificate *tbsc; size_t size; int ret; const AlgorithmIdentifier *sigalg; time_t notBefore; time_t notAfter; unsigned key_usage; sigalg = _hx509_crypto_default_sig_alg; memset(&c, 0, sizeof(c)); /* * Default values are: Valid since 24h ago, valid one year into * the future, KeyUsage digitalSignature and keyEncipherment set, * and keyCertSign for CA certificates. */ notBefore = tbs->notBefore; if (notBefore == 0) notBefore = time(NULL) - 3600 * 24; notAfter = tbs->notAfter; if (notAfter == 0) notAfter = time(NULL) + 3600 * 24 * 365; key_usage = tbs->key_usage; if (key_usage == 0) { KeyUsage ku; memset(&ku, 0, sizeof(ku)); ku.digitalSignature = 1; ku.keyEncipherment = 1; key_usage = KeyUsage2int(ku); } if (tbs->flags.ca) { KeyUsage ku; memset(&ku, 0, sizeof(ku)); ku.keyCertSign = 1; ku.cRLSign = 1; key_usage |= KeyUsage2int(ku); } /* * */ tbsc = &c.tbsCertificate; if (tbs->flags.key == 0) { ret = EINVAL; hx509_set_error_string(context, 0, ret, "No public key set"); return ret; } /* * Don't put restrictions on proxy certificate's subject name, it * will be generated below. */ if (!tbs->flags.proxy) { if (tbs->subject == NULL) { hx509_set_error_string(context, 0, EINVAL, "No subject name set"); return EINVAL; } if (hx509_name_is_null_p(tbs->subject) && tbs->san.len == 0) { hx509_set_error_string(context, 0, EINVAL, "NULL subject and no SubjectAltNames"); return EINVAL; } } if (tbs->flags.ca && tbs->flags.proxy) { hx509_set_error_string(context, 0, EINVAL, "Can't be proxy and CA " "at the same time"); return EINVAL; } if (tbs->flags.proxy) { if (tbs->san.len > 0) { hx509_set_error_string(context, 0, EINVAL, "Proxy certificate is not allowed " "to have SubjectAltNames"); return EINVAL; } } /* version [0] Version OPTIONAL, -- EXPLICIT nnn DEFAULT 1, */ tbsc->version = calloc(1, sizeof(*tbsc->version)); if (tbsc->version == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } *tbsc->version = rfc3280_version_3; /* serialNumber CertificateSerialNumber, */ if (tbs->flags.serial) { ret = der_copy_heim_integer(&tbs->serial, &tbsc->serialNumber); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } } else { tbsc->serialNumber.length = 20; tbsc->serialNumber.data = malloc(tbsc->serialNumber.length); if (tbsc->serialNumber.data == NULL){ ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } /* XXX diffrent */ RAND_bytes(tbsc->serialNumber.data, tbsc->serialNumber.length); ((unsigned char *)tbsc->serialNumber.data)[0] &= 0x7f; } /* signature AlgorithmIdentifier, */ ret = copy_AlgorithmIdentifier(sigalg, &tbsc->signature); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy sigature alg"); goto out; } /* issuer Name, */ if (issuername) ret = copy_Name(issuername, &tbsc->issuer); else ret = hx509_name_to_Name(tbs->subject, &tbsc->issuer); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy issuer name"); goto out; } /* validity Validity, */ tbsc->validity.notBefore.element = choice_Time_generalTime; tbsc->validity.notBefore.u.generalTime = notBefore; tbsc->validity.notAfter.element = choice_Time_generalTime; tbsc->validity.notAfter.u.generalTime = notAfter; /* subject Name, */ if (tbs->flags.proxy) { ret = build_proxy_prefix(context, &tbsc->issuer, &tbsc->subject); if (ret) goto out; } else { ret = hx509_name_to_Name(tbs->subject, &tbsc->subject); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy subject name"); goto out; } } /* subjectPublicKeyInfo SubjectPublicKeyInfo, */ ret = copy_SubjectPublicKeyInfo(&tbs->spki, &tbsc->subjectPublicKeyInfo); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy spki"); goto out; } /* issuerUniqueID [1] IMPLICIT BIT STRING OPTIONAL */ if (tbs->issuerUniqueID.length) { tbsc->issuerUniqueID = calloc(1, sizeof(*tbsc->issuerUniqueID)); if (tbsc->issuerUniqueID == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ret = der_copy_bit_string(&tbs->issuerUniqueID, tbsc->issuerUniqueID); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } } /* subjectUniqueID [2] IMPLICIT BIT STRING OPTIONAL */ if (tbs->subjectUniqueID.length) { tbsc->subjectUniqueID = calloc(1, sizeof(*tbsc->subjectUniqueID)); if (tbsc->subjectUniqueID == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ret = der_copy_bit_string(&tbs->subjectUniqueID, tbsc->subjectUniqueID); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } } /* extensions [3] EXPLICIT Extensions OPTIONAL */ tbsc->extensions = calloc(1, sizeof(*tbsc->extensions)); if (tbsc->extensions == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } /* Add the text BMP string Domaincontroller to the cert */ if (tbs->flags.domaincontroller) { data.data = rk_UNCONST("\x1e\x20\x00\x44\x00\x6f\x00\x6d" "\x00\x61\x00\x69\x00\x6e\x00\x43" "\x00\x6f\x00\x6e\x00\x74\x00\x72" "\x00\x6f\x00\x6c\x00\x6c\x00\x65" "\x00\x72"); data.length = 34; ret = add_extension(context, tbsc, 0, &asn1_oid_id_ms_cert_enroll_domaincontroller, &data); if (ret) goto out; } /* add KeyUsage */ { KeyUsage ku; ku = int2KeyUsage(key_usage); ASN1_MALLOC_ENCODE(KeyUsage, data.data, data.length, &ku, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 1, &asn1_oid_id_x509_ce_keyUsage, &data); free(data.data); if (ret) goto out; } /* add ExtendedKeyUsage */ if (tbs->eku.len > 0) { ASN1_MALLOC_ENCODE(ExtKeyUsage, data.data, data.length, &tbs->eku, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_extKeyUsage, &data); free(data.data); if (ret) goto out; } /* add Subject Alternative Name */ if (tbs->san.len > 0) { ASN1_MALLOC_ENCODE(GeneralNames, data.data, data.length, &tbs->san, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_subjectAltName, &data); free(data.data); if (ret) goto out; } /* Add Authority Key Identifier */ if (ai) { ASN1_MALLOC_ENCODE(AuthorityKeyIdentifier, data.data, data.length, ai, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_authorityKeyIdentifier, &data); free(data.data); if (ret) goto out; } /* Add Subject Key Identifier */ { SubjectKeyIdentifier si; unsigned char hash[SHA_DIGEST_LENGTH]; { EVP_MD_CTX *ctx; ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); EVP_DigestUpdate(ctx, tbs->spki.subjectPublicKey.data, tbs->spki.subjectPublicKey.length / 8); EVP_DigestFinal_ex(ctx, hash, NULL); EVP_MD_CTX_destroy(ctx); } si.data = hash; si.length = sizeof(hash); ASN1_MALLOC_ENCODE(SubjectKeyIdentifier, data.data, data.length, &si, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_subjectKeyIdentifier, &data); free(data.data); if (ret) goto out; } /* Add BasicConstraints */ { BasicConstraints bc; int aCA = 1; unsigned int path; memset(&bc, 0, sizeof(bc)); if (tbs->flags.ca) { bc.cA = &aCA; if (tbs->pathLenConstraint >= 0) { path = tbs->pathLenConstraint; bc.pathLenConstraint = &path; } } ASN1_MALLOC_ENCODE(BasicConstraints, data.data, data.length, &bc, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); /* Critical if this is a CA */ ret = add_extension(context, tbsc, tbs->flags.ca, &asn1_oid_id_x509_ce_basicConstraints, &data); free(data.data); if (ret) goto out; } /* add Proxy */ if (tbs->flags.proxy) { ProxyCertInfo info; memset(&info, 0, sizeof(info)); if (tbs->pathLenConstraint >= 0) { info.pCPathLenConstraint = malloc(sizeof(*info.pCPathLenConstraint)); if (info.pCPathLenConstraint == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } *info.pCPathLenConstraint = tbs->pathLenConstraint; } ret = der_copy_oid(&asn1_oid_id_pkix_ppl_inheritAll, &info.proxyPolicy.policyLanguage); if (ret) { free_ProxyCertInfo(&info); hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ASN1_MALLOC_ENCODE(ProxyCertInfo, data.data, data.length, &info, &size, ret); free_ProxyCertInfo(&info); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_pkix_pe_proxyCertInfo, &data); free(data.data); if (ret) goto out; } if (tbs->crldp.len) { ASN1_MALLOC_ENCODE(CRLDistributionPoints, data.data, data.length, &tbs->crldp, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, FALSE, &asn1_oid_id_x509_ce_cRLDistributionPoints, &data); free(data.data); if (ret) goto out; } ASN1_MALLOC_ENCODE(TBSCertificate, data.data, data.length,tbsc, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "malloc out of memory"); goto out; } if (data.length != size) _hx509_abort("internal ASN.1 encoder error"); ret = _hx509_create_signature_bitstring(context, signer, sigalg, &data, &c.signatureAlgorithm, &c.signatureValue); free(data.data); if (ret) goto out; ret = hx509_cert_init(context, &c, certificate); if (ret) goto out; free_Certificate(&c); return 0; out: free_Certificate(&c); return ret; }
int hx509_ca_tbs_add_san_pkinit(hx509_context context, hx509_ca_tbs tbs, const char *principal) { heim_octet_string os; KRB5PrincipalName p; size_t size; int ret; char *s = NULL; memset(&p, 0, sizeof(p)); /* parse principal */ { const char *str; char *q; int n; /* count number of component */ n = 1; for(str = principal; *str != '\0' && *str != '@'; str++){ if(*str=='\\'){ if(str[1] == '\0' || str[1] == '@') { ret = HX509_PARSING_NAME_FAILED; hx509_set_error_string(context, 0, ret, "trailing \\ in principal name"); goto out; } str++; } else if(*str == '/') n++; } p.principalName.name_string.val = calloc(n, sizeof(*p.principalName.name_string.val)); if (p.principalName.name_string.val == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "malloc: out of memory"); goto out; } p.principalName.name_string.len = n; p.principalName.name_type = KRB5_NT_PRINCIPAL; q = s = strdup(principal); if (q == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "malloc: out of memory"); goto out; } p.realm = strrchr(q, '@'); if (p.realm == NULL) { ret = HX509_PARSING_NAME_FAILED; hx509_set_error_string(context, 0, ret, "Missing @ in principal"); goto out; }; *p.realm++ = '\0'; n = 0; while (q) { p.principalName.name_string.val[n++] = q; q = strchr(q, '/'); if (q) *q++ = '\0'; } } ASN1_MALLOC_ENCODE(KRB5PrincipalName, os.data, os.length, &p, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != os.length) _hx509_abort("internal ASN.1 encoder error"); ret = hx509_ca_tbs_add_san_otherName(context, tbs, &asn1_oid_id_pkinit_san, &os); free(os.data); out: if (p.principalName.name_string.val) free (p.principalName.name_string.val); if (s) free(s); return ret; }
int hx509_cms_create_signed(hx509_context context, int flags, const heim_oid *eContentType, const void *data, size_t length, const AlgorithmIdentifier *digest_alg, hx509_certs certs, hx509_peer_info peer, hx509_certs anchors, hx509_certs pool, heim_octet_string *signed_data) { unsigned int i, j; hx509_name name; int ret; size_t size; struct sigctx sigctx; memset(&sigctx, 0, sizeof(sigctx)); memset(&name, 0, sizeof(name)); if (eContentType == NULL) eContentType = &asn1_oid_id_pkcs7_data; sigctx.digest_alg = digest_alg; sigctx.content.data = rk_UNCONST(data); sigctx.content.length = length; sigctx.eContentType = eContentType; sigctx.peer = peer; /** * Use HX509_CMS_SIGNATURE_ID_NAME to preferred use of issuer name * and serial number if possible. Otherwise subject key identifier * will preferred. */ if (flags & HX509_CMS_SIGNATURE_ID_NAME) sigctx.cmsidflag = CMS_ID_NAME; else sigctx.cmsidflag = CMS_ID_SKI; /** * Use HX509_CMS_SIGNATURE_LEAF_ONLY to only request leaf * certificates to be added to the SignedData. */ sigctx.leafonly = (flags & HX509_CMS_SIGNATURE_LEAF_ONLY) ? 1 : 0; /** * Use HX509_CMS_NO_CERTS to make the SignedData contain no * certificates, overrides HX509_CMS_SIGNATURE_LEAF_ONLY. */ if ((flags & HX509_CMS_SIGNATURE_NO_CERTS) == 0) { ret = hx509_certs_init(context, "MEMORY:certs", 0, NULL, &sigctx.certs); if (ret) return ret; } sigctx.anchors = anchors; sigctx.pool = pool; sigctx.sd.version = CMSVersion_v3; der_copy_oid(eContentType, &sigctx.sd.encapContentInfo.eContentType); /** * Use HX509_CMS_SIGNATURE_DETACHED to create detached signatures. */ if ((flags & HX509_CMS_SIGNATURE_DETACHED) == 0) { ALLOC(sigctx.sd.encapContentInfo.eContent, 1); if (sigctx.sd.encapContentInfo.eContent == NULL) { hx509_clear_error_string(context); ret = ENOMEM; goto out; } sigctx.sd.encapContentInfo.eContent->data = malloc(length); if (sigctx.sd.encapContentInfo.eContent->data == NULL) { hx509_clear_error_string(context); ret = ENOMEM; goto out; } memcpy(sigctx.sd.encapContentInfo.eContent->data, data, length); sigctx.sd.encapContentInfo.eContent->length = length; } /** * Use HX509_CMS_SIGNATURE_NO_SIGNER to create no sigInfo (no * signatures). */ if ((flags & HX509_CMS_SIGNATURE_NO_SIGNER) == 0) { ret = hx509_certs_iter_f(context, certs, sig_process, &sigctx); if (ret) goto out; } if (sigctx.sd.signerInfos.len) { /* * For each signerInfo, collect all different digest types. */ for (i = 0; i < sigctx.sd.signerInfos.len; i++) { AlgorithmIdentifier *di = &sigctx.sd.signerInfos.val[i].digestAlgorithm; for (j = 0; j < sigctx.sd.digestAlgorithms.len; j++) if (cmp_AlgorithmIdentifier(di, &sigctx.sd.digestAlgorithms.val[j]) == 0) break; if (j == sigctx.sd.digestAlgorithms.len) { ret = add_DigestAlgorithmIdentifiers(&sigctx.sd.digestAlgorithms, di); if (ret) { hx509_clear_error_string(context); goto out; } } } } /* * Add certs we think are needed, build as part of sig_process */ if (sigctx.certs) { ALLOC(sigctx.sd.certificates, 1); if (sigctx.sd.certificates == NULL) { hx509_clear_error_string(context); ret = ENOMEM; goto out; } ret = hx509_certs_iter_f(context, sigctx.certs, cert_process, &sigctx); if (ret) goto out; } ASN1_MALLOC_ENCODE(SignedData, signed_data->data, signed_data->length, &sigctx.sd, &size, ret); if (ret) { hx509_clear_error_string(context); goto out; } if (signed_data->length != size) _hx509_abort("internal ASN.1 encoder error"); out: hx509_certs_free(&sigctx.certs); free_SignedData(&sigctx.sd); return ret; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_mk_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *outbuf) { krb5_error_code ret; AP_REP ap; EncAPRepPart body; u_char *buf = NULL; size_t buf_size; size_t len = 0; krb5_crypto crypto; ap.pvno = 5; ap.msg_type = krb_ap_rep; memset (&body, 0, sizeof(body)); body.ctime = auth_context->authenticator->ctime; body.cusec = auth_context->authenticator->cusec; if (auth_context->flags & KRB5_AUTH_CONTEXT_USE_SUBKEY) { if (auth_context->local_subkey == NULL) { ret = krb5_auth_con_generatelocalsubkey(context, auth_context, auth_context->keyblock); if(ret) { free_EncAPRepPart(&body); return ret; } } ret = krb5_copy_keyblock(context, auth_context->local_subkey, &body.subkey); if (ret) { free_EncAPRepPart(&body); return krb5_enomem(context); } } else body.subkey = NULL; if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { if(auth_context->local_seqnumber == 0) krb5_generate_seq_number (context, auth_context->keyblock, &auth_context->local_seqnumber); ALLOC(body.seq_number, 1); if (body.seq_number == NULL) { free_EncAPRepPart(&body); return krb5_enomem(context); } *(body.seq_number) = auth_context->local_seqnumber; } else body.seq_number = NULL; ap.enc_part.etype = auth_context->keyblock->keytype; ap.enc_part.kvno = NULL; ASN1_MALLOC_ENCODE(EncAPRepPart, buf, buf_size, &body, &len, ret); free_EncAPRepPart (&body); if(ret) return ret; if (buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_crypto_init(context, auth_context->keyblock, 0 /* ap.enc_part.etype */, &crypto); if (ret) { free (buf); return ret; } ret = krb5_encrypt (context, crypto, KRB5_KU_AP_REQ_ENC_PART, buf + buf_size - len, len, &ap.enc_part.cipher); krb5_crypto_destroy(context, crypto); free(buf); if (ret) return ret; ASN1_MALLOC_ENCODE(AP_REP, outbuf->data, outbuf->length, &ap, &len, ret); if (ret == 0 && outbuf->length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); free_AP_REP (&ap); return ret; }
static OM_uint32 spnego_reply (OM_uint32 * minor_status, const gssspnego_cred cred, gss_ctx_id_t * context_handle, const gss_name_t target_name, const gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec ) { OM_uint32 ret, minor; NegotiationToken resp; gss_OID_desc mech; int require_mic; size_t buf_len; gss_buffer_desc mic_buf, mech_buf; gss_buffer_desc mech_output_token; gssspnego_ctx ctx; *minor_status = 0; ctx = (gssspnego_ctx)*context_handle; output_token->length = 0; output_token->value = NULL; mech_output_token.length = 0; mech_output_token.value = NULL; mech_buf.value = NULL; mech_buf.length = 0; ret = decode_NegotiationToken(input_token->value, input_token->length, &resp, NULL); if (ret) return ret; if (resp.element != choice_NegotiationToken_negTokenResp) { free_NegotiationToken(&resp); *minor_status = 0; return GSS_S_BAD_MECH; } if (resp.u.negTokenResp.negResult == NULL || *(resp.u.negTokenResp.negResult) == reject /* || resp.u.negTokenResp.supportedMech == NULL */ ) { free_NegotiationToken(&resp); return GSS_S_BAD_MECH; } /* * Pick up the mechanism that the acceptor selected, only allow it * to be sent in packet. */ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); if (resp.u.negTokenResp.supportedMech) { if (ctx->oidlen) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } ret = der_put_oid(ctx->oidbuf + sizeof(ctx->oidbuf) - 1, sizeof(ctx->oidbuf), resp.u.negTokenResp.supportedMech, &ctx->oidlen); /* Avoid recursively embedded SPNEGO */ if (ret || (ctx->oidlen == GSS_SPNEGO_MECHANISM->length && memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, GSS_SPNEGO_MECHANISM->elements, ctx->oidlen) == 0)) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } /* check if the acceptor took our optimistic token */ if (ctx->oidlen != ctx->preferred_mech_type->length || memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, ctx->preferred_mech_type->elements, ctx->oidlen) != 0) { gss_delete_sec_context(&minor, &ctx->negotiated_ctx_id, GSS_C_NO_BUFFER); ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT; } } else if (ctx->oidlen == 0) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } /* if a token (of non zero length), or no context, pass to underlaying mech */ if ((resp.u.negTokenResp.responseToken != NULL && resp.u.negTokenResp.responseToken->length) || ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { gss_buffer_desc mech_input_token; if (resp.u.negTokenResp.responseToken) { mech_input_token.length = resp.u.negTokenResp.responseToken->length; mech_input_token.value = resp.u.negTokenResp.responseToken->data; } else { mech_input_token.length = 0; mech_input_token.value = NULL; } mech.length = ctx->oidlen; mech.elements = ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen; /* Fall through as if the negotiated mechanism was requested explicitly */ ret = gss_init_sec_context(&minor, (cred != NULL) ? cred->negotiated_cred_id : GSS_C_NO_CREDENTIAL, &ctx->negotiated_ctx_id, ctx->target_name, &mech, req_flags, time_req, input_chan_bindings, &mech_input_token, &ctx->negotiated_mech_type, &mech_output_token, &ctx->mech_flags, &ctx->mech_time_rec); if (GSS_ERROR(ret)) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_mg_collect_error(&mech, ret, minor); *minor_status = minor; return ret; } if (ret == GSS_S_COMPLETE) { ctx->open = 1; } } else if (*(resp.u.negTokenResp.negResult) == accept_completed) { if (ctx->maybe_open) ctx->open = 1; } if (*(resp.u.negTokenResp.negResult) == request_mic) { ctx->require_mic = 1; } if (ctx->open) { /* * Verify the mechListMIC if one was provided or CFX was * used and a non-preferred mechanism was selected */ if (resp.u.negTokenResp.mechListMIC != NULL) { require_mic = 1; } else { ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); return ret; } } } else { require_mic = 0; } if (require_mic) { ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length, &ctx->initiator_mech_types, &buf_len, ret); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); *minor_status = ret; return GSS_S_FAILURE; } if (mech_buf.length != buf_len) abort(); if (resp.u.negTokenResp.mechListMIC == NULL) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); free_NegotiationToken(&resp); *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } mic_buf.length = resp.u.negTokenResp.mechListMIC->length; mic_buf.value = resp.u.negTokenResp.mechListMIC->data; if (mech_output_token.length == 0) { ret = gss_verify_mic(minor_status, ctx->negotiated_ctx_id, &mech_buf, &mic_buf, NULL); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); gss_release_buffer(&minor, &mech_output_token); free_NegotiationToken(&resp); return GSS_S_DEFECTIVE_TOKEN; } ctx->verified_mic = 1; } } ret = spnego_reply_internal(minor_status, ctx, require_mic ? &mech_buf : NULL, &mech_output_token, output_token); if (mech_buf.value != NULL) free(mech_buf.value); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); if (actual_mech_type) *actual_mech_type = ctx->negotiated_mech_type; if (ret_flags) *ret_flags = ctx->mech_flags; if (time_rec) *time_rec = ctx->mech_time_rec; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; }
kadm5_ret_t kadm5_s_get_principal(void *server_handle, krb5_principal princ, kadm5_principal_ent_t out, uint32_t mask) { kadm5_server_context *context = server_handle; kadm5_ret_t ret; hdb_entry_ex ent; memset(&ent, 0, sizeof(ent)); if (!context->keep_open) { ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0); if(ret) return ret; } ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, HDB_F_DECRYPT|HDB_F_ALL_KVNOS| HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); if (!context->keep_open) context->db->hdb_close(context->context, context->db); if(ret) return _kadm5_error_code(ret); memset(out, 0, sizeof(*out)); if(mask & KADM5_PRINCIPAL) ret = krb5_copy_principal(context->context, ent.entry.principal, &out->principal); if(ret) goto out; if(mask & KADM5_PRINC_EXPIRE_TIME && ent.entry.valid_end) out->princ_expire_time = *ent.entry.valid_end; if(mask & KADM5_PW_EXPIRATION && ent.entry.pw_end) out->pw_expiration = *ent.entry.pw_end; if(mask & KADM5_LAST_PWD_CHANGE) hdb_entry_get_pw_change_time(&ent.entry, &out->last_pwd_change); if(mask & KADM5_ATTRIBUTES){ out->attributes |= ent.entry.flags.postdate ? 0 : KRB5_KDB_DISALLOW_POSTDATED; out->attributes |= ent.entry.flags.forwardable ? 0 : KRB5_KDB_DISALLOW_FORWARDABLE; out->attributes |= ent.entry.flags.initial ? KRB5_KDB_DISALLOW_TGT_BASED : 0; out->attributes |= ent.entry.flags.renewable ? 0 : KRB5_KDB_DISALLOW_RENEWABLE; out->attributes |= ent.entry.flags.proxiable ? 0 : KRB5_KDB_DISALLOW_PROXIABLE; out->attributes |= ent.entry.flags.invalid ? KRB5_KDB_DISALLOW_ALL_TIX : 0; out->attributes |= ent.entry.flags.require_preauth ? KRB5_KDB_REQUIRES_PRE_AUTH : 0; out->attributes |= ent.entry.flags.server ? 0 : KRB5_KDB_DISALLOW_SVR; out->attributes |= ent.entry.flags.change_pw ? KRB5_KDB_PWCHANGE_SERVICE : 0; out->attributes |= ent.entry.flags.ok_as_delegate ? KRB5_KDB_OK_AS_DELEGATE : 0; out->attributes |= ent.entry.flags.trusted_for_delegation ? KRB5_KDB_TRUSTED_FOR_DELEGATION : 0; out->attributes |= ent.entry.flags.allow_kerberos4 ? KRB5_KDB_ALLOW_KERBEROS4 : 0; out->attributes |= ent.entry.flags.allow_digest ? KRB5_KDB_ALLOW_DIGEST : 0; } if(mask & KADM5_MAX_LIFE) { if(ent.entry.max_life) out->max_life = *ent.entry.max_life; else out->max_life = INT_MAX; } if(mask & KADM5_MOD_TIME) { if(ent.entry.modified_by) out->mod_date = ent.entry.modified_by->time; else out->mod_date = ent.entry.created_by.time; } if(mask & KADM5_MOD_NAME) { if(ent.entry.modified_by) { if (ent.entry.modified_by->principal != NULL) ret = krb5_copy_principal(context->context, ent.entry.modified_by->principal, &out->mod_name); } else if(ent.entry.created_by.principal != NULL) ret = krb5_copy_principal(context->context, ent.entry.created_by.principal, &out->mod_name); else out->mod_name = NULL; } if(ret) goto out; if(mask & KADM5_KVNO) out->kvno = ent.entry.kvno; if(mask & KADM5_MKVNO) { size_t n; out->mkvno = 0; /* XXX */ for(n = 0; n < ent.entry.keys.len; n++) if(ent.entry.keys.val[n].mkvno) { out->mkvno = *ent.entry.keys.val[n].mkvno; /* XXX this isn't right */ break; } } #if 0 /* XXX implement */ if(mask & KADM5_AUX_ATTRIBUTES) ; if(mask & KADM5_LAST_SUCCESS) ; if(mask & KADM5_LAST_FAILED) ; if(mask & KADM5_FAIL_AUTH_COUNT) ; #endif if(mask & KADM5_POLICY) { HDB_extension *ext; ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_policy); if (ext == NULL) { out->policy = strdup("default"); /* It's OK if we retun NULL instead of "default" */ } else { out->policy = strdup(ext->data.u.policy); if (out->policy == NULL) { ret = ENOMEM; goto out; } } } if(mask & KADM5_MAX_RLIFE) { if(ent.entry.max_renew) out->max_renewable_life = *ent.entry.max_renew; else out->max_renewable_life = INT_MAX; } if(mask & KADM5_KEY_DATA){ size_t i; size_t n_keys = ent.entry.keys.len; krb5_salt salt; HDB_extension *ext; HDB_Ext_KeySet *hist_keys = NULL; ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_hist_keys); if (ext != NULL) hist_keys = &ext->data.u.hist_keys; krb5_get_pw_salt(context->context, ent.entry.principal, &salt); for (i = 0; hist_keys != NULL && i < hist_keys->len; i++) n_keys += hist_keys->val[i].keys.len; out->key_data = malloc(n_keys * sizeof(*out->key_data)); if (out->key_data == NULL && n_keys != 0) { ret = ENOMEM; goto out; } out->n_key_data = 0; ret = copy_keyset_to_kadm5(context, ent.entry.kvno, ent.entry.keys.len, ent.entry.keys.val, &salt, out); if (ret) goto out; for (i = 0; hist_keys != NULL && i < hist_keys->len; i++) { ret = copy_keyset_to_kadm5(context, hist_keys->val[i].kvno, hist_keys->val[i].keys.len, hist_keys->val[i].keys.val, &salt, out); if (ret) goto out; } krb5_free_salt(context->context, salt); assert( out->n_key_data == n_keys ); } if(ret){ kadm5_free_principal_ent(context, out); goto out; } if(mask & KADM5_TL_DATA) { time_t last_pw_expire; const HDB_Ext_PKINIT_acl *acl; const HDB_Ext_Aliases *aliases; ret = hdb_entry_get_pw_change_time(&ent.entry, &last_pw_expire); if (ret == 0 && last_pw_expire) { unsigned char buf[4]; _krb5_put_int(buf, last_pw_expire, sizeof(buf)); ret = add_tl_data(out, KRB5_TL_LAST_PWD_CHANGE, buf, sizeof(buf)); } if(ret){ kadm5_free_principal_ent(context, out); goto out; } /* * If the client was allowed to get key data, let it have the * password too. */ if(mask & KADM5_KEY_DATA) { heim_utf8_string pw; ret = hdb_entry_get_password(context->context, context->db, &ent.entry, &pw); if (ret == 0) { ret = add_tl_data(out, KRB5_TL_PASSWORD, pw, strlen(pw) + 1); free(pw); } krb5_clear_error_message(context->context); } ret = hdb_entry_get_pkinit_acl(&ent.entry, &acl); if (ret == 0 && acl) { krb5_data buf; size_t len; ASN1_MALLOC_ENCODE(HDB_Ext_PKINIT_acl, buf.data, buf.length, acl, &len, ret); if (ret) { kadm5_free_principal_ent(context, out); goto out; } if (len != buf.length) krb5_abortx(context->context, "internal ASN.1 encoder error"); ret = add_tl_data(out, KRB5_TL_PKINIT_ACL, buf.data, buf.length); free(buf.data); if (ret) { kadm5_free_principal_ent(context, out); goto out; } } if(ret){ kadm5_free_principal_ent(context, out); goto out; } ret = hdb_entry_get_aliases(&ent.entry, &aliases); if (ret == 0 && aliases) { krb5_data buf; size_t len; ASN1_MALLOC_ENCODE(HDB_Ext_Aliases, buf.data, buf.length, aliases, &len, ret); if (ret) { kadm5_free_principal_ent(context, out); goto out; } if (len != buf.length) krb5_abortx(context->context, "internal ASN.1 encoder error"); ret = add_tl_data(out, KRB5_TL_ALIASES, buf.data, buf.length); free(buf.data); if (ret) { kadm5_free_principal_ent(context, out); goto out; } } if(ret){ kadm5_free_principal_ent(context, out); goto out; } } out: hdb_free_entry(context->context, &ent); return _kadm5_error_code(ret); }
static krb5_error_code tkt_referral_send(krb5_context context, krb5_tkt_creds_context ctx, krb5_data *in, krb5_data *out, krb5_realm *realm, unsigned int *flags) { krb5_error_code ret; TGS_REQ req; size_t len; METHOD_DATA padata; padata.val = NULL; padata.len = 0; krb5_generate_random_block(&ctx->nonce, sizeof(ctx->nonce)); ctx->nonce &= 0xffffffff; if (_krb5_have_debug(context, 10)) { char *sname, *tgtname; krb5_unparse_name(context, ctx->tgt.server, &tgtname); krb5_unparse_name(context, ctx->next.server, &sname); _krb5_debugx(context, 10, "sending TGS-REQ for %s using %s", sname, tgtname); } ret = _krb5_init_tgs_req(context, ctx->ccache, ctx->addreseses, ctx->kdc_flags, ctx->impersonate_principal, NULL, &ctx->next, &ctx->tgt, ctx->nonce, &padata, &ctx->subkey, &req); if (ret) goto out; ASN1_MALLOC_ENCODE(TGS_REQ, out->data, out->length, &req, &len, ret); if (ret) goto out; if(out->length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); /* don't free addresses */ req.req_body.addresses = NULL; free_TGS_REQ(&req); *realm = ctx->tgt.server->name.name_string.val[1]; *flags |= KRB5_TKT_STATE_CONTINUE; ctx->error = 0; ctx->state = tkt_referral_recv; return 0; out: ctx->error = ret; ctx->state = NULL; return ret; }
krb5_error_code _krb5_init_tgs_req(krb5_context context, krb5_ccache ccache, krb5_addresses *addresses, krb5_kdc_flags flags, krb5_const_principal impersonate_principal, Ticket *second_ticket, krb5_creds *in_creds, krb5_creds *krbtgt, unsigned nonce, METHOD_DATA *padata, krb5_keyblock **subkey, TGS_REQ *t) { krb5_auth_context ac = NULL; krb5_error_code ret = 0; /* inherit the forwardable/proxyable flags from the krbtgt */ flags.b.forwardable = krbtgt->flags.b.forwardable; flags.b.proxiable = krbtgt->flags.b.proxiable; if (ccache->ops->tgt_req) { KERB_TGS_REQ_OUT out; KERB_TGS_REQ_IN in; memset(&in, 0, sizeof(in)); memset(&out, 0, sizeof(out)); ret = ccache->ops->tgt_req(context, ccache, &in, &out); if (ret) return ret; free_KERB_TGS_REQ_OUT(&out); return 0; } memset(t, 0, sizeof(*t)); if (impersonate_principal) { krb5_crypto crypto; PA_S4U2Self self; krb5_data data; void *buf; size_t size, len; self.name = impersonate_principal->name; self.realm = impersonate_principal->realm; self.auth = rk_UNCONST("Kerberos"); ret = _krb5_s4u2self_to_checksumdata(context, &self, &data); if (ret) goto fail; ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto); if (ret) { krb5_data_free(&data); goto fail; } ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, data.data, data.length, &self.cksum); krb5_crypto_destroy(context, crypto); krb5_data_free(&data); if (ret) goto fail; ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret); free_Checksum(&self.cksum); if (ret) goto fail; if (len != size) krb5_abortx(context, "internal asn1 error"); ret = krb5_padata_add(context, padata, KRB5_PADATA_FOR_USER, buf, len); if (ret) goto fail; } t->pvno = 5; t->msg_type = krb_tgs_req; if (in_creds->session.keytype) { ALLOC_SEQ(&t->req_body.etype, 1); if(t->req_body.etype.val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } t->req_body.etype.val[0] = in_creds->session.keytype; } else { ret = _krb5_init_etype(context, KRB5_PDU_TGS_REQUEST, &t->req_body.etype.len, &t->req_body.etype.val, NULL); } if (ret) goto fail; t->req_body.addresses = addresses; t->req_body.kdc_options = flags.b; ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm); if (ret) goto fail; ALLOC(t->req_body.sname, 1); if (t->req_body.sname == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } /* some versions of some code might require that the client be present in TGS-REQs, but this is clearly against the spec */ ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname); if (ret) goto fail; /* req_body.till should be NULL if there is no endtime specified, but old MIT code (like DCE secd) doesn't like that */ ALLOC(t->req_body.till, 1); if(t->req_body.till == NULL){ ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } *t->req_body.till = in_creds->times.endtime; t->req_body.nonce = nonce; if(second_ticket){ ALLOC(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ALLOC_SEQ(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets->val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val); if (ret) goto fail; } ALLOC(t->padata, 1); if (t->padata == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ALLOC_SEQ(t->padata, 1 + padata->len); if (t->padata->val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } { size_t i; for (i = 0; i < padata->len; i++) { ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]); if (ret) { krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } } } ret = krb5_auth_con_init(context, &ac); if(ret) goto fail; ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session); if (ret) goto fail; ret = set_auth_data (context, &t->req_body, &in_creds->authdata, ac->local_subkey); if (ret) goto fail; ret = make_pa_tgs_req(context, ac, &t->req_body, &t->padata->val[0], ccache, krbtgt); if(ret) goto fail; ret = krb5_auth_con_getlocalsubkey(context, ac, subkey); if (ret) goto fail; fail: if (ac) krb5_auth_con_free(context, ac); if (ret) { t->req_body.addresses = NULL; free_TGS_REQ (t); } 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; }
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; }
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; }
static int sig_process(hx509_context context, void *ctx, hx509_cert cert) { struct sigctx *sigctx = ctx; heim_octet_string buf, sigdata = { 0, NULL }; SignerInfo *signer_info = NULL; AlgorithmIdentifier digest; size_t size; void *ptr; int ret; SignedData *sd = &sigctx->sd; hx509_path path; memset(&digest, 0, sizeof(digest)); memset(&path, 0, sizeof(path)); if (_hx509_cert_private_key(cert) == NULL) { hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING, "Private key missing for signing"); return HX509_PRIVATE_KEY_MISSING; } if (sigctx->digest_alg) { ret = copy_AlgorithmIdentifier(sigctx->digest_alg, &digest); if (ret) hx509_clear_error_string(context); } else { ret = hx509_crypto_select(context, HX509_SELECT_DIGEST, _hx509_cert_private_key(cert), sigctx->peer, &digest); } if (ret) goto out; /* * Allocate on more signerInfo and do the signature processing */ ptr = realloc(sd->signerInfos.val, (sd->signerInfos.len + 1) * sizeof(sd->signerInfos.val[0])); if (ptr == NULL) { ret = ENOMEM; goto out; } sd->signerInfos.val = ptr; signer_info = &sd->signerInfos.val[sd->signerInfos.len]; memset(signer_info, 0, sizeof(*signer_info)); signer_info->version = 1; ret = fill_CMSIdentifier(cert, sigctx->cmsidflag, &signer_info->sid); if (ret) { hx509_clear_error_string(context); goto out; } signer_info->signedAttrs = NULL; signer_info->unsignedAttrs = NULL; ret = copy_AlgorithmIdentifier(&digest, &signer_info->digestAlgorithm); if (ret) { hx509_clear_error_string(context); goto out; } /* * If it isn't pkcs7-data send signedAttributes */ if (der_heim_oid_cmp(sigctx->eContentType, &asn1_oid_id_pkcs7_data) != 0) { CMSAttributes sa; heim_octet_string sig; ALLOC(signer_info->signedAttrs, 1); if (signer_info->signedAttrs == NULL) { ret = ENOMEM; goto out; } ret = _hx509_create_signature(context, NULL, &digest, &sigctx->content, NULL, &sig); if (ret) goto out; ASN1_MALLOC_ENCODE(MessageDigest, buf.data, buf.length, &sig, &size, ret); der_free_octet_string(&sig); if (ret) { hx509_clear_error_string(context); goto out; } if (size != buf.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_one_attribute(&signer_info->signedAttrs->val, &signer_info->signedAttrs->len, &asn1_oid_id_pkcs9_messageDigest, &buf); if (ret) { free(buf.data); hx509_clear_error_string(context); goto out; } ASN1_MALLOC_ENCODE(ContentType, buf.data, buf.length, sigctx->eContentType, &size, ret); if (ret) goto out; if (size != buf.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_one_attribute(&signer_info->signedAttrs->val, &signer_info->signedAttrs->len, &asn1_oid_id_pkcs9_contentType, &buf); if (ret) { free(buf.data); hx509_clear_error_string(context); goto out; } sa.val = signer_info->signedAttrs->val; sa.len = signer_info->signedAttrs->len; ASN1_MALLOC_ENCODE(CMSAttributes, sigdata.data, sigdata.length, &sa, &size, ret); if (ret) { hx509_clear_error_string(context); goto out; } if (size != sigdata.length) _hx509_abort("internal ASN.1 encoder error"); } else { sigdata.data = sigctx->content.data; sigdata.length = sigctx->content.length; } { AlgorithmIdentifier sigalg; ret = hx509_crypto_select(context, HX509_SELECT_PUBLIC_SIG, _hx509_cert_private_key(cert), sigctx->peer, &sigalg); if (ret) goto out; ret = _hx509_create_signature(context, _hx509_cert_private_key(cert), &sigalg, &sigdata, &signer_info->signatureAlgorithm, &signer_info->signature); free_AlgorithmIdentifier(&sigalg); if (ret) goto out; } sigctx->sd.signerInfos.len++; signer_info = NULL; /* * Provide best effort path */ if (sigctx->certs) { unsigned int i; if (sigctx->pool && sigctx->leafonly == 0) { _hx509_calculate_path(context, HX509_CALCULATE_PATH_NO_ANCHOR, time(NULL), sigctx->anchors, 0, cert, sigctx->pool, &path); } else _hx509_path_append(context, &path, cert); for (i = 0; i < path.len; i++) { /* XXX remove dups */ ret = hx509_certs_add(context, sigctx->certs, path.val[i]); if (ret) { hx509_clear_error_string(context); goto out; } } } out: if (signer_info) free_SignerInfo(signer_info); if (sigdata.data != sigctx->content.data) der_free_octet_string(&sigdata); _hx509_path_free(&path); free_AlgorithmIdentifier(&digest); return ret; }
krb5_error_code _kdc_as_rep(krb5_context context, krb5_kdc_configuration *config, KDC_REQ *req, const krb5_data *req_buffer, krb5_data *reply, const char *from, struct sockaddr *from_addr, int datagram_reply) { KDC_REQ_BODY *b = &req->req_body; AS_REP rep; KDCOptions f = b->kdc_options; hdb_entry_ex *client = NULL, *server = NULL; HDB *clientdb; krb5_enctype setype, sessionetype; krb5_data e_data; EncTicketPart et; EncKDCRepPart ek; krb5_principal client_princ = NULL, server_princ = NULL; char *client_name = NULL, *server_name = NULL; krb5_error_code ret = 0; const char *e_text = NULL; krb5_crypto crypto; Key *ckey, *skey; EncryptionKey *reply_key = NULL, session_key; int flags = HDB_F_FOR_AS_REQ; #ifdef PKINIT pk_client_params *pkp = NULL; #endif memset(&rep, 0, sizeof(rep)); memset(&session_key, 0, sizeof(session_key)); krb5_data_zero(&e_data); ALLOC(rep.padata); rep.padata->len = 0; rep.padata->val = NULL; if (f.canonicalize) flags |= HDB_F_CANON; if(b->sname == NULL){ ret = KRB5KRB_ERR_GENERIC; e_text = "No server in request"; } else{ ret = _krb5_principalname2krb5_principal (context, &server_princ, *(b->sname), b->realm); if (ret == 0) ret = krb5_unparse_name(context, server_princ, &server_name); } if (ret) { kdc_log(context, config, 0, "AS-REQ malformed server name from %s", from); goto out; } if(b->cname == NULL){ ret = KRB5KRB_ERR_GENERIC; e_text = "No client in request"; } else { ret = _krb5_principalname2krb5_principal (context, &client_princ, *(b->cname), b->realm); if (ret) goto out; ret = krb5_unparse_name(context, client_princ, &client_name); } if (ret) { kdc_log(context, config, 0, "AS-REQ malformed client name from %s", from); goto out; } kdc_log(context, config, 0, "AS-REQ %s from %s for %s", client_name, from, server_name); /* * */ if (_kdc_is_anonymous(context, client_princ)) { if (!b->kdc_options.request_anonymous) { kdc_log(context, config, 0, "Anonymous ticket w/o anonymous flag"); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } } else if (b->kdc_options.request_anonymous) { kdc_log(context, config, 0, "Request for a anonymous ticket with non " "anonymous client name: %s", client_name); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } /* * */ ret = _kdc_db_fetch(context, config, client_princ, HDB_F_GET_CLIENT | flags, NULL, &clientdb, &client); if(ret == HDB_ERR_NOT_FOUND_HERE) { kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy", client_name); goto out; } else if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, msg); krb5_free_error_message(context, msg); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } ret = _kdc_db_fetch(context, config, server_princ, HDB_F_GET_SERVER|HDB_F_GET_KRBTGT | flags, NULL, NULL, &server); if(ret == HDB_ERR_NOT_FOUND_HERE) { kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", server_name); goto out; } else if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name, msg); krb5_free_error_message(context, msg); ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } memset(&et, 0, sizeof(et)); memset(&ek, 0, sizeof(ek)); /* * Select a session enctype from the list of the crypto system * supported enctypes that is supported by the client and is one of * the enctype of the enctype of the service (likely krbtgt). * * The latter is used as a hint of what enctypes all KDC support, * to make sure a newer version of KDC won't generate a session * enctype that an older version of a KDC in the same realm can't * decrypt. */ ret = _kdc_find_etype(context, config->as_use_strongest_session_key, FALSE, client, b->etype.val, b->etype.len, &sessionetype, NULL); if (ret) { kdc_log(context, config, 0, "Client (%s) from %s has no common enctypes with KDC " "to use for the session key", client_name, from); goto out; } /* * But if the KDC admin is paranoid and doesn't want to have "not * the best" enctypes on the krbtgt, lets save the best pick from * the client list and hope that that will work for any other * KDCs. */ /* * Pre-auth processing */ if(req->padata){ int i; const PA_DATA *pa; int found_pa = 0; log_patypes(context, config, req->padata); #ifdef PKINIT kdc_log(context, config, 5, "Looking for PKINIT pa-data -- %s", client_name); e_text = "No PKINIT PA found"; i = 0; pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ); if (pa == NULL) { i = 0; pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN); } if (pa) { char *client_cert = NULL; ret = _kdc_pk_rd_padata(context, config, req, pa, client, &pkp); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 5, "Failed to decode PKINIT PA-DATA -- %s", client_name); goto ts_enc; } if (ret == 0 && pkp == NULL) goto ts_enc; ret = _kdc_pk_check_client(context, config, clientdb, client, pkp, &client_cert); if (ret) { e_text = "PKINIT certificate not allowed to " "impersonate principal"; _kdc_pk_free_client_param(context, pkp); kdc_log(context, config, 0, "%s", e_text); pkp = NULL; goto out; } found_pa = 1; et.flags.pre_authent = 1; kdc_log(context, config, 0, "PKINIT pre-authentication succeeded -- %s using %s", client_name, client_cert); free(client_cert); if (pkp) goto preauth_done; } ts_enc: #endif if (client->entry.flags.locked_out) { ret = KRB5KDC_ERR_CLIENT_REVOKED; kdc_log(context, config, 0, "Client (%s) is locked out", client_name); goto out; } kdc_log(context, config, 5, "Looking for ENC-TS pa-data -- %s", client_name); i = 0; e_text = "No ENC-TS found"; while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){ krb5_data ts_data; PA_ENC_TS_ENC p; size_t len; EncryptedData enc_data; Key *pa_key; char *str; found_pa = 1; if (b->kdc_options.request_anonymous) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 0, "ENC-TS doesn't support anon"); goto out; } ret = decode_EncryptedData(pa->padata_value.data, pa->padata_value.length, &enc_data, &len); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s", client_name); goto out; } ret = hdb_enctype2key(context, &client->entry, enc_data.etype, &pa_key); if(ret){ char *estr; e_text = "No key matches pa-data"; ret = KRB5KDC_ERR_ETYPE_NOSUPP; if(krb5_enctype_to_string(context, enc_data.etype, &estr)) estr = NULL; if(estr == NULL) kdc_log(context, config, 5, "No client key matching pa-data (%d) -- %s", enc_data.etype, client_name); else kdc_log(context, config, 5, "No client key matching pa-data (%s) -- %s", estr, client_name); free(estr); free_EncryptedData(&enc_data); continue; } try_next_key: ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg); krb5_free_error_message(context, msg); free_EncryptedData(&enc_data); continue; } ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_PA_ENC_TIMESTAMP, &enc_data, &ts_data); krb5_crypto_destroy(context, crypto); /* * Since the user might have several keys with the same * enctype but with diffrent salting, we need to try all * the keys with the same enctype. */ if(ret){ krb5_error_code ret2; const char *msg = krb5_get_error_message(context, ret); ret2 = krb5_enctype_to_string(context, pa_key->key.keytype, &str); if (ret2) str = NULL; kdc_log(context, config, 5, "Failed to decrypt PA-DATA -- %s " "(enctype %s) error %s", client_name, str ? str : "unknown enctype", msg); krb5_free_error_message(context, msg); free(str); if(hdb_next_enctype2key(context, &client->entry, enc_data.etype, &pa_key) == 0) goto try_next_key; e_text = "Failed to decrypt PA-DATA"; free_EncryptedData(&enc_data); if (clientdb->hdb_auth_status) (clientdb->hdb_auth_status)(context, clientdb, client, HDB_AUTH_WRONG_PASSWORD); ret = KRB5KDC_ERR_PREAUTH_FAILED; continue; } free_EncryptedData(&enc_data); ret = decode_PA_ENC_TS_ENC(ts_data.data, ts_data.length, &p, &len); krb5_data_free(&ts_data); if(ret){ e_text = "Failed to decode PA-ENC-TS-ENC"; ret = KRB5KDC_ERR_PREAUTH_FAILED; kdc_log(context, config, 5, "Failed to decode PA-ENC-TS_ENC -- %s", client_name); continue; } free_PA_ENC_TS_ENC(&p); if (abs(kdc_time - p.patimestamp) > context->max_skew) { char client_time[100]; krb5_format_time(context, p.patimestamp, client_time, sizeof(client_time), TRUE); ret = KRB5KRB_AP_ERR_SKEW; kdc_log(context, config, 0, "Too large time skew, " "client time %s is out by %u > %u seconds -- %s", client_time, (unsigned)abs(kdc_time - p.patimestamp), context->max_skew, client_name); /* * The following is needed to make windows clients to * retry using the timestamp in the error message, if * there is a e_text, they become unhappy. */ e_text = NULL; goto out; } et.flags.pre_authent = 1; set_salt_padata(rep.padata, pa_key->salt); reply_key = &pa_key->key; ret = krb5_enctype_to_string(context, pa_key->key.keytype, &str); if (ret) str = NULL; kdc_log(context, config, 2, "ENC-TS Pre-authentication succeeded -- %s using %s", client_name, str ? str : "unknown enctype"); free(str); break; } #ifdef PKINIT preauth_done: #endif if(found_pa == 0 && config->require_preauth) goto use_pa; /* We come here if we found a pa-enc-timestamp, but if there was some problem with it, other than too large skew */ if(found_pa && et.flags.pre_authent == 0){ kdc_log(context, config, 0, "%s -- %s", e_text, client_name); e_text = NULL; goto out; } }else if (config->require_preauth || b->kdc_options.request_anonymous /* hack to force anon */ || client->entry.flags.require_preauth || server->entry.flags.require_preauth) { METHOD_DATA method_data; PA_DATA *pa; unsigned char *buf; size_t len; use_pa: method_data.len = 0; method_data.val = NULL; ret = realloc_method_data(&method_data); if (ret) { free_METHOD_DATA(&method_data); goto out; } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP; pa->padata_value.length = 0; pa->padata_value.data = NULL; #ifdef PKINIT ret = realloc_method_data(&method_data); if (ret) { free_METHOD_DATA(&method_data); goto out; } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ; pa->padata_value.length = 0; pa->padata_value.data = NULL; ret = realloc_method_data(&method_data); if (ret) { free_METHOD_DATA(&method_data); goto out; } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ_WIN; pa->padata_value.length = 0; pa->padata_value.data = NULL; #endif /* * If there is a client key, send ETYPE_INFO{,2} */ ret = _kdc_find_etype(context, config->preauth_use_strongest_session_key, TRUE, client, b->etype.val, b->etype.len, NULL, &ckey); if (ret == 0) { /* * RFC4120 requires: * - If the client only knows about old enctypes, then send * both info replies (we send 'info' first in the list). * - If the client is 'modern', because it knows about 'new' * enctype types, then only send the 'info2' reply. * * Before we send the full list of etype-info data, we pick * the client key we would have used anyway below, just pick * that instead. */ if (older_enctype(ckey->key.keytype)) { ret = get_pa_etype_info(context, config, &method_data, ckey); if (ret) { free_METHOD_DATA(&method_data); goto out; } } ret = get_pa_etype_info2(context, config, &method_data, ckey); if (ret) { free_METHOD_DATA(&method_data); goto out; } } ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret); free_METHOD_DATA(&method_data); e_data.data = buf; e_data.length = len; e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ", ret = KRB5KDC_ERR_PREAUTH_REQUIRED; kdc_log(context, config, 0, "No preauth found, returning PREAUTH-REQUIRED -- %s", client_name); goto out; } /* * Verify flags after the user been required to prove its identity * with in a preauth mech. */ ret = _kdc_check_access(context, config, client, client_name, server, server_name, req, &e_data); if(ret) goto out; if (clientdb->hdb_auth_status) (clientdb->hdb_auth_status)(context, clientdb, client, HDB_AUTH_SUCCESS); /* * Selelct the best encryption type for the KDC with out regard to * the client since the client never needs to read that data. */ ret = _kdc_get_preferred_key(context, config, server, server_name, &setype, &skey); if(ret) goto out; if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey || (f.request_anonymous && !config->allow_anonymous)) { ret = KRB5KDC_ERR_BADOPTION; e_text = "Bad KDC options"; kdc_log(context, config, 0, "Bad KDC options -- %s", client_name); goto out; } rep.pvno = 5; rep.msg_type = krb_as_rep; ret = copy_Realm(&client->entry.principal->realm, &rep.crealm); if (ret) goto out; ret = _krb5_principal2principalname(&rep.cname, client->entry.principal); if (ret) goto out; rep.ticket.tkt_vno = 5; copy_Realm(&server->entry.principal->realm, &rep.ticket.realm); _krb5_principal2principalname(&rep.ticket.sname, server->entry.principal); /* java 1.6 expects the name to be the same type, lets allow that * uncomplicated name-types. */ #define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t) if (CNT(b, UNKNOWN) || CNT(b, PRINCIPAL) || CNT(b, SRV_INST) || CNT(b, SRV_HST) || CNT(b, SRV_XHST)) rep.ticket.sname.name_type = b->sname->name_type; #undef CNT et.flags.initial = 1; if(client->entry.flags.forwardable && server->entry.flags.forwardable) et.flags.forwardable = f.forwardable; else if (f.forwardable) { e_text = "Ticket may not be forwardable"; ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be forwardable -- %s", client_name); goto out; } if(client->entry.flags.proxiable && server->entry.flags.proxiable) et.flags.proxiable = f.proxiable; else if (f.proxiable) { e_text = "Ticket may not be proxiable"; ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be proxiable -- %s", client_name); goto out; } if(client->entry.flags.postdate && server->entry.flags.postdate) et.flags.may_postdate = f.allow_postdate; else if (f.allow_postdate){ e_text = "Ticket may not be postdate"; ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be postdatable -- %s", client_name); goto out; } /* check for valid set of addresses */ if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) { e_text = "Bad address list in requested"; ret = KRB5KRB_AP_ERR_BADADDR; kdc_log(context, config, 0, "Bad address list requested -- %s", client_name); goto out; } ret = copy_PrincipalName(&rep.cname, &et.cname); if (ret) goto out; ret = copy_Realm(&rep.crealm, &et.crealm); if (ret) goto out; { time_t start; time_t t; start = et.authtime = kdc_time; if(f.postdated && req->req_body.from){ ALLOC(et.starttime); start = *et.starttime = *req->req_body.from; et.flags.invalid = 1; et.flags.postdated = 1; /* XXX ??? */ } _kdc_fix_time(&b->till); t = *b->till; /* be careful not overflowing */ if(client->entry.max_life) t = start + min(t - start, *client->entry.max_life); if(server->entry.max_life) t = start + min(t - start, *server->entry.max_life); #if 0 t = min(t, start + realm->max_life); #endif et.endtime = t; if(f.renewable_ok && et.endtime < *b->till){ f.renewable = 1; if(b->rtime == NULL){ ALLOC(b->rtime); *b->rtime = 0; } if(*b->rtime < *b->till) *b->rtime = *b->till; } if(f.renewable && b->rtime){ t = *b->rtime; if(t == 0) t = MAX_TIME; if(client->entry.max_renew) t = start + min(t - start, *client->entry.max_renew); if(server->entry.max_renew) t = start + min(t - start, *server->entry.max_renew); #if 0 t = min(t, start + realm->max_renew); #endif ALLOC(et.renew_till); *et.renew_till = t; et.flags.renewable = 1; } } if (f.request_anonymous) et.flags.anonymous = 1; if(b->addresses){ ALLOC(et.caddr); copy_HostAddresses(b->addresses, et.caddr); } et.transited.tr_type = DOMAIN_X500_COMPRESS; krb5_data_zero(&et.transited.contents); /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded * as 0 and as 0x80 (meaning indefinite length) apart, and is thus * incapable of correctly decoding SEQUENCE OF's of zero length. * * To fix this, always send at least one no-op last_req * * If there's a pw_end or valid_end we will use that, * otherwise just a dummy lr. */ ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val)); if (ek.last_req.val == NULL) { ret = ENOMEM; goto out; } ek.last_req.len = 0; if (client->entry.pw_end && (config->kdc_warn_pwexpire == 0 || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) { ek.last_req.val[ek.last_req.len].lr_type = LR_PW_EXPTIME; ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end; ++ek.last_req.len; } if (client->entry.valid_end) { ek.last_req.val[ek.last_req.len].lr_type = LR_ACCT_EXPTIME; ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end; ++ek.last_req.len; } if (ek.last_req.len == 0) { ek.last_req.val[ek.last_req.len].lr_type = LR_NONE; ek.last_req.val[ek.last_req.len].lr_value = 0; ++ek.last_req.len; } ek.nonce = b->nonce; if (client->entry.valid_end || client->entry.pw_end) { ALLOC(ek.key_expiration); if (client->entry.valid_end) { if (client->entry.pw_end) *ek.key_expiration = min(*client->entry.valid_end, *client->entry.pw_end); else *ek.key_expiration = *client->entry.valid_end; } else *ek.key_expiration = *client->entry.pw_end; } else ek.key_expiration = NULL; ek.flags = et.flags; ek.authtime = et.authtime; if (et.starttime) { ALLOC(ek.starttime); *ek.starttime = *et.starttime; } ek.endtime = et.endtime; if (et.renew_till) { ALLOC(ek.renew_till); *ek.renew_till = *et.renew_till; } copy_Realm(&rep.ticket.realm, &ek.srealm); copy_PrincipalName(&rep.ticket.sname, &ek.sname); if(et.caddr){ ALLOC(ek.caddr); copy_HostAddresses(et.caddr, ek.caddr); } #if PKINIT if (pkp) { e_text = "Failed to build PK-INIT reply"; ret = _kdc_pk_mk_pa_reply(context, config, pkp, client, sessionetype, req, req_buffer, &reply_key, &et.key, rep.padata); if (ret) goto out; ret = _kdc_add_inital_verified_cas(context, config, pkp, &et); if (ret) goto out; } else #endif { ret = krb5_generate_random_keyblock(context, sessionetype, &et.key); if (ret) goto out; } if (reply_key == NULL) { e_text = "Client have no reply key"; ret = KRB5KDC_ERR_CLIENT_NOTYET; goto out; } ret = copy_EncryptionKey(&et.key, &ek.key); if (ret) goto out; if (rep.padata->len == 0) { free(rep.padata); rep.padata = NULL; } /* Add the PAC */ if (send_pac_p(context, req)) { krb5_pac p = NULL; krb5_data data; ret = _kdc_pac_generate(context, client, &p); if (ret) { kdc_log(context, config, 0, "PAC generation failed for -- %s", client_name); goto out; } if (p != NULL) { ret = _krb5_pac_sign(context, p, et.authtime, client->entry.principal, &skey->key, /* Server key */ &skey->key, /* FIXME: should be krbtgt key */ &data); krb5_pac_free(context, p); if (ret) { kdc_log(context, config, 0, "PAC signing failed for -- %s", client_name); goto out; } ret = _kdc_tkt_add_if_relevant_ad(context, &et, KRB5_AUTHDATA_WIN2K_PAC, &data); krb5_data_free(&data); if (ret) goto out; } } _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime, et.endtime, et.renew_till); /* do this as the last thing since this signs the EncTicketPart */ ret = _kdc_add_KRB5SignedPath(context, config, server, setype, client->entry.principal, NULL, NULL, &et); if (ret) goto out; log_as_req(context, config, reply_key->keytype, setype, b); ret = _kdc_encode_reply(context, config, &rep, &et, &ek, setype, server->entry.kvno, &skey->key, client->entry.kvno, reply_key, 0, &e_text, reply); free_EncTicketPart(&et); free_EncKDCRepPart(&ek); if (ret) goto out; /* */ if (datagram_reply && reply->length > config->max_datagram_reply_length) { krb5_data_free(reply); ret = KRB5KRB_ERR_RESPONSE_TOO_BIG; e_text = "Reply packet too large"; } out: free_AS_REP(&rep); if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE){ krb5_mk_error(context, ret, e_text, (e_data.data ? &e_data : NULL), client_princ, server_princ, NULL, NULL, reply); ret = 0; } #ifdef PKINIT if (pkp) _kdc_pk_free_client_param(context, pkp); #endif if (e_data.data) free(e_data.data); if (client_princ) krb5_free_principal(context, client_princ); free(client_name); if (server_princ) krb5_free_principal(context, server_princ); free(server_name); if(client) _kdc_free_ent(context, client); if(server) _kdc_free_ent(context, server); return ret; }
static krb5_error_code tgs_check_authenticator(krb5_context context, krb5_kdc_configuration *config, krb5_auth_context ac, KDC_REQ_BODY *b, const char **e_text, krb5_keyblock *key) { krb5_authenticator auth; size_t len; unsigned char *buf; size_t buf_size; krb5_error_code ret; krb5_crypto crypto; krb5_auth_con_getauthenticator(context, ac, &auth); if(auth->cksum == NULL){ kdc_log(context, config, 0, "No authenticator in request"); ret = KRB5KRB_AP_ERR_INAPP_CKSUM; goto out; } /* * according to RFC1510 it doesn't need to be keyed, * but according to the latest draft it needs to. */ if ( #if 0 !krb5_checksum_is_keyed(context, auth->cksum->cksumtype) || #endif !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) { kdc_log(context, config, 0, "Bad checksum type in authenticator: %d", auth->cksum->cksumtype); ret = KRB5KRB_AP_ERR_INAPP_CKSUM; goto out; } /* XXX should not re-encode this */ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret); if(ret){ kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s", krb5_get_err_text(context, ret)); goto out; } if(buf_size != len) { free(buf); kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; ret = KRB5KRB_ERR_GENERIC; goto out; } ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { free(buf); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); goto out; } ret = krb5_verify_checksum(context, crypto, KRB5_KU_TGS_REQ_AUTH_CKSUM, buf, len, auth->cksum); free(buf); krb5_crypto_destroy(context, crypto); if(ret){ kdc_log(context, config, 0, "Failed to verify authenticator checksum: %s", krb5_get_err_text(context, ret)); } out: free_Authenticator(auth); free(auth); return ret; }
/* * Send a reply. Note that we only need to send a reply if we * need to send a MIC or a mechanism token. Otherwise, we can * return an empty buffer. * * The return value of this will be returned to the API, so it * must return GSS_S_CONTINUE_NEEDED if a token was generated. */ static OM_uint32 spnego_reply_internal(OM_uint32 *minor_status, gssspnego_ctx context_handle, const gss_buffer_t mech_buf, gss_buffer_t mech_token, gss_buffer_t output_token) { NegotiationToken nt; gss_buffer_desc mic_buf; OM_uint32 ret; size_t size; if (mech_buf == GSS_C_NO_BUFFER && mech_token->length == 0) { output_token->length = 0; output_token->value = NULL; return context_handle->open ? GSS_S_COMPLETE : GSS_S_FAILURE; } memset(&nt, 0, sizeof(nt)); nt.element = choice_NegotiationToken_negTokenResp; ALLOC(nt.u.negTokenResp.negResult, 1); if (nt.u.negTokenResp.negResult == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } nt.u.negTokenResp.supportedMech = NULL; output_token->length = 0; output_token->value = NULL; if (mech_token->length == 0) { nt.u.negTokenResp.responseToken = NULL; *(nt.u.negTokenResp.negResult) = accept_completed; } else { ALLOC(nt.u.negTokenResp.responseToken, 1); if (nt.u.negTokenResp.responseToken == NULL) { free_NegotiationToken(&nt); *minor_status = ENOMEM; return GSS_S_FAILURE; } nt.u.negTokenResp.responseToken->length = mech_token->length; nt.u.negTokenResp.responseToken->data = mech_token->value; mech_token->length = 0; mech_token->value = NULL; *(nt.u.negTokenResp.negResult) = accept_incomplete; } if (mech_buf != GSS_C_NO_BUFFER) { ret = gss_get_mic(minor_status, context_handle->negotiated_ctx_id, 0, mech_buf, &mic_buf); if (ret == GSS_S_COMPLETE) { ALLOC(nt.u.negTokenResp.mechListMIC, 1); if (nt.u.negTokenResp.mechListMIC == NULL) { gss_release_buffer(minor_status, &mic_buf); free_NegotiationToken(&nt); *minor_status = ENOMEM; return GSS_S_FAILURE; } nt.u.negTokenResp.mechListMIC->length = mic_buf.length; nt.u.negTokenResp.mechListMIC->data = mic_buf.value; } else if (ret == GSS_S_UNAVAILABLE) { nt.u.negTokenResp.mechListMIC = NULL; } if (ret) { free_NegotiationToken(&nt); *minor_status = ENOMEM; return GSS_S_FAILURE; } } else { nt.u.negTokenResp.mechListMIC = NULL; } ASN1_MALLOC_ENCODE(NegotiationToken, output_token->value, output_token->length, &nt, &size, ret); if (ret) { free_NegotiationToken(&nt); *minor_status = ret; return GSS_S_FAILURE; } if (*(nt.u.negTokenResp.negResult) == accept_completed) ret = GSS_S_COMPLETE; else ret = GSS_S_CONTINUE_NEEDED; free_NegotiationToken(&nt); return ret; }
int hx509_ca_tbs_add_crl_dp_uri(hx509_context context, hx509_ca_tbs tbs, const char *uri, hx509_name issuername) { DistributionPoint dp; int ret; memset(&dp, 0, sizeof(dp)); dp.distributionPoint = ecalloc(1, sizeof(*dp.distributionPoint)); { DistributionPointName name; GeneralName gn; size_t size; name.element = choice_DistributionPointName_fullName; name.u.fullName.len = 1; name.u.fullName.val = &gn; gn.element = choice_GeneralName_uniformResourceIdentifier; gn.u.uniformResourceIdentifier.data = rk_UNCONST(uri); gn.u.uniformResourceIdentifier.length = strlen(uri); ASN1_MALLOC_ENCODE(DistributionPointName, dp.distributionPoint->data, dp.distributionPoint->length, &name, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to encoded DistributionPointName"); goto out; } if (dp.distributionPoint->length != size) _hx509_abort("internal ASN.1 encoder error"); } if (issuername) { #if 1 /** * issuername not supported */ hx509_set_error_string(context, 0, EINVAL, "CRLDistributionPoints.name.issuername not yet supported"); return EINVAL; #else GeneralNames *crlissuer; GeneralName gn; Name n; crlissuer = calloc(1, sizeof(*crlissuer)); if (crlissuer == NULL) { return ENOMEM; } memset(&gn, 0, sizeof(gn)); gn.element = choice_GeneralName_directoryName; ret = hx509_name_to_Name(issuername, &n); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } gn.u.directoryName.element = n.element; gn.u.directoryName.u.rdnSequence = n.u.rdnSequence; ret = add_GeneralNames(&crlissuer, &gn); free_Name(&n); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } dp.cRLIssuer = &crlissuer; #endif } ret = add_CRLDistributionPoints(&tbs->crldp, &dp); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } out: free_DistributionPoint(&dp); return ret; }
krb5_error_code _kdc_add_KRB5SignedPath(krb5_context context, krb5_kdc_configuration *config, hdb_entry_ex *krbtgt, krb5_enctype enctype, krb5_const_principal server, KRB5SignedPathPrincipals *principals, EncTicketPart *tkt) { krb5_error_code ret; KRB5SignedPath sp; krb5_data data; krb5_crypto crypto = NULL; size_t size; if (server && principals) { ret = add_KRB5SignedPathPrincipals(principals, server); if (ret) return ret; } { KRB5SignedPathData spd; spd.encticket = *tkt; spd.delegated = principals; ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length, &spd, &size, ret); if (ret) return ret; if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); } { Key *key; ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key); if (ret == 0) ret = krb5_crypto_init(context, &key->key, 0, &crypto); if (ret) { free(data.data); return ret; } } /* * Fill in KRB5SignedPath */ sp.etype = enctype; sp.delegated = principals; ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0, data.data, data.length, &sp.cksum); krb5_crypto_destroy(context, crypto); free(data.data); if (ret) return ret; ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret); free_Checksum(&sp.cksum); if (ret) return ret; if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); /* * Add IF-RELEVANT(KRB5SignedPath) to the last slot in * authorization data field. */ ret = _kdc_tkt_add_if_relevant_ad(context, tkt, KRB5_AUTHDATA_SIGNTICKET, &data); krb5_data_free(&data); return ret; }
int hx509_cms_envelope_1(hx509_context context, int flags, hx509_cert cert, const void *data, size_t length, const heim_oid *encryption_type, const heim_oid *contentType, heim_octet_string *content) { KeyTransRecipientInfo *ri; heim_octet_string ivec; heim_octet_string key; hx509_crypto crypto = NULL; int ret, cmsidflag; EnvelopedData ed; size_t size; memset(&ivec, 0, sizeof(ivec)); memset(&key, 0, sizeof(key)); memset(&ed, 0, sizeof(ed)); memset(content, 0, sizeof(*content)); if (encryption_type == NULL) encryption_type = &asn1_oid_id_aes_256_cbc; if ((flags & HX509_CMS_EV_NO_KU_CHECK) == 0) { ret = _hx509_check_key_usage(context, cert, 1 << 2, TRUE); if (ret) goto out; } ret = hx509_crypto_init(context, NULL, encryption_type, &crypto); if (ret) goto out; if (flags & HX509_CMS_EV_ALLOW_WEAK) hx509_crypto_allow_weak(crypto); ret = hx509_crypto_set_random_key(crypto, &key); if (ret) { hx509_set_error_string(context, 0, ret, "Create random key for EnvelopedData content"); goto out; } ret = hx509_crypto_random_iv(crypto, &ivec); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to create a random iv"); goto out; } ret = hx509_crypto_encrypt(crypto, data, length, &ivec, &ed.encryptedContentInfo.encryptedContent); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to encrypt EnvelopedData content"); goto out; } { AlgorithmIdentifier *enc_alg; enc_alg = &ed.encryptedContentInfo.contentEncryptionAlgorithm; ret = der_copy_oid(encryption_type, &enc_alg->algorithm); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to set crypto oid " "for EnvelopedData"); goto out; } ALLOC(enc_alg->parameters, 1); if (enc_alg->parameters == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Failed to allocate crypto paramaters " "for EnvelopedData"); goto out; } ret = hx509_crypto_get_params(context, crypto, &ivec, enc_alg->parameters); if (ret) { goto out; } } ALLOC_SEQ(&ed.recipientInfos, 1); if (ed.recipientInfos.val == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Failed to allocate recipients info " "for EnvelopedData"); goto out; } ri = &ed.recipientInfos.val[0]; if (flags & HX509_CMS_EV_ID_NAME) { ri->version = 0; cmsidflag = CMS_ID_NAME; } else { ri->version = 2; cmsidflag = CMS_ID_SKI; } ret = fill_CMSIdentifier(cert, cmsidflag, &ri->rid); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to set CMS identifier info " "for EnvelopedData"); goto out; } ret = hx509_cert_public_encrypt(context, &key, cert, &ri->keyEncryptionAlgorithm.algorithm, &ri->encryptedKey); if (ret) { hx509_set_error_string(context, HX509_ERROR_APPEND, ret, "Failed to encrypt transport key for " "EnvelopedData"); goto out; } /* * */ ed.version = 0; ed.originatorInfo = NULL; ret = der_copy_oid(contentType, &ed.encryptedContentInfo.contentType); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy content oid for " "EnvelopedData"); goto out; } ed.unprotectedAttrs = NULL; ASN1_MALLOC_ENCODE(EnvelopedData, content->data, content->length, &ed, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to encode EnvelopedData"); goto out; } if (size != content->length) _hx509_abort("internal ASN.1 encoder error"); out: if (crypto) hx509_crypto_destroy(crypto); if (ret) der_free_octet_string(content); der_free_octet_string(&key); der_free_octet_string(&ivec); free_EnvelopedData(&ed); return ret; }
static krb5_error_code get_cred_kdc(krb5_context context, krb5_ccache id, krb5_kdc_flags flags, krb5_addresses *addresses, krb5_creds *in_creds, krb5_creds *krbtgt, krb5_principal impersonate_principal, Ticket *second_ticket, krb5_creds *out_creds) { TGS_REQ req; krb5_data enc; krb5_data resp; krb5_kdc_rep rep; KRB_ERROR error; krb5_error_code ret; unsigned nonce; krb5_keyblock *subkey = NULL; size_t len; Ticket second_ticket_data; METHOD_DATA padata; krb5_data_zero(&resp); krb5_data_zero(&enc); padata.val = NULL; padata.len = 0; krb5_generate_random_block(&nonce, sizeof(nonce)); nonce &= 0xffffffff; if(flags.b.enc_tkt_in_skey && second_ticket == NULL){ ret = decode_Ticket(in_creds->second_ticket.data, in_creds->second_ticket.length, &second_ticket_data, &len); if(ret) return ret; second_ticket = &second_ticket_data; } if (impersonate_principal) { krb5_crypto crypto; PA_S4U2Self self; krb5_data data; void *buf; size_t size; self.name = impersonate_principal->name; self.realm = impersonate_principal->realm; self.auth = estrdup("Kerberos"); ret = _krb5_s4u2self_to_checksumdata(context, &self, &data); if (ret) { free(self.auth); goto out; } ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto); if (ret) { free(self.auth); krb5_data_free(&data); goto out; } ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, data.data, data.length, &self.cksum); krb5_crypto_destroy(context, crypto); krb5_data_free(&data); if (ret) { free(self.auth); goto out; } ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret); free(self.auth); free_Checksum(&self.cksum); if (ret) goto out; if (len != size) krb5_abortx(context, "internal asn1 error"); ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len); if (ret) goto out; } ret = init_tgs_req (context, id, addresses, flags, second_ticket, in_creds, krbtgt, nonce, &padata, &subkey, &req); if (ret) goto out; ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret); if (ret) goto out; if(enc.length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); /* don't free addresses */ req.req_body.addresses = NULL; free_TGS_REQ(&req); /* * Send and receive */ { krb5_sendto_ctx stctx; ret = krb5_sendto_ctx_alloc(context, &stctx); if (ret) return ret; krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); ret = krb5_sendto_context (context, stctx, &enc, krbtgt->server->name.name_string.val[1], &resp); krb5_sendto_ctx_free(context, stctx); } if(ret) goto out; memset(&rep, 0, sizeof(rep)); if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) { unsigned eflags = 0; ret = krb5_copy_principal(context, in_creds->client, &out_creds->client); if(ret) goto out2; ret = krb5_copy_principal(context, in_creds->server, &out_creds->server); if(ret) goto out2; /* this should go someplace else */ out_creds->times.endtime = in_creds->times.endtime; /* XXX should do better testing */ if (flags.b.constrained_delegation || impersonate_principal) eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; ret = _krb5_extract_ticket(context, &rep, out_creds, &krbtgt->session, NULL, 0, &krbtgt->addresses, nonce, eflags, decrypt_tkt_with_subkey, subkey); out2: krb5_free_kdc_rep(context, &rep); } else if(krb5_rd_error(context, &resp, &error) == 0) { ret = krb5_error_from_rd_error(context, &error, in_creds); krb5_free_error_contents(context, &error); } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) { ret = KRB5KRB_AP_ERR_V4_REPLY; krb5_clear_error_message(context); } else { ret = KRB5KRB_AP_ERR_MSG_TYPE; krb5_clear_error_message(context); } out: if (second_ticket == &second_ticket_data) free_Ticket(&second_ticket_data); free_METHOD_DATA(&padata); krb5_data_free(&resp); krb5_data_free(&enc); if(subkey) krb5_free_keyblock(context, subkey); return ret; }
static krb5_error_code check_KRB5SignedPath(krb5_context context, krb5_kdc_configuration *config, hdb_entry_ex *krbtgt, EncTicketPart *tkt, KRB5SignedPathPrincipals **delegated, int require_signedpath) { krb5_error_code ret; krb5_data data; krb5_crypto crypto = NULL; *delegated = NULL; ret = find_KRB5SignedPath(context, tkt->authorization_data, &data); if (ret == 0) { KRB5SignedPathData spd; KRB5SignedPath sp; AuthorizationData *ad; size_t size; ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL); krb5_data_free(&data); if (ret) return ret; spd.encticket = *tkt; /* the KRB5SignedPath is the last entry */ ad = spd.encticket.authorization_data; if (--ad->len == 0) spd.encticket.authorization_data = NULL; spd.delegated = sp.delegated; ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length, &spd, &size, ret); ad->len++; spd.encticket.authorization_data = ad; if (ret) { free_KRB5SignedPath(&sp); return ret; } if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); { Key *key; ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key); if (ret == 0) ret = krb5_crypto_init(context, &key->key, 0, &crypto); if (ret) { free(data.data); free_KRB5SignedPath(&sp); return ret; } } ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, data.data, data.length, &sp.cksum); krb5_crypto_destroy(context, crypto); free(data.data); if (ret) { free_KRB5SignedPath(&sp); return ret; } if (sp.delegated) { *delegated = malloc(sizeof(*sp.delegated)); if (*delegated == NULL) { free_KRB5SignedPath(&sp); return ENOMEM; } ret = copy_KRB5SignedPathPrincipals(*delegated, sp.delegated); if (ret) { free_KRB5SignedPath(&sp); free(*delegated); *delegated = NULL; return ret; } } free_KRB5SignedPath(&sp); } else { if (require_signedpath) return KRB5KDC_ERR_BADOPTION; } return 0; }