krb5_error_code KRB5_CALLCONV krb5_encode_authdata_container(krb5_context context, krb5_authdatatype type, krb5_authdata *const*authdata, krb5_authdata ***container) { krb5_error_code code; krb5_data *data; krb5_authdata ad_datum; krb5_authdata *ad_data[2]; *container = NULL; code = encode_krb5_authdata((krb5_authdata * const *)authdata, &data); if (code) return code; ad_datum.ad_type = type & AD_TYPE_FIELD_TYPE_MASK; ad_datum.length = data->length; ad_datum.contents = (unsigned char *)data->data; ad_data[0] = &ad_datum; ad_data[1] = NULL; code = krb5_copy_authdata(context, ad_data, container); krb5_free_data(context, data); return code; }
/* RFC 4537 */ static krb5_error_code make_etype_list(krb5_context context, krb5_enctype *desired_etypes, krb5_enctype tkt_enctype, krb5_authdata ***authdata) { krb5_error_code code; krb5_etype_list etypes; krb5_data *enc_etype_list; krb5_data *ad_if_relevant; krb5_authdata *etype_adata[2], etype_adatum, **adata; int i; etypes.etypes = desired_etypes; for (etypes.length = 0; etypes.etypes[etypes.length] != ENCTYPE_NULL; etypes.length++) { /* * RFC 4537: * * If the enctype of the ticket session key is included in the enctype * list sent by the client, it SHOULD be the last on the list; */ if (etypes.length && etypes.etypes[etypes.length - 1] == tkt_enctype) break; } code = encode_krb5_etype_list(&etypes, &enc_etype_list); if (code) { return code; } etype_adatum.magic = KV5M_AUTHDATA; etype_adatum.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION; etype_adatum.length = enc_etype_list->length; etype_adatum.contents = (krb5_octet *)enc_etype_list->data; etype_adata[0] = &etype_adatum; etype_adata[1] = NULL; /* Wrap in AD-IF-RELEVANT container */ code = encode_krb5_authdata(etype_adata, &ad_if_relevant); if (code) { krb5_free_data(context, enc_etype_list); return code; } krb5_free_data(context, enc_etype_list); adata = *authdata; if (adata == NULL) { adata = (krb5_authdata **)calloc(2, sizeof(krb5_authdata *)); i = 0; } else { for (i = 0; adata[i] != NULL; i++) ; adata = (krb5_authdata **)realloc(*authdata, (i + 2) * sizeof(krb5_authdata *)); } if (adata == NULL) { krb5_free_data(context, ad_if_relevant); return ENOMEM; } *authdata = adata; adata[i] = (krb5_authdata *)malloc(sizeof(krb5_authdata)); if (adata[i] == NULL) { krb5_free_data(context, ad_if_relevant); return ENOMEM; } adata[i]->magic = KV5M_AUTHDATA; adata[i]->ad_type = KRB5_AUTHDATA_IF_RELEVANT; adata[i]->length = ad_if_relevant->length; adata[i]->contents = (krb5_octet *)ad_if_relevant->data; free(ad_if_relevant); /* contents owned by adata[i] */ adata[i + 1] = NULL; return 0; }
/* * Solaris Kerberos * Same as krb5_send_tgs plus an extra arg to return the FQDN * of the KDC sent the request. */ krb5_error_code krb5_send_tgs2(krb5_context context, krb5_flags kdcoptions, const krb5_ticket_times *timestruct, const krb5_enctype *ktypes, krb5_const_principal sname, krb5_address *const *addrs, krb5_authdata *const *authorization_data, krb5_pa_data *const *padata, const krb5_data *second_ticket, krb5_creds *in_cred, krb5_response *rep, char **hostname_used) { krb5_error_code retval; krb5_kdc_req tgsreq; krb5_data *scratch, scratch2; krb5_ticket *sec_ticket = 0; krb5_ticket *sec_ticket_arr[2]; krb5_timestamp time_now; krb5_pa_data **combined_padata; krb5_pa_data ap_req_padata; int tcp_only = 0, use_master; /* * in_creds MUST be a valid credential NOT just a partially filled in * place holder for us to get credentials for the caller. */ if (!in_cred->ticket.length) return(KRB5_NO_TKT_SUPPLIED); memset((char *)&tgsreq, 0, sizeof(tgsreq)); tgsreq.kdc_options = kdcoptions; tgsreq.server = (krb5_principal) sname; tgsreq.from = timestruct->starttime; tgsreq.till = timestruct->endtime ? timestruct->endtime : in_cred->times.endtime; tgsreq.rtime = timestruct->renew_till; if ((retval = krb5_timeofday(context, &time_now))) return(retval); /* XXX we know they are the same size... */ rep->expected_nonce = tgsreq.nonce = (krb5_int32) time_now; rep->request_time = time_now; tgsreq.addresses = (krb5_address **) addrs; if (authorization_data) { /* need to encrypt it in the request */ if ((retval = encode_krb5_authdata(authorization_data, &scratch))) return(retval); if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock, KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY, scratch, &tgsreq.authorization_data))) { krb5_xfree(tgsreq.authorization_data.ciphertext.data); krb5_free_data(context, scratch); return retval; } krb5_free_data(context, scratch); } /* Get the encryption types list */ if (ktypes) { /* Check passed ktypes and make sure they're valid. */ for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) { if (!krb5_c_valid_enctype(ktypes[tgsreq.nktypes])) return KRB5_PROG_ETYPE_NOSUPP; } tgsreq.ktype = (krb5_enctype *)ktypes; } else { /* Get the default ktypes */ /* Solaris Kerberos */ if ((retval = krb5_get_tgs_ktypes(context, sname, &(tgsreq.ktype)))) goto send_tgs_error_2; for(tgsreq.nktypes = 0; tgsreq.ktype[tgsreq.nktypes]; tgsreq.nktypes++); } if (second_ticket) { if ((retval = decode_krb5_ticket(second_ticket, &sec_ticket))) goto send_tgs_error_1; sec_ticket_arr[0] = sec_ticket; sec_ticket_arr[1] = 0; tgsreq.second_ticket = sec_ticket_arr; } else tgsreq.second_ticket = 0; /* encode the body; then checksum it */ if ((retval = encode_krb5_kdc_req_body(&tgsreq, &scratch))) goto send_tgs_error_2; /* * Get an ap_req. */ if ((retval = krb5_send_tgs_basic(context, scratch, in_cred, &scratch2))) { krb5_free_data(context, scratch); goto send_tgs_error_2; } krb5_free_data(context, scratch); ap_req_padata.pa_type = KRB5_PADATA_AP_REQ; ap_req_padata.length = scratch2.length; ap_req_padata.contents = (krb5_octet *)scratch2.data; /* combine in any other supplied padata */ if (padata) { krb5_pa_data * const * counter; register unsigned int i = 0; for (counter = padata; *counter; counter++, i++); combined_padata = malloc((i+2) * sizeof(*combined_padata)); if (!combined_padata) { krb5_xfree(ap_req_padata.contents); retval = ENOMEM; goto send_tgs_error_2; } combined_padata[0] = &ap_req_padata; for (i = 1, counter = padata; *counter; counter++, i++) combined_padata[i] = (krb5_pa_data *) *counter; combined_padata[i] = 0; } else { combined_padata = (krb5_pa_data **)malloc(2*sizeof(*combined_padata)); if (!combined_padata) { krb5_xfree(ap_req_padata.contents); retval = ENOMEM; goto send_tgs_error_2; } combined_padata[0] = &ap_req_padata; combined_padata[1] = 0; } tgsreq.padata = combined_padata; /* the TGS_REQ is assembled in tgsreq, so encode it */ if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch))) { krb5_xfree(ap_req_padata.contents); krb5_xfree(combined_padata); goto send_tgs_error_2; } krb5_xfree(ap_req_padata.contents); krb5_xfree(combined_padata); /* now send request & get response from KDC */ send_again: use_master = 0; retval = krb5_sendto_kdc2(context, scratch, krb5_princ_realm(context, sname), &rep->response, &use_master, tcp_only, hostname_used); if (retval == 0) { if (krb5_is_krb_error(&rep->response)) { if (!tcp_only) { krb5_error *err_reply; retval = decode_krb5_error(&rep->response, &err_reply); /* Solaris Kerberos */ if (retval == 0) { if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) { tcp_only = 1; krb5_free_error(context, err_reply); free(rep->response.data); rep->response.data = 0; goto send_again; } krb5_free_error(context, err_reply); } } } else if (krb5_is_tgs_rep(&rep->response)) rep->message_type = KRB5_TGS_REP; else /* XXX: assume it's an error */ rep->message_type = KRB5_ERROR; } krb5_free_data(context, scratch); send_tgs_error_2:; if (sec_ticket) krb5_free_ticket(context, sec_ticket); send_tgs_error_1:; if (ktypes == NULL) krb5_xfree(tgsreq.ktype); if (tgsreq.authorization_data.ciphertext.data) { memset(tgsreq.authorization_data.ciphertext.data, 0, tgsreq.authorization_data.ciphertext.length); krb5_xfree(tgsreq.authorization_data.ciphertext.data); } return retval; }
/* * Construct a TGS request and return its ASN.1 encoding as well as the * timestamp, nonce, and subkey used. The pacb_fn callback allows the caller * to amend the request padata after the nonce and subkey are determined. */ krb5_error_code k5_make_tgs_req(krb5_context context, struct krb5int_fast_request_state *fast_state, krb5_creds *tgt, krb5_flags kdcoptions, krb5_address *const *addrs, krb5_pa_data **in_padata, krb5_creds *desired, k5_pacb_fn pacb_fn, void *pacb_data, krb5_data *req_asn1_out, krb5_timestamp *timestamp_out, krb5_int32 *nonce_out, krb5_keyblock **subkey_out) { krb5_error_code ret; krb5_kdc_req req; krb5_data *authdata_asn1 = NULL, *req_body_asn1 = NULL; krb5_data *ap_req_asn1 = NULL, *tgs_req_asn1 = NULL; krb5_ticket *sec_ticket = NULL; krb5_ticket *sec_ticket_arr[2]; krb5_timestamp time_now; krb5_pa_data **padata = NULL, *pa; krb5_keyblock *subkey = NULL; krb5_enc_data authdata_enc; krb5_enctype enctypes[2], *defenctypes = NULL; size_t count, i; *req_asn1_out = empty_data(); *timestamp_out = 0; *nonce_out = 0; *subkey_out = NULL; memset(&req, 0, sizeof(req)); memset(&authdata_enc, 0, sizeof(authdata_enc)); /* tgt's client principal must match the desired client principal. */ if (!krb5_principal_compare(context, tgt->client, desired->client)) return KRB5_PRINC_NOMATCH; /* tgt must be an actual credential, not a template. */ if (!tgt->ticket.length) return KRB5_NO_TKT_SUPPLIED; req.kdc_options = kdcoptions; req.server = desired->server; req.from = desired->times.starttime; req.till = desired->times.endtime ? desired->times.endtime : tgt->times.endtime; req.rtime = desired->times.renew_till; ret = krb5_timeofday(context, &time_now); if (ret) return ret; *nonce_out = req.nonce = (krb5_int32)time_now; *timestamp_out = time_now; req.addresses = (krb5_address **)addrs; /* Generate subkey. */ ret = krb5_generate_subkey(context, &tgt->keyblock, &subkey); if (ret) return ret; TRACE_SEND_TGS_SUBKEY(context, subkey); ret = krb5int_fast_tgs_armor(context, fast_state, subkey, &tgt->keyblock, NULL, NULL); if (ret) goto cleanup; if (desired->authdata != NULL) { ret = encode_krb5_authdata(desired->authdata, &authdata_asn1); if (ret) goto cleanup; ret = krb5_encrypt_helper(context, subkey, KRB5_KEYUSAGE_TGS_REQ_AD_SUBKEY, authdata_asn1, &authdata_enc); if (ret) goto cleanup; req.authorization_data = authdata_enc; } if (desired->keyblock.enctype != ENCTYPE_NULL) { if (!krb5_c_valid_enctype(desired->keyblock.enctype)) { ret = KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } enctypes[0] = desired->keyblock.enctype; enctypes[1] = ENCTYPE_NULL; req.ktype = enctypes; req.nktypes = 1; } else { /* Get the default TGS enctypes. */ ret = krb5_get_tgs_ktypes(context, desired->server, &defenctypes); if (ret) goto cleanup; for (count = 0; defenctypes[count]; count++); req.ktype = defenctypes; req.nktypes = count; } TRACE_SEND_TGS_ETYPES(context, req.ktype); if (kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) { if (desired->second_ticket.length == 0) { ret = KRB5_NO_2ND_TKT; goto cleanup; } ret = decode_krb5_ticket(&desired->second_ticket, &sec_ticket); if (ret) goto cleanup; sec_ticket_arr[0] = sec_ticket; sec_ticket_arr[1] = NULL; req.second_ticket = sec_ticket_arr; } /* Encode the request body. */ ret = krb5int_fast_prep_req_body(context, fast_state, &req, &req_body_asn1); if (ret) goto cleanup; ret = tgs_construct_ap_req(context, req_body_asn1, tgt, subkey, &ap_req_asn1); if (ret) goto cleanup; for (count = 0; in_padata != NULL && in_padata[count] != NULL; count++); /* Construct a padata array for the request, beginning with the ap-req. */ padata = k5calloc(count + 2, sizeof(krb5_pa_data *), &ret); if (padata == NULL) goto cleanup; padata[0] = k5alloc(sizeof(krb5_pa_data), &ret); if (padata[0] == NULL) goto cleanup; padata[0]->pa_type = KRB5_PADATA_AP_REQ; padata[0]->contents = k5memdup(ap_req_asn1->data, ap_req_asn1->length, &ret); if (padata[0] == NULL) goto cleanup; padata[0]->length = ap_req_asn1->length; /* Append copies of any other supplied padata. */ for (i = 0; in_padata != NULL && in_padata[i] != NULL; i++) { pa = k5alloc(sizeof(krb5_pa_data), &ret); if (pa == NULL) goto cleanup; pa->pa_type = in_padata[i]->pa_type; pa->length = in_padata[i]->length; pa->contents = k5memdup(in_padata[i]->contents, in_padata[i]->length, &ret); if (pa->contents == NULL) goto cleanup; padata[i + 1] = pa; } req.padata = padata; if (pacb_fn != NULL) { ret = (*pacb_fn)(context, subkey, &req, pacb_data); if (ret) goto cleanup; } /* Encode the TGS-REQ. Discard the krb5_data container. */ ret = krb5int_fast_prep_req(context, fast_state, &req, ap_req_asn1, encode_krb5_tgs_req, &tgs_req_asn1); if (ret) goto cleanup; *req_asn1_out = *tgs_req_asn1; free(tgs_req_asn1); tgs_req_asn1 = NULL; *subkey_out = subkey; subkey = NULL; cleanup: krb5_free_data(context, authdata_asn1); krb5_free_data(context, req_body_asn1); krb5_free_data(context, ap_req_asn1); krb5_free_pa_data(context, req.padata); krb5_free_ticket(context, sec_ticket); krb5_free_data_contents(context, &authdata_enc.ciphertext); krb5_free_keyblock(context, subkey); free(defenctypes); return ret; }
/* * Create a CAMMAC for contents, using enc_tkt and the first key from krbtgt * for the KDC verifier. Set *cammac_out to a single-element authdata list * containing the CAMMAC inside an IF-RELEVANT container. */ krb5_error_code cammac_create(krb5_context context, krb5_enc_tkt_part *enc_tkt, krb5_keyblock *server_key, krb5_db_entry *krbtgt, krb5_authdata **contents, krb5_authdata ***cammac_out) { krb5_error_code ret; krb5_data *der_authdata = NULL, *der_enctkt = NULL, *der_cammac = NULL; krb5_authdata ad, *list[2]; krb5_cammac cammac; krb5_verifier_mac kdc_verifier, svc_verifier; krb5_key_data *kd; krb5_keyblock tgtkey; krb5_checksum kdc_cksum, svc_cksum; *cammac_out = NULL; memset(&tgtkey, 0, sizeof(tgtkey)); memset(&kdc_cksum, 0, sizeof(kdc_cksum)); memset(&svc_cksum, 0, sizeof(svc_cksum)); /* Fetch the first krbtgt key for the KDC verifier. */ ret = krb5_dbe_find_enctype(context, krbtgt, -1, -1, 0, &kd); if (ret) goto cleanup; ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &tgtkey, NULL); if (ret) goto cleanup; /* Checksum the reply with contents as authdata for the KDC verifier. */ ret = encode_kdcver_encpart(enc_tkt, contents, &der_enctkt); if (ret) goto cleanup; ret = krb5_c_make_checksum(context, 0, &tgtkey, KRB5_KEYUSAGE_CAMMAC, der_enctkt, &kdc_cksum); if (ret) goto cleanup; kdc_verifier.princ = NULL; kdc_verifier.kvno = kd->key_data_kvno; kdc_verifier.enctype = ENCTYPE_NULL; kdc_verifier.checksum = kdc_cksum; /* Encode the authdata and checksum it for the service verifier. */ ret = encode_krb5_authdata(contents, &der_authdata); if (ret) goto cleanup; ret = krb5_c_make_checksum(context, 0, server_key, KRB5_KEYUSAGE_CAMMAC, der_authdata, &svc_cksum); if (ret) goto cleanup; svc_verifier.princ = NULL; svc_verifier.kvno = 0; svc_verifier.enctype = ENCTYPE_NULL; svc_verifier.checksum = svc_cksum; cammac.elements = contents; cammac.kdc_verifier = &kdc_verifier; cammac.svc_verifier = &svc_verifier; cammac.other_verifiers = NULL; ret = encode_krb5_cammac(&cammac, &der_cammac); if (ret) goto cleanup; /* Wrap the encoded CAMMAC in an IF-RELEVANT container and return it as a * single-element authdata list. */ ad.ad_type = KRB5_AUTHDATA_CAMMAC; ad.length = der_cammac->length; ad.contents = (uint8_t *)der_cammac->data; list[0] = &ad; list[1] = NULL; ret = krb5_encode_authdata_container(context, KRB5_AUTHDATA_IF_RELEVANT, list, cammac_out); cleanup: krb5_free_data(context, der_enctkt); krb5_free_data(context, der_authdata); krb5_free_data(context, der_cammac); krb5_free_keyblock_contents(context, &tgtkey); krb5_free_checksum_contents(context, &kdc_cksum); krb5_free_checksum_contents(context, &svc_cksum); return ret; }