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 int 58 krb5_db_get_key_data_kvno(krb5_context context, int count, krb5_key_data *data) 59 { 60 int i, kvno; 61 /* Find last key version number */ 62 for (kvno = i = 0; i < count; i++) { 63 if (kvno < data[i].key_data_kvno) { 64 kvno = data[i].key_data_kvno; 65 } 66 } 67 return(kvno); 68 } 69 70 static void 71 cleanup_key_data(krb5_context context, int count, krb5_key_data *data) 72 { 73 int i; 74 75 /* If data is NULL, count is always 0 */ 76 if (data == NULL) return; 77 78 for (i = 0; i < count; i++) 79 krb5_dbe_free_key_data_contents(context, &data[i]); 80 free(data); 81 } 82 83 /* Transfer key data from old_kd to new_kd, making sure that new_kd is 84 * encrypted with mkey. May steal from old_kd and zero it out. */ 85 static krb5_error_code 86 preserve_one_old_key(krb5_context context, krb5_keyblock *mkey, 87 krb5_db_entry *dbent, krb5_key_data *old_kd, 88 krb5_key_data *new_kd) 89 { 90 krb5_error_code ret; 91 krb5_keyblock kb; 92 krb5_keysalt salt; 93 94 memset(new_kd, 0, sizeof(*new_kd)); 95 96 ret = krb5_dbe_decrypt_key_data(context, mkey, old_kd, &kb, NULL); 97 if (ret == 0) { 98 /* old_kd is already encrypted in mkey, so just move it. */ 99 *new_kd = *old_kd; 100 memset(old_kd, 0, sizeof(*old_kd)); 101 krb5_free_keyblock_contents(context, &kb); 102 return 0; 103 } 104 105 /* Decrypt and re-encrypt old_kd using mkey. */ 106 ret = krb5_dbe_decrypt_key_data(context, NULL, old_kd, &kb, &salt); 107 if (ret) 108 return ret; 109 ret = krb5_dbe_encrypt_key_data(context, mkey, &kb, &salt, 110 old_kd->key_data_kvno, new_kd); 111 krb5_free_keyblock_contents(context, &kb); 112 krb5_free_data_contents(context, &salt.data); 113 return ret; 114 } 115 116 /* 117 * Add key_data to dbent, making sure that each entry is encrypted in mkey. If 118 * keepold is greater than 1, preserve only the first (keepold-1) key versions, 119 * so that the total number of key versions including the new key set is 120 * keepold. May steal some elements from key_data and zero them out. 121 */ 122 static krb5_error_code 123 preserve_old_keys(krb5_context context, krb5_keyblock *mkey, 124 krb5_db_entry *dbent, unsigned int keepold, int n_key_data, 125 krb5_key_data *key_data) 126 { 127 krb5_error_code ret; 128 krb5_kvno last_kvno = 0, kvno_changes = 0; 129 int i; 130 131 for (i = 0; i < n_key_data; i++) { 132 if (keepold > 1) { 133 if (i > 0 && key_data[i].key_data_kvno != last_kvno) 134 kvno_changes++; 135 if (kvno_changes >= keepold - 1) 136 break; 137 last_kvno = key_data[i].key_data_kvno; 138 } 139 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(krb5_context context, krb5_keyblock *master_key, 153 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, 154 krb5_db_entry *db_entry, int kvno) 155 { 156 krb5_keyblock key; 157 int i, j; 158 krb5_error_code retval; 159 krb5_key_data *kd_slot; 160 161 for (i = 0; i < ks_tuple_count; i++) { 162 krb5_boolean similar; 163 164 similar = 0; 165 166 /* 167 * We could use krb5_keysalt_iterate to replace this loop, or use 168 * krb5_keysalt_is_present for the loop below, but we want to avoid 169 * circular library dependencies. 170 */ 171 for (j = 0; j < i; j++) { 172 if ((retval = krb5_c_enctype_compare(context, 173 ks_tuple[i].ks_enctype, 174 ks_tuple[j].ks_enctype, 175 &similar))) 176 return(retval); 177 178 if (similar) 179 break; 180 } 181 182 if (similar) 183 continue; 184 185 if ((retval = krb5_dbe_create_key_data(context, db_entry))) 186 return retval; 187 kd_slot = &db_entry->key_data[db_entry->n_key_data - 1]; 188 189 /* there used to be code here to extract the old key, and derive 190 a new key from it. Now that there's a unified prng, that isn't 191 necessary. */ 192 193 /* make new key */ 194 if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype, 195 &key))) 196 return retval; 197 198 retval = krb5_dbe_encrypt_key_data(context, master_key, &key, NULL, 199 kvno, kd_slot); 200 201 krb5_free_keyblock_contents(context, &key); 202 if( retval ) 203 return retval; 204 } 205 206 return 0; 207 } 208 209 /* Construct a random explicit salt. */ 210 static krb5_error_code 211 make_random_salt(krb5_context context, krb5_keysalt *salt_out) 212 { 213 krb5_error_code retval; 214 unsigned char rndbuf[8]; 215 krb5_data salt, rnd = make_data(rndbuf, sizeof(rndbuf)); 216 unsigned int i; 217 218 /* 219 * Salts are limited by RFC 4120 to 7-bit ASCII. For ease of examination 220 * and to avoid certain folding issues for older enctypes, we use printable 221 * characters with four fixed bits and four random bits, encoding 64 222 * psuedo-random bits into 16 bytes. 223 */ 224 retval = krb5_c_random_make_octets(context, &rnd); 225 if (retval) 226 return retval; 227 retval = alloc_data(&salt, sizeof(rndbuf) * 2); 228 if (retval) 229 return retval; 230 for (i = 0; i < sizeof(rndbuf); i++) { 231 salt.data[i * 2] = 0x40 | (rndbuf[i] >> 4); 232 salt.data[i * 2 + 1] = 0x40 | (rndbuf[i] & 0xf); 233 } 234 235 salt_out->type = KRB5_KDB_SALTTYPE_SPECIAL; 236 salt_out->data = salt; 237 return 0; 238 } 239 240 /* 241 * Add key_data for a krb5_db_entry 242 * If passwd is NULL the assumes that the caller wants a random password. 243 */ 244 static krb5_error_code 245 add_key_pwd(krb5_context context, krb5_keyblock *master_key, 246 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, 247 const char *passwd, krb5_db_entry *db_entry, int kvno) 248 { 249 krb5_error_code retval; 250 krb5_keysalt key_salt; 251 krb5_keyblock key; 252 krb5_data pwd; 253 int i, j; 254 krb5_key_data *kd_slot; 255 256 for (i = 0; i < ks_tuple_count; i++) { 257 krb5_boolean similar; 258 259 similar = 0; 260 261 /* 262 * We could use krb5_keysalt_iterate to replace this loop, or use 263 * krb5_keysalt_is_present for the loop below, but we want to avoid 264 * circular library dependencies. 265 */ 266 for (j = 0; j < i; j++) { 267 if ((retval = krb5_c_enctype_compare(context, 268 ks_tuple[i].ks_enctype, 269 ks_tuple[j].ks_enctype, 270 &similar))) 271 return(retval); 272 273 if (similar) 274 break; 275 } 276 277 if (j < i) 278 continue; 279 280 if ((retval = krb5_dbe_create_key_data(context, db_entry))) 281 return(retval); 282 kd_slot = &db_entry->key_data[db_entry->n_key_data - 1]; 283 284 /* Convert password string to key using appropriate salt */ 285 switch (key_salt.type = ks_tuple[i].ks_salttype) { 286 case KRB5_KDB_SALTTYPE_ONLYREALM: { 287 krb5_data * saltdata; 288 if ((retval = krb5_copy_data(context, krb5_princ_realm(context, 289 db_entry->princ), &saltdata))) 290 return(retval); 291 292 key_salt.data = *saltdata; 293 free(saltdata); 294 } 295 break; 296 case KRB5_KDB_SALTTYPE_NOREALM: 297 if ((retval=krb5_principal2salt_norealm(context, db_entry->princ, 298 &key_salt.data))) 299 return(retval); 300 break; 301 case KRB5_KDB_SALTTYPE_NORMAL: 302 if ((retval = krb5_principal2salt(context, db_entry->princ, 303 &key_salt.data))) 304 return(retval); 305 break; 306 case KRB5_KDB_SALTTYPE_SPECIAL: 307 retval = make_random_salt(context, &key_salt); 308 if (retval) 309 return retval; 310 break; 311 default: 312 return(KRB5_KDB_BAD_SALTTYPE); 313 } 314 315 pwd = string2data((char *)passwd); 316 317 retval = krb5_c_string_to_key_with_params(context, 318 ks_tuple[i].ks_enctype, 319 &pwd, &key_salt.data, 320 NULL, &key); 321 if (retval) { 322 free(key_salt.data.data); 323 return retval; 324 } 325 326 retval = krb5_dbe_encrypt_key_data(context, master_key, &key, 327 (const krb5_keysalt *)&key_salt, 328 kvno, kd_slot); 329 if (key_salt.data.data) 330 free(key_salt.data.data); 331 free(key.contents); 332 333 if( retval ) 334 return retval; 335 } 336 337 return 0; 338 } 339 340 static krb5_error_code 341 rekey(krb5_context context, krb5_keyblock *mkey, krb5_key_salt_tuple *ks_tuple, 342 int ks_tuple_count, const char *password, int new_kvno, 343 unsigned int keepold, krb5_db_entry *db_entry) 344 { 345 krb5_error_code ret; 346 krb5_key_data *key_data; 347 int n_key_data, old_kvno; 348 349 /* Save aside the old key data. */ 350 n_key_data = db_entry->n_key_data; 351 key_data = db_entry->key_data; 352 db_entry->n_key_data = 0; 353 db_entry->key_data = NULL; 354 355 /* Make sure the new kvno is greater than the old largest kvno. */ 356 old_kvno = krb5_db_get_key_data_kvno(context, n_key_data, key_data); 357 if (new_kvno < old_kvno + 1) 358 new_kvno = old_kvno + 1; 359 /* Wrap from 65535 to 1; we can only store 16-bit kvno values in key_data, 360 * and we assign special meaning to kvno 0. */ 361 if (new_kvno == (1 << 16)) 362 new_kvno = 1; 363 364 /* Add new keys to the front of the list. */ 365 if (password != NULL) { 366 ret = add_key_pwd(context, mkey, ks_tuple, ks_tuple_count, password, 367 db_entry, new_kvno); 368 } else { 369 ret = add_key_rnd(context, mkey, ks_tuple, ks_tuple_count, db_entry, 370 new_kvno); 371 } 372 if (ret) { 373 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); 374 db_entry->n_key_data = n_key_data; 375 db_entry->key_data = key_data; 376 return ret; 377 } 378 379 /* Possibly add some or all of the old keys to the back of the list. May 380 * steal from and zero out some of the old key data entries. */ 381 if (keepold > 0) { 382 ret = preserve_old_keys(context, mkey, db_entry, keepold, n_key_data, 383 key_data); 384 } 385 386 /* Free any old key data entries not stolen and zeroed out above. */ 387 cleanup_key_data(context, n_key_data, key_data); 388 return ret; 389 } 390 391 /* 392 * Change random key for a krb5_db_entry 393 * Assumes the max kvno 394 * 395 * As a side effect all old keys are nuked if keepold is false. 396 */ 397 krb5_error_code 398 krb5_dbe_crk(krb5_context context, krb5_keyblock *mkey, 399 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, 400 unsigned int keepold, krb5_db_entry *dbent) 401 { 402 return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0, keepold, 403 dbent); 404 } 405 406 /* 407 * Add random key for a krb5_db_entry 408 * Assumes the max kvno 409 * 410 * As a side effect all old keys older than the max kvno are nuked. 411 */ 412 krb5_error_code 413 krb5_dbe_ark(krb5_context context, krb5_keyblock *mkey, 414 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, 415 krb5_db_entry *dbent) 416 { 417 return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0, 2, dbent); 418 } 419 420 /* 421 * Change password for a krb5_db_entry 422 * Assumes the max kvno 423 * 424 * As a side effect all old keys are nuked if keepold is false. 425 */ 426 krb5_error_code 427 krb5_dbe_def_cpw(krb5_context context, krb5_keyblock *mkey, 428 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, 429 char *password, int new_kvno, unsigned int keepold, 430 krb5_db_entry *dbent) 431 { 432 return rekey(context, mkey, ks_tuple, ks_tuple_count, password, new_kvno, 433 keepold, dbent); 434 } 435 436 /* 437 * Add password for a krb5_db_entry 438 * Assumes the max kvno 439 * 440 * As a side effect all old keys older than the max kvno are nuked. 441 */ 442 krb5_error_code 443 krb5_dbe_apw(krb5_context context, krb5_keyblock *mkey, 444 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, char *password, 445 krb5_db_entry *dbent) 446 { 447 return rekey(context, mkey, ks_tuple, ks_tuple_count, password, 0, 2, 448 dbent); 449 } 450