1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* lib/kdb/kdb_cpw.c */ 3 /* 4 * Copyright 1995, 2009, 2014 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 * Copyright (C) 1998 by the FundsXpress, INC. 28 * 29 * All rights reserved. 30 * 31 * Export of this software from the United States of America may require 32 * a specific license from the United States Government. It is the 33 * responsibility of any person or organization contemplating export to 34 * obtain such a license before exporting. 35 * 36 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 37 * distribute this software and its documentation for any purpose and 38 * without fee is hereby granted, provided that the above copyright 39 * notice appear in all copies and that both that copyright notice and 40 * this permission notice appear in supporting documentation, and that 41 * the name of FundsXpress. not be used in advertising or publicity pertaining 42 * to distribution of the software without specific, written prior 43 * permission. FundsXpress makes no representations about the suitability of 44 * this software for any purpose. It is provided "as is" without express 45 * or implied warranty. 46 * 47 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 48 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 49 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 50 */ 51 52 #include "k5-int.h" 53 #include "kdb.h" 54 #include <stdio.h> 55 #include <errno.h> 56 57 enum save { DISCARD_ALL, KEEP_LAST_KVNO, KEEP_ALL }; 58 59 int 60 krb5_db_get_key_data_kvno(context, count, data) 61 krb5_context context; 62 int count; 63 krb5_key_data * data; 64 { 65 int i, kvno; 66 /* Find last key version number */ 67 for (kvno = i = 0; i < count; i++) { 68 if (kvno < data[i].key_data_kvno) { 69 kvno = data[i].key_data_kvno; 70 } 71 } 72 return(kvno); 73 } 74 75 static void 76 cleanup_key_data(context, count, data) 77 krb5_context context; 78 int count; 79 krb5_key_data * data; 80 { 81 int i; 82 83 /* If data is NULL, count is always 0 */ 84 if (data == NULL) return; 85 86 for (i = 0; i < count; i++) 87 krb5_dbe_free_key_data_contents(context, &data[i]); 88 free(data); 89 } 90 91 /* Transfer key data from old_kd to new_kd, making sure that new_kd is 92 * encrypted with mkey. May steal from old_kd and zero it out. */ 93 static krb5_error_code 94 preserve_one_old_key(krb5_context context, krb5_keyblock *mkey, 95 krb5_db_entry *dbent, krb5_key_data *old_kd, 96 krb5_key_data *new_kd) 97 { 98 krb5_error_code ret; 99 krb5_keyblock kb; 100 krb5_keysalt salt; 101 102 memset(new_kd, 0, sizeof(*new_kd)); 103 104 ret = krb5_dbe_decrypt_key_data(context, mkey, old_kd, &kb, NULL); 105 if (ret == 0) { 106 /* old_kd is already encrypted in mkey, so just move it. */ 107 *new_kd = *old_kd; 108 memset(old_kd, 0, sizeof(*old_kd)); 109 krb5_free_keyblock_contents(context, &kb); 110 return 0; 111 } 112 113 /* Decrypt and re-encrypt old_kd using mkey. */ 114 ret = krb5_dbe_decrypt_key_data(context, NULL, old_kd, &kb, &salt); 115 if (ret) 116 return ret; 117 ret = krb5_dbe_encrypt_key_data(context, mkey, &kb, &salt, 118 old_kd->key_data_kvno, new_kd); 119 krb5_free_keyblock_contents(context, &kb); 120 krb5_free_data_contents(context, &salt.data); 121 return ret; 122 } 123 124 /* 125 * Add key_data to dbent, making sure that each entry is encrypted in mkey. If 126 * kvno is non-zero, preserve only keys of that kvno. May steal some elements 127 * from key_data and zero them out. 128 */ 129 static krb5_error_code 130 preserve_old_keys(krb5_context context, krb5_keyblock *mkey, 131 krb5_db_entry *dbent, int kvno, int n_key_data, 132 krb5_key_data *key_data) 133 { 134 krb5_error_code ret; 135 int i; 136 137 for (i = 0; i < n_key_data; i++) { 138 if (kvno != 0 && key_data[i].key_data_kvno != kvno) 139 continue; 140 ret = krb5_dbe_create_key_data(context, dbent); 141 if (ret) 142 return ret; 143 ret = preserve_one_old_key(context, mkey, dbent, &key_data[i], 144 &dbent->key_data[dbent->n_key_data - 1]); 145 if (ret) 146 return ret; 147 } 148 return 0; 149 } 150 151 static krb5_error_code 152 add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno) 153 krb5_context context; 154 krb5_keyblock * master_key; 155 krb5_key_salt_tuple * ks_tuple; 156 int ks_tuple_count; 157 krb5_db_entry * db_entry; 158 int kvno; 159 { 160 krb5_keyblock key; 161 int i, j; 162 krb5_error_code retval; 163 krb5_key_data *kd_slot; 164 165 for (i = 0; i < ks_tuple_count; i++) { 166 krb5_boolean similar; 167 168 similar = 0; 169 170 /* 171 * We could use krb5_keysalt_iterate to replace this loop, or use 172 * krb5_keysalt_is_present for the loop below, but we want to avoid 173 * circular library dependencies. 174 */ 175 for (j = 0; j < i; j++) { 176 if ((retval = krb5_c_enctype_compare(context, 177 ks_tuple[i].ks_enctype, 178 ks_tuple[j].ks_enctype, 179 &similar))) 180 return(retval); 181 182 if (similar) 183 break; 184 } 185 186 if (similar) 187 continue; 188 189 if ((retval = krb5_dbe_create_key_data(context, db_entry))) 190 return retval; 191 kd_slot = &db_entry->key_data[db_entry->n_key_data - 1]; 192 193 /* there used to be code here to extract the old key, and derive 194 a new key from it. Now that there's a unified prng, that isn't 195 necessary. */ 196 197 /* make new key */ 198 if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype, 199 &key))) 200 return retval; 201 202 retval = krb5_dbe_encrypt_key_data(context, master_key, &key, NULL, 203 kvno, kd_slot); 204 205 krb5_free_keyblock_contents(context, &key); 206 if( retval ) 207 return retval; 208 } 209 210 return 0; 211 } 212 213 /* Construct a random explicit salt. */ 214 static krb5_error_code 215 make_random_salt(krb5_context context, krb5_keysalt *salt_out) 216 { 217 krb5_error_code retval; 218 unsigned char rndbuf[8]; 219 krb5_data salt, rnd = make_data(rndbuf, sizeof(rndbuf)); 220 unsigned int i; 221 222 /* 223 * Salts are limited by RFC 4120 to 7-bit ASCII. For ease of examination 224 * and to avoid certain folding issues for older enctypes, we use printable 225 * characters with four fixed bits and four random bits, encoding 64 226 * psuedo-random bits into 16 bytes. 227 */ 228 retval = krb5_c_random_make_octets(context, &rnd); 229 if (retval) 230 return retval; 231 retval = alloc_data(&salt, sizeof(rndbuf) * 2); 232 if (retval) 233 return retval; 234 for (i = 0; i < sizeof(rndbuf); i++) { 235 salt.data[i * 2] = 0x40 | (rndbuf[i] >> 4); 236 salt.data[i * 2 + 1] = 0x40 | (rndbuf[i] & 0xf); 237 } 238 239 salt_out->type = KRB5_KDB_SALTTYPE_SPECIAL; 240 salt_out->data = salt; 241 return 0; 242 } 243 244 /* 245 * Add key_data for a krb5_db_entry 246 * If passwd is NULL the assumes that the caller wants a random password. 247 */ 248 static krb5_error_code 249 add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd, 250 db_entry, kvno) 251 krb5_context context; 252 krb5_keyblock * master_key; 253 krb5_key_salt_tuple * ks_tuple; 254 int ks_tuple_count; 255 const char * passwd; 256 krb5_db_entry * db_entry; 257 int kvno; 258 { 259 krb5_error_code retval; 260 krb5_keysalt key_salt; 261 krb5_keyblock key; 262 krb5_data pwd; 263 int i, j; 264 krb5_key_data *kd_slot; 265 266 for (i = 0; i < ks_tuple_count; i++) { 267 krb5_boolean similar; 268 269 similar = 0; 270 271 /* 272 * We could use krb5_keysalt_iterate to replace this loop, or use 273 * krb5_keysalt_is_present for the loop below, but we want to avoid 274 * circular library dependencies. 275 */ 276 for (j = 0; j < i; j++) { 277 if ((retval = krb5_c_enctype_compare(context, 278 ks_tuple[i].ks_enctype, 279 ks_tuple[j].ks_enctype, 280 &similar))) 281 return(retval); 282 283 if (similar && 284 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype)) 285 break; 286 } 287 288 if (j < i) 289 continue; 290 291 if ((retval = krb5_dbe_create_key_data(context, db_entry))) 292 return(retval); 293 kd_slot = &db_entry->key_data[db_entry->n_key_data - 1]; 294 295 /* Convert password string to key using appropriate salt */ 296 switch (key_salt.type = ks_tuple[i].ks_salttype) { 297 case KRB5_KDB_SALTTYPE_ONLYREALM: { 298 krb5_data * saltdata; 299 if ((retval = krb5_copy_data(context, krb5_princ_realm(context, 300 db_entry->princ), &saltdata))) 301 return(retval); 302 303 key_salt.data = *saltdata; 304 free(saltdata); 305 } 306 break; 307 case KRB5_KDB_SALTTYPE_NOREALM: 308 if ((retval=krb5_principal2salt_norealm(context, db_entry->princ, 309 &key_salt.data))) 310 return(retval); 311 break; 312 case KRB5_KDB_SALTTYPE_NORMAL: 313 if ((retval = krb5_principal2salt(context, db_entry->princ, 314 &key_salt.data))) 315 return(retval); 316 break; 317 case KRB5_KDB_SALTTYPE_SPECIAL: 318 retval = make_random_salt(context, &key_salt); 319 if (retval) 320 return retval; 321 break; 322 default: 323 return(KRB5_KDB_BAD_SALTTYPE); 324 } 325 326 pwd = string2data((char *)passwd); 327 328 retval = krb5_c_string_to_key_with_params(context, 329 ks_tuple[i].ks_enctype, 330 &pwd, &key_salt.data, 331 NULL, &key); 332 if (retval) { 333 free(key_salt.data.data); 334 return retval; 335 } 336 337 retval = krb5_dbe_encrypt_key_data(context, master_key, &key, 338 (const krb5_keysalt *)&key_salt, 339 kvno, kd_slot); 340 if (key_salt.data.data) 341 free(key_salt.data.data); 342 free(key.contents); 343 344 if( retval ) 345 return retval; 346 } 347 348 return 0; 349 } 350 351 static krb5_error_code 352 rekey(krb5_context context, krb5_keyblock *mkey, krb5_key_salt_tuple *ks_tuple, 353 int ks_tuple_count, const char *password, int new_kvno, 354 enum save savekeys, krb5_db_entry *db_entry) 355 { 356 krb5_error_code ret; 357 krb5_key_data *key_data; 358 int n_key_data, old_kvno, save_kvno; 359 360 /* Save aside the old key data. */ 361 n_key_data = db_entry->n_key_data; 362 key_data = db_entry->key_data; 363 db_entry->n_key_data = 0; 364 db_entry->key_data = NULL; 365 366 /* Make sure the new kvno is greater than the old largest kvno. */ 367 old_kvno = krb5_db_get_key_data_kvno(context, n_key_data, key_data); 368 if (new_kvno < old_kvno + 1) 369 new_kvno = old_kvno + 1; 370 /* Wrap from 65535 to 1; we can only store 16-bit kvno values in key_data, 371 * and we assign special meaning to kvno 0. */ 372 if (new_kvno == (1 << 16)) 373 new_kvno = 1; 374 375 /* Add new keys to the front of the list. */ 376 if (password != NULL) { 377 ret = add_key_pwd(context, mkey, ks_tuple, ks_tuple_count, password, 378 db_entry, new_kvno); 379 } else { 380 ret = add_key_rnd(context, mkey, ks_tuple, ks_tuple_count, db_entry, 381 new_kvno); 382 } 383 if (ret) { 384 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); 385 db_entry->n_key_data = n_key_data; 386 db_entry->key_data = key_data; 387 return ret; 388 } 389 390 /* Possibly add some or all of the old keys to the back of the list. May 391 * steal from and zero out some of the old key data entries. */ 392 if (savekeys != DISCARD_ALL) { 393 save_kvno = (savekeys == KEEP_LAST_KVNO) ? old_kvno : 0; 394 ret = preserve_old_keys(context, mkey, db_entry, save_kvno, n_key_data, 395 key_data); 396 } 397 398 /* Free any old key data entries not stolen and zeroed out above. */ 399 cleanup_key_data(context, n_key_data, key_data); 400 return ret; 401 } 402 403 /* 404 * Change random key for a krb5_db_entry 405 * Assumes the max kvno 406 * 407 * As a side effect all old keys are nuked if keepold is false. 408 */ 409 krb5_error_code 410 krb5_dbe_crk(krb5_context context, krb5_keyblock *mkey, 411 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, 412 krb5_boolean keepold, krb5_db_entry *dbent) 413 { 414 return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0, 415 keepold ? KEEP_ALL : DISCARD_ALL, dbent); 416 } 417 418 /* 419 * Add random key for a krb5_db_entry 420 * Assumes the max kvno 421 * 422 * As a side effect all old keys older than the max kvno are nuked. 423 */ 424 krb5_error_code 425 krb5_dbe_ark(krb5_context context, krb5_keyblock *mkey, 426 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, 427 krb5_db_entry *dbent) 428 { 429 return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0, 430 KEEP_LAST_KVNO, dbent); 431 } 432 433 /* 434 * Change password for a krb5_db_entry 435 * Assumes the max kvno 436 * 437 * As a side effect all old keys are nuked if keepold is false. 438 */ 439 krb5_error_code 440 krb5_dbe_def_cpw(krb5_context context, krb5_keyblock *mkey, 441 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, 442 char *password, int new_kvno, krb5_boolean keepold, 443 krb5_db_entry *dbent) 444 { 445 return rekey(context, mkey, ks_tuple, ks_tuple_count, password, new_kvno, 446 keepold ? KEEP_ALL : DISCARD_ALL, dbent); 447 } 448 449 /* 450 * Add password for a krb5_db_entry 451 * Assumes the max kvno 452 * 453 * As a side effect all old keys older than the max kvno are nuked. 454 */ 455 krb5_error_code 456 krb5_dbe_apw(krb5_context context, krb5_keyblock *mkey, 457 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, char *password, 458 krb5_db_entry *dbent) 459 { 460 return rekey(context, mkey, ks_tuple, ks_tuple_count, password, 0, 461 KEEP_LAST_KVNO, dbent); 462 } 463