summaryrefslogtreecommitdiff
path: root/source4/heimdal/lib/hdb
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2005-08-20 06:00:50 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:33:35 -0500
commit55f5453bc81d9a3a4fe67ff0a6ba528d8d0f7984 (patch)
tree19d28e75e07ead51f0096328ddf913726cfbd59e /source4/heimdal/lib/hdb
parentb456bfa0155ea70ca60590a83e19232c63ec1ac1 (diff)
downloadsamba-55f5453bc81d9a3a4fe67ff0a6ba528d8d0f7984.tar.gz
samba-55f5453bc81d9a3a4fe67ff0a6ba528d8d0f7984.tar.bz2
samba-55f5453bc81d9a3a4fe67ff0a6ba528d8d0f7984.zip
r9413: Bring Samba4 back up to date with lorikeet-heimdal.
Delete test_crypto_wrapping.c, previously included but unbuilt. Andrew Bartlett (This used to be commit d5fb30fb0cef330e0947969f0c9afc1f58fc4c7d)
Diffstat (limited to 'source4/heimdal/lib/hdb')
-rw-r--r--source4/heimdal/lib/hdb/ext.c366
-rw-r--r--source4/heimdal/lib/hdb/hdb-private.h28
-rw-r--r--source4/heimdal/lib/hdb/hdb-protos.h59
-rw-r--r--source4/heimdal/lib/hdb/hdb.asn150
-rw-r--r--source4/heimdal/lib/hdb/hdb.h17
-rw-r--r--source4/heimdal/lib/hdb/hdb_err.et3
-rw-r--r--source4/heimdal/lib/hdb/mkey.c595
7 files changed, 1105 insertions, 13 deletions
diff --git a/source4/heimdal/lib/hdb/ext.c b/source4/heimdal/lib/hdb/ext.c
new file mode 100644
index 0000000000..850b23fb04
--- /dev/null
+++ b/source4/heimdal/lib/hdb/ext.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2004 - 2005 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"
+#include <der.h>
+
+RCSID("$Id: ext.c,v 1.1 2005/08/11 20:49:31 lha Exp $");
+
+krb5_error_code
+hdb_entry_check_mandatory(krb5_context context, const hdb_entry *ent)
+{
+ int i;
+
+ if (ent->extensions == NULL)
+ return 0;
+
+ /*
+ * check for unknown extensions and if they where tagged mandatory
+ */
+
+ for (i = 0; i < ent->extensions->len; i++) {
+ if (ent->extensions->val[i].data.element !=
+ choice_HDB_extension_data_asn1_ellipsis)
+ continue;
+ if (ent->extensions->val[i].mandatory) {
+ krb5_set_error_string(context, "Principal have unknown "
+ "mandatory extension");
+ return HDB_ERR_MANDATORY_OPTION;
+ }
+ }
+ return 0;
+}
+
+HDB_extension *
+hdb_find_extension(const hdb_entry *entry, int type)
+{
+ int i;
+
+ if (entry->extensions == NULL)
+ return NULL;
+
+ for (i = 0; i < entry->extensions->len; i++)
+ if (entry->extensions->val[i].data.element == type)
+ return &entry->extensions->val[i];
+ return NULL;
+}
+
+/*
+ * Replace the extension `ext' in `entry'. Make a copy of the
+ * extension, so the caller must still free `ext' on both success and
+ * failure. Returns 0 or error code.
+ */
+
+krb5_error_code
+hdb_replace_extension(krb5_context context,
+ hdb_entry *entry,
+ const HDB_extension *ext)
+{
+ HDB_extension *ext2;
+ HDB_extension *es;
+ int ret;
+
+ ext2 = NULL;
+
+ if (entry->extensions == NULL) {
+ entry->extensions = calloc(1, sizeof(*entry->extensions));
+ if (entry->extensions == NULL) {
+ krb5_set_error_string(context, "malloc: out of memory");
+ return ENOMEM;
+ }
+ } else if (ext->data.element != choice_HDB_extension_data_asn1_ellipsis) {
+ ext2 = hdb_find_extension(entry, ext->data.element);
+ } else {
+ /*
+ * This is an unknown extention, and we are asked to replace a
+ * possible entry in `entry' that is of the same type. This
+ * might seem impossible, but ASN.1 CHOICE comes to our
+ * rescue. The first tag in each branch in the CHOICE is
+ * unique, so just find the element in the list that have the
+ * same tag was we are putting into the list.
+ */
+ Der_class replace_class, list_class;
+ Der_type replace_type, list_type;
+ unsigned int replace_tag, list_tag;
+ size_t size;
+ int i;
+
+ ret = der_get_tag(ext->data.u.asn1_ellipsis.data,
+ ext->data.u.asn1_ellipsis.length,
+ &replace_class, &replace_type, &replace_tag,
+ &size);
+ if (ret) {
+ krb5_set_error_string(context, "hdb: failed to decode "
+ "replacement hdb extention");
+ return ret;
+ }
+
+ for (i = 0; i < entry->extensions->len; i++) {
+ HDB_extension *ext3 = &entry->extensions->val[i];
+
+ if (ext3->data.element != choice_HDB_extension_data_asn1_ellipsis)
+ continue;
+
+ ret = der_get_tag(ext3->data.u.asn1_ellipsis.data,
+ ext3->data.u.asn1_ellipsis.length,
+ &list_class, &list_type, &list_tag,
+ &size);
+ if (ret) {
+ krb5_set_error_string(context, "hdb: failed to decode "
+ "present hdb extention");
+ return ret;
+ }
+
+ if (MAKE_TAG(replace_class,replace_type,replace_type) ==
+ MAKE_TAG(list_class,list_type,list_type)) {
+ ext2 = ext3;
+ break;
+ }
+ }
+ }
+
+ if (ext2) {
+ free_HDB_extension(ext2);
+ ret = copy_HDB_extension(ext, ext2);
+ if (ret)
+ krb5_set_error_string(context, "hdb: failed to copy replacement "
+ "hdb extention");
+ return ret;
+ }
+
+ es = realloc(entry->extensions->val,
+ (entry->extensions->len+1)*sizeof(entry->extensions->val[0]));
+ if (es == NULL) {
+ krb5_set_error_string(context, "malloc: out of memory");
+ return ENOMEM;
+ }
+ entry->extensions->val = es;
+
+ ret = copy_HDB_extension(ext,
+ &entry->extensions->val[entry->extensions->len]);
+ if (ret == 0) {
+ entry->extensions->len++;
+ krb5_set_error_string(context, "hdb: failed to copy new extension");
+ }
+
+ return ret;
+}
+
+krb5_error_code
+hdb_clear_extension(krb5_context context,
+ hdb_entry *entry,
+ int type)
+{
+ int i;
+
+ if (entry->extensions == NULL)
+ return 0;
+
+ for (i = 0; i < entry->extensions->len; i++) {
+ if (entry->extensions->val[i].data.element == type) {
+ free_HDB_extension(&entry->extensions->val[i]);
+ memmove(&entry->extensions->val[i],
+ &entry->extensions->val[i + 1],
+ sizeof(entry->extensions->val[i]) * (entry->extensions->len - i - 1));
+ entry->extensions->len--;
+ }
+ }
+ if (entry->extensions->len == 0) {
+ free(entry->extensions->val);
+ free(entry->extensions);
+ entry->extensions = NULL;
+ }
+
+ return 0;
+}
+
+
+krb5_error_code
+hdb_entry_get_pkinit_acl(const hdb_entry *entry, const HDB_Ext_PKINIT_acl **a)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_acl);
+ if (ext)
+ *a = &ext->data.u.pkinit_acl;
+ else
+ *a = NULL;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change);
+ if (ext)
+ *t = ext->data.u.last_pw_change;
+ else
+ *t = 0;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_entry_set_pw_change_time(krb5_context context,
+ hdb_entry *entry,
+ time_t t)
+{
+ HDB_extension ext;
+
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_last_pw_change;
+ if (t == 0)
+ t = time(NULL);
+ ext.data.u.last_pw_change = t;
+
+ return hdb_replace_extension(context, entry, &ext);
+}
+
+int
+hdb_entry_get_password(krb5_context context, HDB *db,
+ const hdb_entry *entry, char **p)
+{
+ HDB_extension *ext;
+ int ret;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_password);
+ if (ext) {
+ heim_utf8_string str;
+ heim_octet_string pw;
+
+ if (db->hdb_master_key_set && ext->data.u.password.mkvno) {
+ hdb_master_key key;
+
+ key = _hdb_find_master_key(ext->data.u.password.mkvno,
+ db->hdb_master_key);
+
+ if (key == NULL) {
+ krb5_set_error_string(context, "master key %d missing",
+ *ext->data.u.password.mkvno);
+ return HDB_ERR_NO_MKEY;
+ }
+
+ ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
+ ext->data.u.password.password.data,
+ ext->data.u.password.password.length,
+ &pw);
+ } else {
+ ret = copy_octet_string(&ext->data.u.password.password, &pw);
+ }
+ if (ret) {
+ krb5_clear_error_string(context);
+ return ret;
+ }
+
+ str = pw.data;
+ if (str[pw.length - 1] != '\0') {
+ krb5_set_error_string(context, "password malformated");
+ return EINVAL;
+ }
+
+ *p = strdup(str);
+
+ free_octet_string(&pw);
+ if (*p == NULL) {
+ krb5_set_error_string(context, "malloc: out of memory");
+ return ENOMEM;
+ }
+ return 0;
+ }
+ krb5_set_error_string(context, "password attribute not found");
+ return ENOENT;
+}
+
+int
+hdb_entry_set_password(krb5_context context, HDB *db,
+ hdb_entry *entry, const char *p)
+{
+ HDB_extension ext;
+ hdb_master_key key;
+ int ret;
+
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_password;
+
+ if (db->hdb_master_key_set) {
+
+ key = _hdb_find_master_key(NULL, db->hdb_master_key);
+ if (key == NULL) {
+ krb5_set_error_string(context, "hdb_entry_set_password: "
+ "failed to find masterkey");
+ return HDB_ERR_NO_MKEY;
+ }
+
+ ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
+ p, strlen(p) + 1,
+ &ext.data.u.password.password);
+ if (ret)
+ return ret;
+
+ ext.data.u.password.mkvno =
+ malloc(sizeof(*ext.data.u.password.mkvno));
+ if (ext.data.u.password.mkvno == NULL) {
+ free_HDB_extension(&ext);
+ krb5_set_error_string(context, "malloc: out of memory");
+ return ENOMEM;
+ }
+ *ext.data.u.password.mkvno = _hdb_mkey_version(key);
+
+ } else {
+ ext.data.u.password.mkvno = NULL;
+
+ ret = krb5_data_copy(&ext.data.u.password.password,
+ p, strlen(p) + 1);
+ if (ret) {
+ krb5_set_error_string(context, "malloc: out of memory");
+ free_HDB_extension(&ext);
+ return ret;
+ }
+ }
+
+ ret = hdb_replace_extension(context, entry, &ext);
+
+ free_HDB_extension(&ext);
+
+ return ret;
+}
+
+int
+hdb_entry_clear_password(krb5_context context, hdb_entry *entry)
+{
+ return hdb_clear_extension(context, entry,
+ choice_HDB_extension_data_password);
+}
diff --git a/source4/heimdal/lib/hdb/hdb-private.h b/source4/heimdal/lib/hdb/hdb-private.h
index a2b96bb047..7baa944053 100644
--- a/source4/heimdal/lib/hdb/hdb-private.h
+++ b/source4/heimdal/lib/hdb/hdb-private.h
@@ -9,10 +9,36 @@ _hdb_fetch (
krb5_context /*context*/,
HDB */*db*/,
unsigned /*flags*/,
- krb5_principal /*principal*/,
+ krb5_const_principal /*principal*/,
enum hdb_ent_type /*ent_type*/,
hdb_entry */*entry*/);
+hdb_master_key
+_hdb_find_master_key (
+ u_int32_t */*mkvno*/,
+ hdb_master_key /*mkey*/);
+
+int
+_hdb_mkey_decrypt (
+ krb5_context /*context*/,
+ hdb_master_key /*key*/,
+ krb5_key_usage /*usage*/,
+ void */*ptr*/,
+ size_t /*size*/,
+ krb5_data */*res*/);
+
+int
+_hdb_mkey_encrypt (
+ krb5_context /*context*/,
+ hdb_master_key /*key*/,
+ krb5_key_usage /*usage*/,
+ const void */*ptr*/,
+ size_t /*size*/,
+ krb5_data */*res*/);
+
+int
+_hdb_mkey_version (hdb_master_key /*mkey*/);
+
krb5_error_code
_hdb_remove (
krb5_context /*context*/,
diff --git a/source4/heimdal/lib/hdb/hdb-protos.h b/source4/heimdal/lib/hdb/hdb-protos.h
index 886d48e5bd..799f013eba 100644
--- a/source4/heimdal/lib/hdb/hdb-protos.h
+++ b/source4/heimdal/lib/hdb/hdb-protos.h
@@ -20,6 +20,12 @@ hdb_check_db_format (
HDB */*db*/);
krb5_error_code
+hdb_clear_extension (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/,
+ int /*type*/);
+
+krb5_error_code
hdb_clear_master_key (
krb5_context /*context*/,
HDB */*db*/);
@@ -56,6 +62,51 @@ hdb_entry2value (
krb5_data */*value*/);
krb5_error_code
+hdb_entry_check_mandatory (
+ krb5_context /*context*/,
+ const hdb_entry */*ent*/);
+
+int
+hdb_entry_clear_password (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/);
+
+int
+hdb_entry_get_password (
+ krb5_context /*context*/,
+ HDB */*db*/,
+ const hdb_entry */*entry*/,
+ char **/*p*/);
+
+krb5_error_code
+hdb_entry_get_pkinit_acl (
+ const hdb_entry */*entry*/,
+ const HDB_Ext_PKINIT_acl **/*a*/);
+
+krb5_error_code
+hdb_entry_get_pw_change_time (
+ const hdb_entry */*entry*/,
+ time_t */*t*/);
+
+int
+hdb_entry_set_password (
+ krb5_context /*context*/,
+ HDB */*db*/,
+ hdb_entry */*entry*/,
+ const char */*p*/);
+
+krb5_error_code
+hdb_entry_set_pw_change_time (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/,
+ time_t /*t*/);
+
+HDB_extension *
+hdb_find_extension (
+ const hdb_entry */*entry*/,
+ int /*type*/);
+
+krb5_error_code
hdb_foreach (
krb5_context /*context*/,
HDB */*db*/,
@@ -141,7 +192,7 @@ hdb_next_enctype2key (
int
hdb_principal2key (
krb5_context /*context*/,
- krb5_principal /*p*/,
+ krb5_const_principal /*p*/,
krb5_data */*key*/);
krb5_error_code
@@ -166,6 +217,12 @@ hdb_read_master_key (
hdb_master_key */*mkey*/);
krb5_error_code
+hdb_replace_extension (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/,
+ const HDB_extension */*ext*/);
+
+krb5_error_code
hdb_seal_key (
krb5_context /*context*/,
HDB */*db*/,
diff --git a/source4/heimdal/lib/hdb/hdb.asn1 b/source4/heimdal/lib/hdb/hdb.asn1
index 770acf4dce..c8a1a34b4f 100644
--- a/source4/heimdal/lib/hdb/hdb.asn1
+++ b/source4/heimdal/lib/hdb/hdb.asn1
@@ -1,4 +1,4 @@
--- $Id: hdb.asn1,v 1.12 2004/11/10 18:50:27 lha Exp $
+-- $Id: hdb.asn1,v 1.13 2005/08/11 13:15:44 lha Exp $
HDB DEFINITIONS ::=
BEGIN
@@ -50,6 +50,51 @@ GENERATION ::= SEQUENCE {
gen[2] INTEGER (0..4294967295) -- generation number
}
+HDB-Ext-PKINIT-acl ::= SEQUENCE OF SEQUENCE {
+ subject[0] UTF8String,
+ issuer[1] UTF8String
+}
+
+HDB-Ext-PKINIT-certificate ::= SEQUENCE OF OCTET STRING
+
+HDB-Ext-Constrained-delegation-acl ::= SEQUENCE OF Principal
+
+-- hdb-ext-referrals ::= PA-SERVER-REFERRAL-DATA
+
+HDB-Ext-Lan-Manager-OWF ::= OCTET STRING
+
+HDB-Ext-Password ::= SEQUENCE {
+ mkvno[0] INTEGER (0..4294967295) OPTIONAL, -- master key version number
+ password OCTET STRING
+}
+
+HDB-Ext-Aliases ::= SEQUENCE {
+ case-insensitive[0] BOOLEAN, -- case insensitive name allowed
+ aliases[1] SEQUENCE OF Principal -- all names, inc primary
+}
+
+
+HDB-extension ::= SEQUENCE {
+ mandatory[0] BOOLEAN, -- kdc MUST understand this extension,
+ -- if not the whole entry must
+ -- be rejected
+ data[1] CHOICE {
+ pkinit-acl[0] HDB-Ext-PKINIT-acl,
+ pkinit-cert[1] HDB-Ext-PKINIT-certificate,
+ allowed-to-delegate-to[2] HDB-Ext-Constrained-delegation-acl,
+-- referral-info[3] HDB-Ext-Referrals,
+ lm-owf[4] HDB-Ext-Lan-Manager-OWF,
+ password[5] HDB-Ext-Password,
+ aliases[6] HDB-Ext-Aliases,
+ last-pw-change[7] KerberosTime,
+ ...
+ },
+ ...
+}
+
+HDB-extensions ::= SEQUENCE OF HDB-extension
+
+
hdb_entry ::= SEQUENCE {
principal[0] Principal OPTIONAL, -- this is optional only
-- for compatibility with libkrb5
@@ -64,7 +109,8 @@ hdb_entry ::= SEQUENCE {
max-renew[9] INTEGER (0..4294967295) OPTIONAL,
flags[10] HDBFlags,
etypes[11] SEQUENCE OF INTEGER (0..4294967295) OPTIONAL,
- generation[12] GENERATION OPTIONAL
+ generation[12] GENERATION OPTIONAL,
+ extensions[13] HDB-extensions OPTIONAL
}
END
diff --git a/source4/heimdal/lib/hdb/hdb.h b/source4/heimdal/lib/hdb/hdb.h
index 481d4ea93d..fe86f0ae72 100644
--- a/source4/heimdal/lib/hdb/hdb.h
+++ b/source4/heimdal/lib/hdb/hdb.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -31,20 +31,21 @@
* SUCH DAMAGE.
*/
-/* $Id: hdb.h,v 1.33 2003/09/19 00:19:36 lha Exp $ */
+/* $Id: hdb.h,v 1.35 2005/08/11 13:16:44 lha Exp $ */
#ifndef __HDB_H__
#define __HDB_H__
#include <hdb_err.h>
+#include <heim_asn1.h>
#include <hdb_asn1.h>
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 */
+#define HDB_F_DECRYPT 1 /* decrypt keys */
+#define HDB_F_REPLACE 2 /* replace entry */
/* key usage for master key */
#define HDB_KU_MKEY 0x484442
@@ -68,20 +69,20 @@ typedef struct HDB{
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*);
+ unsigned, hdb_entry*);
krb5_error_code (*hdb_nextkey)(krb5_context, struct HDB*,
- unsigned, hdb_entry*);
+ 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_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
+#define HDB_INTERFACE_VERSION 2
struct hdb_so_method {
int version;
diff --git a/source4/heimdal/lib/hdb/hdb_err.et b/source4/heimdal/lib/hdb/hdb_err.et
index 9929a56311..f2636b2fea 100644
--- a/source4/heimdal/lib/hdb/hdb_err.et
+++ b/source4/heimdal/lib/hdb/hdb_err.et
@@ -3,7 +3,7 @@
#
# 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 $"
+id "$Id: hdb_err.et,v 1.6 2005/08/11 13:17:22 lha Exp $"
error_table hdb
@@ -23,5 +23,6 @@ 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"
+error_code MANDATORY_OPTION, "Entry contains unknown mandatory extension"
end
diff --git a/source4/heimdal/lib/hdb/mkey.c b/source4/heimdal/lib/hdb/mkey.c
new file mode 100644
index 0000000000..9e04dc6d8d
--- /dev/null
+++ b/source4/heimdal/lib/hdb/mkey.c
@@ -0,0 +1,595 @@
+/*
+ * Copyright (c) 2000 - 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"
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+RCSID("$Id: mkey.c,v 1.20 2005/08/10 08:41:03 lha Exp $");
+
+struct hdb_master_key_data {
+ krb5_keytab_entry keytab;
+ krb5_crypto crypto;
+ struct hdb_master_key_data *next;
+};
+
+void
+hdb_free_master_key(krb5_context context, hdb_master_key mkey)
+{
+ struct hdb_master_key_data *ptr;
+ while(mkey) {
+ krb5_kt_free_entry(context, &mkey->keytab);
+ if (mkey->crypto)
+ krb5_crypto_destroy(context, mkey->crypto);
+ ptr = mkey;
+ mkey = mkey->next;
+ free(ptr);
+ }
+}
+
+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 ret;
+
+ *mkey = calloc(1, sizeof(**mkey));
+ if(*mkey == NULL) {
+ krb5_set_error_string(context, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*mkey)->keytab.vno = kvno;
+ ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal);
+ if(ret)
+ goto fail;
+ ret = krb5_copy_keyblock_contents(context, key, &(*mkey)->keytab.keyblock);
+ if(ret)
+ goto fail;
+ if(etype != 0)
+ (*mkey)->keytab.keyblock.keytype = etype;
+ (*mkey)->keytab.timestamp = time(NULL);
+ ret = krb5_crypto_init(context, key, etype, &(*mkey)->crypto);
+ if(ret)
+ goto fail;
+ return 0;
+ fail:
+ hdb_free_master_key(context, *mkey);
+ *mkey = NULL;
+ return ret;
+}
+
+krb5_error_code
+hdb_add_master_key(krb5_context context, krb5_keyblock *key,
+ hdb_master_key *inout)
+{
+ int vno = 0;
+ hdb_master_key p;
+ krb5_error_code ret;
+
+ for(p = *inout; p; p = p->next)
+ vno = max(vno, p->keytab.vno);
+ vno++;
+ ret = hdb_process_master_key(context, vno, key, 0, &p);
+ if(ret)
+ return ret;
+ p->next = *inout;
+ *inout = p;
+ return 0;
+}
+
+static krb5_error_code
+read_master_keytab(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ krb5_error_code ret;
+ krb5_keytab id;
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry entry;
+ hdb_master_key p;
+
+ ret = krb5_kt_resolve(context, filename, &id);
+ if(ret)
+ return ret;
+
+ ret = krb5_kt_start_seq_get(context, id, &cursor);
+ if(ret)
+ goto out;
+ *mkey = NULL;
+ while(krb5_kt_next_entry(context, id, &entry, &cursor) == 0) {
+ p = calloc(1, sizeof(*p));
+ p->keytab = entry;
+ ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
+ p->next = *mkey;
+ *mkey = p;
+ }
+ krb5_kt_end_seq_get(context, id, &cursor);
+ out:
+ krb5_kt_close(context, id);
+ return ret;
+}
+
+/* read a MIT master keyfile */
+static krb5_error_code
+read_master_mit(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ int fd;
+ krb5_error_code ret;
+ krb5_storage *sp;
+ int16_t enctype;
+ krb5_keyblock key;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if(fd < 0) {
+ int save_errno = errno;
+ krb5_set_error_string(context, "failed to open %s: %s", filename,
+ strerror(save_errno));
+ return save_errno;
+ }
+ sp = krb5_storage_from_fd(fd);
+ if(sp == NULL) {
+ close(fd);
+ return errno;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_HOST_BYTEORDER);
+#if 0
+ /* could possibly use ret_keyblock here, but do it with more
+ checks for now */
+ ret = krb5_ret_keyblock(sp, &key);
+#else
+ ret = krb5_ret_int16(sp, &enctype);
+ if((htons(enctype) & 0xff00) == 0x3000) {
+ krb5_set_error_string(context, "unknown keytype in %s: %#x, expected %#x",
+ filename, htons(enctype), 0x3000);
+ ret = HEIM_ERR_BAD_MKEY;
+ goto out;
+ }
+ key.keytype = enctype;
+ ret = krb5_ret_data(sp, &key.keyvalue);
+ if(ret)
+ goto out;
+#endif
+ ret = hdb_process_master_key(context, 0, &key, 0, mkey);
+ krb5_free_keyblock_contents(context, &key);
+ out:
+ krb5_storage_free(sp);
+ close(fd);
+ return ret;
+}
+
+/* read an old master key file */
+static krb5_error_code
+read_master_encryptionkey(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ int fd;
+ krb5_keyblock key;
+ krb5_error_code ret;
+ unsigned char buf[256];
+ ssize_t len;
+ size_t ret_len;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if(fd < 0) {
+ int save_errno = errno;
+ krb5_set_error_string(context, "failed to open %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ len = read(fd, buf, sizeof(buf));
+ close(fd);
+ if(len < 0) {
+ int save_errno = errno;
+ krb5_set_error_string(context, "error reading %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ ret = decode_EncryptionKey(buf, len, &key, &ret_len);
+ memset(buf, 0, sizeof(buf));
+ if(ret)
+ return ret;
+
+ /* Originally, the keytype was just that, and later it got changed
+ to des-cbc-md5, but we always used des in cfb64 mode. This
+ should cover all cases, but will break if someone has hacked
+ this code to really use des-cbc-md5 -- but then that's not my
+ problem. */
+ if(key.keytype == KEYTYPE_DES || key.keytype == ETYPE_DES_CBC_MD5)
+ key.keytype = ETYPE_DES_CFB64_NONE;
+
+ ret = hdb_process_master_key(context, 0, &key, 0, mkey);
+ krb5_free_keyblock_contents(context, &key);
+ return ret;
+}
+
+/* read a krb4 /.k style file */
+static krb5_error_code
+read_master_krb4(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ int fd;
+ krb5_keyblock key;
+ krb5_error_code ret;
+ unsigned char buf[256];
+ ssize_t len;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if(fd < 0) {
+ int save_errno = errno;
+ krb5_set_error_string(context, "failed to open %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ len = read(fd, buf, sizeof(buf));
+ close(fd);
+ if(len < 0) {
+ int save_errno = errno;
+ krb5_set_error_string(context, "error reading %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+ if(len != 8) {
+ krb5_set_error_string(context, "bad contents of %s", filename);
+ return HEIM_ERR_EOF; /* XXX file might be too large */
+ }
+
+ memset(&key, 0, sizeof(key));
+ key.keytype = ETYPE_DES_PCBC_NONE;
+ ret = krb5_data_copy(&key.keyvalue, buf, len);
+ memset(buf, 0, sizeof(buf));
+ if(ret)
+ return ret;
+
+ ret = hdb_process_master_key(context, 0, &key, 0, mkey);
+ krb5_free_keyblock_contents(context, &key);
+ return ret;
+}
+
+krb5_error_code
+hdb_read_master_key(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ FILE *f;
+ unsigned char buf[16];
+ krb5_error_code ret;
+
+ off_t len;
+
+ *mkey = NULL;
+
+ if(filename == NULL)
+ filename = HDB_DB_DIR "/m-key";
+
+ f = fopen(filename, "r");
+ if(f == NULL) {
+ int save_errno = errno;
+ krb5_set_error_string(context, "failed to open %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ if(fread(buf, 1, 2, f) != 2) {
+ krb5_set_error_string(context, "end of file reading %s", filename);
+ fclose(f);
+ return HEIM_ERR_EOF;
+ }
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+
+ if(fclose(f) != 0)
+ return errno;
+
+ if(len < 0)
+ return errno;
+
+ if(len == 8) {
+ ret = read_master_krb4(context, filename, mkey);
+ } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) {
+ ret = read_master_encryptionkey(context, filename, mkey);
+ } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) {
+ ret = read_master_keytab(context, filename, mkey);
+ } else {
+ ret = read_master_mit(context, filename, mkey);
+ }
+ return ret;
+}
+
+krb5_error_code
+hdb_write_master_key(krb5_context context, const char *filename,
+ hdb_master_key mkey)
+{
+ krb5_error_code ret;
+ hdb_master_key p;
+ krb5_keytab kt;
+
+ if(filename == NULL)
+ filename = HDB_DB_DIR "/m-key";
+
+ ret = krb5_kt_resolve(context, filename, &kt);
+ if(ret)
+ return ret;
+
+ for(p = mkey; p; p = p->next) {
+ ret = krb5_kt_add_entry(context, kt, &p->keytab);
+ }
+
+ krb5_kt_close(context, kt);
+
+ return ret;
+}
+
+hdb_master_key
+_hdb_find_master_key(u_int32_t *mkvno, hdb_master_key mkey)
+{
+ hdb_master_key ret = NULL;
+ while(mkey) {
+ if(ret == NULL && mkey->keytab.vno == 0)
+ ret = mkey;
+ if(mkvno == NULL) {
+ if(ret == NULL || mkey->keytab.vno > ret->keytab.vno)
+ ret = mkey;
+ } else if(mkey->keytab.vno == *mkvno)
+ return mkey;
+ mkey = mkey->next;
+ }
+ return ret;
+}
+
+int
+_hdb_mkey_version(hdb_master_key mkey)
+{
+ return mkey->keytab.vno;
+}
+
+int
+_hdb_mkey_decrypt(krb5_context context, hdb_master_key key,
+ krb5_key_usage usage,
+ void *ptr, size_t size, krb5_data *res)
+{
+ return krb5_decrypt(context, key->crypto, usage,
+ ptr, size, res);
+}
+
+int
+_hdb_mkey_encrypt(krb5_context context, hdb_master_key key,
+ krb5_key_usage usage,
+ const void *ptr, size_t size, krb5_data *res)
+{
+ return krb5_encrypt(context, key->crypto, usage,
+ ptr, size, res);
+}
+
+krb5_error_code
+hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
+{
+
+ krb5_error_code ret;
+ krb5_data res;
+ size_t keysize;
+
+ hdb_master_key key;
+
+ if(k->mkvno == NULL)
+ return 0;
+
+ key = _hdb_find_master_key(k->mkvno, mkey);
+
+ if (key == NULL)
+ return HDB_ERR_NO_MKEY;
+
+ ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
+ k->key.keyvalue.data,
+ k->key.keyvalue.length,
+ &res);
+ if(ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+ /* try to decrypt with MIT key usage */
+ ret = _hdb_mkey_decrypt(context, key, 0,
+ k->key.keyvalue.data,
+ k->key.keyvalue.length,
+ &res);
+ }
+ if (ret)
+ return ret;
+
+ /* fixup keylength if the key got padded when encrypting it */
+ ret = krb5_enctype_keysize(context, k->key.keytype, &keysize);
+ if (ret) {
+ krb5_data_free(&res);
+ return ret;
+ }
+ if (keysize > res.length) {
+ krb5_data_free(&res);
+ return KRB5_BAD_KEYSIZE;
+ }
+
+ memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
+ free(k->key.keyvalue.data);
+ k->key.keyvalue = res;
+ k->key.keyvalue.length = keysize;
+ free(k->mkvno);
+ k->mkvno = NULL;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
+{
+ int i;
+
+ for(i = 0; i < ent->keys.len; i++){
+ krb5_error_code ret;
+
+ ret = hdb_unseal_key_mkey(context, &ent->keys.val[i], mkey);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+krb5_error_code
+hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+ return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_unseal_key(krb5_context context, HDB *db, Key *k)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+ return hdb_unseal_key_mkey(context, k, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
+{
+ krb5_error_code ret;
+ krb5_data res;
+ hdb_master_key key;
+
+ key = _hdb_find_master_key(k->mkvno, mkey);
+
+ if (key == NULL)
+ return HDB_ERR_NO_MKEY;
+
+ ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
+ k->key.keyvalue.data,
+ k->key.keyvalue.length,
+ &res);
+ if (ret)
+ return ret;
+
+ memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
+ free(k->key.keyvalue.data);
+ k->key.keyvalue = res;
+
+ if (k->mkvno == NULL) {
+ k->mkvno = malloc(sizeof(*k->mkvno));
+ if (k->mkvno == NULL)
+ return ENOMEM;
+ }
+ *k->mkvno = key->keytab.vno;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
+{
+ int i;
+ for(i = 0; i < ent->keys.len; i++){
+ krb5_error_code ret;
+
+ ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+krb5_error_code
+hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+
+ return hdb_seal_keys_mkey(context, ent, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_seal_key(krb5_context context, HDB *db, Key *k)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+
+ return hdb_seal_key_mkey(context, k, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_set_master_key (krb5_context context,
+ HDB *db,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ hdb_master_key mkey;
+
+ ret = hdb_process_master_key(context, 0, key, 0, &mkey);
+ if (ret)
+ return ret;
+ db->hdb_master_key = mkey;
+#if 0 /* XXX - why? */
+ des_set_random_generator_seed(key.keyvalue.data);
+#endif
+ db->hdb_master_key_set = 1;
+ return 0;
+}
+
+krb5_error_code
+hdb_set_master_keyfile (krb5_context context,
+ HDB *db,
+ const char *keyfile)
+{
+ hdb_master_key key;
+ krb5_error_code ret;
+
+ ret = hdb_read_master_key(context, keyfile, &key);
+ if (ret) {
+ if (ret != ENOENT)
+ return ret;
+ krb5_clear_error_string(context);
+ return 0;
+ }
+ db->hdb_master_key = key;
+ db->hdb_master_key_set = 1;
+ return ret;
+}
+
+krb5_error_code
+hdb_clear_master_key (krb5_context context,
+ HDB *db)
+{
+ if (db->hdb_master_key_set) {
+ hdb_free_master_key(context, db->hdb_master_key);
+ db->hdb_master_key_set = 0;
+ }
+ return 0;
+}