static void otp_edata(krb5_context context, krb5_kdc_req *request, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, krb5_kdcpreauth_edata_respond_fn respond, void *arg) { krb5_otp_tokeninfo ti, *tis[2] = { &ti, NULL }; krb5_keyblock *armor_key = NULL; krb5_pa_otp_challenge chl; krb5_pa_data *pa = NULL; krb5_error_code retval; krb5_data *encoding; char *config; /* Determine if otp is enabled for the user. */ retval = cb->get_string(context, rock, "otp", &config); if (retval == 0 && config == NULL) retval = ENOENT; if (retval != 0) goto out; cb->free_string(context, rock, config); /* Get the armor key. This indicates the length of random data to use in * the nonce. */ armor_key = cb->fast_armor(context, rock); if (armor_key == NULL) { retval = ENOENT; goto out; } /* Build the (mostly empty) challenge. */ memset(&ti, 0, sizeof(ti)); memset(&chl, 0, sizeof(chl)); chl.tokeninfo = tis; ti.format = -1; ti.length = -1; ti.iteration_count = -1; /* Generate the nonce. */ retval = nonce_generate(context, armor_key->length, &chl.nonce); if (retval != 0) goto out; /* Build the output pa-data. */ retval = encode_krb5_pa_otp_challenge(&chl, &encoding); if (retval != 0) goto out; pa = k5alloc(sizeof(krb5_pa_data), &retval); if (pa == NULL) { krb5_free_data(context, encoding); goto out; } pa->pa_type = KRB5_PADATA_OTP_CHALLENGE; pa->contents = (krb5_octet *)encoding->data; pa->length = encoding->length; free(encoding); out: (*respond)(arg, retval, pa); }
static void enc_ts_get(krb5_context context, krb5_kdc_req *request, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, krb5_kdcpreauth_edata_respond_fn respond, void *arg) { krb5_keyblock *armor_key = cb->fast_armor(context, rock); /* Encrypted timestamp must not be used with FAST, and requires a key. */ if (armor_key != NULL || !cb->have_client_keys(context, rock)) (*respond)(arg, ENOENT, NULL); else (*respond)(arg, 0, NULL); }
static krb5_error_code ec_edata(krb5_context context, krb5_kdc_req *request, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_pa_data *data) { krb5_keyblock *armor_key = cb->fast_armor(context, rock); return (armor_key == NULL) ? ENOENT : 0; }
static void enc_ts_get(krb5_context context, krb5_kdc_req *request, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, krb5_kdcpreauth_edata_respond_fn respond, void *arg) { krb5_keyblock *armor_key = cb->fast_armor(context, rock); (*respond)(arg, (armor_key != NULL) ? ENOENT : 0, NULL); }
static krb5_error_code otp_return_padata(krb5_context context, krb5_pa_data *padata, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa_out, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_modreq modreq) { krb5_keyblock *armor_key = NULL; if (padata->length == 0) return 0; /* Get the armor key. */ armor_key = cb->fast_armor(context, rock); if (!armor_key) { com_err("otp", ENOENT, "No armor key found when returning padata"); return ENOENT; } /* Replace the reply key with the FAST armor key. */ krb5_free_keyblock_contents(context, encrypting_key); return krb5_copy_keyblock_contents(context, armor_key, encrypting_key); }
static void kdc_verify_preauth(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa_data, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_verify_respond_fn respond, void *arg) { krb5_error_code retval, saved_retval = 0; krb5_sam_response_2 *sr2 = NULL; krb5_data scratch, *scratch2, *e_data = NULL; char *client_name = NULL; krb5_sam_challenge_2 *out_sc2 = NULL; krb5_db_entry *client = cb->client_entry(context, rock); scratch.data = (char *) pa_data->contents; scratch.length = pa_data->length; retval = krb5_unparse_name(context, client->princ, &client_name); if (retval) goto cleanup; retval = decode_krb5_sam_response_2(&scratch, &sr2); if (retval) { com_err("krb5kdc", retval, "while decoding SAM_RESPONSE_2 in verify_sam_response_2"); sr2 = NULL; goto cleanup; } switch (sr2->sam_type) { #ifdef ARL_SECURID_PREAUTH case PA_SAM_TYPE_SECURID: retval = verify_securid_data_2(context, client, sr2, enc_tkt_reply, pa_data, &out_sc2); if (retval) goto cleanup; break; #endif /* ARL_SECURID_PREAUTH */ #ifdef GRAIL_PREAUTH case PA_SAM_TYPE_GRAIL: retval = verify_grail_data(context, client, sr2, enc_tkt_reply, pa_data, &out_sc2); if (retval) goto cleanup; break; #endif /* GRAIL_PREAUTH */ default: retval = KRB5_PREAUTH_BAD_TYPE; com_err("krb5kdc", retval, "while verifying SAM 2 data"); break; } /* * It is up to the method-specific verify routine to set the * ticket flags to indicate TKT_FLG_HW_AUTH and/or * TKT_FLG_PRE_AUTH. Some methods may require more than one round * of dialog with the client and must return successfully from * their verify routine. If does not set the TGT flags, the * required_preauth conditions will not be met and it will try * again to get enough preauth data from the client. Do not set * TGT flags here. */ cleanup: /* * Note that e_data is an output even in error conditions. If we * successfully encode the output e_data, we return whatever error is * received above. Otherwise we return the encoding error. */ saved_retval = retval; if (out_sc2) { krb5_pa_data pa_out; krb5_pa_data *pa_array[2]; pa_array[0] = &pa_out; pa_array[1] = NULL; pa_out.pa_type = KRB5_PADATA_SAM_CHALLENGE_2; retval = encode_krb5_sam_challenge_2(out_sc2, &scratch2); krb5_free_sam_challenge_2(context, out_sc2); if (retval) goto encode_error; pa_out.contents = (krb5_octet *) scratch2->data; pa_out.length = scratch2->length; retval = encode_krb5_padata_sequence(pa_array, &e_data); krb5_free_data(context, scratch2); } encode_error: krb5_free_sam_response_2(context, sr2); free(client_name); if (retval == 0) retval = saved_retval; (*respond)(arg, retval, NULL, NULL, NULL); }
static void kdc_include_padata(krb5_context context, krb5_kdc_req *request, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, krb5_kdcpreauth_edata_respond_fn respond, void *arg) { krb5_error_code retval; krb5_keyblock *client_key = NULL; krb5_sam_challenge_2 sc2; int sam_type = 0; /* unknown */ krb5_db_entry *sam_db_entry = NULL, *client; krb5_data *encoded_challenge = NULL; krb5_pa_data *pa_data = NULL; memset(&sc2, 0, sizeof(sc2)); client = cb->client_entry(context, rock); retval = sam_get_db_entry(context, client->princ, &sam_type, &sam_db_entry); if (retval) goto cleanup; retval = cb->client_keys(context, rock, &client_key); if (retval) goto cleanup; if (client_key->enctype == 0) { retval = KRB5KDC_ERR_ETYPE_NOSUPP; com_err("krb5kdc", retval, "No client keys found in processing SAM2 challenge"); goto cleanup; } if (sam_type == 0) { retval = KRB5_PREAUTH_BAD_TYPE; goto cleanup; } /* * Defer getting the key for the SAM principal associated with the client * until the mechanism-specific code. The mechanism may want to get a * specific keytype. */ switch (sam_type) { #ifdef ARL_SECURID_PREAUTH case PA_SAM_TYPE_SECURID: retval = get_securid_edata_2(context, client, client_key, &sc2); if (retval) goto cleanup; break; #endif /* ARL_SECURID_PREAUTH */ #ifdef GRAIL_PREAUTH case PA_SAM_TYPE_GRAIL: retval = get_grail_edata(context, client, client_key, &sc2); if (retval) goto cleanup; break; #endif /* GRAIL_PREAUTH */ default: retval = KRB5_PREAUTH_BAD_TYPE; goto cleanup; } retval = encode_krb5_sam_challenge_2(&sc2, &encoded_challenge); if (retval) { com_err("krb5kdc", retval, "while encoding SECURID SAM_CHALLENGE_2"); goto cleanup; } pa_data = k5alloc(sizeof(*pa_data), &retval); if (pa_data == NULL) goto cleanup; pa_data->magic = KV5M_PA_DATA; pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE_2; pa_data->contents = (krb5_octet *)encoded_challenge->data; pa_data->length = encoded_challenge->length; encoded_challenge->data = NULL; cleanup: krb5_free_data(context, encoded_challenge); if (sam_db_entry) krb5_db_free_principal(context, sam_db_entry); cb->free_keys(context, rock, client_key); (*respond)(arg, retval, pa_data); }
static void ec_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_verify_respond_fn respond, void *arg) { krb5_error_code retval = 0; krb5_timestamp now; krb5_enc_data *enc = NULL; krb5_data scratch, plain; krb5_keyblock *armor_key = cb->fast_armor(context, rock); krb5_pa_enc_ts *ts = NULL; krb5_keyblock *client_keys = NULL; krb5_keyblock *challenge_key = NULL; krb5_keyblock *kdc_challenge_key; krb5_kdcpreauth_modreq modreq = NULL; int i = 0; plain.data = NULL; if (armor_key == NULL) { retval = ENOENT; krb5_set_error_message(context, ENOENT, _("Encrypted Challenge used outside of FAST " "tunnel")); } scratch.data = (char *) data->contents; scratch.length = data->length; if (retval == 0) retval = decode_krb5_enc_data(&scratch, &enc); if (retval == 0) { plain.data = malloc(enc->ciphertext.length); plain.length = enc->ciphertext.length; if (plain.data == NULL) retval = ENOMEM; } if (retval == 0) retval = cb->client_keys(context, rock, &client_keys); if (retval == 0) { for (i = 0; client_keys[i].enctype&& (retval == 0); i++ ) { retval = krb5_c_fx_cf2_simple(context, armor_key, "clientchallengearmor", &client_keys[i], "challengelongterm", &challenge_key); if (retval == 0) retval = krb5_c_decrypt(context, challenge_key, KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT, NULL, enc, &plain); if (challenge_key) krb5_free_keyblock(context, challenge_key); challenge_key = NULL; if (retval == 0) break; /*We failed to decrypt. Try next key*/ retval = 0; } if (client_keys[i].enctype == 0) { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, _("Incorrect password in encrypted " "challenge")); } } if (retval == 0) retval = decode_krb5_pa_enc_ts(&plain, &ts); if (retval == 0) retval = krb5_timeofday(context, &now); if (retval == 0) { if (labs(now-ts->patimestamp) < context->clockskew) { enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; /* * If this fails, we won't generate a reply to the client. That * may cause the client to fail, but at this point the KDC has * considered this a success, so the return value is ignored. */ if (krb5_c_fx_cf2_simple(context, armor_key, "kdcchallengearmor", &client_keys[i], "challengelongterm", &kdc_challenge_key) == 0) modreq = (krb5_kdcpreauth_modreq)kdc_challenge_key; } else { /*skew*/ retval = KRB5KRB_AP_ERR_SKEW; } } cb->free_keys(context, rock, client_keys); if (plain.data) free(plain.data); if (enc) krb5_free_enc_data(context, enc); if (ts) krb5_free_pa_enc_ts(context, ts); (*respond)(arg, retval, modreq, NULL, NULL); }
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); }