krb5_error_code KRB5_CALLCONV krb5_tkt_creds_get(krb5_context context, krb5_tkt_creds_context ctx) { krb5_error_code code; krb5_data request = empty_data(), reply = empty_data(); krb5_data realm = empty_data(); unsigned int flags = 0; int tcp_only = 0, use_master; for (;;) { /* Get the next request and realm. Turn on TCP if necessary. */ code = krb5_tkt_creds_step(context, ctx, &reply, &request, &realm, &flags); if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !tcp_only) { TRACE_TKT_CREDS_RETRY_TCP(context); tcp_only = 1; } else if (code != 0 || !(flags & KRB5_TKT_CREDS_STEP_FLAG_CONTINUE)) break; krb5_free_data_contents(context, &reply); /* Send it to a KDC for the appropriate realm. */ use_master = 0; code = krb5_sendto_kdc(context, &request, &realm, &reply, &use_master, tcp_only); if (code != 0) break; krb5_free_data_contents(context, &request); krb5_free_data_contents(context, &realm); } krb5_free_data_contents(context, &request); krb5_free_data_contents(context, &reply); krb5_free_data_contents(context, &realm); return code; }
static krb5_error_code digest_request(krb5_context context, krb5_realm realm, krb5_ccache ccache, krb5_key_usage usage, const DigestReqInner *ireq, DigestRepInner *irep) { DigestREQ req; DigestREP rep; krb5_error_code ret; krb5_data data, data2; size_t size = 0; krb5_crypto crypto = NULL; krb5_auth_context ac = NULL; krb5_principal principal = NULL; krb5_ccache id = NULL; krb5_realm r = NULL; krb5_data_zero(&data); krb5_data_zero(&data2); memset(&req, 0, sizeof(req)); memset(&rep, 0, sizeof(rep)); if (ccache == NULL) { ret = krb5_cc_default(context, &id); if (ret) goto out; } else id = ccache; if (realm == NULL) { ret = krb5_get_default_realm(context, &r); if (ret) goto out; } else r = realm; /* * */ ret = krb5_make_principal(context, &principal, r, KRB5_DIGEST_NAME, r, NULL); if (ret) goto out; ASN1_MALLOC_ENCODE(DigestReqInner, data.data, data.length, ireq, &size, ret); if (ret) { krb5_set_error_message(context, ret, N_("Failed to encode digest inner request", "")); goto out; } if (size != data.length) krb5_abortx(context, "ASN.1 internal encoder error"); ret = krb5_mk_req_exact(context, &ac, AP_OPTS_USE_SUBKEY|AP_OPTS_MUTUAL_REQUIRED, principal, NULL, id, &req.apReq); if (ret) goto out; { krb5_keyblock *key; ret = krb5_auth_con_getlocalsubkey(context, ac, &key); if (ret) goto out; if (key == NULL) { ret = EINVAL; krb5_set_error_message(context, ret, N_("Digest failed to get local subkey", "")); goto out; } ret = krb5_crypto_init(context, key, 0, &crypto); krb5_free_keyblock (context, key); if (ret) goto out; } ret = krb5_encrypt_EncryptedData(context, crypto, usage, data.data, data.length, 0, &req.innerReq); if (ret) goto out; krb5_data_free(&data); ASN1_MALLOC_ENCODE(DigestREQ, data.data, data.length, &req, &size, ret); if (ret) { krb5_set_error_message(context, ret, N_("Failed to encode DigestREQest", "")); goto out; } if (size != data.length) krb5_abortx(context, "ASN.1 internal encoder error"); ret = krb5_sendto_kdc(context, &data, &r, &data2); if (ret) goto out; ret = decode_DigestREP(data2.data, data2.length, &rep, NULL); if (ret) { krb5_set_error_message(context, ret, N_("Failed to parse digest response", "")); goto out; } { krb5_ap_rep_enc_part *repl; ret = krb5_rd_rep(context, ac, &rep.apRep, &repl); if (ret) goto out; krb5_free_ap_rep_enc_part(context, repl); } { krb5_keyblock *key; ret = krb5_auth_con_getremotesubkey(context, ac, &key); if (ret) goto out; if (key == NULL) { ret = EINVAL; krb5_set_error_message(context, ret, N_("Digest reply have no remote subkey", "")); goto out; } krb5_crypto_destroy(context, crypto); ret = krb5_crypto_init(context, key, 0, &crypto); krb5_free_keyblock (context, key); if (ret) goto out; } krb5_data_free(&data); ret = krb5_decrypt_EncryptedData(context, crypto, usage, &rep.innerRep, &data); if (ret) goto out; ret = decode_DigestRepInner(data.data, data.length, irep, NULL); if (ret) { krb5_set_error_message(context, ret, N_("Failed to decode digest inner reply", "")); goto out; } out: if (ccache == NULL && id) krb5_cc_close(context, id); if (realm == NULL && r) free(r); if (crypto) krb5_crypto_destroy(context, crypto); if (ac) krb5_auth_con_free(context, ac); if (principal) krb5_free_principal(context, principal); krb5_data_free(&data); krb5_data_free(&data2); free_DigestREQ(&req); free_DigestREP(&rep); return ret; }
/* * Parse the IAKERB token in input_token and send the contained KDC * request to the KDC for the realm. * * Wrap the KDC reply in output_token. */ static krb5_error_code iakerb_acceptor_step(iakerb_ctx_id_t ctx, int initialContextToken, const gss_buffer_t input_token, gss_buffer_t output_token) { krb5_error_code code; krb5_data request = empty_data(), reply = empty_data(); krb5_data realm = empty_data(); OM_uint32 tmp; int tcp_only, use_master; krb5_ui_4 kdc_code; output_token->length = 0; output_token->value = NULL; if (ctx->count >= IAKERB_MAX_HOPS) { code = KRB5_KDC_UNREACH; goto cleanup; } code = iakerb_parse_token(ctx, initialContextToken, input_token, &realm, NULL, &request); if (code != 0) goto cleanup; if (realm.length == 0 || request.length == 0) { code = KRB5_BAD_MSIZE; goto cleanup; } code = iakerb_save_token(ctx, input_token); if (code != 0) goto cleanup; for (tcp_only = 0; tcp_only <= 1; tcp_only++) { use_master = 0; code = krb5_sendto_kdc(ctx->k5c, &request, &realm, &reply, &use_master, tcp_only); if (code == 0 && krb5_is_krb_error(&reply)) { krb5_error *error; code = decode_krb5_error(&reply, &error); if (code != 0) goto cleanup; kdc_code = error->error; krb5_free_error(ctx->k5c, error); if (kdc_code == KRB_ERR_RESPONSE_TOO_BIG) { krb5_free_data_contents(ctx->k5c, &reply); reply = empty_data(); continue; } } break; } if (code == KRB5_KDC_UNREACH || code == KRB5_REALM_UNKNOWN) { krb5_error error; memset(&error, 0, sizeof(error)); if (code == KRB5_KDC_UNREACH) error.error = KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE; else if (code == KRB5_REALM_UNKNOWN) error.error = KRB_AP_ERR_IAKERB_KDC_NOT_FOUND; code = krb5_mk_error(ctx->k5c, &error, &reply); if (code != 0) goto cleanup; } else if (code != 0) goto cleanup; code = iakerb_make_token(ctx, &realm, NULL, &reply, 0, output_token); if (code != 0) goto cleanup; code = iakerb_save_token(ctx, output_token); if (code != 0) goto cleanup; ctx->count++; cleanup: if (code != 0) gss_release_buffer(&tmp, output_token); /* request is a pointer into input_token, no need to free */ krb5_free_data_contents(ctx->k5c, &realm); krb5_free_data_contents(ctx->k5c, &reply); return code; }
krb5_error_code krb5_get_cred_via_tkt_ext(krb5_context context, krb5_creds *tkt, krb5_flags kdcoptions, krb5_address *const *address, krb5_pa_data **in_padata, krb5_creds *in_cred, k5_pacb_fn pacb_fn, void *pacb_data, krb5_pa_data ***out_padata, krb5_pa_data ***out_enc_padata, krb5_creds **out_cred, krb5_keyblock **out_subkey) { krb5_error_code retval; krb5_data request_data; krb5_data response_data; krb5_timestamp timestamp; krb5_int32 nonce; krb5_keyblock *subkey = NULL; int tcp_only = 0, use_master = 0; struct krb5int_fast_request_state *fast_state = NULL; request_data.data = NULL; request_data.length = 0; response_data.data = NULL; response_data.length = 0; retval = krb5int_fast_make_state(context, &fast_state); if (retval) goto cleanup; TRACE_GET_CRED_VIA_TKT_EXT(context, in_cred->server, tkt->server, kdcoptions); retval = k5_make_tgs_req(context, fast_state, tkt, kdcoptions, address, in_padata, in_cred, pacb_fn, pacb_data, &request_data, ×tamp, &nonce, &subkey); if (retval != 0) goto cleanup; send_again: use_master = 0; retval = krb5_sendto_kdc(context, &request_data, &in_cred->server->realm, &response_data, &use_master, tcp_only); if (retval == 0) { if (krb5_is_krb_error(&response_data)) { if (!tcp_only) { krb5_error *err_reply; 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; if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) { tcp_only = 1; krb5_free_error(context, err_reply); krb5_free_data_contents(context, &response_data); goto send_again; } krb5_free_error(context, err_reply); } } } else goto cleanup; retval = krb5int_process_tgs_reply(context, fast_state, &response_data, tkt, kdcoptions, address, in_padata, in_cred, timestamp, nonce, subkey, out_padata, out_enc_padata, out_cred); if (retval != 0) goto cleanup; cleanup: krb5int_fast_free_state(context, fast_state); TRACE_GET_CRED_VIA_TKT_EXT_RETURN(context, retval); krb5_free_data_contents(context, &request_data); krb5_free_data_contents(context, &response_data); if (subkey != NULL) { if (retval == 0 && out_subkey != NULL) *out_subkey = subkey; else krb5_free_keyblock(context, subkey); } return retval; }
krb5_error_code krb5_get_cred_via_tkt_ext(krb5_context context, krb5_creds *tkt, krb5_flags kdcoptions, krb5_address *const *address, krb5_pa_data **in_padata, krb5_creds *in_cred, krb5_error_code (*pacb_fct)(krb5_context, krb5_keyblock *, krb5_kdc_req *, void *), void *pacb_data, krb5_pa_data ***out_padata, krb5_pa_data ***out_enc_padata, krb5_creds **out_cred, krb5_keyblock **out_subkey) { krb5_error_code retval; krb5_data request_data; krb5_data response_data; krb5_timestamp timestamp; krb5_int32 nonce; krb5_keyblock *subkey = NULL; int tcp_only = 0, use_master = 0; request_data.data = NULL; request_data.length = 0; response_data.data = NULL; response_data.length = 0; #ifdef DEBUG_REFERRALS printf("krb5_get_cred_via_tkt starting; referral flag is %s\n", kdcoptions&KDC_OPT_CANONICALIZE?"on":"off"); krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt requested ticket", in_cred->server); krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt TGT in use", tkt->server); #endif retval = krb5int_make_tgs_request(context, tkt, kdcoptions, address, in_padata, in_cred, pacb_fct, pacb_data, &request_data, ×tamp, &nonce, &subkey); if (retval != 0) goto cleanup; send_again: use_master = 0; retval = krb5_sendto_kdc(context, &request_data, krb5_princ_realm(context, in_cred->server), &response_data, &use_master, tcp_only); if (retval == 0) { if (krb5_is_krb_error(&response_data)) { if (!tcp_only) { krb5_error *err_reply; retval = decode_krb5_error(&response_data, &err_reply); if (retval != 0) goto cleanup; if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) { tcp_only = 1; krb5_free_error(context, err_reply); krb5_free_data_contents(context, &response_data); goto send_again; } krb5_free_error(context, err_reply); } } } else goto cleanup; retval = krb5int_process_tgs_reply(context, &response_data, tkt, kdcoptions, address, in_padata, in_cred, timestamp, nonce, subkey, out_padata, out_enc_padata, out_cred); if (retval != 0) goto cleanup; cleanup: #ifdef DEBUG_REFERRALS printf("krb5_get_cred_via_tkt ending; %s\n", retval?error_message(retval):"no error"); #endif krb5_free_data_contents(context, &request_data); krb5_free_data_contents(context, &response_data); if (subkey != NULL) { if (retval == 0 && out_subkey != NULL) *out_subkey = subkey; else krb5_free_keyblock(context, subkey); } return retval; }
krb5_error_code KRB5_LIB_FUNCTION krb5_get_in_cred(krb5_context context, krb5_flags options, const krb5_addresses *addrs, const krb5_enctype *etypes, const krb5_preauthtype *ptypes, const krb5_preauthdata *preauth, krb5_key_proc key_proc, krb5_const_pointer keyseed, krb5_decrypt_proc decrypt_proc, krb5_const_pointer decryptarg, krb5_creds *creds, krb5_kdc_rep *ret_as_reply) { krb5_error_code ret; AS_REQ a; krb5_kdc_rep rep; krb5_data req, resp; size_t len; krb5_salt salt; krb5_keyblock *key; size_t size; KDCOptions opts; PA_DATA *pa; krb5_enctype etype; krb5_preauthdata *my_preauth = NULL; unsigned nonce; int done; opts = int2KDCOptions(options); krb5_generate_random_block (&nonce, sizeof(nonce)); nonce &= 0xffffffff; do { done = 1; ret = init_as_req (context, opts, creds, addrs, etypes, ptypes, preauth, key_proc, keyseed, nonce, &a); if (my_preauth) { free_ETYPE_INFO(&my_preauth->val[0].info); free (my_preauth->val); my_preauth = NULL; } if (ret) return ret; ASN1_MALLOC_ENCODE(AS_REQ, req.data, req.length, &a, &len, ret); free_AS_REQ(&a); if (ret) return ret; if(len != req.length) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_sendto_kdc (context, &req, &creds->client->realm, &resp); krb5_data_free(&req); if (ret) return ret; memset (&rep, 0, sizeof(rep)); ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size); if(ret) { /* let's try to parse it as a KRB-ERROR */ KRB_ERROR error; int ret2; ret2 = krb5_rd_error(context, &resp, &error); if(ret2 && resp.data && ((char*)resp.data)[0] == 4) ret = KRB5KRB_AP_ERR_V4_REPLY; krb5_data_free(&resp); if (ret2 == 0) { ret = krb5_error_from_rd_error(context, &error, creds); /* if no preauth was set and KDC requires it, give it one more try */ if (!ptypes && !preauth && ret == KRB5KDC_ERR_PREAUTH_REQUIRED #if 0 || ret == KRB5KDC_ERR_BADOPTION #endif && set_ptypes(context, &error, &ptypes, &my_preauth)) { done = 0; preauth = my_preauth; krb5_free_error_contents(context, &error); krb5_clear_error_string(context); continue; } if(ret_as_reply) ret_as_reply->error = error; else free_KRB_ERROR (&error); return ret; } return ret; } krb5_data_free(&resp); } while(!done); pa = NULL; etype = rep.kdc_rep.enc_part.etype; if(rep.kdc_rep.padata){ int i = 0; pa = krb5_find_padata(rep.kdc_rep.padata->val, rep.kdc_rep.padata->len, KRB5_PADATA_PW_SALT, &i); if(pa == NULL) { i = 0; pa = krb5_find_padata(rep.kdc_rep.padata->val, rep.kdc_rep.padata->len, KRB5_PADATA_AFS3_SALT, &i); } } if(pa) { salt.salttype = pa->padata_type; salt.saltvalue = pa->padata_value; ret = (*key_proc)(context, etype, salt, keyseed, &key); } else { /* make a v5 salted pa-data */ ret = krb5_get_pw_salt (context, creds->client, &salt); if (ret) goto out; ret = (*key_proc)(context, etype, salt, keyseed, &key); krb5_free_salt(context, salt); } if (ret) goto out; { unsigned flags = 0; if (opts.request_anonymous) flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH; ret = _krb5_extract_ticket(context, &rep, creds, key, keyseed, KRB5_KU_AS_REP_ENC_PART, NULL, nonce, flags, decrypt_proc, decryptarg); } memset (key->keyvalue.data, 0, key->keyvalue.length); krb5_free_keyblock_contents (context, key); free (key); out: if (ret == 0 && ret_as_reply) *ret_as_reply = rep; else krb5_free_kdc_rep (context, &rep); return ret; }