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