1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * lib/kdb/kdb_cpw.c 8 * 9 * Copyright 1995 by the Massachusetts Institute of Technology. 10 * All Rights Reserved. 11 * 12 * Export of this software from the United States of America may 13 * require a specific license from the United States Government. 14 * It is the responsibility of any person or organization contemplating 15 * export to obtain such a license before exporting. 16 * 17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 18 * distribute this software and its documentation for any purpose and 19 * without fee is hereby granted, provided that the above copyright 20 * notice appear in all copies and that both that copyright notice and 21 * this permission notice appear in supporting documentation, and that 22 * the name of M.I.T. not be used in advertising or publicity pertaining 23 * to distribution of the software without specific, written prior 24 * permission. Furthermore if you modify this software you must label 25 * your software as modified software and not distribute it in such a 26 * fashion that it might be confused with the original M.I.T. software. 27 * M.I.T. makes no representations about the suitability of 28 * this software for any purpose. It is provided "as is" without express 29 * or implied warranty. 30 * 31 */ 32 33 /* 34 * Copyright (C) 1998 by the FundsXpress, INC. 35 * 36 * All rights reserved. 37 * 38 * Export of this software from the United States of America may require 39 * a specific license from the United States Government. It is the 40 * responsibility of any person or organization contemplating export to 41 * obtain such a license before exporting. 42 * 43 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 44 * distribute this software and its documentation for any purpose and 45 * without fee is hereby granted, provided that the above copyright 46 * notice appear in all copies and that both that copyright notice and 47 * this permission notice appear in supporting documentation, and that 48 * the name of FundsXpress. not be used in advertising or publicity pertaining 49 * to distribution of the software without specific, written prior 50 * permission. FundsXpress makes no representations about the suitability of 51 * this software for any purpose. It is provided "as is" without express 52 * or implied warranty. 53 * 54 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 55 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 56 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 57 */ 58 59 #include "k5-int.h" 60 #include "kdb.h" 61 #include <stdio.h> 62 #include <errno.h> 63 64 static int 65 get_key_data_kvno(context, count, data) 66 krb5_context context; 67 int count; 68 krb5_key_data * data; 69 { 70 int i, kvno; 71 /* Find last key version number */ 72 for (kvno = i = 0; i < count; i++) { 73 if (kvno < data[i].key_data_kvno) { 74 kvno = data[i].key_data_kvno; 75 } 76 } 77 return(kvno); 78 } 79 80 static void 81 cleanup_key_data(context, count, data) 82 krb5_context context; 83 int count; 84 krb5_key_data * data; 85 { 86 int i, j; 87 88 /* If data is NULL, count is always 0 */ 89 if (data == NULL) return; 90 91 for (i = 0; i < count; i++) { 92 for (j = 0; j < data[i].key_data_ver; j++) { 93 if (data[i].key_data_length[j]) { 94 krb5_db_free(context, data[i].key_data_contents[j]); 95 } 96 } 97 } 98 krb5_db_free(context, data); 99 } 100 101 static krb5_error_code 102 add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno) 103 krb5_context context; 104 krb5_keyblock * master_key; 105 krb5_key_salt_tuple * ks_tuple; 106 int ks_tuple_count; 107 krb5_db_entry * db_entry; 108 int kvno; 109 { 110 krb5_principal krbtgt_princ; 111 krb5_keyblock key; 112 krb5_db_entry krbtgt_entry; 113 krb5_boolean more; 114 int max_kvno, one, i, j, k; 115 krb5_error_code retval; 116 krb5_key_data tmp_key_data; 117 krb5_key_data *tptr; 118 119 memset( &tmp_key_data, 0, sizeof(tmp_key_data)); 120 121 122 retval = krb5_build_principal_ext(context, &krbtgt_princ, 123 db_entry->princ->realm.length, 124 db_entry->princ->realm.data, 125 KRB5_TGS_NAME_SIZE, 126 KRB5_TGS_NAME, 127 db_entry->princ->realm.length, 128 db_entry->princ->realm.data, 129 0); 130 if (retval) 131 return retval; 132 133 /* Get tgt from database */ 134 retval = krb5_db_get_principal(context, krbtgt_princ, &krbtgt_entry, 135 &one, &more); 136 krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */ 137 if (retval) 138 return(retval); 139 if ((one > 1) || (more)) { 140 krb5_db_free_principal(context, &krbtgt_entry, one); 141 return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; 142 } 143 if (!one) 144 return KRB5_KDB_NOENTRY; 145 146 /* Get max kvno */ 147 for (max_kvno = j = 0; j < krbtgt_entry.n_key_data; j++) { 148 if (max_kvno < krbtgt_entry.key_data[j].key_data_kvno) { 149 max_kvno = krbtgt_entry.key_data[j].key_data_kvno; 150 } 151 } 152 153 for (i = 0; i < ks_tuple_count; i++) { 154 krb5_boolean similar; 155 156 similar = 0; 157 158 /* 159 * We could use krb5_keysalt_iterate to replace this loop, or use 160 * krb5_keysalt_is_present for the loop below, but we want to avoid 161 * circular library dependencies. 162 */ 163 for (j = 0; j < i; j++) { 164 if ((retval = krb5_c_enctype_compare(context, 165 ks_tuple[i].ks_enctype, 166 ks_tuple[j].ks_enctype, 167 &similar))) 168 return(retval); 169 170 if (similar) 171 break; 172 } 173 174 if (similar) 175 continue; 176 177 if ((retval = krb5_dbe_create_key_data(context, db_entry))) 178 goto add_key_rnd_err; 179 180 /* there used to be code here to extract the old key, and derive 181 a new key from it. Now that there's a unified prng, that isn't 182 necessary. */ 183 184 /* make new key */ 185 if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype, 186 &key))) 187 goto add_key_rnd_err; 188 189 190 /* db library will free this. Since, its a so, it could actually be using different memory management 191 function. So, its better if the memory is allocated by the db's malloc. So, a temporary memory is used 192 here which will later be copied to the db_entry */ 193 retval = krb5_dbekd_encrypt_key_data(context, master_key, 194 &key, NULL, kvno, 195 &tmp_key_data); 196 197 krb5_free_keyblock_contents(context, &key); 198 if( retval ) 199 goto add_key_rnd_err; 200 201 tptr = &db_entry->key_data[db_entry->n_key_data-1]; 202 203 tptr->key_data_ver = tmp_key_data.key_data_ver; 204 tptr->key_data_kvno = tmp_key_data.key_data_kvno; 205 206 for( k = 0; k < tmp_key_data.key_data_ver; k++ ) 207 { 208 tptr->key_data_type[k] = tmp_key_data.key_data_type[k]; 209 tptr->key_data_length[k] = tmp_key_data.key_data_length[k]; 210 if( tmp_key_data.key_data_contents[k] ) 211 { 212 tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]); 213 if( tptr->key_data_contents[k] == NULL ) 214 { 215 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); 216 db_entry->key_data = NULL; 217 db_entry->n_key_data = 0; 218 retval = ENOMEM; 219 goto add_key_rnd_err; 220 } 221 memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]); 222 223 memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]); 224 free( tmp_key_data.key_data_contents[k] ); 225 tmp_key_data.key_data_contents[k] = NULL; 226 } 227 } 228 229 } 230 231 add_key_rnd_err: 232 krb5_db_free_principal(context, &krbtgt_entry, one); 233 234 for( i = 0; i < tmp_key_data.key_data_ver; i++ ) 235 { 236 if( tmp_key_data.key_data_contents[i] ) 237 { 238 memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]); 239 free( tmp_key_data.key_data_contents[i] ); 240 } 241 } 242 return(retval); 243 } 244 245 /* 246 * Change random key for a krb5_db_entry 247 * Assumes the max kvno 248 * 249 * As a side effect all old keys are nuked if keepold is false. 250 */ 251 krb5_error_code 252 krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry) 253 krb5_context context; 254 krb5_keyblock * master_key; 255 krb5_key_salt_tuple * ks_tuple; 256 int ks_tuple_count; 257 krb5_boolean keepold; 258 krb5_db_entry * db_entry; 259 { 260 int key_data_count; 261 int n_new_key_data; 262 krb5_key_data * key_data; 263 krb5_error_code retval; 264 int kvno; 265 int i; 266 267 /* First save the old keydata */ 268 kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data); 269 key_data_count = db_entry->n_key_data; 270 key_data = db_entry->key_data; 271 db_entry->key_data = NULL; 272 db_entry->n_key_data = 0; 273 274 /* increment the kvno */ 275 kvno++; 276 277 retval = add_key_rnd(context, master_key, ks_tuple, 278 ks_tuple_count, db_entry, kvno); 279 if (retval) { 280 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); 281 db_entry->n_key_data = key_data_count; 282 db_entry->key_data = key_data; 283 } else if (keepold) { 284 n_new_key_data = db_entry->n_key_data; 285 for (i = 0; i < key_data_count; i++) { 286 retval = krb5_dbe_create_key_data(context, db_entry); 287 if (retval) { 288 cleanup_key_data(context, db_entry->n_key_data, 289 db_entry->key_data); 290 break; 291 } 292 db_entry->key_data[i+n_new_key_data] = key_data[i]; 293 memset(&key_data[i], 0, sizeof(krb5_key_data)); 294 } 295 krb5_db_free(context, key_data); /* we moved the cotents to new memory. But, the original block which contained the data */ 296 } else { 297 cleanup_key_data(context, key_data_count, key_data); 298 } 299 return(retval); 300 } 301 302 /* 303 * Add random key for a krb5_db_entry 304 * Assumes the max kvno 305 * 306 * As a side effect all old keys older than the max kvno are nuked. 307 */ 308 krb5_error_code 309 krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry) 310 krb5_context context; 311 krb5_keyblock * master_key; 312 krb5_key_salt_tuple * ks_tuple; 313 int ks_tuple_count; 314 krb5_db_entry * db_entry; 315 { 316 int key_data_count; 317 krb5_key_data * key_data; 318 krb5_error_code retval; 319 int kvno; 320 int i; 321 322 /* First save the old keydata */ 323 kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data); 324 key_data_count = db_entry->n_key_data; 325 key_data = db_entry->key_data; 326 db_entry->key_data = NULL; 327 db_entry->n_key_data = 0; 328 329 /* increment the kvno */ 330 kvno++; 331 332 if ((retval = add_key_rnd(context, master_key, ks_tuple, 333 ks_tuple_count, db_entry, kvno))) { 334 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); 335 db_entry->n_key_data = key_data_count; 336 db_entry->key_data = key_data; 337 } else { 338 /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */ 339 for (i = 0; i < key_data_count; i++) { 340 if (key_data[i].key_data_kvno == (kvno - 1)) { 341 if ((retval = krb5_dbe_create_key_data(context, db_entry))) { 342 cleanup_key_data(context, db_entry->n_key_data, 343 db_entry->key_data); 344 break; 345 } 346 /* We should decrypt/re-encrypt the data to use the same mkvno*/ 347 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i]; 348 memset(&key_data[i], 0, sizeof(krb5_key_data)); 349 } 350 } 351 cleanup_key_data(context, key_data_count, key_data); 352 } 353 return(retval); 354 } 355 356 /* 357 * Add key_data for a krb5_db_entry 358 * If passwd is NULL the assumes that the caller wants a random password. 359 */ 360 static krb5_error_code 361 add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd, 362 db_entry, kvno) 363 krb5_context context; 364 krb5_keyblock * master_key; 365 krb5_key_salt_tuple * ks_tuple; 366 int ks_tuple_count; 367 char * passwd; 368 krb5_db_entry * db_entry; 369 int kvno; 370 { 371 krb5_error_code retval; 372 krb5_keysalt key_salt; 373 krb5_keyblock key; 374 krb5_data pwd; 375 int i, j, k; 376 krb5_key_data tmp_key_data; 377 krb5_key_data *tptr; 378 379 memset( &tmp_key_data, 0, sizeof(tmp_key_data)); 380 381 retval = 0; 382 383 for (i = 0; i < ks_tuple_count; i++) { 384 krb5_boolean similar; 385 386 similar = 0; 387 388 /* 389 * We could use krb5_keysalt_iterate to replace this loop, or use 390 * krb5_keysalt_is_present for the loop below, but we want to avoid 391 * circular library dependencies. 392 */ 393 for (j = 0; j < i; j++) { 394 if ((retval = krb5_c_enctype_compare(context, 395 ks_tuple[i].ks_enctype, 396 ks_tuple[j].ks_enctype, 397 &similar))) 398 return(retval); 399 400 if (similar && 401 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype)) 402 break; 403 } 404 405 if (j < i) 406 continue; 407 408 if ((retval = krb5_dbe_create_key_data(context, db_entry))) 409 return(retval); 410 411 /* Convert password string to key using appropriate salt */ 412 switch (key_salt.type = ks_tuple[i].ks_salttype) { 413 case KRB5_KDB_SALTTYPE_ONLYREALM: { 414 krb5_data * saltdata; 415 if ((retval = krb5_copy_data(context, krb5_princ_realm(context, 416 db_entry->princ), &saltdata))) 417 return(retval); 418 419 key_salt.data = *saltdata; 420 krb5_xfree(saltdata); 421 } 422 break; 423 case KRB5_KDB_SALTTYPE_NOREALM: 424 if ((retval=krb5_principal2salt_norealm(context, db_entry->princ, 425 &key_salt.data))) 426 return(retval); 427 break; 428 case KRB5_KDB_SALTTYPE_NORMAL: 429 if ((retval = krb5_principal2salt(context, db_entry->princ, 430 &key_salt.data))) 431 return(retval); 432 break; 433 case KRB5_KDB_SALTTYPE_V4: 434 key_salt.data.length = 0; 435 key_salt.data.data = 0; 436 break; 437 case KRB5_KDB_SALTTYPE_AFS3: { 438 #if 0 439 krb5_data * saltdata; 440 if (retval = krb5_copy_data(context, krb5_princ_realm(context, 441 db_entry->princ), &saltdata)) 442 return(retval); 443 444 key_salt.data = *saltdata; 445 key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/ 446 krb5_xfree(saltdata); 447 #else 448 /* Why do we do this? Well, the afs_mit_string_to_key needs to 449 use strlen, and the realm is not NULL terminated.... */ 450 unsigned int slen = 451 (*krb5_princ_realm(context,db_entry->princ)).length; 452 if(!(key_salt.data.data = (char *) malloc(slen+1))) 453 return ENOMEM; 454 key_salt.data.data[slen] = 0; 455 memcpy((char *)key_salt.data.data, 456 (char *)(*krb5_princ_realm(context,db_entry->princ)).data, 457 slen); 458 key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/ 459 #endif 460 461 } 462 break; 463 default: 464 return(KRB5_KDB_BAD_SALTTYPE); 465 } 466 467 pwd.data = passwd; 468 pwd.length = strlen(passwd); 469 470 /* Solaris Kerberos */ 471 memset(&key, 0, sizeof (krb5_keyblock)); 472 473 /* AFS string to key will happen here */ 474 if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype, 475 &pwd, &key_salt.data, &key))) { 476 if (key_salt.data.data) 477 free(key_salt.data.data); 478 return(retval); 479 } 480 481 if (key_salt.data.length == SALT_TYPE_AFS_LENGTH) 482 key_salt.data.length = 483 krb5_princ_realm(context, db_entry->princ)->length; 484 485 /* memory allocation to be done by db. So, use temporary block and later copy 486 it to the memory allocated by db */ 487 retval = krb5_dbekd_encrypt_key_data(context, master_key, &key, 488 (const krb5_keysalt *)&key_salt, 489 kvno, &tmp_key_data); 490 if (key_salt.data.data) 491 free(key_salt.data.data); 492 493 /* Solaris Kerberos */ 494 krb5_free_keyblock_contents(context, &key); 495 496 if( retval ) 497 return retval; 498 499 tptr = &db_entry->key_data[db_entry->n_key_data-1]; 500 501 tptr->key_data_ver = tmp_key_data.key_data_ver; 502 tptr->key_data_kvno = tmp_key_data.key_data_kvno; 503 504 for( k = 0; k < tmp_key_data.key_data_ver; k++ ) 505 { 506 tptr->key_data_type[k] = tmp_key_data.key_data_type[k]; 507 tptr->key_data_length[k] = tmp_key_data.key_data_length[k]; 508 if( tmp_key_data.key_data_contents[k] ) 509 { 510 tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]); 511 if( tptr->key_data_contents[k] == NULL ) 512 { 513 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); 514 db_entry->key_data = NULL; 515 db_entry->n_key_data = 0; 516 retval = ENOMEM; 517 goto add_key_pwd_err; 518 } 519 memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]); 520 521 memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]); 522 free( tmp_key_data.key_data_contents[k] ); 523 tmp_key_data.key_data_contents[k] = NULL; 524 } 525 } 526 } 527 add_key_pwd_err: 528 for( i = 0; i < tmp_key_data.key_data_ver; i++ ) 529 { 530 if( tmp_key_data.key_data_contents[i] ) 531 { 532 memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]); 533 free( tmp_key_data.key_data_contents[i] ); 534 } 535 } 536 537 return(retval); 538 } 539 540 /* 541 * Change password for a krb5_db_entry 542 * Assumes the max kvno 543 * 544 * As a side effect all old keys are nuked if keepold is false. 545 */ 546 krb5_error_code 547 krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd, 548 new_kvno, keepold, db_entry) 549 krb5_context context; 550 krb5_keyblock * master_key; 551 krb5_key_salt_tuple * ks_tuple; 552 int ks_tuple_count; 553 char * passwd; 554 int new_kvno; 555 krb5_boolean keepold; 556 krb5_db_entry * db_entry; 557 { 558 int key_data_count; 559 int n_new_key_data; 560 krb5_key_data * key_data; 561 krb5_error_code retval; 562 int old_kvno; 563 int i; 564 565 /* First save the old keydata */ 566 old_kvno = get_key_data_kvno(context, db_entry->n_key_data, 567 db_entry->key_data); 568 key_data_count = db_entry->n_key_data; 569 key_data = db_entry->key_data; 570 db_entry->key_data = NULL; 571 db_entry->n_key_data = 0; 572 573 /* increment the kvno. if the requested kvno is too small, 574 increment the old kvno */ 575 if (new_kvno < old_kvno+1) 576 new_kvno = old_kvno+1; 577 578 retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, 579 passwd, db_entry, new_kvno); 580 if (retval) { 581 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); 582 db_entry->n_key_data = key_data_count; 583 db_entry->key_data = key_data; 584 } else if (keepold) { 585 n_new_key_data = db_entry->n_key_data; 586 for (i = 0; i < key_data_count; i++) { 587 retval = krb5_dbe_create_key_data(context, db_entry); 588 if (retval) { 589 cleanup_key_data(context, db_entry->n_key_data, 590 db_entry->key_data); 591 break; 592 } 593 db_entry->key_data[i+n_new_key_data] = key_data[i]; 594 memset(&key_data[i], 0, sizeof(krb5_key_data)); 595 } 596 krb5_db_free( context, key_data ); 597 } else { 598 cleanup_key_data(context, key_data_count, key_data); 599 } 600 return(retval); 601 } 602 603 /* 604 * Add password for a krb5_db_entry 605 * Assumes the max kvno 606 * 607 * As a side effect all old keys older than the max kvno are nuked. 608 */ 609 krb5_error_code 610 krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry) 611 krb5_context context; 612 krb5_keyblock * master_key; 613 krb5_key_salt_tuple * ks_tuple; 614 int ks_tuple_count; 615 char * passwd; 616 krb5_db_entry * db_entry; 617 { 618 int key_data_count; 619 krb5_key_data * key_data; 620 krb5_error_code retval; 621 int old_kvno, new_kvno; 622 int i; 623 624 /* First save the old keydata */ 625 old_kvno = get_key_data_kvno(context, db_entry->n_key_data, 626 db_entry->key_data); 627 key_data_count = db_entry->n_key_data; 628 key_data = db_entry->key_data; 629 db_entry->key_data = NULL; 630 db_entry->n_key_data = 0; 631 632 /* increment the kvno */ 633 new_kvno = old_kvno+1; 634 635 if ((retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, 636 passwd, db_entry, new_kvno))) { 637 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); 638 db_entry->n_key_data = key_data_count; 639 db_entry->key_data = key_data; 640 } else { 641 /* Copy keys with key_data_kvno == old_kvno */ 642 for (i = 0; i < key_data_count; i++) { 643 if (key_data[i].key_data_kvno == old_kvno) { 644 if ((retval = krb5_dbe_create_key_data(context, db_entry))) { 645 cleanup_key_data(context, db_entry->n_key_data, 646 db_entry->key_data); 647 break; 648 } 649 /* We should decrypt/re-encrypt the data to use the same mkvno*/ 650 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i]; 651 memset(&key_data[i], 0, sizeof(krb5_key_data)); 652 } 653 } 654 cleanup_key_data(context, key_data_count, key_data); 655 } 656 return(retval); 657 } 658 659 660