aboutsummaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2009-10-27 14:24:01 +0000
committerGreg Hudson <ghudson@mit.edu>2009-10-27 14:24:01 +0000
commit2a5ccaf5a2456e8cfc3f774df307386404bfbec3 (patch)
tree73f2ca8f5b0860aef61fac578a21370e5c34e98e /src/plugins
parent8552d685d38e6d664186ac671f6bcd2269f41398 (diff)
downloadkrb5-2a5ccaf5a2456e8cfc3f774df307386404bfbec3.zip
krb5-2a5ccaf5a2456e8cfc3f774df307386404bfbec3.tar.gz
krb5-2a5ccaf5a2456e8cfc3f774df307386404bfbec3.tar.bz2
Heimdal DB bridge plugin for KDC back end
Merge Luke's users/lhoward/heimmig branch to trunk. Implements a KDC back-end plugin which interfaces to a Heimdal HDB plugin. ticket: 6578 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23073 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/authdata/greet_server/greet_auth.c1
-rw-r--r--src/plugins/kdb/hdb/Makefile.in64
-rw-r--r--src/plugins/kdb/hdb/hdb.exports1
-rw-r--r--src/plugins/kdb/hdb/hdb.h141
-rw-r--r--src/plugins/kdb/hdb/hdb_asn1.h586
-rw-r--r--src/plugins/kdb/hdb/hdb_err.h32
-rw-r--r--src/plugins/kdb/hdb/kdb_hdb.c1426
-rw-r--r--src/plugins/kdb/hdb/kdb_hdb.h172
-rw-r--r--src/plugins/kdb/hdb/kdb_marshal.c810
-rw-r--r--src/plugins/kdb/hdb/kdb_windc.c615
-rw-r--r--src/plugins/kdb/hdb/windc_plugin.h82
11 files changed, 3930 insertions, 0 deletions
diff --git a/src/plugins/authdata/greet_server/greet_auth.c b/src/plugins/authdata/greet_server/greet_auth.c
index 3f1e3d9..80a68a8 100644
--- a/src/plugins/authdata/greet_server/greet_auth.c
+++ b/src/plugins/authdata/greet_server/greet_auth.c
@@ -155,6 +155,7 @@ greet_authdata(krb5_context context,
krb5_db_entry *tgs,
krb5_keyblock *client_key,
krb5_keyblock *server_key,
+ krb5_keyblock *krbtgt_key,
krb5_data *req_pkt,
krb5_kdc_req *request,
krb5_const_principal for_user_princ,
diff --git a/src/plugins/kdb/hdb/Makefile.in b/src/plugins/kdb/hdb/Makefile.in
new file mode 100644
index 0000000..3f2bccf
--- /dev/null
+++ b/src/plugins/kdb/hdb/Makefile.in
@@ -0,0 +1,64 @@
+thisconfigdir=../../..
+myfulldir=plugins/kdb/hdb
+mydir=plugins/kdb/hdb
+BUILDTOP=$(REL)..$(S)..$(S)..
+KRB5_RUN_ENV = @KRB5_RUN_ENV@
+KRB5_CONFIG_SETUP = KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; export KRB5_CONFIG ;
+PROG_LIBPATH=-L$(TOPLIBD)
+PROG_RPATH=$(KRB5_LIBDIR)
+MODULE_INSTALL_DIR = $(KRB5_DB_MODULE_DIR)
+DEFS=
+
+LOCALINCLUDES = -I../../../lib/kdb -I$(srcdir)/../../../lib/kdb
+DEFINES = -DPLUGIN -DSHLIBEXT=\"$(SHLIBEXT)\"
+
+LIBBASE=hdb
+LIBMAJOR=0
+LIBMINOR=0
+SO_EXT=.so
+RELDIR=../plugins/kdb/hdb
+# Depends on libk5crypto and libkrb5
+# Also on gssrpc, for xdr stuff.
+SHLIB_EXPDEPS = \
+ $(GSSRPC_DEPLIBS) \
+ $(TOPLIBD)/libkdb5$(SHLIBEXT) \
+ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
+ $(TOPLIBD)/libkrb5$(SHLIBEXT)
+SHLIB_EXPLIBS= $(GSSRPC_LIBS) -lkdb5 -lkrb5 -lcom_err -lk5crypto $(KDB5_DB_LIB) $(SUPPORT_LIB) $(LIBS) @DB_EXTRA_LIBS@
+
+SHLIB_DIRS=-L$(TOPLIBD)
+SHLIB_RDIRS=$(KRB5_LIBDIR)
+
+SRCS= \
+ $(srcdir)/kdb_hdb.c \
+ $(srcdir)/kdb_marshal.c \
+ $(srcdir)/kdb_windc.c
+
+STOBJLISTS=OBJS.ST $(DBOBJLISTS)
+STLIBOBJS= \
+ kdb_hdb.o \
+ kdb_marshal.o \
+ kdb_windc.o
+
+all-unix:: all-liblinks
+install-unix:: install-libs
+clean-unix:: clean-libs clean-libobjs
+
+$(DB_DEPS) $(DBOBJLISTS-k5) $(DBSHOBJLISTS): all-recurse
+
+clean::
+ $(RM) lib$(LIBBASE)$(SO_EXT)
+
+@libnover_frag@
+@libobj_frag@
+
+.depend-verify-db: depend-verify-db-$(DB_VERSION)
+depend-verify-db-k5:
+ @if test -r .depend-verify-db; then :; \
+ else (set -x; touch .depend-verify-db); fi
+depend-verify-db-sys:
+ @echo 1>&2 error: cannot build dependencies using system db package
+ @exit 1
+
+.d: .depend-verify-db
+
diff --git a/src/plugins/kdb/hdb/hdb.exports b/src/plugins/kdb/hdb/hdb.exports
new file mode 100644
index 0000000..f2b7c11
--- /dev/null
+++ b/src/plugins/kdb/hdb/hdb.exports
@@ -0,0 +1 @@
+kdb_function_table
diff --git a/src/plugins/kdb/hdb/hdb.h b/src/plugins/kdb/hdb/hdb.h
new file mode 100644
index 0000000..39fbec7
--- /dev/null
+++ b/src/plugins/kdb/hdb/hdb.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1997 - 2007 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 22198 2007-12-07 13:09:25Z lha $ */
+
+#ifndef __HDB_H__
+#define __HDB_H__
+
+#include "hdb_err.h"
+#include "hdb_asn1.h"
+
+struct hdb_dbinfo;
+
+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_GET_CLIENT 4 /* fetch client */
+#define HDB_F_GET_SERVER 8 /* fetch server */
+#define HDB_F_GET_KRBTGT 16 /* fetch krbtgt */
+#define HDB_F_GET_ANY 28 /* fetch any of client,server,krbtgt */
+#define HDB_F_CANON 32 /* want canonicalition */
+
+/* key usage for master key */
+#define HDB_KU_MKEY 0x484442
+
+typedef struct heim_context *heim_context;
+
+typedef struct hdb_master_key_data *hdb_master_key;
+
+typedef struct hdb_entry_ex {
+ void *ctx;
+ hdb_entry entry;
+ void (*free_entry)(heim_context, struct hdb_entry_ex *);
+} hdb_entry_ex;
+
+typedef struct HDB{
+ void *hdb_db;
+ void *hdb_dbc;
+ char *hdb_name;
+ int hdb_master_key_set;
+ hdb_master_key hdb_master_key;
+ int hdb_openp;
+
+ krb5_error_code (*hdb_open)(heim_context,
+ struct HDB*,
+ int,
+ mode_t);
+ krb5_error_code (*hdb_close)(heim_context,
+ struct HDB*);
+ void (*hdb_free)(heim_context,
+ struct HDB*,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_fetch)(heim_context,
+ struct HDB*,
+ const Principal *,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_store)(heim_context,
+ struct HDB*,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_remove)(heim_context,
+ struct HDB*,
+ const Principal *);
+ krb5_error_code (*hdb_firstkey)(heim_context,
+ struct HDB*,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_nextkey)(heim_context,
+ struct HDB*,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_lock)(heim_context,
+ struct HDB*,
+ int operation);
+ krb5_error_code (*hdb_unlock)(heim_context,
+ struct HDB*);
+ krb5_error_code (*hdb_rename)(heim_context,
+ struct HDB*,
+ const char*);
+ krb5_error_code (*hdb__get)(heim_context,
+ struct HDB*,
+ heim_octet_string,
+ heim_octet_string*);
+ krb5_error_code (*hdb__put)(heim_context,
+ struct HDB*,
+ int,
+ heim_octet_string,
+ heim_octet_string);
+ krb5_error_code (*hdb__del)(heim_context,
+ struct HDB*,
+ heim_octet_string);
+ krb5_error_code (*hdb_destroy)(heim_context,
+ struct HDB*);
+}HDB;
+
+#define HDB_INTERFACE_VERSION 4
+
+struct hdb_so_method {
+ int version;
+ const char *prefix;
+ krb5_error_code (*create)(heim_context, HDB **, const char *filename);
+};
+
+typedef krb5_error_code (*hdb_foreach_func_t)(heim_context, HDB*,
+ hdb_entry_ex*, void*);
+extern krb5_kt_ops hdb_kt_ops;
+
+#endif /* __HDB_H__ */
diff --git a/src/plugins/kdb/hdb/hdb_asn1.h b/src/plugins/kdb/hdb/hdb_asn1.h
new file mode 100644
index 0000000..87aa2a4
--- /dev/null
+++ b/src/plugins/kdb/hdb/hdb_asn1.h
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 1997 - 2007 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.
+ */
+/* Generated from ./hdb.asn1 */
+/* Do not edit */
+
+#ifndef __hdb_asn1_h__
+#define __hdb_asn1_h__
+
+#include <k5-int.h>
+#include <stddef.h>
+#include <time.h>
+
+#ifndef __asn1_common_definitions__
+#define __asn1_common_definitions__
+
+typedef struct heim_integer {
+ size_t length;
+ void *data;
+ int negative;
+} heim_integer;
+
+typedef struct heim_octet_string {
+ size_t length;
+ void *data;
+} heim_octet_string;
+
+typedef char *heim_general_string;
+
+typedef char *heim_utf8_string;
+
+typedef char *heim_printable_string;
+
+typedef char *heim_ia5_string;
+
+typedef struct heim_bmp_string {
+ size_t length;
+ uint16_t *data;
+} heim_bmp_string;
+
+typedef struct heim_universal_string {
+ size_t length;
+ uint32_t *data;
+} heim_universal_string;
+
+typedef char *heim_visible_string;
+
+typedef struct heim_oid {
+ size_t length;
+ unsigned *components;
+} heim_oid;
+
+typedef struct heim_bit_string {
+ size_t length;
+ void *data;
+} heim_bit_string;
+
+typedef struct heim_octet_string heim_any;
+typedef struct heim_octet_string heim_any_set;
+
+#define ASN1_MALLOC_ENCODE(T, B, BL, S, L, R) \
+ do { \
+ (BL) = length_##T((S)); \
+ (B) = malloc((BL)); \
+ if((B) == NULL) { \
+ (R) = ENOMEM; \
+ } else { \
+ (R) = encode_##T(((unsigned char*)(B)) + (BL) - 1, (BL), \
+ (S), (L)); \
+ if((R) != 0) { \
+ free((B)); \
+ (B) = NULL; \
+ } \
+ } \
+ } while (0)
+
+struct units;
+
+#endif
+
+typedef int NAME_TYPE;
+
+typedef struct PrincipalName {
+ NAME_TYPE name_type;
+ struct {
+ unsigned int len;
+ heim_general_string *val;
+ } name_string;
+} PrincipalName;
+
+typedef heim_general_string Realm;
+
+typedef struct Principal {
+ PrincipalName name;
+ Realm realm;
+} Principal;
+
+typedef struct KDCOptions {
+ unsigned int reserved:1;
+ unsigned int forwardable:1;
+ unsigned int forwarded:1;
+ unsigned int proxiable:1;
+ unsigned int proxy:1;
+ unsigned int allow_postdate:1;
+ unsigned int postdated:1;
+ unsigned int unused7:1;
+ unsigned int renewable:1;
+ unsigned int unused9:1;
+ unsigned int unused10:1;
+ unsigned int unused11:1;
+ unsigned int request_anonymous:1;
+ unsigned int canonicalize:1;
+ unsigned int constrained_delegation:1;
+ unsigned int disable_transited_check:1;
+ unsigned int renewable_ok:1;
+ unsigned int enc_tkt_in_skey:1;
+ unsigned int renew:1;
+ unsigned int validate:1;
+} KDCOptions;
+
+typedef struct HostAddress {
+ krb5_ui_4 addr_type;
+ heim_octet_string address;
+} HostAddress;
+
+typedef struct HostAddresses {
+ unsigned int len;
+ HostAddress *val;
+} HostAddresses;
+
+typedef time_t KerberosTime;
+
+typedef krb5_enctype ENCTYPE;
+
+typedef struct EncryptionKey {
+ ENCTYPE keytype;
+ heim_octet_string keyvalue;
+} EncryptionKey;
+
+typedef struct _METHOD_DATA *METHOD_DATA;
+
+typedef struct KDC_REQ_BODY {
+ KDCOptions kdc_options;
+ PrincipalName *cname;
+ Realm realm;
+ PrincipalName *sname;
+ KerberosTime *from;
+ KerberosTime *till;
+ KerberosTime *rtime;
+ krb5_ui_4 nonce;
+ struct {
+ unsigned int len;
+ ENCTYPE *val;
+ } etype;
+ HostAddresses *addresses;
+ krb5_pointer *enc_authorization_data;
+ struct {
+ unsigned int len;
+ krb5_pointer *val;
+ } *additional_tickets;
+} KDC_REQ_BODY;
+
+typedef struct KDC_REQ {
+ krb5_ui_4 pvno;
+ krb5_ui_4 msg_type;
+ METHOD_DATA *padata;
+ KDC_REQ_BODY req_body;
+} KDC_REQ;
+
+enum { HDB_DB_FORMAT = 2 };
+
+enum { hdb_pw_salt = 3 };
+
+enum { hdb_afs3_salt = 10 };
+
+/*
+Salt ::= SEQUENCE {
+ type [0] INTEGER (0..-1),
+ salt [1] OCTET STRING,
+}
+*/
+
+typedef struct Salt {
+ unsigned int type;
+ heim_octet_string salt;
+} Salt;
+
+int encode_Salt(unsigned char *, size_t, const Salt *, size_t *);
+int decode_Salt(const unsigned char *, size_t, Salt *, size_t *);
+void free_Salt (Salt *);
+size_t length_Salt(const Salt *);
+int copy_Salt (const Salt *, Salt *);
+
+
+/*
+Key ::= SEQUENCE {
+ mkvno [0] INTEGER (0..-1) OPTIONAL,
+ key [1] EncryptionKey,
+ salt [2] Salt OPTIONAL,
+}
+*/
+
+typedef struct Key {
+ unsigned int *mkvno;
+ EncryptionKey key;
+ Salt *salt;
+} Key;
+
+int encode_Key(unsigned char *, size_t, const Key *, size_t *);
+int decode_Key(const unsigned char *, size_t, Key *, size_t *);
+void free_Key (Key *);
+size_t length_Key(const Key *);
+int copy_Key (const Key *, Key *);
+
+
+/*
+Event ::= SEQUENCE {
+ time [0] KerberosTime,
+ principal [1] Principal OPTIONAL,
+}
+*/
+
+typedef struct Event {
+ KerberosTime time;
+ Principal *principal;
+} Event;
+
+int encode_Event(unsigned char *, size_t, const Event *, size_t *);
+int decode_Event(const unsigned char *, size_t, Event *, size_t *);
+void free_Event (Event *);
+size_t length_Event(const Event *);
+int copy_Event (const Event *, Event *);
+
+
+/*
+HDBFlags ::= BIT STRING {
+ initial(0),
+ forwardable(1),
+ proxiable(2),
+ renewable(3),
+ postdate(4),
+ server(5),
+ client(6),
+ invalid(7),
+ require-preauth(8),
+ change-pw(9),
+ require-hwauth(10),
+ ok-as-delegate(11),
+ user-to-user(12),
+ immutable(13),
+ trusted-for-delegation(14),
+ allow-kerberos4(15),
+ allow-digest(16)
+}
+*/
+
+typedef struct HDBFlags {
+ unsigned int initial:1;
+ unsigned int forwardable:1;
+ unsigned int proxiable:1;
+ unsigned int renewable:1;
+ unsigned int postdate:1;
+ unsigned int server:1;
+ unsigned int client:1;
+ unsigned int invalid:1;
+ unsigned int require_preauth:1;
+ unsigned int change_pw:1;
+ unsigned int require_hwauth:1;
+ unsigned int ok_as_delegate:1;
+ unsigned int user_to_user:1;
+ unsigned int immutable:1;
+ unsigned int trusted_for_delegation:1;
+ unsigned int allow_kerberos4:1;
+ unsigned int allow_digest:1;
+} HDBFlags;
+
+
+int encode_HDBFlags(unsigned char *, size_t, const HDBFlags *, size_t *);
+int decode_HDBFlags(const unsigned char *, size_t, HDBFlags *, size_t *);
+void free_HDBFlags (HDBFlags *);
+size_t length_HDBFlags(const HDBFlags *);
+int copy_HDBFlags (const HDBFlags *, HDBFlags *);
+unsigned HDBFlags2int(HDBFlags);
+HDBFlags int2HDBFlags(unsigned);
+const struct units * asn1_HDBFlags_units(void);
+
+/*
+GENERATION ::= SEQUENCE {
+ time [0] KerberosTime,
+ usec [1] INTEGER (0..-1),
+ gen [2] INTEGER (0..-1),
+}
+*/
+
+typedef struct GENERATION {
+ KerberosTime time;
+ unsigned int usec;
+ unsigned int gen;
+} GENERATION;
+
+int encode_GENERATION(unsigned char *, size_t, const GENERATION *, size_t *);
+int decode_GENERATION(const unsigned char *, size_t, GENERATION *, size_t *);
+void free_GENERATION (GENERATION *);
+size_t length_GENERATION(const GENERATION *);
+int copy_GENERATION (const GENERATION *, GENERATION *);
+
+
+/*
+HDB-Ext-PKINIT-acl ::= SEQUENCE OF SEQUENCE {
+ subject [0] UTF8String,
+ issuer [1] UTF8String OPTIONAL,
+ anchor [2] UTF8String OPTIONAL,
+}
+*/
+
+typedef struct HDB_Ext_PKINIT_acl {
+ unsigned int len;
+ struct {
+ heim_utf8_string subject;
+ heim_utf8_string *issuer;
+ heim_utf8_string *anchor;
+ } *val;
+} HDB_Ext_PKINIT_acl;
+
+int encode_HDB_Ext_PKINIT_acl(unsigned char *, size_t, const HDB_Ext_PKINIT_acl *, size_t *);
+int decode_HDB_Ext_PKINIT_acl(const unsigned char *, size_t, HDB_Ext_PKINIT_acl *, size_t *);
+void free_HDB_Ext_PKINIT_acl (HDB_Ext_PKINIT_acl *);
+size_t length_HDB_Ext_PKINIT_acl(const HDB_Ext_PKINIT_acl *);
+int copy_HDB_Ext_PKINIT_acl (const HDB_Ext_PKINIT_acl *, HDB_Ext_PKINIT_acl *);
+
+
+/*
+HDB-Ext-PKINIT-hash ::= SEQUENCE OF SEQUENCE {
+ digest-type [0] OBJECT IDENTIFIER,
+ digest [1] OCTET STRING,
+}
+*/
+
+typedef struct HDB_Ext_PKINIT_hash {
+ unsigned int len;
+ struct {
+ heim_oid digest_type;
+ heim_octet_string digest;
+ } *val;
+} HDB_Ext_PKINIT_hash;
+
+int encode_HDB_Ext_PKINIT_hash(unsigned char *, size_t, const HDB_Ext_PKINIT_hash *, size_t *);
+int decode_HDB_Ext_PKINIT_hash(const unsigned char *, size_t, HDB_Ext_PKINIT_hash *, size_t *);
+void free_HDB_Ext_PKINIT_hash (HDB_Ext_PKINIT_hash *);
+size_t length_HDB_Ext_PKINIT_hash(const HDB_Ext_PKINIT_hash *);
+int copy_HDB_Ext_PKINIT_hash (const HDB_Ext_PKINIT_hash *, HDB_Ext_PKINIT_hash *);
+
+
+/*
+HDB-Ext-Constrained-delegation-acl ::= SEQUENCE OF Principal
+*/
+
+typedef struct HDB_Ext_Constrained_delegation_acl {
+ unsigned int len;
+ Principal *val;
+} HDB_Ext_Constrained_delegation_acl;
+
+int encode_HDB_Ext_Constrained_delegation_acl(unsigned char *, size_t, const HDB_Ext_Constrained_delegation_acl *, size_t *);
+int decode_HDB_Ext_Constrained_delegation_acl(const unsigned char *, size_t, HDB_Ext_Constrained_delegation_acl *, size_t *);
+void free_HDB_Ext_Constrained_delegation_acl (HDB_Ext_Constrained_delegation_acl *);
+size_t length_HDB_Ext_Constrained_delegation_acl(const HDB_Ext_Constrained_delegation_acl *);
+int copy_HDB_Ext_Constrained_delegation_acl (const HDB_Ext_Constrained_delegation_acl *, HDB_Ext_Constrained_delegation_acl *);
+
+
+/*
+HDB-Ext-Lan-Manager-OWF ::= OCTET STRING
+*/
+
+typedef heim_octet_string HDB_Ext_Lan_Manager_OWF;
+
+int encode_HDB_Ext_Lan_Manager_OWF(unsigned char *, size_t, const HDB_Ext_Lan_Manager_OWF *, size_t *);
+int decode_HDB_Ext_Lan_Manager_OWF(const unsigned char *, size_t, HDB_Ext_Lan_Manager_OWF *, size_t *);
+void free_HDB_Ext_Lan_Manager_OWF (HDB_Ext_Lan_Manager_OWF *);
+size_t length_HDB_Ext_Lan_Manager_OWF(const HDB_Ext_Lan_Manager_OWF *);
+int copy_HDB_Ext_Lan_Manager_OWF (const HDB_Ext_Lan_Manager_OWF *, HDB_Ext_Lan_Manager_OWF *);
+
+
+/*
+HDB-Ext-Password ::= SEQUENCE {
+ mkvno [0] INTEGER (0..-1) OPTIONAL,
+ password OCTET STRING,
+}
+*/
+
+typedef struct HDB_Ext_Password {
+ unsigned int *mkvno;
+ heim_octet_string password;
+} HDB_Ext_Password;
+
+int encode_HDB_Ext_Password(unsigned char *, size_t, const HDB_Ext_Password *, size_t *);
+int decode_HDB_Ext_Password(const unsigned char *, size_t, HDB_Ext_Password *, size_t *);
+void free_HDB_Ext_Password (HDB_Ext_Password *);
+size_t length_HDB_Ext_Password(const HDB_Ext_Password *);
+int copy_HDB_Ext_Password (const HDB_Ext_Password *, HDB_Ext_Password *);
+
+
+/*
+HDB-Ext-Aliases ::= SEQUENCE {
+ case-insensitive [0] BOOLEAN,
+ aliases [1] SEQUENCE OF Principal,
+}
+*/
+
+typedef struct HDB_Ext_Aliases {
+ int case_insensitive;
+ struct {
+ unsigned int len;
+ Principal *val;
+ } aliases;
+} HDB_Ext_Aliases;
+
+int encode_HDB_Ext_Aliases(unsigned char *, size_t, const HDB_Ext_Aliases *, size_t *);
+int decode_HDB_Ext_Aliases(const unsigned char *, size_t, HDB_Ext_Aliases *, size_t *);
+void free_HDB_Ext_Aliases (HDB_Ext_Aliases *);
+size_t length_HDB_Ext_Aliases(const HDB_Ext_Aliases *);
+int copy_HDB_Ext_Aliases (const HDB_Ext_Aliases *, HDB_Ext_Aliases *);
+
+
+/*
+HDB-extension ::= SEQUENCE {
+ mandatory [0] BOOLEAN,
+ data [1] CHOICE {
+ pkinit-acl [0] HDB-Ext-PKINIT-acl,
+ pkinit-cert-hash [1] HDB-Ext-PKINIT-hash,
+ allowed-to-delegate-to [2] HDB-Ext-Constrained-delegation-acl,
+ lm-owf [4] HDB-Ext-Lan-Manager-OWF,
+ password [5] HDB-Ext-Password,
+ aliases [6] HDB-Ext-Aliases,
+ last-pw-change [7] KerberosTime,
+ ...,
+ },
+ ...,
+}
+*/
+
+typedef struct HDB_extension {
+ int mandatory;
+ struct {
+ enum {
+ choice_HDB_extension_data_asn1_ellipsis = 0,
+ choice_HDB_extension_data_pkinit_acl,
+ choice_HDB_extension_data_pkinit_cert_hash,
+ choice_HDB_extension_data_allowed_to_delegate_to,
+ choice_HDB_extension_data_lm_owf,
+ choice_HDB_extension_data_password,
+ choice_HDB_extension_data_aliases,
+ choice_HDB_extension_data_last_pw_change
+ /* ... */
+ } element;
+ union {
+ HDB_Ext_PKINIT_acl pkinit_acl;
+ HDB_Ext_PKINIT_hash pkinit_cert_hash;
+ HDB_Ext_Constrained_delegation_acl allowed_to_delegate_to;
+ HDB_Ext_Lan_Manager_OWF lm_owf;
+ HDB_Ext_Password password;
+ HDB_Ext_Aliases aliases;
+ KerberosTime last_pw_change;
+ heim_octet_string asn1_ellipsis;
+ } u;
+ } data;
+} HDB_extension;
+
+int encode_HDB_extension(unsigned char *, size_t, const HDB_extension *, size_t *);
+int decode_HDB_extension(const unsigned char *, size_t, HDB_extension *, size_t *);
+void free_HDB_extension (HDB_extension *);
+size_t length_HDB_extension(const HDB_extension *);
+int copy_HDB_extension (const HDB_extension *, HDB_extension *);
+
+
+/*
+HDB-extensions ::= SEQUENCE OF HDB-extension
+*/
+
+typedef struct HDB_extensions {
+ unsigned int len;
+ HDB_extension *val;
+} HDB_extensions;
+
+int encode_HDB_extensions(unsigned char *, size_t, const HDB_extensions *, size_t *);
+int decode_HDB_extensions(const unsigned char *, size_t, HDB_extensions *, size_t *);
+void free_HDB_extensions (HDB_extensions *);
+size_t length_HDB_extensions(const HDB_extensions *);
+int copy_HDB_extensions (const HDB_extensions *, HDB_extensions *);
+
+
+/*
+hdb_entry ::= SEQUENCE {
+ principal [0] Principal OPTIONAL,
+ kvno [1] INTEGER (0..-1),
+ 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..-1) OPTIONAL,
+ max-renew [9] INTEGER (0..-1) OPTIONAL,
+ flags [10] HDBFlags,
+ etypes [11] SEQUENCE OF INTEGER (0..-1) OPTIONAL,
+ generation [12] GENERATION OPTIONAL,
+ extensions [13] HDB-extensions OPTIONAL,
+}
+*/
+
+typedef struct hdb_entry {
+ Principal *principal;
+ unsigned int kvno;
+ struct {
+ unsigned int len;
+ Key *val;
+ } keys;
+ Event created_by;
+ Event *modified_by;
+ KerberosTime *valid_start;
+ KerberosTime *valid_end;
+ KerberosTime *pw_end;
+ unsigned int *max_life;
+ unsigned int *max_renew;
+ HDBFlags flags;
+ struct {
+ unsigned int len;
+ unsigned int *val;
+ } *etypes;
+ GENERATION *generation;
+ HDB_extensions *extensions;
+} hdb_entry;
+
+int encode_hdb_entry(unsigned char *, size_t, const hdb_entry *, size_t *);
+int decode_hdb_entry(const unsigned char *, size_t, hdb_entry *, size_t *);
+void free_hdb_entry (hdb_entry *);
+size_t length_hdb_entry(const hdb_entry *);
+int copy_hdb_entry (const hdb_entry *, hdb_entry *);
+
+
+/*
+hdb_entry_alias ::= [APPLICATION 0] SEQUENCE {
+ principal [0] Principal OPTIONAL,
+}
+*/
+
+typedef struct hdb_entry_alias {
+ Principal *principal;
+} hdb_entry_alias;
+
+int encode_hdb_entry_alias(unsigned char *, size_t, const hdb_entry_alias *, size_t *);
+int decode_hdb_entry_alias(const unsigned char *, size_t, hdb_entry_alias *, size_t *);
+void free_hdb_entry_alias (hdb_entry_alias *);
+size_t length_hdb_entry_alias(const hdb_entry_alias *);
+int copy_hdb_entry_alias (const hdb_entry_alias *, hdb_entry_alias *);
+
+
+#endif /* __hdb_asn1_h__ */
diff --git a/src/plugins/kdb/hdb/hdb_err.h b/src/plugins/kdb/hdb/hdb_err.h
new file mode 100644
index 0000000..a47797a
--- /dev/null
+++ b/src/plugins/kdb/hdb/hdb_err.h
@@ -0,0 +1,32 @@
+/* Generated from hdb_err.et */
+/* $Id: hdb_err.et 15878 2005-08-11 13:17:22Z lha $ */
+
+#ifndef __hdb_err_h__
+#define __hdb_err_h__
+
+struct et_list;
+
+void initialize_hdb_error_table_r(struct et_list **);
+
+void initialize_hdb_error_table(void);
+#define init_hdb_err_tbl initialize_hdb_error_table
+
+typedef enum hdb_error_number{
+ HDB_ERR_UK_SERROR = 36150273,
+ HDB_ERR_UK_RERROR = 36150274,
+ HDB_ERR_NOENTRY = 36150275,
+ HDB_ERR_DB_INUSE = 36150276,
+ HDB_ERR_DB_CHANGED = 36150277,
+ HDB_ERR_RECURSIVELOCK = 36150278,
+ HDB_ERR_NOTLOCKED = 36150279,
+ HDB_ERR_BADLOCKMODE = 36150280,
+ HDB_ERR_CANT_LOCK_DB = 36150281,
+ HDB_ERR_EXISTS = 36150282,
+ HDB_ERR_BADVERSION = 36150283,
+ HDB_ERR_NO_MKEY = 36150284,
+ HDB_ERR_MANDATORY_OPTION = 36150285
+} hdb_error_number;
+
+#define ERROR_TABLE_BASE_hdb 36150272
+
+#endif /* __hdb_err_h__ */
diff --git a/src/plugins/kdb/hdb/kdb_hdb.c b/src/plugins/kdb/hdb/kdb_hdb.c
new file mode 100644
index 0000000..d22489e
--- /dev/null
+++ b/src/plugins/kdb/hdb/kdb_hdb.c
@@ -0,0 +1,1426 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/hdb/kdb_hdb.c
+ *
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#include "k5-int.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <db.h>
+#include <stdio.h>
+#include <errno.h>
+#include <utime.h>
+#include "kdb5.h"
+#include "kdb_hdb.h"
+
+static krb5_error_code
+kh_init(void)
+{
+ return 0;
+}
+
+static krb5_error_code
+kh_fini(void)
+{
+ return 0;
+}
+
+krb5_error_code
+kh_map_error(heim_error_code code)
+{
+ switch (code) {
+ case HDB_ERR_UK_SERROR:
+ code = KRB5_KDB_UK_SERROR;
+ break;
+ case HDB_ERR_UK_RERROR:
+ code = KRB5_KDB_UK_RERROR;
+ break;
+ case HDB_ERR_NOENTRY:
+ code = KRB5_KDB_NOENTRY;
+ break;
+ case HDB_ERR_DB_INUSE:
+ code = KRB5_KDB_DB_INUSE;
+ break;
+ case HDB_ERR_DB_CHANGED:
+ code = KRB5_KDB_DB_CHANGED;
+ break;
+ case HDB_ERR_RECURSIVELOCK:
+ code = KRB5_KDB_RECURSIVELOCK;
+ break;
+ case HDB_ERR_NOTLOCKED:
+ code = KRB5_KDB_NOTLOCKED;
+ break;
+ case HDB_ERR_BADLOCKMODE:
+ code = KRB5_KDB_BADLOCKMODE;
+ break;
+ case HDB_ERR_CANT_LOCK_DB:
+ code = KRB5_KDB_CANTLOCK_DB;
+ break;
+ case HDB_ERR_EXISTS:
+ code = EEXIST;
+ break;
+ case HDB_ERR_BADVERSION:
+ code = KRB5_KDB_BAD_VERSION;
+ break;
+ case HDB_ERR_NO_MKEY:
+ code = KRB5_KDB_NOMASTERKEY;
+ break;
+ case HDB_ERR_MANDATORY_OPTION:
+ code = KRB5_KDB_DBTYPE_NOSUP;
+ break;
+ default:
+ break;
+ }
+
+ return code;
+}
+
+static void
+kh_db_context_free(krb5_context context, kh_db_context *kh)
+{
+ if (kh != NULL) {
+ if (kh->hdb != NULL)
+ (*kh->hdb->hdb_destroy)(kh->hcontext, kh->hdb);
+ if (kh->hcontext != NULL)
+ (*kh->heim_free_context)(kh->hcontext);
+ if (kh->libkrb5 != NULL)
+ krb5int_close_plugin(kh->libkrb5);
+ if (kh->libhdb != NULL)
+ krb5int_close_plugin(kh->libhdb);
+ if (kh->windc != NULL)
+ (*kh->windc->fini)(kh->windc_ctx);
+ krb5int_close_plugin_dirs(&kh->windc_plugins);
+ krb5int_mutex_free(kh->lock);
+ free(kh);
+ }
+}
+
+static krb5_error_code
+kh_db_context_init(krb5_context context,
+ const char *libdir,
+ const char *filename,
+ int mode,
+ kh_db_context **pkh)
+{
+ kh_db_context *kh;
+ krb5_error_code code;
+ char *libhdb = NULL;
+ char *libkrb5 = NULL;
+ struct errinfo errinfo;
+ int *hdb_interface_version;
+
+ if (libdir == NULL)
+ return KRB5_KDB_DBTYPE_INIT; /* XXX */
+
+ memset(&errinfo, 0, sizeof(errinfo));
+
+ kh = k5alloc(sizeof(*kh), &code);
+ if (code != 0)
+ goto cleanup;
+
+ code = krb5int_mutex_alloc(&kh->lock);
+ if (code != 0)
+ goto cleanup;
+
+ if (asprintf(&libkrb5, "%s/libkrb5%s", libdir, SHLIBEXT) < 0) {
+ code = ENOMEM;
+ goto cleanup;
+ }
+
+#define GET_PLUGIN_FUNC(_lib, _sym, _member) do { \
+ code = krb5int_get_plugin_func(kh->_lib, _sym, \
+ (void (**)())&kh->_member, &errinfo); \
+ if (code != 0) \
+ goto cleanup; \
+ } while (0)
+
+ /* libkrb5 */
+ code = krb5int_open_plugin(libkrb5, &kh->libkrb5, &errinfo);
+ if (code != 0)
+ goto cleanup;
+
+ GET_PLUGIN_FUNC(libkrb5, "krb5_init_context", heim_init_context);
+ GET_PLUGIN_FUNC(libkrb5, "krb5_free_context", heim_free_context);
+ GET_PLUGIN_FUNC(libkrb5, "krb5_free_principal", heim_free_principal);
+ GET_PLUGIN_FUNC(libkrb5, "krb5_free_addresses", heim_free_addresses);
+ GET_PLUGIN_FUNC(libkrb5, "krb5_pac_free", heim_pac_free);
+ GET_PLUGIN_FUNC(libkrb5, "krb5_pac_parse", heim_pac_parse);
+ GET_PLUGIN_FUNC(libkrb5, "krb5_pac_verify", heim_pac_verify);
+ GET_PLUGIN_FUNC(libkrb5, "_krb5_pac_sign", heim_pac_sign);
+
+ if (asprintf(&libhdb, "%s/libhdb%s", libdir, SHLIBEXT) < 0)
+ goto cleanup;
+
+ /* libhdb */
+ code = krb5int_open_plugin(libhdb, &kh->libhdb, &errinfo);
+ if (code != 0)
+ goto cleanup;
+
+ /*
+ * New versions of Heimdal export this symbol to mark the
+ * HDB ABI version.
+ */
+ if (krb5int_get_plugin_data(kh->libhdb, "hdb_interface_version",
+ (void **)&hdb_interface_version,
+ &errinfo) == 0 &&
+ *hdb_interface_version != HDB_INTERFACE_VERSION) {
+ code = KRB5_KDB_DBTYPE_NOSUP;
+ goto cleanup;
+ }
+
+ GET_PLUGIN_FUNC(libhdb, "hdb_create", hdb_create);
+ GET_PLUGIN_FUNC(libhdb, "hdb_seal_key", hdb_seal_key);
+ GET_PLUGIN_FUNC(libhdb, "hdb_unseal_key", hdb_unseal_key);
+ GET_PLUGIN_FUNC(libhdb, "hdb_set_master_key", hdb_set_master_key);
+ GET_PLUGIN_FUNC(libhdb, "hdb_free_entry", hdb_free_entry);
+
+ code = kh_map_error((*kh->heim_init_context)(&kh->hcontext));
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_map_error((*kh->hdb_create)(kh->hcontext, &kh->hdb, filename));
+ if (code != 0)
+ goto cleanup;
+
+ if (mode & KRB5_KDB_OPEN_RO)
+ kh->mode = O_RDONLY;
+ else
+ kh->mode = O_RDWR;
+
+ if (mode & KRB5_KDB_SRV_TYPE_KDC)
+ kh_hdb_windc_init(context, libdir, kh);
+
+cleanup:
+ if (code != 0) {
+ kh_db_context_free(context, kh);
+ kh = NULL;
+ }
+
+ krb5int_free_error(&errinfo, NULL);
+
+ *pkh = kh;
+
+ return code;
+}
+
+static krb5_error_code
+kh_init_module(krb5_context context,
+ char *conf_section,
+ char **db_args,
+ int mode)
+{
+ kdb5_dal_handle *dal_handle = context->dal_handle;
+ krb5_error_code code;
+ kh_db_context *kh;
+ char *libdir = NULL;
+ char *filename = NULL;
+
+ if (dal_handle->db_context != NULL) {
+ kh_db_context_free(context, dal_handle->db_context);
+ dal_handle->db_context = NULL;
+ }
+
+ code = profile_get_string(context->profile,
+ KDB_MODULE_SECTION,
+ conf_section,
+ "heimdal_libdir",
+ NULL,
+ &libdir);
+ if (code != 0)
+ goto cleanup;
+
+ code = profile_get_string(context->profile,
+ KDB_MODULE_SECTION,
+ conf_section,
+ "heimdal_dbname",
+ NULL,
+ &filename);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_db_context_init(context, libdir, filename, mode, &kh);
+ if (code != 0)
+ goto cleanup;
+
+ dal_handle->db_context = kh;
+
+cleanup:
+ if (libdir != NULL)
+ free(libdir);
+ if (filename != NULL)
+ free(filename);
+
+ return 0;
+}
+
+static krb5_error_code
+kh_fini_module(krb5_context context)
+{
+ kdb5_dal_handle *dal_handle = context->dal_handle;
+
+ kh_db_context_free(context, dal_handle->db_context);
+ dal_handle->db_context = NULL;
+
+ return 0;
+}
+
+/*
+ * Heimdal API and SPI wrappers.
+ */
+
+static krb5_error_code
+kh_hdb_open(krb5_context context,
+ kh_db_context *kh,
+ int oflag,
+ mode_t mode)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb->hdb_open)(kh->hcontext, kh->hdb, oflag, mode);
+
+ return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_close(krb5_context context,kh_db_context *kh)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb->hdb_close)(kh->hcontext, kh->hdb);
+
+ return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_fetch(krb5_context context,
+ kh_db_context *kh,
+ const Principal *princ,
+ unsigned int flags,
+ hdb_entry_ex *entry)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb->hdb_fetch)(kh->hcontext, kh->hdb, princ, flags, entry);
+
+ return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_store(krb5_context context,
+ kh_db_context *kh,
+ unsigned int flags,
+ hdb_entry_ex *entry)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb->hdb_store)(kh->hcontext, kh->hdb, flags, entry);
+
+ return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_remove(krb5_context context,
+ kh_db_context *kh,
+ const Principal *princ)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb->hdb_remove)(kh->hcontext, kh->hdb, princ);
+
+ return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_firstkey(krb5_context context,
+ kh_db_context *kh,
+ unsigned int flags,
+ hdb_entry_ex *entry)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb->hdb_firstkey)(kh->hcontext, kh->hdb, flags, entry);
+
+ return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_nextkey(krb5_context context,
+ kh_db_context *kh,
+ unsigned int flags,
+ hdb_entry_ex *entry)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb->hdb_nextkey)(kh->hcontext, kh->hdb, flags, entry);
+
+ return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_lock(krb5_context context,
+ kh_db_context *kh,
+ int operation)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb->hdb_lock)(kh->hcontext, kh->hdb, operation);
+
+ return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_unlock(krb5_context context,
+ kh_db_context *kh)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb->hdb_unlock)(kh->hcontext, kh->hdb);
+
+ return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_rename(krb5_context context,
+ kh_db_context *kh,
+ const char *name)
+{
+ heim_error_code hcode;
+
+ if (kh->hdb->hdb_rename == NULL)
+ return KRB5_KDB_DBTYPE_NOSUP;
+
+ hcode = (*kh->hdb->hdb_rename)(kh->hcontext, kh->hdb, name);
+
+ return kh_map_error(hcode);
+}
+
+static HDB_extension *
+kh_hdb_find_extension(const hdb_entry *entry, unsigned int type)
+{
+ unsigned int i;
+ HDB_extension *ret = NULL;
+
+ if (entry->extensions != NULL) {
+ for (i = 0; i < entry->extensions->len; i++) {
+ if (entry->extensions->val[i].data.element == type) {
+ ret = &entry->extensions->val[i];
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static krb5_error_code
+kh_hdb_seal_key(krb5_context context,
+ kh_db_context *kh,
+ Key *key)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb_seal_key)(kh->hcontext, kh->hdb, key);
+
+ return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_unseal_key(krb5_context context,
+ kh_db_context *kh,
+ Key *key)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb_unseal_key)(kh->hcontext, kh->hdb, key);
+
+ return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_set_master_key(krb5_context context,
+ kh_db_context *kh,
+ EncryptionKey *key)
+{
+ heim_error_code hcode;
+
+ hcode = (*kh->hdb_set_master_key)(kh->hcontext, kh->hdb, key);
+
+ return kh_map_error(hcode);
+}
+
+void
+kh_hdb_free_entry(krb5_context context,
+ kh_db_context *kh,
+ hdb_entry_ex *entry)
+{
+ (*kh->hdb_free_entry)(kh->hcontext, entry);
+}
+
+void
+kh_kdb_free_entry(krb5_context context,
+ kh_db_context *kh,
+ krb5_db_entry *entry)
+{
+ krb5_tl_data *tl_data_next = NULL;
+ krb5_tl_data *tl_data = NULL;
+ int i, j;
+
+ if (entry->e_data != NULL) {
+ assert(entry->e_length == sizeof(hdb_entry_ex));
+ kh_hdb_free_entry(context, kh, KH_DB_ENTRY(entry));
+ free(entry->e_data);
+ }
+
+ krb5_free_principal(context, entry->princ);
+
+ for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) {
+ tl_data_next = tl_data->tl_data_next;
+ if (tl_data->tl_data_contents != NULL)
+ free(tl_data->tl_data_contents);
+ free(tl_data);
+ }
+
+ if (entry->key_data != NULL) {
+ for (i = 0; i < entry->n_key_data; i++) {
+ for (j = 0; j < entry->key_data[i].key_data_ver; j++) {
+ if (entry->key_data[i].key_data_length[j] != 0) {
+ if (entry->key_data[i].key_data_contents[j] != NULL) {
+ memset(entry->key_data[i].key_data_contents[j],
+ 0,
+ entry->key_data[i].key_data_length[j]);
+ free(entry->key_data[i].key_data_contents[j]);
+ }
+ }
+ entry->key_data[i].key_data_contents[j] = NULL;
+ entry->key_data[i].key_data_length[j] = 0;
+ entry->key_data[i].key_data_type[j] = 0;
+ }
+ }
+ free(entry->key_data);
+ }
+
+ memset(entry, 0, sizeof(*entry));
+}
+
+static krb5_error_code
+kh_db_create(krb5_context context,
+ char *conf_section,
+ char **db_args)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ krb5_error_code code;
+
+ if (kh == NULL)
+ return KRB5_KDB_DBTYPE_NOSUP;
+
+ code = k5_mutex_lock(kh->lock);
+ if (code != 0)
+ return code;
+
+ code = kh_hdb_open(context, kh, kh->mode, 0);
+
+ k5_mutex_unlock(kh->lock);
+
+ return code;
+}
+
+static krb5_error_code
+kh_db_destroy(krb5_context context,
+ char *conf_section,
+ char **db_args)
+{
+ return KRB5_KDB_DBTYPE_NOSUP;
+}
+
+static krb5_error_code
+kh_db_get_age(krb5_context context,
+ char *db_name,
+ time_t *age)
+{
+ return KRB5_KDB_DBTYPE_NOSUP;
+}
+
+static krb5_error_code
+kh_db_set_option(krb5_context context,
+ int option,
+ void *value)
+{
+ return KRB5_KDB_DBTYPE_NOSUP;
+}
+
+static krb5_error_code
+kh_db_lock(krb5_context context, int kmode)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ krb5_error_code code;
+ enum hdb_lockop hmode;
+
+ if (kh == NULL)
+ return KRB5_KDB_DBTYPE_NOSUP;
+
+ code = k5_mutex_lock(kh->lock);
+ if (code != 0)
+ return code;
+
+ if (kmode & KRB5_DB_LOCKMODE_EXCLUSIVE)
+ hmode = HDB_WLOCK;
+ else
+ hmode = HDB_RLOCK;
+
+ code = kh_hdb_lock(context, kh, hmode);
+
+ k5_mutex_unlock(kh->lock);
+
+ return code;
+}
+
+static krb5_error_code
+kh_db_unlock(krb5_context context)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ krb5_error_code code;
+
+ if (kh == NULL)
+ return KRB5_KDB_DBTYPE_NOSUP;
+
+ code = k5_mutex_lock(kh->lock);
+ if (code != 0)
+ return code;
+
+ code = kh_hdb_unlock(context, kh);
+
+ k5_mutex_unlock(kh->lock);
+
+ return code;
+}
+
+krb5_error_code
+kh_get_principal(krb5_context context,
+ kh_db_context *kh,
+ krb5_const_principal princ,
+ unsigned int hflags,
+ krb5_db_entry *kentry)
+{
+ krb5_error_code code;
+ Principal *hprinc = NULL;
+ hdb_entry_ex *hentry = NULL;
+
+ code = kh_marshal_Principal(context, princ, &hprinc);
+ if (code != 0)
+ return code;
+
+ code = kh_hdb_open(context, kh, kh->mode, 0);
+ if (code != 0) {
+ kh_free_Principal(context, hprinc);
+ return code;
+ }
+
+ hentry = k5alloc(sizeof(*hentry), &code);
+ if (code != 0) {
+ kh_free_Principal(context, hprinc);
+ return code;
+ }
+
+ code = kh_hdb_fetch(context, kh, hprinc, hflags, hentry);
+ if (code != 0) {
+ kh_hdb_close(context, kh);
+ kh_free_Principal(context, hprinc);
+ return code;
+ }
+
+ code = kh_unmarshal_hdb_entry(context, &hentry->entry, kentry);
+ if (code == 0) {
+ kentry->e_length = sizeof(*hentry);
+ kentry->e_data = (krb5_octet *)hentry;
+ } else {
+ kh_hdb_free_entry(context, kh, hentry);
+ free(hentry);
+ }
+
+ kh_hdb_close(context, kh);
+ kh_free_Principal(context, hprinc);
+
+ return code;
+}
+
+static krb5_boolean
+kh_is_master_key_principal(krb5_context context,
+ krb5_const_principal princ)
+{
+ return krb5_princ_size(context, princ) == 2 &&
+ data_eq_string(princ->data[0], "K") &&
+ data_eq_string(princ->data[1], "M");
+}
+
+static krb5_error_code
+kh_is_tgs_principal(krb5_context context,
+ krb5_const_principal princ)
+{
+ return krb5_princ_size(context, princ) == 2 &&
+ data_eq_string(princ->data[0], KRB5_TGS_NAME);
+}
+
+static krb5_error_code
+kh_get_master_key_principal(krb5_context context,
+ kh_db_context *kh,
+ krb5_const_principal princ,
+ krb5_db_entry *kentry,
+ int *nentries)
+{
+ krb5_error_code code;
+ krb5_key_data *key_data;
+ krb5_timestamp now;
+
+ memset(kentry, 0, sizeof(*kentry));
+ *nentries = 0;
+
+ kentry->magic = KRB5_KDB_MAGIC_NUMBER;
+ kentry->len = KRB5_KDB_V1_BASE_LENGTH;
+ kentry->attributes = KRB5_KDB_DISALLOW_ALL_TIX;
+
+ if (princ == NULL)
+ code = krb5_parse_name(context, KRB5_KDB_M_NAME, &kentry->princ);
+ else
+ code = krb5_copy_principal(context, princ, &kentry->princ);
+ if (code != 0)
+ return code;
+
+ now = time(NULL);
+
+ code = krb5_dbe_update_mod_princ_data(context, kentry, now, kentry->princ);
+ if (code != 0) {
+ kh_kdb_free_entry(context, kh, kentry);
+ return code;
+ }
+
+ /* Return a dummy principal */
+ kentry->n_key_data = 1;
+ kentry->key_data = k5alloc(sizeof(krb5_key_data), &code);
+ if (code != 0) {
+ kh_kdb_free_entry(context, kh, kentry);
+ return code;
+ }
+
+ key_data = &kentry->key_data[0];
+
+ key_data->key_data_ver = KRB5_KDB_V1_KEY_DATA_ARRAY;
+ key_data->key_data_kvno = 1;
+ key_data->key_data_type[0] = ENCTYPE_UNKNOWN;
+
+ *nentries = 1;
+
+ return 0;
+}
+
+static krb5_error_code
+kh_db_get_principal(krb5_context context,
+ krb5_const_principal princ,
+ unsigned int kflags,
+ krb5_db_entry *kentry,
+ int *nentries,
+ krb5_boolean *more)
+{
+ krb5_error_code code;
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ unsigned int hflags;
+
+ *nentries = 0;
+ *more = FALSE;
+ memset(kentry, 0, sizeof(*kentry));
+
+ if (kh == NULL)
+ return KRB5_KDB_DBNOTINITED;
+
+ if (kh_is_master_key_principal(context, princ))
+ return kh_get_master_key_principal(context, kh, princ,
+ kentry, nentries);
+
+ code = k5_mutex_lock(kh->lock);
+ if (code != 0)
+ return code;
+
+ hflags = 0;
+ if (kflags & KRB5_KDB_FLAG_CANONICALIZE)
+ hflags |= HDB_F_CANON;
+ if (kflags & (KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY |
+ KRB5_KDB_FLAG_INCLUDE_PAC))
+ hflags |= HDB_F_GET_CLIENT;
+ else if (kh_is_tgs_principal(context, princ))
+ hflags |= HDB_F_GET_KRBTGT;
+ else
+ hflags |= HDB_F_GET_ANY;
+
+ code = kh_get_principal(context, kh, princ, hflags, kentry);
+ switch (code) {
+ case 0:
+ *nentries = 1;
+ break;
+ case KRB5_KDB_NOENTRY:
+ code = 0;
+ break;
+ default:
+ break;
+ }
+
+ k5_mutex_unlock(kh->lock);
+
+ return code;
+}
+
+static krb5_error_code
+kh_db_free_principal(krb5_context context,
+ krb5_db_entry *entry,
+ int count)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ krb5_error_code code;
+ int i;
+
+ code = k5_mutex_lock(kh->lock);
+ if (code != 0)
+ return code;
+
+ for (i = 0; i < count; i++)
+ kh_kdb_free_entry(context, kh, &entry[i]);
+
+ k5_mutex_unlock(kh->lock);
+
+ return 0;
+}
+
+static krb5_error_code
+kh_put_principal(krb5_context context,
+ kh_db_context *kh,
+ krb5_db_entry *kentry)
+{
+ krb5_error_code code;
+ hdb_entry_ex *hentry = NULL;
+ unsigned int hflags;
+
+ hflags = 0;
+
+ if ((kentry->attributes & KRB5_KDB_NEW_PRINC) == 0)
+ hflags |= HDB_F_REPLACE;
+
+ hentry = k5alloc(sizeof(*hentry), &code);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_marshal_hdb_entry(context, kentry, &hentry->entry);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_hdb_open(context, kh, kh->mode, 0);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_hdb_store(context, kh, hflags, hentry);
+ if (code != 0) {
+ kh_hdb_close(context, kh);
+ goto cleanup;
+ }
+
+ if (kentry->e_data != NULL) {
+ assert(kentry->e_length == sizeof(hdb_entry_ex));
+ kh_hdb_free_entry(context, kh, KH_DB_ENTRY(kentry));
+ free(kentry->e_data);
+ }
+
+ kentry->e_length = sizeof(*hentry);
+ kentry->e_data = (krb5_octet *)hentry;
+ hentry = NULL;
+
+ kh_hdb_close(context, kh);
+
+cleanup:
+ if (hentry != NULL) {
+ kh_hdb_free_entry(context, kh, hentry);
+ free(hentry);
+ }
+
+ return code;
+}
+
+static krb5_error_code
+kh_db_put_principal(krb5_context context,
+ krb5_db_entry *entries,
+ int *nentries,
+ char **db_args)
+{
+ krb5_error_code code;
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ int i;
+
+ if (kh == NULL)
+ return KRB5_KDB_DBNOTINITED;
+
+ code = k5_mutex_lock(kh->lock);
+ if (code != 0)
+ return code;
+
+ for (i = 0; i < *nentries; i++) {
+ code = kh_put_principal(context, kh, &entries[i]);
+ if (code != 0)
+ break;
+ }
+
+ k5_mutex_unlock(kh->lock);
+
+ return code;
+}
+
+static krb5_error_code
+kh_delete_principal(krb5_context context,
+ kh_db_context *kh,
+ krb5_const_principal princ)
+{
+ krb5_error_code code;
+ Principal *hprinc;
+
+ code = kh_marshal_Principal(context, princ, &hprinc);
+ if (code != 0)
+ return code;
+
+ code = kh_hdb_open(context, kh, kh->mode, 0);
+ if (code != 0) {
+ kh_free_Principal(context, hprinc);
+ return code;
+ }
+
+ code = kh_hdb_remove(context, kh, hprinc);
+
+ kh_hdb_close(context, kh);
+ kh_free_Principal(context, hprinc);
+
+ return code;
+}
+
+static krb5_error_code
+kh_db_delete_principal(krb5_context context,
+ krb5_const_principal princ,
+ int *nentries)
+{
+ krb5_error_code code;
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+
+ if (kh == NULL)
+ return KRB5_KDB_DBNOTINITED;
+
+ code = k5_mutex_lock(kh->lock);
+ if (code != 0)
+ return code;
+
+ code = kh_delete_principal(context, kh, princ);
+
+ *nentries = (code == 0) ? 1 : 0;
+
+ k5_mutex_unlock(kh->lock);
+
+ return code;
+}
+
+static krb5_error_code
+kh_db_iterate(krb5_context context,
+ char *match_entry,
+ int (*func)(krb5_pointer, krb5_db_entry *),
+ krb5_pointer func_arg)
+{
+ krb5_error_code code;
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ hdb_entry_ex hentry;
+ unsigned int hflags = HDB_F_GET_ANY;
+
+ if (kh == NULL)
+ return KRB5_KDB_DBNOTINITED;
+
+ code = k5_mutex_lock(kh->lock);
+ if (code != 0)
+ return code;
+
+ memset(&hentry, 0, sizeof(hentry));
+
+ code = kh_hdb_open(context, kh, kh->mode, 0);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_hdb_firstkey(context, kh, hflags, &hentry);
+ while (code == 0) {
+ krb5_db_entry kentry;
+
+ if (kh_unmarshal_hdb_entry(context, &hentry.entry, &kentry) == 0) {
+ code = (*func)(func_arg, &kentry);
+ kh_kdb_free_entry(context, kh, &kentry);
+ }
+
+ kh_hdb_free_entry(context, kh, &hentry);
+
+ if (code != 0)
+ break;
+
+ code = kh_hdb_nextkey(context, kh, hflags, &hentry);
+ }
+
+ if (code == KRB5_KDB_NOENTRY) {
+ krb5_db_entry kentry;
+ int nentries;
+
+ /* Return the fake master key principal */
+ if (kh_get_master_key_principal(context, kh, NULL,
+ &kentry, &nentries) == 0) {
+ code = (*func)(func_arg, &kentry);
+ kh_kdb_free_entry(context, kh, &kentry);
+ }
+
+ code = 0;
+ }
+
+ kh_hdb_close(context, kh);
+
+cleanup:
+ k5_mutex_unlock(kh->lock);
+
+ return 0;
+}
+
+static krb5_error_code
+kh_fetch_master_key(krb5_context context,
+ krb5_principal name,
+ krb5_keyblock *key,
+ krb5_kvno *kvno,
+ char *db_args)
+{
+ return 0;
+}
+
+static krb5_error_code
+kh_fetch_master_key_list(krb5_context context,
+ krb5_principal mname,
+ const krb5_keyblock *key,
+ krb5_kvno kvno,
+ krb5_keylist_node **mkeys_list)
+{
+ /* just create a dummy one so that the KDC doesn't balk */
+ krb5_keylist_node *mkey;
+ krb5_error_code code;
+
+ mkey = k5alloc(sizeof(*mkey), &code);
+ if (code != 0)
+ return code;
+
+ mkey->keyblock.magic = KV5M_KEYBLOCK;
+ mkey->keyblock.enctype = ENCTYPE_UNKNOWN;
+ mkey->kvno = 1;
+
+ *mkeys_list = mkey;
+
+ return 0;
+}
+
+static void *
+kh_db_alloc(krb5_context context, void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+static void
+kh_db_free(krb5_context context, void *ptr)
+{
+ free(ptr);
+}
+
+static krb5_error_code
+kh_set_master_key(krb5_context context,
+ char *pwd,
+ krb5_keyblock *kkey)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ krb5_error_code code;
+ EncryptionKey hkey;
+
+ if (kh == NULL)
+ return KRB5_KDB_DBNOTINITED;
+
+ if (kkey->enctype == ENCTYPE_UNKNOWN)
+ return 0;
+
+ code = k5_mutex_lock(kh->lock);
+ if (code != 0)
+ return code;
+
+ KH_MARSHAL_KEY(kkey, &hkey);
+
+ code = kh_hdb_set_master_key(context, kh, &hkey);
+
+ k5_mutex_unlock(kh->lock);
+
+ return code;
+}
+
+static krb5_error_code
+kh_get_master_key(krb5_context context,
+ krb5_keyblock **pkey)
+{
+ krb5_error_code code;
+ krb5_keyblock *key;
+
+ /*
+ * The Heimdal master key interface is opaque; we can't
+ * return the master key without poking into internal data
+ * structures that would make this bridge even more brittle.
+ * So, we just return a dummy key.
+ */
+ key = k5alloc(sizeof(krb5_keyblock), &code);
+ if (code != 0)
+ return code;
+
+ key->magic = KV5M_KEYBLOCK;
+ key->enctype = ENCTYPE_UNKNOWN;
+
+ *pkey = key;
+
+ return 0;
+}
+
+static krb5_error_code
+kh_promote_db(krb5_context context,
+ char *conf_section,
+ char **db_args)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ krb5_error_code code;
+ char *name;
+
+ if (kh == NULL)
+ return KRB5_KDB_DBNOTINITED;
+
+ if (kh->hdb->hdb_name == NULL)
+ return KRB5_KDB_DBTYPE_NOSUP;
+
+ if (asprintf(&name, "%s~", kh->hdb->hdb_name) < 0)
+ return ENOMEM;
+
+ code = k5_mutex_lock(kh->lock);
+ if (code != 0) {
+ free(name);
+ return code;
+ }
+
+ code = kh_hdb_rename(context, kh, name);
+
+ k5_mutex_unlock(kh->lock);
+ free(name);
+
+ return code;
+}
+
+krb5_error_code
+kh_decrypt_key(krb5_context context,
+ kh_db_context *kh,
+ const krb5_key_data *key_data,
+ krb5_keyblock *kkey,
+ krb5_keysalt *keysalt)
+{
+ krb5_error_code code;
+ Key hkey;
+
+ memset(&hkey, 0, sizeof(hkey));
+
+ hkey.key.keytype = key_data->key_data_type[0];
+ hkey.key.keyvalue.data = k5alloc(key_data->key_data_length[0], &code);
+ if (code != 0)
+ return code;
+
+ memcpy(hkey.key.keyvalue.data, key_data->key_data_contents[0],
+ key_data->key_data_length[0]);
+ hkey.key.keyvalue.length = key_data->key_data_length[0];
+
+ code = kh_hdb_unseal_key(context, kh, &hkey);
+ if (code != 0) {
+ memset(hkey.key.keyvalue.data, 0, hkey.key.keyvalue.length);
+ free(hkey.key.keyvalue.data);
+ return code;
+ }
+
+ kkey->magic = KV5M_KEYBLOCK;
+ kkey->enctype = hkey.key.keytype;
+ kkey->contents = hkey.key.keyvalue.data;
+ kkey->length = hkey.key.keyvalue.length;
+
+ if (keysalt != NULL) {
+ keysalt->type = key_data->key_data_type[1];
+ keysalt->data.data = k5alloc(key_data->key_data_length[1], &code);
+ if (code != 0) {
+ memset(hkey.key.keyvalue.data, 0, hkey.key.keyvalue.length);
+ free(hkey.key.keyvalue.data);
+ return code;
+ }
+
+ memcpy(keysalt->data.data, key_data->key_data_contents[1],
+ key_data->key_data_length[1]);
+ keysalt->data.length = key_data->key_data_length[1];
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+kh_dbekd_decrypt_key_data(krb5_context context,
+ const krb5_keyblock *mkey,
+ const krb5_key_data *key_data,
+ krb5_keyblock *kkey,
+ krb5_keysalt *keysalt)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ krb5_error_code code;
+
+ if (kh == NULL)
+ return KRB5_KDB_DBNOTINITED;
+
+ if (mkey->enctype != ENCTYPE_UNKNOWN)
+ code = krb5_dbekd_def_decrypt_key_data(context, mkey, key_data,
+ kkey, keysalt);
+ else
+ code = kh_decrypt_key(context, kh, key_data, kkey, keysalt);
+
+ return code;
+}
+
+static krb5_error_code
+kh_encrypt_key(krb5_context context,
+ kh_db_context *kh,
+ const krb5_keyblock *kkey,
+ const krb5_keysalt *keysalt,
+ int keyver,
+ krb5_key_data *key_data)
+{
+ krb5_error_code code;
+ Key hkey;
+
+ memset(&hkey, 0, sizeof(hkey));
+ memset(key_data, 0, sizeof(*key_data));
+
+ hkey.key.keytype = kkey->enctype;
+ hkey.key.keyvalue.data = k5alloc(kkey->length, &code);
+ if (code != 0)
+ return code;
+
+ memcpy(hkey.key.keyvalue.data, kkey->contents, kkey->length);
+ hkey.key.keyvalue.length = kkey->length;
+
+ code = kh_hdb_seal_key(context, kh, &hkey);
+ if (code != 0) {
+ memset(hkey.key.keyvalue.data, 0, hkey.key.keyvalue.length);
+ free(hkey.key.keyvalue.data);
+ return code;
+ }
+
+ key_data->key_data_ver = KRB5_KDB_V1_KEY_DATA_ARRAY;
+ key_data->key_data_kvno = keyver;
+ key_data->key_data_type[0] = hkey.key.keytype;
+ key_data->key_data_contents[0] = hkey.key.keyvalue.data;
+ key_data->key_data_length[0] = hkey.key.keyvalue.length;
+
+ if (keysalt != NULL) {
+ key_data->key_data_type[1] = keysalt->type;
+ key_data->key_data_contents[1] = k5alloc(keysalt->data.length, &code);
+ if (code != 0) {
+ memset(hkey.key.keyvalue.data, 0, hkey.key.keyvalue.length);
+ free(hkey.key.keyvalue.data);
+ return code;
+ }
+
+ memcpy(key_data->key_data_contents[1],
+ keysalt->data.data, keysalt->data.length);
+ key_data->key_data_length[1] = keysalt->data.length;
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+kh_dbekd_encrypt_key_data(krb5_context context,
+ const krb5_keyblock *mkey,
+ const krb5_keyblock *kkey,
+ const krb5_keysalt *keysalt,
+ int keyver,
+ krb5_key_data *key_data)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ krb5_error_code code;
+
+ if (kh == NULL)
+ return KRB5_KDB_DBNOTINITED;
+
+ /* For migration */
+ if (mkey->enctype != ENCTYPE_UNKNOWN)
+ code = krb5_dbekd_def_encrypt_key_data(context, mkey, kkey,
+ keysalt, keyver, key_data);
+ else
+ code = kh_encrypt_key(context, kh, kkey, keysalt, keyver, key_data);
+
+ return code;
+}
+
+/*
+ * Invoke methods
+ */
+
+static krb5_error_code
+kh_db_check_allowed_to_delegate(krb5_context context,
+ unsigned int method,
+ const krb5_data *req_data,
+ krb5_data *rep_data)
+{
+ kdb_check_allowed_to_delegate_req *req;
+ krb5_error_code code;
+ hdb_entry_ex *hentry;
+ HDB_extension *ext;
+ HDB_Ext_Constrained_delegation_acl *acl;
+ unsigned int i;
+
+ req = (kdb_check_allowed_to_delegate_req *)req_data->data;
+ hentry = KH_DB_ENTRY(req->server);
+ ext = kh_hdb_find_extension(&hentry->entry,
+ choice_HDB_extension_data_allowed_to_delegate_to);
+
+ code = KRB5KDC_ERR_POLICY;
+
+ if (ext != NULL) {
+ acl = &ext->data.u.allowed_to_delegate_to;
+
+ for (i = 0; i < acl->len; i++) {
+ krb5_principal princ;
+
+ if (kh_unmarshal_Principal(context, &acl->val[i], &princ) == 0) {
+ if (krb5_principal_compare(context, req->proxy, princ)) {
+ code = 0;
+ krb5_free_principal(context, princ);
+ break;
+ }
+ krb5_free_principal(context, princ);
+ }
+ }
+ }
+
+ return code;
+}
+
+static struct _kh_invoke_fn {
+ unsigned int method;
+ krb5_error_code (*function)(krb5_context, unsigned int,
+ const krb5_data *, krb5_data *);
+} kh_invoke_vtable[] = {
+ { KRB5_KDB_METHOD_CHECK_POLICY_AS, kh_db_check_policy_as },
+ { KRB5_KDB_METHOD_SIGN_AUTH_DATA, kh_db_sign_auth_data },
+ { KRB5_KDB_METHOD_CHECK_ALLOWED_TO_DELEGATE, kh_db_check_allowed_to_delegate },
+};
+
+static krb5_error_code
+kh_db_invoke(krb5_context context,
+ unsigned int method,
+ const krb5_data *req,
+ krb5_data *rep)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ size_t i;
+ krb5_error_code code;
+
+ if (kh == NULL)
+ return KRB5_KDB_DBNOTINITED;
+
+ code = k5_mutex_lock(kh->lock);
+ if (code != 0)
+ return code;
+
+ code = KRB5_KDB_DBTYPE_NOSUP;
+
+ for (i = 0;
+ i < sizeof(kh_invoke_vtable) / sizeof(kh_invoke_vtable[0]);
+ i++) {
+ struct _kh_invoke_fn *fn = &kh_invoke_vtable[i];
+
+ if (fn->method == method) {
+ code = (*fn->function)(context, method, req, rep);
+ break;
+ }
+ }
+
+ k5_mutex_unlock(kh->lock);
+
+ return code;
+}
+
+kdb_vftabl kdb_function_table = {
+ 1,
+ 0,
+ kh_init,
+ kh_fini,
+ kh_init_module,
+ kh_fini_module,
+ kh_db_create,
+ kh_db_destroy,
+ kh_db_get_age,
+ kh_db_set_option,
+ kh_db_lock,
+ kh_db_unlock,
+ kh_db_get_principal,
+ kh_db_free_principal,
+ kh_db_put_principal,
+ kh_db_delete_principal,
+ kh_db_iterate,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ kh_db_alloc,
+ kh_db_free,
+ kh_set_master_key,
+ kh_get_master_key,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ kh_fetch_master_key,
+ NULL,
+ kh_fetch_master_key_list,
+ NULL,
+ NULL,
+ NULL,
+ kh_promote_db,
+ kh_dbekd_decrypt_key_data,
+ kh_dbekd_encrypt_key_data,
+ kh_db_invoke,
+};
+
diff --git a/src/plugins/kdb/hdb/kdb_hdb.h b/src/plugins/kdb/hdb/kdb_hdb.h
new file mode 100644
index 0000000..9cfbef6
--- /dev/null
+++ b/src/plugins/kdb/hdb/kdb_hdb.h
@@ -0,0 +1,172 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/hdb/kdb_hdb.c
+ *
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#ifndef KRB5_KDB_HDB_H
+#define KRB5_KDB_HDB_H
+
+#include "k5-plugin.h"
+#include "hdb.h"
+#include "windc_plugin.h"
+
+typedef krb5_int32 heim_error_code;
+
+typedef struct _kh_db_context {
+ k5_mutex_t *lock;
+ heim_context hcontext;
+ HDB *hdb;
+ int mode;
+
+ /* libkrb5 APIs */
+ struct plugin_file_handle *libkrb5;
+ heim_error_code (*heim_init_context)(heim_context *);
+ void (*heim_free_context)(heim_context);
+ void (*heim_free_principal)(heim_context, Principal *);
+ heim_error_code (*heim_free_addresses)(heim_context, HostAddresses *);
+ void (*heim_pac_free)(heim_context, heim_pac);
+ heim_error_code (*heim_pac_parse)(heim_context, const void *,
+ size_t, heim_pac *);
+ heim_error_code (*heim_pac_verify)(heim_context, const heim_pac,
+ time_t, const Principal *,
+ const EncryptionKey *,
+ const EncryptionKey *);
+ heim_error_code (*heim_pac_sign)(heim_context, heim_pac,
+ time_t, Principal *,
+ const EncryptionKey *,
+ const EncryptionKey *,
+ heim_octet_string *);
+
+ /* libhdb APIs */
+ struct plugin_file_handle *libhdb;
+ heim_error_code (*hdb_create)(heim_context, HDB **, const char *);
+ heim_error_code (*hdb_seal_key)(heim_context, HDB *, Key *);
+ heim_error_code (*hdb_unseal_key)(heim_context, HDB *, Key *);
+ heim_error_code (*hdb_set_master_key)(heim_context, HDB *, EncryptionKey *);
+ void (*hdb_free_entry)(heim_context, hdb_entry_ex *);
+
+ /* widdc SPIs */
+ struct plugin_dir_handle windc_plugins;
+ krb5plugin_windc_ftable *windc;
+ void *windc_ctx;
+} kh_db_context;
+
+#define KH_DB_CONTEXT(_context) \
+ ((kh_db_context *)(_context)->dal_handle->db_context)
+
+#define KH_DB_ENTRY(_entry) \
+ ((hdb_entry_ex *)(_entry)->e_data)
+
+/* kdb_hdb.c */
+
+krb5_error_code
+kh_map_error(heim_error_code code);
+
+krb5_error_code
+kh_get_principal(krb5_context context,
+ kh_db_context *kh,
+ krb5_const_principal princ,
+ unsigned int hflags,
+ krb5_db_entry *kentry);
+
+void
+kh_kdb_free_entry(krb5_context context,
+ kh_db_context *kh,
+ krb5_db_entry *entry);
+
+krb5_error_code
+kh_decrypt_key(krb5_context context,
+ kh_db_context *kh,
+ const krb5_key_data *key_data,
+ krb5_keyblock *dbkey,
+ krb5_keysalt *keysalt);
+
+void
+kh_hdb_free_entry(krb5_context context,
+ kh_db_context *kh,
+ hdb_entry_ex *entry);
+
+/* kdb_marshal.c */
+
+#define KH_MARSHAL_KEY(_kkey, _hkey) do { \
+ (_hkey)->keytype = (_kkey)->enctype; \
+ (_hkey)->keyvalue.data = (_kkey)->contents; \
+ (_hkey)->keyvalue.length = (_kkey)->length; \
+ } while (0)
+
+krb5_error_code
+kh_marshal_Principal(krb5_context context,
+ krb5_const_principal kprinc,
+ Principal **out_hprinc);
+
+krb5_error_code
+kh_unmarshal_Principal(krb5_context context,
+ const Principal *hprinc,
+ krb5_principal *out_kprinc);
+
+void
+kh_free_Principal(krb5_context context,
+ Principal *principal);
+
+void
+kh_free_Event(krb5_context context,
+ Event *event);
+
+void
+kh_free_HostAddresses(krb5_context context,
+ HostAddresses *addrs);
+
+krb5_error_code
+kh_unmarshal_hdb_entry(krb5_context context,
+ const hdb_entry *hentry,
+ krb5_db_entry *kentry);
+
+krb5_error_code
+kh_marshal_hdb_entry(krb5_context context,
+ const krb5_db_entry *kentry,
+ hdb_entry *hentry);
+
+/* kdb_windc.c */
+
+krb5_error_code
+kh_db_sign_auth_data(krb5_context context,
+ unsigned int method,
+ const krb5_data *req_data,
+ krb5_data *rep_data);
+
+krb5_error_code
+kh_db_check_policy_as(krb5_context context,
+ unsigned int method,
+ const krb5_data *req_data,
+ krb5_data *rep_data);
+
+krb5_error_code
+kh_hdb_windc_init(krb5_context context,
+ const char *libdir,
+ kh_db_context *kh);
+
+#endif /* KRB5_KDB_HDB_H */
+
diff --git a/src/plugins/kdb/hdb/kdb_marshal.c b/src/plugins/kdb/hdb/kdb_marshal.c
new file mode 100644
index 0000000..17bbdc8
--- /dev/null
+++ b/src/plugins/kdb/hdb/kdb_marshal.c
@@ -0,0 +1,810 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/hdb/kdb_marshal.c
+ *
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#include "k5-int.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <db.h>
+#include <stdio.h>
+#include <errno.h>
+#include <utime.h>
+#include "kdb5.h"
+#include "kdb_hdb.h"
+
+void
+kh_free_Principal(krb5_context context,
+ Principal *principal)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+
+ if (principal != NULL)
+ (*kh->heim_free_principal)(kh->hcontext, principal);
+}
+
+void
+kh_free_Event(krb5_context context,
+ Event *event)
+{
+ kh_free_Principal(context, event->principal);
+}
+
+void
+kh_free_HostAddresses(krb5_context context,
+ HostAddresses *addrs)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+
+ if (addrs != NULL)
+ (*kh->heim_free_addresses)(kh->hcontext, addrs);
+}
+
+#if 0
+static krb5_error_code
+kh_marshal_octet_string(krb5_context context,
+ const krb5_data *in_data,
+ heim_octet_string *out_data)
+{
+ out_data->data = malloc(in_data->length);
+ if (out_data->data == NULL)
+ return ENOMEM;
+
+ memcpy(out_data->data, in_data->data, in_data->length);
+
+ out_data->length = in_data->length;
+
+ return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_octet_string_contents(krb5_context context,
+ const heim_octet_string *in_data,
+ krb5_data *out_data)
+{
+ out_data->magic = KV5M_DATA;
+ out_data->data = malloc(in_data->length);
+ if (out_data->data == NULL)
+ return ENOMEM;
+
+ memcpy(out_data->data, in_data->data, in_data->length);
+
+ out_data->length = in_data->length;
+
+ return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_octet_string(krb5_context context,
+ heim_octet_string *in_data,
+ krb5_data **out_data)
+{
+ krb5_error_code code;
+
+ *out_data = k5alloc(sizeof(krb5_data), &code);
+ if (code != 0)
+ return code;
+
+ code = kh_unmarshal_octet_string_contents(context, in_data, *out_data);
+ if (code != 0) {
+ free(*out_data);
+ *out_data = NULL;
+ return code;
+ }
+
+ return 0;
+}
+#endif
+
+static krb5_error_code
+kh_marshal_general_string(krb5_context context,
+ const krb5_data *in_data,
+ heim_general_string *out_str)
+{
+ *out_str = malloc(in_data->length + 1);
+ if (*out_str == NULL)
+ return ENOMEM;
+
+ memcpy(*out_str, in_data->data, in_data->length);
+ (*out_str)[in_data->length] = '\0';
+
+ return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_general_string_contents(krb5_context context,
+ const heim_general_string in_str,
+ krb5_data *out_data)
+{
+ out_data->magic = KV5M_DATA;
+ out_data->length = strlen(in_str);
+ out_data->data = malloc(out_data->length);
+ if (out_data->data == NULL)
+ return ENOMEM;
+
+ memcpy(out_data->data, in_str, out_data->length);
+ return 0;
+}
+
+#if 0
+static krb5_error_code
+kh_unmarshal_general_string(krb5_context context,
+ const heim_general_string in_str,
+ krb5_data **out_data)
+{
+ krb5_error_code code;
+
+ *out_data = k5alloc(sizeof(krb5_data), &code);
+ if (code != 0)
+ return code;
+
+ code = kh_unmarshal_general_string_contents(context, in_str, *out_data);
+ if (code != 0) {
+ free(*out_data);
+ *out_data = NULL;
+ return code;
+ }
+
+ return 0;
+}
+#endif
+
+krb5_error_code
+kh_marshal_Principal(krb5_context context,
+ krb5_const_principal kprinc,
+ Principal **out_hprinc)
+{
+ krb5_error_code code;
+ Principal *hprinc;
+ int i;
+
+ hprinc = k5alloc(sizeof(*hprinc), &code);
+ if (code != 0)
+ return code;
+
+ hprinc->name.name_type = kprinc->type;
+ hprinc->name.name_string.val = k5alloc(kprinc->length *
+ sizeof(heim_general_string),
+ &code);
+ if (code != 0) {
+ kh_free_Principal(context, hprinc);
+ return code;
+ }
+ for (i = 0; i < kprinc->length; i++) {
+ code = kh_marshal_general_string(context, &kprinc->data[i],
+ &hprinc->name.name_string.val[i]);
+ if (code != 0) {
+ kh_free_Principal(context, hprinc);
+ return code;
+ }
+ hprinc->name.name_string.len++;
+ }
+ code = kh_marshal_general_string(context, &kprinc->realm, &hprinc->realm);
+ if (code != 0) {
+ kh_free_Principal(context, hprinc);
+ return code;
+ }
+
+ *out_hprinc = hprinc;
+
+ return 0;
+}
+
+krb5_error_code
+kh_unmarshal_Principal(krb5_context context,
+ const Principal *hprinc,
+ krb5_principal *out_kprinc)
+{
+ krb5_error_code code;
+ krb5_principal kprinc;
+ unsigned int i;
+
+ kprinc = k5alloc(sizeof(*kprinc), &code);
+ if (code != 0)
+ return code;
+
+ kprinc->magic = KV5M_PRINCIPAL;
+ kprinc->type = hprinc->name.name_type;
+ kprinc->data = k5alloc(hprinc->name.name_string.len * sizeof(krb5_data),
+ &code);
+ if (code != 0) {
+ krb5_free_principal(context, kprinc);
+ return code;
+ }
+ for (i = 0; i < hprinc->name.name_string.len; i++) {
+ code = kh_unmarshal_general_string_contents(context,
+ hprinc->name.name_string.val[i],
+ &kprinc->data[i]);
+ if (code != 0) {
+ krb5_free_principal(context, kprinc);
+ return code;
+ }
+ kprinc->length++;
+ }
+ code = kh_unmarshal_general_string_contents(context,
+ hprinc->realm,
+ &kprinc->realm);
+ if (code != 0) {
+ krb5_free_principal(context, kprinc);
+ return code;
+ }
+
+ *out_kprinc = kprinc;
+
+ return 0;
+}
+
+static krb5_error_code
+kh_marshal_Event(krb5_context context,
+ const krb5_db_entry *kentry,
+ Event *event)
+{
+ krb5_error_code code;
+ krb5_timestamp mod_time = 0;
+ krb5_principal mod_princ = NULL;
+
+ memset(event, 0, sizeof(*event));
+
+ code = krb5_dbe_lookup_mod_princ_data(context, (krb5_db_entry *)kentry,
+ &mod_time, &mod_princ);
+ if (code != 0)
+ return code;
+
+ event->time = mod_time;
+
+ if (mod_princ != NULL) {
+ code = kh_marshal_Principal(context, mod_princ, &event->principal);
+ if (code != 0) {
+ krb5_free_principal(context, mod_princ);
+ return code;
+ }
+ }
+
+ krb5_free_principal(context, mod_princ);
+
+ return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_Event(krb5_context context,
+ const Event *event,
+ krb5_db_entry *kentry)
+{
+ krb5_error_code code;
+ krb5_principal princ = NULL;
+
+ if (event->principal != NULL) {
+ code = kh_unmarshal_Principal(context, event->principal, &princ);
+ if (code != 0)
+ return code;
+ }
+
+ code = krb5_dbe_update_mod_princ_data(context, kentry,
+ event->time, princ);
+
+ krb5_free_principal(context, princ);
+
+ return code;
+}
+
+static krb5_error_code
+kh_marshal_HDBFlags(krb5_context context,
+ krb5_flags kflags,
+ HDBFlags *hflags)
+{
+ memset(hflags, 0, sizeof(*hflags));
+
+ if (kflags & KRB5_KDB_DISALLOW_TGT_BASED)
+ hflags->initial = 1;
+ if ((kflags & KRB5_KDB_DISALLOW_FORWARDABLE) == 0)
+ hflags->forwardable = 1;
+ if ((kflags & KRB5_KDB_DISALLOW_PROXIABLE) == 0)
+ hflags->proxiable = 1;
+ if ((kflags & KRB5_KDB_DISALLOW_RENEWABLE) == 0)
+ hflags->renewable = 1;
+ if ((kflags & KRB5_KDB_DISALLOW_POSTDATED) == 0)
+ hflags->postdate = 1;
+ if ((kflags & KRB5_KDB_DISALLOW_SVR) == 0)
+ hflags->server = 1;
+ hflags->client = 1;
+ if (kflags & KRB5_KDB_DISALLOW_ALL_TIX)
+ hflags->invalid = 1;
+ if (kflags & KRB5_KDB_REQUIRES_PRE_AUTH)
+ hflags->require_preauth = 1;
+ if (kflags & KRB5_KDB_PWCHANGE_SERVICE)
+ hflags->change_pw = 1;
+ if (kflags & KRB5_KDB_REQUIRES_HW_AUTH)
+ hflags->require_hwauth = 1;
+ if (kflags & KRB5_KDB_OK_AS_DELEGATE)
+ hflags->ok_as_delegate = 1;
+ /* hflags->user_to_user */
+ /* hflags->immutable */
+ if (kflags & KRB5_KDB_OK_TO_AUTH_AS_DELEGATE)
+ hflags->trusted_for_delegation = 1;
+ /* hflags->allow_kerberos4 */
+ /* hflags->allow_digest */
+
+ return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_HDBFlags(krb5_context context,
+ HDBFlags hflags,
+ krb5_flags *kflags)
+{
+ *kflags = 0;
+
+ if (hflags.initial)
+ *kflags |= KRB5_KDB_DISALLOW_TGT_BASED;
+ if (!hflags.forwardable)
+ *kflags |= KRB5_KDB_DISALLOW_FORWARDABLE;
+ if (!hflags.proxiable)
+ *kflags |= KRB5_KDB_DISALLOW_PROXIABLE;
+ if (!hflags.renewable)
+ *kflags |= KRB5_KDB_DISALLOW_RENEWABLE;
+ if (!hflags.postdate)
+ *kflags |= KRB5_KDB_DISALLOW_POSTDATED;
+ if (!hflags.server)
+ *kflags |= KRB5_KDB_DISALLOW_SVR;
+ if (hflags.client)
+ ;
+ if (hflags.invalid)
+ *kflags |= KRB5_KDB_DISALLOW_ALL_TIX;
+ if (hflags.require_preauth)
+ *kflags |= KRB5_KDB_REQUIRES_PRE_AUTH;
+ if (hflags.change_pw)
+ *kflags |= KRB5_KDB_PWCHANGE_SERVICE;
+ if (hflags.require_hwauth)
+ *kflags |= KRB5_KDB_REQUIRES_HW_AUTH;
+ if (hflags.ok_as_delegate)
+ *kflags |= KRB5_KDB_OK_AS_DELEGATE;
+ if (hflags.user_to_user)
+ ;
+ if (hflags.immutable)
+ ;
+ if (hflags.trusted_for_delegation)
+ *kflags |= KRB5_KDB_OK_TO_AUTH_AS_DELEGATE;
+ if (hflags.allow_kerberos4)
+ ;
+ if (hflags.allow_digest)
+ ;
+ return 0;
+}
+
+static krb5_error_code
+kh_marshal_Key(krb5_context context,
+ const krb5_key_data *kkey,
+ Key *hkey)
+{
+ krb5_error_code code;
+
+ memset(hkey, 0, sizeof(*hkey));
+
+ hkey->key.keytype = kkey->key_data_type[0];
+ hkey->key.keyvalue.data = k5alloc(kkey->key_data_length[0], &code);
+ if (code != 0)
+ return code;
+ memcpy(hkey->key.keyvalue.data, kkey->key_data_contents[0],
+ kkey->key_data_length[0]);
+ hkey->key.keyvalue.length = kkey->key_data_length[0];
+
+ if (kkey->key_data_contents[1] != NULL) {
+ Salt *salt;
+
+ salt = k5alloc(sizeof(*salt), &code);
+ if (code != 0)
+ goto cleanup;
+
+ switch (kkey->key_data_type[1]) {
+ case KRB5_KDB_SALTTYPE_NORMAL:
+ salt->type = hdb_pw_salt;
+ break;
+ case KRB5_KDB_SALTTYPE_AFS3:
+ salt->type = hdb_afs3_salt;
+ break;
+ default:
+ salt->type = 0;
+ break;
+ }
+
+ salt->salt.data = k5alloc(kkey->key_data_length[1], &code);
+ if (code != 0) {
+ free(salt);
+ goto cleanup;
+ }
+ memcpy(salt->salt.data, kkey->key_data_contents[1],
+ kkey->key_data_length[1]);
+ salt->salt.length = kkey->key_data_length[1];
+
+ hkey->salt = salt;
+ }
+
+cleanup:
+ if (code != 0 && hkey->key.keyvalue.data != NULL)
+ free(hkey->key.keyvalue.data);
+
+ return code;
+}
+
+static krb5_error_code
+kh_unmarshal_Key(krb5_context context,
+ const hdb_entry *hentry,
+ const Key *hkey,
+ krb5_key_data *kkey)
+{
+ memset(kkey, 0, sizeof(*kkey));
+
+ kkey->key_data_ver = KRB5_KDB_V1_KEY_DATA_ARRAY;
+ kkey->key_data_kvno = hentry->kvno;
+
+ kkey->key_data_type[0] = hkey->key.keytype;
+ kkey->key_data_contents[0] = malloc(hkey->key.keyvalue.length);
+ if (kkey->key_data_contents[0] == NULL)
+ return ENOMEM;
+
+ memcpy(kkey->key_data_contents[0], hkey->key.keyvalue.data,
+ hkey->key.keyvalue.length);
+ kkey->key_data_length[0] = hkey->key.keyvalue.length;
+
+ if (hkey->salt != NULL) {
+ switch (hkey->salt->type) {
+ case hdb_pw_salt:
+ kkey->key_data_type[1] = KRB5_KDB_SALTTYPE_NORMAL;
+ break;
+ case hdb_afs3_salt:
+ kkey->key_data_type[1] = KRB5_KDB_SALTTYPE_AFS3;
+ break;
+ default:
+ kkey->key_data_type[1] = KRB5_KDB_SALTTYPE_SPECIAL;
+ break;
+ }
+
+ kkey->key_data_contents[1] = malloc(hkey->salt->salt.length);
+ if (kkey->key_data_contents[1] == NULL) {
+ memset(kkey->key_data_contents[0], 0, kkey->key_data_length[0]);
+ free(kkey->key_data_contents[0]);
+ return ENOMEM;
+ }
+ memcpy(kkey->key_data_contents[1], hkey->salt->salt.data,
+ hkey->salt->salt.length);
+ kkey->key_data_length[1] = hkey->salt->salt.length;
+ }
+
+ return 0;
+}
+
+/*
+ * Extension marshalers
+ */
+
+static krb5_error_code
+kh_marshal_HDB_extension_data_last_pw_change(krb5_context context,
+ const krb5_db_entry *kentry,
+ HDB_extension *hext)
+{
+ krb5_timestamp stamp;
+ krb5_error_code code;
+
+ code = krb5_dbe_lookup_last_pwd_change(context,
+ (krb5_db_entry *)kentry, &stamp);
+ if (code != 0)
+ return code;
+
+ hext->data.u.last_pw_change = stamp;
+
+ return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_HDB_extension_data_last_pw_change(krb5_context context,
+ HDB_extension *hext,
+ krb5_db_entry *kentry)
+{
+ return krb5_dbe_update_last_pwd_change(context, kentry,
+ hext->data.u.last_pw_change);
+}
+
+typedef krb5_error_code (*kh_hdb_marshal_extension_fn)(krb5_context,
+ const krb5_db_entry *,
+ HDB_extension *);
+
+typedef krb5_error_code (*kh_hdb_unmarshal_extension_fn)(krb5_context,
+ HDB_extension *,
+ krb5_db_entry *);
+
+struct {
+ kh_hdb_marshal_extension_fn marshal;
+ kh_hdb_unmarshal_extension_fn unmarshal;
+} kh_hdb_extension_vtable[] = {
+ { NULL, NULL }, /* choice_HDB_extension_data_asn1_ellipsis */
+ { NULL, NULL }, /* choice_HDB_extension_data_pkinit_acl */
+ { NULL, NULL }, /* choice_HDB_extension_data_pkinit_cert_hash */
+ { NULL, NULL }, /* choice_HDB_extension_data_allowed_to_delegate_to */
+ { NULL, NULL }, /* choice_HDB_extension_data_lm_owf */
+ { NULL, NULL }, /* choice_HDB_extension_data_password */
+ { NULL, NULL }, /* choice_HDB_extension_data_aliases */
+ { kh_marshal_HDB_extension_data_last_pw_change,
+ kh_unmarshal_HDB_extension_data_last_pw_change }
+};
+
+static const size_t kh_hdb_extension_count =
+ sizeof(kh_hdb_extension_vtable) / sizeof(kh_hdb_extension_vtable[0]);
+
+static krb5_error_code
+kh_marshal_HDB_extension(krb5_context context,
+ const krb5_db_entry *kentry,
+ HDB_extension *hext)
+{
+ kh_hdb_marshal_extension_fn marshal = NULL;
+
+ if (hext->data.element < kh_hdb_extension_count)
+ marshal = kh_hdb_extension_vtable[hext->data.element].marshal;
+
+ if (marshal == NULL)
+ return KRB5_KDB_DBTYPE_NOSUP;
+
+ return (*marshal)(context, kentry, hext);
+}
+
+static krb5_error_code
+kh_unmarshal_HDB_extension(krb5_context context,
+ HDB_extension *hext,
+ krb5_db_entry *kentry)
+{
+ kh_hdb_unmarshal_extension_fn unmarshal = NULL;
+
+ if (hext->data.element < kh_hdb_extension_count)
+ unmarshal = kh_hdb_extension_vtable[hext->data.element].unmarshal;
+
+ if (unmarshal == NULL)
+ return hext->mandatory ? KRB5_KDB_DBTYPE_NOSUP : 0;
+
+ return (*unmarshal)(context, hext, kentry);
+}
+
+static krb5_error_code
+kh_marshal_HDB_extensions(krb5_context context,
+ const krb5_db_entry *kentry,
+ HDB_extensions *hexts)
+{
+ unsigned int i;
+ krb5_error_code code;
+
+ hexts->val = k5alloc(kh_hdb_extension_count * sizeof(HDB_extension), &code);
+ if (code != 0)
+ return code;
+
+ hexts->len = 0;
+
+ for (i = 0; i < kh_hdb_extension_count; i++) {
+ HDB_extension *hext = &hexts->val[hexts->len];
+
+ hext->data.element = i;
+
+ code = kh_marshal_HDB_extension(context, kentry, hext);
+ if (code == KRB5_KDB_DBTYPE_NOSUP)
+ continue;
+ else if (code != 0)
+ break;
+
+ hexts->len++;
+ }
+
+ return code;
+}
+
+static krb5_error_code
+kh_unmarshal_HDB_extensions(krb5_context context,
+ HDB_extensions *hexts,
+ krb5_db_entry *kentry)
+{
+ unsigned int i;
+ krb5_error_code code = 0;
+
+ for (i = 0; i < hexts->len; i++) {
+ code = kh_unmarshal_HDB_extension(context, &hexts->val[i], kentry);
+ if (code != 0)
+ break;
+ }
+
+ return code;
+}
+
+krb5_error_code
+kh_marshal_hdb_entry(krb5_context context,
+ const krb5_db_entry *kentry,
+ hdb_entry *hentry)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ krb5_error_code code;
+ krb5_int16 kvno = 0;
+ int i;
+
+ memset(hentry, 0, sizeof(*hentry));
+
+ code = kh_marshal_Principal(context, kentry->princ, &hentry->principal);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_marshal_HDBFlags(context, kentry->attributes, &hentry->flags);
+ if (code != 0)
+ goto cleanup;
+
+ if (kentry->expiration) {
+ hentry->valid_end = k5alloc(sizeof(KerberosTime), &code);
+ if (code != 0)
+ goto cleanup;
+ *(hentry->valid_end) = kentry->expiration;
+ }
+ if (kentry->pw_expiration) {
+ hentry->pw_end = k5alloc(sizeof(KerberosTime), &code);
+ if (code != 0)
+ goto cleanup;
+ *(hentry->pw_end) = kentry->pw_expiration;
+ }
+ if (kentry->max_life) {
+ hentry->max_life = k5alloc(sizeof(unsigned int), &code);
+ if (code != 0)
+ goto cleanup;
+ *(hentry->max_life) = kentry->max_life;
+ }
+ if (kentry->max_renewable_life) {
+ hentry->max_renew = k5alloc(sizeof(unsigned int), &code);
+ if (code != 0)
+ goto cleanup;
+ *(hentry->max_renew) = kentry->max_renewable_life;
+ }
+
+ /* last_success */
+ /* last_failed */
+ /* fail_auth_count */
+ /* n_tl_data */
+
+ if ((kentry->attributes & KRB5_KDB_NEW_PRINC) == 0) {
+ hentry->modified_by = k5alloc(sizeof(Event), &code);
+ if (code != 0)
+ goto cleanup;
+ code = kh_marshal_Event(context, kentry, hentry->modified_by);
+ } else {
+ code = kh_marshal_Event(context, kentry, &hentry->created_by);
+ }
+ if (code != 0)
+ goto cleanup;
+
+ hentry->extensions = k5alloc(sizeof(HDB_extensions), &code);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_marshal_HDB_extensions(context, kentry, hentry->extensions);
+ if (code != 0)
+ goto cleanup;
+
+ hentry->keys.len = 0;
+ hentry->keys.val = k5alloc(kentry->n_key_data * sizeof(Key), &code);
+ if (code != 0)
+ goto cleanup;
+
+ for (i = 0; i < kentry->n_key_data; i++) {
+ code = kh_marshal_Key(context,
+ &kentry->key_data[i],
+ &hentry->keys.val[hentry->keys.len]);
+ if (code != 0)
+ goto cleanup;
+
+ if (kentry->key_data[i].key_data_kvno > kvno)
+ kvno = kentry->key_data[i].key_data_kvno;
+
+ hentry->keys.len++;
+ }
+
+ hentry->kvno = kvno;
+
+cleanup:
+ if (code != 0) {
+ hdb_entry_ex hext;
+
+ hext.ctx = NULL;
+ hext.entry = *hentry;
+ hext.free_entry = NULL;
+
+ kh_hdb_free_entry(context, kh, &hext);
+ memset(hentry, 0, sizeof(*hentry));
+ }
+
+ return code;
+}
+
+krb5_error_code
+kh_unmarshal_hdb_entry(krb5_context context,
+ const hdb_entry *hentry,
+ krb5_db_entry *kentry)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ krb5_error_code code;
+ unsigned int i;
+
+ memset(kentry, 0, sizeof(*kentry));
+
+ kentry->magic = KRB5_KDB_MAGIC_NUMBER;
+ kentry->len = KRB5_KDB_V1_BASE_LENGTH;
+
+ code = kh_unmarshal_Principal(context, hentry->principal, &kentry->princ);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_unmarshal_HDBFlags(context, hentry->flags, &kentry->attributes);
+ if (code != 0)
+ goto cleanup;
+
+ if (hentry->max_life != NULL)
+ kentry->max_life = *(hentry->max_life);
+ if (hentry->max_renew != NULL)
+ kentry->max_renewable_life = *(hentry->max_renew);
+ if (hentry->valid_end != NULL)
+ kentry->expiration = *(hentry->valid_end);
+ if (hentry->pw_end != NULL)
+ kentry->pw_expiration = *(hentry->pw_end);
+
+ /* last_success */
+ /* last_failed */
+ /* fail_auth_count */
+ /* n_tl_data */
+
+ code = kh_unmarshal_Event(context,
+ hentry->modified_by ? hentry->modified_by :
+ &hentry->created_by,
+ kentry);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_unmarshal_HDB_extensions(context, hentry->extensions, kentry);
+ if (code != 0)
+ goto cleanup;
+
+ kentry->key_data = k5alloc(hentry->keys.len * sizeof(krb5_key_data), &code);
+ if (code != 0)
+ goto cleanup;
+
+ for (i = 0; i < hentry->keys.len; i++) {
+ code = kh_unmarshal_Key(context, hentry,
+ &hentry->keys.val[i],
+ &kentry->key_data[i]);
+ if (code != 0)
+ goto cleanup;
+
+ kentry->n_key_data++;
+ }
+
+cleanup:
+ if (code != 0)
+ kh_kdb_free_entry(context, kh, kentry);
+
+ return code;
+}
+
diff --git a/src/plugins/kdb/hdb/kdb_windc.c b/src/plugins/kdb/hdb/kdb_windc.c
new file mode 100644
index 0000000..a419d29
--- /dev/null
+++ b/src/plugins/kdb/hdb/kdb_windc.c
@@ -0,0 +1,615 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/hdb/kdb_windc.c
+ *
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#include "k5-int.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <db.h>
+#include <stdio.h>
+#include <errno.h>
+#include <utime.h>
+#include "kdb5.h"
+#include "kdb_hdb.h"
+
+/*
+ * WinDC helpers
+ */
+
+static krb5_error_code
+kh_windc_pac_generate(krb5_context context,
+ kh_db_context *kh,
+ struct hdb_entry_ex *hentry,
+ heim_pac *pac)
+{
+ if (kh->windc == NULL || kh->windc->pac_generate == NULL)
+ return KRB5_KDB_DBTYPE_NOSUP;
+
+ return kh_map_error((*kh->windc->pac_generate)(kh->windc_ctx,
+ kh->hcontext,
+ hentry,
+ pac));
+}
+
+static krb5_error_code
+kh_windc_pac_verify(krb5_context context,
+ kh_db_context *kh,
+ const Principal *principal,
+ struct hdb_entry_ex *client,
+ struct hdb_entry_ex *server,
+ heim_pac *pac)
+{
+ if (kh->windc == NULL || kh->windc->pac_verify == NULL)
+ return KRB5_KDB_DBTYPE_NOSUP;
+
+ return kh_map_error((*kh->windc->pac_verify)(kh->windc_ctx,
+ kh->hcontext,
+ principal,
+ client,
+ server,
+ pac));
+}
+
+static krb5_error_code
+kh_windc_client_access(krb5_context context,
+ kh_db_context *kh,
+ struct hdb_entry_ex *client,
+ KDC_REQ *req,
+ heim_octet_string *e_data)
+{
+ if (kh->windc == NULL || kh->windc->client_access == NULL)
+ return KRB5_KDB_DBTYPE_NOSUP;
+
+ return kh_map_error((*kh->windc->client_access)(kh->windc_ctx,
+ kh->hcontext,
+ client,
+ req,
+ e_data));
+}
+
+static void
+kh_pac_free(krb5_context context, heim_pac pac)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+
+ assert(kh->heim_pac_free != NULL);
+
+ if (pac != NULL)
+ (*kh->heim_pac_free)(kh->hcontext, pac);
+}
+
+static krb5_error_code
+kh_pac_parse(krb5_context context,
+ const void *data,
+ size_t len,
+ heim_pac *pac)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+
+ return kh_map_error((*kh->heim_pac_parse)(kh->hcontext,
+ data,
+ len,
+ pac));
+}
+
+static krb5_error_code
+kh_pac_verify(krb5_context context,
+ const heim_pac pac,
+ time_t authtime,
+ const Principal *princ,
+ const EncryptionKey *server,
+ const EncryptionKey *krbtgt)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+
+ assert(kh->heim_pac_verify != NULL);
+
+ return kh_map_error((*kh->heim_pac_verify)(kh->hcontext,
+ pac,
+ authtime,
+ princ,
+ server,
+ krbtgt));
+}
+
+static krb5_error_code
+kh_pac_sign(krb5_context context,
+ heim_pac pac,
+ time_t authtime,
+ Principal *princ,
+ const EncryptionKey *server,
+ const EncryptionKey *krbtgt,
+ heim_octet_string *data)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+
+ assert(kh->heim_pac_sign != NULL);
+
+ return kh_map_error((*kh->heim_pac_sign)(kh->hcontext,
+ pac,
+ authtime,
+ princ,
+ server,
+ krbtgt,
+ data));
+}
+
+/*
+ * Get local TGS key for the realm of the supplied principal.
+ */
+static krb5_error_code
+kh_get_tgs_key(krb5_context context,
+ kh_db_context *kh,
+ const krb5_principal princ,
+ krb5_keyblock *krbtgt_keyblock)
+{
+ krb5_error_code code;
+ krb5_principal tgsname = NULL;
+ krb5_key_data *krbtgt_key = NULL;
+ krb5_db_entry krbtgt;
+
+ memset(&krbtgt, 0, sizeof(krbtgt));
+ krbtgt_keyblock->contents = NULL;
+
+ code = krb5_build_principal_ext(context,
+ &tgsname,
+ princ->realm.length,
+ princ->realm.data,
+ KRB5_TGS_NAME_SIZE,
+ KRB5_TGS_NAME,
+ princ->realm.length,
+ princ->realm.data,
+ 0);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_get_principal(context, kh, tgsname, HDB_F_GET_KRBTGT, &krbtgt);
+ if (code != 0)
+ goto cleanup;
+
+ code = krb5_dbe_find_enctype(context,
+ &krbtgt,
+ -1, /* ignore enctype */
+ -1, /* ignore salttype */
+ 0, /* highest kvno */
+ &krbtgt_key);
+ if (code != 0)
+ goto cleanup;
+ else if (krbtgt_key == NULL) {
+ code = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ goto cleanup;
+ }
+
+ code = kh_decrypt_key(context,
+ KH_DB_CONTEXT(context),
+ krbtgt_key,
+ krbtgt_keyblock,
+ NULL);
+ if (code != 0)
+ goto cleanup;
+
+cleanup:
+ kh_kdb_free_entry(context, KH_DB_CONTEXT(context), &krbtgt);
+ krb5_free_principal(context, tgsname);
+
+ return code;
+}
+
+krb5_error_code
+kh_db_sign_auth_data(krb5_context context,
+ unsigned int method,
+ const krb5_data *req_data,
+ krb5_data *rep_data)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ kdb_sign_auth_data_req *req = (kdb_sign_auth_data_req *)req_data->data;
+ kdb_sign_auth_data_rep *rep = (kdb_sign_auth_data_rep *)rep_data->data;
+ heim_pac hpac = NULL;
+ heim_octet_string pac_data;
+ krb5_boolean is_as_req;
+ krb5_error_code code;
+ krb5_authdata **authdata = NULL;
+ Principal *client_hprinc = NULL;
+ EncryptionKey server_hkey;
+ EncryptionKey krbtgt_hkey;
+ krb5_keyblock krbtgt_kkey;
+
+ if (kh->windc == NULL)
+ return KRB5_KDB_DBTYPE_NOSUP; /* short circuit */
+
+ memset(rep, 0, sizeof(*rep));
+ memset(&krbtgt_kkey, 0, sizeof(krbtgt_kkey));
+ pac_data.data = NULL;
+
+ is_as_req = ((req->flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0);
+
+ /* Prefer canonicalised name from client entry */
+ if (req->client != NULL) {
+ client_hprinc = KH_DB_ENTRY(req->client)->entry.principal;
+ } else {
+ code = kh_marshal_Principal(context, req->client_princ, &client_hprinc);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ KH_MARSHAL_KEY(req->server_key, &server_hkey);
+ KH_MARSHAL_KEY(req->krbtgt_key, &krbtgt_hkey);
+
+ if (!is_as_req) {
+ /* find the existing PAC, if present */
+ code = krb5int_find_authdata(context,
+ req->auth_data,
+ NULL,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ &authdata);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ if ((is_as_req && (req->flags & KRB5_KDB_FLAG_INCLUDE_PAC)) ||
+ (authdata == NULL && req->client != NULL)) {
+ code = kh_windc_pac_generate(context, kh,
+ KH_DB_ENTRY(req->client), &hpac);
+ if (code != 0)
+ goto cleanup;
+ } else if (authdata != NULL) {
+ assert(authdata[0] != NULL);
+
+ if (authdata[1] != NULL) {
+ code = KRB5KDC_ERR_BADOPTION; /* XXX */
+ goto cleanup;
+ }
+
+ pac_data.data = authdata[0]->contents;
+ pac_data.length = authdata[0]->length;
+
+ code = kh_pac_parse(context, pac_data.data, pac_data.length, &hpac);
+ if (code != 0)
+ goto cleanup;
+
+ /*
+ * In the constrained delegation case, the PAC is from a service
+ * ticket rather than a TGT; we must verify the server and KDC
+ * signatures to assert that the server did not forge the PAC.
+ */
+ if (req->flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
+ code = kh_pac_verify(context, hpac, req->authtime,
+ client_hprinc, &server_hkey, &krbtgt_hkey);
+ } else {
+ code = kh_pac_verify(context, hpac, req->authtime,
+ client_hprinc, &krbtgt_hkey, NULL);
+ }
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_windc_pac_verify(context, kh, client_hprinc,
+ req->client ?
+ KH_DB_ENTRY(req->client) : NULL,
+ KH_DB_ENTRY(req->server),
+ &hpac);
+ if (code != 0)
+ goto cleanup;
+ } else {
+ code = KRB5_KDB_DBTYPE_NOSUP;
+ goto cleanup;
+ }
+
+ /*
+ * In the cross-realm case, krbtgt_hkey refers to the cross-realm
+ * TGS key, so we need to explicitly lookup our TGS key.
+ */
+ if (req->flags & KRB5_KDB_FLAG_CROSS_REALM) {
+ assert(!is_as_req);
+
+ code = kh_get_tgs_key(context, kh, req->server->princ, &krbtgt_kkey);
+ if (code != 0)
+ goto cleanup;
+
+ KH_MARSHAL_KEY(&krbtgt_kkey, &krbtgt_hkey);
+ }
+
+ code = kh_pac_sign(context, hpac, req->authtime, client_hprinc,
+ &server_hkey, &krbtgt_hkey, &pac_data);
+ if (code != 0)
+ goto cleanup;
+
+ if (authdata == NULL) {
+ authdata = k5alloc(2 * sizeof(krb5_authdata *), &code);
+ if (code != 0)
+ goto cleanup;
+
+ authdata[0] = k5alloc(sizeof(krb5_authdata), &code);
+ if (code != 0)
+ goto cleanup;
+
+ authdata[1] = NULL;
+ } else {
+ free(authdata[0]->contents);
+ authdata[0]->contents = NULL;
+ authdata[0]->length = 0;
+ }
+
+ /* take ownership of pac_data */
+ authdata[0]->magic = KV5M_AUTHDATA;
+ authdata[0]->ad_type = KRB5_AUTHDATA_WIN2K_PAC;
+ authdata[0]->contents = pac_data.data;
+ authdata[0]->length = pac_data.length;
+
+ pac_data.data = NULL;
+
+ code = krb5_encode_authdata_container(context,
+ KRB5_AUTHDATA_IF_RELEVANT,
+ authdata,
+ &rep->auth_data);
+ if (code != 0)
+ goto cleanup;
+
+cleanup:
+ if (req->client == NULL)
+ kh_free_Principal(context, client_hprinc);
+ kh_pac_free(context, hpac);
+ if (pac_data.data != NULL)
+ free(pac_data.data);
+ krb5_free_authdata(context, authdata);
+ krb5_free_keyblock_contents(context, &krbtgt_kkey);
+
+ return code;
+}
+
+static krb5_error_code
+kh_marshal_KDCOptions(krb5_context context,
+ krb5_flags koptions,
+ KDCOptions *hoptions)
+{
+ memset(hoptions, 0, sizeof(*hoptions));
+
+ if (koptions & KDC_OPT_FORWARDABLE)
+ hoptions->forwardable = 1;
+ if (koptions & KDC_OPT_FORWARDED)
+ hoptions->forwarded = 1;
+ if (koptions & KDC_OPT_PROXIABLE)
+ hoptions->proxiable = 1;
+ if (koptions & KDC_OPT_PROXY)
+ hoptions->proxy = 1;
+ if (koptions & KDC_OPT_ALLOW_POSTDATE)
+ hoptions->allow_postdate = 1;
+ if (koptions & KDC_OPT_POSTDATED)
+ hoptions->postdated = 1;
+ if (koptions & KDC_OPT_RENEWABLE)
+ hoptions->renewable = 1;
+ if (koptions & KDC_OPT_REQUEST_ANONYMOUS)
+ hoptions->request_anonymous = 1;
+ if (koptions & KDC_OPT_CANONICALIZE)
+ hoptions->canonicalize = 1;
+ if (koptions & KDC_OPT_DISABLE_TRANSITED_CHECK)
+ hoptions->disable_transited_check = 1;
+ if (koptions & KDC_OPT_RENEWABLE_OK)
+ hoptions->renewable_ok = 1;
+ if (koptions & KDC_OPT_ENC_TKT_IN_SKEY)
+ hoptions->enc_tkt_in_skey = 1;
+ if (koptions & KDC_OPT_RENEW)
+ hoptions->renew = 1;
+ if (koptions & KDC_OPT_VALIDATE)
+ hoptions->validate = 1;
+
+ return 0;
+}
+
+static krb5_error_code
+kh_marshall_HostAddress(krb5_context context,
+ krb5_address *kaddress,
+ HostAddress *haddress)
+{
+ haddress->addr_type = kaddress->addrtype;
+ haddress->address.data = malloc(kaddress->length);
+ if (haddress->address.data == NULL)
+ return ENOMEM;
+
+ memcpy(haddress->address.data, kaddress->contents, kaddress->length);
+ haddress->address.length = kaddress->length;
+
+ return 0;
+}
+
+static krb5_error_code
+kh_marshall_HostAddresses(krb5_context context,
+ krb5_address **kaddresses,
+ HostAddresses **phaddresses)
+{
+ krb5_error_code code;
+ HostAddresses *haddresses;
+ int i;
+
+ *phaddresses = NULL;
+
+ if (kaddresses == NULL)
+ return 0;
+
+ for (i = 0; kaddresses[i] != NULL; i++)
+ ;
+
+ haddresses = k5alloc(sizeof(*haddresses), &code);
+ if (code != 0)
+ return code;
+
+ haddresses->len = 0;
+ haddresses->val = k5alloc(i * sizeof(HostAddress), &code);
+ if (code != 0)
+ return code;
+
+ for (i = 0; kaddresses[i] != NULL; i++) {
+ code = kh_marshall_HostAddress(context,
+ kaddresses[i],
+ &haddresses->val[i]);
+ if (code != 0)
+ break;
+
+ haddresses->len++;
+ }
+
+ if (code != 0) {
+ free(haddresses->val);
+ free(haddresses);
+ } else {
+ *phaddresses = haddresses;
+ }
+
+ return code;
+}
+
+krb5_error_code
+kh_db_check_policy_as(krb5_context context,
+ unsigned int method,
+ const krb5_data *req_data,
+ krb5_data *rep_data)
+{
+ kh_db_context *kh = KH_DB_CONTEXT(context);
+ kdb_check_policy_as_req *req = (kdb_check_policy_as_req *)req_data->data;
+ kdb_check_policy_as_rep *rep = (kdb_check_policy_as_rep *)rep_data->data;
+ krb5_error_code code;
+ heim_octet_string e_data;
+ krb5_kdc_req *kkdcreq = req->request;
+ KDC_REQ hkdcreq;
+ Principal *hclient = NULL;
+ Principal *hserver = NULL;
+ time_t from, till, rtime;
+
+ if (kh->windc == NULL)
+ return KRB5_KDB_DBTYPE_NOSUP; /* short circuit */
+
+ memset(&hkdcreq, 0, sizeof(hkdcreq));
+
+ hkdcreq.pvno = KRB5_PVNO;
+ hkdcreq.msg_type = kkdcreq->msg_type;
+ hkdcreq.padata = NULL; /* FIXME */
+ code = kh_marshal_KDCOptions(context,
+ kkdcreq->kdc_options,
+ &hkdcreq.req_body.kdc_options);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_marshal_Principal(context, kkdcreq->client, &hclient);
+ if (code != 0)
+ goto cleanup;
+
+ code = kh_marshal_Principal(context, kkdcreq->server, &hserver);
+ if (code != 0)
+ goto cleanup;
+
+ hkdcreq.req_body.cname = &hclient->name;
+ hkdcreq.req_body.realm = hserver->realm;
+ hkdcreq.req_body.sname = &hserver->name;
+
+ from = kkdcreq->from; hkdcreq.req_body.from = &from;
+ till = kkdcreq->till; hkdcreq.req_body.till = &till;
+ rtime = kkdcreq->rtime; hkdcreq.req_body.rtime = &rtime;
+
+ hkdcreq.req_body.nonce = kkdcreq->nonce;
+ hkdcreq.req_body.etype.len = kkdcreq->nktypes;
+ hkdcreq.req_body.etype.val = kkdcreq->ktype;
+
+ code = kh_marshall_HostAddresses(context,
+ kkdcreq->addresses,
+ &hkdcreq.req_body.addresses);
+ if (code != 0)
+ goto cleanup;
+
+ /* FIXME hkdcreq.req_body.enc_authorization_data */
+ /* FIXME hkdcreq.req_body.additional_tickets */
+
+ code = kh_windc_client_access(context, kh,
+ KH_DB_ENTRY(req->client),
+ &hkdcreq, &e_data);
+
+ rep->e_data.data = e_data.data;
+ rep->e_data.length = e_data.length;
+
+cleanup:
+ kh_free_HostAddresses(context, hkdcreq.req_body.addresses);
+ kh_free_Principal(context, hclient);
+ kh_free_Principal(context, hserver);
+
+ return code;
+}
+
+krb5_error_code
+kh_hdb_windc_init(krb5_context context,
+ const char *libdir,
+ kh_db_context *kh)
+{
+ krb5_error_code code;
+ const char *objdirs[2];
+ void **tables = NULL;
+ int i;
+
+ memset(&kh->windc_plugins, 0, sizeof(kh->windc_plugins));
+
+ code = PLUGIN_DIR_OPEN(&kh->windc_plugins);
+ if (code != 0)
+ return code;
+
+ objdirs[0] = libdir;
+ objdirs[1] = NULL;
+
+ code = krb5int_open_plugin_dirs(objdirs, NULL,
+ &kh->windc_plugins,
+ &context->err);
+ if (code != 0)
+ return code;
+
+ code = krb5int_get_plugin_dir_data(&kh->windc_plugins,
+ "windc",
+ &tables,
+ &context->err);
+ if (code != 0)
+ return code;
+
+ code = KRB5_KDB_DBTYPE_NOSUP;
+
+ for (i = 0; tables[i] != NULL; i++) {
+ krb5plugin_windc_ftable *windc = tables[i];
+
+ if (windc->minor_version < KRB5_WINDC_PLUGIN_MINOR)
+ continue;
+
+ code = kh_map_error((*windc->init)(kh->hcontext, &kh->windc_ctx));
+ if (code != 0)
+ continue;
+
+ kh->windc = windc;
+ break;
+ }
+
+ if (tables != NULL)
+ krb5int_free_plugin_dir_data(tables);
+
+ return code;
+}
+
diff --git a/src/plugins/kdb/hdb/windc_plugin.h b/src/plugins/kdb/hdb/windc_plugin.h
new file mode 100644
index 0000000..7df2a21
--- /dev/null
+++ b/src/plugins/kdb/hdb/windc_plugin.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2006 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: windc_plugin.h 22693 2008-03-19 08:57:49Z lha $ */
+
+#ifndef HEIMDAL_WINDC_PLUGIN_H
+#define HEIMDAL_WINDC_PLUGIN_H 1
+
+/*
+ * The PAC generate function should allocate a heim_pac using
+ * heim_pac_init and fill in the PAC structure for the principal using
+ * heim_pac_add_buffer.
+ *
+ * The PAC verify function should verify all components in the PAC
+ * using heim_pac_get_types and heim_pac_get_buffer for all types.
+ *
+ * Check client access function check if the client is authorized.
+ */
+
+struct hdb_entry_ex;
+
+struct _heim_pac_data;
+typedef struct _heim_pac_data *heim_pac;
+
+typedef krb5_error_code
+(*krb5plugin_windc_pac_generate)(void *, heim_context,
+ struct hdb_entry_ex *, heim_pac *);
+
+typedef krb5_error_code
+(*krb5plugin_windc_pac_verify)(void *, heim_context,
+ const Principal *,
+ struct hdb_entry_ex *,
+ struct hdb_entry_ex *,
+ heim_pac *);
+typedef krb5_error_code
+(*krb5plugin_windc_client_access)(
+ void *, heim_context, struct hdb_entry_ex *, KDC_REQ *, heim_octet_string *);
+
+
+#define KRB5_WINDC_PLUGIN_MINOR 3
+
+typedef struct krb5plugin_windc_ftable {
+ int minor_version;
+ krb5_error_code (*init)(heim_context, void **);
+ void (*fini)(void *);
+ krb5plugin_windc_pac_generate pac_generate;
+ krb5plugin_windc_pac_verify pac_verify;
+ krb5plugin_windc_client_access client_access;
+} krb5plugin_windc_ftable;
+
+#endif /* HEIMDAL_WINDC_PLUGIN_H */
+