1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* kdc/kdc_preauth_ec.c - Encrypted challenge kdcpreauth module */ 3 /* 4 * Copyright (C) 2009 by the Massachusetts Institute of Technology. 5 * All rights reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 27 /* 28 * Implement Encrypted Challenge fast factor from 29 * draft-ietf-krb-wg-preauth-framework 30 */ 31 32 #include <k5-int.h> 33 #include <krb5/kdcpreauth_plugin.h> 34 #include "kdc_util.h" 35 36 static void 37 ec_edata(krb5_context context, krb5_kdc_req *request, 38 krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, 39 krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, 40 krb5_kdcpreauth_edata_respond_fn respond, void *arg) 41 { 42 krb5_keyblock *armor_key = cb->fast_armor(context, rock); 43 44 /* Encrypted challenge only works with FAST, and requires a client key. */ 45 if (armor_key == NULL || !cb->have_client_keys(context, rock)) 46 (*respond)(arg, ENOENT, NULL); 47 else 48 (*respond)(arg, 0, NULL); 49 } 50 51 static void 52 ec_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request, 53 krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data, 54 krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, 55 krb5_kdcpreauth_moddata moddata, 56 krb5_kdcpreauth_verify_respond_fn respond, void *arg) 57 { 58 krb5_error_code ret; 59 krb5_enc_data *enc = NULL; 60 krb5_data der_enc_ts = empty_data(), der_enc_data; 61 krb5_keyblock *armor_key = cb->fast_armor(context, rock); 62 krb5_pa_enc_ts *ts = NULL; 63 krb5_keyblock *client_keys = NULL; 64 krb5_keyblock *challenge_key = NULL; 65 krb5_keyblock *kdc_challenge_key; 66 krb5_kdcpreauth_modreq modreq = NULL; 67 int i = 0; 68 char *ai = NULL, *realmstr = NULL; 69 krb5_data realm = request->server->realm; 70 71 if (armor_key == NULL) { 72 ret = ENOENT; 73 k5_setmsg(context, ret, 74 _("Encrypted Challenge used outside of FAST tunnel")); 75 goto cleanup; 76 } 77 78 der_enc_data = make_data(data->contents, data->length); 79 ret = decode_krb5_enc_data(&der_enc_data, &enc); 80 if (ret) 81 goto cleanup; 82 83 ret = alloc_data(&der_enc_ts, enc->ciphertext.length); 84 if (ret) 85 goto cleanup; 86 87 /* Check for a configured auth indicator. */ 88 realmstr = k5memdup0(realm.data, realm.length, &ret); 89 if (realmstr == NULL) 90 goto cleanup; 91 ret = profile_get_string(context->profile, KRB5_CONF_REALMS, realmstr, 92 KRB5_CONF_ENCRYPTED_CHALLENGE_INDICATOR, NULL, 93 &ai); 94 if (ret) 95 goto cleanup; 96 97 ret = cb->client_keys(context, rock, &client_keys); 98 if (ret) 99 goto cleanup; 100 for (i = 0; client_keys[i].enctype != ENCTYPE_NULL; i++) { 101 ret = krb5_c_fx_cf2_simple(context, armor_key, "clientchallengearmor", 102 &client_keys[i], "challengelongterm", 103 &challenge_key); 104 if (ret) 105 goto cleanup; 106 ret = krb5_c_decrypt(context, challenge_key, 107 KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT, NULL, enc, 108 &der_enc_ts); 109 krb5_free_keyblock(context, challenge_key); 110 if (!ret) 111 break; 112 } 113 114 if (client_keys[i].enctype == ENCTYPE_NULL) { 115 ret = KRB5KDC_ERR_PREAUTH_FAILED; 116 k5_setmsg(context, ret, 117 _("Incorrect password in encrypted challenge")); 118 goto cleanup; 119 } 120 121 ret = decode_krb5_pa_enc_ts(&der_enc_ts, &ts); 122 if (ret) 123 goto cleanup; 124 ret = krb5_check_clockskew(context, ts->patimestamp); 125 if (ret) 126 goto cleanup; 127 128 enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; 129 130 /* 131 * If this fails, we won't generate a reply to the client. That may cause 132 * the client to fail, but at this point the KDC has considered this a 133 * success, so the return value is ignored. 134 */ 135 if (krb5_c_fx_cf2_simple(context, armor_key, "kdcchallengearmor", 136 &client_keys[i], "challengelongterm", 137 &kdc_challenge_key) == 0) { 138 modreq = (krb5_kdcpreauth_modreq)kdc_challenge_key; 139 if (ai != NULL) 140 cb->add_auth_indicator(context, rock, ai); 141 } 142 143 cleanup: 144 cb->free_keys(context, rock, client_keys); 145 free(der_enc_ts.data); 146 krb5_free_enc_data(context, enc); 147 krb5_free_pa_enc_ts(context, ts); 148 free(realmstr); 149 free(ai); 150 151 (*respond)(arg, ret, modreq, NULL, NULL); 152 } 153 154 static krb5_error_code 155 ec_return(krb5_context context, krb5_pa_data *padata, krb5_data *req_pkt, 156 krb5_kdc_req *request, krb5_kdc_rep *reply, 157 krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, 158 krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, 159 krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_modreq modreq) 160 { 161 krb5_error_code ret; 162 krb5_keyblock *challenge_key = (krb5_keyblock *)modreq; 163 krb5_pa_enc_ts ts; 164 krb5_data *der_enc_ts = NULL, *der_enc_data = NULL; 165 krb5_enc_data enc; 166 krb5_pa_data *pa = NULL; 167 168 enc.ciphertext.data = NULL; 169 170 if (challenge_key == NULL) 171 return 0; 172 173 ret = krb5_us_timeofday(context, &ts.patimestamp, &ts.pausec); 174 if (ret) 175 goto cleanup; 176 ret = encode_krb5_pa_enc_ts(&ts, &der_enc_ts); 177 if (ret) 178 goto cleanup; 179 ret = krb5_encrypt_helper(context, challenge_key, 180 KRB5_KEYUSAGE_ENC_CHALLENGE_KDC, der_enc_ts, 181 &enc); 182 if (ret) 183 goto cleanup; 184 ret = encode_krb5_enc_data(&enc, &der_enc_data); 185 if (ret) 186 goto cleanup; 187 188 pa = k5alloc(sizeof(*pa), &ret); 189 if (pa == NULL) 190 goto cleanup; 191 pa->pa_type = KRB5_PADATA_ENCRYPTED_CHALLENGE; 192 pa->length = der_enc_data->length; 193 /* Steal the data pointer from der_enc_data. */ 194 pa->contents = (unsigned char *)der_enc_data->data; 195 der_enc_data->data = NULL; 196 197 *send_pa = pa; 198 199 cleanup: 200 krb5_free_keyblock(context, challenge_key); 201 krb5_free_data(context, der_enc_data); 202 krb5_free_data(context, der_enc_ts); 203 krb5_free_data_contents(context, &enc.ciphertext); 204 return ret; 205 } 206 207 static krb5_preauthtype ec_types[] = { 208 KRB5_PADATA_ENCRYPTED_CHALLENGE, 0}; 209 210 krb5_error_code 211 kdcpreauth_encrypted_challenge_initvt(krb5_context context, int maj_ver, 212 int min_ver, krb5_plugin_vtable vtable) 213 { 214 krb5_kdcpreauth_vtable vt; 215 216 if (maj_ver != 1) 217 return KRB5_PLUGIN_VER_NOTSUPP; 218 vt = (krb5_kdcpreauth_vtable)vtable; 219 vt->name = "encrypted_challenge"; 220 vt->pa_type_list = ec_types; 221 vt->edata = ec_edata; 222 vt->verify = ec_verify; 223 vt->return_padata = ec_return; 224 return 0; 225 } 226