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