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.14 2002/08/16 18:59:49 assar 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 Key *k; 381 382 for(i = 0; i < ent->keys.len; i++){ 383 hdb_master_key key; 384 385 k = &ent->keys.val[i]; 386 if(k->mkvno == NULL) 387 continue; 388 389 key = find_master_key(&ent->keys.val[i], mkey); 390 391 if (key == NULL) 392 return HDB_ERR_NO_MKEY; 393 394 ret = krb5_decrypt(context, key->crypto, HDB_KU_MKEY, 395 k->key.keyvalue.data, 396 k->key.keyvalue.length, 397 &res); 398 if (ret) 399 return ret; 400 401 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length); 402 free(k->key.keyvalue.data); 403 k->key.keyvalue = res; 404 free(k->mkvno); 405 k->mkvno = NULL; 406 } 407 return 0; 408 } 409 410 krb5_error_code 411 hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent) 412 { 413 if (db->master_key_set == 0) 414 return 0; 415 return hdb_unseal_keys_mkey(context, ent, db->master_key); 416 } 417 418 krb5_error_code 419 hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey) 420 { 421 int i; 422 krb5_error_code ret; 423 krb5_data res; 424 for(i = 0; i < ent->keys.len; i++){ 425 Key *k = &ent->keys.val[i]; 426 hdb_master_key key; 427 428 if(k->mkvno != NULL) 429 continue; 430 431 key = find_master_key(k, mkey); 432 433 if (key == NULL) 434 return HDB_ERR_NO_MKEY; 435 436 ret = krb5_encrypt(context, key->crypto, HDB_KU_MKEY, 437 k->key.keyvalue.data, 438 k->key.keyvalue.length, 439 &res); 440 if (ret) 441 return ret; 442 443 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length); 444 free(k->key.keyvalue.data); 445 k->key.keyvalue = res; 446 447 k->mkvno = malloc(sizeof(*k->mkvno)); 448 if (k->mkvno == NULL) 449 return ENOMEM; 450 *k->mkvno = key->keytab.vno; 451 } 452 return 0; 453 } 454 455 krb5_error_code 456 hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent) 457 { 458 if (db->master_key_set == 0) 459 return 0; 460 461 return hdb_seal_keys_mkey(context, ent, db->master_key); 462 } 463 464 krb5_error_code 465 hdb_set_master_key (krb5_context context, 466 HDB *db, 467 krb5_keyblock *key) 468 { 469 krb5_error_code ret; 470 hdb_master_key mkey; 471 472 ret = hdb_process_master_key(context, 0, key, 0, &mkey); 473 if (ret) 474 return ret; 475 db->master_key = mkey; 476 #if 0 /* XXX - why? */ 477 des_set_random_generator_seed(key.keyvalue.data); 478 #endif 479 db->master_key_set = 1; 480 return 0; 481 } 482 483 krb5_error_code 484 hdb_set_master_keyfile (krb5_context context, 485 HDB *db, 486 const char *keyfile) 487 { 488 hdb_master_key key; 489 krb5_error_code ret; 490 491 ret = hdb_read_master_key(context, keyfile, &key); 492 if (ret) { 493 if (ret != ENOENT) 494 return ret; 495 krb5_clear_error_string(context); 496 return 0; 497 } 498 db->master_key = key; 499 db->master_key_set = 1; 500 return ret; 501 } 502 503 krb5_error_code 504 hdb_clear_master_key (krb5_context context, 505 HDB *db) 506 { 507 if (db->master_key_set) { 508 hdb_free_master_key(context, db->master_key); 509 db->master_key_set = 0; 510 } 511 return 0; 512 } 513