1 /* 2 * Copyright (c) 2000 - 2002 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "hdb_locl.h" 35 #ifndef O_BINARY 36 #define O_BINARY 0 37 #endif 38 39 RCSID("$Id: mkey.c,v 1.15 2003/03/28 02:01:33 lha Exp $"); 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_string(context, "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 p->keytab = entry; 133 ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto); 134 p->next = *mkey; 135 *mkey = p; 136 } 137 krb5_kt_end_seq_get(context, id, &cursor); 138 out: 139 krb5_kt_close(context, id); 140 return ret; 141 } 142 143 /* read a MIT master keyfile */ 144 static krb5_error_code 145 read_master_mit(krb5_context context, const char *filename, 146 hdb_master_key *mkey) 147 { 148 int fd; 149 krb5_error_code ret; 150 krb5_storage *sp; 151 u_int16_t enctype; 152 krb5_keyblock key; 153 154 fd = open(filename, O_RDONLY | O_BINARY); 155 if(fd < 0) { 156 int save_errno = errno; 157 krb5_set_error_string(context, "failed to open %s: %s", filename, 158 strerror(save_errno)); 159 return save_errno; 160 } 161 sp = krb5_storage_from_fd(fd); 162 if(sp == NULL) { 163 close(fd); 164 return errno; 165 } 166 krb5_storage_set_flags(sp, KRB5_STORAGE_HOST_BYTEORDER); 167 #if 0 168 /* could possibly use ret_keyblock here, but do it with more 169 checks for now */ 170 ret = krb5_ret_keyblock(sp, &key); 171 #else 172 ret = krb5_ret_int16(sp, &enctype); 173 if((htons(enctype) & 0xff00) == 0x3000) { 174 krb5_set_error_string(context, "unknown keytype in %s: %#x, expected %#x", 175 filename, htons(enctype), 0x3000); 176 ret = HEIM_ERR_BAD_MKEY; 177 goto out; 178 } 179 key.keytype = enctype; 180 ret = krb5_ret_data(sp, &key.keyvalue); 181 if(ret) 182 goto out; 183 #endif 184 ret = hdb_process_master_key(context, 0, &key, 0, mkey); 185 krb5_free_keyblock_contents(context, &key); 186 out: 187 krb5_storage_free(sp); 188 close(fd); 189 return ret; 190 } 191 192 /* read an old master key file */ 193 static krb5_error_code 194 read_master_encryptionkey(krb5_context context, const char *filename, 195 hdb_master_key *mkey) 196 { 197 int fd; 198 krb5_keyblock key; 199 krb5_error_code ret; 200 unsigned char buf[256]; 201 ssize_t len; 202 size_t ret_len; 203 204 fd = open(filename, O_RDONLY | O_BINARY); 205 if(fd < 0) { 206 int save_errno = errno; 207 krb5_set_error_string(context, "failed to open %s: %s", 208 filename, strerror(save_errno)); 209 return save_errno; 210 } 211 212 len = read(fd, buf, sizeof(buf)); 213 close(fd); 214 if(len < 0) { 215 int save_errno = errno; 216 krb5_set_error_string(context, "error reading %s: %s", 217 filename, strerror(save_errno)); 218 return save_errno; 219 } 220 221 ret = decode_EncryptionKey(buf, len, &key, &ret_len); 222 memset(buf, 0, sizeof(buf)); 223 if(ret) 224 return ret; 225 226 /* Originally, the keytype was just that, and later it got changed 227 to des-cbc-md5, but we always used des in cfb64 mode. This 228 should cover all cases, but will break if someone has hacked 229 this code to really use des-cbc-md5 -- but then that's not my 230 problem. */ 231 if(key.keytype == KEYTYPE_DES || key.keytype == ETYPE_DES_CBC_MD5) 232 key.keytype = ETYPE_DES_CFB64_NONE; 233 234 ret = hdb_process_master_key(context, 0, &key, 0, mkey); 235 krb5_free_keyblock_contents(context, &key); 236 return ret; 237 } 238 239 /* read a krb4 /.k style file */ 240 static krb5_error_code 241 read_master_krb4(krb5_context context, const char *filename, 242 hdb_master_key *mkey) 243 { 244 int fd; 245 krb5_keyblock key; 246 krb5_error_code ret; 247 unsigned char buf[256]; 248 ssize_t len; 249 250 fd = open(filename, O_RDONLY | O_BINARY); 251 if(fd < 0) { 252 int save_errno = errno; 253 krb5_set_error_string(context, "failed to open %s: %s", 254 filename, strerror(save_errno)); 255 return save_errno; 256 } 257 258 len = read(fd, buf, sizeof(buf)); 259 close(fd); 260 if(len < 0) { 261 int save_errno = errno; 262 krb5_set_error_string(context, "error reading %s: %s", 263 filename, strerror(save_errno)); 264 return save_errno; 265 } 266 if(len != 8) { 267 krb5_set_error_string(context, "bad contents of %s", filename); 268 return HEIM_ERR_EOF; /* XXX file might be too large */ 269 } 270 271 memset(&key, 0, sizeof(key)); 272 key.keytype = ETYPE_DES_PCBC_NONE; 273 ret = krb5_data_copy(&key.keyvalue, buf, len); 274 memset(buf, 0, sizeof(buf)); 275 if(ret) 276 return ret; 277 278 ret = hdb_process_master_key(context, 0, &key, 0, mkey); 279 krb5_free_keyblock_contents(context, &key); 280 return ret; 281 } 282 283 krb5_error_code 284 hdb_read_master_key(krb5_context context, const char *filename, 285 hdb_master_key *mkey) 286 { 287 FILE *f; 288 unsigned char buf[16]; 289 krb5_error_code ret; 290 291 off_t len; 292 293 *mkey = NULL; 294 295 if(filename == NULL) 296 filename = HDB_DB_DIR "/m-key"; 297 298 f = fopen(filename, "r"); 299 if(f == NULL) { 300 int save_errno = errno; 301 krb5_set_error_string(context, "failed to open %s: %s", 302 filename, strerror(save_errno)); 303 return save_errno; 304 } 305 306 if(fread(buf, 1, 2, f) != 2) { 307 krb5_set_error_string(context, "end of file reading %s", filename); 308 fclose(f); 309 return HEIM_ERR_EOF; 310 } 311 312 fseek(f, 0, SEEK_END); 313 len = ftell(f); 314 315 if(fclose(f) != 0) 316 return errno; 317 318 if(len < 0) 319 return errno; 320 321 if(len == 8) { 322 ret = read_master_krb4(context, filename, mkey); 323 } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) { 324 ret = read_master_encryptionkey(context, filename, mkey); 325 } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) { 326 ret = read_master_keytab(context, filename, mkey); 327 } else { 328 ret = read_master_mit(context, filename, mkey); 329 } 330 return ret; 331 } 332 333 krb5_error_code 334 hdb_write_master_key(krb5_context context, const char *filename, 335 hdb_master_key mkey) 336 { 337 krb5_error_code ret; 338 hdb_master_key p; 339 krb5_keytab kt; 340 341 if(filename == NULL) 342 filename = HDB_DB_DIR "/m-key"; 343 344 ret = krb5_kt_resolve(context, filename, &kt); 345 if(ret) 346 return ret; 347 348 for(p = mkey; p; p = p->next) { 349 ret = krb5_kt_add_entry(context, kt, &p->keytab); 350 } 351 352 krb5_kt_close(context, kt); 353 354 return ret; 355 } 356 357 static hdb_master_key 358 find_master_key(Key *key, hdb_master_key mkey) 359 { 360 hdb_master_key ret = NULL; 361 while(mkey) { 362 if(ret == NULL && mkey->keytab.vno == 0) 363 ret = mkey; 364 if(key->mkvno == NULL) { 365 if(ret == NULL || mkey->keytab.vno > ret->keytab.vno) 366 ret = mkey; 367 } else if(mkey->keytab.vno == *key->mkvno) 368 return mkey; 369 mkey = mkey->next; 370 } 371 return ret; 372 } 373 374 krb5_error_code 375 hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey) 376 { 377 int i; 378 krb5_error_code ret; 379 krb5_data res; 380 size_t keysize; 381 Key *k; 382 383 for(i = 0; i < ent->keys.len; i++){ 384 hdb_master_key key; 385 386 k = &ent->keys.val[i]; 387 if(k->mkvno == NULL) 388 continue; 389 390 key = find_master_key(&ent->keys.val[i], mkey); 391 392 if (key == NULL) 393 return HDB_ERR_NO_MKEY; 394 395 ret = krb5_decrypt(context, key->crypto, HDB_KU_MKEY, 396 k->key.keyvalue.data, 397 k->key.keyvalue.length, 398 &res); 399 if (ret) 400 return ret; 401 402 /* fixup keylength if the key got padded when encrypting it */ 403 ret = krb5_enctype_keysize(context, k->key.keytype, &keysize); 404 if (ret) { 405 krb5_data_free(&res); 406 return ret; 407 } 408 if (keysize > res.length) { 409 krb5_data_free(&res); 410 return KRB5_BAD_KEYSIZE; 411 } 412 413 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length); 414 free(k->key.keyvalue.data); 415 k->key.keyvalue = res; 416 k->key.keyvalue.length = keysize; 417 free(k->mkvno); 418 k->mkvno = NULL; 419 } 420 return 0; 421 } 422 423 krb5_error_code 424 hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent) 425 { 426 if (db->master_key_set == 0) 427 return 0; 428 return hdb_unseal_keys_mkey(context, ent, db->master_key); 429 } 430 431 krb5_error_code 432 hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey) 433 { 434 int i; 435 krb5_error_code ret; 436 krb5_data res; 437 for(i = 0; i < ent->keys.len; i++){ 438 Key *k = &ent->keys.val[i]; 439 hdb_master_key key; 440 441 if(k->mkvno != NULL) 442 continue; 443 444 key = find_master_key(k, mkey); 445 446 if (key == NULL) 447 return HDB_ERR_NO_MKEY; 448 449 ret = krb5_encrypt(context, key->crypto, HDB_KU_MKEY, 450 k->key.keyvalue.data, 451 k->key.keyvalue.length, 452 &res); 453 if (ret) 454 return ret; 455 456 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length); 457 free(k->key.keyvalue.data); 458 k->key.keyvalue = res; 459 460 k->mkvno = malloc(sizeof(*k->mkvno)); 461 if (k->mkvno == NULL) 462 return ENOMEM; 463 *k->mkvno = key->keytab.vno; 464 } 465 return 0; 466 } 467 468 krb5_error_code 469 hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent) 470 { 471 if (db->master_key_set == 0) 472 return 0; 473 474 return hdb_seal_keys_mkey(context, ent, db->master_key); 475 } 476 477 krb5_error_code 478 hdb_set_master_key (krb5_context context, 479 HDB *db, 480 krb5_keyblock *key) 481 { 482 krb5_error_code ret; 483 hdb_master_key mkey; 484 485 ret = hdb_process_master_key(context, 0, key, 0, &mkey); 486 if (ret) 487 return ret; 488 db->master_key = mkey; 489 #if 0 /* XXX - why? */ 490 des_set_random_generator_seed(key.keyvalue.data); 491 #endif 492 db->master_key_set = 1; 493 return 0; 494 } 495 496 krb5_error_code 497 hdb_set_master_keyfile (krb5_context context, 498 HDB *db, 499 const char *keyfile) 500 { 501 hdb_master_key key; 502 krb5_error_code ret; 503 504 ret = hdb_read_master_key(context, keyfile, &key); 505 if (ret) { 506 if (ret != ENOENT) 507 return ret; 508 krb5_clear_error_string(context); 509 return 0; 510 } 511 db->master_key = key; 512 db->master_key_set = 1; 513 return ret; 514 } 515 516 krb5_error_code 517 hdb_clear_master_key (krb5_context context, 518 HDB *db) 519 { 520 if (db->master_key_set) { 521 hdb_free_master_key(context, db->master_key); 522 db->master_key_set = 0; 523 } 524 return 0; 525 } 526