Home | History | Annotate | Line # | Download | only in hdb
mkey.c revision 1.1
      1 /*	$NetBSD: mkey.c,v 1.1 2011/04/13 18:14:42 elric Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2000 - 2004 Kungliga Tekniska Hgskolan
      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 == KEYTYPE_DES || 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(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     int 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     int 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