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