1 /* 2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 10 * 11 * Openvision retains the copyright to derivative works of 12 * this source code. Do *NOT* create a derivative of this 13 * source code before consulting with your legal department. 14 * Do *NOT* integrate *ANY* of this source code into another 15 * product before consulting with your legal department. 16 * 17 * For further information, read the top-level Openvision 18 * copyright which is contained in the top-level MIT Kerberos 19 * copyright. 20 * 21 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 22 * 23 */ 24 25 26 /* 27 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved 28 * 29 * $Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/svr_principal.c,v 1.30.8.1 2004/12/20 21:16:20 tlyu Exp $ 30 */ 31 32 #if !defined(lint) && !defined(__CODECENTER__) 33 static char *rcsid = "$Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/svr_principal.c,v 1.30.8.1 2004/12/20 21:16:20 tlyu Exp $"; 34 #endif 35 36 #include <sys/types.h> 37 #include <sys/time.h> 38 #include <kadm5/admin.h> 39 #include "adb.h" 40 #include "k5-int.h" 41 #include <krb5/kdb.h> 42 #include <stdio.h> 43 #include <string.h> 44 #include "server_internal.h" 45 #include <stdarg.h> 46 #include <stdlib.h> 47 #ifdef USE_PASSWORD_SERVER 48 #include <sys/wait.h> 49 #endif 50 51 extern krb5_principal master_princ; 52 extern krb5_principal hist_princ; 53 extern krb5_keyblock hist_key; 54 extern krb5_db_entry master_db; 55 extern krb5_db_entry hist_db; 56 extern krb5_kvno hist_kvno; 57 58 extern kadm5_ret_t 59 krb5_free_key_data_contents(krb5_context context, krb5_key_data *key); 60 61 static int decrypt_key_data(krb5_context context, 62 krb5_keyblock *, int n_key_data, krb5_key_data *key_data, 63 krb5_keyblock **keyblocks, int *n_keys); 64 65 /* 66 * XXX Functions that ought to be in libkrb5.a, but aren't. 67 */ 68 kadm5_ret_t krb5_copy_key_data_contents(context, from, to) 69 krb5_context context; 70 krb5_key_data *from, *to; 71 { 72 int i, idx; 73 74 *to = *from; 75 76 idx = (from->key_data_ver == 1 ? 1 : 2); 77 78 for (i = 0; i < idx; i++) { 79 if ( from->key_data_length[i] ) { 80 to->key_data_contents[i] = malloc(from->key_data_length[i]); 81 if (to->key_data_contents[i] == NULL) { 82 for (i = 0; i < idx; i++) { 83 if (to->key_data_contents[i]) { 84 memset(to->key_data_contents[i], 0, 85 to->key_data_length[i]); 86 free(to->key_data_contents[i]); 87 } 88 } 89 return ENOMEM; 90 } 91 memcpy(to->key_data_contents[i], from->key_data_contents[i], 92 from->key_data_length[i]); 93 } 94 } 95 return 0; 96 } 97 98 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl) 99 { 100 krb5_tl_data *n; 101 102 n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)); 103 if (n == NULL) 104 return NULL; 105 n->tl_data_contents = malloc(tl->tl_data_length); 106 if (n->tl_data_contents == NULL) { 107 free(n); 108 return NULL; 109 } 110 memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length); 111 n->tl_data_type = tl->tl_data_type; 112 n->tl_data_length = tl->tl_data_length; 113 n->tl_data_next = NULL; 114 return n; 115 } 116 117 /* This is in lib/kdb/kdb_cpw.c, but is static */ 118 static void cleanup_key_data(context, count, data) 119 krb5_context context; 120 int count; 121 krb5_key_data * data; 122 { 123 int i, j; 124 125 for (i = 0; i < count; i++) 126 for (j = 0; j < data[i].key_data_ver; j++) 127 if (data[i].key_data_length[j]) 128 free(data[i].key_data_contents[j]); 129 free(data); 130 } 131 132 kadm5_ret_t 133 kadm5_create_principal(void *server_handle, 134 kadm5_principal_ent_t entry, long mask, 135 char *password) 136 { 137 /* 138 * Default to using the new API with the default set of 139 * key/salt combinations. 140 */ 141 return 142 kadm5_create_principal_3(server_handle, entry, mask, 143 0, NULL, password); 144 } 145 kadm5_ret_t 146 kadm5_create_principal_3(void *server_handle, 147 kadm5_principal_ent_t entry, long mask, 148 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 149 char *password) 150 { 151 krb5_db_entry kdb; 152 osa_princ_ent_rec adb; 153 kadm5_policy_ent_rec polent; 154 krb5_int32 now; 155 krb5_tl_data *tl_data_orig, *tl_data_tail; 156 unsigned int ret; 157 kadm5_server_handle_t handle = server_handle; 158 159 CHECK_HANDLE(server_handle); 160 161 /* 162 * Argument sanity checking, and opening up the DB 163 */ 164 if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) || 165 (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) || 166 (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) || 167 (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) || 168 (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) || 169 (mask & KADM5_FAIL_AUTH_COUNT)) 170 return KADM5_BAD_MASK; 171 if((mask & ~ALL_PRINC_MASK)) 172 return KADM5_BAD_MASK; 173 if (entry == (kadm5_principal_ent_t) NULL || password == NULL) 174 return EINVAL; 175 176 /* 177 * Check to see if the principal exists 178 */ 179 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb); 180 181 switch(ret) { 182 case KADM5_UNK_PRINC: 183 break; 184 case 0: 185 kdb_free_entry(handle, &kdb, &adb); 186 return KADM5_DUP; 187 default: 188 return ret; 189 } 190 191 memset(&kdb, 0, sizeof(krb5_db_entry)); 192 memset(&adb, 0, sizeof(osa_princ_ent_rec)); 193 194 /* 195 * If a policy was specified, load it. 196 * If we can not find the one specified return an error 197 */ 198 if ((mask & KADM5_POLICY)) { 199 if ((ret = kadm5_get_policy(handle->lhandle, entry->policy, 200 &polent)) != KADM5_OK) { 201 if(ret == EINVAL) 202 return KADM5_BAD_POLICY; 203 else 204 return ret; 205 } 206 } 207 if ((ret = passwd_check(handle, password, (mask & KADM5_POLICY), 208 &polent, entry->principal))) { 209 if (mask & KADM5_POLICY) 210 (void) kadm5_free_policy_ent(handle->lhandle, &polent); 211 return ret; 212 } 213 /* 214 * Start populating the various DB fields, using the 215 * "defaults" for fields that were not specified by the 216 * mask. 217 */ 218 if ((ret = krb5_timeofday(handle->context, &now))) { 219 if (mask & KADM5_POLICY) 220 (void) kadm5_free_policy_ent(handle->lhandle, &polent); 221 return ret; 222 } 223 224 kdb.magic = KRB5_KDB_MAGIC_NUMBER; 225 kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */ 226 227 /* 228 * If KADM5_ATTRIBUTES is set, we want to rope in not only 229 * entry->attributes, but also the generic params.flags 230 * obtained previously via kadm5_get_config_params. 231 */ 232 if ((mask & KADM5_ATTRIBUTES)) { 233 kdb.attributes = handle->params.flags; 234 kdb.attributes |= entry->attributes; 235 } else { 236 kdb.attributes = handle->params.flags; 237 } 238 239 if ((mask & KADM5_MAX_LIFE)) 240 kdb.max_life = entry->max_life; 241 else 242 kdb.max_life = handle->params.max_life; 243 244 if (mask & KADM5_MAX_RLIFE) 245 kdb.max_renewable_life = entry->max_renewable_life; 246 else 247 kdb.max_renewable_life = handle->params.max_rlife; 248 249 if ((mask & KADM5_PRINC_EXPIRE_TIME)) 250 kdb.expiration = entry->princ_expire_time; 251 else 252 kdb.expiration = handle->params.expiration; 253 254 kdb.pw_expiration = 0; 255 if ((mask & KADM5_POLICY)) { 256 if(polent.pw_max_life) 257 kdb.pw_expiration = now + polent.pw_max_life; 258 else 259 kdb.pw_expiration = 0; 260 } 261 if ((mask & KADM5_PW_EXPIRATION)) 262 kdb.pw_expiration = entry->pw_expiration; 263 264 kdb.last_success = 0; 265 kdb.last_failed = 0; 266 kdb.fail_auth_count = 0; 267 268 /* this is kind of gross, but in order to free the tl data, I need 269 to free the entire kdb entry, and that will try to free the 270 principal. */ 271 272 if ((ret = krb5_copy_principal(handle->context, 273 entry->principal, &(kdb.princ)))) { 274 if (mask & KADM5_POLICY) 275 (void) kadm5_free_policy_ent(handle->lhandle, &polent); 276 return(ret); 277 } 278 279 if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))) { 280 krb5_dbe_free_contents(handle->context, &kdb); 281 if (mask & KADM5_POLICY) 282 (void) kadm5_free_policy_ent(handle->lhandle, &polent); 283 return(ret); 284 } 285 286 /* initialize the keys */ 287 288 if ((ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock, 289 n_ks_tuple?ks_tuple:handle->params.keysalts, 290 n_ks_tuple?n_ks_tuple:handle->params.num_keysalts, 291 password, 292 (mask & KADM5_KVNO)?entry->kvno:1, 293 FALSE, &kdb))) { 294 krb5_dbe_free_contents(handle->context, &kdb); 295 if (mask & KADM5_POLICY) 296 (void) kadm5_free_policy_ent(handle->lhandle, &polent); 297 return(ret); 298 } 299 300 /* populate the admin-server-specific fields. In the OV server, 301 this used to be in a separate database. Since there's already 302 marshalling code for the admin fields, to keep things simple, 303 I'm going to keep it, and make all the admin stuff occupy a 304 single tl_data record, */ 305 306 adb.admin_history_kvno = hist_kvno; 307 if ((mask & KADM5_POLICY)) { 308 adb.aux_attributes = KADM5_POLICY; 309 310 /* this does *not* need to be strdup'ed, because adb is xdr */ 311 /* encoded in osa_adb_create_princ, and not ever freed */ 312 313 adb.policy = entry->policy; 314 } 315 316 /* increment the policy ref count, if any */ 317 318 if ((mask & KADM5_POLICY)) { 319 polent.policy_refcnt++; 320 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent, 321 KADM5_REF_COUNT)) 322 != KADM5_OK) { 323 krb5_dbe_free_contents(handle->context, &kdb); 324 if (mask & KADM5_POLICY) 325 (void) kadm5_free_policy_ent(handle->lhandle, &polent); 326 return(ret); 327 } 328 } 329 330 if (mask & KADM5_TL_DATA) { 331 /* splice entry->tl_data onto the front of kdb.tl_data */ 332 tl_data_orig = kdb.tl_data; 333 for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next; 334 tl_data_tail = tl_data_tail->tl_data_next) 335 ; 336 tl_data_tail->tl_data_next = kdb.tl_data; 337 kdb.tl_data = entry->tl_data; 338 } 339 340 /* store the new db entry */ 341 ret = kdb_put_entry(handle, &kdb, &adb); 342 343 if (mask & KADM5_TL_DATA) { 344 /* remove entry->tl_data from the front of kdb.tl_data */ 345 tl_data_tail->tl_data_next = NULL; 346 kdb.tl_data = tl_data_orig; 347 } 348 349 krb5_dbe_free_contents(handle->context, &kdb); 350 351 if (ret) { 352 if ((mask & KADM5_POLICY)) { 353 /* decrement the policy ref count */ 354 355 polent.policy_refcnt--; 356 /* 357 * if this fails, there's nothing we can do anyway. the 358 * policy refcount wil be too high. 359 */ 360 (void) kadm5_modify_policy_internal(handle->lhandle, &polent, 361 KADM5_REF_COUNT); 362 } 363 364 if (mask & KADM5_POLICY) 365 (void) kadm5_free_policy_ent(handle->lhandle, &polent); 366 return(ret); 367 } 368 369 if (mask & KADM5_POLICY) 370 (void) kadm5_free_policy_ent(handle->lhandle, &polent); 371 372 return KADM5_OK; 373 } 374 375 376 kadm5_ret_t 377 kadm5_delete_principal(void *server_handle, krb5_principal principal) 378 { 379 unsigned int ret; 380 kadm5_policy_ent_rec polent; 381 krb5_db_entry kdb; 382 osa_princ_ent_rec adb; 383 kadm5_server_handle_t handle = server_handle; 384 385 CHECK_HANDLE(server_handle); 386 387 if (principal == NULL) 388 return EINVAL; 389 390 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) 391 return(ret); 392 393 if ((adb.aux_attributes & KADM5_POLICY)) { 394 if ((ret = kadm5_get_policy(handle->lhandle, 395 adb.policy, &polent)) 396 == KADM5_OK) { 397 polent.policy_refcnt--; 398 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent, 399 KADM5_REF_COUNT)) 400 != KADM5_OK) { 401 (void) kadm5_free_policy_ent(handle->lhandle, &polent); 402 kdb_free_entry(handle, &kdb, &adb); 403 return(ret); 404 } 405 } 406 if ((ret = kadm5_free_policy_ent(handle->lhandle, &polent))) { 407 kdb_free_entry(handle, &kdb, &adb); 408 return ret; 409 } 410 } 411 412 ret = kdb_delete_entry(handle, principal); 413 414 kdb_free_entry(handle, &kdb, &adb); 415 416 return ret; 417 } 418 419 kadm5_ret_t 420 kadm5_modify_principal(void *server_handle, 421 kadm5_principal_ent_t entry, long mask) 422 { 423 int ret, ret2, i; 424 kadm5_policy_ent_rec npol, opol; 425 int have_npol = 0, have_opol = 0; 426 krb5_db_entry kdb; 427 krb5_tl_data *tl_data_orig; 428 osa_princ_ent_rec adb; 429 kadm5_server_handle_t handle = server_handle; 430 431 CHECK_HANDLE(server_handle); 432 433 if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) || 434 (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) || 435 (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) || 436 (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) || 437 (mask & KADM5_LAST_FAILED)) 438 return KADM5_BAD_MASK; 439 if((mask & ~ALL_PRINC_MASK)) 440 return KADM5_BAD_MASK; 441 if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR)) 442 return KADM5_BAD_MASK; 443 if(entry == (kadm5_principal_ent_t) NULL) 444 return EINVAL; 445 if (mask & KADM5_TL_DATA) { 446 tl_data_orig = entry->tl_data; 447 while (tl_data_orig) { 448 if (tl_data_orig->tl_data_type < 256) 449 return KADM5_BAD_TL_TYPE; 450 tl_data_orig = tl_data_orig->tl_data_next; 451 } 452 } 453 454 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb); 455 if (ret) 456 return(ret); 457 458 /* 459 * This is pretty much the same as create ... 460 */ 461 462 if ((mask & KADM5_POLICY)) { 463 /* get the new policy */ 464 ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol); 465 if (ret) { 466 switch (ret) { 467 case EINVAL: 468 ret = KADM5_BAD_POLICY; 469 break; 470 case KADM5_UNK_POLICY: 471 case KADM5_BAD_POLICY: 472 ret = KADM5_UNK_POLICY; 473 break; 474 } 475 goto done; 476 } 477 have_npol = 1; 478 479 /* if we already have a policy, get it to decrement the refcnt */ 480 if(adb.aux_attributes & KADM5_POLICY) { 481 /* ... but not if the old and new are the same */ 482 if(strcmp(adb.policy, entry->policy)) { 483 ret = kadm5_get_policy(handle->lhandle, 484 adb.policy, &opol); 485 switch(ret) { 486 case EINVAL: 487 case KADM5_BAD_POLICY: 488 case KADM5_UNK_POLICY: 489 break; 490 case KADM5_OK: 491 have_opol = 1; 492 opol.policy_refcnt--; 493 break; 494 default: 495 goto done; 496 break; 497 } 498 npol.policy_refcnt++; 499 } 500 } else npol.policy_refcnt++; 501 502 /* set us up to use the new policy */ 503 adb.aux_attributes |= KADM5_POLICY; 504 if (adb.policy) 505 free(adb.policy); 506 adb.policy = strdup(entry->policy); 507 508 /* set pw_max_life based on new policy */ 509 if (npol.pw_max_life) { 510 ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb, 511 &(kdb.pw_expiration)); 512 if (ret) 513 goto done; 514 kdb.pw_expiration += npol.pw_max_life; 515 } else { 516 kdb.pw_expiration = 0; 517 } 518 } 519 520 if ((mask & KADM5_POLICY_CLR) && 521 (adb.aux_attributes & KADM5_POLICY)) { 522 ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol); 523 switch(ret) { 524 case EINVAL: 525 case KADM5_BAD_POLICY: 526 case KADM5_UNK_POLICY: 527 ret = KADM5_BAD_DB; 528 goto done; 529 break; 530 case KADM5_OK: 531 have_opol = 1; 532 if (adb.policy) 533 free(adb.policy); 534 adb.policy = NULL; 535 adb.aux_attributes &= ~KADM5_POLICY; 536 kdb.pw_expiration = 0; 537 opol.policy_refcnt--; 538 break; 539 default: 540 goto done; 541 break; 542 } 543 } 544 545 if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) && 546 (((have_opol) && 547 (ret = 548 kadm5_modify_policy_internal(handle->lhandle, &opol, 549 KADM5_REF_COUNT))) || 550 ((have_npol) && 551 (ret = 552 kadm5_modify_policy_internal(handle->lhandle, &npol, 553 KADM5_REF_COUNT))))) 554 goto done; 555 556 if ((mask & KADM5_ATTRIBUTES)) 557 kdb.attributes = entry->attributes; 558 if ((mask & KADM5_MAX_LIFE)) 559 kdb.max_life = entry->max_life; 560 if ((mask & KADM5_PRINC_EXPIRE_TIME)) 561 kdb.expiration = entry->princ_expire_time; 562 if (mask & KADM5_PW_EXPIRATION) 563 kdb.pw_expiration = entry->pw_expiration; 564 if (mask & KADM5_MAX_RLIFE) 565 kdb.max_renewable_life = entry->max_renewable_life; 566 if (mask & KADM5_FAIL_AUTH_COUNT) 567 kdb.fail_auth_count = entry->fail_auth_count; 568 569 if((mask & KADM5_KVNO)) { 570 for (i = 0; i < kdb.n_key_data; i++) 571 kdb.key_data[i].key_data_kvno = entry->kvno; 572 } 573 574 if (mask & KADM5_TL_DATA) { 575 krb5_tl_data *tl, *tl2; 576 /* 577 * Replace kdb.tl_data with what was passed in. The 578 * KRB5_TL_KADM_DATA will be re-added (based on adb) by 579 * kdb_put_entry, below. 580 * 581 * Note that we have to duplicate the passed in tl_data 582 * before adding it to kdb. The reason is that kdb_put_entry 583 * will add its own tl_data entries that we will need to 584 * free, but we cannot free the caller's tl_data (an 585 * alternative would be to scan the tl_data after put_entry 586 * and only free those entries that were not passed in). 587 */ 588 while (kdb.tl_data) { 589 tl = kdb.tl_data->tl_data_next; 590 free(kdb.tl_data->tl_data_contents); 591 free(kdb.tl_data); 592 kdb.tl_data = tl; 593 } 594 595 kdb.n_tl_data = entry->n_tl_data; 596 kdb.tl_data = NULL; 597 tl2 = entry->tl_data; 598 while (tl2) { 599 tl = dup_tl_data(tl2); 600 tl->tl_data_next = kdb.tl_data; 601 kdb.tl_data = tl; 602 tl2 = tl2->tl_data_next; 603 } 604 } 605 606 ret = kdb_put_entry(handle, &kdb, &adb); 607 if (ret) goto done; 608 609 ret = KADM5_OK; 610 done: 611 if (have_opol) { 612 ret2 = kadm5_free_policy_ent(handle->lhandle, &opol); 613 ret = ret ? ret : ret2; 614 } 615 if (have_npol) { 616 ret2 = kadm5_free_policy_ent(handle->lhandle, &npol); 617 ret = ret ? ret : ret2; 618 } 619 kdb_free_entry(handle, &kdb, &adb); 620 return ret; 621 } 622 623 kadm5_ret_t 624 kadm5_rename_principal(void *server_handle, 625 krb5_principal source, krb5_principal target) 626 { 627 krb5_db_entry kdb; 628 osa_princ_ent_rec adb; 629 int ret, i; 630 kadm5_server_handle_t handle = server_handle; 631 632 CHECK_HANDLE(server_handle); 633 634 if (source == NULL || target == NULL) 635 return EINVAL; 636 637 if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) { 638 kdb_free_entry(handle, &kdb, &adb); 639 return(KADM5_DUP); 640 } 641 642 if ((ret = kdb_get_entry(handle, source, &kdb, &adb))) 643 return ret; 644 645 /* this is kinda gross, but unavoidable */ 646 647 for (i=0; i<kdb.n_key_data; i++) { 648 if ((kdb.key_data[i].key_data_ver == 1) || 649 (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) { 650 ret = KADM5_NO_RENAME_SALT; 651 goto done; 652 } 653 } 654 655 krb5_free_principal(handle->context, kdb.princ); 656 ret = krb5_copy_principal(handle->context, target, &kdb.princ); 657 if (ret) { 658 kdb.princ = NULL; /* so freeing the dbe doesn't lose */ 659 goto done; 660 } 661 662 if ((ret = kdb_put_entry(handle, &kdb, &adb))) 663 goto done; 664 665 ret = kdb_delete_entry(handle, source); 666 667 done: 668 kdb_free_entry(handle, &kdb, &adb); 669 return ret; 670 } 671 672 kadm5_ret_t 673 kadm5_get_principal(void *server_handle, krb5_principal principal, 674 kadm5_principal_ent_t entry, 675 long in_mask) 676 { 677 krb5_db_entry kdb; 678 osa_princ_ent_rec adb; 679 osa_adb_ret_t ret = 0; 680 long mask; 681 int i; 682 kadm5_server_handle_t handle = server_handle; 683 kadm5_principal_ent_rec entry_local, *entry_orig; 684 685 CHECK_HANDLE(server_handle); 686 687 /* 688 * In version 1, all the defined fields are always returned. 689 * entry is a pointer to a kadm5_principal_ent_t_v1 that should be 690 * filled with allocated memory. 691 */ 692 if (handle->api_version == KADM5_API_VERSION_1) { 693 mask = KADM5_PRINCIPAL_NORMAL_MASK; 694 entry_orig = entry; 695 entry = &entry_local; 696 } else { 697 mask = in_mask; 698 } 699 700 memset((char *) entry, 0, sizeof(*entry)); 701 702 if (principal == NULL) 703 return EINVAL; 704 705 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) 706 return ret; 707 708 if ((mask & KADM5_POLICY) && 709 adb.policy && (adb.aux_attributes & KADM5_POLICY)) { 710 if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) { 711 ret = ENOMEM; 712 goto done; 713 } 714 strcpy(entry->policy, adb.policy); 715 } 716 717 if (mask & KADM5_AUX_ATTRIBUTES) 718 entry->aux_attributes = adb.aux_attributes; 719 720 if ((mask & KADM5_PRINCIPAL) && 721 (ret = krb5_copy_principal(handle->context, principal, 722 &entry->principal))) { 723 goto done; 724 } 725 726 if (mask & KADM5_PRINC_EXPIRE_TIME) 727 entry->princ_expire_time = kdb.expiration; 728 729 if ((mask & KADM5_LAST_PWD_CHANGE) && 730 (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb, 731 &(entry->last_pwd_change)))) { 732 goto done; 733 } 734 735 if (mask & KADM5_PW_EXPIRATION) 736 entry->pw_expiration = kdb.pw_expiration; 737 if (mask & KADM5_MAX_LIFE) 738 entry->max_life = kdb.max_life; 739 740 /* this is a little non-sensical because the function returns two */ 741 /* values that must be checked separately against the mask */ 742 if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) { 743 ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb, 744 &(entry->mod_date), 745 &(entry->mod_name)); 746 if (ret) { 747 goto done; 748 } 749 750 if (! (mask & KADM5_MOD_TIME)) 751 entry->mod_date = 0; 752 if (! (mask & KADM5_MOD_NAME)) { 753 krb5_free_principal(handle->context, entry->principal); 754 entry->principal = NULL; 755 } 756 } 757 758 if (mask & KADM5_ATTRIBUTES) 759 entry->attributes = kdb.attributes; 760 761 if (mask & KADM5_KVNO) 762 for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++) 763 if (kdb.key_data[i].key_data_kvno > entry->kvno) 764 entry->kvno = kdb.key_data[i].key_data_kvno; 765 766 if (handle->api_version == KADM5_API_VERSION_2) 767 entry->mkvno = 0; 768 else { 769 /* XXX I'll be damned if I know how to deal with this one --marc */ 770 entry->mkvno = 1; 771 } 772 773 /* 774 * The new fields that only exist in version 2 start here 775 */ 776 if (handle->api_version == KADM5_API_VERSION_2) { 777 if (mask & KADM5_MAX_RLIFE) 778 entry->max_renewable_life = kdb.max_renewable_life; 779 if (mask & KADM5_LAST_SUCCESS) 780 entry->last_success = kdb.last_success; 781 if (mask & KADM5_LAST_FAILED) 782 entry->last_failed = kdb.last_failed; 783 if (mask & KADM5_FAIL_AUTH_COUNT) 784 entry->fail_auth_count = kdb.fail_auth_count; 785 if (mask & KADM5_TL_DATA) { 786 krb5_tl_data *tl, *tl2; 787 788 entry->tl_data = NULL; 789 790 tl = kdb.tl_data; 791 while (tl) { 792 if (tl->tl_data_type > 255) { 793 if ((tl2 = dup_tl_data(tl)) == NULL) { 794 ret = ENOMEM; 795 goto done; 796 } 797 tl2->tl_data_next = entry->tl_data; 798 entry->tl_data = tl2; 799 entry->n_tl_data++; 800 } 801 802 tl = tl->tl_data_next; 803 } 804 } 805 if (mask & KADM5_KEY_DATA) { 806 entry->n_key_data = kdb.n_key_data; 807 if(entry->n_key_data) { 808 entry->key_data = (krb5_key_data *) 809 malloc(entry->n_key_data*sizeof(krb5_key_data)); 810 if (entry->key_data == NULL) { 811 ret = ENOMEM; 812 goto done; 813 } 814 } else 815 entry->key_data = NULL; 816 817 for (i = 0; i < entry->n_key_data; i++) 818 ret = krb5_copy_key_data_contents(handle->context, 819 &kdb.key_data[i], 820 &entry->key_data[i]); 821 if (ret) 822 goto done; 823 } 824 } 825 826 /* 827 * If KADM5_API_VERSION_1, we return an allocated structure, and 828 * we need to convert the new structure back into the format the 829 * caller is expecting. 830 */ 831 if (handle->api_version == KADM5_API_VERSION_1) { 832 kadm5_principal_ent_t_v1 newv1; 833 834 newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1))); 835 if (newv1 == NULL) { 836 ret = ENOMEM; 837 goto done; 838 } 839 840 newv1->principal = entry->principal; 841 newv1->princ_expire_time = entry->princ_expire_time; 842 newv1->last_pwd_change = entry->last_pwd_change; 843 newv1->pw_expiration = entry->pw_expiration; 844 newv1->max_life = entry->max_life; 845 newv1->mod_name = entry->mod_name; 846 newv1->mod_date = entry->mod_date; 847 newv1->attributes = entry->attributes; 848 newv1->kvno = entry->kvno; 849 newv1->mkvno = entry->mkvno; 850 newv1->policy = entry->policy; 851 newv1->aux_attributes = entry->aux_attributes; 852 853 *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1; 854 } 855 856 ret = KADM5_OK; 857 858 done: 859 if (ret && entry->principal) 860 krb5_free_principal(handle->context, entry->principal); 861 kdb_free_entry(handle, &kdb, &adb); 862 863 return ret; 864 } 865 866 /* 867 * Function: check_pw_reuse 868 * 869 * Purpose: Check if a key appears in a list of keys, in order to 870 * enforce password history. 871 * 872 * Arguments: 873 * 874 * context (r) the krb5 context 875 * hist_keyblock (r) the key that hist_key_data is 876 * encrypted in 877 * n_new_key_data (r) length of new_key_data 878 * new_key_data (r) keys to check against 879 * pw_hist_data, encrypted in hist_keyblock 880 * n_pw_hist_data (r) length of pw_hist_data 881 * pw_hist_data (r) passwords to check new_key_data against 882 * 883 * Effects: 884 * For each new_key in new_key_data: 885 * decrypt new_key with the master_keyblock 886 * for each password in pw_hist_data: 887 * for each hist_key in password: 888 * decrypt hist_key with hist_keyblock 889 * compare the new_key and hist_key 890 * 891 * Returns krb5 errors, KADM5_PASS_RESUSE if a key in 892 * new_key_data is the same as a key in pw_hist_data, or 0. 893 */ 894 static kadm5_ret_t 895 check_pw_reuse(krb5_context context, 896 krb5_keyblock *master_keyblock, 897 krb5_keyblock *hist_keyblock, 898 int n_new_key_data, krb5_key_data *new_key_data, 899 unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data) 900 { 901 int x, y, z; 902 krb5_keyblock newkey, histkey; 903 krb5_error_code ret; 904 905 for (x = 0; x < n_new_key_data; x++) { 906 ret = krb5_dbekd_decrypt_key_data(context, 907 master_keyblock, 908 &(new_key_data[x]), 909 &newkey, NULL); 910 if (ret) 911 return(ret); 912 for (y = 0; y < n_pw_hist_data; y++) { 913 for (z = 0; z < pw_hist_data[y].n_key_data; z++) { 914 ret = krb5_dbekd_decrypt_key_data(context, 915 hist_keyblock, 916 &pw_hist_data[y].key_data[z], 917 &histkey, NULL); 918 if (ret) 919 return(ret); 920 921 if ((newkey.length == histkey.length) && 922 (newkey.enctype == histkey.enctype) && 923 (memcmp(newkey.contents, histkey.contents, 924 histkey.length) == 0)) { 925 krb5_free_keyblock_contents(context, &histkey); 926 krb5_free_keyblock_contents(context, &newkey); 927 928 return(KADM5_PASS_REUSE); 929 } 930 krb5_free_keyblock_contents(context, &histkey); 931 } 932 } 933 krb5_free_keyblock_contents(context, &newkey); 934 } 935 936 return(0); 937 } 938 939 /* 940 * Function: create_history_entry 941 * 942 * Purpose: Creates a password history entry from an array of 943 * key_data. 944 * 945 * Arguments: 946 * 947 * context (r) krb5_context to use 948 * master_keyblcok (r) master key block 949 * n_key_data (r) number of elements in key_data 950 * key_data (r) keys to add to the history entry 951 * hist (w) history entry to fill in 952 * 953 * Effects: 954 * 955 * hist->key_data is allocated to store n_key_data key_datas. Each 956 * element of key_data is decrypted with master_keyblock, re-encrypted 957 * in hist_key, and added to hist->key_data. hist->n_key_data is 958 * set to n_key_data. 959 */ 960 static 961 int create_history_entry(krb5_context context, 962 krb5_keyblock *master_keyblock, int n_key_data, 963 krb5_key_data *key_data, osa_pw_hist_ent *hist) 964 { 965 int i, ret; 966 krb5_keyblock key; 967 krb5_keysalt salt; 968 969 hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data)); 970 if (hist->key_data == NULL) 971 return ENOMEM; 972 memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data)); 973 974 for (i = 0; i < n_key_data; i++) { 975 ret = krb5_dbekd_decrypt_key_data(context, 976 master_keyblock, 977 &key_data[i], 978 &key, &salt); 979 if (ret) 980 return ret; 981 982 ret = krb5_dbekd_encrypt_key_data(context, &hist_key, 983 &key, &salt, 984 key_data[i].key_data_kvno, 985 &hist->key_data[i]); 986 if (ret) 987 return ret; 988 989 krb5_free_keyblock_contents(context, &key); 990 /* krb5_free_keysalt(context, &salt); */ 991 } 992 993 hist->n_key_data = n_key_data; 994 return 0; 995 } 996 997 static 998 void free_history_entry(krb5_context context, osa_pw_hist_ent *hist) 999 { 1000 int i; 1001 1002 for (i = 0; i < hist->n_key_data; i++) 1003 krb5_free_key_data_contents(context, &hist->key_data[i]); 1004 free(hist->key_data); 1005 } 1006 1007 /* 1008 * Function: add_to_history 1009 * 1010 * Purpose: Adds a password to a principal's password history. 1011 * 1012 * Arguments: 1013 * 1014 * context (r) krb5_context to use 1015 * adb (r/w) admin principal entry to add keys to 1016 * pol (r) adb's policy 1017 * pw (r) keys for the password to add to adb's key history 1018 * 1019 * Effects: 1020 * 1021 * add_to_history adds a single password to adb's password history. 1022 * pw contains n_key_data keys in its key_data, in storage should be 1023 * allocated but not freed by the caller (XXX blech!). 1024 * 1025 * This function maintains adb->old_keys as a circular queue. It 1026 * starts empty, and grows each time this function is called until it 1027 * is pol->pw_history_num items long. adb->old_key_len holds the 1028 * number of allocated entries in the array, and must therefore be [0, 1029 * pol->pw_history_num). adb->old_key_next is the index into the 1030 * array where the next element should be written, and must be [0, 1031 * adb->old_key_len). 1032 */ 1033 #define KADM_MOD(x) (x + adb->old_key_next) % adb->old_key_len 1034 static kadm5_ret_t add_to_history(krb5_context context, 1035 osa_princ_ent_t adb, 1036 kadm5_policy_ent_t pol, 1037 osa_pw_hist_ent *pw) 1038 { 1039 osa_pw_hist_ent *histp; 1040 int i; 1041 1042 /* A history of 1 means just check the current password */ 1043 if (pol->pw_history_num == 1) 1044 return (0); 1045 1046 /* resize the adb->old_keys array if necessary */ 1047 if (adb->old_key_len < pol->pw_history_num-1) { 1048 if (adb->old_keys == NULL) { 1049 adb->old_keys = (osa_pw_hist_ent *) 1050 malloc((adb->old_key_len + 1) * 1051 sizeof (osa_pw_hist_ent)); 1052 } else { 1053 adb->old_keys = (osa_pw_hist_ent *) 1054 realloc(adb->old_keys, 1055 (adb->old_key_len + 1) * 1056 sizeof (osa_pw_hist_ent)); 1057 } 1058 if (adb->old_keys == NULL) 1059 return (ENOMEM); 1060 1061 memset(&adb->old_keys[adb->old_key_len], 0, 1062 sizeof (osa_pw_hist_ent)); 1063 adb->old_key_len++; 1064 for (i = adb->old_key_len - 1; i > adb->old_key_next; i--) 1065 adb->old_keys[i] = adb->old_keys[i - 1]; 1066 memset(&adb->old_keys[adb->old_key_next], 0, 1067 sizeof (osa_pw_hist_ent)); 1068 } else if (adb->old_key_len > pol->pw_history_num-1) { 1069 /* 1070 * The policy must have changed! Shrink the array. 1071 * Can't simply realloc() down, since it might be wrapped. 1072 * To understand the arithmetic below, note that we are 1073 * copying into new positions 0 .. N-1 from old positions 1074 * old_key_next-N .. old_key_next-1, modulo old_key_len, 1075 * where N = pw_history_num - 1 is the length of the 1076 * shortened list. Matt Crawford, FNAL 1077 */ 1078 int j; 1079 histp = (osa_pw_hist_ent *) 1080 malloc((pol->pw_history_num - 1) * sizeof (osa_pw_hist_ent)); 1081 if (histp) { 1082 for (i = 0; i < pol->pw_history_num - 1; i++) { 1083 /* 1084 * We need the number we use the modulus 1085 * operator on to be positive, so after 1086 * subtracting pol->pw_history_num-1, we 1087 * add back adb->old_key_len. 1088 */ 1089 j = KADM_MOD(i - (pol->pw_history_num - 1) + 1090 adb->old_key_len); 1091 histp[i] = adb->old_keys[j]; 1092 } 1093 /* Now free the ones we don't keep (the oldest ones) */ 1094 for (i = 0; i < adb->old_key_len - \ 1095 (pol->pw_history_num-1); i++) { 1096 for (j = 0; j < \ 1097 adb->old_keys[KADM_MOD(i)].n_key_data; j++) 1098 krb5_free_key_data_contents(context, 1099 &adb->old_keys[KADM_MOD(i)]. 1100 key_data[j]); 1101 free(adb->old_keys[KADM_MOD(i)].key_data); 1102 } 1103 free((void *)adb->old_keys); 1104 adb->old_keys = histp; 1105 adb->old_key_len = pol->pw_history_num - 1; 1106 adb->old_key_next = 0; 1107 } else { 1108 return (ENOMEM); 1109 } 1110 } 1111 1112 if (adb->old_key_next + 1 > adb->old_key_len) 1113 adb->old_key_next = 0; 1114 1115 /* free the old pw history entry if it contains data */ 1116 histp = &adb->old_keys[adb->old_key_next]; 1117 for (i = 0; i < histp->n_key_data; i++) 1118 krb5_free_key_data_contents(context, &histp->key_data[i]); 1119 free(histp->key_data); 1120 1121 /* store the new entry */ 1122 adb->old_keys[adb->old_key_next] = *pw; 1123 1124 /* update the next pointer */ 1125 if (++adb->old_key_next == pol->pw_history_num-1) 1126 adb->old_key_next = 0; 1127 1128 return (0); 1129 } 1130 #undef KADM_MOD 1131 1132 kadm5_ret_t 1133 kadm5_chpass_principal(void *server_handle, 1134 krb5_principal principal, char *password) 1135 { 1136 /* 1137 * Default to using the new API with the default set of 1138 * key/salt combinations. 1139 */ 1140 return 1141 kadm5_chpass_principal_3(server_handle, principal, FALSE, 1142 0, NULL, password); 1143 } 1144 1145 kadm5_ret_t 1146 kadm5_chpass_principal_3(void *server_handle, 1147 krb5_principal principal, krb5_boolean keepold, 1148 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 1149 char *password) 1150 { 1151 krb5_int32 now; 1152 kadm5_policy_ent_rec pol; 1153 osa_princ_ent_rec adb; 1154 krb5_db_entry kdb, kdb_save; 1155 int ret, ret2, last_pwd, hist_added; 1156 int have_pol = 0; 1157 kadm5_server_handle_t handle = server_handle; 1158 osa_pw_hist_ent hist; 1159 1160 CHECK_HANDLE(server_handle); 1161 1162 hist_added = 0; 1163 memset(&hist, 0, sizeof(hist)); 1164 1165 if (principal == NULL || password == NULL) 1166 return EINVAL; 1167 if ((krb5_principal_compare(handle->context, 1168 principal, hist_princ)) == TRUE) 1169 return KADM5_PROTECT_PRINCIPAL; 1170 1171 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) 1172 return(ret); 1173 1174 /* we are going to need the current keys after the new keys are set */ 1175 if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) { 1176 kdb_free_entry(handle, &kdb, &adb); 1177 return(ret); 1178 } 1179 1180 if ((adb.aux_attributes & KADM5_POLICY)) { 1181 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol))) 1182 goto done; 1183 have_pol = 1; 1184 } 1185 1186 if ((ret = passwd_check(handle, password, adb.aux_attributes & 1187 KADM5_POLICY, &pol, principal))) 1188 goto done; 1189 1190 ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock, 1191 n_ks_tuple?ks_tuple:handle->params.keysalts, 1192 n_ks_tuple?n_ks_tuple:handle->params.num_keysalts, 1193 password, 0 /* increment kvno */, 1194 keepold, &kdb); 1195 if (ret) 1196 goto done; 1197 1198 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; 1199 1200 ret = krb5_timeofday(handle->context, &now); 1201 if (ret) 1202 goto done; 1203 1204 if ((adb.aux_attributes & KADM5_POLICY)) { 1205 /* the policy was loaded before */ 1206 1207 ret = krb5_dbe_lookup_last_pwd_change(handle->context, 1208 &kdb, &last_pwd); 1209 if (ret) 1210 goto done; 1211 1212 #if 0 1213 /* 1214 * The spec says this check is overridden if the caller has 1215 * modify privilege. The admin server therefore makes this 1216 * check itself (in chpass_principal_wrapper, misc.c). A 1217 * local caller implicitly has all authorization bits. 1218 */ 1219 if ((now - last_pwd) < pol.pw_min_life && 1220 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { 1221 ret = KADM5_PASS_TOOSOON; 1222 goto done; 1223 } 1224 #endif 1225 1226 ret = create_history_entry(handle->context, 1227 &handle->master_keyblock, kdb_save.n_key_data, 1228 kdb_save.key_data, &hist); 1229 if (ret) 1230 goto done; 1231 1232 ret = check_pw_reuse(handle->context, 1233 &handle->master_keyblock, 1234 &hist_key, 1235 kdb.n_key_data, kdb.key_data, 1236 1, &hist); 1237 if (ret) 1238 goto done; 1239 1240 if (pol.pw_history_num > 1) { 1241 if (adb.admin_history_kvno != hist_kvno) { 1242 ret = KADM5_BAD_HIST_KEY; 1243 goto done; 1244 } 1245 1246 ret = check_pw_reuse(handle->context, 1247 &handle->master_keyblock, 1248 &hist_key, 1249 kdb.n_key_data, kdb.key_data, 1250 adb.old_key_len, adb.old_keys); 1251 if (ret) 1252 goto done; 1253 1254 ret = add_to_history(handle->context, &adb, &pol, &hist); 1255 if (ret) 1256 goto done; 1257 hist_added = 1; 1258 } 1259 1260 if (pol.pw_max_life) 1261 kdb.pw_expiration = now + pol.pw_max_life; 1262 else 1263 kdb.pw_expiration = 0; 1264 } else { 1265 kdb.pw_expiration = 0; 1266 } 1267 1268 ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now); 1269 if (ret) 1270 goto done; 1271 1272 if ((ret = kdb_put_entry(handle, &kdb, &adb))) 1273 goto done; 1274 1275 ret = KADM5_OK; 1276 done: 1277 if (!hist_added && hist.key_data) 1278 free_history_entry(handle->context, &hist); 1279 kdb_free_entry(handle, &kdb, &adb); 1280 kdb_free_entry(handle, &kdb_save, NULL); 1281 krb5_dbe_free_contents(handle->context, &kdb); 1282 1283 if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol)) 1284 && !ret) 1285 ret = ret2; 1286 1287 return ret; 1288 } 1289 1290 kadm5_ret_t 1291 kadm5_randkey_principal(void *server_handle, 1292 krb5_principal principal, 1293 krb5_keyblock **keyblocks, 1294 int *n_keys) 1295 { 1296 krb5_key_salt_tuple keysalts[2]; 1297 1298 /* 1299 * Anyone calling this routine is forced to use only DES 1300 * enctypes to be compatible with earlier releases that 1301 * did not support stronger crypto. 1302 * 1303 * S10 (and later) kadmin clients will not use this API, 1304 * so we can assume the request is from an older version. 1305 */ 1306 keysalts[0].ks_enctype = ENCTYPE_DES_CBC_MD5; 1307 keysalts[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL; 1308 keysalts[1].ks_enctype = ENCTYPE_DES_CBC_CRC; 1309 keysalts[1].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL; 1310 1311 return (kadm5_randkey_principal_3(server_handle, principal, 1312 FALSE, 2, keysalts, keyblocks, n_keys)); 1313 } 1314 1315 kadm5_ret_t 1316 kadm5_randkey_principal_3(void *server_handle, 1317 krb5_principal principal, 1318 krb5_boolean keepold, 1319 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 1320 krb5_keyblock **keyblocks, 1321 int *n_keys) 1322 { 1323 krb5_db_entry kdb; 1324 osa_princ_ent_rec adb; 1325 krb5_int32 now; 1326 kadm5_policy_ent_rec pol; 1327 krb5_key_data *key_data; 1328 krb5_keyblock *keyblock; 1329 int ret, last_pwd, have_pol = 0; 1330 kadm5_server_handle_t handle = server_handle; 1331 1332 if (keyblocks) 1333 *keyblocks = NULL; 1334 1335 CHECK_HANDLE(server_handle); 1336 1337 if (principal == NULL) 1338 return EINVAL; 1339 if (hist_princ && /* this will be NULL when initializing the databse */ 1340 ((krb5_principal_compare(handle->context, 1341 principal, hist_princ)) == TRUE)) 1342 return KADM5_PROTECT_PRINCIPAL; 1343 1344 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) 1345 return(ret); 1346 1347 ret = krb5_dbe_crk(handle->context, &handle->master_keyblock, 1348 n_ks_tuple?ks_tuple:handle->params.keysalts, 1349 n_ks_tuple?n_ks_tuple:handle->params.num_keysalts, 1350 keepold, 1351 &kdb); 1352 if (ret) 1353 goto done; 1354 1355 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; 1356 1357 ret = krb5_timeofday(handle->context, &now); 1358 if (ret) 1359 goto done; 1360 1361 if ((adb.aux_attributes & KADM5_POLICY)) { 1362 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, 1363 &pol)) != KADM5_OK) 1364 goto done; 1365 have_pol = 1; 1366 1367 ret = krb5_dbe_lookup_last_pwd_change(handle->context, 1368 &kdb, &last_pwd); 1369 if (ret) 1370 goto done; 1371 1372 #if 0 1373 /* 1374 * The spec says this check is overridden if the caller has 1375 * modify privilege. The admin server therefore makes this 1376 * check itself (in chpass_principal_wrapper, misc.c). A 1377 * local caller implicitly has all authorization bits. 1378 */ 1379 if((now - last_pwd) < pol.pw_min_life && 1380 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { 1381 ret = KADM5_PASS_TOOSOON; 1382 goto done; 1383 } 1384 #endif 1385 1386 if(pol.pw_history_num > 1) { 1387 if(adb.admin_history_kvno != hist_kvno) { 1388 ret = KADM5_BAD_HIST_KEY; 1389 goto done; 1390 } 1391 1392 ret = check_pw_reuse(handle->context, 1393 &handle->master_keyblock, 1394 &hist_key, 1395 kdb.n_key_data, kdb.key_data, 1396 adb.old_key_len, adb.old_keys); 1397 if (ret) 1398 goto done; 1399 } 1400 if (pol.pw_max_life) 1401 kdb.pw_expiration = now + pol.pw_max_life; 1402 else 1403 kdb.pw_expiration = 0; 1404 } else { 1405 kdb.pw_expiration = 0; 1406 } 1407 1408 ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now); 1409 if (ret) 1410 goto done; 1411 1412 if (keyblocks) { 1413 if (handle->api_version == KADM5_API_VERSION_1) { 1414 /* Version 1 clients will expect to see a DES_CRC enctype. */ 1415 ret = krb5_dbe_find_enctype(handle->context, &kdb, 1416 ENCTYPE_DES_CBC_CRC, 1417 -1, -1, &key_data); 1418 if (ret) 1419 goto done; 1420 1421 ret = decrypt_key_data(handle->context, 1422 &handle->master_keyblock, 1, key_data, 1423 keyblocks, NULL); 1424 if (ret) 1425 goto done; 1426 } else { 1427 ret = decrypt_key_data(handle->context, 1428 &handle->master_keyblock, 1429 kdb.n_key_data, kdb.key_data, 1430 keyblocks, n_keys); 1431 if (ret) 1432 goto done; 1433 } 1434 } 1435 1436 if ((ret = kdb_put_entry(handle, &kdb, &adb))) 1437 goto done; 1438 1439 ret = KADM5_OK; 1440 done: 1441 kdb_free_entry(handle, &kdb, &adb); 1442 if (have_pol) 1443 kadm5_free_policy_ent(handle->lhandle, &pol); 1444 1445 return ret; 1446 } 1447 1448 kadm5_ret_t 1449 kadm5_setkey_principal(void *server_handle, 1450 krb5_principal principal, 1451 krb5_keyblock *keyblocks, 1452 int n_keys) 1453 { 1454 return 1455 kadm5_setkey_principal_3(server_handle, principal, 1456 FALSE, 0, NULL, 1457 keyblocks, n_keys); 1458 } 1459 1460 kadm5_ret_t 1461 kadm5_setkey_principal_3(void *server_handle, 1462 krb5_principal principal, 1463 krb5_boolean keepold, 1464 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 1465 krb5_keyblock *keyblocks, 1466 int n_keys) 1467 { 1468 krb5_db_entry kdb; 1469 osa_princ_ent_rec adb; 1470 krb5_int32 now; 1471 kadm5_policy_ent_rec pol; 1472 krb5_key_data *old_key_data; 1473 int n_old_keys; 1474 int i, j, kvno, ret, last_pwd, have_pol = 0; 1475 kadm5_server_handle_t handle = server_handle; 1476 krb5_boolean similar; 1477 krb5_keysalt keysalt; 1478 1479 CHECK_HANDLE(server_handle); 1480 1481 if (principal == NULL || keyblocks == NULL) 1482 return EINVAL; 1483 if (hist_princ && /* this will be NULL when initializing the databse */ 1484 ((krb5_principal_compare(handle->context, 1485 principal, hist_princ)) == TRUE)) 1486 return KADM5_PROTECT_PRINCIPAL; 1487 1488 for (i = 0; i < n_keys; i++) { 1489 for (j = i+1; j < n_keys; j++) { 1490 if ((ret = krb5_c_enctype_compare(handle->context, 1491 keyblocks[i].enctype, 1492 keyblocks[j].enctype, 1493 &similar))) 1494 return(ret); 1495 if (similar) { 1496 if (n_ks_tuple) { 1497 if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype) 1498 return KADM5_SETKEY_DUP_ENCTYPES; 1499 } else 1500 return KADM5_SETKEY_DUP_ENCTYPES; 1501 } 1502 } 1503 } 1504 1505 if (n_ks_tuple && n_ks_tuple != n_keys) 1506 return KADM5_SETKEY3_ETYPE_MISMATCH; 1507 1508 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) 1509 return(ret); 1510 1511 for (kvno = 0, i=0; i<kdb.n_key_data; i++) 1512 if (kdb.key_data[i].key_data_kvno > kvno) 1513 kvno = kdb.key_data[i].key_data_kvno; 1514 1515 if (keepold) { 1516 old_key_data = kdb.key_data; 1517 n_old_keys = kdb.n_key_data; 1518 } else { 1519 if (kdb.key_data != NULL) 1520 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data); 1521 n_old_keys = 0; 1522 old_key_data = NULL; 1523 } 1524 1525 kdb.key_data = (krb5_key_data*)malloc((n_keys+n_old_keys) 1526 *sizeof(krb5_key_data)); 1527 if (kdb.key_data == NULL) 1528 return ENOMEM; 1529 memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data)); 1530 kdb.n_key_data = 0; 1531 1532 for (i = 0; i < n_keys; i++) { 1533 if (n_ks_tuple) { 1534 keysalt.type = ks_tuple[i].ks_salttype; 1535 keysalt.data.length = 0; 1536 keysalt.data.data = NULL; 1537 if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) { 1538 cleanup_key_data(handle->context, kdb.n_key_data, 1539 kdb.key_data); 1540 return KADM5_SETKEY3_ETYPE_MISMATCH; 1541 } 1542 } 1543 ret = krb5_dbekd_encrypt_key_data(handle->context, 1544 &handle->master_keyblock, 1545 &keyblocks[i], 1546 n_ks_tuple ? &keysalt : NULL, 1547 kvno + 1, 1548 &kdb.key_data[i]); 1549 if (ret) { 1550 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data); 1551 return ret; 1552 } 1553 kdb.n_key_data++; 1554 } 1555 1556 /* copy old key data if necessary */ 1557 for (i = 0; i < n_old_keys; i++) { 1558 kdb.key_data[i+n_keys] = old_key_data[i]; 1559 memset(&old_key_data[i], 0, sizeof (krb5_key_data)); 1560 kdb.n_key_data++; 1561 } 1562 /* assert(kdb.n_key_data == n_keys + n_old_keys) */ 1563 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; 1564 1565 if ((ret = krb5_timeofday(handle->context, &now))) 1566 goto done; 1567 1568 if ((adb.aux_attributes & KADM5_POLICY)) { 1569 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, 1570 &pol)) != KADM5_OK) 1571 goto done; 1572 have_pol = 1; 1573 1574 #if 0 1575 /* 1576 * The spec says this check is overridden if the caller has 1577 * modify privilege. The admin server therefore makes this 1578 * check itself (in chpass_principal_wrapper, misc.c). A 1579 * local caller implicitly has all authorization bits. 1580 */ 1581 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context, 1582 &kdb, &last_pwd)) 1583 goto done; 1584 if((now - last_pwd) < pol.pw_min_life && 1585 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { 1586 ret = KADM5_PASS_TOOSOON; 1587 goto done; 1588 } 1589 #endif 1590 #if 0 1591 /* 1592 * Should we be checking/updating pw history here? 1593 */ 1594 if(pol.pw_history_num > 1) { 1595 if(adb.admin_history_kvno != hist_kvno) { 1596 ret = KADM5_BAD_HIST_KEY; 1597 goto done; 1598 } 1599 1600 if (ret = check_pw_reuse(handle->context, 1601 &handle->master_keyblock, 1602 &hist_key, 1603 kdb.n_key_data, kdb.key_data, 1604 adb.old_key_len, adb.old_keys)) 1605 goto done; 1606 } 1607 #endif 1608 1609 if (pol.pw_max_life) 1610 kdb.pw_expiration = now + pol.pw_max_life; 1611 else 1612 kdb.pw_expiration = 0; 1613 } else { 1614 kdb.pw_expiration = 0; 1615 } 1616 1617 if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))) 1618 goto done; 1619 1620 if ((ret = kdb_put_entry(handle, &kdb, &adb))) 1621 goto done; 1622 1623 ret = KADM5_OK; 1624 done: 1625 kdb_free_entry(handle, &kdb, &adb); 1626 if (have_pol) 1627 kadm5_free_policy_ent(handle->lhandle, &pol); 1628 1629 return ret; 1630 } 1631 1632 /* 1633 * Allocate an array of n_key_data krb5_keyblocks, fill in each 1634 * element with the results of decrypting the nth key in key_data with 1635 * master_keyblock, and if n_keys is not NULL fill it in with the 1636 * number of keys decrypted. 1637 */ 1638 static int decrypt_key_data(krb5_context context, 1639 krb5_keyblock *master_keyblock, 1640 int n_key_data, krb5_key_data *key_data, 1641 krb5_keyblock **keyblocks, int *n_keys) 1642 { 1643 krb5_keyblock *keys; 1644 int ret, i; 1645 1646 keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock)); 1647 if (keys == NULL) 1648 return ENOMEM; 1649 memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock)); 1650 1651 for (i = 0; i < n_key_data; i++) { 1652 ret = krb5_dbekd_decrypt_key_data(context, 1653 master_keyblock, 1654 &key_data[i], 1655 &keys[i], NULL); 1656 if (ret) { 1657 1658 memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock)); 1659 free(keys); 1660 return ret; 1661 } 1662 } 1663 1664 *keyblocks = keys; 1665 if (n_keys) 1666 *n_keys = n_key_data; 1667 1668 return 0; 1669 } 1670 1671 /* 1672 * Function: kadm5_decrypt_key 1673 * 1674 * Purpose: Retrieves and decrypts a principal key. 1675 * 1676 * Arguments: 1677 * 1678 * server_handle (r) kadm5 handle 1679 * entry (r) principal retrieved with kadm5_get_principal 1680 * ktype (r) enctype to search for, or -1 to ignore 1681 * stype (r) salt type to search for, or -1 to ignore 1682 * kvno (r) kvno to search for, -1 for max, 0 for max 1683 * only if it also matches ktype and stype 1684 * keyblock (w) keyblock to fill in 1685 * keysalt (w) keysalt to fill in, or NULL 1686 * kvnop (w) kvno to fill in, or NULL 1687 * 1688 * Effects: Searches the key_data array of entry, which must have been 1689 * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to 1690 * find a key with a specified enctype, salt type, and kvno in a 1691 * principal entry. If not found, return ENOENT. Otherwise, decrypt 1692 * it with the master key, and return the key in keyblock, the salt 1693 * in salttype, and the key version number in kvno. 1694 * 1695 * If ktype or stype is -1, it is ignored for the search. If kvno is 1696 * -1, ktype and stype are ignored and the key with the max kvno is 1697 * returned. If kvno is 0, only the key with the max kvno is returned 1698 * and only if it matches the ktype and stype; otherwise, ENOENT is 1699 * returned. 1700 */ 1701 kadm5_ret_t kadm5_decrypt_key(void *server_handle, 1702 kadm5_principal_ent_t entry, krb5_int32 1703 ktype, krb5_int32 stype, krb5_int32 1704 kvno, krb5_keyblock *keyblock, 1705 krb5_keysalt *keysalt, int *kvnop) 1706 { 1707 kadm5_server_handle_t handle = server_handle; 1708 krb5_db_entry dbent; 1709 krb5_key_data *key_data; 1710 int ret; 1711 1712 CHECK_HANDLE(server_handle); 1713 1714 if (entry->n_key_data == 0 || entry->key_data == NULL) 1715 return EINVAL; 1716 1717 /* find_enctype only uses these two fields */ 1718 dbent.n_key_data = entry->n_key_data; 1719 dbent.key_data = entry->key_data; 1720 if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype, 1721 stype, kvno, &key_data))) 1722 return ret; 1723 1724 if ((ret = krb5_dbekd_decrypt_key_data(handle->context, 1725 &handle->master_keyblock, key_data, 1726 keyblock, keysalt))) 1727 return ret; 1728 1729 if (kvnop) 1730 *kvnop = key_data->key_data_kvno; 1731 1732 return KADM5_OK; 1733 } 1734