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