/* * Try to find tokeninfos which match configuration data recorded in the input * ccache, and if exactly one is found, drop the rest. */ static krb5_error_code filter_config_tokeninfos(krb5_context context, krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, krb5_otp_tokeninfo **tis) { krb5_otp_tokeninfo *match = NULL; size_t i, j; const char *vendor, *alg_id, *token_id; /* Pull up what we know about the token we want to use. */ vendor = cb->get_cc_config(context, rock, "vendor"); alg_id = cb->get_cc_config(context, rock, "algID"); token_id = cb->get_cc_config(context, rock, "tokenID"); /* Look for a single matching entry. */ for (i = 0; tis[i] != NULL; i++) { if (vendor != NULL && tis[i]->vendor.length > 0 && !data_eq_string(tis[i]->vendor, vendor)) continue; if (alg_id != NULL && tis[i]->alg_id.length > 0 && !data_eq_string(tis[i]->alg_id, alg_id)) continue; if (token_id != NULL && tis[i]->token_id.length > 0 && !data_eq_string(tis[i]->token_id, token_id)) continue; /* Oh, we already had a matching entry. More than one -> no change. */ if (match != NULL) return 0; match = tis[i]; } /* No matching entry -> no change. */ if (match == NULL) return 0; /* Prune out everything except the best match. */ for (i = 0, j = 0; tis[i] != NULL; i++) { if (tis[i] != match) k5_free_otp_tokeninfo(context, tis[i]); else tis[j++] = tis[i]; } tis[j] = NULL; return 0; }
static krb5_error_code ec_prep_questions(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) { cb->need_as_key(context, rock); return 0; }
static krb5_error_code test_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 ***out_pa_data) { struct client_state *st = (struct client_state *)moddata; struct client_request_state *reqst = (struct client_request_state *)modreq; krb5_error_code ret; krb5_keyblock *k; krb5_enc_data enc; krb5_data plain; const char *indstr; if (pa_data->length == 0) { /* This is an optimistic preauth test. Send a recognizable padata * value so the KDC knows not to expect a cookie. */ if (st->fail_optimistic) { k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced optimistic fail"); return KRB5_PREAUTH_FAILED; } *out_pa_data = make_pa_list("optimistic", 10); return 0; } else if (reqst->second_round_trip) { printf("2rt: %.*s\n", pa_data->length, pa_data->contents); if (st->fail_2rt) { k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced 2rt fail"); return KRB5_PREAUTH_FAILED; } } else if (pa_data->length == 6 && memcmp(pa_data->contents, "no key", 6) == 0) { printf("no key\n"); } else { /* This fails during s4u_identify_user(), so don't assert. */ ret = cb->get_as_key(context, rock, &k); if (ret) return ret; ret = alloc_data(&plain, pa_data->length); assert(!ret); enc.enctype = k->enctype; enc.ciphertext = make_data(pa_data->contents, pa_data->length); ret = krb5_c_decrypt(context, k, 1024, NULL, &enc, &plain); assert(!ret); printf("%.*s\n", plain.length, plain.data); free(plain.data); } reqst->second_round_trip = TRUE; indstr = (st->indicators != NULL) ? st->indicators : ""; *out_pa_data = make_pa_list(indstr, strlen(indstr)); return 0; }
/* * Save the vendor, algID, and tokenID values for the selected token to the * out_ccache, so that later we can try to use them to select the right one * without having ot ask the user. */ static void save_config_tokeninfo(krb5_context context, krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, krb5_otp_tokeninfo *ti) { char *tmp; if (ti->vendor.length > 0 && asprintf(&tmp, "%.*s", ti->vendor.length, ti->vendor.data) >= 0) { cb->set_cc_config(context, rock, "vendor", tmp); free(tmp); } if (ti->alg_id.length > 0 && asprintf(&tmp, "%.*s", ti->alg_id.length, ti->alg_id.data) >= 0) { cb->set_cc_config(context, rock, "algID", tmp); free(tmp); } if (ti->token_id.length > 0 && asprintf(&tmp, "%.*s", ti->token_id.length, ti->token_id.data) >= 0) { cb->set_cc_config(context, rock, "tokenID", tmp); free(tmp); } }
static krb5_error_code otp_client_prep_questions(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_pa_otp_challenge *chl; krb5_error_code retval; krb5_data tmp; char *json; if (modreq == NULL) return ENOMEM; /* Decode the challenge. */ tmp = make_data(pa_data->contents, pa_data->length); retval = decode_krb5_pa_otp_challenge(&tmp, (krb5_pa_otp_challenge **)modreq); if (retval != 0) return retval; chl = *(krb5_pa_otp_challenge **)modreq; /* Remove unsupported tokeninfos. */ retval = filter_supported_tokeninfos(context, chl->tokeninfo); if (retval != 0) return retval; /* Remove tokeninfos that don't match the recorded description, if that * results in there being only one that does. */ retval = filter_config_tokeninfos(context, cb, rock, chl->tokeninfo); if (retval != 0) return retval; /* Make the JSON representation. */ retval = codec_encode_challenge(context, chl, &json); if (retval != 0) return retval; /* Ask the question. */ retval = cb->ask_responder_question(context, rock, KRB5_RESPONDER_QUESTION_OTP, json); free(json); return retval; }
static krb5_error_code encts_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 ret; krb5_pa_enc_ts pa_enc; krb5_data *ts = NULL, *enc_ts = NULL; krb5_enc_data enc_data; krb5_pa_data **pa = NULL; krb5_keyblock *as_key; enc_data.ciphertext = empty_data(); ret = cb->get_as_key(context, rock, &as_key); if (ret) goto cleanup; TRACE_PREAUTH_ENC_TS_KEY_GAK(context, as_key); /* now get the time of day, and encrypt it accordingly */ ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec); if (ret) goto cleanup; ret = encode_krb5_pa_enc_ts(&pa_enc, &ts); if (ret) goto cleanup; ret = krb5_encrypt_helper(context, as_key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS, ts, &enc_data); if (ret) goto cleanup; TRACE_PREAUTH_ENC_TS(context, pa_enc.patimestamp, pa_enc.pausec, ts, &enc_data.ciphertext); ret = encode_krb5_enc_data(&enc_data, &enc_ts); if (ret) goto cleanup; pa = k5alloc(2 * sizeof(krb5_pa_data *), &ret); if (pa == NULL) goto cleanup; pa[0] = k5alloc(sizeof(krb5_pa_data), &ret); if (pa[0] == NULL) goto cleanup; pa[0]->magic = KV5M_PA_DATA; pa[0]->pa_type = KRB5_PADATA_ENC_TIMESTAMP; pa[0]->length = enc_ts->length; pa[0]->contents = (krb5_octet *) enc_ts->data; enc_ts->data = NULL; pa[1] = NULL; *out_padata = pa; pa = NULL; cleanup: krb5_free_data(context, ts); krb5_free_data(context, enc_ts); free(enc_data.ciphertext.data); free(pa); return ret; }
static krb5_error_code encts_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_clpreauth_get_as_key_fn gak_fct, void *gak_data, krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key, krb5_pa_data ***out_padata) { krb5_error_code ret; krb5_pa_enc_ts pa_enc; krb5_data *ts = NULL, *enc_ts = NULL; krb5_enc_data enc_data; krb5_pa_data **pa = NULL; krb5_enctype etype = cb->get_etype(context, rock); enc_data.ciphertext = empty_data(); if (as_key->length == 0) { #ifdef DEBUG fprintf (stderr, "%s:%d: salt len=%d", __FILE__, __LINE__, salt->length); if ((int) salt->length > 0) fprintf (stderr, " '%.*s'", salt->length, salt->data); fprintf (stderr, "; *etype=%d request->ktype[0]=%d\n", etype, request->ktype[0]); #endif ret = (*gak_fct)(context, request->client, etype, prompter, prompter_data, salt, s2kparams, as_key, gak_data); if (ret) goto cleanup; TRACE_PREAUTH_ENC_TS_KEY_GAK(context, as_key); } /* now get the time of day, and encrypt it accordingly */ ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec); if (ret) goto cleanup; ret = encode_krb5_pa_enc_ts(&pa_enc, &ts); if (ret) goto cleanup; ret = krb5_encrypt_helper(context, as_key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS, ts, &enc_data); if (ret) goto cleanup; TRACE_PREAUTH_ENC_TS(context, pa_enc.patimestamp, pa_enc.pausec, ts, &enc_data.ciphertext); ret = encode_krb5_enc_data(&enc_data, &enc_ts); if (ret) goto cleanup; pa = k5alloc(2 * sizeof(krb5_pa_data *), &ret); if (pa == NULL) goto cleanup; pa[0] = k5alloc(sizeof(krb5_pa_data), &ret); if (pa[0] == NULL) goto cleanup; pa[0]->magic = KV5M_PA_DATA; pa[0]->pa_type = KRB5_PADATA_ENC_TIMESTAMP; pa[0]->length = enc_ts->length; pa[0]->contents = (krb5_octet *) enc_ts->data; enc_ts->data = NULL; pa[1] = NULL; *out_padata = pa; pa = NULL; cleanup: krb5_free_data(context, ts); krb5_free_data(context, enc_ts); free(enc_data.ciphertext.data); free(pa); return ret; }
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; }
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 test_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 ***out_pa_data) { struct client_state *st = (struct client_state *)moddata; struct client_request_state *reqst = (struct client_request_state *)modreq; krb5_error_code ret; krb5_pa_data **list, *pa; krb5_keyblock *k; krb5_enc_data enc; krb5_data plain; const char *indstr; if (pa_data->length == 0) { /* This is an optimistic preauth test. Send a recognizable padata * value so the KDC knows not to expect a cookie. */ list = k5calloc(2, sizeof(*list), &ret); assert(!ret); pa = k5alloc(sizeof(*pa), &ret); assert(!ret); pa->pa_type = TEST_PA_TYPE; pa->contents = (uint8_t *)strdup("optimistic"); assert(pa->contents != NULL); pa->length = 10; list[0] = pa; list[1] = NULL; *out_pa_data = list; return 0; } else if (reqst->second_round_trip) { printf("2rt: %.*s\n", pa_data->length, pa_data->contents); } else if (pa_data->length == 6 && memcmp(pa_data->contents, "no key", 6) == 0) { printf("no key\n"); } else { /* This fails during s4u_identify_user(), so don't assert. */ ret = cb->get_as_key(context, rock, &k); if (ret) return ret; ret = alloc_data(&plain, pa_data->length); assert(!ret); enc.enctype = k->enctype; enc.ciphertext = make_data(pa_data->contents, pa_data->length); ret = krb5_c_decrypt(context, k, 1024, NULL, &enc, &plain); assert(!ret); printf("%.*s\n", plain.length, plain.data); free(plain.data); } reqst->second_round_trip = TRUE; indstr = (st->indicators != NULL) ? st->indicators : ""; list = k5calloc(2, sizeof(*list), &ret); assert(!ret); pa = k5alloc(sizeof(*pa), &ret); assert(!ret); pa->pa_type = TEST_PA_TYPE; pa->contents = (uint8_t *)strdup(indstr); assert(pa->contents != NULL); pa->length = strlen(indstr); list[0] = pa; list[1] = NULL; *out_pa_data = list; return 0; }
static krb5_error_code encts_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 ret; krb5_pa_enc_ts pa_enc; krb5_data *ts = NULL, *enc_ts = NULL; krb5_enc_data enc_data; krb5_pa_data **pa = NULL; krb5_keyblock *as_key; enc_data.ciphertext = empty_data(); ret = cb->get_as_key(context, rock, &as_key); if (ret) goto cleanup; TRACE_PREAUTH_ENC_TS_KEY_GAK(context, as_key); /* * Try and use the timestamp of the preauth request, even if it's * unauthenticated. We could be fooled into making a preauth response for * a future time, but that has no security consequences other than the * KDC's audit logs. If kdc_timesync is not configured, then this will * just use local time. */ ret = cb->get_preauth_time(context, rock, TRUE, &pa_enc.patimestamp, &pa_enc.pausec); if (ret) goto cleanup; ret = encode_krb5_pa_enc_ts(&pa_enc, &ts); if (ret) goto cleanup; ret = krb5_encrypt_helper(context, as_key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS, ts, &enc_data); if (ret) goto cleanup; TRACE_PREAUTH_ENC_TS(context, pa_enc.patimestamp, pa_enc.pausec, ts, &enc_data.ciphertext); ret = encode_krb5_enc_data(&enc_data, &enc_ts); if (ret) goto cleanup; pa = k5alloc(2 * sizeof(krb5_pa_data *), &ret); if (pa == NULL) goto cleanup; pa[0] = k5alloc(sizeof(krb5_pa_data), &ret); if (pa[0] == NULL) goto cleanup; pa[0]->magic = KV5M_PA_DATA; pa[0]->pa_type = KRB5_PADATA_ENC_TIMESTAMP; pa[0]->length = enc_ts->length; pa[0]->contents = (krb5_octet *) enc_ts->data; enc_ts->data = NULL; pa[1] = NULL; *out_padata = pa; pa = NULL; cleanup: krb5_free_data(context, ts); krb5_free_data(context, enc_ts); free(enc_data.ciphertext.data); free(pa); return ret; }