1 /* 2 * Copyright (c) 2000 - 2001 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.8 2001/01/30 01:20:57 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 krb5_crypto_destroy(context, mkey->crypto); 54 ptr = mkey; 55 mkey = mkey->next; 56 free(ptr); 57 } 58 } 59 60 krb5_error_code 61 hdb_process_master_key(krb5_context context, 62 int kvno, krb5_keyblock *key, krb5_enctype etype, 63 hdb_master_key *mkey) 64 { 65 krb5_error_code ret; 66 *mkey = calloc(1, sizeof(**mkey)); 67 if(*mkey == NULL) 68 return ENOMEM; 69 (*mkey)->keytab.vno = kvno; 70 ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal); 71 ret = krb5_copy_keyblock_contents(context, key, &(*mkey)->keytab.keyblock); 72 if(ret) { 73 free(*mkey); 74 *mkey = NULL; 75 return ret; 76 } 77 if(etype != 0) 78 (*mkey)->keytab.keyblock.keytype = etype; 79 (*mkey)->keytab.timestamp = time(NULL); 80 ret = krb5_crypto_init(context, key, etype, &(*mkey)->crypto); 81 if(ret) { 82 krb5_free_keyblock_contents(context, &(*mkey)->keytab.keyblock); 83 free(*mkey); 84 *mkey = NULL; 85 } 86 return ret; 87 } 88 89 krb5_error_code 90 hdb_add_master_key(krb5_context context, krb5_keyblock *key, 91 hdb_master_key *inout) 92 { 93 int vno = 0; 94 hdb_master_key p; 95 krb5_error_code ret; 96 97 for(p = *inout; p; p = p->next) 98 vno = max(vno, p->keytab.vno); 99 vno++; 100 ret = hdb_process_master_key(context, vno, key, 0, &p); 101 if(ret) 102 return ret; 103 p->next = *inout; 104 *inout = p; 105 return 0; 106 } 107 108 static krb5_error_code 109 read_master_keytab(krb5_context context, const char *filename, 110 hdb_master_key *mkey) 111 { 112 krb5_error_code ret; 113 krb5_keytab id; 114 krb5_kt_cursor cursor; 115 krb5_keytab_entry entry; 116 hdb_master_key p; 117 118 ret = krb5_kt_resolve(context, filename, &id); 119 if(ret) 120 return ret; 121 122 ret = krb5_kt_start_seq_get(context, id, &cursor); 123 if(ret) 124 goto out; 125 *mkey = NULL; 126 while(krb5_kt_next_entry(context, id, &entry, &cursor) == 0) { 127 p = calloc(1, sizeof(*p)); 128 p->keytab = entry; 129 ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto); 130 p->next = *mkey; 131 *mkey = p; 132 } 133 krb5_kt_end_seq_get(context, id, &cursor); 134 out: 135 krb5_kt_close(context, id); 136 return ret; 137 } 138 139 /* read a MIT master keyfile */ 140 static krb5_error_code 141 read_master_mit(krb5_context context, const char *filename, 142 hdb_master_key *mkey) 143 { 144 int fd; 145 krb5_error_code ret; 146 krb5_storage *sp; 147 u_int16_t enctype; 148 krb5_keyblock key; 149 150 fd = open(filename, O_RDONLY | O_BINARY); 151 if(fd < 0) 152 return errno; 153 sp = krb5_storage_from_fd(fd); 154 if(sp == NULL) { 155 close(fd); 156 return errno; 157 } 158 krb5_storage_set_flags(sp, KRB5_STORAGE_HOST_BYTEORDER); 159 #if 0 160 /* could possibly use ret_keyblock here, but do it with more 161 checks for now */ 162 ret = krb5_ret_keyblock(sp, &key); 163 #else 164 ret = krb5_ret_int16(sp, &enctype); 165 if((htons(enctype) & 0xff00) == 0x3000) { 166 ret = HEIM_ERR_BAD_MKEY; 167 goto out; 168 } 169 key.keytype = enctype; 170 ret = krb5_ret_data(sp, &key.keyvalue); 171 if(ret) 172 goto out; 173 #endif 174 ret = hdb_process_master_key(context, 0, &key, 0, mkey); 175 krb5_free_keyblock_contents(context, &key); 176 out: 177 krb5_storage_free(sp); 178 close(fd); 179 return ret; 180 } 181 182 /* read an old master key file */ 183 static krb5_error_code 184 read_master_encryptionkey(krb5_context context, const char *filename, 185 hdb_master_key *mkey) 186 { 187 int fd; 188 krb5_keyblock key; 189 krb5_error_code ret; 190 unsigned char buf[256]; 191 ssize_t len; 192 193 fd = open(filename, O_RDONLY | O_BINARY); 194 if(fd < 0) 195 return errno; 196 197 len = read(fd, buf, sizeof(buf)); 198 close(fd); 199 if(len < 0) 200 return errno; 201 202 ret = decode_EncryptionKey(buf, len, &key, &len); 203 memset(buf, 0, sizeof(buf)); 204 if(ret) 205 return ret; 206 207 /* Originally, the keytype was just that, and later it got changed 208 to des-cbc-md5, but we always used des in cfb64 mode. This 209 should cover all cases, but will break if someone has hacked 210 this code to really use des-cbc-md5 -- but then that's not my 211 problem. */ 212 if(key.keytype == KEYTYPE_DES || key.keytype == ETYPE_DES_CBC_MD5) 213 key.keytype = ETYPE_DES_CFB64_NONE; 214 215 ret = hdb_process_master_key(context, 0, &key, 0, mkey); 216 krb5_free_keyblock_contents(context, &key); 217 return ret; 218 } 219 220 /* read a krb4 /.k style file */ 221 static krb5_error_code 222 read_master_krb4(krb5_context context, const char *filename, 223 hdb_master_key *mkey) 224 { 225 int fd; 226 krb5_keyblock key; 227 krb5_error_code ret; 228 unsigned char buf[256]; 229 ssize_t len; 230 231 fd = open(filename, O_RDONLY | O_BINARY); 232 if(fd < 0) 233 return errno; 234 235 len = read(fd, buf, sizeof(buf)); 236 close(fd); 237 if(len < 0) 238 return errno; 239 240 memset(&key, 0, sizeof(key)); 241 key.keytype = ETYPE_DES_PCBC_NONE; 242 ret = krb5_data_copy(&key.keyvalue, buf, len); 243 memset(buf, 0, sizeof(buf)); 244 if(ret) 245 return ret; 246 247 ret = hdb_process_master_key(context, 0, &key, 0, mkey); 248 krb5_free_keyblock_contents(context, &key); 249 return ret; 250 } 251 252 krb5_error_code 253 hdb_read_master_key(krb5_context context, const char *filename, 254 hdb_master_key *mkey) 255 { 256 FILE *f; 257 unsigned char buf[16]; 258 krb5_error_code ret; 259 260 off_t len; 261 262 *mkey = NULL; 263 264 if(filename == NULL) 265 filename = HDB_DB_DIR "/m-key"; 266 267 f = fopen(filename, "r"); 268 if(f == NULL) 269 return errno; 270 271 if(fread(buf, 1, 2, f) != 2) { 272 fclose(f); 273 return HEIM_ERR_EOF; 274 } 275 276 fseek(f, 0, SEEK_END); 277 len = ftell(f); 278 279 if(fclose(f) != 0) 280 return errno; 281 282 if(len < 0) 283 return errno; 284 285 if(len == 8) { 286 ret = read_master_krb4(context, filename, mkey); 287 } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) { 288 ret = read_master_encryptionkey(context, filename, mkey); 289 } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) { 290 ret = read_master_keytab(context, filename, mkey); 291 } else { 292 ret = read_master_mit(context, filename, mkey); 293 } 294 return ret; 295 } 296 297 krb5_error_code 298 hdb_write_master_key(krb5_context context, const char *filename, 299 hdb_master_key mkey) 300 { 301 krb5_error_code ret; 302 hdb_master_key p; 303 krb5_keytab kt; 304 305 if(filename == NULL) 306 filename = HDB_DB_DIR "/m-key"; 307 308 ret = krb5_kt_resolve(context, filename, &kt); 309 if(ret) 310 return ret; 311 312 for(p = mkey; p; p = p->next) { 313 ret = krb5_kt_add_entry(context, kt, &p->keytab); 314 } 315 316 krb5_kt_close(context, kt); 317 318 return ret; 319 } 320 321 static hdb_master_key 322 find_master_key(Key *key, hdb_master_key mkey) 323 { 324 hdb_master_key ret = NULL; 325 while(mkey) { 326 if(ret == NULL && mkey->keytab.vno == 0) 327 ret = mkey; 328 if(key->mkvno == NULL) { 329 if(ret == NULL || mkey->keytab.vno > ret->keytab.vno) 330 ret = mkey; 331 } else if(mkey->keytab.vno == *key->mkvno) 332 return mkey; 333 mkey = mkey->next; 334 } 335 return ret; 336 } 337 338 krb5_error_code 339 hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey) 340 { 341 int i; 342 krb5_error_code ret; 343 krb5_data res; 344 Key *k; 345 346 for(i = 0; i < ent->keys.len; i++){ 347 hdb_master_key key; 348 349 k = &ent->keys.val[i]; 350 if(k->mkvno == NULL) 351 continue; 352 353 key = find_master_key(&ent->keys.val[i], mkey); 354 355 if (key == NULL) 356 return HDB_ERR_NO_MKEY; 357 358 ret = krb5_decrypt(context, key->crypto, HDB_KU_MKEY, 359 k->key.keyvalue.data, 360 k->key.keyvalue.length, 361 &res); 362 if (ret) 363 return ret; 364 365 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length); 366 free(k->key.keyvalue.data); 367 k->key.keyvalue = res; 368 free(k->mkvno); 369 k->mkvno = NULL; 370 } 371 return 0; 372 } 373 374 krb5_error_code 375 hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent) 376 { 377 if (db->master_key_set == 0) 378 return 0; 379 return hdb_unseal_keys_mkey(context, ent, db->master_key); 380 } 381 382 krb5_error_code 383 hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey) 384 { 385 int i; 386 krb5_error_code ret; 387 krb5_data res; 388 for(i = 0; i < ent->keys.len; i++){ 389 Key *k = &ent->keys.val[i]; 390 hdb_master_key key; 391 392 if(k->mkvno != NULL) 393 continue; 394 395 key = find_master_key(k, mkey); 396 397 if (key == NULL) 398 return HDB_ERR_NO_MKEY; 399 400 ret = krb5_encrypt(context, key->crypto, HDB_KU_MKEY, 401 k->key.keyvalue.data, 402 k->key.keyvalue.length, 403 &res); 404 if (ret) 405 return ret; 406 407 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length); 408 free(k->key.keyvalue.data); 409 k->key.keyvalue = res; 410 411 k->mkvno = malloc(sizeof(*k->mkvno)); 412 if (k->mkvno == NULL) 413 return ENOMEM; 414 *k->mkvno = key->keytab.vno; 415 } 416 return 0; 417 } 418 419 krb5_error_code 420 hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent) 421 { 422 if (db->master_key_set == 0) 423 return 0; 424 425 return hdb_seal_keys_mkey(context, ent, db->master_key); 426 } 427 428 krb5_error_code 429 hdb_set_master_key (krb5_context context, 430 HDB *db, 431 krb5_keyblock *key) 432 { 433 krb5_error_code ret; 434 hdb_master_key mkey; 435 436 ret = hdb_process_master_key(context, 0, key, 0, &mkey); 437 if (ret) 438 return ret; 439 db->master_key = mkey; 440 #if 0 /* XXX - why? */ 441 des_set_random_generator_seed(key.keyvalue.data); 442 #endif 443 db->master_key_set = 1; 444 return 0; 445 } 446 447 krb5_error_code 448 hdb_set_master_keyfile (krb5_context context, 449 HDB *db, 450 const char *keyfile) 451 { 452 hdb_master_key key; 453 krb5_error_code ret; 454 455 ret = hdb_read_master_key(context, keyfile, &key); 456 if (ret) { 457 if (ret != ENOENT) 458 return ret; 459 return 0; 460 } 461 db->master_key = key; 462 db->master_key_set = 1; 463 return ret; 464 } 465 466 krb5_error_code 467 hdb_clear_master_key (krb5_context context, 468 HDB *db) 469 { 470 if (db->master_key_set) { 471 hdb_free_master_key(context, db->master_key); 472 db->master_key_set = 0; 473 } 474 return 0; 475 } 476