1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* lib/kdb/kdb_default.c */ 3 /* 4 * Copyright 1995, 2009 by the Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 /* 27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 #include "k5-int.h" 32 #include "kdb.h" 33 #include <string.h> 34 #include <stdio.h> 35 #include <errno.h> 36 #include <arpa/inet.h> 37 38 39 /* 40 * Set *kd_out to the key data entry matching kvno, enctype, and salttype. If 41 * any of those three parameters are -1, ignore them. If kvno is 0, match only 42 * the highest kvno. Begin searching at the index *start and set *start to the 43 * index after the match. Do not return keys of non-permitted enctypes; return 44 * KRB5_KDB_NO_PERMITTED_KEY if the whole list was searched and only 45 * non-permitted matches were found. 46 */ 47 krb5_error_code 48 krb5_dbe_def_search_enctype(krb5_context context, krb5_db_entry *ent, 49 krb5_int32 *start, krb5_int32 enctype, 50 krb5_int32 salttype, krb5_int32 kvno, 51 krb5_key_data **kd_out) 52 { 53 krb5_key_data *kd; 54 krb5_int32 db_salttype; 55 krb5_boolean saw_non_permitted = FALSE; 56 int i; 57 58 *kd_out = NULL; 59 60 if (enctype != -1 && !krb5_is_permitted_enctype(context, enctype)) 61 return KRB5_KDB_NO_PERMITTED_KEY; 62 if (ent->n_key_data == 0) 63 return KRB5_KDB_NO_MATCHING_KEY; 64 65 /* Match the highest kvno if kvno is 0. Key data is sorted in descending 66 * order of kvno. */ 67 if (kvno == 0) 68 kvno = ent->key_data[0].key_data_kvno; 69 70 for (i = *start; i < ent->n_key_data; i++) { 71 kd = &ent->key_data[i]; 72 db_salttype = (kd->key_data_ver > 1) ? kd->key_data_type[1] : 73 KRB5_KDB_SALTTYPE_NORMAL; 74 75 /* Match this entry against the arguments. Stop searching if we have 76 * passed the entries for the requested kvno. */ 77 if (enctype != -1 && kd->key_data_type[0] != enctype) 78 continue; 79 if (salttype >= 0 && db_salttype != salttype) 80 continue; 81 if (kvno >= 0 && kd->key_data_kvno < kvno) 82 break; 83 if (kvno >= 0 && kd->key_data_kvno != kvno) 84 continue; 85 86 /* Filter out non-permitted enctypes. */ 87 if (!krb5_is_permitted_enctype(context, kd->key_data_type[0])) { 88 saw_non_permitted = TRUE; 89 continue; 90 } 91 92 *start = i + 1; 93 *kd_out = kd; 94 return 0; 95 } 96 97 /* If we scanned the whole set of keys and matched only non-permitted 98 * enctypes, indicate that. */ 99 return (*start == 0 && saw_non_permitted) ? KRB5_KDB_NO_PERMITTED_KEY : 100 KRB5_KDB_NO_MATCHING_KEY; 101 } 102 103 /* 104 * kdb default functions. Ideally, some other file should have this functions. For now, TBD. 105 */ 106 #ifndef min 107 #define min(a,b) (((a) < (b)) ? (a) : (b)) 108 #endif 109 110 krb5_error_code 111 krb5_def_store_mkey_list(krb5_context context, 112 char *keyfile, 113 krb5_principal mname, 114 krb5_keylist_node *keylist, 115 char *master_pwd) 116 { 117 krb5_error_code retval = 0; 118 char defkeyfile[MAXPATHLEN+1]; 119 char *tmp_ktname = NULL, *tmp_ktpath; 120 krb5_data *realm = krb5_princ_realm(context, mname); 121 krb5_keytab kt = NULL; 122 krb5_keytab_entry new_entry; 123 struct stat stb; 124 int statrc; 125 126 if (!keyfile) { 127 (void) snprintf(defkeyfile, sizeof(defkeyfile), "%s%s", 128 DEFAULT_KEYFILE_STUB, realm->data); 129 keyfile = defkeyfile; 130 } 131 132 if ((statrc = stat(keyfile, &stb)) >= 0) { 133 /* if keyfile exists it better be a regular file */ 134 if (!S_ISREG(stb.st_mode)) { 135 retval = EINVAL; 136 k5_setmsg(context, retval, 137 _("keyfile (%s) is not a regular file: %s"), 138 keyfile, error_message(retval)); 139 goto out; 140 } 141 } 142 143 /* 144 * We assume the stash file is in a directory writable only by root. 145 * As such, don't worry about collisions, just do an atomic rename. 146 */ 147 retval = asprintf(&tmp_ktname, "FILE:%s_tmp", keyfile); 148 if (retval < 0) { 149 k5_setmsg(context, retval, 150 _("Could not create temp keytab file name.")); 151 goto out; 152 } 153 154 /* 155 * Set tmp_ktpath to point to the keyfile path (skip FILE:). Subtracting 156 * 1 to account for NULL terminator in sizeof calculation of a string 157 * constant. Used further down. 158 */ 159 tmp_ktpath = tmp_ktname + (sizeof("FILE:") - 1); 160 161 /* 162 * This time-of-check-to-time-of-access race is fine; we care only 163 * about an administrator running the command twice, not an attacker 164 * trying to beat us to creating the file. Per the above comment, we 165 * assume the stash file is in a directory writable only by root. 166 */ 167 statrc = stat(tmp_ktpath, &stb); 168 if (statrc == -1 && errno != ENOENT) { 169 /* ENOENT is the expected case */ 170 retval = errno; 171 goto out; 172 } else if (statrc == 0) { 173 retval = EEXIST; 174 k5_setmsg(context, retval, 175 _("Temporary stash file already exists: %s."), tmp_ktpath); 176 goto out; 177 } 178 179 /* create new stash keytab using temp file name */ 180 retval = krb5_kt_resolve(context, tmp_ktname, &kt); 181 if (retval != 0) 182 goto out; 183 184 while (keylist && !retval) { 185 memset(&new_entry, 0, sizeof(new_entry)); 186 new_entry.principal = mname; 187 new_entry.key = keylist->keyblock; 188 new_entry.vno = keylist->kvno; 189 190 retval = krb5_kt_add_entry(context, kt, &new_entry); 191 keylist = keylist->next; 192 } 193 krb5_kt_close(context, kt); 194 195 if (retval != 0) { 196 /* Clean up by deleting the tmp keyfile if it exists. */ 197 (void)unlink(tmp_ktpath); 198 } else { 199 /* Atomically rename temp keyfile to original filename. */ 200 if (rename(tmp_ktpath, keyfile) < 0) { 201 retval = errno; 202 k5_setmsg(context, retval, 203 _("rename of temporary keyfile (%s) to (%s) failed: %s"), 204 tmp_ktpath, keyfile, error_message(errno)); 205 } 206 } 207 208 out: 209 if (tmp_ktname != NULL) 210 free(tmp_ktname); 211 212 return retval; 213 } 214 215 static krb5_error_code 216 krb5_db_def_fetch_mkey_stash(krb5_context context, 217 const char *keyfile, 218 krb5_keyblock *key, 219 krb5_kvno *kvno) 220 { 221 krb5_error_code retval = 0; 222 krb5_ui_2 enctype; 223 krb5_ui_4 keylength; 224 FILE *kf = NULL; 225 226 if (!(kf = fopen(keyfile, "rb"))) 227 return KRB5_KDB_CANTREAD_STORED; 228 set_cloexec_file(kf); 229 230 if (fread((krb5_pointer) &enctype, 2, 1, kf) != 1) { 231 retval = KRB5_KDB_CANTREAD_STORED; 232 goto errout; 233 } 234 235 #if BIG_ENDIAN_MASTER_KEY 236 enctype = ntohs((uint16_t) enctype); 237 #endif 238 239 if (key->enctype == ENCTYPE_UNKNOWN) 240 key->enctype = enctype; 241 else if (enctype != key->enctype) { 242 retval = KRB5_KDB_BADSTORED_MKEY; 243 goto errout; 244 } 245 246 if (fread((krb5_pointer) &keylength, 247 sizeof(keylength), 1, kf) != 1) { 248 retval = KRB5_KDB_CANTREAD_STORED; 249 goto errout; 250 } 251 252 #if BIG_ENDIAN_MASTER_KEY 253 key->length = ntohl((uint32_t) keylength); 254 #else 255 key->length = keylength; 256 #endif 257 258 if (!key->length || key->length > 1024) { 259 retval = KRB5_KDB_BADSTORED_MKEY; 260 goto errout; 261 } 262 263 if (!(key->contents = (krb5_octet *)malloc(key->length))) { 264 retval = ENOMEM; 265 goto errout; 266 } 267 268 if (fread((krb5_pointer) key->contents, sizeof(key->contents[0]), 269 key->length, kf) != key->length) { 270 retval = KRB5_KDB_CANTREAD_STORED; 271 zap(key->contents, key->length); 272 free(key->contents); 273 key->contents = 0; 274 } else 275 retval = 0; 276 277 /* 278 * Note, the old stash format did not store the kvno and at this point it 279 * can be assumed to be 1 as is the case for the mkey princ. If the kvno is 280 * passed in and isn't ignore_vno just leave it alone as this could cause 281 * verifcation trouble if the mkey princ is using a kvno other than 1. 282 */ 283 if (kvno && *kvno == IGNORE_VNO) 284 *kvno = 1; 285 286 errout: 287 (void) fclose(kf); 288 return retval; 289 } 290 291 static krb5_error_code 292 krb5_db_def_fetch_mkey_keytab(krb5_context context, 293 const char *keyfile, 294 krb5_principal mname, 295 krb5_keyblock *key, 296 krb5_kvno *kvno) 297 { 298 krb5_error_code retval = 0; 299 krb5_keytab kt = NULL; 300 krb5_keytab_entry kt_ent; 301 krb5_enctype enctype = IGNORE_ENCTYPE; 302 303 if ((retval = krb5_kt_resolve(context, keyfile, &kt)) != 0) 304 goto errout; 305 306 /* override default */ 307 if (key->enctype != ENCTYPE_UNKNOWN) 308 enctype = key->enctype; 309 310 if ((retval = krb5_kt_get_entry(context, kt, mname, 311 kvno ? *kvno : IGNORE_VNO, 312 enctype, 313 &kt_ent)) == 0) { 314 315 if (key->enctype == ENCTYPE_UNKNOWN) 316 key->enctype = kt_ent.key.enctype; 317 318 if (((int) kt_ent.key.length) < 0) { 319 retval = KRB5_KDB_BADSTORED_MKEY; 320 krb5_kt_free_entry(context, &kt_ent); 321 goto errout; 322 } 323 324 key->length = kt_ent.key.length; 325 326 /* 327 * If a kvno pointer was passed in and it dereferences the 328 * IGNORE_VNO value then it should be assigned the value of the kvno 329 * found in the keytab otherwise the KNVO specified should be the 330 * same as the one returned from the keytab. 331 */ 332 if (kvno != NULL && *kvno == IGNORE_VNO) 333 *kvno = kt_ent.vno; 334 335 /* 336 * kt_ent will be free'd so need to allocate and copy key contents for 337 * output to caller. 338 */ 339 key->contents = k5memdup(kt_ent.key.contents, kt_ent.key.length, 340 &retval); 341 if (key->contents == NULL) { 342 krb5_kt_free_entry(context, &kt_ent); 343 goto errout; 344 } 345 krb5_kt_free_entry(context, &kt_ent); 346 } 347 348 errout: 349 if (kt) 350 krb5_kt_close(context, kt); 351 352 return retval; 353 } 354 355 krb5_error_code 356 krb5_db_def_fetch_mkey(krb5_context context, 357 krb5_principal mname, 358 krb5_keyblock *key, 359 krb5_kvno *kvno, 360 char *db_args) 361 { 362 krb5_error_code retval; 363 char keyfile[MAXPATHLEN+1]; 364 krb5_data *realm = krb5_princ_realm(context, mname); 365 366 key->magic = KV5M_KEYBLOCK; 367 368 if (db_args != NULL) { 369 (void) strncpy(keyfile, db_args, sizeof(keyfile)); 370 } else { 371 (void) snprintf(keyfile, sizeof(keyfile), "%s%s", 372 DEFAULT_KEYFILE_STUB, realm->data); 373 } 374 /* null terminate no matter what */ 375 keyfile[sizeof(keyfile) - 1] = '\0'; 376 377 /* Try the keytab and old stash file formats. */ 378 retval = krb5_db_def_fetch_mkey_keytab(context, keyfile, mname, key, kvno); 379 if (retval == KRB5_KEYTAB_BADVNO) 380 retval = krb5_db_def_fetch_mkey_stash(context, keyfile, key, kvno); 381 382 /* 383 * Use a generic error code for failure to retrieve the master 384 * key, but set a message indicating the actual error. 385 */ 386 if (retval != 0) { 387 k5_setmsg(context, KRB5_KDB_CANTREAD_STORED, 388 _("Can not fetch master key (error: %s)."), 389 error_message(retval)); 390 return KRB5_KDB_CANTREAD_STORED; 391 } else 392 return 0; 393 } 394 395 krb5_error_code 396 krb5_def_fetch_mkey_list(krb5_context context, 397 krb5_principal mprinc, 398 const krb5_keyblock *mkey, 399 krb5_keylist_node **mkeys_list) 400 { 401 krb5_error_code retval; 402 krb5_db_entry *master_entry; 403 krb5_boolean found_key = FALSE; 404 krb5_keyblock cur_mkey; 405 krb5_keylist_node *mkey_list_head = NULL, **mkey_list_node; 406 krb5_key_data *key_data; 407 krb5_mkey_aux_node *mkey_aux_data_list = NULL, *aux_data_entry; 408 int i; 409 410 if (mkeys_list == NULL) 411 return (EINVAL); 412 413 memset(&cur_mkey, 0, sizeof(cur_mkey)); 414 415 retval = krb5_db_get_principal(context, mprinc, 0, &master_entry); 416 if (retval == KRB5_KDB_NOENTRY) 417 return (KRB5_KDB_NOMASTERKEY); 418 if (retval) 419 return (retval); 420 421 if (master_entry->n_key_data == 0) { 422 retval = KRB5_KDB_NOMASTERKEY; 423 goto clean_n_exit; 424 } 425 426 /* 427 * Check if the input mkey is the latest key and if it isn't then find the 428 * latest mkey. 429 */ 430 431 if (mkey->enctype == master_entry->key_data[0].key_data_type[0]) { 432 if (krb5_dbe_decrypt_key_data(context, mkey, 433 &master_entry->key_data[0], 434 &cur_mkey, NULL) == 0) { 435 found_key = TRUE; 436 } 437 } 438 439 if (!found_key) { 440 if ((retval = krb5_dbe_lookup_mkey_aux(context, master_entry, 441 &mkey_aux_data_list))) 442 goto clean_n_exit; 443 444 for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL; 445 aux_data_entry = aux_data_entry->next) { 446 447 if (krb5_dbe_decrypt_key_data(context, mkey, 448 &aux_data_entry->latest_mkey, 449 &cur_mkey, NULL) == 0) { 450 found_key = TRUE; 451 break; 452 } 453 } 454 if (found_key != TRUE) { 455 k5_setmsg(context, KRB5_KDB_BADMASTERKEY, 456 _("Unable to decrypt latest master key with the " 457 "provided master key\n")); 458 retval = KRB5_KDB_BADMASTERKEY; 459 goto clean_n_exit; 460 } 461 } 462 463 /* 464 * Extract all the mkeys from master_entry using the most current mkey and 465 * create a mkey list for the mkeys field in kdc_realm_t. 466 */ 467 468 mkey_list_head = (krb5_keylist_node *) malloc(sizeof(krb5_keylist_node)); 469 if (mkey_list_head == NULL) { 470 retval = ENOMEM; 471 goto clean_n_exit; 472 } 473 474 memset(mkey_list_head, 0, sizeof(krb5_keylist_node)); 475 476 /* Set mkey_list_head to the current mkey as an optimization. */ 477 /* mkvno may not be latest so ... */ 478 mkey_list_head->kvno = master_entry->key_data[0].key_data_kvno; 479 /* this is the latest clear mkey (avoids a redundant decrypt) */ 480 mkey_list_head->keyblock = cur_mkey; 481 482 /* loop through any other master keys creating a list of krb5_keylist_nodes */ 483 mkey_list_node = &mkey_list_head->next; 484 for (i = 1; i < master_entry->n_key_data; i++) { 485 if (*mkey_list_node == NULL) { 486 /* *mkey_list_node points to next field of previous node */ 487 *mkey_list_node = (krb5_keylist_node *) malloc(sizeof(krb5_keylist_node)); 488 if (*mkey_list_node == NULL) { 489 retval = ENOMEM; 490 goto clean_n_exit; 491 } 492 memset(*mkey_list_node, 0, sizeof(krb5_keylist_node)); 493 } 494 key_data = &master_entry->key_data[i]; 495 retval = krb5_dbe_decrypt_key_data(context, &cur_mkey, key_data, 496 &((*mkey_list_node)->keyblock), 497 NULL); 498 if (retval) 499 goto clean_n_exit; 500 501 (*mkey_list_node)->kvno = key_data->key_data_kvno; 502 mkey_list_node = &((*mkey_list_node)->next); 503 } 504 505 *mkeys_list = mkey_list_head; 506 507 clean_n_exit: 508 krb5_db_free_principal(context, master_entry); 509 krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_list); 510 if (retval != 0) 511 krb5_dbe_free_key_list(context, mkey_list_head); 512 return retval; 513 } 514 515 krb5_error_code 516 krb5_db_def_rename_principal(krb5_context kcontext, 517 krb5_const_principal source, 518 krb5_const_principal target) 519 { 520 krb5_db_entry *kdb = NULL; 521 krb5_principal oldprinc; 522 krb5_error_code ret; 523 524 if (source == NULL || target == NULL) 525 return EINVAL; 526 527 ret = krb5_db_get_principal(kcontext, source, 0, &kdb); 528 if (ret) 529 goto cleanup; 530 531 /* Store salt values explicitly so that they don't depend on the principal 532 * name. */ 533 ret = krb5_dbe_specialize_salt(kcontext, kdb); 534 if (ret) 535 goto cleanup; 536 537 /* Temporarily alias kdb->princ to target and put the principal entry. */ 538 oldprinc = kdb->princ; 539 kdb->princ = (krb5_principal)target; 540 ret = krb5_db_put_principal(kcontext, kdb); 541 kdb->princ = oldprinc; 542 if (ret) 543 goto cleanup; 544 545 ret = krb5_db_delete_principal(kcontext, (krb5_principal)source); 546 547 548 cleanup: 549 krb5_db_free_principal(kcontext, kdb); 550 return ret; 551 } 552