1 /* $NetBSD: mkey.c,v 1.1.1.2 2014/04/24 12:45:28 pettai Exp $ */ 2 3 /* 4 * Copyright (c) 2000 - 2004 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "hdb_locl.h" 37 #ifndef O_BINARY 38 #define O_BINARY 0 39 #endif 40 41 struct hdb_master_key_data { 42 krb5_keytab_entry keytab; 43 krb5_crypto crypto; 44 struct hdb_master_key_data *next; 45 }; 46 47 void 48 hdb_free_master_key(krb5_context context, hdb_master_key mkey) 49 { 50 struct hdb_master_key_data *ptr; 51 while(mkey) { 52 krb5_kt_free_entry(context, &mkey->keytab); 53 if (mkey->crypto) 54 krb5_crypto_destroy(context, mkey->crypto); 55 ptr = mkey; 56 mkey = mkey->next; 57 free(ptr); 58 } 59 } 60 61 krb5_error_code 62 hdb_process_master_key(krb5_context context, 63 int kvno, krb5_keyblock *key, krb5_enctype etype, 64 hdb_master_key *mkey) 65 { 66 krb5_error_code ret; 67 68 *mkey = calloc(1, sizeof(**mkey)); 69 if(*mkey == NULL) { 70 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 71 return ENOMEM; 72 } 73 (*mkey)->keytab.vno = kvno; 74 ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal); 75 if(ret) 76 goto fail; 77 ret = krb5_copy_keyblock_contents(context, key, &(*mkey)->keytab.keyblock); 78 if(ret) 79 goto fail; 80 if(etype != 0) 81 (*mkey)->keytab.keyblock.keytype = etype; 82 (*mkey)->keytab.timestamp = time(NULL); 83 ret = krb5_crypto_init(context, key, etype, &(*mkey)->crypto); 84 if(ret) 85 goto fail; 86 return 0; 87 fail: 88 hdb_free_master_key(context, *mkey); 89 *mkey = NULL; 90 return ret; 91 } 92 93 krb5_error_code 94 hdb_add_master_key(krb5_context context, krb5_keyblock *key, 95 hdb_master_key *inout) 96 { 97 int vno = 0; 98 hdb_master_key p; 99 krb5_error_code ret; 100 101 for(p = *inout; p; p = p->next) 102 vno = max(vno, p->keytab.vno); 103 vno++; 104 ret = hdb_process_master_key(context, vno, key, 0, &p); 105 if(ret) 106 return ret; 107 p->next = *inout; 108 *inout = p; 109 return 0; 110 } 111 112 static krb5_error_code 113 read_master_keytab(krb5_context context, const char *filename, 114 hdb_master_key *mkey) 115 { 116 krb5_error_code ret; 117 krb5_keytab id; 118 krb5_kt_cursor cursor; 119 krb5_keytab_entry entry; 120 hdb_master_key p; 121 122 ret = krb5_kt_resolve(context, filename, &id); 123 if(ret) 124 return ret; 125 126 ret = krb5_kt_start_seq_get(context, id, &cursor); 127 if(ret) 128 goto out; 129 *mkey = NULL; 130 while(krb5_kt_next_entry(context, id, &entry, &cursor) == 0) { 131 p = calloc(1, sizeof(*p)); 132 if(p == NULL) { 133 krb5_kt_end_seq_get(context, id, &cursor); 134 ret = ENOMEM; 135 goto out; 136 } 137 p->keytab = entry; 138 ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto); 139 p->next = *mkey; 140 *mkey = p; 141 } 142 krb5_kt_end_seq_get(context, id, &cursor); 143 out: 144 krb5_kt_close(context, id); 145 return ret; 146 } 147 148 /* read a MIT master keyfile */ 149 static krb5_error_code 150 read_master_mit(krb5_context context, const char *filename, 151 int byteorder, hdb_master_key *mkey) 152 { 153 int fd; 154 krb5_error_code ret; 155 krb5_storage *sp; 156 int16_t enctype; 157 krb5_keyblock key; 158 159 fd = open(filename, O_RDONLY | O_BINARY); 160 if(fd < 0) { 161 int save_errno = errno; 162 krb5_set_error_message(context, save_errno, "failed to open %s: %s", 163 filename, strerror(save_errno)); 164 return save_errno; 165 } 166 sp = krb5_storage_from_fd(fd); 167 if(sp == NULL) { 168 close(fd); 169 return errno; 170 } 171 krb5_storage_set_flags(sp, byteorder); 172 /* could possibly use ret_keyblock here, but do it with more 173 checks for now */ 174 { 175 ret = krb5_ret_int16(sp, &enctype); 176 if (ret) 177 goto out; 178 ret = krb5_enctype_valid(context, enctype); 179 if (ret) 180 goto out; 181 key.keytype = enctype; 182 ret = krb5_ret_data(sp, &key.keyvalue); 183 if(ret) 184 goto out; 185 } 186 ret = hdb_process_master_key(context, 1, &key, 0, mkey); 187 krb5_free_keyblock_contents(context, &key); 188 out: 189 krb5_storage_free(sp); 190 close(fd); 191 return ret; 192 } 193 194 /* read an old master key file */ 195 static krb5_error_code 196 read_master_encryptionkey(krb5_context context, const char *filename, 197 hdb_master_key *mkey) 198 { 199 int fd; 200 krb5_keyblock key; 201 krb5_error_code ret; 202 unsigned char buf[256]; 203 ssize_t len; 204 size_t ret_len; 205 206 fd = open(filename, O_RDONLY | O_BINARY); 207 if(fd < 0) { 208 int save_errno = errno; 209 krb5_set_error_message(context, save_errno, "failed to open %s: %s", 210 filename, strerror(save_errno)); 211 return save_errno; 212 } 213 214 len = read(fd, buf, sizeof(buf)); 215 close(fd); 216 if(len < 0) { 217 int save_errno = errno; 218 krb5_set_error_message(context, save_errno, "error reading %s: %s", 219 filename, strerror(save_errno)); 220 return save_errno; 221 } 222 223 ret = decode_EncryptionKey(buf, len, &key, &ret_len); 224 memset(buf, 0, sizeof(buf)); 225 if(ret) 226 return ret; 227 228 /* Originally, the keytype was just that, and later it got changed 229 to des-cbc-md5, but we always used des in cfb64 mode. This 230 should cover all cases, but will break if someone has hacked 231 this code to really use des-cbc-md5 -- but then that's not my 232 problem. */ 233 if(key.keytype == ETYPE_DES_CBC_CRC || key.keytype == ETYPE_DES_CBC_MD5) 234 key.keytype = ETYPE_DES_CFB64_NONE; 235 236 ret = hdb_process_master_key(context, 0, &key, 0, mkey); 237 krb5_free_keyblock_contents(context, &key); 238 return ret; 239 } 240 241 /* read a krb4 /.k style file */ 242 static krb5_error_code 243 read_master_krb4(krb5_context context, const char *filename, 244 hdb_master_key *mkey) 245 { 246 int fd; 247 krb5_keyblock key; 248 krb5_error_code ret; 249 unsigned char buf[256]; 250 ssize_t len; 251 252 fd = open(filename, O_RDONLY | O_BINARY); 253 if(fd < 0) { 254 int save_errno = errno; 255 krb5_set_error_message(context, save_errno, "failed to open %s: %s", 256 filename, strerror(save_errno)); 257 return save_errno; 258 } 259 260 len = read(fd, buf, sizeof(buf)); 261 close(fd); 262 if(len < 0) { 263 int save_errno = errno; 264 krb5_set_error_message(context, save_errno, "error reading %s: %s", 265 filename, strerror(save_errno)); 266 return save_errno; 267 } 268 if(len != 8) { 269 krb5_set_error_message(context, HEIM_ERR_EOF, 270 "bad contents of %s", filename); 271 return HEIM_ERR_EOF; /* XXX file might be too large */ 272 } 273 274 memset(&key, 0, sizeof(key)); 275 key.keytype = ETYPE_DES_PCBC_NONE; 276 ret = krb5_data_copy(&key.keyvalue, buf, len); 277 memset(buf, 0, sizeof(buf)); 278 if(ret) 279 return ret; 280 281 ret = hdb_process_master_key(context, 0, &key, 0, mkey); 282 krb5_free_keyblock_contents(context, &key); 283 return ret; 284 } 285 286 krb5_error_code 287 hdb_read_master_key(krb5_context context, const char *filename, 288 hdb_master_key *mkey) 289 { 290 FILE *f; 291 unsigned char buf[16]; 292 krb5_error_code ret; 293 294 off_t len; 295 296 *mkey = NULL; 297 298 if(filename == NULL) 299 filename = HDB_DB_DIR "/m-key"; 300 301 f = fopen(filename, "r"); 302 if(f == NULL) { 303 int save_errno = errno; 304 krb5_set_error_message(context, save_errno, "failed to open %s: %s", 305 filename, strerror(save_errno)); 306 return save_errno; 307 } 308 309 if(fread(buf, 1, 2, f) != 2) { 310 fclose(f); 311 krb5_set_error_message(context, HEIM_ERR_EOF, "end of file reading %s", filename); 312 return HEIM_ERR_EOF; 313 } 314 315 fseek(f, 0, SEEK_END); 316 len = ftell(f); 317 318 if(fclose(f) != 0) 319 return errno; 320 321 if(len < 0) 322 return errno; 323 324 if(len == 8) { 325 ret = read_master_krb4(context, filename, mkey); 326 } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) { 327 ret = read_master_encryptionkey(context, filename, mkey); 328 } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) { 329 ret = read_master_keytab(context, filename, mkey); 330 } else { 331 /* 332 * Check both LittleEndian and BigEndian since they key file 333 * might be moved from a machine with diffrent byte order, or 334 * its running on MacOS X that always uses BE master keys. 335 */ 336 ret = read_master_mit(context, filename, KRB5_STORAGE_BYTEORDER_LE, mkey); 337 if (ret) 338 ret = read_master_mit(context, filename, KRB5_STORAGE_BYTEORDER_BE, mkey); 339 } 340 return ret; 341 } 342 343 krb5_error_code 344 hdb_write_master_key(krb5_context context, const char *filename, 345 hdb_master_key mkey) 346 { 347 krb5_error_code ret; 348 hdb_master_key p; 349 krb5_keytab kt; 350 351 if(filename == NULL) 352 filename = HDB_DB_DIR "/m-key"; 353 354 ret = krb5_kt_resolve(context, filename, &kt); 355 if(ret) 356 return ret; 357 358 for(p = mkey; p; p = p->next) { 359 ret = krb5_kt_add_entry(context, kt, &p->keytab); 360 } 361 362 krb5_kt_close(context, kt); 363 364 return ret; 365 } 366 367 hdb_master_key 368 _hdb_find_master_key(uint32_t *mkvno, hdb_master_key mkey) 369 { 370 hdb_master_key ret = NULL; 371 while(mkey) { 372 if(ret == NULL && mkey->keytab.vno == 0) 373 ret = mkey; 374 if(mkvno == NULL) { 375 if(ret == NULL || mkey->keytab.vno > ret->keytab.vno) 376 ret = mkey; 377 } else if((uint32_t)mkey->keytab.vno == *mkvno) 378 return mkey; 379 mkey = mkey->next; 380 } 381 return ret; 382 } 383 384 int 385 _hdb_mkey_version(hdb_master_key mkey) 386 { 387 return mkey->keytab.vno; 388 } 389 390 int 391 _hdb_mkey_decrypt(krb5_context context, hdb_master_key key, 392 krb5_key_usage usage, 393 void *ptr, size_t size, krb5_data *res) 394 { 395 return krb5_decrypt(context, key->crypto, usage, 396 ptr, size, res); 397 } 398 399 int 400 _hdb_mkey_encrypt(krb5_context context, hdb_master_key key, 401 krb5_key_usage usage, 402 const void *ptr, size_t size, krb5_data *res) 403 { 404 return krb5_encrypt(context, key->crypto, usage, 405 ptr, size, res); 406 } 407 408 krb5_error_code 409 hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey) 410 { 411 412 krb5_error_code ret; 413 krb5_data res; 414 size_t keysize; 415 416 hdb_master_key key; 417 418 if(k->mkvno == NULL) 419 return 0; 420 421 key = _hdb_find_master_key(k->mkvno, mkey); 422 423 if (key == NULL) 424 return HDB_ERR_NO_MKEY; 425 426 ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY, 427 k->key.keyvalue.data, 428 k->key.keyvalue.length, 429 &res); 430 if(ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) { 431 /* try to decrypt with MIT key usage */ 432 ret = _hdb_mkey_decrypt(context, key, 0, 433 k->key.keyvalue.data, 434 k->key.keyvalue.length, 435 &res); 436 } 437 if (ret) 438 return ret; 439 440 /* fixup keylength if the key got padded when encrypting it */ 441 ret = krb5_enctype_keysize(context, k->key.keytype, &keysize); 442 if (ret) { 443 krb5_data_free(&res); 444 return ret; 445 } 446 if (keysize > res.length) { 447 krb5_data_free(&res); 448 return KRB5_BAD_KEYSIZE; 449 } 450 451 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length); 452 free(k->key.keyvalue.data); 453 k->key.keyvalue = res; 454 k->key.keyvalue.length = keysize; 455 free(k->mkvno); 456 k->mkvno = NULL; 457 458 return 0; 459 } 460 461 krb5_error_code 462 hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey) 463 { 464 size_t i; 465 466 for(i = 0; i < ent->keys.len; i++){ 467 krb5_error_code ret; 468 469 ret = hdb_unseal_key_mkey(context, &ent->keys.val[i], mkey); 470 if (ret) 471 return ret; 472 } 473 return 0; 474 } 475 476 krb5_error_code 477 hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent) 478 { 479 if (db->hdb_master_key_set == 0) 480 return 0; 481 return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key); 482 } 483 484 krb5_error_code 485 hdb_unseal_key(krb5_context context, HDB *db, Key *k) 486 { 487 if (db->hdb_master_key_set == 0) 488 return 0; 489 return hdb_unseal_key_mkey(context, k, db->hdb_master_key); 490 } 491 492 krb5_error_code 493 hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey) 494 { 495 krb5_error_code ret; 496 krb5_data res; 497 hdb_master_key key; 498 499 if(k->mkvno != NULL) 500 return 0; 501 502 key = _hdb_find_master_key(k->mkvno, mkey); 503 504 if (key == NULL) 505 return HDB_ERR_NO_MKEY; 506 507 ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY, 508 k->key.keyvalue.data, 509 k->key.keyvalue.length, 510 &res); 511 if (ret) 512 return ret; 513 514 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length); 515 free(k->key.keyvalue.data); 516 k->key.keyvalue = res; 517 518 if (k->mkvno == NULL) { 519 k->mkvno = malloc(sizeof(*k->mkvno)); 520 if (k->mkvno == NULL) 521 return ENOMEM; 522 } 523 *k->mkvno = key->keytab.vno; 524 525 return 0; 526 } 527 528 krb5_error_code 529 hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey) 530 { 531 size_t i; 532 for(i = 0; i < ent->keys.len; i++){ 533 krb5_error_code ret; 534 535 ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey); 536 if (ret) 537 return ret; 538 } 539 return 0; 540 } 541 542 krb5_error_code 543 hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent) 544 { 545 if (db->hdb_master_key_set == 0) 546 return 0; 547 548 return hdb_seal_keys_mkey(context, ent, db->hdb_master_key); 549 } 550 551 krb5_error_code 552 hdb_seal_key(krb5_context context, HDB *db, Key *k) 553 { 554 if (db->hdb_master_key_set == 0) 555 return 0; 556 557 return hdb_seal_key_mkey(context, k, db->hdb_master_key); 558 } 559 560 krb5_error_code 561 hdb_set_master_key (krb5_context context, 562 HDB *db, 563 krb5_keyblock *key) 564 { 565 krb5_error_code ret; 566 hdb_master_key mkey; 567 568 ret = hdb_process_master_key(context, 0, key, 0, &mkey); 569 if (ret) 570 return ret; 571 db->hdb_master_key = mkey; 572 #if 0 /* XXX - why? */ 573 des_set_random_generator_seed(key.keyvalue.data); 574 #endif 575 db->hdb_master_key_set = 1; 576 return 0; 577 } 578 579 krb5_error_code 580 hdb_set_master_keyfile (krb5_context context, 581 HDB *db, 582 const char *keyfile) 583 { 584 hdb_master_key key; 585 krb5_error_code ret; 586 587 ret = hdb_read_master_key(context, keyfile, &key); 588 if (ret) { 589 if (ret != ENOENT) 590 return ret; 591 krb5_clear_error_message(context); 592 return 0; 593 } 594 db->hdb_master_key = key; 595 db->hdb_master_key_set = 1; 596 return ret; 597 } 598 599 krb5_error_code 600 hdb_clear_master_key (krb5_context context, 601 HDB *db) 602 { 603 if (db->hdb_master_key_set) { 604 hdb_free_master_key(context, db->hdb_master_key); 605 db->hdb_master_key_set = 0; 606 } 607 return 0; 608 } 609