1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* plugins/preauth/securid_sam2/grail.c - Test method for SAM-2 preauth */ 3 /* 4 * Copyright (C) 2012 by the Massachusetts Institute of Technology. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 * OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * This test method exists to exercise the client SAM-2 code and some of the 35 * KDC SAM-2 code. We make up a weakly random number and presents it to the 36 * client in the prompt (in plain text), as well as encrypted in the track ID. 37 * To verify, we compare the decrypted track ID to the entered value. 38 * 39 * Do not use this method in production; it is not secure. 40 */ 41 42 #ifdef GRAIL_PREAUTH 43 44 #include "k5-int.h" 45 #include <kdb.h> 46 #include <adm_proto.h> 47 #include <ctype.h> 48 #include "extern.h" 49 50 static krb5_error_code 51 get_grail_key(krb5_context context, krb5_db_entry *client, 52 krb5_keyblock *key_out) 53 { 54 krb5_db_entry *grail_entry = NULL; 55 krb5_key_data *kd; 56 int sam_type = PA_SAM_TYPE_GRAIL; 57 krb5_error_code ret = 0; 58 59 ret = sam_get_db_entry(context, client->princ, &sam_type, &grail_entry); 60 if (ret) 61 return KRB5_PREAUTH_NO_KEY; 62 ret = krb5_dbe_find_enctype(context, grail_entry, -1, -1, -1, &kd); 63 if (ret) 64 goto cleanup; 65 ret = krb5_dbe_decrypt_key_data(context, NULL, kd, key_out, NULL); 66 if (ret) 67 goto cleanup; 68 69 cleanup: 70 if (grail_entry) 71 krb5_db_free_principal(context, grail_entry); 72 return ret; 73 } 74 75 static krb5_error_code 76 decrypt_track_data(krb5_context context, krb5_db_entry *client, 77 krb5_data *enc_track_data, krb5_data *output) 78 { 79 krb5_error_code ret; 80 krb5_keyblock sam_key; 81 krb5_enc_data enc; 82 krb5_data result = empty_data(); 83 84 sam_key.contents = NULL; 85 *output = empty_data(); 86 87 ret = get_grail_key(context, client, &sam_key); 88 if (ret != 0) 89 return ret; 90 enc.ciphertext = *enc_track_data; 91 enc.enctype = ENCTYPE_UNKNOWN; 92 enc.kvno = 0; 93 ret = alloc_data(&result, enc_track_data->length); 94 if (ret) 95 goto cleanup; 96 ret = krb5_c_decrypt(context, &sam_key, 97 KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0, &enc, 98 &result); 99 if (ret) 100 goto cleanup; 101 102 *output = result; 103 result = empty_data(); 104 105 cleanup: 106 krb5_free_keyblock_contents(context, &sam_key); 107 krb5_free_data_contents(context, &result); 108 return ret; 109 } 110 111 static krb5_error_code 112 encrypt_track_data(krb5_context context, krb5_db_entry *client, 113 krb5_data *track_data, krb5_data *output) 114 { 115 krb5_error_code ret; 116 size_t olen; 117 krb5_keyblock sam_key; 118 krb5_enc_data enc; 119 120 *output = empty_data(); 121 enc.ciphertext = empty_data(); 122 sam_key.contents = NULL; 123 124 ret = get_grail_key(context, client, &sam_key); 125 if (ret != 0) 126 return ret; 127 128 ret = krb5_c_encrypt_length(context, sam_key.enctype, 129 track_data->length, &olen); 130 if (ret != 0) 131 goto cleanup; 132 assert(olen <= 65536); 133 ret = alloc_data(&enc.ciphertext, olen); 134 if (ret) 135 goto cleanup; 136 enc.enctype = sam_key.enctype; 137 enc.kvno = 0; 138 139 ret = krb5_c_encrypt(context, &sam_key, 140 KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0, 141 track_data, &enc); 142 if (ret) 143 goto cleanup; 144 145 *output = enc.ciphertext; 146 enc.ciphertext = empty_data(); 147 148 cleanup: 149 krb5_free_keyblock_contents(context, &sam_key); 150 krb5_free_data_contents(context, &enc.ciphertext); 151 return ret; 152 } 153 154 krb5_error_code 155 get_grail_edata(krb5_context context, krb5_db_entry *client, 156 krb5_keyblock *client_key, krb5_sam_challenge_2 *sc2_out) 157 { 158 krb5_error_code ret; 159 krb5_data tmp_data, track_id = empty_data(); 160 int tval = time(NULL) % 77777; 161 krb5_sam_challenge_2_body sc2b; 162 char tval_string[256], prompt[256]; 163 164 snprintf(tval_string, sizeof(tval_string), "%d", tval); 165 snprintf(prompt, sizeof(prompt), "Enter %d", tval); 166 167 memset(&sc2b, 0, sizeof(sc2b)); 168 sc2b.magic = KV5M_SAM_CHALLENGE_2; 169 sc2b.sam_track_id = empty_data(); 170 sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD; 171 sc2b.sam_type_name = empty_data(); 172 sc2b.sam_challenge_label = empty_data(); 173 sc2b.sam_challenge = empty_data(); 174 sc2b.sam_response_prompt = string2data(prompt); 175 sc2b.sam_pk_for_sad = empty_data(); 176 sc2b.sam_type = PA_SAM_TYPE_GRAIL; 177 sc2b.sam_etype = client_key->enctype; 178 179 tmp_data = string2data(tval_string); 180 ret = encrypt_track_data(context, client, &tmp_data, &track_id); 181 if (ret) 182 goto cleanup; 183 sc2b.sam_track_id = track_id; 184 185 tmp_data = make_data(&sc2b.sam_nonce, sizeof(sc2b.sam_nonce)); 186 ret = krb5_c_random_make_octets(context, &tmp_data); 187 if (ret) 188 goto cleanup; 189 190 ret = sam_make_challenge(context, &sc2b, client_key, sc2_out); 191 192 cleanup: 193 krb5_free_data_contents(context, &track_id); 194 return ret; 195 } 196 197 krb5_error_code 198 verify_grail_data(krb5_context context, krb5_db_entry *client, 199 krb5_sam_response_2 *sr2, krb5_enc_tkt_part *enc_tkt_reply, 200 krb5_pa_data *pa, krb5_sam_challenge_2 **sc2_out) 201 { 202 krb5_error_code ret; 203 krb5_key_data *client_key_data = NULL; 204 krb5_keyblock client_key; 205 krb5_data scratch = empty_data(), track_id_data = empty_data(); 206 krb5_enc_sam_response_enc_2 *esre2 = NULL; 207 208 *sc2_out = NULL; 209 memset(&client_key, 0, sizeof(client_key)); 210 211 if ((sr2->sam_enc_nonce_or_sad.ciphertext.data == NULL) || 212 (sr2->sam_enc_nonce_or_sad.ciphertext.length <= 0)) 213 return KRB5KDC_ERR_PREAUTH_FAILED; 214 215 ret = krb5_dbe_find_enctype(context, client, 216 sr2->sam_enc_nonce_or_sad.enctype, -1, 217 sr2->sam_enc_nonce_or_sad.kvno, 218 &client_key_data); 219 if (ret) 220 goto cleanup; 221 222 ret = krb5_dbe_decrypt_key_data(context, NULL, client_key_data, 223 &client_key, NULL); 224 if (ret) 225 goto cleanup; 226 ret = alloc_data(&scratch, sr2->sam_enc_nonce_or_sad.ciphertext.length); 227 if (ret) 228 goto cleanup; 229 ret = krb5_c_decrypt(context, &client_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE, 230 NULL, &sr2->sam_enc_nonce_or_sad, &scratch); 231 if (ret) 232 goto cleanup; 233 234 ret = decode_krb5_enc_sam_response_enc_2(&scratch, &esre2); 235 if (ret) 236 goto cleanup; 237 238 if (sr2->sam_nonce != esre2->sam_nonce) { 239 ret = KRB5KDC_ERR_PREAUTH_FAILED; 240 goto cleanup; 241 } 242 243 if (esre2->sam_sad.length == 0 || esre2->sam_sad.data == NULL) { 244 ret = KRB5KDC_ERR_PREAUTH_FAILED; 245 goto cleanup; 246 } 247 248 ret = decrypt_track_data(context, client, &sr2->sam_track_id, 249 &track_id_data); 250 if (ret) 251 goto cleanup; 252 253 /* Some enctypes aren't length-preserving; try to work anyway. */ 254 while (track_id_data.length > 0 && 255 !isdigit(track_id_data.data[track_id_data.length - 1])) 256 track_id_data.length--; 257 258 if (!data_eq(track_id_data, esre2->sam_sad)) { 259 ret = KRB5KDC_ERR_PREAUTH_FAILED; 260 goto cleanup; 261 } 262 263 enc_tkt_reply->flags |= (TKT_FLG_HW_AUTH | TKT_FLG_PRE_AUTH); 264 265 cleanup: 266 krb5_free_keyblock_contents(context, &client_key); 267 krb5_free_data_contents(context, &scratch); 268 krb5_free_enc_sam_response_enc_2(context, esre2); 269 return ret; 270 } 271 272 #endif /* GRAIL_PREAUTH */ 273