int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_pac pac; krb5_data data; krb5_principal p; ret = krb5_init_context(&context); if (ret) err(NULL, 0, "krb5_init_contex"); krb5_set_default_realm(context, "WIN2K3.THINKER.LOCAL"); ret = krb5_parse_name(context, user, &p); if (ret) err(context, ret, "krb5_parse_name"); ret = krb5_pac_parse(context, saved_pac, sizeof(saved_pac), &pac); if (ret) err(context, ret, "krb5_pac_parse"); ret = krb5_pac_verify(context, pac, authtime, p, &member_keyblock, &kdc_keyblock); if (ret) err(context, ret, "krb5_pac_verify"); ret = krb5int_pac_sign(context, pac, authtime, p, &member_keyblock, &kdc_keyblock, &data); if (ret) err(context, ret, "krb5int_pac_sign"); krb5_pac_free(context, pac); ret = krb5_pac_parse(context, data.data, data.length, &pac); krb5_free_data_contents(context, &data); if (ret) err(context, ret, "krb5_pac_parse 2"); ret = krb5_pac_verify(context, pac, authtime, p, &member_keyblock, &kdc_keyblock); if (ret) err(context, ret, "krb5_pac_verify 2"); /* make a copy and try to reproduce it */ { uint32_t *list; size_t len, i; krb5_pac pac2; ret = krb5_pac_init(context, &pac2); if (ret) err(context, ret, "krb5_pac_init"); /* our two user buffer plus the three "system" buffers */ ret = krb5_pac_get_types(context, pac, &len, &list); if (ret) err(context, ret, "krb5_pac_get_types"); for (i = 0; i < len; i++) { /* skip server_cksum, privsvr_cksum, and logon_name */ if (list[i] == 6 || list[i] == 7 || list[i] == 10) continue; ret = krb5_pac_get_buffer(context, pac, list[i], &data); if (ret) err(context, ret, "krb5_pac_get_buffer"); if (list[i] == 1) { if (type_1_length != data.length) err(context, 0, "type 1 have wrong length: %lu", (unsigned long)data.length); } else err(context, 0, "unknown type %lu", (unsigned long)list[i]); ret = krb5_pac_add_buffer(context, pac2, list[i], &data); if (ret) err(context, ret, "krb5_pac_add_buffer"); krb5_free_data_contents(context, &data); } free(list); ret = krb5int_pac_sign(context, pac2, authtime, p, &member_keyblock, &kdc_keyblock, &data); if (ret) err(context, ret, "krb5int_pac_sign 4"); krb5_pac_free(context, pac2); ret = krb5_pac_parse(context, data.data, data.length, &pac2); if (ret) err(context, ret, "krb5_pac_parse 4"); ret = krb5_pac_verify(context, pac2, authtime, p, &member_keyblock, &kdc_keyblock); if (ret) err(context, ret, "krb5_pac_verify 4"); krb5_free_data_contents(context, &data); krb5_pac_free(context, pac2); } krb5_pac_free(context, pac); /* * Test empty free */ ret = krb5_pac_init(context, &pac); if (ret) err(context, ret, "krb5_pac_init"); krb5_pac_free(context, pac); /* * Test add remove buffer */ ret = krb5_pac_init(context, &pac); if (ret) err(context, ret, "krb5_pac_init"); { const krb5_data cdata = { 0, 2, "\x00\x01" } ; ret = krb5_pac_add_buffer(context, pac, 1, &cdata); if (ret) err(context, ret, "krb5_pac_add_buffer"); } { ret = krb5_pac_get_buffer(context, pac, 1, &data); if (ret) err(context, ret, "krb5_pac_get_buffer"); if (data.length != 2 || memcmp(data.data, "\x00\x01", 2) != 0) err(context, 0, "krb5_pac_get_buffer data not the same"); krb5_free_data_contents(context, &data); } { const krb5_data cdata = { 0, 2, "\x02\x00" } ; ret = krb5_pac_add_buffer(context, pac, 2, &cdata); if (ret) err(context, ret, "krb5_pac_add_buffer"); } { ret = krb5_pac_get_buffer(context, pac, 1, &data); if (ret) err(context, ret, "krb5_pac_get_buffer"); if (data.length != 2 || memcmp(data.data, "\x00\x01", 2) != 0) err(context, 0, "krb5_pac_get_buffer data not the same"); krb5_free_data_contents(context, &data); /* */ ret = krb5_pac_get_buffer(context, pac, 2, &data); if (ret) err(context, ret, "krb5_pac_get_buffer"); if (data.length != 2 || memcmp(data.data, "\x02\x00", 2) != 0) err(context, 0, "krb5_pac_get_buffer data not the same"); krb5_free_data_contents(context, &data); } ret = krb5int_pac_sign(context, pac, authtime, p, &member_keyblock, &kdc_keyblock, &data); if (ret) err(context, ret, "krb5int_pac_sign"); krb5_pac_free(context, pac); ret = krb5_pac_parse(context, data.data, data.length, &pac); krb5_free_data_contents(context, &data); if (ret) err(context, ret, "krb5_pac_parse 3"); ret = krb5_pac_verify(context, pac, authtime, p, &member_keyblock, &kdc_keyblock); if (ret) err(context, ret, "krb5_pac_verify 3"); { uint32_t *list; size_t len; /* our two user buffer plus the three "system" buffers */ ret = krb5_pac_get_types(context, pac, &len, &list); if (ret) err(context, ret, "krb5_pac_get_types"); if (len != 5) err(context, 0, "list wrong length"); free(list); } krb5_pac_free(context, pac); krb5_free_principal(context, p); krb5_free_context(context); return 0; }
/* Advance the referral request loop. */ static krb5_error_code step_referrals(krb5_context context, krb5_tkt_creds_context ctx) { krb5_error_code code; const krb5_data *referral_realm; /* Possibly retry with the fallback realm on error. */ if (ctx->reply_code != 0) return try_fallback_realm(context, ctx); if (krb5_principal_compare(context, ctx->reply_creds->server, ctx->server)) { /* We got the ticket we asked for... but we didn't necessarily ask for * it with the right enctypes. Try a non-referral request if so. */ if (wrong_enctype(context, ctx->reply_creds->keyblock.enctype)) { TRACE_TKT_CREDS_WRONG_ENCTYPE(context); return begin_non_referral(context, ctx); } return complete(context, ctx); } /* Old versions of Active Directory can rewrite the server name instead of * returning a referral. Try a non-referral query if we see this. */ if (!IS_TGS_PRINC(context, ctx->reply_creds->server)) { TRACE_TKT_CREDS_NON_TGT(context, ctx->reply_creds->server); return begin_non_referral(context, ctx); } if (ctx->referral_count == 1) { /* Cache the referral TGT only if it's from the local realm. * Make sure to note the associated authdata, if any. */ code = krb5_copy_authdata(context, ctx->authdata, &ctx->reply_creds->authdata); if (code != 0) return code; (void) krb5_cc_store_cred(context, ctx->ccache, ctx->reply_creds); /* The authdata in this TGT will be copied into subsequent TGTs or the * final credentials, so we don't need to request it again. */ krb5_free_authdata(context, ctx->in_creds->authdata); ctx->in_creds->authdata = NULL; } /* Give up if we've gotten too many referral TGTs. */ if (ctx->referral_count++ >= KRB5_REFERRAL_MAXHOPS) return KRB5_KDC_UNREACH; /* Check for referral loops. */ referral_realm = &ctx->reply_creds->server->data[1]; if (seen_realm_before(context, ctx, referral_realm)) return KRB5_KDC_UNREACH; code = remember_realm(context, ctx, referral_realm); if (code != 0) return code; /* Use the referral TGT for the next request. */ krb5_free_creds(context, ctx->cur_tgt); ctx->cur_tgt = ctx->reply_creds; ctx->reply_creds = NULL; TRACE_TKT_CREDS_REFERRAL(context, ctx->cur_tgt->server); /* Rewrite the server realm to be the referral realm. */ krb5_free_data_contents(context, &ctx->server->realm); code = krb5int_copy_data_contents(context, referral_realm, &ctx->server->realm); if (code != 0) return code; /* Generate the next referral request. */ return make_request_for_service(context, ctx, TRUE); }
/* Set etype info parameters in rock based on padata. */ static krb5_error_code get_etype_info(krb5_context context, krb5_pa_data **padata, krb5_kdc_req *request, krb5_clpreauth_rock rock) { krb5_error_code ret = 0; krb5_pa_data *pa; krb5_data d; krb5_etype_info etype_info = NULL, e; krb5_etype_info_entry *entry; krb5_boolean valid_found; int i; /* Find an etype-info2 or etype-info element in padata. */ pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO2); if (pa != NULL) { d = padata2data(*pa); (void)decode_krb5_etype_info2(&d, &etype_info); } else { pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO); if (pa != NULL) { d = padata2data(*pa); (void)decode_krb5_etype_info(&d, &etype_info); } } /* Fall back to pw-salt/afs3-salt if no etype-info element is present. */ if (etype_info == NULL) return get_salt(context, padata, request, rock); /* Search entries in order of the request's enctype preference. */ entry = NULL; valid_found = FALSE; for (i = 0; i < request->nktypes && entry == NULL; i++) { for (e = etype_info; *e != NULL && entry == NULL; e++) { if ((*e)->etype == request->ktype[i]) entry = *e; if (krb5_c_valid_enctype((*e)->etype)) valid_found = TRUE; } } if (entry == NULL) { ret = (valid_found) ? KRB5_CONFIG_ETYPE_NOSUPP : KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } /* Set rock fields based on the entry we selected. */ *rock->etype = entry->etype; krb5_free_data_contents(context, rock->salt); if (entry->length != KRB5_ETYPE_NO_SALT) { *rock->salt = make_data(entry->salt, entry->length); entry->salt = NULL; *rock->default_salt = FALSE; } else { *rock->salt = empty_data(); *rock->default_salt = TRUE; } krb5_free_data_contents(context, rock->s2kparams); *rock->s2kparams = entry->s2kparams; entry->s2kparams = empty_data(); TRACE_PREAUTH_ETYPE_INFO(context, *rock->etype, rock->salt, rock->s2kparams); cleanup: krb5_free_etype_info(context, etype_info); return ret; }
/* Construct a cookie pa-data item using the cookie values from state, or a * trivial "MIT" cookie if no values are set. */ krb5_error_code kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state, krb5_db_entry *local_tgt, krb5_const_principal client_princ, krb5_pa_data **cookie_out) { krb5_error_code ret; krb5_secure_cookie cookie; krb5_pa_data **contents = state->out_cookie_padata, *pa; krb5_keyblock *key = NULL; krb5_timestamp now; krb5_enc_data enc; krb5_data *der_cookie = NULL; krb5_kvno kvno; size_t ctlen; *cookie_out = NULL; memset(&enc, 0, sizeof(enc)); /* Make a trivial cookie if there are no contents to marshal or we don't * have a TGT entry to encrypt them. */ if (contents == NULL || *contents == NULL || local_tgt == NULL) return make_padata(KRB5_PADATA_FX_COOKIE, "MIT", 3, cookie_out); ret = get_cookie_key(context, local_tgt, 0, client_princ, &key, &kvno); if (ret) goto cleanup; /* Encode the cookie. */ ret = krb5_timeofday(context, &now); if (ret) goto cleanup; cookie.time = now; cookie.data = contents; ret = encode_krb5_secure_cookie(&cookie, &der_cookie); if (ret) goto cleanup; /* Encrypt the cookie in key. */ ret = krb5_c_encrypt_length(context, key->enctype, der_cookie->length, &ctlen); if (ret) goto cleanup; ret = alloc_data(&enc.ciphertext, ctlen); if (ret) goto cleanup; ret = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL, der_cookie, &enc); if (ret) goto cleanup; /* Construct the cookie pa-data entry. */ ret = alloc_padata(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length, &pa); memcpy(pa->contents, "MIT1", 4); store_32_be(kvno, pa->contents + 4); memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length); *cookie_out = pa; cleanup: krb5_free_keyblock(context, key); if (der_cookie != NULL) { zapfree(der_cookie->data, der_cookie->length); free(der_cookie); } krb5_free_data_contents(context, &enc.ciphertext); return ret; }
static void otp_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_verify_respond_fn respond, void *arg) { krb5_keyblock *armor_key = NULL; krb5_pa_otp_req *req = NULL; struct request_state *rs; krb5_error_code retval; krb5_data d, plaintext; char *config; enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; /* Get the FAST armor key. */ armor_key = cb->fast_armor(context, rock); if (armor_key == NULL) { retval = KRB5KDC_ERR_PREAUTH_FAILED; com_err("otp", retval, "No armor key found when verifying padata"); goto error; } /* Decode the request. */ d = make_data(pa->contents, pa->length); retval = decode_krb5_pa_otp_req(&d, &req); if (retval != 0) { com_err("otp", retval, "Unable to decode OTP request"); goto error; } /* Decrypt the nonce from the request. */ retval = decrypt_encdata(context, armor_key, req, &plaintext); if (retval != 0) { com_err("otp", retval, "Unable to decrypt nonce"); goto error; } /* Verify the nonce or timestamp. */ retval = nonce_verify(context, armor_key, &plaintext); if (retval != 0) retval = timestamp_verify(context, &plaintext); krb5_free_data_contents(context, &plaintext); if (retval != 0) { com_err("otp", retval, "Unable to verify nonce or timestamp"); goto error; } /* Create the request state. */ rs = k5alloc(sizeof(struct request_state), &retval); if (rs == NULL) goto error; rs->arg = arg; rs->respond = respond; /* Get the principal's OTP configuration string. */ retval = cb->get_string(context, rock, "otp", &config); if (retval == 0 && config == NULL) retval = KRB5_PREAUTH_FAILED; if (retval != 0) { free(rs); goto error; } /* Send the request. */ otp_state_verify((otp_state *)moddata, cb->event_context(context, rock), request->client, config, req, on_response, rs); cb->free_string(context, rock, config); k5_free_pa_otp_req(context, req); return; error: k5_free_pa_otp_req(context, req); (*respond)(arg, retval, NULL, NULL, NULL); }
/* * 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; }
static krb5_error_code process_preauth(krb5_context context, void *plugin_context, void *request_context, krb5_get_init_creds_opt *opt, preauth_get_client_data_proc get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *padata, krb5_prompter_fct prompter, void *prompter_data, preauth_get_as_key_proc gak_fct, void *gak_data, krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key, krb5_pa_data ***out_padata) { krb5_error_code retval = 0; krb5_enctype enctype = 0; krb5_keyblock *challenge_key = NULL, *armor_key = NULL; krb5_data *etype_data = NULL; krb5int_access kaccess; if (krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION) != 0) return 0; retval = fast_get_armor_key(context, get_data_proc, rock, &armor_key); if (retval || armor_key == NULL) return 0; retval = get_data_proc(context, rock, krb5plugin_preauth_client_get_etype, &etype_data); if (retval == 0) { enctype = *((krb5_enctype *)etype_data->data); if (as_key->length == 0 ||as_key->enctype != enctype) retval = gak_fct(context, request->client, enctype, prompter, prompter_data, salt, s2kparams, as_key, gak_data); } if (retval == 0 && padata->length) { krb5_enc_data *enc = NULL; krb5_data scratch; scratch.length = padata->length; scratch.data = (char *) padata->contents; retval = krb5_c_fx_cf2_simple(context,armor_key, "kdcchallengearmor", as_key, "challengelongterm", &challenge_key); if (retval == 0) retval =kaccess.decode_enc_data(&scratch, &enc); scratch.data = NULL; if (retval == 0) { scratch.data = malloc(enc->ciphertext.length); scratch.length = enc->ciphertext.length; if (scratch.data == NULL) retval = ENOMEM; } if (retval == 0) retval = krb5_c_decrypt(context, challenge_key, KRB5_KEYUSAGE_ENC_CHALLENGE_KDC, NULL, enc, &scratch); /* * Per draft 11 of the preauth framework, the client MAY but is not * required to actually check the timestamp from the KDC other than to * confirm it decrypts. This code does not perform that check. */ if (scratch.data) krb5_free_data_contents(context, &scratch); if (retval == 0) fast_set_kdc_verified(context, get_data_proc, rock); if (enc) kaccess.free_enc_data(context, enc); } else if (retval == 0) { /*No padata; we send*/ krb5_enc_data enc; krb5_pa_data *pa = NULL; krb5_pa_data **pa_array = NULL; krb5_data *encoded_ts = NULL; krb5_pa_enc_ts ts; enc.ciphertext.data = NULL; retval = krb5_us_timeofday(context, &ts.patimestamp, &ts.pausec); if (retval == 0) retval = kaccess.encode_enc_ts(&ts, &encoded_ts); if (retval == 0) retval = krb5_c_fx_cf2_simple(context, armor_key, "clientchallengearmor", as_key, "challengelongterm", &challenge_key); if (retval == 0) retval = kaccess.encrypt_helper(context, challenge_key, KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT, encoded_ts, &enc); if (encoded_ts) krb5_free_data(context, encoded_ts); encoded_ts = NULL; if (retval == 0) { retval = kaccess.encode_enc_data(&enc, &encoded_ts); krb5_free_data_contents(context, &enc.ciphertext); } if (retval == 0) { pa = calloc(1, sizeof(krb5_pa_data)); if (pa == NULL) retval = ENOMEM; } if (retval == 0) { pa_array = calloc(2, sizeof(krb5_pa_data *)); if (pa_array == NULL) retval = ENOMEM; } if (retval == 0) { pa->length = encoded_ts->length; pa->contents = (unsigned char *) encoded_ts->data; pa->pa_type = KRB5_PADATA_ENCRYPTED_CHALLENGE; free(encoded_ts); encoded_ts = NULL; pa_array[0] = pa; pa = NULL; *out_padata = pa_array; pa_array = NULL; } if (pa) free(pa); if (encoded_ts) krb5_free_data(context, encoded_ts); if (pa_array) free(pa_array); } if (challenge_key) krb5_free_keyblock(context, challenge_key); if (armor_key) krb5_free_keyblock(context, armor_key); if (etype_data != NULL) get_data_proc(context, rock, krb5plugin_preauth_client_free_etype, &etype_data); return retval; }
DWORD LwKrb5InitializeUserLoginCredentials( IN PCSTR pszUserPrincipalName, IN PCSTR pszPassword, IN uid_t uid, IN gid_t gid, IN LW_KRB5_LOGIN_FLAGS Flags, IN PCSTR pszServicePrincipal, IN PCSTR pszServiceRealm, IN PCSTR pszServicePassword, OUT PVOID* ppNdrPacInfo, OUT size_t* pNdrPacInfoSize, OUT PDWORD pdwGoodUntilTime ) { DWORD dwError = 0; krb5_error_code ret = 0; krb5_context ctx = NULL; krb5_ccache cc = NULL; // Free with krb5_free_cred_contents krb5_creds credsRequest = {0}; krb5_creds *pTgsCreds = NULL; krb5_ticket *pTgsTicket = NULL; krb5_ticket *pDecryptedTgs = NULL; krb5_auth_context authContext = NULL; krb5_data apReqPacket = {0}; krb5_keyblock serviceKey = {0}; krb5_data salt = {0}; // Do not free krb5_data machinePassword = {0}; krb5_flags flags = 0; krb5_int32 authcon_flags = 0; BOOLEAN bInLock = FALSE; PCSTR pszTempCacheName = NULL; PSTR pszTempCachePath = NULL; PVOID pNdrPacInfo = NULL; size_t ndrPacInfoSize = 0; DWORD dwGoodUntilTime = 0; ret = krb5_init_context(&ctx); BAIL_ON_KRB_ERROR(ctx, ret); /* Generates a new filed based credentials cache in /tmp. The file will * be owned by root and only accessible by root. */ ret = krb5_cc_new_unique( ctx, "FILE", "hint", &cc); BAIL_ON_KRB_ERROR(ctx, ret); if (Flags & LW_KRB5_LOGIN_FLAG_SMART_CARD) { dwError = LwKrb5GetTgtWithSmartCard( pszUserPrincipalName, pszPassword, krb5_cc_get_name(ctx, cc), &dwGoodUntilTime); } else { dwError = LwKrb5GetTgt( pszUserPrincipalName, pszPassword, krb5_cc_get_name(ctx, cc), &dwGoodUntilTime); } BAIL_ON_LW_ERROR(dwError); ret = krb5_parse_name(ctx, pszServicePrincipal, &credsRequest.server); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_cc_get_principal(ctx, cc, &credsRequest.client); BAIL_ON_KRB_ERROR(ctx, ret); /* Get a TGS for our service using the tgt in the cache */ ret = krb5_get_credentials( ctx, 0, /*no options (not user to user encryption, and not only cached) */ cc, &credsRequest, &pTgsCreds); // Don't trust pTgsCreds on an unsuccessful return // This may be non-zero due to the krb5 libs following referrals // but has been freed in the krb5 libs themselves and any useful // tickets have already been cached. if (ret != 0) { pTgsCreds = NULL; } BAIL_ON_KRB_ERROR(ctx, ret); //No need to store the tgs in the cc. Kerberos does that automatically /* Generate an ap_req message, but don't send it anywhere. Just decode it * immediately. This is the only way to get kerberos to decrypt the tgs * using public APIs */ ret = krb5_mk_req_extended( ctx, &authContext, 0, /* no options necessary */ NULL, /* since this isn't a real ap_req, we don't have any supplemental data to send with it. */ pTgsCreds, &apReqPacket); BAIL_ON_KRB_ERROR(ctx, ret); /* Decode (but not decrypt) the tgs ticket so that we can figure out * which encryption type was used in it. */ ret = krb5_decode_ticket(&pTgsCreds->ticket, &pTgsTicket); /* The TGS ticket is encrypted with the machine password and salted with * the service principal. pszServicePrincipal could probably be used * directly, but it's safer to unparse pTgsCreds->server, because the KDC * sent that to us. */ salt.magic = KV5M_DATA; ret = krb5_unparse_name( ctx, pTgsCreds->server, &salt.data); BAIL_ON_KRB_ERROR(ctx, ret); salt.length = strlen(salt.data); machinePassword.magic = KV5M_DATA; machinePassword.data = (PSTR)pszServicePassword, machinePassword.length = strlen(pszServicePassword), /* Generate a key to decrypt the TGS */ ret = krb5_c_string_to_key( ctx, pTgsTicket->enc_part.enctype, &machinePassword, &salt, &serviceKey); BAIL_ON_KRB_ERROR(ctx, ret); /* Typically krb5_rd_req would decode the AP_REQ using the keytab, but * we don't want to depend on the keytab. As a side effect of kerberos' * user to user authentication support, if a key is explictly set on the * auth context, that key will be used to decrypt the TGS instead of the * keytab. * * By manually generating the key and setting it, we don't require * a keytab. */ if (authContext != NULL) { ret = krb5_auth_con_free(ctx, authContext); BAIL_ON_KRB_ERROR(ctx, ret); } ret = krb5_auth_con_init(ctx, &authContext); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_auth_con_setuseruserkey( ctx, authContext, &serviceKey); BAIL_ON_KRB_ERROR(ctx, ret); /* Disable replay detection which is unnecessary and * can fail when authenticating large numbers of users. */ krb5_auth_con_getflags(ctx, authContext, &authcon_flags); krb5_auth_con_setflags(ctx, authContext, authcon_flags & ~KRB5_AUTH_CONTEXT_DO_TIME); if (pszServiceRealm) { ret = krb5_set_default_realm(ctx, pszServiceRealm); BAIL_ON_KRB_ERROR(ctx, ret); } /* This decrypts the TGS. As a side effect it ensures that the KDC that * the user's TGT came from is in the same realm that the machine was * joined to (this prevents users from spoofing the KDC). */ ret = krb5_rd_req( ctx, &authContext, &apReqPacket, pTgsCreds->server, NULL, /* we're not using the keytab */ &flags, &pDecryptedTgs); BAIL_ON_KRB_ERROR(ctx, ret); dwError = LwKrb5FindPac( ctx, pDecryptedTgs, &serviceKey, &pNdrPacInfo, &ndrPacInfoSize); BAIL_ON_LW_ERROR(dwError); if (Flags & LW_KRB5_LOGIN_FLAG_UPDATE_CACHE) { /* 1. Copy old credentials from the existing user creds cache to * the temporary cache. * 2. Delete the existing creds cache. * 3. Move the temporary cache file into the final path. */ dwError = pthread_mutex_lock(&gLwKrb5State.UserCacheMutex); BAIL_ON_LW_ERROR(dwError); bInLock = TRUE; dwError = LwKrb5CopyFromUserCache( ctx, cc, uid ); BAIL_ON_LW_ERROR(dwError); pszTempCacheName = krb5_cc_get_name(ctx, cc); if (!strncasecmp(pszTempCacheName, "FILE:", sizeof("FILE:")-1)) { pszTempCacheName += sizeof("FILE:") - 1; } dwError = LwAllocateString(pszTempCacheName, &pszTempCachePath); BAIL_ON_LW_ERROR(dwError); krb5_cc_close(ctx, cc); // Just to make sure no one accesses this now invalid pointer cc = NULL; dwError = LwKrb5MoveCCacheToUserPath( ctx, pszTempCachePath, uid, gid); if (dwError != LW_ERROR_SUCCESS) { /* Let the user login, even if we couldn't create the ccache for * them. Possible causes are: * 1. /tmp is readonly * 2. Another user maliciously setup a weird file (such as a * directory) where the ccache would go. * 3. Someone created a ccache in the small window after we delete * the old one and before we move in the new one. */ LW_LOG_WARNING("Unable to set up credentials cache with tgt for uid %ld", (long)uid); dwError = LwRemoveFile(pszTempCachePath); BAIL_ON_LW_ERROR(dwError); } } error: if (dwError) { LW_SAFE_FREE_MEMORY(pNdrPacInfo); ndrPacInfoSize = 0; dwGoodUntilTime = 0; } if (ctx) { // This function skips fields which are NULL krb5_free_cred_contents(ctx, &credsRequest); if (pTgsCreds != NULL) { krb5_free_creds(ctx, pTgsCreds); } if (pTgsTicket != NULL) { krb5_free_ticket(ctx, pTgsTicket); } if (pDecryptedTgs != NULL) { krb5_free_ticket(ctx, pDecryptedTgs); } if (authContext != NULL) { krb5_auth_con_free(ctx, authContext); } krb5_free_data_contents(ctx, &apReqPacket); krb5_free_data_contents(ctx, &salt); krb5_free_keyblock_contents(ctx, &serviceKey); if (cc != NULL) { krb5_cc_destroy(ctx, cc); } krb5_free_context(ctx); } if (bInLock) { pthread_mutex_unlock(&gLwKrb5State.UserCacheMutex); } LW_SAFE_FREE_STRING(pszTempCachePath); *ppNdrPacInfo = pNdrPacInfo; *pNdrPacInfoSize = ndrPacInfoSize; *pdwGoodUntilTime = dwGoodUntilTime; return dwError; }
krb5_error_code KRB5_CALLCONV krb5_verify_init_creds(krb5_context context, krb5_creds *creds, krb5_principal server_arg, krb5_keytab keytab_arg, krb5_ccache *ccache_arg, krb5_verify_init_creds_opt *options) { krb5_error_code ret; krb5_principal server; krb5_keytab keytab; krb5_ccache ccache; krb5_keytab_entry kte; krb5_creds in_creds, *out_creds; krb5_auth_context authcon; krb5_data ap_req; /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */ server = NULL; keytab = NULL; ccache = NULL; out_creds = NULL; authcon = NULL; ap_req.data = NULL; if (keytab_arg) { keytab = keytab_arg; } else { if ((ret = krb5_kt_default(context, &keytab))) goto cleanup; } if (server_arg) { ret = krb5_copy_principal(context, server_arg, &server); if (ret) goto cleanup; } else { /* Use a principal name from the keytab. */ ret = k5_kt_get_principal(context, keytab, &server); if (ret) { /* There's no keytab, or it's empty, or we can't read it. * Allow this unless configuration demands verification. */ if (!nofail(context, options, creds)) ret = 0; goto cleanup; } } /* first, check if the server is in the keytab. If not, there's no reason to continue. rd_req does all this, but there's no way to know that a given error is caused by a missing keytab or key, and not by some other problem. */ if (krb5_is_referral_realm(&server->realm)) { krb5_free_data_contents(context, &server->realm); ret = krb5_get_default_realm(context, &server->realm.data); if (ret) goto cleanup; server->realm.length = strlen(server->realm.data); } if ((ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte))) { /* this means there is no keying material. This is ok, as long as it is not prohibited by the configuration */ if (!nofail(context, options, creds)) ret = 0; goto cleanup; } krb5_kt_free_entry(context, &kte); /* If the creds are for the server principal, we're set, just do a mk_req. * Otherwise, do a get_credentials first. */ if (krb5_principal_compare(context, server, creds->server)) { /* make an ap_req */ if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds, &ap_req))) goto cleanup; } else { /* this is unclean, but it's the easiest way without ripping the library into very small pieces. store the client's initial cred in a memory ccache, then call the library. Later, we'll copy everything except the initial cred into the ccache we return to the user. A clean implementation would involve library internals with a coherent idea of "in" and "out". */ /* insert the initial cred into the ccache */ if ((ret = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) { ccache = NULL; goto cleanup; } if ((ret = krb5_cc_initialize(context, ccache, creds->client))) goto cleanup; if ((ret = krb5_cc_store_cred(context, ccache, creds))) goto cleanup; /* set up for get_creds */ memset(&in_creds, 0, sizeof(in_creds)); in_creds.client = creds->client; in_creds.server = server; if ((ret = krb5_timeofday(context, &in_creds.times.endtime))) goto cleanup; in_creds.times.endtime += 5*60; if ((ret = krb5_get_credentials(context, 0, ccache, &in_creds, &out_creds))) goto cleanup; /* make an ap_req */ if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds, &ap_req))) goto cleanup; } /* wipe the auth context for mk_req */ if (authcon) { krb5_auth_con_free(context, authcon); authcon = NULL; } /* verify the ap_req */ if ((ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab, NULL, NULL))) goto cleanup; /* if we get this far, then the verification succeeded. We can still fail if the library stuff here fails, but that's it */ if (ccache_arg && ccache) { if (*ccache_arg == NULL) { krb5_ccache retcc; retcc = NULL; if ((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) || (ret = krb5_cc_initialize(context, retcc, creds->client)) || (ret = copy_creds_except(context, ccache, retcc, creds->server))) { if (retcc) krb5_cc_destroy(context, retcc); } else { *ccache_arg = retcc; } } else { ret = copy_creds_except(context, ccache, *ccache_arg, server); } } /* if any of the above paths returned an errors, then ret is set accordingly. * Either that, or it's zero, which is fine, too */ cleanup: if ( server) krb5_free_principal(context, server); if (!keytab_arg && keytab) krb5_kt_close(context, keytab); if (ccache) krb5_cc_destroy(context, ccache); if (out_creds) krb5_free_creds(context, out_creds); if (authcon) krb5_auth_con_free(context, authcon); if (ap_req.data) free(ap_req.data); return(ret); }
krb5_error_code krb5int_mk_setpw_req(krb5_context context, krb5_auth_context auth_context, krb5_data *ap_req, krb5_principal targprinc, char *passwd, krb5_data *packet) { krb5_error_code ret; krb5_data cipherpw; krb5_data *encoded_setpw; struct krb5_setpw_req req; char *ptr; cipherpw.data = NULL; cipherpw.length = 0; if ((ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE))) return(ret); req.target = targprinc; req.password.data = passwd; req.password.length = strlen(passwd); ret = encode_krb5_setpw_req(&req, &encoded_setpw); if (ret) { return ret; } if ((ret = krb5_mk_priv(context, auth_context, encoded_setpw, &cipherpw, NULL)) != 0) { krb5_free_data(context, encoded_setpw); return(ret); } krb5_free_data(context, encoded_setpw); packet->length = 6 + ap_req->length + cipherpw.length; packet->data = (char *) malloc(packet->length); if (packet->data == NULL) { ret = ENOMEM; goto cleanup; } ptr = packet->data; /* ** build the packet - */ /* put in the length */ store_16_be(packet->length, ptr); ptr += 2; /* put in the version */ *ptr++ = (char)0xff; *ptr++ = (char)0x80; /* the ap_req length is big endian */ store_16_be(ap_req->length, ptr); ptr += 2; /* put in the request data */ memcpy(ptr, ap_req->data, ap_req->length); ptr += ap_req->length; /* ** put in the "private" password data - */ memcpy(ptr, cipherpw.data, cipherpw.length); ret = 0; cleanup: if (cipherpw.data) krb5_free_data_contents(context, &cipherpw); if ((ret != 0) && packet->data) { free(packet->data); packet->data = NULL; } return ret; }
krb5_error_code KRB5_CALLCONV krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_keytab arg_keytab, krb5_deltat start_time, char *in_tkt_service, krb5_get_init_creds_opt *options) { krb5_error_code ret, ret2; int use_master; krb5_keytab keytab; krb5_gic_opt_ext *opte = NULL; if (arg_keytab == NULL) { if ((ret = krb5_kt_default(context, &keytab))) return ret; } else { keytab = arg_keytab; } ret = krb5int_gic_opt_to_opte(context, options, &opte, 1, "krb5_get_init_creds_keytab"); if (ret) return ret; /* * Solaris Kerberos: * If "client" was constructed from krb5_sname_to_princ() it may * have a referral realm. This happens when there is no applicable * domain-to-realm mapping in the Kerberos configuration file. * If that is the case then the realm of the first principal found * in the keytab which matches the client can be used for the client's * realm. */ if (krb5_is_referral_realm(&client->realm)) { krb5_data realm; ret = krb5_kt_find_realm(context, keytab, client, &realm); if (ret == 0) { krb5_free_data_contents(context, &client->realm); client->realm.length = realm.length; client->realm.data = realm.data; } else { /* Try to set a useful error message */ char *princ = NULL; krb5_unparse_name(context, client, &princ); krb5_set_error_message(context, ret, gettext("Failed to find realm for %s in keytab"), princ ? princ : "<unknown>"); if (princ) krb5_free_unparsed_name(context, princ); } } if (ret != 0) goto cleanup; use_master = 0; /* first try: get the requested tkt from any kdc */ ret = krb5_get_init_creds(context, creds, client, NULL, NULL, start_time, in_tkt_service, opte, krb5_get_as_key_keytab, (void *) keytab, &use_master,NULL); /* check for success */ if (ret == 0) goto cleanup; /* If all the kdc's are unavailable fail */ if ((ret == KRB5_KDC_UNREACH) || (ret == KRB5_REALM_CANT_RESOLVE)) goto cleanup; /* if the reply did not come from the master kdc, try again with the master kdc */ if (!use_master) { use_master = 1; ret2 = krb5_get_init_creds(context, creds, client, NULL, NULL, start_time, in_tkt_service, opte, krb5_get_as_key_keytab, (void *) keytab, &use_master, NULL); if (ret2 == 0) { ret = 0; goto cleanup; } /* if the master is unreachable, return the error from the slave we were able to contact */ if ((ret2 == KRB5_KDC_UNREACH) || (ret2 == KRB5_REALM_CANT_RESOLVE) || (ret2 == KRB5_REALM_UNKNOWN)) goto cleanup; ret = ret2; } /* at this point, we have a response from the master. Since we don't do any prompting or changing for keytabs, that's it. */ cleanup: if (opte && krb5_gic_opt_is_shadowed(opte)) krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte); if (arg_keytab == NULL) (void) krb5_kt_close(context, keytab); /* Solaris Kerberos */ return(ret); }
/* Decode a reply to produce the clear-text output. */ static krb5_error_code get_clear_result(krb5_context context, krb5_auth_context auth_context, const krb5_data *packet, krb5_data **clear_out, krb5_boolean *is_error_out) { krb5_error_code ret; char *ptr, *end = packet->data + packet->length; unsigned int plen, vno, aplen; krb5_data ap_rep, cipher, error; krb5_ap_rep_enc_part *ap_rep_enc; krb5_replay_data replay; krb5_key send_subkey = NULL; krb5_data clear = empty_data(); *clear_out = NULL; *is_error_out = FALSE; /* Check for an unframed KRB-ERROR (expected for RFC 3244 requests; also * received from MS AD for version 1 requests). */ if (krb5_is_krb_error(packet)) { *is_error_out = TRUE; return get_error_edata(context, packet, clear_out); } if (packet->length < 6) return KRB5KRB_AP_ERR_MODIFIED; /* Decode and verify the length. */ ptr = packet->data; plen = (*ptr++ & 0xff); plen = (plen << 8) | (*ptr++ & 0xff); if (plen != packet->length) return KRB5KRB_AP_ERR_MODIFIED; /* Decode and verify the version number. */ vno = (*ptr++ & 0xff); vno = (vno << 8) | (*ptr++ & 0xff); if (vno != 1 && vno != 0xff80) return KRB5KDC_ERR_BAD_PVNO; /* Decode and check the AP-REP length. */ aplen = (*ptr++ & 0xff); aplen = (aplen << 8) | (*ptr++ & 0xff); if (aplen > end - ptr) return KRB5KRB_AP_ERR_MODIFIED; /* A zero-length AP-REQ indicates a framed KRB-ERROR response. (Expected * for protocol version 1; specified but unusual for RFC 3244 requests.) */ if (aplen == 0) { *is_error_out = TRUE; error = make_data(ptr, end - ptr); return get_error_edata(context, &error, clear_out); } /* We have an AP-REP. Save send_subkey to later smash recv_subkey. */ ret = krb5_auth_con_getsendsubkey_k(context, auth_context, &send_subkey); if (ret) return ret; /* Verify the AP-REP. */ ap_rep = make_data(ptr, aplen); ptr += ap_rep.length; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) goto cleanup; krb5_free_ap_rep_enc_part(context, ap_rep_enc); /* Smash recv_subkey to be send_subkey, per spec. */ ret = krb5_auth_con_setrecvsubkey_k(context, auth_context, send_subkey); if (ret) goto cleanup; /* Extract and decrypt the result. */ cipher = make_data(ptr, end - ptr); ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay); if (ret) goto cleanup; ret = krb5_copy_data(context, &clear, clear_out); if (ret) goto cleanup; *is_error_out = FALSE; cleanup: krb5_k_free_key(context, send_subkey); krb5_free_data_contents(context, &clear); return ret; }
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_CALLCONV krb5_mk_priv(krb5_context context, krb5_auth_context auth_context, const krb5_data *userdata, krb5_data *outbuf, krb5_replay_data *outdata) { krb5_error_code retval; krb5_key key; krb5_replay_data replaydata; krb5_data buf = empty_data(); *outbuf = empty_data(); /* Clear replaydata block */ memset(&replaydata, 0, sizeof(krb5_replay_data)); /* Get keyblock */ if ((key = auth_context->send_subkey) == NULL) key = auth_context->key; /* Get replay info */ if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) && (auth_context->rcache == NULL)) return KRB5_RC_REQUIRED; if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && (outdata == NULL)) /* Need a better error */ return KRB5_RC_REQUIRED; if (!auth_context->local_addr) return KRB5_LOCAL_ADDR_REQUIRED; if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME)) { if ((retval = krb5_us_timeofday(context, &replaydata.timestamp, &replaydata.usec))) return retval; if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) { outdata->timestamp = replaydata.timestamp; outdata->usec = replaydata.usec; } } if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) { replaydata.seq = auth_context->local_seq_number++; if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE) outdata->seq = replaydata.seq; } { krb5_address * premote_fulladdr = NULL; krb5_address * plocal_fulladdr; krb5_address remote_fulladdr; krb5_address local_fulladdr; CLEANUP_INIT(2); if (auth_context->local_port) { if (!(retval = krb5_make_fulladdr(context, auth_context->local_addr, auth_context->local_port, &local_fulladdr))) { CLEANUP_PUSH(local_fulladdr.contents, free); plocal_fulladdr = &local_fulladdr; } else { goto error; } } else { plocal_fulladdr = auth_context->local_addr; } if (auth_context->remote_addr) { if (auth_context->remote_port) { if (!(retval = krb5_make_fulladdr(context,auth_context->remote_addr, auth_context->remote_port, &remote_fulladdr))){ CLEANUP_PUSH(remote_fulladdr.contents, free); premote_fulladdr = &remote_fulladdr; } else { CLEANUP_DONE(); goto error; } } else { premote_fulladdr = auth_context->remote_addr; } } if ((retval = mk_priv_basic(context, userdata, key, &replaydata, plocal_fulladdr, premote_fulladdr, auth_context->i_vector, &buf))) { CLEANUP_DONE(); goto error; } CLEANUP_DONE(); } if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_donot_replay replay; if ((retval = krb5_gen_replay_name(context, auth_context->local_addr, "_priv", &replay.client))) goto error; replay.server = ""; /* XXX */ replay.msghash = NULL; replay.cusec = replaydata.usec; replay.ctime = replaydata.timestamp; if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) { /* should we really error out here? XXX */ free(replay.client); goto error; } free(replay.client); } *outbuf = buf; return 0; error: krb5_free_data_contents(context, &buf); if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) auth_context->local_seq_number--; return retval; }
static krb5_error_code sam2_process(krb5_context context, krb5_clpreauth_moddata moddata, krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt, krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *padata, krb5_prompter_fct prompter, void *prompter_data, krb5_pa_data ***out_padata) { krb5_error_code retval; krb5_sam_challenge_2 *sc2 = NULL; krb5_sam_challenge_2_body *sc2b = NULL; krb5_data tmp_data; krb5_data response_data; char name[100], banner[100], prompt[100], response[100]; krb5_prompt kprompt; krb5_prompt_type prompt_type; krb5_data defsalt, *salt; krb5_checksum **cksum; krb5_data *scratch = NULL; krb5_boolean valid_cksum = 0; krb5_enc_sam_response_enc_2 enc_sam_response_enc_2; krb5_sam_response_2 sr2; size_t ciph_len; krb5_pa_data **sam_padata; if (prompter == NULL) return KRB5_LIBOS_CANTREADPWD; tmp_data.length = padata->length; tmp_data.data = (char *)padata->contents; if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2))) return(retval); retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b); if (retval) { krb5_free_sam_challenge_2(context, sc2); return(retval); } if (!sc2->sam_cksum || ! *sc2->sam_cksum) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(KRB5_SAM_NO_CHECKSUM); } if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(KRB5_SAM_UNSUPPORTED); } if (!krb5_c_valid_enctype(sc2b->sam_etype)) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(KRB5_SAM_INVALID_ETYPE); } /* All of the above error checks are KDC-specific, that is, they */ /* assume a failure in the KDC reply. By returning anything other */ /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED, */ /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will */ /* most likely go on to try the AS_REQ against master KDC */ if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { /* We will need the password to obtain the key used for */ /* the checksum, and encryption of the sam_response. */ /* Go ahead and get it now, preserving the ordering of */ /* prompts for the user. */ salt = (*rock->default_salt) ? NULL : rock->salt; retval = (*rock->gak_fct)(context, request->client, sc2b->sam_etype, prompter, prompter_data, rock->salt, rock->s2kparams, rock->as_key, *rock->gak_data, rock->rctx.items); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } } snprintf(name, sizeof(name), "%.*s", SAMDATA(sc2b->sam_type_name, _("SAM Authentication"), sizeof(name) - 1)); snprintf(banner, sizeof(banner), "%.*s", SAMDATA(sc2b->sam_challenge_label, sam_challenge_banner(sc2b->sam_type), sizeof(banner)-1)); snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s", sc2b->sam_challenge.length?"Challenge is [":"", SAMDATA(sc2b->sam_challenge, "", 20), sc2b->sam_challenge.length?"], ":"", SAMDATA(sc2b->sam_response_prompt, "passcode", 55)); response_data.data = response; response_data.length = sizeof(response); kprompt.prompt = prompt; kprompt.hidden = 1; kprompt.reply = &response_data; prompt_type = KRB5_PROMPT_TYPE_PREAUTH; krb5int_set_prompt_types(context, &prompt_type); if ((retval = ((*prompter)(context, prompter_data, name, banner, 1, &kprompt)))) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5int_set_prompt_types(context, 0); return(retval); } krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL); /* Generate salt used by string_to_key() */ if (*rock->default_salt) { if ((retval = krb5_principal2salt(context, request->client, &defsalt))) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } salt = &defsalt; } else { salt = rock->salt; defsalt.length = 0; } /* Get encryption key to be used for checksum and sam_response */ if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { /* as_key = string_to_key(password) */ if (rock->as_key->length) { krb5_free_keyblock_contents(context, rock->as_key); rock->as_key->length = 0; } /* generate a key using the supplied password */ retval = krb5_c_string_to_key(context, sc2b->sam_etype, (krb5_data *)*rock->gak_data, salt, rock->as_key); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); if (defsalt.length) free(defsalt.data); return(retval); } if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) { /* as_key = combine_key (as_key, string_to_key(SAD)) */ krb5_keyblock tmp_kb; retval = krb5_c_string_to_key(context, sc2b->sam_etype, &response_data, salt, &tmp_kb); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); if (defsalt.length) free(defsalt.data); return(retval); } /* This should be a call to the crypto library some day */ /* key types should already match the sam_etype */ retval = krb5int_c_combine_keys(context, rock->as_key, &tmp_kb, rock->as_key); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); if (defsalt.length) free(defsalt.data); return(retval); } krb5_free_keyblock_contents(context, &tmp_kb); } if (defsalt.length) free(defsalt.data); } else { /* as_key = string_to_key(SAD) */ if (rock->as_key->length) { krb5_free_keyblock_contents(context, rock->as_key); rock->as_key->length = 0; } /* generate a key using the supplied password */ retval = krb5_c_string_to_key(context, sc2b->sam_etype, &response_data, salt, rock->as_key); if (defsalt.length) free(defsalt.data); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } } /* Now we have a key, verify the checksum on the sam_challenge */ cksum = sc2->sam_cksum; for (; *cksum; cksum++) { if (!krb5_c_is_keyed_cksum((*cksum)->checksum_type)) continue; /* Check this cksum */ retval = krb5_c_verify_checksum(context, rock->as_key, KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM, &sc2->sam_challenge_2_body, *cksum, &valid_cksum); if (retval) { krb5_free_data(context, scratch); krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } if (valid_cksum) break; } if (!valid_cksum) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); /* * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications * can interpret that as "password incorrect", which is probably * the best error we can return in this situation. */ return(KRB5KRB_AP_ERR_BAD_INTEGRITY); } /* fill in enc_sam_response_enc_2 */ enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2; enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce; if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { enc_sam_response_enc_2.sam_sad = response_data; } else { enc_sam_response_enc_2.sam_sad.data = NULL; enc_sam_response_enc_2.sam_sad.length = 0; } /* encode and encrypt enc_sam_response_enc_2 with as_key */ retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2, &scratch); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } /* Fill in sam_response_2 */ memset(&sr2, 0, sizeof(sr2)); sr2.sam_type = sc2b->sam_type; sr2.sam_flags = sc2b->sam_flags; sr2.sam_track_id = sc2b->sam_track_id; sr2.sam_nonce = sc2b->sam_nonce; /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded */ /* enc_sam_response_enc_2 from above */ retval = krb5_c_encrypt_length(context, rock->as_key->enctype, scratch->length, &ciph_len); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data(context, scratch); return(retval); } sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len; sr2.sam_enc_nonce_or_sad.ciphertext.data = (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length); if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data(context, scratch); return(ENOMEM); } retval = krb5_c_encrypt(context, rock->as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE, NULL, scratch, &sr2.sam_enc_nonce_or_sad); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data(context, scratch); krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); return(retval); } krb5_free_data(context, scratch); scratch = NULL; /* Encode the sam_response_2 */ retval = encode_krb5_sam_response_2(&sr2, &scratch); krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); if (retval) { return (retval); } /* Almost there, just need to make padata ! */ sam_padata = malloc(2 * sizeof(*sam_padata)); if (sam_padata == NULL) { krb5_free_data(context, scratch); return(ENOMEM); } sam_padata[0] = malloc(sizeof(krb5_pa_data)); if (sam_padata[0] == NULL) { krb5_free_data(context, scratch); free(sam_padata); return(ENOMEM); } sam_padata[0]->magic = KV5M_PA_DATA; sam_padata[0]->pa_type = KRB5_PADATA_SAM_RESPONSE_2; sam_padata[0]->length = scratch->length; sam_padata[0]->contents = (krb5_octet *) scratch->data; free(scratch); sam_padata[1] = NULL; *out_padata = sam_padata; return(0); }
static krb5_error_code otp_client_process(krb5_context context, krb5_clpreauth_moddata moddata, krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt, krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *pa_data, krb5_prompter_fct prompter, void *prompter_data, krb5_pa_data ***pa_data_out) { krb5_pa_otp_challenge *chl = NULL; krb5_otp_tokeninfo *ti = NULL; krb5_keyblock *as_key = NULL; krb5_pa_otp_req *req = NULL; krb5_error_code retval = 0; krb5_data value, pin; const char *answer; if (modreq == NULL) return ENOMEM; chl = *(krb5_pa_otp_challenge **)modreq; *pa_data_out = NULL; /* Get FAST armor key. */ as_key = cb->fast_armor(context, rock); if (as_key == NULL) return ENOENT; /* Use FAST armor key as response key. */ retval = cb->set_as_key(context, rock, as_key); if (retval != 0) return retval; /* Attempt to get token selection from the responder. */ pin = empty_data(); value = empty_data(); answer = cb->get_responder_answer(context, rock, KRB5_RESPONDER_QUESTION_OTP); retval = codec_decode_answer(context, answer, chl->tokeninfo, &ti, &value, &pin); if (retval != 0) { /* If the responder doesn't have a token selection, * we need to select the token via prompting. */ retval = prompt_for_token(context, prompter, prompter_data, chl->tokeninfo, &ti, &value, &pin); if (retval != 0) goto error; } /* Make the request. */ retval = make_request(context, ti, &value, &pin, &req); if (retval != 0) goto error; /* Save information about the token which was used. */ save_config_tokeninfo(context, cb, rock, ti); /* Encrypt the challenge's nonce and set it in the request. */ retval = encrypt_nonce(context, as_key, chl, req); if (retval != 0) goto error; /* Encode the request into the pa_data output. */ retval = set_pa_data(req, pa_data_out); error: krb5_free_data_contents(context, &value); krb5_free_data_contents(context, &pin); k5_free_pa_otp_req(context, req); return retval; }
static krb5_error_code ec_process(krb5_context context, krb5_clpreauth_moddata moddata, krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt, krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *padata, krb5_prompter_fct prompter, void *prompter_data, krb5_pa_data ***out_padata) { krb5_error_code retval = 0; krb5_keyblock *challenge_key = NULL, *armor_key, *as_key; armor_key = cb->fast_armor(context, rock); if (armor_key == NULL) return ENOENT; retval = cb->get_as_key(context, rock, &as_key); if (retval == 0 && padata->length) { krb5_enc_data *enc = NULL; krb5_data scratch; scratch.length = padata->length; scratch.data = (char *) padata->contents; retval = krb5_c_fx_cf2_simple(context,armor_key, "kdcchallengearmor", as_key, "challengelongterm", &challenge_key); if (retval == 0) retval = decode_krb5_enc_data(&scratch, &enc); scratch.data = NULL; if (retval == 0) { scratch.data = malloc(enc->ciphertext.length); scratch.length = enc->ciphertext.length; if (scratch.data == NULL) retval = ENOMEM; } if (retval == 0) retval = krb5_c_decrypt(context, challenge_key, KRB5_KEYUSAGE_ENC_CHALLENGE_KDC, NULL, enc, &scratch); /* * Per draft 11 of the preauth framework, the client MAY but is not * required to actually check the timestamp from the KDC other than to * confirm it decrypts. This code does not perform that check. */ if (scratch.data) krb5_free_data_contents(context, &scratch); /* If we had a callback to assert that the KDC is verified, we would * call it here. */ if (enc) krb5_free_enc_data(context, enc); } else if (retval == 0) { /*No padata; we send*/ krb5_enc_data enc; krb5_pa_data **pa = NULL; krb5_data *encoded_ts = NULL; krb5_pa_enc_ts ts; enc.ciphertext.data = NULL; /* Use the timestamp from the preauth-required error if possible. * This time should always be secured by the FAST channel. */ retval = cb->get_preauth_time(context, rock, FALSE, &ts.patimestamp, &ts.pausec); if (retval == 0) retval = encode_krb5_pa_enc_ts(&ts, &encoded_ts); if (retval == 0) retval = krb5_c_fx_cf2_simple(context, armor_key, "clientchallengearmor", as_key, "challengelongterm", &challenge_key); if (retval == 0) retval = krb5_encrypt_helper(context, challenge_key, KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT, encoded_ts, &enc); if (encoded_ts) krb5_free_data(context, encoded_ts); encoded_ts = NULL; if (retval == 0) { retval = encode_krb5_enc_data(&enc, &encoded_ts); krb5_free_data_contents(context, &enc.ciphertext); } if (retval == 0) { pa = calloc(2, sizeof(krb5_pa_data *)); if (pa == NULL) retval = ENOMEM; } if (retval == 0) { pa[0] = calloc(1, sizeof(krb5_pa_data)); if (pa[0] == NULL) retval = ENOMEM; } if (retval == 0) { pa[0]->length = encoded_ts->length; pa[0]->contents = (unsigned char *) encoded_ts->data; pa[0]->pa_type = KRB5_PADATA_ENCRYPTED_CHALLENGE; encoded_ts->data = NULL; *out_padata = pa; pa = NULL; } free(pa); krb5_free_data(context, encoded_ts); } if (challenge_key) krb5_free_keyblock(context, challenge_key); return retval; }
/* Builds a request using the specified tokeninfo, value and pin. */ static krb5_error_code make_request(krb5_context ctx, krb5_otp_tokeninfo *ti, const krb5_data *value, const krb5_data *pin, krb5_pa_otp_req **out_req) { krb5_pa_otp_req *req = NULL; krb5_error_code retval = 0; if (ti == NULL) return 0; if (ti->format == KRB5_OTP_FORMAT_BASE64) return ENOTSUP; req = calloc(1, sizeof(krb5_pa_otp_req)); if (req == NULL) return ENOMEM; req->flags = ti->flags & KRB5_OTP_FLAG_NEXTOTP; retval = krb5int_copy_data_contents(ctx, &ti->vendor, &req->vendor); if (retval != 0) goto error; req->format = ti->format; retval = krb5int_copy_data_contents(ctx, &ti->token_id, &req->token_id); if (retval != 0) goto error; retval = krb5int_copy_data_contents(ctx, &ti->alg_id, &req->alg_id); if (retval != 0) goto error; retval = krb5int_copy_data_contents(ctx, value, &req->otp_value); if (retval != 0) goto error; if (ti->flags & KRB5_OTP_FLAG_COLLECT_PIN) { if (ti->flags & KRB5_OTP_FLAG_SEPARATE_PIN) { if (pin == NULL || pin->data == NULL) { retval = EINVAL; /* No pin found! */ goto error; } retval = krb5int_copy_data_contents(ctx, pin, &req->pin); if (retval != 0) goto error; } else if (pin != NULL && pin->data != NULL) { krb5_free_data_contents(ctx, &req->otp_value); retval = asprintf(&req->otp_value.data, "%.*s%.*s", pin->length, pin->data, value->length, value->data); if (retval < 0) { retval = ENOMEM; req->otp_value = empty_data(); goto error; } req->otp_value.length = req->pin.length + req->otp_value.length; } /* Otherwise, the responder has already combined them. */ } *out_req = req; return 0; error: k5_free_pa_otp_req(ctx, req); return retval; }
/* * Parse the IAKERB token in input_token and process the KDC * response. * * Emit the next KDC request, if any, in output_token. */ static krb5_error_code iakerb_initiator_step(iakerb_ctx_id_t ctx, krb5_gss_cred_id_t cred, krb5_gss_name_t name, OM_uint32 time_req, const gss_buffer_t input_token, gss_buffer_t output_token) { krb5_error_code code = 0; krb5_data in = empty_data(), out = empty_data(), realm = empty_data(); krb5_data *cookie = NULL; OM_uint32 tmp; unsigned int flags = 0; krb5_ticket_times times; output_token->length = 0; output_token->value = NULL; if (input_token != GSS_C_NO_BUFFER) { code = iakerb_parse_token(ctx, 0, input_token, NULL, &cookie, &in); if (code != 0) goto cleanup; code = iakerb_save_token(ctx, input_token); if (code != 0) goto cleanup; } switch (ctx->state) { case IAKERB_AS_REQ: if (ctx->icc == NULL) { code = iakerb_init_creds_ctx(ctx, cred, time_req); if (code != 0) goto cleanup; } code = krb5_init_creds_step(ctx->k5c, ctx->icc, &in, &out, &realm, &flags); if (code != 0) { if (cred->have_tgt) { /* We were trying to refresh; keep going with current creds. */ ctx->state = IAKERB_TGS_REQ; krb5_clear_error_message(ctx->k5c); } else { goto cleanup; } } else if (!(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) { krb5_init_creds_get_times(ctx->k5c, ctx->icc, ×); kg_cred_set_initial_refresh(ctx->k5c, cred, ×); cred->expire = times.endtime; krb5_init_creds_free(ctx->k5c, ctx->icc); ctx->icc = NULL; ctx->state = IAKERB_TGS_REQ; } else break; in = empty_data(); /* Done with AS request; fall through to TGS request. */ case IAKERB_TGS_REQ: if (ctx->tcc == NULL) { code = iakerb_tkt_creds_ctx(ctx, cred, name, time_req); if (code != 0) goto cleanup; } code = krb5_tkt_creds_step(ctx->k5c, ctx->tcc, &in, &out, &realm, &flags); if (code != 0) goto cleanup; if (!(flags & KRB5_TKT_CREDS_STEP_FLAG_CONTINUE)) { krb5_tkt_creds_get_times(ctx->k5c, ctx->tcc, ×); cred->expire = times.endtime; krb5_tkt_creds_free(ctx->k5c, ctx->tcc); ctx->tcc = NULL; ctx->state = IAKERB_AP_REQ; } else break; /* Done with TGS request; fall through to AP request. */ case IAKERB_AP_REQ: break; } if (out.length != 0) { assert(ctx->state != IAKERB_AP_REQ); code = iakerb_make_token(ctx, &realm, cookie, &out, (input_token == GSS_C_NO_BUFFER), output_token); if (code != 0) goto cleanup; /* Save the token for generating a future checksum */ code = iakerb_save_token(ctx, output_token); if (code != 0) goto cleanup; ctx->count++; } cleanup: if (code != 0) gss_release_buffer(&tmp, output_token); krb5_free_data(ctx->k5c, cookie); krb5_free_data_contents(ctx->k5c, &out); krb5_free_data_contents(ctx->k5c, &realm); return code; }
/* Outputs the selected tokeninfo and possibly a value and pin. * Prompting may occur. */ static krb5_error_code prompt_for_token(krb5_context context, krb5_prompter_fct prompter, void *prompter_data, krb5_otp_tokeninfo **tis, krb5_otp_tokeninfo **out_ti, krb5_data *out_value, krb5_data *out_pin) { krb5_otp_tokeninfo **filtered = NULL; krb5_otp_tokeninfo *ti = NULL; krb5_error_code retval; int i, challengers = 0; char *challenge = NULL; char otpvalue[1024]; krb5_data value, pin; memset(otpvalue, 0, sizeof(otpvalue)); if (tis == NULL || tis[0] == NULL || out_ti == NULL) return EINVAL; /* Count how many challenges we have. */ for (i = 0; tis[i] != NULL; i++) { if (tis[i]->challenge.data != NULL) challengers++; } /* If we have only one tokeninfo as input, choose it. */ if (i == 1) ti = tis[0]; /* Setup our challenge, if present. */ if (challengers > 0) { /* If we have multiple tokeninfos still, choose now. */ if (ti == NULL) { retval = prompt_for_tokeninfo(context, prompter, prompter_data, tis, &ti); if (retval != 0) return retval; } /* Create the challenge prompt. */ retval = make_challenge(ti, &challenge); if (retval != 0) return retval; } /* Prompt for token value. */ retval = doprompt(context, prompter, prompter_data, challenge, _("Enter OTP Token Value"), otpvalue, sizeof(otpvalue)); free(challenge); if (retval != 0) return retval; if (ti == NULL) { /* Filter out tokeninfos that don't match our token value. */ retval = filter_tokeninfos(context, otpvalue, tis, &filtered, &ti); if (retval != 0) return retval; /* If we still don't have a single tokeninfo, choose now. */ if (filtered != NULL) { retval = prompt_for_tokeninfo(context, prompter, prompter_data, filtered, &ti); free(filtered); if (retval != 0) return retval; } } assert(ti != NULL); /* Set the value. */ value = make_data(strdup(otpvalue), strlen(otpvalue)); if (value.data == NULL) return ENOMEM; /* Collect the PIN, if necessary. */ retval = collect_pin(context, prompter, prompter_data, ti, &pin); if (retval != 0) { krb5_free_data_contents(context, &value); return retval; } *out_value = value; *out_pin = pin; *out_ti = ti; return 0; }
/* ** The logic for setting and changing a password is mostly the same ** change_set_password handles both cases ** if set_password_for is NULL, then a password change is performed, ** otherwise, the password is set for the principal indicated in set_password_for */ static krb5_error_code change_set_password(krb5_context context, krb5_creds *creds, char *newpw, krb5_principal set_password_for, int *result_code, krb5_data *result_code_string, krb5_data *result_string) { krb5_data chpw_rep; krb5_address remote_kaddr; krb5_boolean use_tcp = 0; GETSOCKNAME_ARG3_TYPE addrlen; krb5_error_code code = 0; char *code_string; int local_result_code; struct sendto_callback_context callback_ctx; struct sendto_callback_info callback_info; struct sockaddr_storage remote_addr; struct serverlist sl = SERVERLIST_INIT; memset(&chpw_rep, 0, sizeof(krb5_data)); memset( &callback_ctx, 0, sizeof(struct sendto_callback_context)); callback_ctx.context = context; callback_ctx.newpw = newpw; callback_ctx.set_password_for = set_password_for; if ((code = krb5_auth_con_init(callback_ctx.context, &callback_ctx.auth_context))) goto cleanup; if ((code = krb5_mk_req_extended(callback_ctx.context, &callback_ctx.auth_context, AP_OPTS_USE_SUBKEY, NULL, creds, &callback_ctx.ap_req))) goto cleanup; callback_ctx.remote_seq_num = callback_ctx.auth_context->remote_seq_number; callback_ctx.local_seq_num = callback_ctx.auth_context->local_seq_number; do { int socktype = (use_tcp ? SOCK_STREAM : SOCK_DGRAM); code = locate_kpasswd(callback_ctx.context, &creds->server->realm, &sl, socktype); if (code) break; addrlen = sizeof(remote_addr); callback_info.data = &callback_ctx; callback_info.pfn_callback = kpasswd_sendto_msg_callback; callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup; krb5_free_data_contents(callback_ctx.context, &chpw_rep); code = k5_sendto(callback_ctx.context, NULL, &sl, socktype, 0, &callback_info, &chpw_rep, ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL); if (code) { /* * Here we may want to switch to TCP on some errors. * right? */ break; } if (remote_addr.ss_family == AF_INET) { remote_kaddr.addrtype = ADDRTYPE_INET; remote_kaddr.length = sizeof(ss2sin(&remote_addr)->sin_addr); remote_kaddr.contents = (krb5_octet *) &ss2sin(&remote_addr)->sin_addr; } else if (remote_addr.ss_family == AF_INET6) { remote_kaddr.addrtype = ADDRTYPE_INET6; remote_kaddr.length = sizeof(ss2sin6(&remote_addr)->sin6_addr); remote_kaddr.contents = (krb5_octet *) &ss2sin6(&remote_addr)->sin6_addr; } else { break; } if ((code = krb5_auth_con_setaddrs(callback_ctx.context, callback_ctx.auth_context, NULL, &remote_kaddr))) break; code = krb5int_rd_chpw_rep(callback_ctx.context, callback_ctx.auth_context, &chpw_rep, &local_result_code, result_string); if (code) { if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) { k5_free_serverlist(&sl); use_tcp = 1; continue; } break; } if (result_code) *result_code = local_result_code; if (result_code_string) { code = krb5_chpw_result_code_string(callback_ctx.context, local_result_code, &code_string); if (code) goto cleanup; result_code_string->length = strlen(code_string); result_code_string->data = malloc(result_code_string->length); if (result_code_string->data == NULL) { code = ENOMEM; goto cleanup; } strncpy(result_code_string->data, code_string, result_code_string->length); } if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) { k5_free_serverlist(&sl); use_tcp = 1; } else { break; } } while (TRUE); cleanup: if (callback_ctx.auth_context != NULL) krb5_auth_con_free(callback_ctx.context, callback_ctx.auth_context); k5_free_serverlist(&sl); krb5_free_data_contents(callback_ctx.context, &callback_ctx.ap_req); krb5_free_data_contents(callback_ctx.context, &chpw_rep); return(code); }
int auks_krb5_cred_get_fwd(char *ccachefilename, char *serverName, char **p_buffer, size_t * p_buffer_length) { int fstatus = AUKS_ERROR ; /* kerberos related variables */ krb5_error_code err_code; krb5_context context; krb5_ccache ccache; krb5_principal principal; krb5_creds **out_creds_array = NULL; krb5_auth_context auth_context; krb5_flags authopts; krb5_data outbuf; krb5_data *p_outbuf; krb5_replay_data krdata; authopts = AP_OPTS_MUTUAL_REQUIRED; authopts &= (~OPTS_FORWARD_CREDS); authopts &= (~OPTS_FORWARDABLE_CREDS); if ( serverName == NULL ) { auks_error("no host specified"); fstatus = AUKS_ERROR_KRB5_CRED_NO_HOST_SPECIFIED ; goto exit; } /* initialize kerberos context */ err_code = krb5_init_context(&context); if (err_code) { auks_error("unable to initialize kerberos context : %s", error_message(err_code)); fstatus = AUKS_ERROR_KRB5_CRED_INIT_CTX ; goto exit; } auks_log("kerberos context successfully initialized"); /* initialize kerberos credential cache structure */ if (ccachefilename == NULL) err_code = krb5_cc_default(context, &ccache); else err_code = krb5_cc_resolve(context,ccachefilename,&ccache); if (err_code) { auks_error("unable to resolve credential cache : %s", error_message(err_code)); fstatus = AUKS_ERROR_KRB5_CRED_OPEN_CC ; goto ctx_exit ; } auks_log("credential cache successfully resolved"); /* get principal using credential cache */ err_code = krb5_cc_get_principal(context,ccache,&principal); if (err_code) { auks_error("unable to get principal from credential cache : %s", error_message(err_code)); fstatus = AUKS_ERROR_KRB5_CRED_GET_PRINC ; goto cc_exit ; } auks_log("principal successfully extracted from credential cache"); /* initialize kerberos authentication context */ err_code = krb5_auth_con_init(context,&auth_context); if (err_code) { auks_error("unable to initialize kerberos authentication " "context : %s",error_message(err_code)); fstatus = AUKS_ERROR_KRB5_CRED_INIT_AUTH_CTX ; goto princ_exit; } auks_log("kerberos authentication context successfully initialized"); /* do replay detection using timestamps */ krb5_auth_con_setflags(context,auth_context,KRB5_AUTH_CONTEXT_RET_TIME); /* get forwarded credential for server */ err_code = krb5_fwd_tgt_creds(context,auth_context,serverName, principal,NULL,NULL,authopts,&outbuf); if (err_code) { auks_error("unable to get serialized and crypted forwarded " "credential for %s from KDC : %s", serverName,error_message(err_code)); fstatus = AUKS_ERROR_KRB5_CRED_GET_FWD_CRED ; goto auth_ctx_exit; } auks_log("serialized and crypted forwarded credential for %s " "successfully got from KDC",serverName); /* desactive replay detection */ krb5_auth_con_setflags(context,auth_context,0); /* decrypt (using session key stored in auth context) and */ /* unserialized forwarded credential in a kerberos credential */ /* structure */ err_code = krb5_rd_cred(context,auth_context,&outbuf,&out_creds_array, &krdata); if (err_code) { auks_error("unable to unserialize and decrypt forwarded " "credential for %s : %s",serverName, error_message(err_code)); fstatus = AUKS_ERROR_KRB5_CRED_RD_CRED ; goto fwd_exit; } auks_log("unserialization and decryption of forwarded " "credential for %s succesfully done",serverName); /* Reinitialize kerberos authentication context in order to */ /* write credential to output buffer */ krb5_auth_con_free(context,auth_context); err_code = krb5_auth_con_init(context,&auth_context); if (err_code) { auks_error("unable to reinitialize kerberos connection " "authentication context : %s",error_message (err_code)); fstatus = AUKS_ERROR_KRB5_CRED_INIT_AUTH_CTX ; goto rd_cred_exit; } auks_log("kerberos connection authentication context " "reinitialization successfully done"); /* no flags */ krb5_auth_con_setflags(context,auth_context,0); /* serialize forwarded credential (no encryption because auth */ /* context session key is nullified) */ err_code = krb5_mk_1cred(context,auth_context,*out_creds_array, &p_outbuf,&krdata); if (err_code) { auks_error("unable to serialize forwarded credential for " "%s : %s",serverName,error_message(err_code)); fstatus = AUKS_ERROR_KRB5_CRED_MK_CRED ; goto rd_cred_exit; } auks_log("forwarded credential for %s successfully serialized", serverName); /* allocate output buffer and store serialized credential */ (*p_buffer) = (char *) malloc(p_outbuf->length * sizeof(char)); if ((*p_buffer) == NULL) { auks_error("unable to allocate serialized credential output " "buffer for %s",serverName); *p_buffer_length = 0 ; fstatus = AUKS_ERROR_KRB5_CRED_MALLOC ; } else { /* copy data */ memcpy(*p_buffer,p_outbuf->data,p_outbuf->length); *p_buffer_length = p_outbuf->length; auks_log("forwarded credential successfully stored " "in output buffer"); fstatus = AUKS_SUCCESS ; } krb5_free_data(context,p_outbuf); rd_cred_exit: krb5_free_creds(context,*out_creds_array); free(out_creds_array); fwd_exit: krb5_free_data_contents(context, &outbuf); auth_ctx_exit: krb5_auth_con_free(context,auth_context); princ_exit: krb5_free_principal(context, principal); cc_exit: krb5_cc_close(context, ccache); ctx_exit: krb5_free_context(context); exit: return fstatus; }
/* returns 0 for failure, 1 for success */ static int k5support_verify_tgt(krb5_context context, krb5_ccache ccache) { krb5_principal server; krb5_data packet; krb5_keyblock *keyblock = NULL; krb5_auth_context auth_context = NULL; krb5_error_code k5_retcode; krb5_keytab kt = NULL; char thishost[BUFSIZ]; int result = 0; memset(&packet, 0, sizeof(packet)); if ((k5_retcode = krb5_sname_to_principal(context, NULL, verify_principal, KRB5_NT_SRV_HST, &server))) { k5support_log_err(context, k5_retcode, "krb5_sname_to_principal()"); return 0; } if (keytabname) { if ((k5_retcode = krb5_kt_resolve(context, keytabname, &kt))) { k5support_log_err(context, k5_retcode, "krb5_kt_resolve()"); goto fini; } } if ((k5_retcode = krb5_kt_read_service_key(context, kt, server, 0, 0, &keyblock))) { k5support_log_err(context, k5_retcode, "krb5_kt_read_service_key()"); goto fini; } if (keyblock) { krb5_free_keyblock(context, keyblock); } /* this duplicates work done in krb5_sname_to_principal * oh well. */ if (gethostname(thishost, BUFSIZ) < 0) { goto fini; } thishost[BUFSIZ-1] = '\0'; if ((k5_retcode = krb5_mk_req(context, &auth_context, 0, verify_principal, thishost, NULL, ccache, &packet))) { k5support_log_err(context, k5_retcode, "krb5_mk_req()"); } if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; } if (k5_retcode) { goto fini; } if ((k5_retcode = krb5_rd_req(context, &auth_context, &packet, server, NULL, NULL, NULL))) { k5support_log_err(context, k5_retcode, "krb5_rd_req()"); goto fini; } if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; } /* all is good now */ result = 1; fini: if (!k5_retcode) { krb5_free_data_contents(context, &packet); } krb5_free_principal(context, server); return result; }
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; }
/* * Now we send over the database. We use the following protocol: * Send over a KRB_SAFE message with the size. Then we send over the * database in blocks of KPROP_BLKSIZE, encrypted using KRB_PRIV. * Then we expect to see a KRB_SAFE message with the size sent back. * * At any point in the protocol, we may send a KRB_ERROR message; this * will abort the entire operation. */ static void xmit_database(krb5_context context, krb5_auth_context auth_context, krb5_creds *my_creds, int fd, int database_fd, int in_database_size) { krb5_int32 n; krb5_data inbuf, outbuf; char buf[KPROP_BUFSIZ]; krb5_error_code retval; krb5_error *error; krb5_ui_4 database_size = in_database_size, send_size, sent_size; /* Send over the size. */ send_size = htonl(database_size); inbuf.data = (char *)&send_size; inbuf.length = sizeof(send_size); /* must be 4, really */ /* KPROP_CKSUMTYPE */ retval = krb5_mk_safe(context, auth_context, &inbuf, &outbuf, NULL); if (retval) { com_err(progname, retval, _("while encoding database size")); send_error(context, my_creds, fd, _("while encoding database size"), retval); exit(1); } retval = krb5_write_message(context, &fd, &outbuf); if (retval) { krb5_free_data_contents(context, &outbuf); com_err(progname, retval, _("while sending database size")); exit(1); } krb5_free_data_contents(context, &outbuf); /* Initialize the initial vector. */ retval = krb5_auth_con_initivector(context, auth_context); if (retval) { send_error(context, my_creds, fd, "failed while initializing i_vector", retval); com_err(progname, retval, _("while allocating i_vector")); exit(1); } /* Send over the file, block by block. */ inbuf.data = buf; sent_size = 0; while ((n = read(database_fd, buf, sizeof(buf)))) { inbuf.length = n; retval = krb5_mk_priv(context, auth_context, &inbuf, &outbuf, NULL); if (retval) { snprintf(buf, sizeof(buf), "while encoding database block starting at %d", sent_size); com_err(progname, retval, "%s", buf); send_error(context, my_creds, fd, buf, retval); exit(1); } retval = krb5_write_message(context, &fd, &outbuf); if (retval) { krb5_free_data_contents(context, &outbuf); com_err(progname, retval, _("while sending database block starting at %d"), sent_size); exit(1); } krb5_free_data_contents(context, &outbuf); sent_size += n; if (debug) printf("%d bytes sent.\n", sent_size); } if (sent_size != database_size) { com_err(progname, 0, _("Premature EOF found for database file!")); send_error(context, my_creds, fd, "Premature EOF found for database file!", KRB5KRB_ERR_GENERIC); exit(1); } /* * OK, we've sent the database; now let's wait for a success * indication from the remote end. */ retval = krb5_read_message(context, &fd, &inbuf); if (retval) { com_err(progname, retval, _("while reading response from server")); exit(1); } /* * If we got an error response back from the server, display * the error message */ if (krb5_is_krb_error(&inbuf)) { retval = krb5_rd_error(context, &inbuf, &error); if (retval) { com_err(progname, retval, _("while decoding error response from server")); exit(1); } if (error->error == KRB_ERR_GENERIC) { if (error->text.data) { fprintf(stderr, _("Generic remote error: %s\n"), error->text.data); } } else if (error->error) { com_err(progname, (krb5_error_code)error->error + ERROR_TABLE_BASE_krb5, _("signalled from server")); if (error->text.data) { fprintf(stderr, _("Error text from server: %s\n"), error->text.data); } } krb5_free_error(context, error); exit(1); } retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL); if (retval) { com_err(progname, retval, "while decoding final size packet from server"); exit(1); } memcpy(&send_size, outbuf.data, sizeof(send_size)); send_size = ntohl(send_size); if (send_size != database_size) { com_err(progname, 0, _("Kpropd sent database size %d, expecting %d"), send_size, database_size); exit(1); } free(inbuf.data); free(outbuf.data); }
void ktest_empty_iakerb_header(krb5_iakerb_header *p) { krb5_free_data_contents(NULL, &p->target_realm); krb5_free_data(NULL, p->cookie); }
static krb5_error_code krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, krb5_creds *in_cred, krb5_creds **out_cred, krb5_creds ***tgts, int kdcopt) { krb5_error_code retval, subretval; krb5_principal client, server, supplied_server, out_supplied_server; krb5_creds tgtq, cc_tgt, *tgtptr, *referral_tgts[KRB5_REFERRAL_MAXHOPS]; krb5_boolean old_use_conf_ktypes; char **hrealms; int referral_count, i; /* * Set up client and server pointers. Make a fresh and modifyable * copy of the in_cred server and save the supplied version. */ client = in_cred->client; if ((retval=krb5_copy_principal(context, in_cred->server, &server))) return retval; /* We need a second copy for the output creds. */ if ((retval = krb5_copy_principal(context, server, &out_supplied_server)) != 0 ) { krb5_free_principal(context, server); return retval; } supplied_server = in_cred->server; in_cred->server=server; DUMP_PRINC("gc_from_kdc initial client", client); DUMP_PRINC("gc_from_kdc initial server", server); memset(&cc_tgt, 0, sizeof(cc_tgt)); memset(&tgtq, 0, sizeof(tgtq)); memset(&referral_tgts, 0, sizeof(referral_tgts)); tgtptr = NULL; *tgts = NULL; *out_cred=NULL; old_use_conf_ktypes = context->use_conf_ktypes; /* Copy client realm to server if no hint. */ if (krb5_is_referral_realm(&server->realm)) { /* Use the client realm. */ DPRINTF(("gc_from_kdc: no server realm supplied, " "using client realm.\n")); krb5_free_data_contents(context, &server->realm); if (!( server->realm.data = (char *)malloc(client->realm.length+1))) return ENOMEM; memcpy(server->realm.data, client->realm.data, client->realm.length); server->realm.length = client->realm.length; server->realm.data[server->realm.length] = 0; } /* * Retreive initial TGT to match the specified server, either for the * local realm in the default (referral) case or for the remote * realm if we're starting someplace non-local. */ retval = tgt_mcred(context, client, server, client, &tgtq); if (retval) goto cleanup; /* Fast path: Is it in the ccache? */ context->use_conf_ktypes = 1; /* * Solaris Kerberos: * Ensure the retrieved cred isn't stale. * Set endtime to now so krb5_cc_retrieve_cred won't return an expired ticket. */ if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) { goto cleanup; } retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS, &tgtq, &cc_tgt); if (!retval) { tgtptr = &cc_tgt; } else if (!HARD_CC_ERR(retval)) { DPRINTF(("gc_from_kdc: starting do_traversal to find " "initial TGT for referral\n")); retval = do_traversal(context, ccache, client, server, &cc_tgt, &tgtptr, tgts); } if (retval) { DPRINTF(("gc_from_kdc: failed to find initial TGT for referral\n")); goto cleanup; } DUMP_PRINC("gc_from_kdc: server as requested", supplied_server); /* * Try requesting a service ticket from our local KDC with referrals * turned on. If the first referral succeeds, follow a referral-only * path, otherwise fall back to old-style assumptions. */ for (referral_count = 0; referral_count < KRB5_REFERRAL_MAXHOPS; referral_count++) { #if 0 DUMP_PRINC("gc_from_kdc: referral loop: tgt in use", tgtptr->server); DUMP_PRINC("gc_from_kdc: referral loop: request is for", server); #endif retval = krb5_get_cred_via_tkt(context, tgtptr, KDC_OPT_CANONICALIZE | FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); if (retval) { DPRINTF(("gc_from_kdc: referral TGS-REQ request failed: <%s>\n", error_message(retval))); /* If we haven't gone anywhere yet, fail through to the non-referral case. */ if (referral_count==0) { DPRINTF(("gc_from_kdc: initial referral failed; " "punting to fallback.\n")); break; } /* Otherwise, try the same query without canonicalization set, and fail hard if that doesn't work. */ DPRINTF(("gc_from_kdc: referral #%d failed; " "retrying without option.\n", referral_count + 1)); retval = krb5_get_cred_via_tkt(context, tgtptr, FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); /* Whether or not that succeeded, we're done. */ goto cleanup; } /* Referral request succeeded; let's see what it is. */ if (krb5_principal_compare(context, in_cred->server, (*out_cred)->server)) { DPRINTF(("gc_from_kdc: request generated ticket " "for requested server principal\n")); DUMP_PRINC("gc_from_kdc final referred reply", in_cred->server); /* * Check if the return enctype is one that we requested if * needed. */ if (old_use_conf_ktypes || context->tgs_ktype_count == 0) goto cleanup; for (i = 0; i < context->tgs_ktype_count; i++) { if ((*out_cred)->keyblock.enctype == context->tgs_ktypes[i]) { /* Found an allowable etype, so we're done */ goto cleanup; } } /* * We need to try again, but this time use the * tgs_ktypes in the context. At this point we should * have all the tgts to succeed. */ /* Free "wrong" credential */ krb5_free_creds(context, *out_cred); *out_cred = NULL; /* Re-establish tgs etypes */ context->use_conf_ktypes = old_use_conf_ktypes; retval = krb5_get_cred_via_tkt(context, tgtptr, KDC_OPT_CANONICALIZE | FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); goto cleanup; } else if (IS_TGS_PRINC(context, (*out_cred)->server)) { krb5_data *r1, *r2; DPRINTF(("gc_from_kdc: request generated referral tgt\n")); DUMP_PRINC("gc_from_kdc credential received", (*out_cred)->server); if (referral_count == 0) r1 = &tgtptr->server->data[1]; else r1 = &referral_tgts[referral_count-1]->server->data[1]; r2 = &(*out_cred)->server->data[1]; if (r1->length == r2->length && !memcmp(r1->data, r2->data, r1->length)) { DPRINTF(("gc_from_kdc: referred back to " "previous realm; fall back\n")); krb5_free_creds(context, *out_cred); *out_cred = NULL; break; } /* Check for referral routing loop. */ for (i=0;i<referral_count;i++) { #if 0 DUMP_PRINC("gc_from_kdc: loop compare #1", (*out_cred)->server); DUMP_PRINC("gc_from_kdc: loop compare #2", referral_tgts[i]->server); #endif if (krb5_principal_compare(context, (*out_cred)->server, referral_tgts[i]->server)) { DFPRINTF((stderr, "krb5_get_cred_from_kdc_opt: " "referral routing loop - " "got referral back to hop #%d\n", i)); retval=KRB5_KDC_UNREACH; goto cleanup; } } /* Point current tgt pointer at newly-received TGT. */ if (tgtptr == &cc_tgt) krb5_free_cred_contents(context, tgtptr); tgtptr=*out_cred; /* Save pointer to tgt in referral_tgts. */ referral_tgts[referral_count]=*out_cred; /* Copy krbtgt realm to server principal. */ krb5_free_data_contents(context, &server->realm); retval = krb5int_copy_data_contents(context, &tgtptr->server->data[1], &server->realm); if (retval) return retval; /* * Future work: rewrite server principal per any * supplied padata. */ } else { /* Not a TGT; punt to fallback. */ krb5_free_creds(context, *out_cred); *out_cred = NULL; break; } } DUMP_PRINC("gc_from_kdc client at fallback", client); DUMP_PRINC("gc_from_kdc server at fallback", server); /* * At this point referrals have been tried and have failed. Go * back to the server principal as originally issued and try the * conventional path. */ /* * Referrals have failed. Look up fallback realm if not * originally provided. */ if (krb5_is_referral_realm(&supplied_server->realm)) { if (server->length >= 2) { retval=krb5_get_fallback_host_realm(context, &server->data[1], &hrealms); if (retval) goto cleanup; #if 0 DPRINTF(("gc_from_kdc: using fallback realm of %s\n", hrealms[0])); #endif krb5_free_data_contents(context,&in_cred->server->realm); server->realm.data=hrealms[0]; server->realm.length=strlen(hrealms[0]); free(hrealms); } else { /* * Problem case: Realm tagged for referral but apparently not * in a <type>/<host> format that * krb5_get_fallback_host_realm can deal with. */ /* Solaris Kerberos */ char *s_name = NULL; char *c_name = NULL; krb5_error_code s_err, c_err; s_err = krb5_unparse_name(context, server, &s_name); c_err = krb5_unparse_name(context, client, &c_name); krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN, dgettext(TEXT_DOMAIN, "Cannot determine realm for host: Referral specified but no fallback realm available. Client is '%s' and Server is '%s'"), c_err ? "unknown" : c_name, s_err ? "unknown" : s_name); if (s_name) krb5_free_unparsed_name(context, s_name); if (c_name) krb5_free_unparsed_name(context, c_name); DPRINTF(("gc_from_kdc: referral specified " "but no fallback realm avaiable!\n")); return KRB5_ERR_HOST_REALM_UNKNOWN; } } DUMP_PRINC("gc_from_kdc server at fallback after fallback rewrite", server); /* * Get a TGT for the target realm. */ krb5_free_cred_contents(context, &tgtq); retval = tgt_mcred(context, client, server, client, &tgtq); if (retval) goto cleanup; /* Fast path: Is it in the ccache? */ /* Free tgtptr data if reused from above. */ if (tgtptr == &cc_tgt) krb5_free_cred_contents(context, tgtptr); /* Free TGTS if previously filled by do_traversal() */ if (*tgts != NULL) { for (i = 0; (*tgts)[i] != NULL; i++) { krb5_free_creds(context, (*tgts)[i]); } free(*tgts); *tgts = NULL; } context->use_conf_ktypes = 1; /* * Solaris Kerberos: * Ensure the retrieved cred isn't stale. * Set endtime to now so krb5_cc_retrieve_cred won't return an expired ticket. */ if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) { goto cleanup; } retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS, &tgtq, &cc_tgt); if (!retval) { tgtptr = &cc_tgt; } else if (!HARD_CC_ERR(retval)) { retval = do_traversal(context, ccache, client, server, &cc_tgt, &tgtptr, tgts); } if (retval) goto cleanup; /* * Finally have TGT for target realm! Try using it to get creds. */ if (!krb5_c_valid_enctype(tgtptr->keyblock.enctype)) { retval = KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } context->use_conf_ktypes = old_use_conf_ktypes; retval = krb5_get_cred_via_tkt(context, tgtptr, FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); cleanup: krb5_free_cred_contents(context, &tgtq); if (tgtptr == &cc_tgt) krb5_free_cred_contents(context, tgtptr); context->use_conf_ktypes = old_use_conf_ktypes; /* Drop the original principal back into in_cred so that it's cached in the expected format. */ DUMP_PRINC("gc_from_kdc: final hacked server principal at cleanup", server); krb5_free_principal(context, server); in_cred->server = supplied_server; if (*out_cred && !retval) { /* Success: free server, swap supplied server back in. */ krb5_free_principal (context, (*out_cred)->server); (*out_cred)->server= out_supplied_server; } else { /* * Failure: free out_supplied_server. Don't free out_cred here * since it's either null or a referral TGT that we free below, * and we may need it to return. */ krb5_free_principal (context, out_supplied_server); } DUMP_PRINC("gc_from_kdc: final server after reversion", in_cred->server); /* * Deal with ccache TGT management: If tgts has been set from * initial non-referral TGT discovery, leave it alone. Otherwise, if * referral_tgts[0] exists return it as the only entry in tgts. * (Further referrals are never cached, only the referral from the * local KDC.) This is part of cleanup because useful received TGTs * should be cached even if the main request resulted in failure. */ if (*tgts == NULL) { if (referral_tgts[0]) { #if 0 /* * This should possibly be a check on the candidate return * credential against the cache, in the circumstance where we * don't want to clutter the cache with near-duplicate * credentials on subsequent iterations. For now, it is * disabled. */ subretval=...?; if (subretval) { #endif /* Allocate returnable TGT list. */ if (!(*tgts=calloc(sizeof (krb5_creds *), 2))) return ENOMEM; subretval=krb5_copy_creds(context, referral_tgts[0], &((*tgts)[0])); if(subretval) return subretval; (*tgts)[1]=NULL; DUMP_PRINC("gc_from_kdc: returning referral TGT for ccache", (*tgts)[0]->server); #if 0 } #endif } } /* Free referral TGTs list. */ for (i=0;i<KRB5_REFERRAL_MAXHOPS;i++) { if(referral_tgts[i]) { krb5_free_creds(context, referral_tgts[i]); } } DPRINTF(("gc_from_kdc finishing with %s\n", retval ? error_message(retval) : "no error")); return retval; }
krb5_error_code krb5_db2_delete_principal(krb5_context context, krb5_const_principal searchfor) { krb5_error_code retval; krb5_db_entry *entry; krb5_db2_context *dbc; DB *db; DBT key, contents; krb5_data keydata, contdata; int i, dbret; if (!inited(context)) return KRB5_KDB_DBNOTINITED; dbc = context->dal_handle->db_context; if ((retval = ctx_lock(context, dbc, KRB5_LOCKMODE_EXCLUSIVE))) return (retval); if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor))) goto cleanup; key.data = keydata.data; key.size = keydata.length; db = dbc->db; dbret = (*db->get) (db, &key, &contents, 0); retval = errno; switch (dbret) { case 1: retval = KRB5_KDB_NOENTRY; /* Fall through. */ case -1: default: goto cleankey; case 0: ; } contdata.data = contents.data; contdata.length = contents.size; retval = krb5_decode_princ_entry(context, &contdata, &entry); if (retval) goto cleankey; /* Clear encrypted key contents */ for (i = 0; i < entry->n_key_data; i++) { if (entry->key_data[i].key_data_length[0]) { memset(entry->key_data[i].key_data_contents[0], 0, (unsigned) entry->key_data[i].key_data_length[0]); } } retval = krb5_encode_princ_entry(context, &contdata, entry); krb5_dbe_free(context, entry); if (retval) goto cleankey; contents.data = contdata.data; contents.size = contdata.length; dbret = (*db->put) (db, &key, &contents, 0); retval = dbret ? errno : 0; krb5_free_data_contents(context, &contdata); if (retval) goto cleankey; dbret = (*db->del) (db, &key, 0); retval = dbret ? errno : 0; cleankey: krb5_free_data_contents(context, &keydata); cleanup: ctx_update_age(dbc); (void) krb5_db2_unlock(context); /* unlock write lock */ return retval; }
int krb4int_send_to_kdc_addr( KTEXT pkt, KTEXT rpkt, char *realm, struct sockaddr *addr, socklen_t *addrlen) { struct addrlist al = ADDRLIST_INIT; char lrealm[REALM_SZ]; krb5int_access internals; krb5_error_code retval; struct servent *sp; int krb_udp_port = 0; int krbsec_udp_port = 0; char krbhst[MAXHOSTNAMELEN]; char *scol; int i; int err; krb5_data message, reply; /* * If "realm" is non-null, use that, otherwise get the * local realm. */ if (realm) strncpy(lrealm, realm, sizeof(lrealm) - 1); else { if (krb_get_lrealm(lrealm, 1)) { DEB (("%s: can't get local realm\n", prog)); return SKDC_CANT; } } lrealm[sizeof(lrealm) - 1] = '\0'; DEB (("lrealm is %s\n", lrealm)); retval = krb5int_accessor(&internals, KRB5INT_ACCESS_VERSION); if (retval) return KFAILURE; /* The first time, decide what port to use for the KDC. */ if (cached_krb_udp_port == 0) { sp = getservbyname("kerberos","udp"); if (sp) cached_krb_udp_port = sp->s_port; else cached_krb_udp_port = htons(KERBEROS_PORT); /* kerberos/udp */ DEB (("cached_krb_udp_port is %d\n", cached_krb_udp_port)); } /* If kerberos/udp isn't 750, try using kerberos-sec/udp (or 750) as a fallback. */ if (cached_krbsec_udp_port == 0 && cached_krb_udp_port != htons(KERBEROS_PORT)) { sp = getservbyname("kerberos-sec","udp"); if (sp) cached_krbsec_udp_port = sp->s_port; else cached_krbsec_udp_port = htons(KERBEROS_PORT); /* kerberos/udp */ DEB (("cached_krbsec_udp_port is %d\n", cached_krbsec_udp_port)); } for (i = 1; krb_get_krbhst(krbhst, lrealm, i) == KSUCCESS; ++i) { #ifdef DEBUG if (krb_debug) { DEB (("Getting host entry for %s...",krbhst)); (void) fflush(stdout); } #endif if (0 != (scol = strchr(krbhst,':'))) { krb_udp_port = htons(atoi(scol+1)); *scol = 0; if (krb_udp_port == 0) { #ifdef DEBUG if (krb_debug) { DEB (("bad port number %s\n",scol+1)); (void) fflush(stdout); } #endif continue; } krbsec_udp_port = 0; } else { krb_udp_port = cached_krb_udp_port; krbsec_udp_port = cached_krbsec_udp_port; } err = internals.add_host_to_list(&al, krbhst, krb_udp_port, krbsec_udp_port, SOCK_DGRAM, PF_INET); if (err) { retval = SKDC_CANT; goto free_al; } } if (al.naddrs == 0) { DEB (("%s: can't find any Kerberos host.\n", prog)); retval = SKDC_CANT; } message.length = pkt->length; message.data = (char *)pkt->dat; /* XXX yuck */ retval = internals.sendto_udp(NULL, &message, &al, NULL, &reply, addr, addrlen, NULL, 0, NULL); DEB(("sendto_udp returns %d\n", retval)); free_al: internals.free_addrlist(&al); if (retval) return SKDC_CANT; DEB(("reply.length=%d\n", reply.length)); if (reply.length > sizeof(rpkt->dat)) retval = SKDC_CANT; rpkt->length = 0; if (!retval) { memcpy(rpkt->dat, reply.data, reply.length); rpkt->length = reply.length; } krb5_free_data_contents(NULL, &reply); return retval; }
OM_uint32 KRB5_CALLCONV krb5_gss_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context, int prf_key, const gss_buffer_t prf_in, ssize_t desired_output_len, gss_buffer_t prf_out) { krb5_error_code code; krb5_key key = NULL; krb5_gss_ctx_id_t ctx; int i; OM_uint32 minor; size_t prflen; krb5_data t, ns; unsigned char *p; prf_out->length = 0; prf_out->value = NULL; t.length = 0; t.data = NULL; ns.length = 0; ns.data = NULL; ctx = (krb5_gss_ctx_id_t)context; switch (prf_key) { case GSS_C_PRF_KEY_FULL: if (ctx->have_acceptor_subkey) { key = ctx->acceptor_subkey; break; } /* fallthrough */ case GSS_C_PRF_KEY_PARTIAL: key = ctx->subkey; break; default: code = EINVAL; goto cleanup; } if (key == NULL) { code = EINVAL; goto cleanup; } if (desired_output_len == 0) return GSS_S_COMPLETE; prf_out->value = k5alloc(desired_output_len, &code); if (prf_out->value == NULL) { code = KG_INPUT_TOO_LONG; goto cleanup; } prf_out->length = desired_output_len; code = krb5_c_prf_length(ctx->k5_context, krb5_k_key_enctype(ctx->k5_context, key), &prflen); if (code != 0) goto cleanup; ns.length = 4 + prf_in->length; ns.data = k5alloc(ns.length, &code); if (ns.data == NULL) { code = KG_INPUT_TOO_LONG; goto cleanup; } t.length = prflen; t.data = k5alloc(t.length, &code); if (t.data == NULL) goto cleanup; memcpy(ns.data + 4, prf_in->value, prf_in->length); i = 0; p = (unsigned char *)prf_out->value; while (desired_output_len > 0) { store_32_be(i, ns.data); code = krb5_k_prf(ctx->k5_context, key, &ns, &t); if (code != 0) goto cleanup; memcpy(p, t.data, MIN(t.length, desired_output_len)); p += t.length; desired_output_len -= t.length; i++; } cleanup: if (code != 0) gss_release_buffer(&minor, prf_out); krb5_free_data_contents(ctx->k5_context, &ns); krb5_free_data_contents(ctx->k5_context, &t); *minor_status = (OM_uint32)code; return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; }