krb5_error_code krb5int_decode_tgs_rep(krb5_context context, krb5_data *enc_rep, const krb5_keyblock *key, krb5_keyusage usage, krb5_kdc_rep **dec_rep) { krb5_error_code retval; krb5_kdc_rep *local_dec_rep; if (krb5_is_as_rep(enc_rep)) { retval = decode_krb5_as_rep(enc_rep, &local_dec_rep); } else if (krb5_is_tgs_rep(enc_rep)) { retval = decode_krb5_tgs_rep(enc_rep, &local_dec_rep); } else { return KRB5KRB_AP_ERR_MSG_TYPE; } if (retval) return retval; if ((retval = krb5_kdc_rep_decrypt_proc(context, key, &usage, local_dec_rep))) krb5_free_kdc_rep(context, local_dec_rep); else *dec_rep = local_dec_rep; return(retval); }
/* * 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; }
krb5_error_code krb5int_process_tgs_reply(krb5_context context, struct krb5int_fast_request_state *fast_state, krb5_data *response_data, krb5_creds *tkt, krb5_flags kdcoptions, krb5_address *const *address, krb5_pa_data **in_padata, krb5_creds *in_cred, krb5_timestamp timestamp, krb5_int32 nonce, krb5_keyblock *subkey, krb5_pa_data ***out_padata, krb5_pa_data ***out_enc_padata, krb5_creds **out_cred) { krb5_error_code retval; krb5_kdc_rep *dec_rep = NULL; krb5_error *err_reply = NULL; krb5_boolean s4u2self; s4u2self = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_S4U_X509_USER) || krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FOR_USER); if (krb5_is_krb_error(response_data)) { retval = decode_krb5_error(response_data, &err_reply); if (retval != 0) goto cleanup; retval = krb5int_fast_process_error(context, fast_state, &err_reply, NULL, NULL); if (retval) goto cleanup; retval = (krb5_error_code) err_reply->error + ERROR_TABLE_BASE_krb5; if (err_reply->text.length > 0) { switch (err_reply->error) { case KRB_ERR_GENERIC: k5_setmsg(context, retval, _("KDC returned error string: %.*s"), err_reply->text.length, err_reply->text.data); break; case KDC_ERR_S_PRINCIPAL_UNKNOWN: { char *s_name; if (err_reply->server && krb5_unparse_name(context, err_reply->server, &s_name) == 0) { k5_setmsg(context, retval, _("Server %s not found in Kerberos database"), s_name); krb5_free_unparsed_name(context, s_name); } else /* In case there's a stale S_PRINCIPAL_UNKNOWN report already noted. */ krb5_clear_error_message(context); } break; } } krb5_free_error(context, err_reply); goto cleanup; } else if (!krb5_is_tgs_rep(response_data)) { retval = KRB5KRB_AP_ERR_MSG_TYPE; goto cleanup; } /* Unfortunately, Heimdal at least up through 1.2 encrypts using the session key not the subsession key. So we try both. */ retval = krb5int_decode_tgs_rep(context, fast_state, response_data, subkey, KRB5_KEYUSAGE_TGS_REP_ENCPART_SUBKEY, &dec_rep); if (retval) { TRACE_TGS_REPLY_DECODE_SESSION(context, &tkt->keyblock); if ((krb5int_decode_tgs_rep(context, fast_state, response_data, &tkt->keyblock, KRB5_KEYUSAGE_TGS_REP_ENCPART_SESSKEY, &dec_rep)) == 0) retval = 0; else goto cleanup; } if (dec_rep->msg_type != KRB5_TGS_REP) { retval = KRB5KRB_AP_ERR_MSG_TYPE; goto cleanup; } /* * Don't trust the ok-as-delegate flag from foreign KDCs unless the * cross-realm TGT also had the ok-as-delegate flag set. */ if (!tgt_is_local_realm(tkt) && !(tkt->ticket_flags & TKT_FLG_OK_AS_DELEGATE)) dec_rep->enc_part2->flags &= ~TKT_FLG_OK_AS_DELEGATE; /* make sure the response hasn't been tampered with..... */ retval = 0; if (s4u2self && !IS_TGS_PRINC(dec_rep->ticket->server)) { /* Final hop, check whether KDC supports S4U2Self */ if (krb5_principal_compare(context, dec_rep->client, in_cred->server)) retval = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; } else if ((kdcoptions & KDC_OPT_CNAME_IN_ADDL_TKT) == 0) { /* XXX for constrained delegation this check must be performed by caller * as we don't have access to the key to decrypt the evidence ticket. */ if (!krb5_principal_compare(context, dec_rep->client, tkt->client)) retval = KRB5_KDCREP_MODIFIED; } if (retval == 0) retval = check_reply_server(context, kdcoptions, in_cred, dec_rep); if (dec_rep->enc_part2->nonce != nonce) retval = KRB5_KDCREP_MODIFIED; if ((kdcoptions & KDC_OPT_POSTDATED) && (in_cred->times.starttime != 0) && (in_cred->times.starttime != dec_rep->enc_part2->times.starttime)) retval = KRB5_KDCREP_MODIFIED; if ((in_cred->times.endtime != 0) && (dec_rep->enc_part2->times.endtime > in_cred->times.endtime)) retval = KRB5_KDCREP_MODIFIED; if ((kdcoptions & KDC_OPT_RENEWABLE) && (in_cred->times.renew_till != 0) && (dec_rep->enc_part2->times.renew_till > in_cred->times.renew_till)) retval = KRB5_KDCREP_MODIFIED; if ((kdcoptions & KDC_OPT_RENEWABLE_OK) && (dec_rep->enc_part2->flags & KDC_OPT_RENEWABLE) && (in_cred->times.endtime != 0) && (dec_rep->enc_part2->times.renew_till > in_cred->times.endtime)) retval = KRB5_KDCREP_MODIFIED; if (retval != 0) goto cleanup; if (!in_cred->times.starttime && !in_clock_skew(dec_rep->enc_part2->times.starttime, timestamp)) { retval = KRB5_KDCREP_SKEW; goto cleanup; } if (out_padata != NULL) { *out_padata = dec_rep->padata; dec_rep->padata = NULL; } if (out_enc_padata != NULL) { *out_enc_padata = dec_rep->enc_part2->enc_padata; dec_rep->enc_part2->enc_padata = NULL; } retval = kdcrep2creds(context, dec_rep, address, &in_cred->second_ticket, out_cred); if (retval != 0) goto cleanup; cleanup: if (dec_rep != NULL) { memset(dec_rep->enc_part2->session->contents, 0, dec_rep->enc_part2->session->length); krb5_free_kdc_rep(context, dec_rep); } return retval; }