From 954c01728e0c7485b72c9a5d5737e5f6bd0cf0b9 Mon Sep 17 00:00:00 2001 From: Heimdal Import User Date: Mon, 11 Jul 2005 01:16:55 +0000 Subject: r8302: import mini HEIMDAL into the tree (This used to be commit 118be28a7aef233799956615a99d1a2a74dac175) --- source4/heimdal/lib/hdb/db.c | 306 ++++++++++++++++++++++++++ source4/heimdal/lib/hdb/hdb-private.h | 25 +++ source4/heimdal/lib/hdb/hdb-protos.h | 247 +++++++++++++++++++++ source4/heimdal/lib/hdb/hdb.asn1 | 70 ++++++ source4/heimdal/lib/hdb/hdb.c | 373 ++++++++++++++++++++++++++++++++ source4/heimdal/lib/hdb/hdb.h | 102 +++++++++ source4/heimdal/lib/hdb/hdb_err.et | 27 +++ source4/heimdal/lib/hdb/hdb_locl.h | 67 ++++++ source4/heimdal/lib/hdb/keys.c | 393 ++++++++++++++++++++++++++++++++++ source4/heimdal/lib/hdb/ndbm.c | 369 +++++++++++++++++++++++++++++++ 10 files changed, 1979 insertions(+) create mode 100644 source4/heimdal/lib/hdb/db.c create mode 100644 source4/heimdal/lib/hdb/hdb-private.h create mode 100644 source4/heimdal/lib/hdb/hdb-protos.h create mode 100644 source4/heimdal/lib/hdb/hdb.asn1 create mode 100644 source4/heimdal/lib/hdb/hdb.c create mode 100644 source4/heimdal/lib/hdb/hdb.h create mode 100644 source4/heimdal/lib/hdb/hdb_err.et create mode 100644 source4/heimdal/lib/hdb/hdb_locl.h create mode 100644 source4/heimdal/lib/hdb/keys.c create mode 100644 source4/heimdal/lib/hdb/ndbm.c (limited to 'source4/heimdal/lib/hdb') diff --git a/source4/heimdal/lib/hdb/db.c b/source4/heimdal/lib/hdb/db.c new file mode 100644 index 0000000000..d7a4cf35ee --- /dev/null +++ b/source4/heimdal/lib/hdb/db.c @@ -0,0 +1,306 @@ +/* + * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hdb_locl.h" + +RCSID("$Id: db.c,v 1.32 2005/06/23 13:34:17 lha Exp $"); + +#if HAVE_DB1 + +#if defined(HAVE_DB_185_H) +#include +#elif defined(HAVE_DB_H) +#include +#endif + +static krb5_error_code +DB_close(krb5_context context, HDB *db) +{ + DB *d = (DB*)db->hdb_db; + d->close(d); + return 0; +} + +static krb5_error_code +DB_destroy(krb5_context context, HDB *db) +{ + krb5_error_code ret; + + ret = hdb_clear_master_key (context, db); + free(db->hdb_name); + free(db); + return ret; +} + +static krb5_error_code +DB_lock(krb5_context context, HDB *db, int operation) +{ + DB *d = (DB*)db->hdb_db; + int fd = (*d->fd)(d); + if(fd < 0) + return HDB_ERR_CANT_LOCK_DB; + return hdb_lock(fd, operation); +} + +static krb5_error_code +DB_unlock(krb5_context context, HDB *db) +{ + DB *d = (DB*)db->hdb_db; + int fd = (*d->fd)(d); + if(fd < 0) + return HDB_ERR_CANT_LOCK_DB; + return hdb_unlock(fd); +} + + +static krb5_error_code +DB_seq(krb5_context context, HDB *db, + unsigned flags, hdb_entry *entry, int flag) +{ + DB *d = (DB*)db->hdb_db; + DBT key, value; + krb5_data key_data, data; + int code; + + code = db->hdb_lock(context, db, HDB_RLOCK); + if(code == -1) + return HDB_ERR_DB_INUSE; + code = d->seq(d, &key, &value, flag); + db->hdb_unlock(context, db); /* XXX check value */ + if(code == -1) + return errno; + if(code == 1) + return HDB_ERR_NOENTRY; + + key_data.data = key.data; + key_data.length = key.size; + data.data = value.data; + data.length = value.size; + if (hdb_value2entry(context, &data, entry)) + return DB_seq(context, db, flags, entry, R_NEXT); + if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { + code = hdb_unseal_keys (context, db, entry); + if (code) + hdb_free_entry (context, entry); + } + if (code == 0 && entry->principal == NULL) { + entry->principal = malloc(sizeof(*entry->principal)); + if (entry->principal == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + code = ENOMEM; + hdb_free_entry (context, entry); + } else { + hdb_key2principal(context, &key_data, entry->principal); + } + } + return code; +} + + +static krb5_error_code +DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) +{ + return DB_seq(context, db, flags, entry, R_FIRST); +} + + +static krb5_error_code +DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) +{ + return DB_seq(context, db, flags, entry, R_NEXT); +} + +static krb5_error_code +DB_rename(krb5_context context, HDB *db, const char *new_name) +{ + int ret; + char *old, *new; + + asprintf(&old, "%s.db", db->hdb_name); + asprintf(&new, "%s.db", new_name); + ret = rename(old, new); + free(old); + free(new); + if(ret) + return errno; + + free(db->hdb_name); + db->hdb_name = strdup(new_name); + return 0; +} + +static krb5_error_code +DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply) +{ + DB *d = (DB*)db->hdb_db; + DBT k, v; + int code; + + k.data = key.data; + k.size = key.length; + code = db->hdb_lock(context, db, HDB_RLOCK); + if(code) + return code; + code = d->get(d, &k, &v, 0); + db->hdb_unlock(context, db); + if(code < 0) + return errno; + if(code == 1) + return HDB_ERR_NOENTRY; + + krb5_data_copy(reply, v.data, v.size); + return 0; +} + +static krb5_error_code +DB__put(krb5_context context, HDB *db, int replace, + krb5_data key, krb5_data value) +{ + DB *d = (DB*)db->hdb_db; + DBT k, v; + int code; + + k.data = key.data; + k.size = key.length; + v.data = value.data; + v.size = value.length; + code = db->hdb_lock(context, db, HDB_WLOCK); + if(code) + return code; + code = d->put(d, &k, &v, replace ? 0 : R_NOOVERWRITE); + db->hdb_unlock(context, db); + if(code < 0) + return errno; + if(code == 1) + return HDB_ERR_EXISTS; + return 0; +} + +static krb5_error_code +DB__del(krb5_context context, HDB *db, krb5_data key) +{ + DB *d = (DB*)db->hdb_db; + DBT k; + krb5_error_code code; + k.data = key.data; + k.size = key.length; + code = db->hdb_lock(context, db, HDB_WLOCK); + if(code) + return code; + code = d->del(d, &k, 0); + db->hdb_unlock(context, db); + if(code == 1) + return HDB_ERR_NOENTRY; + if(code < 0) + return errno; + return 0; +} + +static krb5_error_code +DB_open(krb5_context context, HDB *db, int flags, mode_t mode) +{ + char *fn; + krb5_error_code ret; + + asprintf(&fn, "%s.db", db->hdb_name); + if (fn == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL); + free(fn); + /* try to open without .db extension */ + if(db->hdb_db == NULL && errno == ENOENT) + db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL); + if(db->hdb_db == NULL) { + ret = errno; + krb5_set_error_string(context, "dbopen (%s): %s", + db->hdb_name, strerror(ret)); + return ret; + } + if((flags & O_ACCMODE) == O_RDONLY) + ret = hdb_check_db_format(context, db); + else + ret = hdb_init_db(context, db); + if(ret == HDB_ERR_NOENTRY) { + krb5_clear_error_string(context); + return 0; + } + if (ret) { + DB_close(context, db); + krb5_set_error_string(context, "hdb_open: failed %s database %s", + (flags & O_ACCMODE) == O_RDONLY ? + "checking format of" : "initialize", + db->hdb_name); + } + return ret; +} + +krb5_error_code +hdb_db_create(krb5_context context, HDB **db, + const char *filename) +{ + *db = malloc(sizeof(**db)); + if (*db == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + + (*db)->hdb_db = NULL; + (*db)->hdb_name = strdup(filename); + if ((*db)->hdb_name == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + free(*db); + *db = NULL; + return ENOMEM; + } + (*db)->hdb_master_key_set = 0; + (*db)->hdb_openp = 0; + (*db)->hdb_open = DB_open; + (*db)->hdb_close = DB_close; + (*db)->hdb_fetch = _hdb_fetch; + (*db)->hdb_store = _hdb_store; + (*db)->hdb_remove = _hdb_remove; + (*db)->hdb_firstkey = DB_firstkey; + (*db)->hdb_nextkey= DB_nextkey; + (*db)->hdb_lock = DB_lock; + (*db)->hdb_unlock = DB_unlock; + (*db)->hdb_rename = DB_rename; + (*db)->hdb__get = DB__get; + (*db)->hdb__put = DB__put; + (*db)->hdb__del = DB__del; + (*db)->hdb_destroy = DB_destroy; + return 0; +} + +#endif /* HAVE_DB1 */ diff --git a/source4/heimdal/lib/hdb/hdb-private.h b/source4/heimdal/lib/hdb/hdb-private.h new file mode 100644 index 0000000000..653df8c451 --- /dev/null +++ b/source4/heimdal/lib/hdb/hdb-private.h @@ -0,0 +1,25 @@ +/* This is a generated file */ +#ifndef __hdb_private_h__ +#define __hdb_private_h__ + +#include + +krb5_error_code +_hdb_fetch(krb5_context context, HDB *db, unsigned flags, + krb5_principal principal, + enum hdb_ent_type ent_type, + hdb_entry *entry); +krb5_error_code +_hdb_remove ( + krb5_context /*context*/, + HDB */*db*/, + hdb_entry */*entry*/); + +krb5_error_code +_hdb_store ( + krb5_context /*context*/, + HDB */*db*/, + unsigned /*flags*/, + hdb_entry */*entry*/); + +#endif /* __hdb_private_h__ */ diff --git a/source4/heimdal/lib/hdb/hdb-protos.h b/source4/heimdal/lib/hdb/hdb-protos.h new file mode 100644 index 0000000000..886d48e5bd --- /dev/null +++ b/source4/heimdal/lib/hdb/hdb-protos.h @@ -0,0 +1,247 @@ +/* This is a generated file */ +#ifndef __hdb_protos_h__ +#define __hdb_protos_h__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +krb5_error_code +hdb_add_master_key ( + krb5_context /*context*/, + krb5_keyblock */*key*/, + hdb_master_key */*inout*/); + +krb5_error_code +hdb_check_db_format ( + krb5_context /*context*/, + HDB */*db*/); + +krb5_error_code +hdb_clear_master_key ( + krb5_context /*context*/, + HDB */*db*/); + +krb5_error_code +hdb_create ( + krb5_context /*context*/, + HDB **/*db*/, + const char */*filename*/); + +krb5_error_code +hdb_db_create ( + krb5_context /*context*/, + HDB **/*db*/, + const char */*filename*/); + +krb5_error_code +hdb_enctype2key ( + krb5_context /*context*/, + hdb_entry */*e*/, + krb5_enctype /*enctype*/, + Key **/*key*/); + +krb5_error_code +hdb_entry2string ( + krb5_context /*context*/, + hdb_entry */*ent*/, + char **/*str*/); + +int +hdb_entry2value ( + krb5_context /*context*/, + hdb_entry */*ent*/, + krb5_data */*value*/); + +krb5_error_code +hdb_foreach ( + krb5_context /*context*/, + HDB */*db*/, + unsigned /*flags*/, + hdb_foreach_func_t /*func*/, + void */*data*/); + +void +hdb_free_entry ( + krb5_context /*context*/, + hdb_entry */*ent*/); + +void +hdb_free_key (Key */*key*/); + +void +hdb_free_keys ( + krb5_context /*context*/, + int /*len*/, + Key */*keys*/); + +void +hdb_free_master_key ( + krb5_context /*context*/, + hdb_master_key /*mkey*/); + +krb5_error_code +hdb_generate_key_set ( + krb5_context /*context*/, + krb5_principal /*principal*/, + Key **/*ret_key_set*/, + size_t */*nkeyset*/, + int /*no_salt*/); + +krb5_error_code +hdb_generate_key_set_password ( + krb5_context /*context*/, + krb5_principal /*principal*/, + const char */*password*/, + Key **/*keys*/, + size_t */*num_keys*/); + +krb5_error_code +hdb_init_db ( + krb5_context /*context*/, + HDB */*db*/); + +int +hdb_key2principal ( + krb5_context /*context*/, + krb5_data */*key*/, + krb5_principal /*p*/); + +krb5_error_code +hdb_ldap_create ( + krb5_context /*context*/, + HDB ** /*db*/, + const char */*arg*/); + +krb5_error_code +hdb_list_builtin ( + krb5_context /*context*/, + char **/*list*/); + +krb5_error_code +hdb_lock ( + int /*fd*/, + int /*operation*/); + +krb5_error_code +hdb_ndbm_create ( + krb5_context /*context*/, + HDB **/*db*/, + const char */*filename*/); + +krb5_error_code +hdb_next_enctype2key ( + krb5_context /*context*/, + const hdb_entry */*e*/, + krb5_enctype /*enctype*/, + Key **/*key*/); + +int +hdb_principal2key ( + krb5_context /*context*/, + krb5_principal /*p*/, + krb5_data */*key*/); + +krb5_error_code +hdb_print_entry ( + krb5_context /*context*/, + HDB */*db*/, + hdb_entry */*entry*/, + void */*data*/); + +krb5_error_code +hdb_process_master_key ( + krb5_context /*context*/, + int /*kvno*/, + krb5_keyblock */*key*/, + krb5_enctype /*etype*/, + hdb_master_key */*mkey*/); + +krb5_error_code +hdb_read_master_key ( + krb5_context /*context*/, + const char */*filename*/, + hdb_master_key */*mkey*/); + +krb5_error_code +hdb_seal_key ( + krb5_context /*context*/, + HDB */*db*/, + Key */*k*/); + +krb5_error_code +hdb_seal_key_mkey ( + krb5_context /*context*/, + Key */*k*/, + hdb_master_key /*mkey*/); + +krb5_error_code +hdb_seal_keys ( + krb5_context /*context*/, + HDB */*db*/, + hdb_entry */*ent*/); + +krb5_error_code +hdb_seal_keys_mkey ( + krb5_context /*context*/, + hdb_entry */*ent*/, + hdb_master_key /*mkey*/); + +krb5_error_code +hdb_set_master_key ( + krb5_context /*context*/, + HDB */*db*/, + krb5_keyblock */*key*/); + +krb5_error_code +hdb_set_master_keyfile ( + krb5_context /*context*/, + HDB */*db*/, + const char */*keyfile*/); + +krb5_error_code +hdb_unlock (int /*fd*/); + +krb5_error_code +hdb_unseal_key ( + krb5_context /*context*/, + HDB */*db*/, + Key */*k*/); + +krb5_error_code +hdb_unseal_key_mkey ( + krb5_context /*context*/, + Key */*k*/, + hdb_master_key /*mkey*/); + +krb5_error_code +hdb_unseal_keys ( + krb5_context /*context*/, + HDB */*db*/, + hdb_entry */*ent*/); + +krb5_error_code +hdb_unseal_keys_mkey ( + krb5_context /*context*/, + hdb_entry */*ent*/, + hdb_master_key /*mkey*/); + +int +hdb_value2entry ( + krb5_context /*context*/, + krb5_data */*value*/, + hdb_entry */*ent*/); + +krb5_error_code +hdb_write_master_key ( + krb5_context /*context*/, + const char */*filename*/, + hdb_master_key /*mkey*/); + +#ifdef __cplusplus +} +#endif + +#endif /* __hdb_protos_h__ */ diff --git a/source4/heimdal/lib/hdb/hdb.asn1 b/source4/heimdal/lib/hdb/hdb.asn1 new file mode 100644 index 0000000000..770acf4dce --- /dev/null +++ b/source4/heimdal/lib/hdb/hdb.asn1 @@ -0,0 +1,70 @@ +-- $Id: hdb.asn1,v 1.12 2004/11/10 18:50:27 lha Exp $ +HDB DEFINITIONS ::= +BEGIN + +IMPORTS EncryptionKey, KerberosTime, Principal FROM krb5; + +HDB_DB_FORMAT INTEGER ::= 2 -- format of database, + -- update when making changes + +-- these must have the same value as the pa-* counterparts +hdb-pw-salt INTEGER ::= 3 +hdb-afs3-salt INTEGER ::= 10 + +Salt ::= SEQUENCE { + type[0] INTEGER (0..4294967295), + salt[1] OCTET STRING +} + +Key ::= SEQUENCE { + mkvno[0] INTEGER (0..4294967295) OPTIONAL, -- master key version number + key[1] EncryptionKey, + salt[2] Salt OPTIONAL +} + +Event ::= SEQUENCE { + time[0] KerberosTime, + principal[1] Principal OPTIONAL +} + +HDBFlags ::= BIT STRING { + initial(0), -- require as-req + forwardable(1), -- may issue forwardable + proxiable(2), -- may issue proxiable + renewable(3), -- may issue renewable + postdate(4), -- may issue postdatable + server(5), -- may be server + client(6), -- may be client + invalid(7), -- entry is invalid + require-preauth(8), -- must use preauth + change-pw(9), -- change password service + require-hwauth(10), -- must use hwauth + ok-as-delegate(11), -- as in TicketFlags + user-to-user(12), -- may use user-to-user auth + immutable(13) -- may not be deleted +} + +GENERATION ::= SEQUENCE { + time[0] KerberosTime, -- timestamp + usec[1] INTEGER (0..4294967295), -- microseconds + gen[2] INTEGER (0..4294967295) -- generation number +} + +hdb_entry ::= SEQUENCE { + principal[0] Principal OPTIONAL, -- this is optional only + -- for compatibility with libkrb5 + kvno[1] INTEGER (0..4294967295), + keys[2] SEQUENCE OF Key, + created-by[3] Event, + modified-by[4] Event OPTIONAL, + valid-start[5] KerberosTime OPTIONAL, + valid-end[6] KerberosTime OPTIONAL, + pw-end[7] KerberosTime OPTIONAL, + max-life[8] INTEGER (0..4294967295) OPTIONAL, + max-renew[9] INTEGER (0..4294967295) OPTIONAL, + flags[10] HDBFlags, + etypes[11] SEQUENCE OF INTEGER (0..4294967295) OPTIONAL, + generation[12] GENERATION OPTIONAL +} + +END diff --git a/source4/heimdal/lib/hdb/hdb.c b/source4/heimdal/lib/hdb/hdb.c new file mode 100644 index 0000000000..53c952927f --- /dev/null +++ b/source4/heimdal/lib/hdb/hdb.c @@ -0,0 +1,373 @@ +/* + * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hdb_locl.h" + +RCSID("$Id: hdb.c,v 1.54 2005/05/29 18:12:28 lha Exp $"); + +#ifdef HAVE_DLFCN_H +#include +#endif + +struct hdb_method { + const char *prefix; + krb5_error_code (*create)(krb5_context, HDB **, const char *filename); +}; + +static struct hdb_method methods[] = { +#if HAVE_DB1 || HAVE_DB3 + {"db:", hdb_db_create}, +#endif +#if HAVE_NDBM + {"ndbm:", hdb_ndbm_create}, +#endif +#if defined(OPENLDAP) && !defined(OPENLDAP_MODULE) + {"ldap:", hdb_ldap_create}, +#endif +#if HAVE_DB1 || HAVE_DB3 + {"", hdb_db_create}, +#elif defined(HAVE_NDBM) + {"", hdb_ndbm_create}, +#elif defined(OPENLDAP) && !defined(OPENLDAP_MODULE) + {"", hdb_ldap_create}, +#endif + {NULL, NULL} +}; + +krb5_error_code +hdb_next_enctype2key(krb5_context context, + const hdb_entry *e, + krb5_enctype enctype, + Key **key) +{ + Key *k; + + for (k = *key ? (*key) + 1 : e->keys.val; + k < e->keys.val + e->keys.len; + k++) + if(k->key.keytype == enctype){ + *key = k; + return 0; + } + return KRB5_PROG_ETYPE_NOSUPP; /* XXX */ +} + +krb5_error_code +hdb_enctype2key(krb5_context context, + hdb_entry *e, + krb5_enctype enctype, + Key **key) +{ + *key = NULL; + return hdb_next_enctype2key(context, e, enctype, key); +} + +void +hdb_free_key(Key *key) +{ + memset(key->key.keyvalue.data, + 0, + key->key.keyvalue.length); + free_Key(key); + free(key); +} + + +krb5_error_code +hdb_lock(int fd, int operation) +{ + int i, code = 0; + + for(i = 0; i < 3; i++){ + code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB); + if(code == 0 || errno != EWOULDBLOCK) + break; + sleep(1); + } + if(code == 0) + return 0; + if(errno == EWOULDBLOCK) + return HDB_ERR_DB_INUSE; + return HDB_ERR_CANT_LOCK_DB; +} + +krb5_error_code +hdb_unlock(int fd) +{ + int code; + code = flock(fd, LOCK_UN); + if(code) + return 4711 /* XXX */; + return 0; +} + +void +hdb_free_entry(krb5_context context, hdb_entry *ent) +{ + int i; + + for(i = 0; i < ent->keys.len; ++i) { + Key *k = &ent->keys.val[i]; + + memset (k->key.keyvalue.data, 0, k->key.keyvalue.length); + } + free_hdb_entry(ent); +} + +krb5_error_code +hdb_foreach(krb5_context context, + HDB *db, + unsigned flags, + hdb_foreach_func_t func, + void *data) +{ + krb5_error_code ret; + hdb_entry entry; + ret = db->hdb_firstkey(context, db, flags, &entry); + while(ret == 0){ + ret = (*func)(context, db, &entry, data); + hdb_free_entry(context, &entry); + if(ret == 0) + ret = db->hdb_nextkey(context, db, flags, &entry); + } + if(ret == HDB_ERR_NOENTRY) + ret = 0; + return ret; +} + +krb5_error_code +hdb_check_db_format(krb5_context context, HDB *db) +{ + krb5_data tag; + krb5_data version; + krb5_error_code ret; + unsigned ver; + int foo; + + tag.data = HDB_DB_FORMAT_ENTRY; + tag.length = strlen(tag.data); + ret = (*db->hdb__get)(context, db, tag, &version); + if(ret) + return ret; + foo = sscanf(version.data, "%u", &ver); + krb5_data_free (&version); + if (foo != 1) + return HDB_ERR_BADVERSION; + if(ver != HDB_DB_FORMAT) + return HDB_ERR_BADVERSION; + return 0; +} + +krb5_error_code +hdb_init_db(krb5_context context, HDB *db) +{ + krb5_error_code ret; + krb5_data tag; + krb5_data version; + char ver[32]; + + ret = hdb_check_db_format(context, db); + if(ret != HDB_ERR_NOENTRY) + return ret; + + tag.data = HDB_DB_FORMAT_ENTRY; + tag.length = strlen(tag.data); + snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT); + version.data = ver; + version.length = strlen(version.data) + 1; /* zero terminated */ + ret = (*db->hdb__put)(context, db, 0, tag, version); + return ret; +} + +#ifdef HAVE_DLOPEN + + /* + * Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so, + * looking for the hdb_NAME_create symbol. + */ + +static const struct hdb_method * +find_dynamic_method (krb5_context context, + const char *filename, + const char **rest) +{ + static struct hdb_method method; + struct hdb_so_method *mso; + char *prefix, *path, *symbol; + const char *p; + void *dl; + size_t len; + + p = strchr(filename, ':'); + + /* if no prefix, don't know what module to load, just ignore it */ + if (p == NULL) + return NULL; + + len = p - filename; + *rest = filename + len + 1; + + prefix = strndup(filename, len); + if (prefix == NULL) + krb5_errx(context, 1, "out of memory"); + + if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1) + krb5_errx(context, 1, "out of memory"); + +#ifndef RTLD_NOW +#define RTLD_NOW 0 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + + dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL); + if (dl == NULL) { + krb5_warnx(context, "error trying to load dynamic module %s: %s\n", + path, dlerror()); + free(prefix); + free(path); + return NULL; + } + + if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1) + krb5_errx(context, 1, "out of memory"); + + mso = dlsym(dl, symbol); + if (mso == NULL) { + krb5_warnx(context, "error finding symbol %s in %s: %s\n", + symbol, path, dlerror()); + dlclose(dl); + free(symbol); + free(prefix); + free(path); + return NULL; + } + free(path); + free(symbol); + + if (mso->version != HDB_INTERFACE_VERSION) { + krb5_warnx(context, + "error wrong version in shared module %s " + "version: %d should have been %d\n", + prefix, mso->version, HDB_INTERFACE_VERSION); + dlclose(dl); + free(prefix); + return NULL; + } + + if (mso->create == NULL) { + krb5_errx(context, 1, + "no entry point function in shared mod %s ", + prefix); + dlclose(dl); + free(prefix); + return NULL; + } + + method.create = mso->create; + method.prefix = prefix; + + return &method; +} +#endif /* HAVE_DLOPEN */ + +/* + * find the relevant method for `filename', returning a pointer to the + * rest in `rest'. + * return NULL if there's no such method. + */ + +static const struct hdb_method * +find_method (const char *filename, const char **rest) +{ + const struct hdb_method *h; + + for (h = methods; h->prefix != NULL; ++h) + if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) { + *rest = filename + strlen(h->prefix); + return h; + } + return NULL; +} + +krb5_error_code +hdb_list_builtin(krb5_context context, char **list) +{ + const struct hdb_method *h; + size_t len = 0; + char *buf = NULL; + + for (h = methods; h->prefix != NULL; ++h) { + if (h->prefix[0] == '\0') + continue; + len += strlen(h->prefix) + 2; + } + + len += 1; + buf = malloc(len); + if (buf == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + buf[0] = '\0'; + + for (h = methods; h->prefix != NULL; ++h) { + if (h->prefix[0] == '\0') + continue; + if (h != methods) + strlcat(buf, ", ", len); + strlcat(buf, h->prefix, len); + } + *list = buf; + return 0; +} + +krb5_error_code +hdb_create(krb5_context context, HDB **db, const char *filename) +{ + const struct hdb_method *h; + const char *residual; + + if(filename == NULL) + filename = HDB_DEFAULT_DB; + krb5_add_et_list(context, initialize_hdb_error_table_r); + h = find_method (filename, &residual); +#ifdef HAVE_DLOPEN + if (h == NULL) + h = find_dynamic_method (context, filename, &residual); +#endif + if (h == NULL) + krb5_errx(context, 1, "No database support! (hdb_create)"); + return (*h->create)(context, db, residual); +} diff --git a/source4/heimdal/lib/hdb/hdb.h b/source4/heimdal/lib/hdb/hdb.h new file mode 100644 index 0000000000..481d4ea93d --- /dev/null +++ b/source4/heimdal/lib/hdb/hdb.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id: hdb.h,v 1.33 2003/09/19 00:19:36 lha Exp $ */ + +#ifndef __HDB_H__ +#define __HDB_H__ + +#include + +#include + +enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK }; + +/* flags for various functions */ +#define HDB_F_DECRYPT 1 /* decrypt keys */ +#define HDB_F_REPLACE 2 /* replace entry */ + +/* key usage for master key */ +#define HDB_KU_MKEY 0x484442 + +enum hdb_ent_type{ HDB_ENT_TYPE_CLIENT, HDB_ENT_TYPE_SERVER, HDB_ENT_TYPE_ANY }; + +typedef struct hdb_master_key_data *hdb_master_key; + +typedef struct HDB{ + void *hdb_db; + void *hdb_dbc; + char *hdb_name; + int hdb_master_key_set; + hdb_master_key hdb_master_key; + void *hdb_openp; + + krb5_error_code (*hdb_open)(krb5_context, struct HDB*, int, mode_t); + krb5_error_code (*hdb_close)(krb5_context, struct HDB*); + krb5_error_code (*hdb_fetch)(krb5_context,struct HDB*,unsigned hdb_flags, krb5_const_principal principal, + enum hdb_ent_type ent_type, hdb_entry*); + krb5_error_code (*hdb_store)(krb5_context,struct HDB*,unsigned,hdb_entry*); + krb5_error_code (*hdb_remove)(krb5_context, struct HDB*, hdb_entry*); + krb5_error_code (*hdb_firstkey)(krb5_context, struct HDB*, + unsigned, hdb_entry*); + krb5_error_code (*hdb_nextkey)(krb5_context, struct HDB*, + unsigned, hdb_entry*); + krb5_error_code (*hdb_lock)(krb5_context, struct HDB*, int operation); + krb5_error_code (*hdb_unlock)(krb5_context, struct HDB*); + krb5_error_code (*hdb_rename)(krb5_context, struct HDB*, const char*); + krb5_error_code (*hdb__get)(krb5_context,struct HDB*,krb5_data,krb5_data*); + krb5_error_code (*hdb__put)(krb5_context, struct HDB*, int, + krb5_data, krb5_data); + krb5_error_code (*hdb__del)(krb5_context, struct HDB*, krb5_data); + krb5_error_code (*hdb_destroy)(krb5_context, struct HDB*); +}HDB; + +#define HDB_INTERFACE_VERSION 1 + +struct hdb_so_method { + int version; + const char *prefix; + krb5_error_code (*create)(krb5_context, HDB **, const char *filename); +}; + +#define HDB_DB_DIR "/var/heimdal" +#define HDB_DEFAULT_DB HDB_DB_DIR "/heimdal" +#define HDB_DB_FORMAT_ENTRY "hdb/db-format" + +typedef krb5_error_code (*hdb_foreach_func_t)(krb5_context, HDB*, + hdb_entry*, void*); +extern krb5_kt_ops hdb_kt_ops; + +#include + +#endif /* __HDB_H__ */ diff --git a/source4/heimdal/lib/hdb/hdb_err.et b/source4/heimdal/lib/hdb/hdb_err.et new file mode 100644 index 0000000000..9929a56311 --- /dev/null +++ b/source4/heimdal/lib/hdb/hdb_err.et @@ -0,0 +1,27 @@ +# +# Error messages for the hdb library +# +# This might look like a com_err file, but is not +# +id "$Id: hdb_err.et,v 1.5 2001/01/28 23:05:52 assar Exp $" + +error_table hdb + +prefix HDB_ERR + +index 1 +#error_code INUSE, "Entry already exists in database" +error_code UK_SERROR, "Database store error" +error_code UK_RERROR, "Database read error" +error_code NOENTRY, "No such entry in the database" +error_code DB_INUSE, "Database is locked or in use--try again later" +error_code DB_CHANGED, "Database was modified during read" +error_code RECURSIVELOCK, "Attempt to lock database twice" +error_code NOTLOCKED, "Attempt to unlock database when not locked" +error_code BADLOCKMODE, "Invalid kdb lock mode" +error_code CANT_LOCK_DB, "Insufficient access to lock database" +error_code EXISTS, "Entry already exists in database" +error_code BADVERSION, "Wrong database version" +error_code NO_MKEY, "No correct master key" + +end diff --git a/source4/heimdal/lib/hdb/hdb_locl.h b/source4/heimdal/lib/hdb/hdb_locl.h new file mode 100644 index 0000000000..0d07164bd1 --- /dev/null +++ b/source4/heimdal/lib/hdb/hdb_locl.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1997-2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id: hdb_locl.h,v 1.19 2003/09/10 21:54:58 lha Exp $ */ + +#ifndef __HDB_LOCL_H__ +#define __HDB_LOCL_H__ + +#include + +#include +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_FILE_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#include + +#include "crypto-headers.h" +#include +#include +#include + +#endif /* __HDB_LOCL_H__ */ diff --git a/source4/heimdal/lib/hdb/keys.c b/source4/heimdal/lib/hdb/keys.c new file mode 100644 index 0000000000..c5a2efd758 --- /dev/null +++ b/source4/heimdal/lib/hdb/keys.c @@ -0,0 +1,393 @@ +/* + * Copyright (c) 1997 - 2001, 2003 - 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hdb_locl.h" + +RCSID("$Id: keys.c,v 1.3 2005/03/17 00:42:05 lha Exp $"); + +/* + * free all the memory used by (len, keys) + */ + +void +hdb_free_keys (krb5_context context, int len, Key *keys) +{ + int i; + + for (i = 0; i < len; i++) { + free(keys[i].mkvno); + keys[i].mkvno = NULL; + if (keys[i].salt != NULL) { + free_Salt(keys[i].salt); + free(keys[i].salt); + keys[i].salt = NULL; + } + krb5_free_keyblock_contents(context, &keys[i].key); + } + free (keys); +} + +/* + * for each entry in `default_keys' try to parse it as a sequence + * of etype:salttype:salt, syntax of this if something like: + * [(des|des3|etype):](pw-salt|afs3)[:string], if etype is omitted it + * means all etypes, and if string is omitted is means the default + * string (for that principal). Additional special values: + * v5 == pw-salt, and + * v4 == des:pw-salt: + * afs or afs3 == des:afs3-salt + */ + +/* the 3 DES types must be first */ +static const krb5_enctype all_etypes[] = { + ETYPE_DES_CBC_MD5, + ETYPE_DES_CBC_MD4, + ETYPE_DES_CBC_CRC, + ETYPE_AES256_CTS_HMAC_SHA1_96, + ETYPE_ARCFOUR_HMAC_MD5, + ETYPE_DES3_CBC_SHA1 +}; + +static krb5_error_code +parse_key_set(krb5_context context, const char *key, + krb5_enctype **ret_enctypes, size_t *ret_num_enctypes, + krb5_salt *salt, krb5_principal principal) +{ + const char *p; + char buf[3][256]; + int num_buf = 0; + int i, num_enctypes = 0; + krb5_enctype e; + const krb5_enctype *enctypes = NULL; + krb5_error_code ret; + + p = key; + + *ret_enctypes = NULL; + *ret_num_enctypes = 0; + + /* split p in a list of :-separated strings */ + for(num_buf = 0; num_buf < 3; num_buf++) + if(strsep_copy(&p, ":", buf[num_buf], sizeof(buf[num_buf])) == -1) + break; + + salt->saltvalue.data = NULL; + salt->saltvalue.length = 0; + + for(i = 0; i < num_buf; i++) { + if(enctypes == NULL) { + /* this might be a etype specifier */ + /* XXX there should be a string_to_etypes handling + special cases like `des' and `all' */ + if(strcmp(buf[i], "des") == 0) { + enctypes = all_etypes; + num_enctypes = 3; + continue; + } else if(strcmp(buf[i], "des3") == 0) { + e = ETYPE_DES3_CBC_SHA1; + enctypes = &e; + num_enctypes = 1; + continue; + } else { + ret = krb5_string_to_enctype(context, buf[i], &e); + if (ret == 0) { + enctypes = &e; + num_enctypes = 1; + continue; + } + } + } + + if(salt->salttype == 0) { + /* interpret string as a salt specifier, if no etype + is set, this sets default values */ + /* XXX should perhaps use string_to_salttype, but that + interface sucks */ + if(strcmp(buf[i], "pw-salt") == 0) { + if(enctypes == NULL) { + enctypes = all_etypes; + num_enctypes = sizeof(all_etypes)/sizeof(all_etypes[0]); + } + salt->salttype = KRB5_PW_SALT; + } else if(strcmp(buf[i], "afs3-salt") == 0) { + if(enctypes == NULL) { + enctypes = all_etypes; + num_enctypes = 3; + } + salt->salttype = KRB5_AFS3_SALT; + } + } else { + /* if there is a final string, use it as the string to + salt with, this is mostly useful with null salt for + v4 compat, and a cell name for afs compat */ + salt->saltvalue.data = strdup(buf[i]); + if (salt->saltvalue.data == NULL) { + krb5_set_error_string(context, "malloc out of memory"); + return ENOMEM; + } + salt->saltvalue.length = strlen(buf[i]); + } + } + + if(enctypes == NULL || salt->salttype == 0) { + krb5_set_error_string(context, "bad value for default_keys `%s'", key); + return EINVAL; + } + + /* if no salt was specified make up default salt */ + if(salt->saltvalue.data == NULL) { + if(salt->salttype == KRB5_PW_SALT) + ret = krb5_get_pw_salt(context, principal, salt); + else if(salt->salttype == KRB5_AFS3_SALT) { + krb5_realm *realm = krb5_princ_realm(context, principal); + salt->saltvalue.data = strdup(*realm); + if(salt->saltvalue.data == NULL) { + krb5_set_error_string(context, "out of memory while " + "parsing salt specifiers"); + return ENOMEM; + } + strlwr(salt->saltvalue.data); + salt->saltvalue.length = strlen(*realm); + } + } + + *ret_enctypes = malloc(sizeof(enctypes[0]) * num_enctypes); + if (*ret_enctypes == NULL) { + krb5_free_salt(context, *salt); + krb5_set_error_string(context, "out of memory"); + return ENOMEM; + } + memcpy(*ret_enctypes, enctypes, sizeof(enctypes[0]) * num_enctypes); + *ret_num_enctypes = num_enctypes; + + return 0; +} + +static krb5_error_code +add_enctype_to_key_set(Key **key_set, size_t *nkeyset, + krb5_enctype enctype, krb5_salt *salt) +{ + krb5_error_code ret; + Key key, *tmp; + + memset(&key, 0, sizeof(key)); + + tmp = realloc(*key_set, (*nkeyset + 1) * sizeof((*key_set)[0])); + if (tmp == NULL) + return ENOMEM; + + *key_set = tmp; + + key.key.keytype = enctype; + key.key.keyvalue.length = 0; + key.key.keyvalue.data = NULL; + + if (salt) { + key.salt = malloc(sizeof(*key.salt)); + if (key.salt == NULL) { + free_Key(&key); + return ENOMEM; + } + + key.salt->type = salt->salttype; + krb5_data_zero (&key.salt->salt); + + ret = krb5_data_copy(&key.salt->salt, + salt->saltvalue.data, + salt->saltvalue.length); + if (ret) { + free_Key(&key); + return ret; + } + } else + key.salt = NULL; + + (*key_set)[*nkeyset] = key; + + *nkeyset += 1; + + return 0; +} + + +/* + * Generate the `key_set' from the [kadmin]default_keys statement. If + * `no_salt' is set, salt is not important (and will not be set) since + * its random keys that is going to be created. + */ + +krb5_error_code +hdb_generate_key_set(krb5_context context, krb5_principal principal, + Key **ret_key_set, size_t *nkeyset, int no_salt) +{ + char **ktypes, **kp; + krb5_error_code ret; + Key *k, *key_set; + int i, j; + char *default_keytypes[] = { + "des:pw-salt", + "aes256-cts-hmac-sha1-96:pw-salt", + "des3-cbc-sha1:pw-salt", + "arcfour-hmac-md5:pw-salt", + NULL + }; + + ktypes = krb5_config_get_strings(context, NULL, "kadmin", + "default_keys", NULL); + if (ktypes == NULL) + ktypes = default_keytypes; + + if (ktypes == NULL) + abort(); + + *ret_key_set = key_set = NULL; + *nkeyset = 0; + + ret = 0; + + for(kp = ktypes; kp && *kp; kp++) { + const char *p; + krb5_salt salt; + krb5_enctype *enctypes; + size_t num_enctypes; + + p = *kp; + /* check alias */ + if(strcmp(p, "v5") == 0) + p = "pw-salt"; + else if(strcmp(p, "v4") == 0) + p = "des:pw-salt:"; + else if(strcmp(p, "afs") == 0 || strcmp(p, "afs3") == 0) + p = "des:afs3-salt"; + else if (strcmp(p, "arcfour-hmac-md5") == 0) + p = "arcfour-hmac-md5:pw-salt"; + + memset(&salt, 0, sizeof(salt)); + + ret = parse_key_set(context, p, + &enctypes, &num_enctypes, &salt, principal); + if (ret) { + krb5_warnx(context, "bad value for default_keys `%s'", *kp); + continue; + } + + for (i = 0; i < num_enctypes; i++) { + /* find duplicates */ + for (j = 0; j < *nkeyset; j++) { + + k = &key_set[j]; + + if (k->key.keytype == enctypes[i]) { + if (no_salt) + break; + if (k->salt == NULL && salt.salttype == KRB5_PW_SALT) + break; + if (k->salt->type == salt.salttype && + k->salt->salt.length == salt.saltvalue.length && + memcmp(k->salt->salt.data, salt.saltvalue.data, + salt.saltvalue.length) == 0) + break; + } + } + /* not a duplicate, lets add it */ + if (j == *nkeyset) { + ret = add_enctype_to_key_set(&key_set, nkeyset, enctypes[i], + no_salt ? NULL : &salt); + if (ret) { + free(enctypes); + krb5_free_salt(context, salt); + goto out; + } + } + } + free(enctypes); + krb5_free_salt(context, salt); + } + + out: + if (ret) { + krb5_warn(context, ret, + "failed to parse the [kadmin]default_keys values"); + + for (i = 0; i < *nkeyset; i++) + free_Key(&key_set[i]); + free(key_set); + } else if (*nkeyset == 0) { + krb5_warnx(context, + "failed to parse any of the [kadmin]default_keys values"); + ret = EINVAL; /* XXX */ + } + + *ret_key_set = key_set; + + return ret; +} + + +krb5_error_code +hdb_generate_key_set_password(krb5_context context, + krb5_principal principal, + const char *password, + Key **keys, size_t *num_keys) +{ + krb5_error_code ret; + int i; + + ret = hdb_generate_key_set(context, principal, + keys, num_keys, 0); + if (ret) + return ret; + + for (i = 0; i < (*num_keys); i++) { + krb5_salt salt; + + salt.salttype = (*keys)[i].salt->type; + salt.saltvalue.length = (*keys)[i].salt->salt.length; + salt.saltvalue.data = (*keys)[i].salt->salt.data; + + ret = krb5_string_to_key_salt (context, + (*keys)[i].key.keytype, + password, + salt, + &(*keys)[i].key); + + if(ret) + break; + } + + if(ret) { + hdb_free_keys (context, *num_keys, *keys); + return ret; + } + return ret; +} diff --git a/source4/heimdal/lib/hdb/ndbm.c b/source4/heimdal/lib/hdb/ndbm.c new file mode 100644 index 0000000000..588ff80728 --- /dev/null +++ b/source4/heimdal/lib/hdb/ndbm.c @@ -0,0 +1,369 @@ +/* + * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hdb_locl.h" + +RCSID("$Id: ndbm.c,v 1.35 2005/06/23 13:37:57 lha Exp $"); + +#if HAVE_NDBM + +#if defined(HAVE_GDBM_NDBM_H) +#include +#elif defined(HAVE_NDBM_H) +#include +#elif defined(HAVE_DBM_H) +#include +#endif + +struct ndbm_db { + DBM *db; + int lock_fd; +}; + +static krb5_error_code +NDBM_destroy(krb5_context context, HDB *db) +{ + krb5_error_code ret; + + ret = hdb_clear_master_key (context, db); + free(db->hdb_name); + free(db); + return 0; +} + +static krb5_error_code +NDBM_lock(krb5_context context, HDB *db, int operation) +{ + struct ndbm_db *d = db->hdb_db; + return hdb_lock(d->lock_fd, operation); +} + +static krb5_error_code +NDBM_unlock(krb5_context context, HDB *db) +{ + struct ndbm_db *d = db->hdb_db; + return hdb_unlock(d->lock_fd); +} + +static krb5_error_code +NDBM_seq(krb5_context context, HDB *db, + unsigned flags, hdb_entry *entry, int first) + +{ + struct ndbm_db *d = (struct ndbm_db *)db->hdb_db; + datum key, value; + krb5_data key_data, data; + krb5_error_code ret = 0; + + if(first) + key = dbm_firstkey(d->db); + else + key = dbm_nextkey(d->db); + if(key.dptr == NULL) + return HDB_ERR_NOENTRY; + key_data.data = key.dptr; + key_data.length = key.dsize; + ret = db->hdb_lock(context, db, HDB_RLOCK); + if(ret) return ret; + value = dbm_fetch(d->db, key); + db->hdb_unlock(context, db); + data.data = value.dptr; + data.length = value.dsize; + if(hdb_value2entry(context, &data, entry)) + return NDBM_seq(context, db, flags, entry, 0); + if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { + ret = hdb_unseal_keys (context, db, entry); + if (ret) + hdb_free_entry (context, entry); + } + if (entry->principal == NULL) { + entry->principal = malloc (sizeof(*entry->principal)); + if (entry->principal == NULL) { + ret = ENOMEM; + hdb_free_entry (context, entry); + krb5_set_error_string(context, "malloc: out of memory"); + } else { + hdb_key2principal (context, &key_data, entry->principal); + } + } + return ret; +} + + +static krb5_error_code +NDBM_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) +{ + return NDBM_seq(context, db, flags, entry, 1); +} + + +static krb5_error_code +NDBM_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) +{ + return NDBM_seq(context, db, flags, entry, 0); +} + +static krb5_error_code +NDBM_rename(krb5_context context, HDB *db, const char *new_name) +{ + /* XXX this function will break */ + struct ndbm_db *d = db->hdb_db; + + int ret; + char *old_dir, *old_pag, *new_dir, *new_pag; + char *new_lock; + int lock_fd; + + /* lock old and new databases */ + ret = db->hdb_lock(context, db, HDB_WLOCK); + if(ret) + return ret; + asprintf(&new_lock, "%s.lock", new_name); + if(new_lock == NULL) { + db->hdb_unlock(context, db); + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + lock_fd = open(new_lock, O_RDWR | O_CREAT, 0600); + if(lock_fd < 0) { + ret = errno; + db->hdb_unlock(context, db); + krb5_set_error_string(context, "open(%s): %s", new_lock, + strerror(ret)); + free(new_lock); + return ret; + } + free(new_lock); + ret = hdb_lock(lock_fd, HDB_WLOCK); + if(ret) { + db->hdb_unlock(context, db); + close(lock_fd); + return ret; + } + + asprintf(&old_dir, "%s.dir", db->hdb_name); + asprintf(&old_pag, "%s.pag", db->hdb_name); + asprintf(&new_dir, "%s.dir", new_name); + asprintf(&new_pag, "%s.pag", new_name); + + ret = rename(old_dir, new_dir) || rename(old_pag, new_pag); + free(old_dir); + free(old_pag); + free(new_dir); + free(new_pag); + hdb_unlock(lock_fd); + db->hdb_unlock(context, db); + + if(ret) { + ret = errno; + close(lock_fd); + krb5_set_error_string(context, "rename: %s", strerror(ret)); + return ret; + } + + close(d->lock_fd); + d->lock_fd = lock_fd; + + free(db->hdb_name); + db->hdb_name = strdup(new_name); + return 0; +} + +static krb5_error_code +NDBM__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply) +{ + struct ndbm_db *d = (struct ndbm_db *)db->hdb_db; + datum k, v; + int code; + + k.dptr = key.data; + k.dsize = key.length; + code = db->hdb_lock(context, db, HDB_RLOCK); + if(code) + return code; + v = dbm_fetch(d->db, k); + db->hdb_unlock(context, db); + if(v.dptr == NULL) + return HDB_ERR_NOENTRY; + + krb5_data_copy(reply, v.dptr, v.dsize); + return 0; +} + +static krb5_error_code +NDBM__put(krb5_context context, HDB *db, int replace, + krb5_data key, krb5_data value) +{ + struct ndbm_db *d = (struct ndbm_db *)db->hdb_db; + datum k, v; + int code; + + k.dptr = key.data; + k.dsize = key.length; + v.dptr = value.data; + v.dsize = value.length; + + code = db->hdb_lock(context, db, HDB_WLOCK); + if(code) + return code; + code = dbm_store(d->db, k, v, replace ? DBM_REPLACE : DBM_INSERT); + db->hdb_unlock(context, db); + if(code == 1) + return HDB_ERR_EXISTS; + if (code < 0) + return code; + return 0; +} + +static krb5_error_code +NDBM__del(krb5_context context, HDB *db, krb5_data key) +{ + struct ndbm_db *d = (struct ndbm_db *)db->hdb_db; + datum k; + int code; + krb5_error_code ret; + + k.dptr = key.data; + k.dsize = key.length; + ret = db->hdb_lock(context, db, HDB_WLOCK); + if(ret) return ret; + code = dbm_delete(d->db, k); + db->hdb_unlock(context, db); + if(code < 0) + return errno; + return 0; +} + + +static krb5_error_code +NDBM_close(krb5_context context, HDB *db) +{ + struct ndbm_db *d = db->hdb_db; + dbm_close(d->db); + close(d->lock_fd); + free(d); + return 0; +} + +static krb5_error_code +NDBM_open(krb5_context context, HDB *db, int flags, mode_t mode) +{ + krb5_error_code ret; + struct ndbm_db *d = malloc(sizeof(*d)); + char *lock_file; + + if(d == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + asprintf(&lock_file, "%s.lock", (char*)db->hdb_name); + if(lock_file == NULL) { + free(d); + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + d->db = dbm_open((char*)db->hdb_name, flags, mode); + if(d->db == NULL){ + ret = errno; + free(d); + free(lock_file); + krb5_set_error_string(context, "dbm_open(%s): %s", db->hdb_name, + strerror(ret)); + return ret; + } + d->lock_fd = open(lock_file, O_RDWR | O_CREAT, 0600); + if(d->lock_fd < 0){ + ret = errno; + dbm_close(d->db); + free(d); + krb5_set_error_string(context, "open(%s): %s", lock_file, + strerror(ret)); + free(lock_file); + return ret; + } + free(lock_file); + db->hdb_db = d; + if((flags & O_ACCMODE) == O_RDONLY) + ret = hdb_check_db_format(context, db); + else + ret = hdb_init_db(context, db); + if(ret == HDB_ERR_NOENTRY) + return 0; + if (ret) { + NDBM_close(context, db); + krb5_set_error_string(context, "hdb_open: failed %s database %s", + (flags & O_ACCMODE) == O_RDONLY ? + "checking format of" : "initialize", + db->hdb_name); + } + return ret; +} + +krb5_error_code +hdb_ndbm_create(krb5_context context, HDB **db, + const char *filename) +{ + *db = malloc(sizeof(**db)); + if (*db == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + + (*db)->hdb_db = NULL; + (*db)->hdb_name = strdup(filename); + if ((*db)->hdb_name == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + free(*db); + *db = NULL; + return ENOMEM; + } + (*db)->hdb_master_key_set = 0; + (*db)->hdb_openp = 0; + (*db)->hdb_open = NDBM_open; + (*db)->hdb_close = NDBM_close; + (*db)->hdb_fetch = _hdb_fetch; + (*db)->hdb_store = _hdb_store; + (*db)->hdb_remove = _hdb_remove; + (*db)->hdb_firstkey = NDBM_firstkey; + (*db)->hdb_nextkey= NDBM_nextkey; + (*db)->hdb_lock = NDBM_lock; + (*db)->hdb_unlock = NDBM_unlock; + (*db)->hdb_rename = NDBM_rename; + (*db)->hdb__get = NDBM__get; + (*db)->hdb__put = NDBM__put; + (*db)->hdb__del = NDBM__del; + (*db)->hdb_destroy = NDBM_destroy; + return 0; +} + +#endif /* HAVE_NDBM */ -- cgit