diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/authdata/greet_server/greet_auth.c | 1 | ||||
-rw-r--r-- | src/plugins/kdb/hdb/Makefile.in | 64 | ||||
-rw-r--r-- | src/plugins/kdb/hdb/hdb.exports | 1 | ||||
-rw-r--r-- | src/plugins/kdb/hdb/hdb.h | 141 | ||||
-rw-r--r-- | src/plugins/kdb/hdb/hdb_asn1.h | 586 | ||||
-rw-r--r-- | src/plugins/kdb/hdb/hdb_err.h | 32 | ||||
-rw-r--r-- | src/plugins/kdb/hdb/kdb_hdb.c | 1426 | ||||
-rw-r--r-- | src/plugins/kdb/hdb/kdb_hdb.h | 172 | ||||
-rw-r--r-- | src/plugins/kdb/hdb/kdb_marshal.c | 810 | ||||
-rw-r--r-- | src/plugins/kdb/hdb/kdb_windc.c | 615 | ||||
-rw-r--r-- | src/plugins/kdb/hdb/windc_plugin.h | 82 |
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 3f1e3d9eaa..80a68a86f1 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 0000000000..3f2bccf40e --- /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 0000000000..f2b7c11195 --- /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 0000000000..39fbec7e5d --- /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 0000000000..87aa2a4cbf --- /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 0000000000..a47797a138 --- /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 0000000000..d22489eec5 --- /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 0000000000..9cfbef6a34 --- /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 0000000000..17bbdc8087 --- /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 0000000000..a419d29ded --- /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 0000000000..7df2a21473 --- /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 */ + |