1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved 4 * 5 * $Header$ 6 */ 7 #include "k5-int.h" 8 #include <sys/time.h> 9 #include <kadm5/admin.h> 10 #include <kdb.h> 11 #include "server_internal.h" 12 13 #include <krb5/kadm5_hook_plugin.h> 14 15 #ifdef USE_VALGRIND 16 #include <valgrind/memcheck.h> 17 #else 18 #define VALGRIND_CHECK_DEFINED(LVALUE) ((void)0) 19 #endif 20 21 extern krb5_principal master_princ; 22 extern krb5_principal hist_princ; 23 extern krb5_keyblock master_keyblock; 24 extern krb5_db_entry master_db; 25 26 static int decrypt_key_data(krb5_context context, 27 int n_key_data, krb5_key_data *key_data, 28 krb5_keyblock **keyblocks, int *n_keys); 29 30 /* 31 * XXX Functions that ought to be in libkrb5.a, but aren't. 32 */ 33 kadm5_ret_t 34 krb5_copy_key_data_contents(krb5_context context, krb5_key_data *from, 35 krb5_key_data *to) 36 { 37 int i, idx; 38 39 *to = *from; 40 41 idx = (from->key_data_ver == 1 ? 1 : 2); 42 43 for (i = 0; i < idx; i++) { 44 if ( from->key_data_length[i] ) { 45 to->key_data_contents[i] = malloc(from->key_data_length[i]); 46 if (to->key_data_contents[i] == NULL) { 47 for (i = 0; i < idx; i++) 48 zapfree(to->key_data_contents[i], to->key_data_length[i]); 49 return ENOMEM; 50 } 51 memcpy(to->key_data_contents[i], from->key_data_contents[i], 52 from->key_data_length[i]); 53 } 54 } 55 return 0; 56 } 57 58 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl) 59 { 60 krb5_tl_data *n; 61 62 n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)); 63 if (n == NULL) 64 return NULL; 65 n->tl_data_contents = malloc(tl->tl_data_length); 66 if (n->tl_data_contents == NULL) { 67 free(n); 68 return NULL; 69 } 70 memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length); 71 n->tl_data_type = tl->tl_data_type; 72 n->tl_data_length = tl->tl_data_length; 73 n->tl_data_next = NULL; 74 return n; 75 } 76 77 /* This is in lib/kdb/kdb_cpw.c, but is static */ 78 static void 79 cleanup_key_data(krb5_context context, int count, krb5_key_data *data) 80 { 81 int i; 82 83 for (i = 0; i < count; i++) 84 krb5_free_key_data_contents(context, &data[i]); 85 free(data); 86 } 87 88 /* Check whether a ks_tuple is present in an array of ks_tuples. */ 89 static krb5_boolean 90 ks_tuple_present(int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 91 krb5_key_salt_tuple *looking_for) 92 { 93 int i; 94 95 for (i = 0; i < n_ks_tuple; i++) { 96 if (ks_tuple[i].ks_enctype == looking_for->ks_enctype && 97 ks_tuple[i].ks_salttype == looking_for->ks_salttype) 98 return TRUE; 99 } 100 return FALSE; 101 } 102 103 /* Fetch a policy if it exists; set *have_pol_out appropriately. Return 104 * success whether or not the policy exists. */ 105 static kadm5_ret_t 106 get_policy(kadm5_server_handle_t handle, const char *name, 107 kadm5_policy_ent_t policy_out, krb5_boolean *have_pol_out) 108 { 109 kadm5_ret_t ret; 110 111 *have_pol_out = FALSE; 112 if (name == NULL) 113 return 0; 114 ret = kadm5_get_policy(handle->lhandle, (char *)name, policy_out); 115 if (ret == 0) 116 *have_pol_out = TRUE; 117 return (ret == KADM5_UNK_POLICY) ? 0 : ret; 118 } 119 120 /* 121 * Apply the -allowedkeysalts policy (see kadmin(1)'s addpol/modpol 122 * commands). We use the allowed key/salt tuple list as a default if 123 * no ks tuples as provided by the caller. We reject lists that include 124 * key/salts outside the policy. We re-order the requested ks tuples 125 * (which may be a subset of the policy) to reflect the policy order. 126 */ 127 static kadm5_ret_t 128 apply_keysalt_policy(kadm5_server_handle_t handle, const char *policy, 129 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 130 int *new_n_kstp, krb5_key_salt_tuple **new_kstp) 131 { 132 kadm5_ret_t ret; 133 kadm5_policy_ent_rec polent; 134 krb5_boolean have_polent; 135 int ak_n_ks_tuple = 0; 136 int new_n_ks_tuple = 0; 137 krb5_key_salt_tuple *ak_ks_tuple = NULL; 138 krb5_key_salt_tuple *new_ks_tuple = NULL; 139 krb5_key_salt_tuple *subset; 140 int i, m; 141 142 if (new_n_kstp != NULL) { 143 *new_n_kstp = 0; 144 *new_kstp = NULL; 145 } 146 147 memset(&polent, 0, sizeof(polent)); 148 ret = get_policy(handle, policy, &polent, &have_polent); 149 if (ret) 150 goto cleanup; 151 152 if (polent.allowed_keysalts == NULL) { 153 /* Requested keysalts allowed or default to supported_enctypes. */ 154 if (n_ks_tuple == 0) { 155 /* Default to supported_enctypes. */ 156 n_ks_tuple = handle->params.num_keysalts; 157 ks_tuple = handle->params.keysalts; 158 } 159 /* Dup the requested or defaulted keysalt tuples. */ 160 new_ks_tuple = malloc(n_ks_tuple * sizeof(*new_ks_tuple)); 161 if (new_ks_tuple == NULL) { 162 ret = ENOMEM; 163 goto cleanup; 164 } 165 memcpy(new_ks_tuple, ks_tuple, n_ks_tuple * sizeof(*new_ks_tuple)); 166 new_n_ks_tuple = n_ks_tuple; 167 ret = 0; 168 goto cleanup; 169 } 170 171 ret = krb5_string_to_keysalts(polent.allowed_keysalts, 172 ",", /* Tuple separators */ 173 NULL, /* Key/salt separators */ 174 0, /* No duplicates */ 175 &ak_ks_tuple, 176 &ak_n_ks_tuple); 177 /* 178 * Malformed policy? Shouldn't happen, but it's remotely possible 179 * someday, so we don't assert, just bail. 180 */ 181 if (ret) 182 goto cleanup; 183 184 /* Check that the requested ks_tuples are within policy, if we have one. */ 185 for (i = 0; i < n_ks_tuple; i++) { 186 if (!ks_tuple_present(ak_n_ks_tuple, ak_ks_tuple, &ks_tuple[i])) { 187 ret = KADM5_BAD_KEYSALTS; 188 goto cleanup; 189 } 190 } 191 192 /* Have policy but no ks_tuple input? Output the policy. */ 193 if (n_ks_tuple == 0) { 194 new_n_ks_tuple = ak_n_ks_tuple; 195 new_ks_tuple = ak_ks_tuple; 196 ak_ks_tuple = NULL; 197 goto cleanup; 198 } 199 200 /* 201 * Now filter the policy ks tuples by the requested ones so as to 202 * preserve in the requested sub-set the relative ordering from the 203 * policy. We could optimize this (if (n_ks_tuple == ak_n_ks_tuple) 204 * then skip this), but we don't bother. 205 */ 206 subset = calloc(n_ks_tuple, sizeof(*subset)); 207 if (subset == NULL) { 208 ret = ENOMEM; 209 goto cleanup; 210 } 211 for (m = 0, i = 0; i < ak_n_ks_tuple && m < n_ks_tuple; i++) { 212 if (ks_tuple_present(n_ks_tuple, ks_tuple, &ak_ks_tuple[i])) 213 subset[m++] = ak_ks_tuple[i]; 214 } 215 new_ks_tuple = subset; 216 new_n_ks_tuple = m; 217 ret = 0; 218 219 cleanup: 220 if (have_polent) 221 kadm5_free_policy_ent(handle->lhandle, &polent); 222 free(ak_ks_tuple); 223 224 if (new_n_kstp != NULL) { 225 *new_n_kstp = new_n_ks_tuple; 226 *new_kstp = new_ks_tuple; 227 } else { 228 free(new_ks_tuple); 229 } 230 return ret; 231 } 232 233 234 /* 235 * Set *passptr to NULL if the request looks like the first part of a krb5 1.6 236 * addprinc -randkey operation. The krb5 1.6 dummy password for these requests 237 * was invalid UTF-8, which runs afoul of the arcfour string-to-key. 238 */ 239 static void 240 check_1_6_dummy(kadm5_principal_ent_t entry, long mask, 241 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char **passptr) 242 { 243 int i; 244 char *password = *passptr; 245 246 /* Old-style randkey operations disallowed tickets to start. */ 247 if (password == NULL || !(mask & KADM5_ATTRIBUTES) || 248 !(entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX)) 249 return; 250 251 /* The 1.6 dummy password was the octets 1..255. */ 252 for (i = 0; (unsigned char) password[i] == i + 1; i++); 253 if (password[i] != '\0' || i != 255) 254 return; 255 256 /* This will make the caller use a random password instead. */ 257 *passptr = NULL; 258 } 259 260 /* Return the number of keys with the newest kvno. Assumes that all key data 261 * with the newest kvno are at the front of the key data array. */ 262 static int 263 count_new_keys(int n_key_data, krb5_key_data *key_data) 264 { 265 int n; 266 267 for (n = 1; n < n_key_data; n++) { 268 if (key_data[n - 1].key_data_kvno != key_data[n].key_data_kvno) 269 return n; 270 } 271 return n_key_data; 272 } 273 274 kadm5_ret_t 275 kadm5_create_principal(void *server_handle, 276 kadm5_principal_ent_t entry, long mask, 277 char *password) 278 { 279 return 280 kadm5_create_principal_3(server_handle, entry, mask, 281 0, NULL, password); 282 } 283 kadm5_ret_t 284 kadm5_create_principal_3(void *server_handle, 285 kadm5_principal_ent_t entry, long mask, 286 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 287 char *password) 288 { 289 krb5_db_entry *kdb; 290 osa_princ_ent_rec adb; 291 kadm5_policy_ent_rec polent; 292 krb5_boolean have_polent = FALSE; 293 krb5_timestamp now; 294 krb5_tl_data *tl_data_tail; 295 unsigned int ret; 296 kadm5_server_handle_t handle = server_handle; 297 krb5_keyblock *act_mkey; 298 krb5_kvno act_kvno; 299 int new_n_ks_tuple = 0, i; 300 krb5_key_salt_tuple *new_ks_tuple = NULL; 301 302 CHECK_HANDLE(server_handle); 303 304 krb5_clear_error_message(handle->context); 305 306 check_1_6_dummy(entry, mask, n_ks_tuple, ks_tuple, &password); 307 308 /* 309 * Argument sanity checking, and opening up the DB 310 */ 311 if (entry == NULL) 312 return EINVAL; 313 if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) || 314 (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) || 315 (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) || 316 (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) || 317 (mask & KADM5_FAIL_AUTH_COUNT)) 318 return KADM5_BAD_MASK; 319 if ((mask & KADM5_KEY_DATA) && entry->n_key_data != 0) 320 return KADM5_BAD_MASK; 321 if((mask & KADM5_POLICY) && entry->policy == NULL) 322 return KADM5_BAD_MASK; 323 if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR)) 324 return KADM5_BAD_MASK; 325 if((mask & ~ALL_PRINC_MASK)) 326 return KADM5_BAD_MASK; 327 if (mask & KADM5_TL_DATA) { 328 for (tl_data_tail = entry->tl_data; tl_data_tail != NULL; 329 tl_data_tail = tl_data_tail->tl_data_next) { 330 if (tl_data_tail->tl_data_type < 256) 331 return KADM5_BAD_TL_TYPE; 332 } 333 } 334 335 /* 336 * Check to see if the principal exists 337 */ 338 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb); 339 340 switch(ret) { 341 case KADM5_UNK_PRINC: 342 break; 343 case 0: 344 kdb_free_entry(handle, kdb, &adb); 345 return KADM5_DUP; 346 default: 347 return ret; 348 } 349 350 kdb = calloc(1, sizeof(*kdb)); 351 if (kdb == NULL) 352 return ENOMEM; 353 354 /* In all cases the principal entry is new and key data is set; let the 355 * database provider know. */ 356 kdb->mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL; 357 358 memset(&adb, 0, sizeof(osa_princ_ent_rec)); 359 360 /* 361 * If a policy was specified, load it. 362 * If we can not find the one specified return an error 363 */ 364 if ((mask & KADM5_POLICY)) { 365 ret = get_policy(handle, entry->policy, &polent, &have_polent); 366 if (ret) 367 goto cleanup; 368 } 369 if (password) { 370 ret = passwd_check(handle, password, have_polent ? &polent : NULL, 371 entry->principal); 372 if (ret) 373 goto cleanup; 374 } 375 /* 376 * Start populating the various DB fields, using the 377 * "defaults" for fields that were not specified by the 378 * mask. 379 */ 380 if ((ret = krb5_timeofday(handle->context, &now))) 381 goto cleanup; 382 383 kdb->magic = KRB5_KDB_MAGIC_NUMBER; 384 kdb->len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */ 385 386 if ((mask & KADM5_ATTRIBUTES)) 387 kdb->attributes = entry->attributes; 388 else 389 kdb->attributes = handle->params.flags; 390 391 if ((mask & KADM5_MAX_LIFE)) 392 kdb->max_life = entry->max_life; 393 else 394 kdb->max_life = handle->params.max_life; 395 396 if (mask & KADM5_MAX_RLIFE) 397 kdb->max_renewable_life = entry->max_renewable_life; 398 else 399 kdb->max_renewable_life = handle->params.max_rlife; 400 401 if ((mask & KADM5_PRINC_EXPIRE_TIME)) 402 kdb->expiration = entry->princ_expire_time; 403 else 404 kdb->expiration = handle->params.expiration; 405 406 kdb->pw_expiration = 0; 407 if (mask & KADM5_PW_EXPIRATION) { 408 kdb->pw_expiration = entry->pw_expiration; 409 } else if (have_polent && polent.pw_max_life) { 410 kdb->mask |= KADM5_PW_EXPIRATION; 411 kdb->pw_expiration = ts_incr(now, polent.pw_max_life); 412 } 413 414 kdb->last_success = 0; 415 kdb->last_failed = 0; 416 kdb->fail_auth_count = 0; 417 418 /* this is kind of gross, but in order to free the tl data, I need 419 to free the entire kdb entry, and that will try to free the 420 principal. */ 421 422 ret = krb5_copy_principal(handle->context, entry->principal, &kdb->princ); 423 if (ret) 424 goto cleanup; 425 426 if ((ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now))) 427 goto cleanup; 428 429 if (mask & KADM5_TL_DATA) { 430 /* splice entry->tl_data onto the front of kdb->tl_data */ 431 for (tl_data_tail = entry->tl_data; tl_data_tail; 432 tl_data_tail = tl_data_tail->tl_data_next) 433 { 434 ret = krb5_dbe_update_tl_data(handle->context, kdb, tl_data_tail); 435 if( ret ) 436 goto cleanup; 437 } 438 } 439 440 /* 441 * We need to have setup the TL data, so we have strings, so we can 442 * check enctype policy, which is why we check/initialize ks_tuple 443 * this late. 444 */ 445 ret = apply_keysalt_policy(handle, entry->policy, n_ks_tuple, ks_tuple, 446 &new_n_ks_tuple, &new_ks_tuple); 447 if (ret) 448 goto cleanup; 449 450 /* initialize the keys */ 451 452 ret = kdb_get_active_mkey(handle, &act_kvno, &act_mkey); 453 if (ret) 454 goto cleanup; 455 456 if (mask & KADM5_KEY_DATA) { 457 /* The client requested no keys for this principal. */ 458 assert(entry->n_key_data == 0); 459 } else if (password) { 460 ret = krb5_dbe_cpw(handle->context, act_mkey, new_ks_tuple, 461 new_n_ks_tuple, password, 462 (mask & KADM5_KVNO)?entry->kvno:1, 463 FALSE, kdb); 464 } else { 465 /* Null password means create with random key (new in 1.8). */ 466 ret = krb5_dbe_crk(handle->context, &master_keyblock, 467 new_ks_tuple, new_n_ks_tuple, FALSE, kdb); 468 if (mask & KADM5_KVNO) { 469 for (i = 0; i < kdb->n_key_data; i++) 470 kdb->key_data[i].key_data_kvno = entry->kvno; 471 } 472 } 473 if (ret) 474 goto cleanup; 475 476 /* Record the master key VNO used to encrypt this entry's keys */ 477 ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno); 478 if (ret) 479 goto cleanup; 480 481 ret = k5_kadm5_hook_create(handle->context, handle->hook_handles, 482 KADM5_HOOK_STAGE_PRECOMMIT, entry, mask, 483 new_n_ks_tuple, new_ks_tuple, password); 484 if (ret) 485 goto cleanup; 486 487 /* populate the admin-server-specific fields. In the OV server, 488 this used to be in a separate database. Since there's already 489 marshalling code for the admin fields, to keep things simple, 490 I'm going to keep it, and make all the admin stuff occupy a 491 single tl_data record, */ 492 493 adb.admin_history_kvno = INITIAL_HIST_KVNO; 494 if (mask & KADM5_POLICY) { 495 adb.aux_attributes = KADM5_POLICY; 496 497 /* this does *not* need to be strdup'ed, because adb is xdr */ 498 /* encoded in osa_adb_create_princ, and not ever freed */ 499 500 adb.policy = entry->policy; 501 } 502 503 /* store the new db entry */ 504 ret = kdb_put_entry(handle, kdb, &adb); 505 506 (void) k5_kadm5_hook_create(handle->context, handle->hook_handles, 507 KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask, 508 new_n_ks_tuple, new_ks_tuple, password); 509 510 cleanup: 511 free(new_ks_tuple); 512 krb5_db_free_principal(handle->context, kdb); 513 if (have_polent) 514 (void) kadm5_free_policy_ent(handle->lhandle, &polent); 515 return ret; 516 } 517 518 519 kadm5_ret_t 520 kadm5_delete_principal(void *server_handle, krb5_principal principal) 521 { 522 unsigned int ret; 523 kadm5_server_handle_t handle = server_handle; 524 525 CHECK_HANDLE(server_handle); 526 527 krb5_clear_error_message(handle->context); 528 529 if (principal == NULL) 530 return EINVAL; 531 532 /* Deleting K/M is mostly unrecoverable, so don't allow it. */ 533 if (krb5_principal_compare(handle->context, principal, master_princ)) 534 return KADM5_PROTECT_PRINCIPAL; 535 536 ret = k5_kadm5_hook_remove(handle->context, handle->hook_handles, 537 KADM5_HOOK_STAGE_PRECOMMIT, principal); 538 if (ret) 539 return ret; 540 541 ret = kdb_delete_entry(handle, principal); 542 543 if (ret == 0) 544 (void) k5_kadm5_hook_remove(handle->context, 545 handle->hook_handles, 546 KADM5_HOOK_STAGE_POSTCOMMIT, principal); 547 548 return ret; 549 } 550 551 kadm5_ret_t 552 kadm5_modify_principal(void *server_handle, 553 kadm5_principal_ent_t entry, long mask) 554 { 555 int ret, ret2, i; 556 kadm5_policy_ent_rec pol; 557 krb5_boolean have_pol = FALSE; 558 krb5_db_entry *kdb; 559 krb5_tl_data *tl_data_orig; 560 osa_princ_ent_rec adb; 561 kadm5_server_handle_t handle = server_handle; 562 563 CHECK_HANDLE(server_handle); 564 565 krb5_clear_error_message(handle->context); 566 567 if(entry == NULL) 568 return EINVAL; 569 if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) || 570 (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) || 571 (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) || 572 (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) || 573 (mask & KADM5_LAST_FAILED)) 574 return KADM5_BAD_MASK; 575 if((mask & ~ALL_PRINC_MASK)) 576 return KADM5_BAD_MASK; 577 if((mask & KADM5_POLICY) && entry->policy == NULL) 578 return KADM5_BAD_MASK; 579 if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR)) 580 return KADM5_BAD_MASK; 581 if (mask & KADM5_TL_DATA) { 582 tl_data_orig = entry->tl_data; 583 while (tl_data_orig) { 584 if (tl_data_orig->tl_data_type < 256) 585 return KADM5_BAD_TL_TYPE; 586 tl_data_orig = tl_data_orig->tl_data_next; 587 } 588 } 589 590 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb); 591 if (ret) 592 return(ret); 593 594 /* Let the mask propagate to the database provider. */ 595 kdb->mask = mask; 596 597 /* 598 * This is pretty much the same as create ... 599 */ 600 601 if ((mask & KADM5_POLICY)) { 602 ret = get_policy(handle, entry->policy, &pol, &have_pol); 603 if (ret) 604 goto done; 605 606 /* set us up to use the new policy */ 607 adb.aux_attributes |= KADM5_POLICY; 608 if (adb.policy) 609 free(adb.policy); 610 adb.policy = strdup(entry->policy); 611 } 612 613 if (mask & KADM5_PW_EXPIRATION) { 614 kdb->pw_expiration = entry->pw_expiration; 615 } else if (have_pol) { 616 /* set pw_max_life based on new policy */ 617 kdb->mask |= KADM5_PW_EXPIRATION; 618 if (pol.pw_max_life) { 619 ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, 620 &kdb->pw_expiration); 621 if (ret) 622 goto done; 623 kdb->pw_expiration = ts_incr(kdb->pw_expiration, pol.pw_max_life); 624 } else { 625 kdb->pw_expiration = 0; 626 } 627 } 628 629 if ((mask & KADM5_POLICY_CLR) && (adb.aux_attributes & KADM5_POLICY)) { 630 free(adb.policy); 631 adb.policy = NULL; 632 adb.aux_attributes &= ~KADM5_POLICY; 633 kdb->pw_expiration = 0; 634 } 635 636 if ((mask & KADM5_ATTRIBUTES)) 637 kdb->attributes = entry->attributes; 638 if ((mask & KADM5_MAX_LIFE)) 639 kdb->max_life = entry->max_life; 640 if ((mask & KADM5_PRINC_EXPIRE_TIME)) 641 kdb->expiration = entry->princ_expire_time; 642 if (mask & KADM5_MAX_RLIFE) 643 kdb->max_renewable_life = entry->max_renewable_life; 644 645 if((mask & KADM5_KVNO)) { 646 for (i = 0; i < kdb->n_key_data; i++) 647 kdb->key_data[i].key_data_kvno = entry->kvno; 648 } 649 650 if (mask & KADM5_TL_DATA) { 651 krb5_tl_data *tl; 652 653 /* may have to change the version number of the API. Updates the list with the given tl_data rather than over-writing */ 654 655 for (tl = entry->tl_data; tl; 656 tl = tl->tl_data_next) 657 { 658 ret = krb5_dbe_update_tl_data(handle->context, kdb, tl); 659 if( ret ) 660 { 661 goto done; 662 } 663 } 664 } 665 666 /* 667 * Setting entry->fail_auth_count to 0 can be used to manually unlock 668 * an account. It is not possible to set fail_auth_count to any other 669 * value using kadmin. 670 */ 671 if (mask & KADM5_FAIL_AUTH_COUNT) { 672 if (entry->fail_auth_count != 0) { 673 ret = KADM5_BAD_SERVER_PARAMS; 674 goto done; 675 } 676 677 kdb->fail_auth_count = 0; 678 } 679 680 ret = k5_kadm5_hook_modify(handle->context, handle->hook_handles, 681 KADM5_HOOK_STAGE_PRECOMMIT, entry, mask); 682 if (ret) 683 goto done; 684 685 ret = kdb_put_entry(handle, kdb, &adb); 686 if (ret) goto done; 687 (void) k5_kadm5_hook_modify(handle->context, handle->hook_handles, 688 KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask); 689 690 ret = KADM5_OK; 691 done: 692 if (have_pol) { 693 ret2 = kadm5_free_policy_ent(handle->lhandle, &pol); 694 ret = ret ? ret : ret2; 695 } 696 kdb_free_entry(handle, kdb, &adb); 697 return ret; 698 } 699 700 kadm5_ret_t 701 kadm5_rename_principal(void *server_handle, 702 krb5_principal source, krb5_principal target) 703 { 704 krb5_db_entry *kdb; 705 osa_princ_ent_rec adb; 706 krb5_error_code ret; 707 kadm5_server_handle_t handle = server_handle; 708 709 CHECK_HANDLE(server_handle); 710 711 krb5_clear_error_message(handle->context); 712 713 if (source == NULL || target == NULL) 714 return EINVAL; 715 716 if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) { 717 kdb_free_entry(handle, kdb, &adb); 718 return(KADM5_DUP); 719 } 720 721 ret = k5_kadm5_hook_rename(handle->context, handle->hook_handles, 722 KADM5_HOOK_STAGE_PRECOMMIT, source, target); 723 if (ret) 724 return ret; 725 726 ret = krb5_db_rename_principal(handle->context, source, target); 727 if (ret) 728 return ret; 729 730 /* Update the principal mod data. */ 731 ret = kdb_get_entry(handle, target, &kdb, &adb); 732 if (ret) 733 return ret; 734 kdb->mask = 0; 735 ret = kdb_put_entry(handle, kdb, &adb); 736 kdb_free_entry(handle, kdb, &adb); 737 if (ret) 738 return ret; 739 740 (void) k5_kadm5_hook_rename(handle->context, handle->hook_handles, 741 KADM5_HOOK_STAGE_POSTCOMMIT, source, target); 742 return 0; 743 } 744 745 kadm5_ret_t 746 kadm5_get_principal(void *server_handle, krb5_principal principal, 747 kadm5_principal_ent_t entry, 748 long in_mask) 749 { 750 krb5_db_entry *kdb; 751 osa_princ_ent_rec adb; 752 krb5_error_code ret = 0; 753 long mask; 754 int i; 755 kadm5_server_handle_t handle = server_handle; 756 757 CHECK_HANDLE(server_handle); 758 759 krb5_clear_error_message(handle->context); 760 761 /* 762 * In version 1, all the defined fields are always returned. 763 * entry is a pointer to a kadm5_principal_ent_t_v1 that should be 764 * filled with allocated memory. 765 */ 766 mask = in_mask; 767 768 memset(entry, 0, sizeof(*entry)); 769 770 if (principal == NULL) 771 return EINVAL; 772 773 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) 774 return ret; 775 776 if ((mask & KADM5_POLICY) && 777 adb.policy && (adb.aux_attributes & KADM5_POLICY)) { 778 if ((entry->policy = strdup(adb.policy)) == NULL) { 779 ret = ENOMEM; 780 goto done; 781 } 782 } 783 784 if (mask & KADM5_AUX_ATTRIBUTES) 785 entry->aux_attributes = adb.aux_attributes; 786 787 if ((mask & KADM5_PRINCIPAL) && 788 (ret = krb5_copy_principal(handle->context, kdb->princ, 789 &entry->principal))) { 790 goto done; 791 } 792 793 if (mask & KADM5_PRINC_EXPIRE_TIME) 794 entry->princ_expire_time = kdb->expiration; 795 796 if ((mask & KADM5_LAST_PWD_CHANGE) && 797 (ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, 798 &(entry->last_pwd_change)))) { 799 goto done; 800 } 801 802 if (mask & KADM5_PW_EXPIRATION) 803 entry->pw_expiration = kdb->pw_expiration; 804 if (mask & KADM5_MAX_LIFE) 805 entry->max_life = kdb->max_life; 806 807 /* this is a little non-sensical because the function returns two */ 808 /* values that must be checked separately against the mask */ 809 if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) { 810 ret = krb5_dbe_lookup_mod_princ_data(handle->context, kdb, 811 &(entry->mod_date), 812 &(entry->mod_name)); 813 if (ret) { 814 goto done; 815 } 816 817 if (! (mask & KADM5_MOD_TIME)) 818 entry->mod_date = 0; 819 if (! (mask & KADM5_MOD_NAME)) { 820 krb5_free_principal(handle->context, entry->mod_name); 821 entry->mod_name = NULL; 822 } 823 } 824 825 if (mask & KADM5_ATTRIBUTES) 826 entry->attributes = kdb->attributes; 827 828 if (mask & KADM5_KVNO) 829 for (entry->kvno = 0, i=0; i<kdb->n_key_data; i++) 830 if ((krb5_kvno) kdb->key_data[i].key_data_kvno > entry->kvno) 831 entry->kvno = kdb->key_data[i].key_data_kvno; 832 833 if (mask & KADM5_MKVNO) { 834 ret = krb5_dbe_get_mkvno(handle->context, kdb, &entry->mkvno); 835 if (ret) 836 goto done; 837 } 838 839 if (mask & KADM5_MAX_RLIFE) 840 entry->max_renewable_life = kdb->max_renewable_life; 841 if (mask & KADM5_LAST_SUCCESS) 842 entry->last_success = kdb->last_success; 843 if (mask & KADM5_LAST_FAILED) 844 entry->last_failed = kdb->last_failed; 845 if (mask & KADM5_FAIL_AUTH_COUNT) 846 entry->fail_auth_count = kdb->fail_auth_count; 847 if (mask & KADM5_TL_DATA) { 848 krb5_tl_data *tl, *tl2; 849 850 entry->tl_data = NULL; 851 852 tl = kdb->tl_data; 853 while (tl) { 854 if (tl->tl_data_type > 255) { 855 if ((tl2 = dup_tl_data(tl)) == NULL) { 856 ret = ENOMEM; 857 goto done; 858 } 859 tl2->tl_data_next = entry->tl_data; 860 entry->tl_data = tl2; 861 entry->n_tl_data++; 862 } 863 864 tl = tl->tl_data_next; 865 } 866 } 867 if (mask & KADM5_KEY_DATA) { 868 entry->n_key_data = kdb->n_key_data; 869 if(entry->n_key_data) { 870 entry->key_data = k5calloc(entry->n_key_data, 871 sizeof(krb5_key_data), &ret); 872 if (entry->key_data == NULL) 873 goto done; 874 } else 875 entry->key_data = NULL; 876 877 for (i = 0; i < entry->n_key_data; i++) 878 ret = krb5_copy_key_data_contents(handle->context, 879 &kdb->key_data[i], 880 &entry->key_data[i]); 881 if (ret) 882 goto done; 883 } 884 885 ret = KADM5_OK; 886 887 done: 888 if (ret && entry->principal) { 889 krb5_free_principal(handle->context, entry->principal); 890 entry->principal = NULL; 891 } 892 kdb_free_entry(handle, kdb, &adb); 893 894 return ret; 895 } 896 897 /* 898 * Function: check_pw_reuse 899 * 900 * Purpose: Check if a key appears in a list of keys, in order to 901 * enforce password history. 902 * 903 * Arguments: 904 * 905 * context (r) the krb5 context 906 * hist_keyblock (r) the key that hist_key_data is 907 * encrypted in 908 * n_new_key_data (r) length of new_key_data 909 * new_key_data (r) keys to check against 910 * pw_hist_data, encrypted in hist_keyblock 911 * n_pw_hist_data (r) length of pw_hist_data 912 * pw_hist_data (r) passwords to check new_key_data against 913 * 914 * Effects: 915 * For each new_key in new_key_data: 916 * decrypt new_key with the master_keyblock 917 * for each password in pw_hist_data: 918 * for each hist_key in password: 919 * decrypt hist_key with hist_keyblock 920 * compare the new_key and hist_key 921 * 922 * Returns krb5 errors, KADM5_PASS_RESUSE if a key in 923 * new_key_data is the same as a key in pw_hist_data, or 0. 924 */ 925 static kadm5_ret_t 926 check_pw_reuse(krb5_context context, 927 krb5_keyblock *hist_keyblocks, 928 int n_new_key_data, krb5_key_data *new_key_data, 929 unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data) 930 { 931 unsigned int x, y, z; 932 krb5_keyblock newkey, histkey, *kb; 933 krb5_key_data *key_data; 934 krb5_error_code ret; 935 936 assert (n_new_key_data >= 0); 937 for (x = 0; x < (unsigned) n_new_key_data; x++) { 938 /* Check only entries with the most recent kvno. */ 939 if (new_key_data[x].key_data_kvno != new_key_data[0].key_data_kvno) 940 break; 941 ret = krb5_dbe_decrypt_key_data(context, NULL, &(new_key_data[x]), 942 &newkey, NULL); 943 if (ret) 944 return(ret); 945 for (y = 0; y < n_pw_hist_data; y++) { 946 for (z = 0; z < (unsigned int) pw_hist_data[y].n_key_data; z++) { 947 for (kb = hist_keyblocks; kb->enctype != 0; kb++) { 948 key_data = &pw_hist_data[y].key_data[z]; 949 ret = krb5_dbe_decrypt_key_data(context, kb, key_data, 950 &histkey, NULL); 951 if (ret) 952 continue; 953 if (newkey.length == histkey.length && 954 newkey.enctype == histkey.enctype && 955 memcmp(newkey.contents, histkey.contents, 956 histkey.length) == 0) { 957 krb5_free_keyblock_contents(context, &histkey); 958 krb5_free_keyblock_contents(context, &newkey); 959 return KADM5_PASS_REUSE; 960 } 961 krb5_free_keyblock_contents(context, &histkey); 962 } 963 } 964 } 965 krb5_free_keyblock_contents(context, &newkey); 966 } 967 968 return(0); 969 } 970 971 static void 972 free_history_entry(krb5_context context, osa_pw_hist_ent *hist) 973 { 974 int i; 975 976 for (i = 0; i < hist->n_key_data; i++) 977 krb5_free_key_data_contents(context, &hist->key_data[i]); 978 free(hist->key_data); 979 } 980 981 /* 982 * Function: create_history_entry 983 * 984 * Purpose: Creates a password history entry from an array of 985 * key_data. 986 * 987 * Arguments: 988 * 989 * context (r) krb5_context to use 990 * mkey (r) master keyblock to decrypt key data with 991 * hist_key (r) history keyblock to encrypt key data with 992 * n_key_data (r) number of elements in key_data 993 * key_data (r) keys to add to the history entry 994 * hist_out (w) history entry to fill in 995 * 996 * Effects: 997 * 998 * hist->key_data is allocated to store n_key_data key_datas. Each 999 * element of key_data is decrypted with master_keyblock, re-encrypted 1000 * in hist_key, and added to hist->key_data. hist->n_key_data is 1001 * set to n_key_data. 1002 */ 1003 static 1004 int create_history_entry(krb5_context context, 1005 krb5_keyblock *hist_key, int n_key_data, 1006 krb5_key_data *key_data, osa_pw_hist_ent *hist_out) 1007 { 1008 int i; 1009 krb5_error_code ret = 0; 1010 krb5_keyblock key; 1011 krb5_keysalt salt; 1012 krb5_ui_2 kvno; 1013 osa_pw_hist_ent hist; 1014 1015 hist_out->key_data = NULL; 1016 hist_out->n_key_data = 0; 1017 1018 if (n_key_data < 0) 1019 return EINVAL; 1020 1021 memset(&key, 0, sizeof(key)); 1022 memset(&hist, 0, sizeof(hist)); 1023 1024 if (n_key_data == 0) 1025 goto cleanup; 1026 1027 hist.key_data = k5calloc(n_key_data, sizeof(krb5_key_data), &ret); 1028 if (hist.key_data == NULL) 1029 goto cleanup; 1030 1031 /* We only want to store the most recent kvno, and key_data should already 1032 * be sorted in descending order by kvno. */ 1033 kvno = key_data[0].key_data_kvno; 1034 1035 for (i = 0; i < n_key_data; i++) { 1036 if (key_data[i].key_data_kvno < kvno) 1037 break; 1038 ret = krb5_dbe_decrypt_key_data(context, NULL, 1039 &key_data[i], &key, 1040 &salt); 1041 if (ret) 1042 goto cleanup; 1043 1044 ret = krb5_dbe_encrypt_key_data(context, hist_key, &key, &salt, 1045 key_data[i].key_data_kvno, 1046 &hist.key_data[hist.n_key_data]); 1047 if (ret) 1048 goto cleanup; 1049 hist.n_key_data++; 1050 krb5_free_keyblock_contents(context, &key); 1051 /* krb5_free_keysalt(context, &salt); */ 1052 } 1053 1054 *hist_out = hist; 1055 hist.n_key_data = 0; 1056 hist.key_data = NULL; 1057 1058 cleanup: 1059 krb5_free_keyblock_contents(context, &key); 1060 free_history_entry(context, &hist); 1061 return ret; 1062 } 1063 1064 /* 1065 * Function: add_to_history 1066 * 1067 * Purpose: Adds a password to a principal's password history. 1068 * 1069 * Arguments: 1070 * 1071 * context (r) krb5_context to use 1072 * hist_kvno (r) kvno of current history key 1073 * adb (r/w) admin principal entry to add keys to 1074 * pol (r) adb's policy 1075 * pw (r) keys for the password to add to adb's key history 1076 * 1077 * Effects: 1078 * 1079 * add_to_history adds a single password to adb's password history. 1080 * pw contains n_key_data keys in its key_data, in storage should be 1081 * allocated but not freed by the caller (XXX blech!). 1082 * 1083 * This function maintains adb->old_keys as a circular queue. It 1084 * starts empty, and grows each time this function is called until it 1085 * is pol->pw_history_num items long. adb->old_key_len holds the 1086 * number of allocated entries in the array, and must therefore be [0, 1087 * pol->pw_history_num). adb->old_key_next is the index into the 1088 * array where the next element should be written, and must be [0, 1089 * adb->old_key_len). 1090 */ 1091 static kadm5_ret_t add_to_history(krb5_context context, 1092 krb5_kvno hist_kvno, 1093 osa_princ_ent_t adb, 1094 kadm5_policy_ent_t pol, 1095 osa_pw_hist_ent *pw) 1096 { 1097 osa_pw_hist_ent *histp; 1098 uint32_t nhist; 1099 unsigned int i, knext, nkeys; 1100 1101 nhist = pol->pw_history_num; 1102 /* A history of 1 means just check the current password */ 1103 if (nhist <= 1) 1104 return 0; 1105 1106 if (adb->admin_history_kvno != hist_kvno) { 1107 /* The history key has changed since the last password change, so we 1108 * have to reset the password history. */ 1109 free(adb->old_keys); 1110 adb->old_keys = NULL; 1111 adb->old_key_len = 0; 1112 adb->old_key_next = 0; 1113 adb->admin_history_kvno = hist_kvno; 1114 } 1115 1116 nkeys = adb->old_key_len; 1117 knext = adb->old_key_next; 1118 /* resize the adb->old_keys array if necessary */ 1119 if (nkeys + 1 < nhist) { 1120 if (adb->old_keys == NULL) { 1121 adb->old_keys = (osa_pw_hist_ent *) 1122 malloc((nkeys + 1) * sizeof (osa_pw_hist_ent)); 1123 } else { 1124 adb->old_keys = (osa_pw_hist_ent *) 1125 realloc(adb->old_keys, 1126 (nkeys + 1) * sizeof (osa_pw_hist_ent)); 1127 } 1128 if (adb->old_keys == NULL) 1129 return(ENOMEM); 1130 1131 memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent)); 1132 nkeys = ++adb->old_key_len; 1133 /* 1134 * To avoid losing old keys, shift forward each entry after 1135 * knext. 1136 */ 1137 for (i = nkeys - 1; i > knext; i--) { 1138 adb->old_keys[i] = adb->old_keys[i - 1]; 1139 } 1140 memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent)); 1141 } else if (nkeys + 1 > nhist) { 1142 /* 1143 * The policy must have changed! Shrink the array. 1144 * Can't simply realloc() down, since it might be wrapped. 1145 * To understand the arithmetic below, note that we are 1146 * copying into new positions 0 .. N-1 from old positions 1147 * old_key_next-N .. old_key_next-1, modulo old_key_len, 1148 * where N = pw_history_num - 1 is the length of the 1149 * shortened list. Matt Crawford, FNAL 1150 */ 1151 /* 1152 * M = adb->old_key_len, N = pol->pw_history_num - 1 1153 * 1154 * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M] 1155 */ 1156 int j; 1157 osa_pw_hist_t tmp; 1158 1159 tmp = (osa_pw_hist_ent *) 1160 malloc((nhist - 1) * sizeof (osa_pw_hist_ent)); 1161 if (tmp == NULL) 1162 return ENOMEM; 1163 for (i = 0; i < nhist - 1; i++) { 1164 /* 1165 * Add nkeys once before taking remainder to avoid 1166 * negative values. 1167 */ 1168 j = (i + nkeys + knext - (nhist - 1)) % nkeys; 1169 tmp[i] = adb->old_keys[j]; 1170 } 1171 /* Now free the ones we don't keep (the oldest ones) */ 1172 for (i = 0; i < nkeys - (nhist - 1); i++) { 1173 j = (i + nkeys + knext) % nkeys; 1174 histp = &adb->old_keys[j]; 1175 for (j = 0; j < histp->n_key_data; j++) { 1176 krb5_free_key_data_contents(context, &histp->key_data[j]); 1177 } 1178 free(histp->key_data); 1179 } 1180 free(adb->old_keys); 1181 adb->old_keys = tmp; 1182 nkeys = adb->old_key_len = nhist - 1; 1183 knext = adb->old_key_next = 0; 1184 } 1185 1186 /* 1187 * If nhist decreased since the last password change, and nkeys+1 1188 * is less than the previous nhist, it is possible for knext to 1189 * index into unallocated space. This condition would not be 1190 * caught by the resizing code above. 1191 */ 1192 if (knext + 1 > nkeys) 1193 knext = adb->old_key_next = 0; 1194 /* free the old pw history entry if it contains data */ 1195 histp = &adb->old_keys[knext]; 1196 for (i = 0; i < (unsigned int) histp->n_key_data; i++) 1197 krb5_free_key_data_contents(context, &histp->key_data[i]); 1198 free(histp->key_data); 1199 1200 /* store the new entry */ 1201 adb->old_keys[knext] = *pw; 1202 1203 /* update the next pointer */ 1204 if (++adb->old_key_next == nhist - 1) 1205 adb->old_key_next = 0; 1206 1207 return(0); 1208 } 1209 1210 kadm5_ret_t 1211 kadm5_chpass_principal(void *server_handle, 1212 krb5_principal principal, char *password) 1213 { 1214 return 1215 kadm5_chpass_principal_3(server_handle, principal, FALSE, 1216 0, NULL, password); 1217 } 1218 1219 kadm5_ret_t 1220 kadm5_chpass_principal_3(void *server_handle, 1221 krb5_principal principal, unsigned int keepold, 1222 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 1223 char *password) 1224 { 1225 krb5_timestamp now; 1226 kadm5_policy_ent_rec pol; 1227 osa_princ_ent_rec adb; 1228 krb5_db_entry *kdb; 1229 int ret, ret2, hist_added; 1230 krb5_boolean have_pol = FALSE; 1231 kadm5_server_handle_t handle = server_handle; 1232 osa_pw_hist_ent hist; 1233 krb5_keyblock *act_mkey, *hist_keyblocks = NULL; 1234 krb5_kvno act_kvno, hist_kvno; 1235 int new_n_ks_tuple = 0; 1236 krb5_key_salt_tuple *new_ks_tuple = NULL; 1237 1238 CHECK_HANDLE(server_handle); 1239 1240 krb5_clear_error_message(handle->context); 1241 1242 hist_added = 0; 1243 memset(&hist, 0, sizeof(hist)); 1244 1245 if (principal == NULL || password == NULL) 1246 return EINVAL; 1247 if ((krb5_principal_compare(handle->context, 1248 principal, hist_princ)) == TRUE) 1249 return KADM5_PROTECT_PRINCIPAL; 1250 1251 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) 1252 return(ret); 1253 1254 /* We will always be changing the key data, attributes, auth failure count, 1255 * and password expiration time. */ 1256 kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT | 1257 KADM5_PW_EXPIRATION; 1258 1259 ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple, 1260 &new_n_ks_tuple, &new_ks_tuple); 1261 if (ret) 1262 goto done; 1263 1264 if ((adb.aux_attributes & KADM5_POLICY)) { 1265 ret = get_policy(handle, adb.policy, &pol, &have_pol); 1266 if (ret) 1267 goto done; 1268 } 1269 if (have_pol) { 1270 /* Create a password history entry before we change kdb's key_data. */ 1271 ret = kdb_get_hist_key(handle, &hist_keyblocks, &hist_kvno); 1272 if (ret) 1273 goto done; 1274 ret = create_history_entry(handle->context, &hist_keyblocks[0], 1275 kdb->n_key_data, kdb->key_data, &hist); 1276 if (ret == KRB5_BAD_ENCTYPE) 1277 ret = KADM5_BAD_HIST_KEY; 1278 if (ret) 1279 goto done; 1280 } 1281 1282 if ((ret = passwd_check(handle, password, have_pol ? &pol : NULL, 1283 principal))) 1284 goto done; 1285 1286 ret = kdb_get_active_mkey(handle, &act_kvno, &act_mkey); 1287 if (ret) 1288 goto done; 1289 1290 ret = krb5_dbe_cpw(handle->context, act_mkey, new_ks_tuple, new_n_ks_tuple, 1291 password, 0 /* increment kvno */, 1292 keepold, kdb); 1293 if (ret) 1294 goto done; 1295 1296 ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno); 1297 if (ret) 1298 goto done; 1299 1300 kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; 1301 1302 ret = krb5_timeofday(handle->context, &now); 1303 if (ret) 1304 goto done; 1305 1306 kdb->pw_expiration = 0; 1307 if (have_pol) { 1308 ret = check_pw_reuse(handle->context, hist_keyblocks, 1309 kdb->n_key_data, kdb->key_data, 1310 1, &hist); 1311 if (ret) 1312 goto done; 1313 1314 if (pol.pw_history_num > 1) { 1315 /* If hist_kvno has changed since the last password change, we 1316 * can't check the history. */ 1317 if (adb.admin_history_kvno == hist_kvno) { 1318 ret = check_pw_reuse(handle->context, hist_keyblocks, 1319 kdb->n_key_data, kdb->key_data, 1320 adb.old_key_len, adb.old_keys); 1321 if (ret) 1322 goto done; 1323 } 1324 1325 /* Don't save empty history. */ 1326 if (hist.n_key_data > 0) { 1327 ret = add_to_history(handle->context, hist_kvno, &adb, &pol, 1328 &hist); 1329 if (ret) 1330 goto done; 1331 hist_added = 1; 1332 } 1333 } 1334 1335 if (pol.pw_max_life) 1336 kdb->pw_expiration = ts_incr(now, pol.pw_max_life); 1337 } 1338 1339 ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now); 1340 if (ret) 1341 goto done; 1342 1343 /* unlock principal on this KDC */ 1344 kdb->fail_auth_count = 0; 1345 1346 if (hist_added) 1347 kdb->mask |= KADM5_KEY_HIST; 1348 1349 ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles, 1350 KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold, 1351 new_n_ks_tuple, new_ks_tuple, password); 1352 if (ret) 1353 goto done; 1354 1355 if ((ret = kdb_put_entry(handle, kdb, &adb))) 1356 goto done; 1357 1358 (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles, 1359 KADM5_HOOK_STAGE_POSTCOMMIT, principal, 1360 keepold, new_n_ks_tuple, new_ks_tuple, password); 1361 ret = KADM5_OK; 1362 done: 1363 free(new_ks_tuple); 1364 if (!hist_added && hist.key_data) 1365 free_history_entry(handle->context, &hist); 1366 kdb_free_entry(handle, kdb, &adb); 1367 kdb_free_keyblocks(handle, hist_keyblocks); 1368 1369 if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol)) 1370 && !ret) 1371 ret = ret2; 1372 1373 return ret; 1374 } 1375 1376 kadm5_ret_t 1377 kadm5_randkey_principal(void *server_handle, 1378 krb5_principal principal, 1379 krb5_keyblock **keyblocks, 1380 int *n_keys) 1381 { 1382 return 1383 kadm5_randkey_principal_3(server_handle, principal, 1384 FALSE, 0, NULL, 1385 keyblocks, n_keys); 1386 } 1387 kadm5_ret_t 1388 kadm5_randkey_principal_3(void *server_handle, 1389 krb5_principal principal, 1390 unsigned int keepold, 1391 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 1392 krb5_keyblock **keyblocks, 1393 int *n_keys) 1394 { 1395 krb5_db_entry *kdb; 1396 osa_princ_ent_rec adb; 1397 krb5_timestamp now; 1398 kadm5_policy_ent_rec pol; 1399 int ret, n_new_keys; 1400 krb5_boolean have_pol = FALSE; 1401 kadm5_server_handle_t handle = server_handle; 1402 krb5_keyblock *act_mkey; 1403 krb5_kvno act_kvno; 1404 int new_n_ks_tuple = 0; 1405 krb5_key_salt_tuple *new_ks_tuple = NULL; 1406 1407 if (keyblocks) 1408 *keyblocks = NULL; 1409 1410 CHECK_HANDLE(server_handle); 1411 1412 krb5_clear_error_message(handle->context); 1413 1414 if (principal == NULL) 1415 return EINVAL; 1416 1417 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) 1418 return(ret); 1419 1420 /* We will always be changing the key data, attributes, auth failure count, 1421 * and password expiration time. */ 1422 kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT | 1423 KADM5_PW_EXPIRATION; 1424 1425 ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple, 1426 &new_n_ks_tuple, &new_ks_tuple); 1427 if (ret) 1428 goto done; 1429 1430 if (krb5_principal_compare(handle->context, principal, hist_princ)) { 1431 /* If changing the history entry, the new entry must have exactly one 1432 * key. */ 1433 if (keepold) { 1434 ret = KADM5_PROTECT_PRINCIPAL; 1435 goto done; 1436 } 1437 new_n_ks_tuple = 1; 1438 } 1439 1440 ret = kdb_get_active_mkey(handle, &act_kvno, &act_mkey); 1441 if (ret) 1442 goto done; 1443 1444 ret = krb5_dbe_crk(handle->context, act_mkey, new_ks_tuple, new_n_ks_tuple, 1445 keepold, kdb); 1446 if (ret) 1447 goto done; 1448 1449 ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno); 1450 if (ret) 1451 goto done; 1452 1453 kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; 1454 1455 ret = krb5_timeofday(handle->context, &now); 1456 if (ret) 1457 goto done; 1458 1459 if ((adb.aux_attributes & KADM5_POLICY)) { 1460 ret = get_policy(handle, adb.policy, &pol, &have_pol); 1461 if (ret) 1462 goto done; 1463 } 1464 1465 kdb->pw_expiration = 0; 1466 if (have_pol && pol.pw_max_life) 1467 kdb->pw_expiration = ts_incr(now, pol.pw_max_life); 1468 1469 ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now); 1470 if (ret) 1471 goto done; 1472 1473 /* unlock principal on this KDC */ 1474 kdb->fail_auth_count = 0; 1475 1476 if (keyblocks) { 1477 /* Return only the new keys added by krb5_dbe_crk. */ 1478 n_new_keys = count_new_keys(kdb->n_key_data, kdb->key_data); 1479 ret = decrypt_key_data(handle->context, n_new_keys, kdb->key_data, 1480 keyblocks, n_keys); 1481 if (ret) 1482 goto done; 1483 } 1484 1485 ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles, 1486 KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold, 1487 new_n_ks_tuple, new_ks_tuple, NULL); 1488 if (ret) 1489 goto done; 1490 if ((ret = kdb_put_entry(handle, kdb, &adb))) 1491 goto done; 1492 1493 (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles, 1494 KADM5_HOOK_STAGE_POSTCOMMIT, principal, 1495 keepold, new_n_ks_tuple, new_ks_tuple, NULL); 1496 ret = KADM5_OK; 1497 done: 1498 free(new_ks_tuple); 1499 kdb_free_entry(handle, kdb, &adb); 1500 if (have_pol) 1501 kadm5_free_policy_ent(handle->lhandle, &pol); 1502 1503 return ret; 1504 } 1505 1506 kadm5_ret_t 1507 kadm5_setkey_principal(void *server_handle, 1508 krb5_principal principal, 1509 krb5_keyblock *keyblocks, 1510 int n_keys) 1511 { 1512 return 1513 kadm5_setkey_principal_3(server_handle, principal, 1514 FALSE, 0, NULL, 1515 keyblocks, n_keys); 1516 } 1517 1518 kadm5_ret_t 1519 kadm5_setkey_principal_3(void *server_handle, 1520 krb5_principal principal, 1521 unsigned int keepold, 1522 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 1523 krb5_keyblock *keyblocks, 1524 int n_keys) 1525 { 1526 kadm5_key_data *key_data; 1527 kadm5_ret_t ret; 1528 int i; 1529 1530 if (keyblocks == NULL) 1531 return EINVAL; 1532 1533 if (n_ks_tuple) { 1534 if (n_ks_tuple != n_keys) 1535 return KADM5_SETKEY3_ETYPE_MISMATCH; 1536 for (i = 0; i < n_ks_tuple; i++) { 1537 if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) 1538 return KADM5_SETKEY3_ETYPE_MISMATCH; 1539 } 1540 } 1541 1542 key_data = calloc(n_keys, sizeof(kadm5_key_data)); 1543 if (key_data == NULL) 1544 return ENOMEM; 1545 1546 for (i = 0; i < n_keys; i++) { 1547 key_data[i].key = keyblocks[i]; 1548 key_data[i].salt.type = 1549 n_ks_tuple ? ks_tuple[i].ks_salttype : KRB5_KDB_SALTTYPE_NORMAL; 1550 } 1551 1552 ret = kadm5_setkey_principal_4(server_handle, principal, keepold, 1553 key_data, n_keys); 1554 free(key_data); 1555 return ret; 1556 } 1557 1558 /* Create a key/salt list from a key_data array. */ 1559 static kadm5_ret_t 1560 make_ks_from_key_data(krb5_context context, kadm5_key_data *key_data, 1561 int n_key_data, krb5_key_salt_tuple **out) 1562 { 1563 int i; 1564 krb5_key_salt_tuple *ks; 1565 1566 *out = NULL; 1567 1568 ks = calloc(n_key_data, sizeof(*ks)); 1569 if (ks == NULL) 1570 return ENOMEM; 1571 1572 for (i = 0; i < n_key_data; i++) { 1573 ks[i].ks_enctype = key_data[i].key.enctype; 1574 ks[i].ks_salttype = key_data[i].salt.type; 1575 } 1576 *out = ks; 1577 return 0; 1578 } 1579 1580 kadm5_ret_t 1581 kadm5_setkey_principal_4(void *server_handle, krb5_principal principal, 1582 unsigned int keepold, kadm5_key_data *key_data, 1583 int n_key_data) 1584 { 1585 krb5_db_entry *kdb; 1586 osa_princ_ent_rec adb; 1587 krb5_timestamp now; 1588 kadm5_policy_ent_rec pol; 1589 krb5_key_data *new_key_data = NULL; 1590 int i, j, ret, n_new_key_data = 0; 1591 krb5_kvno kvno; 1592 krb5_boolean similar, have_pol = FALSE; 1593 kadm5_server_handle_t handle = server_handle; 1594 krb5_keyblock *act_mkey; 1595 krb5_key_salt_tuple *ks_from_keys = NULL; 1596 1597 CHECK_HANDLE(server_handle); 1598 1599 krb5_clear_error_message(handle->context); 1600 1601 if (principal == NULL || key_data == NULL || n_key_data == 0) 1602 return EINVAL; 1603 1604 /* hist_princ will be NULL when initializing the database. */ 1605 if (hist_princ != NULL && 1606 krb5_principal_compare(handle->context, principal, hist_princ)) 1607 return KADM5_PROTECT_PRINCIPAL; 1608 1609 /* For now, all keys must have the same kvno. */ 1610 kvno = key_data[0].kvno; 1611 for (i = 1; i < n_key_data; i++) { 1612 if (key_data[i].kvno != kvno) 1613 return KADM5_SETKEY_BAD_KVNO; 1614 } 1615 1616 ret = kdb_get_entry(handle, principal, &kdb, &adb); 1617 if (ret) 1618 return ret; 1619 1620 /* We will always be changing the key data, attributes, auth failure count, 1621 * and password expiration time. */ 1622 kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT | 1623 KADM5_PW_EXPIRATION; 1624 1625 if (kvno == 0) { 1626 /* Pick the next kvno. */ 1627 for (i = 0; i < kdb->n_key_data; i++) { 1628 if (kdb->key_data[i].key_data_kvno > kvno) 1629 kvno = kdb->key_data[i].key_data_kvno; 1630 } 1631 kvno++; 1632 } else if (keepold) { 1633 /* Check that the kvno does collide with existing keys. */ 1634 for (i = 0; i < kdb->n_key_data; i++) { 1635 if (kdb->key_data[i].key_data_kvno == kvno) { 1636 ret = KADM5_SETKEY_BAD_KVNO; 1637 goto done; 1638 } 1639 } 1640 } 1641 1642 ret = make_ks_from_key_data(handle->context, key_data, n_key_data, 1643 &ks_from_keys); 1644 if (ret) 1645 goto done; 1646 1647 ret = apply_keysalt_policy(handle, adb.policy, n_key_data, ks_from_keys, 1648 NULL, NULL); 1649 free(ks_from_keys); 1650 if (ret) 1651 goto done; 1652 1653 for (i = 0; i < n_key_data; i++) { 1654 for (j = i + 1; j < n_key_data; j++) { 1655 ret = krb5_c_enctype_compare(handle->context, 1656 key_data[i].key.enctype, 1657 key_data[j].key.enctype, 1658 &similar); 1659 if (ret) 1660 goto done; 1661 if (similar) { 1662 if (key_data[i].salt.type == key_data[j].salt.type) { 1663 ret = KADM5_SETKEY_DUP_ENCTYPES; 1664 goto done; 1665 } 1666 } 1667 } 1668 } 1669 1670 n_new_key_data = n_key_data + (keepold ? kdb->n_key_data : 0); 1671 new_key_data = calloc(n_new_key_data, sizeof(krb5_key_data)); 1672 if (new_key_data == NULL) { 1673 n_new_key_data = 0; 1674 ret = ENOMEM; 1675 goto done; 1676 } 1677 1678 n_new_key_data = 0; 1679 for (i = 0; i < n_key_data; i++) { 1680 1681 ret = kdb_get_active_mkey(handle, NULL, &act_mkey); 1682 if (ret) 1683 goto done; 1684 1685 ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey, 1686 &key_data[i].key, &key_data[i].salt, 1687 kvno, &new_key_data[i]); 1688 if (ret) 1689 goto done; 1690 1691 n_new_key_data++; 1692 } 1693 1694 /* Copy old key data if necessary. */ 1695 if (keepold) { 1696 memcpy(new_key_data + n_new_key_data, kdb->key_data, 1697 kdb->n_key_data * sizeof(krb5_key_data)); 1698 memset(kdb->key_data, 0, kdb->n_key_data * sizeof(krb5_key_data)); 1699 1700 /* 1701 * Sort the keys to maintain the defined kvno order. We only need to 1702 * sort if we keep old keys, as otherwise we allow only a single kvno 1703 * to be specified. 1704 */ 1705 krb5_dbe_sort_key_data(new_key_data, n_new_key_data); 1706 } 1707 1708 /* Replace kdb->key_data with the new keys. */ 1709 cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data); 1710 kdb->key_data = new_key_data; 1711 kdb->n_key_data = n_new_key_data; 1712 new_key_data = NULL; 1713 n_new_key_data = 0; 1714 1715 kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; 1716 1717 ret = krb5_timeofday(handle->context, &now); 1718 if (ret) 1719 goto done; 1720 1721 if (adb.aux_attributes & KADM5_POLICY) { 1722 ret = get_policy(handle, adb.policy, &pol, &have_pol); 1723 if (ret) 1724 goto done; 1725 } 1726 1727 kdb->pw_expiration = 0; 1728 if (have_pol && pol.pw_max_life) 1729 kdb->pw_expiration = ts_incr(now, pol.pw_max_life); 1730 1731 ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now); 1732 if (ret) 1733 goto done; 1734 1735 /* Unlock principal on this KDC. */ 1736 kdb->fail_auth_count = 0; 1737 1738 ret = kdb_put_entry(handle, kdb, &adb); 1739 if (ret) 1740 goto done; 1741 1742 ret = KADM5_OK; 1743 1744 done: 1745 cleanup_key_data(handle->context, n_new_key_data, new_key_data); 1746 kdb_free_entry(handle, kdb, &adb); 1747 if (have_pol) 1748 kadm5_free_policy_ent(handle->lhandle, &pol); 1749 return ret; 1750 } 1751 1752 /* 1753 * Return the list of keys like kadm5_randkey_principal, 1754 * but don't modify the principal. 1755 */ 1756 kadm5_ret_t 1757 kadm5_get_principal_keys(void *server_handle /* IN */, 1758 krb5_principal principal /* IN */, 1759 krb5_kvno kvno /* IN */, 1760 kadm5_key_data **key_data_out /* OUT */, 1761 int *n_key_data_out /* OUT */) 1762 { 1763 krb5_db_entry *kdb; 1764 osa_princ_ent_rec adb; 1765 kadm5_ret_t ret; 1766 kadm5_server_handle_t handle = server_handle; 1767 kadm5_key_data *key_data = NULL; 1768 int i, nkeys = 0; 1769 1770 if (principal == NULL || key_data_out == NULL || n_key_data_out == NULL) 1771 return EINVAL; 1772 1773 CHECK_HANDLE(server_handle); 1774 1775 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) 1776 return(ret); 1777 1778 key_data = calloc(kdb->n_key_data, sizeof(kadm5_key_data)); 1779 if (key_data == NULL) { 1780 ret = ENOMEM; 1781 goto done; 1782 } 1783 1784 for (i = 0, nkeys = 0; i < kdb->n_key_data; i++) { 1785 if (kvno != 0 && kvno != kdb->key_data[i].key_data_kvno) 1786 continue; 1787 key_data[nkeys].kvno = kdb->key_data[i].key_data_kvno; 1788 1789 ret = krb5_dbe_decrypt_key_data(handle->context, NULL, 1790 &kdb->key_data[i], 1791 &key_data[nkeys].key, 1792 &key_data[nkeys].salt); 1793 if (ret) 1794 goto done; 1795 nkeys++; 1796 } 1797 1798 *n_key_data_out = nkeys; 1799 *key_data_out = key_data; 1800 key_data = NULL; 1801 nkeys = 0; 1802 ret = KADM5_OK; 1803 1804 done: 1805 kdb_free_entry(handle, kdb, &adb); 1806 kadm5_free_kadm5_key_data(handle->context, nkeys, key_data); 1807 1808 return ret; 1809 } 1810 1811 1812 /* 1813 * Allocate an array of n_key_data krb5_keyblocks, fill in each 1814 * element with the results of decrypting the nth key in key_data, 1815 * and if n_keys is not NULL fill it in with the 1816 * number of keys decrypted. 1817 */ 1818 static int decrypt_key_data(krb5_context context, 1819 int n_key_data, krb5_key_data *key_data, 1820 krb5_keyblock **keyblocks, int *n_keys) 1821 { 1822 krb5_keyblock *keys; 1823 int ret, i; 1824 1825 keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock)); 1826 if (keys == NULL) 1827 return ENOMEM; 1828 memset(keys, 0, n_key_data*sizeof(krb5_keyblock)); 1829 1830 for (i = 0; i < n_key_data; i++) { 1831 ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &keys[i], 1832 NULL); 1833 if (ret) { 1834 for (; i >= 0; i--) 1835 krb5_free_keyblock_contents(context, &keys[i]); 1836 free(keys); 1837 return ret; 1838 } 1839 } 1840 1841 *keyblocks = keys; 1842 if (n_keys) 1843 *n_keys = n_key_data; 1844 1845 return 0; 1846 } 1847 1848 /* 1849 * Function: kadm5_decrypt_key 1850 * 1851 * Purpose: Retrieves and decrypts a principal key. 1852 * 1853 * Arguments: 1854 * 1855 * server_handle (r) kadm5 handle 1856 * entry (r) principal retrieved with kadm5_get_principal 1857 * ktype (r) enctype to search for, or -1 to ignore 1858 * stype (r) salt type to search for, or -1 to ignore 1859 * kvno (r) kvno to search for, -1 for max, 0 for max 1860 * only if it also matches ktype and stype 1861 * keyblock (w) keyblock to fill in 1862 * keysalt (w) keysalt to fill in, or NULL 1863 * kvnop (w) kvno to fill in, or NULL 1864 * 1865 * Effects: Searches the key_data array of entry, which must have been 1866 * retrieved with kadm5_get_principal with the KADM5_KEY_DATA mask, to 1867 * find a key with a specified enctype, salt type, and kvno in a 1868 * principal entry. If not found, return ENOENT. Otherwise, decrypt 1869 * it with the master key, and return the key in keyblock, the salt 1870 * in salttype, and the key version number in kvno. 1871 * 1872 * If ktype or stype is -1, it is ignored for the search. If kvno is 1873 * -1, ktype and stype are ignored and the key with the max kvno is 1874 * returned. If kvno is 0, only the key with the max kvno is returned 1875 * and only if it matches the ktype and stype; otherwise, ENOENT is 1876 * returned. 1877 */ 1878 kadm5_ret_t kadm5_decrypt_key(void *server_handle, 1879 kadm5_principal_ent_t entry, krb5_int32 1880 ktype, krb5_int32 stype, krb5_int32 1881 kvno, krb5_keyblock *keyblock, 1882 krb5_keysalt *keysalt, int *kvnop) 1883 { 1884 kadm5_server_handle_t handle = server_handle; 1885 krb5_db_entry dbent; 1886 krb5_key_data *key_data; 1887 krb5_keyblock *mkey_ptr; 1888 int ret; 1889 1890 CHECK_HANDLE(server_handle); 1891 1892 if (entry->n_key_data == 0 || entry->key_data == NULL) 1893 return EINVAL; 1894 1895 /* find_enctype only uses these two fields */ 1896 dbent.n_key_data = entry->n_key_data; 1897 dbent.key_data = entry->key_data; 1898 if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype, 1899 stype, kvno, &key_data))) 1900 return ret; 1901 1902 /* find_mkey only uses this field */ 1903 dbent.tl_data = entry->tl_data; 1904 if ((ret = krb5_dbe_find_mkey(handle->context, &dbent, &mkey_ptr))) { 1905 /* try refreshing master key list */ 1906 /* XXX it would nice if we had the mkvno here for optimization */ 1907 if (krb5_db_fetch_mkey_list(handle->context, master_princ, 1908 &master_keyblock) == 0) { 1909 if ((ret = krb5_dbe_find_mkey(handle->context, &dbent, 1910 &mkey_ptr))) { 1911 return ret; 1912 } 1913 } else { 1914 return ret; 1915 } 1916 } 1917 1918 if ((ret = krb5_dbe_decrypt_key_data(handle->context, NULL, key_data, 1919 keyblock, keysalt))) 1920 return ret; 1921 1922 /* 1923 * Coerce the enctype of the output keyblock in case we got an 1924 * inexact match on the enctype; this behavior will go away when 1925 * the key storage architecture gets redesigned for 1.3. 1926 */ 1927 if (ktype != -1) 1928 keyblock->enctype = ktype; 1929 1930 if (kvnop) 1931 *kvnop = key_data->key_data_kvno; 1932 1933 return KADM5_OK; 1934 } 1935 1936 kadm5_ret_t 1937 kadm5_purgekeys(void *server_handle, 1938 krb5_principal principal, 1939 int keepkvno) 1940 { 1941 kadm5_server_handle_t handle = server_handle; 1942 kadm5_ret_t ret; 1943 krb5_db_entry *kdb; 1944 osa_princ_ent_rec adb; 1945 krb5_key_data *old_keydata; 1946 int n_old_keydata; 1947 int i, j, k; 1948 1949 CHECK_HANDLE(server_handle); 1950 1951 if (principal == NULL) 1952 return EINVAL; 1953 1954 ret = kdb_get_entry(handle, principal, &kdb, &adb); 1955 if (ret) 1956 return(ret); 1957 1958 if (keepkvno <= 0) { 1959 keepkvno = krb5_db_get_key_data_kvno(handle->context, kdb->n_key_data, 1960 kdb->key_data); 1961 } 1962 1963 old_keydata = kdb->key_data; 1964 n_old_keydata = kdb->n_key_data; 1965 kdb->n_key_data = 0; 1966 /* Allocate one extra key_data to avoid allocating 0 bytes. */ 1967 kdb->key_data = calloc(n_old_keydata, sizeof(krb5_key_data)); 1968 if (kdb->key_data == NULL) { 1969 ret = ENOMEM; 1970 goto done; 1971 } 1972 memset(kdb->key_data, 0, n_old_keydata * sizeof(krb5_key_data)); 1973 for (i = 0, j = 0; i < n_old_keydata; i++) { 1974 if (old_keydata[i].key_data_kvno < keepkvno) 1975 continue; 1976 1977 /* Alias the key_data_contents pointers; we null them out in the 1978 * source array immediately after. */ 1979 kdb->key_data[j] = old_keydata[i]; 1980 for (k = 0; k < old_keydata[i].key_data_ver; k++) { 1981 old_keydata[i].key_data_contents[k] = NULL; 1982 } 1983 j++; 1984 } 1985 kdb->n_key_data = j; 1986 cleanup_key_data(handle->context, n_old_keydata, old_keydata); 1987 1988 kdb->mask = KADM5_KEY_DATA; 1989 ret = kdb_put_entry(handle, kdb, &adb); 1990 if (ret) 1991 goto done; 1992 1993 done: 1994 kdb_free_entry(handle, kdb, &adb); 1995 return ret; 1996 } 1997 1998 kadm5_ret_t 1999 kadm5_get_strings(void *server_handle, krb5_principal principal, 2000 krb5_string_attr **strings_out, int *count_out) 2001 { 2002 kadm5_server_handle_t handle = server_handle; 2003 kadm5_ret_t ret; 2004 krb5_db_entry *kdb = NULL; 2005 2006 *strings_out = NULL; 2007 *count_out = 0; 2008 CHECK_HANDLE(server_handle); 2009 if (principal == NULL) 2010 return EINVAL; 2011 2012 ret = kdb_get_entry(handle, principal, &kdb, NULL); 2013 if (ret) 2014 return ret; 2015 2016 ret = krb5_dbe_get_strings(handle->context, kdb, strings_out, count_out); 2017 kdb_free_entry(handle, kdb, NULL); 2018 return ret; 2019 } 2020 2021 kadm5_ret_t 2022 kadm5_set_string(void *server_handle, krb5_principal principal, 2023 const char *key, const char *value) 2024 { 2025 kadm5_server_handle_t handle = server_handle; 2026 kadm5_ret_t ret; 2027 krb5_db_entry *kdb; 2028 osa_princ_ent_rec adb; 2029 2030 CHECK_HANDLE(server_handle); 2031 if (principal == NULL || key == NULL) 2032 return EINVAL; 2033 2034 ret = kdb_get_entry(handle, principal, &kdb, &adb); 2035 if (ret) 2036 return ret; 2037 2038 ret = krb5_dbe_set_string(handle->context, kdb, key, value); 2039 if (ret) 2040 goto done; 2041 2042 kdb->mask = KADM5_TL_DATA; 2043 ret = kdb_put_entry(handle, kdb, &adb); 2044 2045 done: 2046 kdb_free_entry(handle, kdb, &adb); 2047 return ret; 2048 } 2049 2050 kadm5_ret_t 2051 kadm5_create_alias(void *server_handle, krb5_principal alias, 2052 krb5_principal target) 2053 { 2054 krb5_db_entry *kdb; 2055 osa_princ_ent_rec adb = { 0 }; 2056 krb5_error_code ret; 2057 kadm5_server_handle_t handle = server_handle; 2058 2059 CHECK_HANDLE(server_handle); 2060 if (alias == NULL || target == NULL) 2061 return EINVAL; 2062 if (!krb5_realm_compare(handle->context, alias, target)) 2063 return KADM5_ALIAS_REALM; 2064 2065 ret = kdb_get_entry(handle, alias, &kdb, NULL); 2066 if (!ret) { 2067 kdb_free_entry(handle, kdb, NULL); 2068 return KADM5_DUP; 2069 } 2070 2071 ret = k5_kadm5_hook_alias(handle->context, handle->hook_handles, 2072 KADM5_HOOK_STAGE_PRECOMMIT, alias, target); 2073 if (ret) 2074 return ret; 2075 2076 ret = krb5_dbe_make_alias_entry(handle->context, alias, target, &kdb); 2077 if (ret) 2078 return ret; 2079 ret = kdb_put_entry(handle, kdb, &adb); 2080 krb5_db_free_principal(handle->context, kdb); 2081 if (ret) 2082 return ret; 2083 2084 (void) k5_kadm5_hook_alias(handle->context, handle->hook_handles, 2085 KADM5_HOOK_STAGE_POSTCOMMIT, alias, target); 2086 return 0; 2087 } 2088