root/source4/heimdal/lib/hdb/mkey.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hdb_free_master_key
  2. hdb_process_master_key
  3. hdb_add_master_key
  4. read_master_keytab
  5. read_master_mit
  6. read_master_encryptionkey
  7. read_master_krb4
  8. hdb_read_master_key
  9. hdb_write_master_key
  10. _hdb_find_master_key
  11. _hdb_mkey_version
  12. _hdb_mkey_decrypt
  13. _hdb_mkey_encrypt
  14. hdb_unseal_key_mkey
  15. hdb_unseal_keys_mkey
  16. hdb_unseal_keys
  17. hdb_unseal_key
  18. hdb_seal_key_mkey
  19. hdb_seal_keys_mkey
  20. hdb_seal_keys
  21. hdb_seal_key
  22. hdb_set_master_key
  23. hdb_set_master_keyfile
  24. hdb_clear_master_key

   1 /*
   2  * Copyright (c) 2000 - 2004 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$");
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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,
     /* [<][>][^][v][top][bottom][index][help] */
  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,
     /* [<][>][^][v][top][bottom][index][help] */
  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,
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 151                 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, KRB5_STORAGE_HOST_BYTEORDER);
 172 #if 0
 173     /* could possibly use ret_keyblock here, but do it with more
 174        checks for now */
 175     ret = krb5_ret_keyblock(sp, &key);
 176 #else
 177     ret = krb5_ret_int16(sp, &enctype);
 178     if((htons(enctype) & 0xff00) == 0x3000) {
 179         ret = HEIM_ERR_BAD_MKEY;
 180         krb5_set_error_message(context, ret, "unknown keytype in %s: %#x, expected %#x",
 181                                filename, htons(enctype), 0x3000);
 182         goto out;
 183     }
 184     key.keytype = enctype;
 185     ret = krb5_ret_data(sp, &key.keyvalue);
 186     if(ret)
 187         goto out;
 188 #endif
 189     ret = hdb_process_master_key(context, 0, &key, 0, mkey);
 190     krb5_free_keyblock_contents(context, &key);
 191   out:
 192     krb5_storage_free(sp);
 193     close(fd);
 194     return ret;
 195 }
 196 
 197 /* read an old master key file */
 198 static krb5_error_code
 199 read_master_encryptionkey(krb5_context context, const char *filename,
     /* [<][>][^][v][top][bottom][index][help] */
 200                           hdb_master_key *mkey)
 201 {
 202     int fd;
 203     krb5_keyblock key;
 204     krb5_error_code ret;
 205     unsigned char buf[256];
 206     ssize_t len;
 207     size_t ret_len;
 208         
 209     fd = open(filename, O_RDONLY | O_BINARY);
 210     if(fd < 0) {
 211         int save_errno = errno;
 212         krb5_set_error_message(context, save_errno, "failed to open %s: %s",
 213                               filename, strerror(save_errno));
 214         return save_errno;
 215     }
 216 
 217     len = read(fd, buf, sizeof(buf));
 218     close(fd);
 219     if(len < 0) {
 220         int save_errno = errno;
 221         krb5_set_error_message(context, save_errno, "error reading %s: %s",
 222                               filename, strerror(save_errno));
 223         return save_errno;
 224     }
 225 
 226     ret = decode_EncryptionKey(buf, len, &key, &ret_len);
 227     memset(buf, 0, sizeof(buf));
 228     if(ret)
 229         return ret;
 230 
 231     /* Originally, the keytype was just that, and later it got changed
 232        to des-cbc-md5, but we always used des in cfb64 mode. This
 233        should cover all cases, but will break if someone has hacked
 234        this code to really use des-cbc-md5 -- but then that's not my
 235        problem. */
 236     if(key.keytype == KEYTYPE_DES || key.keytype == ETYPE_DES_CBC_MD5)
 237         key.keytype = ETYPE_DES_CFB64_NONE;
 238 
 239     ret = hdb_process_master_key(context, 0, &key, 0, mkey);
 240     krb5_free_keyblock_contents(context, &key);
 241     return ret;
 242 }
 243 
 244 /* read a krb4 /.k style file */
 245 static krb5_error_code
 246 read_master_krb4(krb5_context context, const char *filename,
     /* [<][>][^][v][top][bottom][index][help] */
 247                  hdb_master_key *mkey)
 248 {
 249     int fd;
 250     krb5_keyblock key;
 251     krb5_error_code ret;
 252     unsigned char buf[256];
 253     ssize_t len;
 254         
 255     fd = open(filename, O_RDONLY | O_BINARY);
 256     if(fd < 0) {
 257         int save_errno = errno;
 258         krb5_set_error_message(context, save_errno, "failed to open %s: %s",
 259                                filename, strerror(save_errno));
 260         return save_errno;
 261     }
 262 
 263     len = read(fd, buf, sizeof(buf));
 264     close(fd);
 265     if(len < 0) {
 266         int save_errno = errno;
 267         krb5_set_error_message(context, save_errno, "error reading %s: %s",
 268                                filename, strerror(save_errno));
 269         return save_errno;
 270     }
 271     if(len != 8) {
 272         krb5_set_error_message(context, HEIM_ERR_EOF,
 273                                "bad contents of %s", filename);
 274         return HEIM_ERR_EOF; /* XXX file might be too large */
 275     }
 276 
 277     memset(&key, 0, sizeof(key));
 278     key.keytype = ETYPE_DES_PCBC_NONE;
 279     ret = krb5_data_copy(&key.keyvalue, buf, len);
 280     memset(buf, 0, sizeof(buf));
 281     if(ret)
 282         return ret;
 283 
 284     ret = hdb_process_master_key(context, 0, &key, 0, mkey);
 285     krb5_free_keyblock_contents(context, &key);
 286     return ret;
 287 }
 288 
 289 krb5_error_code
 290 hdb_read_master_key(krb5_context context, const char *filename,
     /* [<][>][^][v][top][bottom][index][help] */
 291                     hdb_master_key *mkey)
 292 {
 293     FILE *f;
 294     unsigned char buf[16];
 295     krb5_error_code ret;
 296 
 297     off_t len;
 298 
 299     *mkey = NULL;
 300 
 301     if(filename == NULL)
 302         filename = HDB_DB_DIR "/m-key";
 303 
 304     f = fopen(filename, "r");
 305     if(f == NULL) {
 306         int save_errno = errno;
 307         krb5_set_error_message(context, save_errno, "failed to open %s: %s",
 308                                filename, strerror(save_errno));
 309         return save_errno;
 310     }
 311 
 312     if(fread(buf, 1, 2, f) != 2) {
 313         fclose(f);
 314         krb5_set_error_message(context, HEIM_ERR_EOF, "end of file reading %s", filename);
 315         return HEIM_ERR_EOF;
 316     }
 317 
 318     fseek(f, 0, SEEK_END);
 319     len = ftell(f);
 320 
 321     if(fclose(f) != 0)
 322         return errno;
 323 
 324     if(len < 0)
 325         return errno;
 326 
 327     if(len == 8) {
 328         ret = read_master_krb4(context, filename, mkey);
 329     } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) {
 330         ret = read_master_encryptionkey(context, filename, mkey);
 331     } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) {
 332         ret = read_master_keytab(context, filename, mkey);
 333     } else {
 334         ret = read_master_mit(context, filename, mkey);
 335     }
 336     return ret;
 337 }
 338 
 339 krb5_error_code
 340 hdb_write_master_key(krb5_context context, const char *filename,
     /* [<][>][^][v][top][bottom][index][help] */
 341                      hdb_master_key mkey)
 342 {
 343     krb5_error_code ret;
 344     hdb_master_key p;
 345     krb5_keytab kt;
 346 
 347     if(filename == NULL)
 348         filename = HDB_DB_DIR "/m-key";
 349 
 350     ret = krb5_kt_resolve(context, filename, &kt);
 351     if(ret)
 352         return ret;
 353 
 354     for(p = mkey; p; p = p->next) {
 355         ret = krb5_kt_add_entry(context, kt, &p->keytab);
 356     }
 357 
 358     krb5_kt_close(context, kt);
 359 
 360     return ret;
 361 }
 362 
 363 hdb_master_key
 364 _hdb_find_master_key(uint32_t *mkvno, hdb_master_key mkey)
     /* [<][>][^][v][top][bottom][index][help] */
 365 {
 366     hdb_master_key ret = NULL;
 367     while(mkey) {
 368         if(ret == NULL && mkey->keytab.vno == 0)
 369             ret = mkey;
 370         if(mkvno == NULL) {
 371             if(ret == NULL || mkey->keytab.vno > ret->keytab.vno)
 372                 ret = mkey;
 373         } else if(mkey->keytab.vno == *mkvno)
 374             return mkey;
 375         mkey = mkey->next;
 376     }
 377     return ret;
 378 }
 379 
 380 int
 381 _hdb_mkey_version(hdb_master_key mkey)
     /* [<][>][^][v][top][bottom][index][help] */
 382 {
 383     return mkey->keytab.vno;
 384 }
 385 
 386 int
 387 _hdb_mkey_decrypt(krb5_context context, hdb_master_key key,
     /* [<][>][^][v][top][bottom][index][help] */
 388                   krb5_key_usage usage,
 389                   void *ptr, size_t size, krb5_data *res)
 390 {
 391     return krb5_decrypt(context, key->crypto, usage,
 392                         ptr, size, res);
 393 }
 394 
 395 int
 396 _hdb_mkey_encrypt(krb5_context context, hdb_master_key key,
     /* [<][>][^][v][top][bottom][index][help] */
 397                   krb5_key_usage usage,
 398                   const void *ptr, size_t size, krb5_data *res)
 399 {
 400     return krb5_encrypt(context, key->crypto, usage,
 401                         ptr, size, res);
 402 }
 403 
 404 krb5_error_code
 405 hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
     /* [<][>][^][v][top][bottom][index][help] */
 406 {
 407         
 408     krb5_error_code ret;
 409     krb5_data res;
 410     size_t keysize;
 411 
 412     hdb_master_key key;
 413 
 414     if(k->mkvno == NULL)
 415         return 0;
 416         
 417     key = _hdb_find_master_key(k->mkvno, mkey);
 418 
 419     if (key == NULL)
 420         return HDB_ERR_NO_MKEY;
 421 
 422     ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
 423                             k->key.keyvalue.data,
 424                             k->key.keyvalue.length,
 425                             &res);
 426     if(ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
 427         /* try to decrypt with MIT key usage */
 428         ret = _hdb_mkey_decrypt(context, key, 0,
 429                                 k->key.keyvalue.data,
 430                                 k->key.keyvalue.length,
 431                                 &res);
 432     }
 433     if (ret)
 434         return ret;
 435 
 436     /* fixup keylength if the key got padded when encrypting it */
 437     ret = krb5_enctype_keysize(context, k->key.keytype, &keysize);
 438     if (ret) {
 439         krb5_data_free(&res);
 440         return ret;
 441     }
 442     if (keysize > res.length) {
 443         krb5_data_free(&res);
 444         return KRB5_BAD_KEYSIZE;
 445     }
 446 
 447     memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
 448     free(k->key.keyvalue.data);
 449     k->key.keyvalue = res;
 450     k->key.keyvalue.length = keysize;
 451     free(k->mkvno);
 452     k->mkvno = NULL;
 453 
 454     return 0;
 455 }
 456 
 457 krb5_error_code
 458 hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
     /* [<][>][^][v][top][bottom][index][help] */
 459 {
 460     int i;
 461 
 462     for(i = 0; i < ent->keys.len; i++){
 463         krb5_error_code ret;
 464 
 465         ret = hdb_unseal_key_mkey(context, &ent->keys.val[i], mkey);
 466         if (ret)
 467             return ret;
 468     }
 469     return 0;
 470 }
 471 
 472 krb5_error_code
 473 hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
     /* [<][>][^][v][top][bottom][index][help] */
 474 {
 475     if (db->hdb_master_key_set == 0)
 476         return 0;
 477     return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
 478 }
 479 
 480 krb5_error_code
 481 hdb_unseal_key(krb5_context context, HDB *db, Key *k)
     /* [<][>][^][v][top][bottom][index][help] */
 482 {
 483     if (db->hdb_master_key_set == 0)
 484         return 0;
 485     return hdb_unseal_key_mkey(context, k, db->hdb_master_key);
 486 }
 487 
 488 krb5_error_code
 489 hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
     /* [<][>][^][v][top][bottom][index][help] */
 490 {
 491     krb5_error_code ret;
 492     krb5_data res;
 493     hdb_master_key key;
 494 
 495     if(k->mkvno != NULL)
 496         return 0;
 497 
 498     key = _hdb_find_master_key(k->mkvno, mkey);
 499 
 500     if (key == NULL)
 501         return HDB_ERR_NO_MKEY;
 502 
 503     ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
 504                             k->key.keyvalue.data,
 505                             k->key.keyvalue.length,
 506                             &res);
 507     if (ret)
 508         return ret;
 509 
 510     memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
 511     free(k->key.keyvalue.data);
 512     k->key.keyvalue = res;
 513 
 514     if (k->mkvno == NULL) {
 515         k->mkvno = malloc(sizeof(*k->mkvno));
 516         if (k->mkvno == NULL)
 517             return ENOMEM;
 518     }
 519     *k->mkvno = key->keytab.vno;
 520         
 521     return 0;
 522 }
 523 
 524 krb5_error_code
 525 hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
     /* [<][>][^][v][top][bottom][index][help] */
 526 {
 527     int i;
 528     for(i = 0; i < ent->keys.len; i++){
 529         krb5_error_code ret;
 530 
 531         ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
 532         if (ret)
 533             return ret;
 534     }
 535     return 0;
 536 }
 537 
 538 krb5_error_code
 539 hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
     /* [<][>][^][v][top][bottom][index][help] */
 540 {
 541     if (db->hdb_master_key_set == 0)
 542         return 0;
 543 
 544     return hdb_seal_keys_mkey(context, ent, db->hdb_master_key);
 545 }
 546 
 547 krb5_error_code
 548 hdb_seal_key(krb5_context context, HDB *db, Key *k)
     /* [<][>][^][v][top][bottom][index][help] */
 549 {
 550     if (db->hdb_master_key_set == 0)
 551         return 0;
 552 
 553     return hdb_seal_key_mkey(context, k, db->hdb_master_key);
 554 }
 555 
 556 krb5_error_code
 557 hdb_set_master_key (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 558                     HDB *db,
 559                     krb5_keyblock *key)
 560 {
 561     krb5_error_code ret;
 562     hdb_master_key mkey;
 563 
 564     ret = hdb_process_master_key(context, 0, key, 0, &mkey);
 565     if (ret)
 566         return ret;
 567     db->hdb_master_key = mkey;
 568 #if 0 /* XXX - why? */
 569     des_set_random_generator_seed(key.keyvalue.data);
 570 #endif
 571     db->hdb_master_key_set = 1;
 572     return 0;
 573 }
 574 
 575 krb5_error_code
 576 hdb_set_master_keyfile (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 577                         HDB *db,
 578                         const char *keyfile)
 579 {
 580     hdb_master_key key;
 581     krb5_error_code ret;
 582 
 583     ret = hdb_read_master_key(context, keyfile, &key);
 584     if (ret) {
 585         if (ret != ENOENT)
 586             return ret;
 587         krb5_clear_error_message(context);
 588         return 0;
 589     }
 590     db->hdb_master_key = key;
 591     db->hdb_master_key_set = 1;
 592     return ret;
 593 }
 594 
 595 krb5_error_code
 596 hdb_clear_master_key (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 597                       HDB *db)
 598 {
 599     if (db->hdb_master_key_set) {
 600         hdb_free_master_key(context, db->hdb_master_key);
 601         db->hdb_master_key_set = 0;
 602     }
 603     return 0;
 604 }

/* [<][>][^][v][top][bottom][index][help] */