mkey.c revision 102644
172445Sassar/*
2102644Snectar * Copyright (c) 2000 - 2002 Kungliga Tekniska H�gskolan
372445Sassar * (Royal Institute of Technology, Stockholm, Sweden).
472445Sassar * All rights reserved.
572445Sassar *
672445Sassar * Redistribution and use in source and binary forms, with or without
772445Sassar * modification, are permitted provided that the following conditions
872445Sassar * are met:
972445Sassar *
1072445Sassar * 1. Redistributions of source code must retain the above copyright
1172445Sassar *    notice, this list of conditions and the following disclaimer.
1272445Sassar *
1372445Sassar * 2. Redistributions in binary form must reproduce the above copyright
1472445Sassar *    notice, this list of conditions and the following disclaimer in the
1572445Sassar *    documentation and/or other materials provided with the distribution.
1672445Sassar *
1772445Sassar * 3. Neither the name of the Institute nor the names of its contributors
1872445Sassar *    may be used to endorse or promote products derived from this software
1972445Sassar *    without specific prior written permission.
2072445Sassar *
2172445Sassar * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2272445Sassar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2372445Sassar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2472445Sassar * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2572445Sassar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2672445Sassar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2772445Sassar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2872445Sassar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2972445Sassar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3072445Sassar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3172445Sassar * SUCH DAMAGE.
3272445Sassar */
3372445Sassar
3472445Sassar#include "hdb_locl.h"
3572445Sassar#ifndef O_BINARY
3672445Sassar#define O_BINARY 0
3772445Sassar#endif
3872445Sassar
39102644SnectarRCSID("$Id: mkey.c,v 1.14 2002/08/16 18:59:49 assar Exp $");
4072445Sassar
4172445Sassarstruct hdb_master_key_data {
4272445Sassar    krb5_keytab_entry keytab;
4372445Sassar    krb5_crypto crypto;
4472445Sassar    struct hdb_master_key_data *next;
4572445Sassar};
4672445Sassar
4772445Sassarvoid
4872445Sassarhdb_free_master_key(krb5_context context, hdb_master_key mkey)
4972445Sassar{
5072445Sassar    struct hdb_master_key_data *ptr;
5172445Sassar    while(mkey) {
5272445Sassar	krb5_kt_free_entry(context, &mkey->keytab);
5390926Snectar	if (mkey->crypto)
5490926Snectar	    krb5_crypto_destroy(context, mkey->crypto);
5572445Sassar	ptr = mkey;
5672445Sassar	mkey = mkey->next;
5772445Sassar	free(ptr);
5872445Sassar    }
5972445Sassar}
6072445Sassar
6172445Sassarkrb5_error_code
6272445Sassarhdb_process_master_key(krb5_context context,
6372445Sassar		       int kvno, krb5_keyblock *key, krb5_enctype etype,
6472445Sassar		       hdb_master_key *mkey)
6572445Sassar{
6672445Sassar    krb5_error_code ret;
6790926Snectar
6872445Sassar    *mkey = calloc(1, sizeof(**mkey));
6990926Snectar    if(*mkey == NULL) {
7090926Snectar	krb5_set_error_string(context, "malloc: out of memory");
7172445Sassar	return ENOMEM;
7290926Snectar    }
7372445Sassar    (*mkey)->keytab.vno = kvno;
7472445Sassar    ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal);
7590926Snectar    if(ret)
7690926Snectar	goto fail;
7772445Sassar    ret = krb5_copy_keyblock_contents(context, key, &(*mkey)->keytab.keyblock);
7890926Snectar    if(ret)
7990926Snectar	goto fail;
8072445Sassar    if(etype != 0)
8172445Sassar	(*mkey)->keytab.keyblock.keytype = etype;
8272445Sassar    (*mkey)->keytab.timestamp = time(NULL);
8372445Sassar    ret = krb5_crypto_init(context, key, etype, &(*mkey)->crypto);
8490926Snectar    if(ret)
8590926Snectar	goto fail;
8690926Snectar    return 0;
8790926Snectar fail:
8890926Snectar    hdb_free_master_key(context, *mkey);
8990926Snectar    *mkey = NULL;
9072445Sassar    return ret;
9172445Sassar}
9272445Sassar
9372445Sassarkrb5_error_code
9472445Sassarhdb_add_master_key(krb5_context context, krb5_keyblock *key,
9572445Sassar		   hdb_master_key *inout)
9672445Sassar{
9772445Sassar    int vno = 0;
9872445Sassar    hdb_master_key p;
9972445Sassar    krb5_error_code ret;
10072445Sassar
10172445Sassar    for(p = *inout; p; p = p->next)
10272445Sassar	vno = max(vno, p->keytab.vno);
10372445Sassar    vno++;
10472445Sassar    ret = hdb_process_master_key(context, vno, key, 0, &p);
10572445Sassar    if(ret)
10672445Sassar	return ret;
10772445Sassar    p->next = *inout;
10872445Sassar    *inout = p;
10972445Sassar    return 0;
11072445Sassar}
11172445Sassar
11272445Sassarstatic krb5_error_code
11372445Sassarread_master_keytab(krb5_context context, const char *filename,
11472445Sassar		   hdb_master_key *mkey)
11572445Sassar{
11672445Sassar    krb5_error_code ret;
11772445Sassar    krb5_keytab id;
11872445Sassar    krb5_kt_cursor cursor;
11972445Sassar    krb5_keytab_entry entry;
12072445Sassar    hdb_master_key p;
12172445Sassar
12272445Sassar    ret = krb5_kt_resolve(context, filename, &id);
12372445Sassar    if(ret)
12472445Sassar	return ret;
12572445Sassar
12672445Sassar    ret = krb5_kt_start_seq_get(context, id, &cursor);
12772445Sassar    if(ret)
12872445Sassar	goto out;
12972445Sassar    *mkey = NULL;
13072445Sassar    while(krb5_kt_next_entry(context, id, &entry, &cursor) == 0) {
13172445Sassar	p = calloc(1, sizeof(*p));
13272445Sassar	p->keytab = entry;
13372445Sassar	ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
13472445Sassar	p->next = *mkey;
13572445Sassar	*mkey = p;
13672445Sassar    }
13772445Sassar    krb5_kt_end_seq_get(context, id, &cursor);
13872445Sassar  out:
13972445Sassar    krb5_kt_close(context, id);
14072445Sassar    return ret;
14172445Sassar}
14272445Sassar
14372445Sassar/* read a MIT master keyfile */
14472445Sassarstatic krb5_error_code
14572445Sassarread_master_mit(krb5_context context, const char *filename,
14672445Sassar		hdb_master_key *mkey)
14772445Sassar{
14872445Sassar    int fd;
14972445Sassar    krb5_error_code ret;
15072445Sassar    krb5_storage *sp;
15172445Sassar    u_int16_t enctype;
15272445Sassar    krb5_keyblock key;
15372445Sassar
15472445Sassar    fd = open(filename, O_RDONLY | O_BINARY);
15590926Snectar    if(fd < 0) {
15690926Snectar	int save_errno = errno;
15790926Snectar	krb5_set_error_string(context, "failed to open %s: %s", filename,
15890926Snectar			      strerror(save_errno));
15990926Snectar	return save_errno;
16090926Snectar    }
16172445Sassar    sp = krb5_storage_from_fd(fd);
16272445Sassar    if(sp == NULL) {
16372445Sassar	close(fd);
16472445Sassar	return errno;
16572445Sassar    }
16672445Sassar    krb5_storage_set_flags(sp, KRB5_STORAGE_HOST_BYTEORDER);
16772445Sassar#if 0
16872445Sassar    /* could possibly use ret_keyblock here, but do it with more
16972445Sassar       checks for now */
17072445Sassar    ret = krb5_ret_keyblock(sp, &key);
17172445Sassar#else
17272445Sassar    ret = krb5_ret_int16(sp, &enctype);
17372445Sassar    if((htons(enctype) & 0xff00) == 0x3000) {
17490926Snectar	krb5_set_error_string(context, "unknown keytype in %s: %#x, expected %#x",
17590926Snectar			      filename, htons(enctype), 0x3000);
17672445Sassar	ret = HEIM_ERR_BAD_MKEY;
17772445Sassar	goto out;
17872445Sassar    }
17972445Sassar    key.keytype = enctype;
18072445Sassar    ret = krb5_ret_data(sp, &key.keyvalue);
18172445Sassar    if(ret)
18272445Sassar	goto out;
18372445Sassar#endif
18472445Sassar    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
18572445Sassar    krb5_free_keyblock_contents(context, &key);
18672445Sassar  out:
18772445Sassar    krb5_storage_free(sp);
18872445Sassar    close(fd);
18972445Sassar    return ret;
19072445Sassar}
19172445Sassar
19272445Sassar/* read an old master key file */
19372445Sassarstatic krb5_error_code
19472445Sassarread_master_encryptionkey(krb5_context context, const char *filename,
19572445Sassar			  hdb_master_key *mkey)
19672445Sassar{
19772445Sassar    int fd;
19872445Sassar    krb5_keyblock key;
19972445Sassar    krb5_error_code ret;
20072445Sassar    unsigned char buf[256];
20172445Sassar    ssize_t len;
202102644Snectar    size_t ret_len;
20372445Sassar
20472445Sassar    fd = open(filename, O_RDONLY | O_BINARY);
20590926Snectar    if(fd < 0) {
20690926Snectar	int save_errno = errno;
20790926Snectar	krb5_set_error_string(context, "failed to open %s: %s",
20890926Snectar			      filename, strerror(save_errno));
20990926Snectar	return save_errno;
21090926Snectar    }
21172445Sassar
21272445Sassar    len = read(fd, buf, sizeof(buf));
21372445Sassar    close(fd);
21490926Snectar    if(len < 0) {
21590926Snectar	int save_errno = errno;
21690926Snectar	krb5_set_error_string(context, "error reading %s: %s",
21790926Snectar			      filename, strerror(save_errno));
21890926Snectar	return save_errno;
21990926Snectar    }
22072445Sassar
221102644Snectar    ret = decode_EncryptionKey(buf, len, &key, &ret_len);
22272445Sassar    memset(buf, 0, sizeof(buf));
22372445Sassar    if(ret)
22472445Sassar	return ret;
22572445Sassar
22672445Sassar    /* Originally, the keytype was just that, and later it got changed
22772445Sassar       to des-cbc-md5, but we always used des in cfb64 mode. This
22872445Sassar       should cover all cases, but will break if someone has hacked
22972445Sassar       this code to really use des-cbc-md5 -- but then that's not my
23072445Sassar       problem. */
23172445Sassar    if(key.keytype == KEYTYPE_DES || key.keytype == ETYPE_DES_CBC_MD5)
23272445Sassar	key.keytype = ETYPE_DES_CFB64_NONE;
23372445Sassar
23472445Sassar    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
23572445Sassar    krb5_free_keyblock_contents(context, &key);
23672445Sassar    return ret;
23772445Sassar}
23872445Sassar
23972445Sassar/* read a krb4 /.k style file */
24072445Sassarstatic krb5_error_code
24172445Sassarread_master_krb4(krb5_context context, const char *filename,
24272445Sassar		 hdb_master_key *mkey)
24372445Sassar{
24472445Sassar    int fd;
24572445Sassar    krb5_keyblock key;
24672445Sassar    krb5_error_code ret;
24772445Sassar    unsigned char buf[256];
24872445Sassar    ssize_t len;
24972445Sassar
25072445Sassar    fd = open(filename, O_RDONLY | O_BINARY);
25190926Snectar    if(fd < 0) {
25290926Snectar	int save_errno = errno;
25390926Snectar	krb5_set_error_string(context, "failed to open %s: %s",
25490926Snectar			      filename, strerror(save_errno));
25590926Snectar	return save_errno;
25690926Snectar    }
25772445Sassar
25872445Sassar    len = read(fd, buf, sizeof(buf));
25972445Sassar    close(fd);
26090926Snectar    if(len < 0) {
26190926Snectar	int save_errno = errno;
26290926Snectar	krb5_set_error_string(context, "error reading %s: %s",
26390926Snectar			      filename, strerror(save_errno));
26490926Snectar	return save_errno;
26590926Snectar    }
26690926Snectar    if(len != 8) {
26790926Snectar	krb5_set_error_string(context, "bad contents of %s", filename);
26890926Snectar	return HEIM_ERR_EOF; /* XXX file might be too large */
26990926Snectar    }
27072445Sassar
27172445Sassar    memset(&key, 0, sizeof(key));
27272445Sassar    key.keytype = ETYPE_DES_PCBC_NONE;
27372445Sassar    ret = krb5_data_copy(&key.keyvalue, buf, len);
27472445Sassar    memset(buf, 0, sizeof(buf));
27572445Sassar    if(ret)
27672445Sassar	return ret;
27772445Sassar
27872445Sassar    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
27972445Sassar    krb5_free_keyblock_contents(context, &key);
28072445Sassar    return ret;
28172445Sassar}
28272445Sassar
28372445Sassarkrb5_error_code
28472445Sassarhdb_read_master_key(krb5_context context, const char *filename,
28572445Sassar		    hdb_master_key *mkey)
28672445Sassar{
28772445Sassar    FILE *f;
28872445Sassar    unsigned char buf[16];
28972445Sassar    krb5_error_code ret;
29072445Sassar
29172445Sassar    off_t len;
29272445Sassar
29372445Sassar    *mkey = NULL;
29472445Sassar
29572445Sassar    if(filename == NULL)
29672445Sassar	filename = HDB_DB_DIR "/m-key";
29772445Sassar
29872445Sassar    f = fopen(filename, "r");
29990926Snectar    if(f == NULL) {
30090926Snectar	int save_errno = errno;
30190926Snectar	krb5_set_error_string(context, "failed to open %s: %s",
30290926Snectar			      filename, strerror(save_errno));
30390926Snectar	return save_errno;
30490926Snectar    }
30572445Sassar
30672445Sassar    if(fread(buf, 1, 2, f) != 2) {
30790926Snectar	krb5_set_error_string(context, "end of file reading %s", filename);
30872445Sassar	fclose(f);
30972445Sassar	return HEIM_ERR_EOF;
31072445Sassar    }
31172445Sassar
31272445Sassar    fseek(f, 0, SEEK_END);
31372445Sassar    len = ftell(f);
31472445Sassar
31572445Sassar    if(fclose(f) != 0)
31672445Sassar	return errno;
31772445Sassar
31872445Sassar    if(len < 0)
31972445Sassar	return errno;
32072445Sassar
32172445Sassar    if(len == 8) {
32272445Sassar	ret = read_master_krb4(context, filename, mkey);
32372445Sassar    } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) {
32472445Sassar	ret = read_master_encryptionkey(context, filename, mkey);
32572445Sassar    } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) {
32672445Sassar	ret = read_master_keytab(context, filename, mkey);
32772445Sassar    } else {
32872445Sassar	ret = read_master_mit(context, filename, mkey);
32972445Sassar    }
33072445Sassar    return ret;
33172445Sassar}
33272445Sassar
33372445Sassarkrb5_error_code
33472445Sassarhdb_write_master_key(krb5_context context, const char *filename,
33572445Sassar		     hdb_master_key mkey)
33672445Sassar{
33772445Sassar    krb5_error_code ret;
33872445Sassar    hdb_master_key p;
33972445Sassar    krb5_keytab kt;
34072445Sassar
34172445Sassar    if(filename == NULL)
34272445Sassar	filename = HDB_DB_DIR "/m-key";
34372445Sassar
34472445Sassar    ret = krb5_kt_resolve(context, filename, &kt);
34572445Sassar    if(ret)
34672445Sassar	return ret;
34772445Sassar
34872445Sassar    for(p = mkey; p; p = p->next) {
34972445Sassar	ret = krb5_kt_add_entry(context, kt, &p->keytab);
35072445Sassar    }
35172445Sassar
35272445Sassar    krb5_kt_close(context, kt);
35372445Sassar
35472445Sassar    return ret;
35572445Sassar}
35672445Sassar
35772445Sassarstatic hdb_master_key
35872445Sassarfind_master_key(Key *key, hdb_master_key mkey)
35972445Sassar{
36072445Sassar    hdb_master_key ret = NULL;
36172445Sassar    while(mkey) {
36272445Sassar	if(ret == NULL && mkey->keytab.vno == 0)
36372445Sassar	    ret = mkey;
36472445Sassar	if(key->mkvno == NULL) {
36572445Sassar	    if(ret == NULL || mkey->keytab.vno > ret->keytab.vno)
36672445Sassar		ret = mkey;
36772445Sassar	} else if(mkey->keytab.vno == *key->mkvno)
36872445Sassar	    return mkey;
36972445Sassar	mkey = mkey->next;
37072445Sassar    }
37172445Sassar    return ret;
37272445Sassar}
37372445Sassar
37472445Sassarkrb5_error_code
37572445Sassarhdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
37672445Sassar{
37772445Sassar    int i;
37872445Sassar    krb5_error_code ret;
37972445Sassar    krb5_data res;
38072445Sassar    Key *k;
38172445Sassar
38272445Sassar    for(i = 0; i < ent->keys.len; i++){
38372445Sassar	hdb_master_key key;
38472445Sassar
38572445Sassar	k = &ent->keys.val[i];
38672445Sassar	if(k->mkvno == NULL)
38772445Sassar	    continue;
38872445Sassar
38972445Sassar	key = find_master_key(&ent->keys.val[i], mkey);
39072445Sassar
39172445Sassar	if (key == NULL)
39272445Sassar	    return HDB_ERR_NO_MKEY;
39372445Sassar
39472445Sassar	ret = krb5_decrypt(context, key->crypto, HDB_KU_MKEY,
39572445Sassar			   k->key.keyvalue.data,
39672445Sassar			   k->key.keyvalue.length,
39772445Sassar			   &res);
39872445Sassar	if (ret)
39972445Sassar	    return ret;
40072445Sassar
40172445Sassar	memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
40272445Sassar	free(k->key.keyvalue.data);
40372445Sassar	k->key.keyvalue = res;
40472445Sassar	free(k->mkvno);
40572445Sassar	k->mkvno = NULL;
40672445Sassar    }
40772445Sassar    return 0;
40872445Sassar}
40972445Sassar
41072445Sassarkrb5_error_code
41172445Sassarhdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
41272445Sassar{
41372445Sassar    if (db->master_key_set == 0)
41472445Sassar	return 0;
41572445Sassar    return hdb_unseal_keys_mkey(context, ent, db->master_key);
41672445Sassar}
41772445Sassar
41872445Sassarkrb5_error_code
41972445Sassarhdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
42072445Sassar{
42172445Sassar    int i;
42272445Sassar    krb5_error_code ret;
42372445Sassar    krb5_data res;
42472445Sassar    for(i = 0; i < ent->keys.len; i++){
42572445Sassar	Key *k = &ent->keys.val[i];
42672445Sassar	hdb_master_key key;
42772445Sassar
42872445Sassar	if(k->mkvno != NULL)
42972445Sassar	    continue;
43072445Sassar
43172445Sassar	key = find_master_key(k, mkey);
43272445Sassar
43372445Sassar	if (key == NULL)
43472445Sassar	    return HDB_ERR_NO_MKEY;
43572445Sassar
43672445Sassar	ret = krb5_encrypt(context, key->crypto, HDB_KU_MKEY,
43772445Sassar			   k->key.keyvalue.data,
43872445Sassar			   k->key.keyvalue.length,
43972445Sassar			   &res);
44072445Sassar	if (ret)
44172445Sassar	    return ret;
44272445Sassar
44372445Sassar	memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
44472445Sassar	free(k->key.keyvalue.data);
44572445Sassar	k->key.keyvalue = res;
44672445Sassar
44772445Sassar	k->mkvno = malloc(sizeof(*k->mkvno));
44872445Sassar	if (k->mkvno == NULL)
44972445Sassar	    return ENOMEM;
45072445Sassar	*k->mkvno = key->keytab.vno;
45172445Sassar    }
45272445Sassar    return 0;
45372445Sassar}
45472445Sassar
45572445Sassarkrb5_error_code
45672445Sassarhdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
45772445Sassar{
45872445Sassar    if (db->master_key_set == 0)
45972445Sassar	return 0;
46072445Sassar
46172445Sassar    return hdb_seal_keys_mkey(context, ent, db->master_key);
46272445Sassar}
46372445Sassar
46472445Sassarkrb5_error_code
46572445Sassarhdb_set_master_key (krb5_context context,
46672445Sassar		    HDB *db,
46772445Sassar		    krb5_keyblock *key)
46872445Sassar{
46972445Sassar    krb5_error_code ret;
47072445Sassar    hdb_master_key mkey;
47172445Sassar
47272445Sassar    ret = hdb_process_master_key(context, 0, key, 0, &mkey);
47372445Sassar    if (ret)
47472445Sassar	return ret;
47572445Sassar    db->master_key = mkey;
47672445Sassar#if 0 /* XXX - why? */
47772445Sassar    des_set_random_generator_seed(key.keyvalue.data);
47872445Sassar#endif
47972445Sassar    db->master_key_set = 1;
48072445Sassar    return 0;
48172445Sassar}
48272445Sassar
48372445Sassarkrb5_error_code
48472445Sassarhdb_set_master_keyfile (krb5_context context,
48572445Sassar			HDB *db,
48672445Sassar			const char *keyfile)
48772445Sassar{
48872445Sassar    hdb_master_key key;
48972445Sassar    krb5_error_code ret;
49072445Sassar
49172445Sassar    ret = hdb_read_master_key(context, keyfile, &key);
49272445Sassar    if (ret) {
49372445Sassar	if (ret != ENOENT)
49472445Sassar	    return ret;
49590926Snectar	krb5_clear_error_string(context);
49672445Sassar	return 0;
49772445Sassar    }
49872445Sassar    db->master_key = key;
49972445Sassar    db->master_key_set = 1;
50072445Sassar    return ret;
50172445Sassar}
50272445Sassar
50372445Sassarkrb5_error_code
50472445Sassarhdb_clear_master_key (krb5_context context,
50572445Sassar		      HDB *db)
50672445Sassar{
50772445Sassar    if (db->master_key_set) {
50872445Sassar	hdb_free_master_key(context, db->master_key);
50972445Sassar	db->master_key_set = 0;
51072445Sassar    }
51172445Sassar    return 0;
51272445Sassar}
513