diff options
Diffstat (limited to 'lib/hdb')
| -rw-r--r-- | lib/hdb/Makefile.am | 79 | ||||
| -rw-r--r-- | lib/hdb/Makefile.in | 1467 | ||||
| -rw-r--r-- | lib/hdb/NTMakefile | 18 | ||||
| -rw-r--r-- | lib/hdb/common.c | 1718 | ||||
| -rw-r--r-- | lib/hdb/db.c | 25 | ||||
| -rw-r--r-- | lib/hdb/db3.c | 25 | ||||
| -rw-r--r-- | lib/hdb/dbinfo.c | 18 | ||||
| -rw-r--r-- | lib/hdb/ext.c | 304 | ||||
| -rw-r--r-- | lib/hdb/hdb-keytab.c | 27 | ||||
| -rw-r--r-- | lib/hdb/hdb-ldap.c | 390 | ||||
| -rw-r--r-- | lib/hdb/hdb-mdb.c | 549 | ||||
| -rw-r--r-- | lib/hdb/hdb-mitdb.c | 118 | ||||
| -rw-r--r-- | lib/hdb/hdb-private.h | 81 | ||||
| -rw-r--r-- | lib/hdb/hdb-protos.h | 592 | ||||
| -rw-r--r-- | lib/hdb/hdb-sqlite.c | 66 | ||||
| -rw-r--r-- | lib/hdb/hdb.asn1 | 115 | ||||
| -rw-r--r-- | lib/hdb/hdb.c | 547 | ||||
| -rw-r--r-- | lib/hdb/hdb.h | 130 | ||||
| -rw-r--r-- | lib/hdb/hdb.opt | 9 | ||||
| -rw-r--r-- | lib/hdb/keys.c | 208 | ||||
| -rw-r--r-- | lib/hdb/keytab.c | 60 | ||||
| -rw-r--r-- | lib/hdb/libhdb-exports.def | 106 | ||||
| -rw-r--r-- | lib/hdb/ndbm.c | 64 | ||||
| -rw-r--r-- | lib/hdb/print.c | 20 | ||||
| -rw-r--r-- | lib/hdb/test_concurrency.c | 506 | ||||
| -rw-r--r-- | lib/hdb/test_dbinfo.c | 61 | ||||
| -rw-r--r-- | lib/hdb/test_hdbkeys.c | 2 | ||||
| -rw-r--r-- | lib/hdb/test_hdbplugin.c | 107 | ||||
| -rw-r--r-- | lib/hdb/test_namespace.c | 941 | ||||
| -rw-r--r-- | lib/hdb/version-script.map | 68 |
30 files changed, 5210 insertions, 3211 deletions
diff --git a/lib/hdb/Makefile.am b/lib/hdb/Makefile.am index 448b0242f3e7..4a5599532436 100644 --- a/lib/hdb/Makefile.am +++ b/lib/hdb/Makefile.am @@ -2,43 +2,55 @@ include $(top_srcdir)/Makefile.am.common +WFLAGS += $(WFLAGS_ENUM_CONV) + AM_CPPFLAGS += -I../asn1 -I$(srcdir)/../asn1 AM_CPPFLAGS += $(INCLUDE_openldap) -DHDB_DB_DIR=\"$(DIR_hdbdir)\" AM_CPPFLAGS += -I$(srcdir)/../krb5 AM_CPPFLAGS += $(INCLUDE_sqlite3) AM_CPPFLAGS += $(INCLUDE_libintl) +AM_CPPFLAGS += -DHDB_DEFAULT_DB_TYPE=\"$(db_type):\" if HAVE_DBHEADER AM_CPPFLAGS += -I$(DBHEADER) endif BUILT_SOURCES = \ - $(gen_files_hdb:.x=.c) \ + $(gen_files_hdb) \ hdb_err.c \ - hdb_err.h + hdb_err.h \ + $(srcdir)/hdb-protos.h \ + $(srcdir)/hdb-private.h gen_files_hdb = \ - asn1_Salt.x \ - asn1_Key.x \ - asn1_Event.x \ - asn1_HDBFlags.x \ - asn1_GENERATION.x \ - asn1_HDB_Ext_PKINIT_acl.x \ - asn1_HDB_Ext_PKINIT_cert.x \ - asn1_HDB_Ext_PKINIT_hash.x \ - asn1_HDB_Ext_Constrained_delegation_acl.x \ - asn1_HDB_Ext_Lan_Manager_OWF.x \ - asn1_HDB_Ext_Password.x \ - asn1_HDB_Ext_Aliases.x \ - asn1_HDB_Ext_KeySet.x \ - asn1_HDB_extension.x \ - asn1_HDB_extensions.x \ - asn1_hdb_entry.x \ - asn1_hdb_entry_alias.x \ - asn1_hdb_keyset.x \ - asn1_Keys.x + asn1_Event.c \ + asn1_GENERATION.c \ + asn1_HDB_EncTypeList.c \ + asn1_HDB_Ext_Aliases.c \ + asn1_HDB_Ext_Constrained_delegation_acl.c \ + asn1_HDB_Ext_KeyRotation.c \ + asn1_HDB_Ext_KeySet.c \ + asn1_HDB_Ext_Lan_Manager_OWF.c \ + asn1_HDB_Ext_Password.c \ + asn1_HDB_Ext_PKINIT_acl.c \ + asn1_HDB_Ext_PKINIT_cert.c \ + asn1_HDB_Ext_PKINIT_hash.c \ + asn1_HDB_EntryOrAlias.c \ + asn1_HDB_entry_alias.c \ + asn1_HDB_entry.c \ + asn1_HDB_extension.c \ + asn1_HDB_extensions.c \ + asn1_HDB_keyset.c \ + asn1_HDBFlags.c \ + asn1_Key.c \ + asn1_KeyRotation.c \ + asn1_KeyRotationFlags.c \ + asn1_Keys.c \ + asn1_Salt.c CLEANFILES = $(BUILT_SOURCES) $(gen_files_hdb) \ - hdb_asn1{,-priv}.h* hdb_asn1_files hdb_asn1-template.[cx] + hdb_asn1{,-priv}.h hdb_asn1_files hdb_asn1-template.c \ + hdb_asn1_syms.c hdb_asn1_oids.c hdb_asn1.json \ + testhdb-* LDADD = libhdb.la \ ../krb5/libkrb5.la \ @@ -72,7 +84,9 @@ if versionscript libhdb_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map endif -noinst_PROGRAMS = test_dbinfo test_hdbkeys test_mkey test_hdbplugin +# test_hdbkeys and test_mkey are not tests -- they are manual test utils +noinst_PROGRAMS = test_dbinfo test_hdbkeys test_mkey test_namespace test_concurrency +TESTS = test_dbinfo test_namespace test_concurrency dist_libhdb_la_SOURCES = \ common.c \ @@ -118,26 +132,27 @@ ALL_OBJECTS = $(libhdb_la_OBJECTS) ALL_OBJECTS += $(test_dbinfo_OBJECTS) ALL_OBJECTS += $(test_hdbkeys_OBJECTS) ALL_OBJECTS += $(test_mkey_OBJECTS) -ALL_OBJECTS += $(test_hdbplugin_OBJECTS) +ALL_OBJECTS += $(test_namespace_OBJECTS) +ALL_OBJECTS += $(test_concurrency_OBJECTS) $(ALL_OBJECTS): $(HDB_PROTOS) hdb_asn1.h hdb_asn1-priv.h hdb_err.h +test_namespace_LDADD = $(LDADD) $(test_hdbkeys_LIBS) $(LIB_heimbase) + $(srcdir)/hdb-protos.h: $(dist_libhdb_la_SOURCES) cd $(srcdir); perl ../../cf/make-proto.pl -q -P comment -o hdb-protos.h $(dist_libhdb_la_SOURCES) || rm -f hdb-protos.h $(srcdir)/hdb-private.h: $(dist_libhdb_la_SOURCES) cd $(srcdir); perl ../../cf/make-proto.pl -q -P comment -p hdb-private.h $(dist_libhdb_la_SOURCES) || rm -f hdb-private.h -$(gen_files_hdb) hdb_asn1.hx hdb_asn1-priv.hx: hdb_asn1_files +$(gen_files_hdb) hdb_asn1.h hdb_asn1-priv.h: hdb_asn1_files + for genfile in '$(gen_files_hdb)'; do \ + $(CLANG_FORMAT) -style=$(CLANG_FORMAT_STYLE) -i $${genfile}; \ + done hdb_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/hdb.asn1 - $(ASN1_COMPILE) --sequence=HDB-Ext-KeySet --sequence=Keys $(srcdir)/hdb.asn1 hdb_asn1 - -test_dbinfo_LIBS = libhdb.la - -test_hdbkeys_LIBS = ../krb5/libkrb5.la libhdb.la -test_mkey_LIBS = $(test_hdbkeys_LIBS) -test_hdbplugin_LIBS = $(test_hdbkeys_LIBS) + $(ASN1_COMPILE) --option-file=$(srcdir)/hdb.opt $(srcdir)/hdb.asn1 hdb_asn1 + @$(CLANG_FORMAT) -style=$(CLANG_FORMAT_STYLE) -i $$(cat hdb_asn1_files) # to help stupid solaris make diff --git a/lib/hdb/Makefile.in b/lib/hdb/Makefile.in deleted file mode 100644 index ab4074f09808..000000000000 --- a/lib/hdb/Makefile.in +++ /dev/null @@ -1,1467 +0,0 @@ -# Makefile.in generated by automake 1.16.5 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2021 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# $Id$ - -# $Id$ - -# $Id$ - - - -VPATH = @srcdir@ -am__is_gnu_make = { \ - if test -z '$(MAKELEVEL)'; then \ - false; \ - elif test -n '$(MAKE_HOST)'; then \ - true; \ - elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ - true; \ - else \ - false; \ - fi; \ -} -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -@HAVE_DBHEADER_TRUE@am__append_1 = -I$(DBHEADER) -@versionscript_TRUE@am__append_2 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map -noinst_PROGRAMS = test_dbinfo$(EXEEXT) test_hdbkeys$(EXEEXT) \ - test_mkey$(EXEEXT) test_hdbplugin$(EXEEXT) -subdir = lib/hdb -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \ - $(top_srcdir)/cf/auth-modules.m4 \ - $(top_srcdir)/cf/broken-glob.m4 \ - $(top_srcdir)/cf/broken-realloc.m4 \ - $(top_srcdir)/cf/broken-snprintf.m4 $(top_srcdir)/cf/broken.m4 \ - $(top_srcdir)/cf/broken2.m4 $(top_srcdir)/cf/c-attribute.m4 \ - $(top_srcdir)/cf/capabilities.m4 \ - $(top_srcdir)/cf/check-compile-et.m4 \ - $(top_srcdir)/cf/check-getpwnam_r-posix.m4 \ - $(top_srcdir)/cf/check-man.m4 \ - $(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \ - $(top_srcdir)/cf/check-type-extra.m4 \ - $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \ - $(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \ - $(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \ - $(top_srcdir)/cf/find-func-no-libs.m4 \ - $(top_srcdir)/cf/find-func-no-libs2.m4 \ - $(top_srcdir)/cf/find-func.m4 \ - $(top_srcdir)/cf/find-if-not-broken.m4 \ - $(top_srcdir)/cf/framework-security.m4 \ - $(top_srcdir)/cf/have-struct-field.m4 \ - $(top_srcdir)/cf/have-type.m4 $(top_srcdir)/cf/irix.m4 \ - $(top_srcdir)/cf/krb-bigendian.m4 \ - $(top_srcdir)/cf/krb-func-getlogin.m4 \ - $(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \ - $(top_srcdir)/cf/krb-prog-perl.m4 \ - $(top_srcdir)/cf/krb-readline.m4 \ - $(top_srcdir)/cf/krb-struct-spwd.m4 \ - $(top_srcdir)/cf/krb-struct-winsize.m4 \ - $(top_srcdir)/cf/largefile.m4 $(top_srcdir)/cf/libtool.m4 \ - $(top_srcdir)/cf/ltoptions.m4 $(top_srcdir)/cf/ltsugar.m4 \ - $(top_srcdir)/cf/ltversion.m4 $(top_srcdir)/cf/lt~obsolete.m4 \ - $(top_srcdir)/cf/mips-abi.m4 $(top_srcdir)/cf/misc.m4 \ - $(top_srcdir)/cf/need-proto.m4 $(top_srcdir)/cf/osfc2.m4 \ - $(top_srcdir)/cf/otp.m4 $(top_srcdir)/cf/pkg.m4 \ - $(top_srcdir)/cf/proto-compat.m4 $(top_srcdir)/cf/pthreads.m4 \ - $(top_srcdir)/cf/resolv.m4 $(top_srcdir)/cf/retsigtype.m4 \ - $(top_srcdir)/cf/roken-frag.m4 \ - $(top_srcdir)/cf/socket-wrapper.m4 $(top_srcdir)/cf/sunos.m4 \ - $(top_srcdir)/cf/telnet.m4 $(top_srcdir)/cf/test-package.m4 \ - $(top_srcdir)/cf/version-script.m4 $(top_srcdir)/cf/wflags.m4 \ - $(top_srcdir)/cf/win32.m4 $(top_srcdir)/cf/with-all.m4 \ - $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \ - $(noinst_HEADERS) $(am__DIST_COMMON) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/include/config.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -PROGRAMS = $(noinst_PROGRAMS) -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -am__install_max = 40 -am__nobase_strip_setup = \ - srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -am__nobase_strip = \ - for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -am__nobase_list = $(am__nobase_strip_setup); \ - for p in $$list; do echo "$$p $$p"; done | \ - sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ - $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ - if (++n[$$2] == $(am__install_max)) \ - { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ - END { for (dir in files) print dir, files[dir] }' -am__base_list = \ - sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ - sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ - } -am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)" \ - "$(DESTDIR)$(includedir)" -LTLIBRARIES = $(lib_LTLIBRARIES) -am__DEPENDENCIES_1 = -@OPENLDAP_MODULE_TRUE@hdb_ldap_la_DEPENDENCIES = \ -@OPENLDAP_MODULE_TRUE@ $(am__DEPENDENCIES_1) libhdb.la -am__hdb_ldap_la_SOURCES_DIST = hdb-ldap.c -@OPENLDAP_MODULE_TRUE@am_hdb_ldap_la_OBJECTS = hdb-ldap.lo -hdb_ldap_la_OBJECTS = $(am_hdb_ldap_la_OBJECTS) -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent -am__v_lt_1 = -hdb_ldap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(hdb_ldap_la_LDFLAGS) $(LDFLAGS) -o $@ -@OPENLDAP_MODULE_TRUE@am_hdb_ldap_la_rpath = -rpath $(libdir) -@OPENLDAP_MODULE_FALSE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) -am__dist_libhdb_la_SOURCES_DIST = common.c db.c db3.c ext.c hdb-ldap.c \ - hdb.c hdb-sqlite.c hdb-keytab.c hdb-mdb.c hdb-mitdb.c \ - hdb_locl.h keys.c keytab.c dbinfo.c mkey.c ndbm.c print.c -@OPENLDAP_MODULE_FALSE@am__objects_1 = hdb-ldap.lo -dist_libhdb_la_OBJECTS = common.lo db.lo db3.lo ext.lo \ - $(am__objects_1) hdb.lo hdb-sqlite.lo hdb-keytab.lo hdb-mdb.lo \ - hdb-mitdb.lo keys.lo keytab.lo dbinfo.lo mkey.lo ndbm.lo \ - print.lo -am__objects_2 = asn1_Salt.lo asn1_Key.lo asn1_Event.lo \ - asn1_HDBFlags.lo asn1_GENERATION.lo asn1_HDB_Ext_PKINIT_acl.lo \ - asn1_HDB_Ext_PKINIT_cert.lo asn1_HDB_Ext_PKINIT_hash.lo \ - asn1_HDB_Ext_Constrained_delegation_acl.lo \ - asn1_HDB_Ext_Lan_Manager_OWF.lo asn1_HDB_Ext_Password.lo \ - asn1_HDB_Ext_Aliases.lo asn1_HDB_Ext_KeySet.lo \ - asn1_HDB_extension.lo asn1_HDB_extensions.lo asn1_hdb_entry.lo \ - asn1_hdb_entry_alias.lo asn1_hdb_keyset.lo asn1_Keys.lo -am__objects_3 = $(am__objects_2) hdb_err.lo -nodist_libhdb_la_OBJECTS = $(am__objects_3) -libhdb_la_OBJECTS = $(dist_libhdb_la_OBJECTS) \ - $(nodist_libhdb_la_OBJECTS) -libhdb_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(libhdb_la_LDFLAGS) $(LDFLAGS) -o $@ -test_dbinfo_SOURCES = test_dbinfo.c -test_dbinfo_OBJECTS = test_dbinfo.$(OBJEXT) -test_dbinfo_LDADD = $(LDADD) -test_dbinfo_DEPENDENCIES = libhdb.la ../krb5/libkrb5.la \ - ../asn1/libasn1.la $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -test_hdbkeys_SOURCES = test_hdbkeys.c -test_hdbkeys_OBJECTS = test_hdbkeys.$(OBJEXT) -test_hdbkeys_LDADD = $(LDADD) -test_hdbkeys_DEPENDENCIES = libhdb.la ../krb5/libkrb5.la \ - ../asn1/libasn1.la $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -test_hdbplugin_SOURCES = test_hdbplugin.c -test_hdbplugin_OBJECTS = test_hdbplugin.$(OBJEXT) -test_hdbplugin_LDADD = $(LDADD) -test_hdbplugin_DEPENDENCIES = libhdb.la ../krb5/libkrb5.la \ - ../asn1/libasn1.la $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -test_mkey_SOURCES = test_mkey.c -test_mkey_OBJECTS = test_mkey.$(OBJEXT) -test_mkey_LDADD = $(LDADD) -test_mkey_DEPENDENCIES = libhdb.la ../krb5/libkrb5.la \ - ../asn1/libasn1.la $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -depcomp = $(SHELL) $(top_srcdir)/depcomp -am__maybe_remake_depfiles = depfiles -am__depfiles_remade = ./$(DEPDIR)/asn1_Event.Plo \ - ./$(DEPDIR)/asn1_GENERATION.Plo ./$(DEPDIR)/asn1_HDBFlags.Plo \ - ./$(DEPDIR)/asn1_HDB_Ext_Aliases.Plo \ - ./$(DEPDIR)/asn1_HDB_Ext_Constrained_delegation_acl.Plo \ - ./$(DEPDIR)/asn1_HDB_Ext_KeySet.Plo \ - ./$(DEPDIR)/asn1_HDB_Ext_Lan_Manager_OWF.Plo \ - ./$(DEPDIR)/asn1_HDB_Ext_PKINIT_acl.Plo \ - ./$(DEPDIR)/asn1_HDB_Ext_PKINIT_cert.Plo \ - ./$(DEPDIR)/asn1_HDB_Ext_PKINIT_hash.Plo \ - ./$(DEPDIR)/asn1_HDB_Ext_Password.Plo \ - ./$(DEPDIR)/asn1_HDB_extension.Plo \ - ./$(DEPDIR)/asn1_HDB_extensions.Plo ./$(DEPDIR)/asn1_Key.Plo \ - ./$(DEPDIR)/asn1_Keys.Plo ./$(DEPDIR)/asn1_Salt.Plo \ - ./$(DEPDIR)/asn1_hdb_entry.Plo \ - ./$(DEPDIR)/asn1_hdb_entry_alias.Plo \ - ./$(DEPDIR)/asn1_hdb_keyset.Plo ./$(DEPDIR)/common.Plo \ - ./$(DEPDIR)/db.Plo ./$(DEPDIR)/db3.Plo ./$(DEPDIR)/dbinfo.Plo \ - ./$(DEPDIR)/ext.Plo ./$(DEPDIR)/hdb-keytab.Plo \ - ./$(DEPDIR)/hdb-ldap.Plo ./$(DEPDIR)/hdb-mdb.Plo \ - ./$(DEPDIR)/hdb-mitdb.Plo ./$(DEPDIR)/hdb-sqlite.Plo \ - ./$(DEPDIR)/hdb.Plo ./$(DEPDIR)/hdb_err.Plo \ - ./$(DEPDIR)/keys.Plo ./$(DEPDIR)/keytab.Plo \ - ./$(DEPDIR)/mkey.Plo ./$(DEPDIR)/ndbm.Plo \ - ./$(DEPDIR)/print.Plo ./$(DEPDIR)/test_dbinfo.Po \ - ./$(DEPDIR)/test_hdbkeys.Po ./$(DEPDIR)/test_hdbplugin.Po \ - ./$(DEPDIR)/test_mkey.Po -am__mv = mv -f -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_@AM_V@) -am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -am__v_CC_1 = -CCLD = $(CC) -LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_@AM_V@) -am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -am__v_CCLD_1 = -SOURCES = $(hdb_ldap_la_SOURCES) $(dist_libhdb_la_SOURCES) \ - $(nodist_libhdb_la_SOURCES) test_dbinfo.c test_hdbkeys.c \ - test_hdbplugin.c test_mkey.c -DIST_SOURCES = $(am__hdb_ldap_la_SOURCES_DIST) \ - $(am__dist_libhdb_la_SOURCES_DIST) test_dbinfo.c \ - test_hdbkeys.c test_hdbplugin.c test_mkey.c -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -HEADERS = $(include_HEADERS) $(nodist_include_HEADERS) \ - $(noinst_HEADERS) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` -am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/Makefile.am.common \ - $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -AR = @AR@ -AS = @AS@ -ASN1_COMPILE = @ASN1_COMPILE@ -ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -CANONICAL_HOST = @CANONICAL_HOST@ -CAPNG_CFLAGS = @CAPNG_CFLAGS@ -CAPNG_LIBS = @CAPNG_LIBS@ -CATMAN = @CATMAN@ -CATMANEXT = @CATMANEXT@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CLANG_FORMAT = @CLANG_FORMAT@ -COMPILE_ET = @COMPILE_ET@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CSCOPE = @CSCOPE@ -CTAGS = @CTAGS@ -CYGPATH_W = @CYGPATH_W@ -DB1LIB = @DB1LIB@ -DB3LIB = @DB3LIB@ -DBHEADER = @DBHEADER@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DIR_com_err = @DIR_com_err@ -DIR_hdbdir = @DIR_hdbdir@ -DIR_roken = @DIR_roken@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@ -ETAGS = @ETAGS@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -FILECMD = @FILECMD@ -GCD_MIG = @GCD_MIG@ -GREP = @GREP@ -GROFF = @GROFF@ -INCLUDES_roken = @INCLUDES_roken@ -INCLUDE_libedit = @INCLUDE_libedit@ -INCLUDE_libintl = @INCLUDE_libintl@ -INCLUDE_openldap = @INCLUDE_openldap@ -INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@ -INCLUDE_readline = @INCLUDE_readline@ -INCLUDE_sqlite3 = @INCLUDE_sqlite3@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LDFLAGS_VERSION_SCRIPT = @LDFLAGS_VERSION_SCRIPT@ -LEX = @LEX@ -LEXLIB = @LEXLIB@ -LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ -LIBADD_roken = @LIBADD_roken@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBTOOL = @LIBTOOL@ -LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@ -LIB_bswap16 = @LIB_bswap16@ -LIB_bswap32 = @LIB_bswap32@ -LIB_bswap64 = @LIB_bswap64@ -LIB_com_err = @LIB_com_err@ -LIB_com_err_a = @LIB_com_err_a@ -LIB_com_err_so = @LIB_com_err_so@ -LIB_crypt = @LIB_crypt@ -LIB_db_create = @LIB_db_create@ -LIB_dbm_firstkey = @LIB_dbm_firstkey@ -LIB_dbopen = @LIB_dbopen@ -LIB_dispatch_async_f = @LIB_dispatch_async_f@ -LIB_dladdr = @LIB_dladdr@ -LIB_dlopen = @LIB_dlopen@ -LIB_dn_expand = @LIB_dn_expand@ -LIB_dns_search = @LIB_dns_search@ -LIB_door_create = @LIB_door_create@ -LIB_freeaddrinfo = @LIB_freeaddrinfo@ -LIB_gai_strerror = @LIB_gai_strerror@ -LIB_getaddrinfo = @LIB_getaddrinfo@ -LIB_gethostbyname = @LIB_gethostbyname@ -LIB_gethostbyname2 = @LIB_gethostbyname2@ -LIB_getnameinfo = @LIB_getnameinfo@ -LIB_getpwnam_r = @LIB_getpwnam_r@ -LIB_getsockopt = @LIB_getsockopt@ -LIB_hcrypto = @LIB_hcrypto@ -LIB_hcrypto_a = @LIB_hcrypto_a@ -LIB_hcrypto_appl = @LIB_hcrypto_appl@ -LIB_hcrypto_so = @LIB_hcrypto_so@ -LIB_hstrerror = @LIB_hstrerror@ -LIB_kdb = @LIB_kdb@ -LIB_libedit = @LIB_libedit@ -LIB_libintl = @LIB_libintl@ -LIB_loadquery = @LIB_loadquery@ -LIB_logout = @LIB_logout@ -LIB_logwtmp = @LIB_logwtmp@ -LIB_openldap = @LIB_openldap@ -LIB_openpty = @LIB_openpty@ -LIB_openssl_crypto = @LIB_openssl_crypto@ -LIB_otp = @LIB_otp@ -LIB_pidfile = @LIB_pidfile@ -LIB_readline = @LIB_readline@ -LIB_res_ndestroy = @LIB_res_ndestroy@ -LIB_res_nsearch = @LIB_res_nsearch@ -LIB_res_search = @LIB_res_search@ -LIB_roken = @LIB_roken@ -LIB_security = @LIB_security@ -LIB_setsockopt = @LIB_setsockopt@ -LIB_socket = @LIB_socket@ -LIB_sqlite3 = @LIB_sqlite3@ -LIB_syslog = @LIB_syslog@ -LIB_tgetent = @LIB_tgetent@ -LIPO = @LIPO@ -LMDBLIB = @LMDBLIB@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ -MAINT = @MAINT@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NDBMLIB = @NDBMLIB@ -NM = @NM@ -NMEDIT = @NMEDIT@ -NO_AFS = @NO_AFS@ -NROFF = @NROFF@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PERL = @PERL@ -PKG_CONFIG = @PKG_CONFIG@ -PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ -PTHREAD_LDADD = @PTHREAD_LDADD@ -PTHREAD_LIBADD = @PTHREAD_LIBADD@ -PYTHON = @PYTHON@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -SLC = @SLC@ -SLC_DEP = @SLC_DEP@ -STRIP = @STRIP@ -VERSION = @VERSION@ -VERSIONING = @VERSIONING@ -WFLAGS = @WFLAGS@ -WFLAGS_LITE = @WFLAGS_LITE@ -YACC = @YACC@ -YFLAGS = @YFLAGS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -db_type = @db_type@ -db_type_preference = @db_type_preference@ -docdir = @docdir@ -dpagaix_cflags = @dpagaix_cflags@ -dpagaix_ldadd = @dpagaix_ldadd@ -dpagaix_ldflags = @dpagaix_ldflags@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -runstatedir = @runstatedir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -subdirs = @subdirs@ -sysconfdir = @sysconfdir@ -target_alias = @target_alias@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \ - .cat5 .cat7 .cat8 -DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include -AM_CPPFLAGS = $(INCLUDES_roken) -I../asn1 -I$(srcdir)/../asn1 \ - $(INCLUDE_openldap) -DHDB_DB_DIR=\"$(DIR_hdbdir)\" \ - -I$(srcdir)/../krb5 $(INCLUDE_sqlite3) $(INCLUDE_libintl) \ - $(am__append_1) -@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME -AM_CFLAGS = $(WFLAGS) -CP = cp -buildinclude = $(top_builddir)/include -LIB_XauReadAuth = @LIB_XauReadAuth@ -LIB_el_init = @LIB_el_init@ -LIB_getattr = @LIB_getattr@ -LIB_getpwent_r = @LIB_getpwent_r@ -LIB_odm_initialize = @LIB_odm_initialize@ -LIB_setpcred = @LIB_setpcred@ -INCLUDE_krb4 = @INCLUDE_krb4@ -LIB_krb4 = @LIB_krb4@ -libexec_heimdaldir = $(libexecdir)/heimdal -NROFF_MAN = groff -mandoc -Tascii -@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS) -@NO_AFS_TRUE@LIB_kafs = -@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \ -@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la - -@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la -LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la -@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la - -#silent-rules -heim_verbose = $(heim_verbose_$(V)) -heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY)) -heim_verbose_0 = @echo " GEN "$@; -BUILT_SOURCES = \ - $(gen_files_hdb:.x=.c) \ - hdb_err.c \ - hdb_err.h - -gen_files_hdb = \ - asn1_Salt.x \ - asn1_Key.x \ - asn1_Event.x \ - asn1_HDBFlags.x \ - asn1_GENERATION.x \ - asn1_HDB_Ext_PKINIT_acl.x \ - asn1_HDB_Ext_PKINIT_cert.x \ - asn1_HDB_Ext_PKINIT_hash.x \ - asn1_HDB_Ext_Constrained_delegation_acl.x \ - asn1_HDB_Ext_Lan_Manager_OWF.x \ - asn1_HDB_Ext_Password.x \ - asn1_HDB_Ext_Aliases.x \ - asn1_HDB_Ext_KeySet.x \ - asn1_HDB_extension.x \ - asn1_HDB_extensions.x \ - asn1_hdb_entry.x \ - asn1_hdb_entry_alias.x \ - asn1_hdb_keyset.x \ - asn1_Keys.x - -CLEANFILES = $(BUILT_SOURCES) $(gen_files_hdb) \ - hdb_asn1{,-priv}.h* hdb_asn1_files hdb_asn1-template.[cx] - -LDADD = libhdb.la \ - ../krb5/libkrb5.la \ - ../asn1/libasn1.la \ - $(LIB_hcrypto) \ - $(LIB_roken) \ - $(LIB_openldap) \ - $(LIB_libintl) \ - $(LIB_ldopen) - -@OPENLDAP_MODULE_TRUE@ldap_so = hdb_ldap.la -@OPENLDAP_MODULE_TRUE@hdb_ldap_la_SOURCES = hdb-ldap.c -@OPENLDAP_MODULE_TRUE@hdb_ldap_la_LDFLAGS = -module -avoid-version -@OPENLDAP_MODULE_TRUE@hdb_ldap_la_LIBADD = $(LIB_openldap) libhdb.la -@OPENLDAP_MODULE_FALSE@ldap = hdb-ldap.c -@OPENLDAP_MODULE_FALSE@ldap_lib = $(LIB_openldap) -lib_LTLIBRARIES = libhdb.la $(ldap_so) -libhdb_la_LDFLAGS = -version-info 11:0:2 $(am__append_2) -dist_libhdb_la_SOURCES = \ - common.c \ - db.c \ - db3.c \ - ext.c \ - $(ldap) \ - hdb.c \ - hdb-sqlite.c \ - hdb-keytab.c \ - hdb-mdb.c \ - hdb-mitdb.c \ - hdb_locl.h \ - keys.c \ - keytab.c \ - dbinfo.c \ - mkey.c \ - ndbm.c \ - print.c - -nodist_libhdb_la_SOURCES = $(BUILT_SOURCES) -libhdb_la_DEPENDENCIES = version-script.map -include_HEADERS = hdb.h $(srcdir)/hdb-protos.h -nodist_include_HEADERS = hdb_err.h hdb_asn1.h -noinst_HEADERS = $(srcdir)/hdb-private.h -libhdb_la_LIBADD = \ - $(LIB_com_err) \ - ../krb5/libkrb5.la \ - ../asn1/libasn1.la \ - $(LIB_sqlite3) \ - $(LIBADD_roken) \ - $(ldap_lib) \ - $(LIB_dlopen) \ - $(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB) - -HDB_PROTOS = $(srcdir)/hdb-protos.h $(srcdir)/hdb-private.h -ALL_OBJECTS = $(libhdb_la_OBJECTS) $(test_dbinfo_OBJECTS) \ - $(test_hdbkeys_OBJECTS) $(test_mkey_OBJECTS) \ - $(test_hdbplugin_OBJECTS) -test_dbinfo_LIBS = libhdb.la -test_hdbkeys_LIBS = ../krb5/libkrb5.la libhdb.la -test_mkey_LIBS = $(test_hdbkeys_LIBS) -test_hdbplugin_LIBS = $(test_hdbkeys_LIBS) -EXTRA_DIST = \ - NTMakefile \ - libhdb-version.rc \ - libhdb-exports.def \ - hdb.asn1 \ - hdb_err.et \ - hdb.schema \ - version-script.map \ - data-mkey.mit.des3.le \ - data-mkey.mit.des3.be - -all: $(BUILT_SOURCES) - $(MAKE) $(AM_MAKEFLAGS) all-am - -.SUFFIXES: -.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .o .obj -$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/hdb/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --foreign lib/hdb/Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ - esac; -$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty): - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): - -clean-noinstPROGRAMS: - @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ - echo " rm -f" $$list; \ - rm -f $$list || exit $$?; \ - test -n "$(EXEEXT)" || exit 0; \ - list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f" $$list; \ - rm -f $$list - -install-libLTLIBRARIES: $(lib_LTLIBRARIES) - @$(NORMAL_INSTALL) - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ - list2=; for p in $$list; do \ - if test -f $$p; then \ - list2="$$list2 $$p"; \ - else :; fi; \ - done; \ - test -z "$$list2" || { \ - echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ - } - -uninstall-libLTLIBRARIES: - @$(NORMAL_UNINSTALL) - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ - for p in $$list; do \ - $(am__strip_dir) \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ - done - -clean-libLTLIBRARIES: - -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) - @list='$(lib_LTLIBRARIES)'; \ - locs=`for p in $$list; do echo $$p; done | \ - sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ - sort -u`; \ - test -z "$$locs" || { \ - echo rm -f $${locs}; \ - rm -f $${locs}; \ - } - -hdb_ldap.la: $(hdb_ldap_la_OBJECTS) $(hdb_ldap_la_DEPENDENCIES) $(EXTRA_hdb_ldap_la_DEPENDENCIES) - $(AM_V_CCLD)$(hdb_ldap_la_LINK) $(am_hdb_ldap_la_rpath) $(hdb_ldap_la_OBJECTS) $(hdb_ldap_la_LIBADD) $(LIBS) - -libhdb.la: $(libhdb_la_OBJECTS) $(libhdb_la_DEPENDENCIES) $(EXTRA_libhdb_la_DEPENDENCIES) - $(AM_V_CCLD)$(libhdb_la_LINK) -rpath $(libdir) $(libhdb_la_OBJECTS) $(libhdb_la_LIBADD) $(LIBS) - -test_dbinfo$(EXEEXT): $(test_dbinfo_OBJECTS) $(test_dbinfo_DEPENDENCIES) $(EXTRA_test_dbinfo_DEPENDENCIES) - @rm -f test_dbinfo$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(test_dbinfo_OBJECTS) $(test_dbinfo_LDADD) $(LIBS) - -test_hdbkeys$(EXEEXT): $(test_hdbkeys_OBJECTS) $(test_hdbkeys_DEPENDENCIES) $(EXTRA_test_hdbkeys_DEPENDENCIES) - @rm -f test_hdbkeys$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(test_hdbkeys_OBJECTS) $(test_hdbkeys_LDADD) $(LIBS) - -test_hdbplugin$(EXEEXT): $(test_hdbplugin_OBJECTS) $(test_hdbplugin_DEPENDENCIES) $(EXTRA_test_hdbplugin_DEPENDENCIES) - @rm -f test_hdbplugin$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(test_hdbplugin_OBJECTS) $(test_hdbplugin_LDADD) $(LIBS) - -test_mkey$(EXEEXT): $(test_mkey_OBJECTS) $(test_mkey_DEPENDENCIES) $(EXTRA_test_mkey_DEPENDENCIES) - @rm -f test_mkey$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(test_mkey_OBJECTS) $(test_mkey_LDADD) $(LIBS) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -distclean-compile: - -rm -f *.tab.c - -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_Event.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_GENERATION.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDBFlags.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_Aliases.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_Constrained_delegation_acl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_KeySet.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_Lan_Manager_OWF.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_PKINIT_acl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_PKINIT_cert.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_PKINIT_hash.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_Password.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_extension.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_extensions.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_Key.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_Keys.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_Salt.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_hdb_entry.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_hdb_entry_alias.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_hdb_keyset.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db3.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbinfo.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb-keytab.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb-ldap.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb-mdb.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb-mitdb.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb-sqlite.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb_err.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keys.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keytab.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkey.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ndbm.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dbinfo.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_hdbkeys.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_hdbplugin.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mkey.Po@am__quote@ # am--include-marker - -$(am__depfiles_remade): - @$(MKDIR_P) $(@D) - @echo '# dummy' >$@-t && $(am__mv) $@-t $@ - -am--depfiles: $(am__depfiles_remade) - -.c.o: -@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< - -.c.obj: -@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - -.c.lo: -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -install-includeHEADERS: $(include_HEADERS) - @$(NORMAL_INSTALL) - @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ - fi; \ - for p in $$list; do \ - if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ - echo "$$d$$p"; \ - done | $(am__base_list) | \ - while read files; do \ - echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ - $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ - done - -uninstall-includeHEADERS: - @$(NORMAL_UNINSTALL) - @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ - files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) -install-nodist_includeHEADERS: $(nodist_include_HEADERS) - @$(NORMAL_INSTALL) - @list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ - fi; \ - for p in $$list; do \ - if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ - echo "$$d$$p"; \ - done | $(am__base_list) | \ - while read files; do \ - echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ - $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ - done - -uninstall-nodist_includeHEADERS: - @$(NORMAL_UNINSTALL) - @list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \ - files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-am -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-am - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-am - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(BUILT_SOURCES) - $(MAKE) $(AM_MAKEFLAGS) distdir-am - -distdir-am: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done - $(MAKE) $(AM_MAKEFLAGS) \ - top_distdir="$(top_distdir)" distdir="$(distdir)" \ - dist-hook -check-am: all-am - $(MAKE) $(AM_MAKEFLAGS) check-local -check: $(BUILT_SOURCES) - $(MAKE) $(AM_MAKEFLAGS) check-am -all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) all-local -installdirs: - for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)" "$(DESTDIR)$(includedir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: $(BUILT_SOURCES) - $(MAKE) $(AM_MAKEFLAGS) install-am -install-exec: $(BUILT_SOURCES) - $(MAKE) $(AM_MAKEFLAGS) install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." - -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) -clean: clean-am - -clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ - clean-noinstPROGRAMS mostlyclean-am - -distclean: distclean-am - -rm -f ./$(DEPDIR)/asn1_Event.Plo - -rm -f ./$(DEPDIR)/asn1_GENERATION.Plo - -rm -f ./$(DEPDIR)/asn1_HDBFlags.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_Aliases.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_Constrained_delegation_acl.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_KeySet.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_Lan_Manager_OWF.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_PKINIT_acl.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_PKINIT_cert.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_PKINIT_hash.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_Password.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_extension.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_extensions.Plo - -rm -f ./$(DEPDIR)/asn1_Key.Plo - -rm -f ./$(DEPDIR)/asn1_Keys.Plo - -rm -f ./$(DEPDIR)/asn1_Salt.Plo - -rm -f ./$(DEPDIR)/asn1_hdb_entry.Plo - -rm -f ./$(DEPDIR)/asn1_hdb_entry_alias.Plo - -rm -f ./$(DEPDIR)/asn1_hdb_keyset.Plo - -rm -f ./$(DEPDIR)/common.Plo - -rm -f ./$(DEPDIR)/db.Plo - -rm -f ./$(DEPDIR)/db3.Plo - -rm -f ./$(DEPDIR)/dbinfo.Plo - -rm -f ./$(DEPDIR)/ext.Plo - -rm -f ./$(DEPDIR)/hdb-keytab.Plo - -rm -f ./$(DEPDIR)/hdb-ldap.Plo - -rm -f ./$(DEPDIR)/hdb-mdb.Plo - -rm -f ./$(DEPDIR)/hdb-mitdb.Plo - -rm -f ./$(DEPDIR)/hdb-sqlite.Plo - -rm -f ./$(DEPDIR)/hdb.Plo - -rm -f ./$(DEPDIR)/hdb_err.Plo - -rm -f ./$(DEPDIR)/keys.Plo - -rm -f ./$(DEPDIR)/keytab.Plo - -rm -f ./$(DEPDIR)/mkey.Plo - -rm -f ./$(DEPDIR)/ndbm.Plo - -rm -f ./$(DEPDIR)/print.Plo - -rm -f ./$(DEPDIR)/test_dbinfo.Po - -rm -f ./$(DEPDIR)/test_hdbkeys.Po - -rm -f ./$(DEPDIR)/test_hdbplugin.Po - -rm -f ./$(DEPDIR)/test_mkey.Po - -rm -f Makefile -distclean-am: clean-am distclean-compile distclean-generic \ - distclean-tags - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: install-includeHEADERS install-nodist_includeHEADERS - @$(NORMAL_INSTALL) - $(MAKE) $(AM_MAKEFLAGS) install-data-hook -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: install-exec-local install-libLTLIBRARIES - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f ./$(DEPDIR)/asn1_Event.Plo - -rm -f ./$(DEPDIR)/asn1_GENERATION.Plo - -rm -f ./$(DEPDIR)/asn1_HDBFlags.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_Aliases.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_Constrained_delegation_acl.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_KeySet.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_Lan_Manager_OWF.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_PKINIT_acl.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_PKINIT_cert.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_PKINIT_hash.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_Ext_Password.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_extension.Plo - -rm -f ./$(DEPDIR)/asn1_HDB_extensions.Plo - -rm -f ./$(DEPDIR)/asn1_Key.Plo - -rm -f ./$(DEPDIR)/asn1_Keys.Plo - -rm -f ./$(DEPDIR)/asn1_Salt.Plo - -rm -f ./$(DEPDIR)/asn1_hdb_entry.Plo - -rm -f ./$(DEPDIR)/asn1_hdb_entry_alias.Plo - -rm -f ./$(DEPDIR)/asn1_hdb_keyset.Plo - -rm -f ./$(DEPDIR)/common.Plo - -rm -f ./$(DEPDIR)/db.Plo - -rm -f ./$(DEPDIR)/db3.Plo - -rm -f ./$(DEPDIR)/dbinfo.Plo - -rm -f ./$(DEPDIR)/ext.Plo - -rm -f ./$(DEPDIR)/hdb-keytab.Plo - -rm -f ./$(DEPDIR)/hdb-ldap.Plo - -rm -f ./$(DEPDIR)/hdb-mdb.Plo - -rm -f ./$(DEPDIR)/hdb-mitdb.Plo - -rm -f ./$(DEPDIR)/hdb-sqlite.Plo - -rm -f ./$(DEPDIR)/hdb.Plo - -rm -f ./$(DEPDIR)/hdb_err.Plo - -rm -f ./$(DEPDIR)/keys.Plo - -rm -f ./$(DEPDIR)/keytab.Plo - -rm -f ./$(DEPDIR)/mkey.Plo - -rm -f ./$(DEPDIR)/ndbm.Plo - -rm -f ./$(DEPDIR)/print.Plo - -rm -f ./$(DEPDIR)/test_dbinfo.Po - -rm -f ./$(DEPDIR)/test_hdbkeys.Po - -rm -f ./$(DEPDIR)/test_hdbplugin.Po - -rm -f ./$(DEPDIR)/test_mkey.Po - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES \ - uninstall-nodist_includeHEADERS - @$(NORMAL_INSTALL) - $(MAKE) $(AM_MAKEFLAGS) uninstall-hook -.MAKE: all check check-am install install-am install-data-am \ - install-exec install-strip uninstall-am - -.PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \ - check-am check-local clean clean-generic clean-libLTLIBRARIES \ - clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ - ctags-am dist-hook distclean distclean-compile \ - distclean-generic distclean-libtool distclean-tags distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-data-hook install-dvi \ - install-dvi-am install-exec install-exec-am install-exec-local \ - install-html install-html-am install-includeHEADERS \ - install-info install-info-am install-libLTLIBRARIES \ - install-man install-nodist_includeHEADERS install-pdf \ - install-pdf-am install-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-compile \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am uninstall-hook \ - uninstall-includeHEADERS uninstall-libLTLIBRARIES \ - uninstall-nodist_includeHEADERS - -.PRECIOUS: Makefile - - -install-suid-programs: - @foo='$(bin_SUIDS)'; \ - for file in $$foo; do \ - x=$(DESTDIR)$(bindir)/$$file; \ - if chown 0:0 $$x && chmod u+s $$x; then :; else \ - echo "*"; \ - echo "* Failed to install $$x setuid root"; \ - echo "*"; \ - fi; \ - done - -install-exec-local: install-suid-programs - -codesign-all: - @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \ - foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \ - for file in $$foo ; do \ - echo "CODESIGN $$file" ; \ - codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \ - done ; \ - fi - -all-local: codesign-all - -install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS) - @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \ - for f in $$foo; do \ - f=`basename $$f`; \ - if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \ - else file="$$f"; fi; \ - if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \ - : ; else \ - echo " $(CP) $$file $(buildinclude)/$$f"; \ - $(CP) $$file $(buildinclude)/$$f || true; \ - fi ; \ - done ; \ - foo='$(nobase_include_HEADERS)'; \ - for f in $$foo; do \ - if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \ - else file="$$f"; fi; \ - $(mkdir_p) $(buildinclude)/`dirname $$f` ; \ - if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \ - : ; else \ - echo " $(CP) $$file $(buildinclude)/$$f"; \ - $(CP) $$file $(buildinclude)/$$f; \ - fi ; \ - done - -all-local: install-build-headers - -check-local:: - @if test '$(CHECK_LOCAL)' = "no-check-local"; then \ - foo=''; elif test '$(CHECK_LOCAL)'; then \ - foo='$(CHECK_LOCAL)'; else \ - foo='$(PROGRAMS)'; fi; \ - if test "$$foo"; then \ - failed=0; all=0; \ - for i in $$foo; do \ - all=`expr $$all + 1`; \ - if (./$$i --version && ./$$i --help) > /dev/null 2>&1; then \ - echo "PASS: $$i"; \ - else \ - echo "FAIL: $$i"; \ - failed=`expr $$failed + 1`; \ - fi; \ - done; \ - if test "$$failed" -eq 0; then \ - banner="All $$all tests passed"; \ - else \ - banner="$$failed of $$all tests failed"; \ - fi; \ - dashes=`echo "$$banner" | sed s/./=/g`; \ - echo "$$dashes"; \ - echo "$$banner"; \ - echo "$$dashes"; \ - test "$$failed" -eq 0 || exit 1; \ - fi - -# It's useful for debugging to format generated sources. The default for all -# clang-format styles is to sort includes, but in many cases in-tree we really -# don't want to do that. -.x.c: - @if [ -z "$(CLANG_FORMAT)" ]; then \ - cmp -s $< $@ 2> /dev/null || cp $< $@; \ - else \ - cp $< $@.tmp.c; \ - $(CLANG_FORMAT) -style='{BasedOnStyle: Chromium, SortIncludes: false}' -i $@.tmp.c; \ - cmp -s $@.tmp.c $@ 2> /dev/null || mv $@.tmp.c $@; \ - fi - -.hx.h: - @cmp -s $< $@ 2> /dev/null || cp $< $@; -#NROFF_MAN = nroff -man -.1.cat1: - $(NROFF_MAN) $< > $@ -.3.cat3: - $(NROFF_MAN) $< > $@ -.5.cat5: - $(NROFF_MAN) $< > $@ -.7.cat7: - $(NROFF_MAN) $< > $@ -.8.cat8: - $(NROFF_MAN) $< > $@ - -dist-cat1-mans: - @foo='$(man1_MANS)'; \ - bar='$(man_MANS)'; \ - for i in $$bar; do \ - case $$i in \ - *.1) foo="$$foo $$i";; \ - esac; done ;\ - for i in $$foo; do \ - x=`echo $$i | sed 's/\.[^.]*$$/.cat1/'`; \ - echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \ - $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \ - done - -dist-cat3-mans: - @foo='$(man3_MANS)'; \ - bar='$(man_MANS)'; \ - for i in $$bar; do \ - case $$i in \ - *.3) foo="$$foo $$i";; \ - esac; done ;\ - for i in $$foo; do \ - x=`echo $$i | sed 's/\.[^.]*$$/.cat3/'`; \ - echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \ - $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \ - done - -dist-cat5-mans: - @foo='$(man5_MANS)'; \ - bar='$(man_MANS)'; \ - for i in $$bar; do \ - case $$i in \ - *.5) foo="$$foo $$i";; \ - esac; done ;\ - for i in $$foo; do \ - x=`echo $$i | sed 's/\.[^.]*$$/.cat5/'`; \ - echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \ - $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \ - done - -dist-cat7-mans: - @foo='$(man7_MANS)'; \ - bar='$(man_MANS)'; \ - for i in $$bar; do \ - case $$i in \ - *.7) foo="$$foo $$i";; \ - esac; done ;\ - for i in $$foo; do \ - x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \ - echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \ - $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \ - done - -dist-cat8-mans: - @foo='$(man8_MANS)'; \ - bar='$(man_MANS)'; \ - for i in $$bar; do \ - case $$i in \ - *.8) foo="$$foo $$i";; \ - esac; done ;\ - for i in $$foo; do \ - x=`echo $$i | sed 's/\.[^.]*$$/.cat8/'`; \ - echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \ - $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \ - done - -dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans - -install-cat-mans: - $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS) - -uninstall-cat-mans: - $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS) - -install-data-hook: install-cat-mans -uninstall-hook: uninstall-cat-mans - -.et.h: - $(COMPILE_ET) $< -.et.c: - $(COMPILE_ET) $< - -# -# Useful target for debugging -# - -check-valgrind: - tobjdir=`cd $(top_builddir) && pwd` ; \ - tsrcdir=`cd $(top_srcdir) && pwd` ; \ - env TESTS_ENVIRONMENT="$${tsrcdir}/cf/maybe-valgrind.sh -s $${tsrcdir} -o $${tobjdir}" make check - -# -# Target to please samba build farm, builds distfiles in-tree. -# Will break when automake changes... -# - -distdir-in-tree: $(DISTFILES) $(INFO_DEPS) - list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" != .; then \ - (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) distdir-in-tree) ; \ - fi ; \ - done - -$(ALL_OBJECTS): $(HDB_PROTOS) hdb_asn1.h hdb_asn1-priv.h hdb_err.h - -$(srcdir)/hdb-protos.h: $(dist_libhdb_la_SOURCES) - cd $(srcdir); perl ../../cf/make-proto.pl -q -P comment -o hdb-protos.h $(dist_libhdb_la_SOURCES) || rm -f hdb-protos.h - -$(srcdir)/hdb-private.h: $(dist_libhdb_la_SOURCES) - cd $(srcdir); perl ../../cf/make-proto.pl -q -P comment -p hdb-private.h $(dist_libhdb_la_SOURCES) || rm -f hdb-private.h - -$(gen_files_hdb) hdb_asn1.hx hdb_asn1-priv.hx: hdb_asn1_files - -hdb_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/hdb.asn1 - $(ASN1_COMPILE) --sequence=HDB-Ext-KeySet --sequence=Keys $(srcdir)/hdb.asn1 hdb_asn1 - -# to help stupid solaris make - -hdb_err.h: hdb_err.et - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/lib/hdb/NTMakefile b/lib/hdb/NTMakefile index 01671b07805c..f4801f7c54e9 100644 --- a/lib/hdb/NTMakefile +++ b/lib/hdb/NTMakefile @@ -31,17 +31,15 @@ RELDIR=lib\hdb -!include ../../windows/NTMakefile.w32 +intcflags=-DASN1_LIB -gen_files_hdb = $(OBJ)\asn1_hdb_asn1.x +!include ../../windows/NTMakefile.w32 -$(gen_files_hdb) $(OBJ)\hdb_asn1.hx $(OBJ)\hdb_asn1-priv.hx: $(BINDIR)\asn1_compile.exe hdb.asn1 +$(OBJ)\asn1_hdb_asn1.c $(OBJ)\hdb_asn1.h $(OBJ)\hdb_asn1-priv.h: $(BINDIR)\asn1_compile.exe hdb.asn1 cd $(OBJ) - $(BINDIR)\asn1_compile.exe --sequence=HDB-Ext-KeySet --sequence=Keys --one-code-file $(SRCDIR)\hdb.asn1 hdb_asn1 + $(BINDIR)\asn1_compile.exe --one-code-file --option-file=$(SRCDIR)\hdb.opt $(SRCDIR)\hdb.asn1 hdb_asn1 cd $(SRCDIR) -$(gen_files_hdb:.x=.c): $$(@R).x - !ifdef OPENLDAP_MODULE ldap_dll = $(BINDIR)\hdb_ldap.dll @@ -98,7 +96,7 @@ libhdb_OBJs = \ $(OBJ)\mkey.obj \ $(OBJ)\ndbm.obj \ $(OBJ)\print.obj \ - $(gen_files_hdb:.x=.obj) \ + $(OBJ)\asn1_hdb_asn1.obj \ $(OBJ)\hdb_err.obj $(OBJ)\hdb_err.c $(OBJ)\hdb_err.h: hdb_err.et @@ -150,7 +148,7 @@ clean:: test:: test-binaries test-run -test-binaries: $(OBJ)\test_dbinfo.exe $(OBJ)\test_hdbkeys.exe $(OBJ)\test_hdbplugin.exe +test-binaries: $(OBJ)\test_dbinfo.exe $(OBJ)\test_hdbkeys.exe $(OBJ)\test_namespace.exe $(OBJ)\test_dbinfo.exe: $(OBJ)\test_dbinfo.obj $(LIBHDB) $(LIBHEIMDAL) $(LIBROKEN) $(LIBVERS) $(EXECONLINK) @@ -160,7 +158,7 @@ $(OBJ)\test_hdbkeys.exe: $(OBJ)\test_hdbkeys.obj $(LIBHDB) $(LIBHEIMDAL) $(LIBRO $(EXECONLINK) $(EXEPREP_NODIST) -$(OBJ)\test_hdbplugin.exe: $(OBJ)\test_hdbplugin.obj $(LIBHDB) $(LIBHEIMDAL) $(LIBROKEN) $(LIBVERS) +$(OBJ)\test_namespace.exe: $(OBJ)\test_namespace.obj $(LIBHDB) $(LIBHEIMDAL) $(LIBHEIMBASE) $(LIBROKEN) $(LIBVERS) $(EXECONLINK) $(EXEPREP_NODIST) @@ -168,7 +166,7 @@ test-run: cd $(OBJ) -test_dbinfo.exe -test_hdbkeys.exe - -test_hdbplugin.exe + -test_namespace.exe cd $(SRCDIR) !ifdef OPENLDAP_INC diff --git a/lib/hdb/common.c b/lib/hdb/common.c index 2c8bb9f305e0..1c947b3cfc54 100644 --- a/lib/hdb/common.c +++ b/lib/hdb/common.c @@ -31,6 +31,7 @@ * SUCH DAMAGE. */ +#include "krb5_locl.h" #include "hdb_locl.h" int @@ -64,7 +65,7 @@ hdb_entry2value(krb5_context context, const hdb_entry *ent, krb5_data *value) size_t len = 0; int ret; - ASN1_MALLOC_ENCODE(hdb_entry, value->data, value->length, ent, &len, ret); + ASN1_MALLOC_ENCODE(HDB_entry, value->data, value->length, ent, &len, ret); if (ret == 0 && value->length != len) krb5_abortx(context, "internal asn.1 encoder error"); return ret; @@ -73,7 +74,7 @@ hdb_entry2value(krb5_context context, const hdb_entry *ent, krb5_data *value) int hdb_value2entry(krb5_context context, krb5_data *value, hdb_entry *ent) { - return decode_hdb_entry(value->data, value->length, ent, NULL); + return decode_HDB_entry(value->data, value->length, ent, NULL); } int @@ -84,7 +85,7 @@ hdb_entry_alias2value(krb5_context context, size_t len = 0; int ret; - ASN1_MALLOC_ENCODE(hdb_entry_alias, value->data, value->length, + ASN1_MALLOC_ENCODE(HDB_entry_alias, value->data, value->length, alias, &len, ret); if (ret == 0 && value->length != len) krb5_abortx(context, "internal asn.1 encoder error"); @@ -95,17 +96,69 @@ int hdb_value2entry_alias(krb5_context context, krb5_data *value, hdb_entry_alias *ent) { - return decode_hdb_entry_alias(value->data, value->length, ent, NULL); + return decode_HDB_entry_alias(value->data, value->length, ent, NULL); } -krb5_error_code -_hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, - unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry) +/* + * Some old databases may not have stored the salt with each key, which will + * break clients when aliases or canonicalization are used. Generate a + * default salt based on the real principal name in the entry to handle + * this case. + */ +static krb5_error_code +add_default_salts(krb5_context context, HDB *db, hdb_entry *entry) { + krb5_error_code ret; + size_t i; + krb5_salt pwsalt; + + ret = krb5_get_pw_salt(context, entry->principal, &pwsalt); + if (ret) + return ret; + + for (i = 0; i < entry->keys.len; i++) { + Key *key = &entry->keys.val[i]; + + if (key->salt != NULL || + _krb5_enctype_requires_random_salt(context, key->key.keytype)) + continue; + + key->salt = calloc(1, sizeof(*key->salt)); + if (key->salt == NULL) { + ret = krb5_enomem(context); + break; + } + + key->salt->type = KRB5_PADATA_PW_SALT; + + ret = krb5_data_copy(&key->salt->salt, + pwsalt.saltvalue.data, + pwsalt.saltvalue.length); + if (ret) + break; + } + + krb5_free_salt(context, pwsalt); + + return ret; +} + +static krb5_error_code +fetch_entry_or_alias(krb5_context context, + HDB *db, + krb5_const_principal principal, + unsigned flags, + hdb_entry *entry) +{ + HDB_EntryOrAlias eoa; krb5_principal enterprise_principal = NULL; krb5_data key, value; krb5_error_code ret; + value.length = 0; + value.data = 0; + key = value; + if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { if (principal->name.name_string.len != 1) { ret = KRB5_PARSE_MALFORMED; @@ -121,59 +174,131 @@ _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, principal = enterprise_principal; } - hdb_principal2key(context, principal, &key); - if (enterprise_principal) - krb5_free_principal(context, enterprise_principal); - ret = db->hdb__get(context, db, key, &value); - krb5_data_free(&key); - if(ret) - return ret; - ret = hdb_value2entry(context, &value, &entry->entry); - if (ret == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) { - krb5_data_free(&value); - return HDB_ERR_NOENTRY; - } else if (ret == ASN1_BAD_ID) { - hdb_entry_alias alias; - - ret = hdb_value2entry_alias(context, &value, &alias); - if (ret) { + ret = hdb_principal2key(context, principal, &key); + if (ret == 0) + ret = db->hdb__get(context, db, key, &value); + if (ret == 0) + ret = decode_HDB_EntryOrAlias(value.data, value.length, &eoa, NULL); + if (ret == 0 && eoa.element == choice_HDB_EntryOrAlias_entry) { + *entry = eoa.u.entry; + entry->aliased = 0; + } else if (ret == 0 && eoa.element == choice_HDB_EntryOrAlias_alias) { + krb5_data_free(&key); + ret = hdb_principal2key(context, eoa.u.alias.principal, &key); + if (ret == 0) { krb5_data_free(&value); - return ret; + ret = db->hdb__get(context, db, key, &value); } - hdb_principal2key(context, alias.principal, &key); - krb5_data_free(&value); - free_hdb_entry_alias(&alias); + if (ret == 0) + /* No alias chaining */ + ret = hdb_value2entry(context, &value, entry); + krb5_free_principal(context, eoa.u.alias.principal); + entry->aliased = 1; + } else if (ret == 0) + ret = ENOTSUP; + if (ret == 0 && enterprise_principal) { + /* + * Whilst Windows does not canonicalize enterprise principal names if + * the canonicalize flag is unset, the original specification in + * draft-ietf-krb-wg-kerberos-referrals-03.txt says we should. + */ + entry->flags.force_canonicalize = 1; + } - ret = db->hdb__get(context, db, key, &value); - krb5_data_free(&key); - if (ret) - return ret; - ret = hdb_value2entry(context, &value, &entry->entry); - if (ret) { - krb5_data_free(&value); - return ret; - } +#if 0 + /* HDB_F_GET_ANY indicates request originated from KDC (not kadmin) */ + if (ret == 0 && eoa.element == choice_HDB_EntryOrAlias_alias && + (flags & (HDB_F_CANON|HDB_F_GET_ANY)) == 0) { + + /* `principal' was alias but canon not req'd */ + free_HDB_entry(entry); + ret = HDB_ERR_NOENTRY; } +#endif + + krb5_free_principal(context, enterprise_principal); krb5_data_free(&value); + krb5_data_free(&key); + principal = enterprise_principal = NULL; + return ret; +} + +/* + * We have only one type of aliases in our HDB entries, but we really need two: + * hard and soft. + * + * Hard aliases should be treated as if they were distinct principals with the + * same keys. + * + * Soft aliases should be treated as configuration to issue referrals, and they + * can only result in referrals to other realms. + * + * Rather than add a type of aliases, we'll use a convention where the form of + * the target of the alias indicates whether the alias is hard or soft. + * + * TODO We could also use an attribute of the aliased entry. + */ +static int +is_soft_alias_p(krb5_context context, + krb5_const_principal principal, + unsigned int flags, + hdb_entry *h) +{ + /* Target is a WELLKNOWN/REFERRALS/TARGET/... -> soft alias */ + if (krb5_principal_get_num_comp(context, h->principal) >= 3 && + strcmp(krb5_principal_get_comp_string(context, h->principal, 0), + KRB5_WELLKNOWN_NAME) == 0 && + strcmp(krb5_principal_get_comp_string(context, h->principal, 1), + "REFERRALS") == 0 && + strcmp(krb5_principal_get_comp_string(context, h->principal, 2), + "TARGET") == 0) + return 1; + + /* + * Pre-8.0 we had only soft aliases for a while, and one site used aliases + * of referrals-targetNN@TARGET-REALM. + */ + if (krb5_principal_get_num_comp(context, h->principal) == 1 && + strncmp("referrals-target", + krb5_principal_get_comp_string(context, h->principal, 0), + sizeof("referrals-target") - 1) == 0) + return 1; + + /* All other cases are hard aliases */ + return 0; +} + +krb5_error_code +_hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, + unsigned flags, krb5_kvno kvno, hdb_entry *entry) +{ + krb5_error_code ret; + int soft_aliased = 0; + int same_realm; + + ret = fetch_entry_or_alias(context, db, principal, flags, entry); + if (ret) + return ret; + if ((flags & HDB_F_DECRYPT) && (flags & HDB_F_ALL_KVNOS)) { /* Decrypt the current keys */ - ret = hdb_unseal_keys(context, db, &entry->entry); + ret = hdb_unseal_keys(context, db, entry); if (ret) { - hdb_free_entry(context, entry); + hdb_free_entry(context, db, entry); return ret; } /* Decrypt the key history too */ - ret = hdb_unseal_keys_kvno(context, db, 0, flags, &entry->entry); + ret = hdb_unseal_keys_kvno(context, db, 0, flags, entry); if (ret) { - hdb_free_entry(context, entry); + hdb_free_entry(context, db, entry); return ret; } } else if ((flags & HDB_F_DECRYPT)) { - if ((flags & HDB_F_KVNO_SPECIFIED) == 0 || kvno == entry->entry.kvno) { + if ((flags & HDB_F_KVNO_SPECIFIED) == 0 || kvno == entry->kvno) { /* Decrypt the current keys */ - ret = hdb_unseal_keys(context, db, &entry->entry); + ret = hdb_unseal_keys(context, db, entry); if (ret) { - hdb_free_entry(context, entry); + hdb_free_entry(context, db, entry); return ret; } } else { @@ -183,15 +308,75 @@ _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, * Find and decrypt the keys from the history that we want, * and swap them with the current keys */ - ret = hdb_unseal_keys_kvno(context, db, kvno, flags, &entry->entry); + ret = hdb_unseal_keys_kvno(context, db, kvno, flags, entry); if (ret) { - hdb_free_entry(context, entry); + hdb_free_entry(context, db, entry); return ret; } } } + if ((flags & HDB_F_FOR_AS_REQ) && (flags & HDB_F_GET_CLIENT)) { + /* + * Generate default salt for any principals missing one; note such + * principals could include those for which a random (non-password) + * key was generated, but given the salt will be ignored by a keytab + * client it doesn't hurt to include the default salt. + */ + ret = add_default_salts(context, db, entry); + if (ret) { + hdb_free_entry(context, db, entry); + return ret; + } + } - return 0; + if (!entry->aliased) + return 0; + + soft_aliased = is_soft_alias_p(context, principal, flags, entry); + + /* Never return HDB_ERR_WRONG_REALM to kadm5 or other non-KDC callers */ + if ((flags & HDB_F_ADMIN_DATA)) + return 0; + + same_realm = krb5_realm_compare(context, principal, entry->principal); + + if (entry->aliased && !soft_aliased) { + /* + * This is a hard alias. We'll make the entry's name be the same as + * the alias. + * + * Except, we allow for disabling this for same-realm aliases, mainly + * for our tests. + */ + if (same_realm && + krb5_config_get_bool_default(context, NULL, FALSE, "hdb", + "same_realm_aliases_are_soft", NULL)) + return 0; + + /* EPNs are always soft */ + if (principal->name.name_type != KRB5_NT_ENTERPRISE_PRINCIPAL) { + krb5_free_principal(context, entry->principal); + ret = krb5_copy_principal(context, principal, &entry->principal); + if (ret) { + hdb_free_entry(context, db, entry); + return ret; + } + } + return 0; + } + + /* Same realm -> not a referral, therefore this is a hard alias */ + if (same_realm) { + if (soft_aliased) { + /* Soft alias to the same realm?! No. */ + hdb_free_entry(context, db, entry); + return HDB_ERR_NOENTRY; + } + return 0; + } + + /* Not same realm && not hard alias */ + return HDB_ERR_WRONG_REALM; } static krb5_error_code @@ -216,7 +401,7 @@ hdb_remove_aliases(krb5_context context, HDB *db, krb5_data *key) code = hdb_entry_get_aliases(&oldentry, &aliases); if (code || aliases == NULL) { - free_hdb_entry(&oldentry); + free_HDB_entry(&oldentry); return code; } for (i = 0; i < aliases->aliases.len; i++) { @@ -226,32 +411,34 @@ hdb_remove_aliases(krb5_context context, HDB *db, krb5_data *key) if (code == 0) { code = db->hdb__del(context, db, akey); krb5_data_free(&akey); + if (code == HDB_ERR_NOENTRY) + code = 0; } if (code) { - free_hdb_entry(&oldentry); + free_HDB_entry(&oldentry); return code; } } - free_hdb_entry(&oldentry); + free_HDB_entry(&oldentry); return 0; } static krb5_error_code hdb_add_aliases(krb5_context context, HDB *db, - unsigned flags, hdb_entry_ex *entry) + unsigned flags, hdb_entry *entry) { const HDB_Ext_Aliases *aliases; krb5_error_code code; krb5_data key, value; size_t i; - code = hdb_entry_get_aliases(&entry->entry, &aliases); + code = hdb_entry_get_aliases(entry, &aliases); if (code || aliases == NULL) return code; for (i = 0; i < aliases->aliases.len; i++) { hdb_entry_alias entryalias; - entryalias.principal = entry->entry.principal; + entryalias.principal = entry->principal; code = hdb_entry_alias2value(context, &entryalias, &value); if (code) @@ -261,6 +448,12 @@ hdb_add_aliases(krb5_context context, HDB *db, if (code == 0) { code = db->hdb__put(context, db, flags, key, value); krb5_data_free(&key); + if (code == HDB_ERR_EXISTS) + /* + * Assuming hdb_check_aliases() was called, this must be a + * duplicate in the alias list. + */ + code = 0; } krb5_data_free(&value); if (code) @@ -269,57 +462,116 @@ hdb_add_aliases(krb5_context context, HDB *db, return 0; } +/* Check if new aliases are already used for other entries */ static krb5_error_code -hdb_check_aliases(krb5_context context, HDB *db, hdb_entry_ex *entry) +hdb_check_aliases(krb5_context context, HDB *db, hdb_entry *entry) { - const HDB_Ext_Aliases *aliases; - int code; + const HDB_Ext_Aliases *aliases = NULL; + HDB_EntryOrAlias eoa; + krb5_data akey, value; size_t i; + int ret; - /* check if new aliases already is used */ + memset(&eoa, 0, sizeof(eoa)); + krb5_data_zero(&value); + akey = value; - code = hdb_entry_get_aliases(&entry->entry, &aliases); - if (code) - return code; + ret = hdb_entry_get_aliases(entry, &aliases); + for (i = 0; ret == 0 && aliases && i < aliases->aliases.len; i++) { + ret = hdb_principal2key(context, &aliases->aliases.val[i], &akey); + if (ret == 0) + ret = db->hdb__get(context, db, akey, &value); + if (ret == 0) + ret = decode_HDB_EntryOrAlias(value.data, value.length, &eoa, NULL); + if (ret == 0 && eoa.element != choice_HDB_EntryOrAlias_entry && + eoa.element != choice_HDB_EntryOrAlias_alias) + ret = ENOTSUP; + if (ret == 0 && eoa.element == choice_HDB_EntryOrAlias_entry) + /* New alias names an existing non-alias entry in the HDB */ + ret = HDB_ERR_EXISTS; + if (ret == 0 && eoa.element == choice_HDB_EntryOrAlias_alias && + !krb5_principal_compare(context, eoa.u.alias.principal, + entry->principal)) + /* New alias names an existing alias of a different entry */ + ret = HDB_ERR_EXISTS; + if (ret == HDB_ERR_NOENTRY) /* from db->hdb__get */ + /* New alias is a name that doesn't exist in the HDB */ + ret = 0; - for (i = 0; aliases && i < aliases->aliases.len; i++) { - hdb_entry_alias alias; - krb5_data akey, value; + free_HDB_EntryOrAlias(&eoa); + krb5_data_free(&value); + krb5_data_free(&akey); + } + return ret; +} - code = hdb_principal2key(context, &aliases->aliases.val[i], &akey); - if (code == 0) { - code = db->hdb__get(context, db, akey, &value); - krb5_data_free(&akey); - } - if (code == HDB_ERR_NOENTRY) - continue; - else if (code) - return code; +/* + * Many HDB entries don't have `etypes' setup. Historically we use the + * enctypes of the selected keyset as the entry's supported enctypes, but that + * is problematic. By doing this at store time and, if need be, at fetch time, + * we can make sure to stop deriving supported etypes from keys in the long + * run. We also need kadm5/kadmin support for etypes. We'll use this function + * there to derive etypes when using a kadm5_principal_ent_t that lacks the new + * TL data for etypes. + */ +krb5_error_code +hdb_derive_etypes(krb5_context context, hdb_entry *e, HDB_Ext_KeySet *base_keys) +{ + krb5_error_code ret = 0; + size_t i, k, netypes; + HDB_extension *ext; - code = hdb_value2entry_alias(context, &value, &alias); - krb5_data_free(&value); + if (!base_keys && + (ext = hdb_find_extension(e, choice_HDB_extension_data_hist_keys))) + base_keys = &ext->data.u.hist_keys; - if (code == ASN1_BAD_ID) - return HDB_ERR_EXISTS; - else if (code) - return code; + netypes = e->keys.len; + if (netypes == 0 && base_keys) { + /* There's no way that base_keys->val[i].keys.len == 0, but hey */ + for (i = 0; netypes == 0 && i < base_keys->len; i++) + netypes = base_keys->val[i].keys.len; + } + + if (netypes == 0) + return 0; - code = krb5_principal_compare(context, alias.principal, - entry->entry.principal); - free_hdb_entry_alias(&alias); - if (code == 0) - return HDB_ERR_EXISTS; + if (e->etypes != NULL) { + free(e->etypes->val); + e->etypes->len = 0; + e->etypes->val = 0; + } else if ((e->etypes = calloc(1, sizeof(e->etypes[0]))) == NULL) { + ret = krb5_enomem(context); + } + if (ret == 0 && + (e->etypes->val = calloc(netypes, sizeof(e->etypes->val[0]))) == NULL) + ret = krb5_enomem(context); + if (ret) { + free(e->etypes); + e->etypes = 0; + return ret; + } + e->etypes->len = netypes; + for (i = 0; i < e->keys.len && i < netypes; i++) + e->etypes->val[i] = e->keys.val[i].key.keytype; + if (!base_keys || i) + return 0; + for (k = 0; i == 0 && k < base_keys->len; k++) { + if (!base_keys->val[k].keys.len) + continue; + for (; i < base_keys->val[k].keys.len; i++) + e->etypes->val[i] = base_keys->val[k].keys.val[i].key.keytype; } return 0; } krb5_error_code -_hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +_hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) { krb5_data key, value; int code; - if (entry->entry.flags.do_not_store) + if (entry->flags.do_not_store || + entry->flags.force_canonicalize) return HDB_ERR_MISUSE; /* check if new aliases already is used */ code = hdb_check_aliases(context, db, entry); @@ -330,7 +582,7 @@ _hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) return 0; if ((flags & HDB_F_PRECHECK)) { - code = hdb_principal2key(context, entry->entry.principal, &key); + code = hdb_principal2key(context, entry->principal, &key); if (code) return code; code = db->hdb__get(context, db, key, &value); @@ -342,25 +594,31 @@ _hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) return code ? code : HDB_ERR_EXISTS; } - if(entry->entry.generation == NULL) { + if ((entry->etypes == NULL || entry->etypes->len == 0) && + (code = hdb_derive_etypes(context, entry, NULL))) + return code; + + if (entry->generation == NULL) { struct timeval t; - entry->entry.generation = malloc(sizeof(*entry->entry.generation)); - if(entry->entry.generation == NULL) { + entry->generation = malloc(sizeof(*entry->generation)); + if(entry->generation == NULL) { krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } gettimeofday(&t, NULL); - entry->entry.generation->time = t.tv_sec; - entry->entry.generation->usec = t.tv_usec; - entry->entry.generation->gen = 0; + entry->generation->time = t.tv_sec; + entry->generation->usec = t.tv_usec; + entry->generation->gen = 0; } else - entry->entry.generation->gen++; + entry->generation->gen++; - code = hdb_seal_keys(context, db, &entry->entry); + code = hdb_seal_keys(context, db, entry); if (code) return code; - hdb_principal2key(context, entry->entry.principal, &key); + code = hdb_principal2key(context, entry->principal, &key); + if (code) + return code; /* remove aliases */ code = hdb_remove_aliases(context, db, &key); @@ -368,8 +626,9 @@ _hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) krb5_data_free(&key); return code; } - hdb_entry2value(context, &entry->entry, &value); - code = db->hdb__put(context, db, flags & HDB_F_REPLACE, key, value); + code = hdb_entry2value(context, entry, &value); + if (code == 0) + code = db->hdb__put(context, db, flags & HDB_F_REPLACE, key, value); krb5_data_free(&value); krb5_data_free(&key); if (code) @@ -385,35 +644,1264 @@ _hdb_remove(krb5_context context, HDB *db, unsigned flags, krb5_const_principal principal) { krb5_data key, value; + HDB_EntryOrAlias eoa; + int is_alias = -1; int code; - hdb_principal2key(context, principal, &key); + /* + * We only allow deletion of entries by canonical name. To remove an + * alias use kadm5_modify_principal(). + * + * We need to determine if this is an alias. We decode as a + * HDB_EntryOrAlias, which is expensive -- we could decode as a + * HDB_entry_alias instead and assume it's an entry if decoding fails... + */ + + code = hdb_principal2key(context, principal, &key); + if (code == 0) + code = db->hdb__get(context, db, key, &value); + if (code == 0) { + code = decode_HDB_EntryOrAlias(value.data, value.length, &eoa, NULL); + krb5_data_free(&value); + } + if (code == 0) { + is_alias = eoa.element == choice_HDB_EntryOrAlias_entry ? 0 : 1; + free_HDB_EntryOrAlias(&eoa); + } if ((flags & HDB_F_PRECHECK)) { + if (code == 0 && is_alias) + krb5_set_error_message(context, code = HDB_ERR_NOENTRY, + "Cannot delete alias of principal"); + krb5_data_free(&key); + return code; + } + + if (code == 0) + code = hdb_remove_aliases(context, db, &key); + if (code == 0) + code = db->hdb__del(context, db, key); + krb5_data_free(&key); + return code; +} + +/* PRF+(K_base, pad, keylen(etype)) */ +static krb5_error_code +derive_Key1(krb5_context context, + krb5_data *pad, + EncryptionKey *base, + krb5int32 etype, + EncryptionKey *nk) +{ + krb5_error_code ret; + krb5_crypto crypto = NULL; + krb5_data out; + size_t len; + + out.data = 0; + out.length = 0; + + ret = krb5_enctype_keysize(context, base->keytype, &len); + if (ret == 0) + ret = krb5_crypto_init(context, base, 0, &crypto); + if (ret == 0) + ret = krb5_crypto_prfplus(context, crypto, pad, len, &out); + if (crypto) + krb5_crypto_destroy(context, crypto); + if (ret == 0) + ret = krb5_random_to_key(context, etype, out.data, out.length, nk); + krb5_data_free(&out); + return ret; +} + +/* PRF+(PRF+(K_base, princ, keylen(etype)), kvno, keylen(etype)) */ +/* XXX Make it PRF+(PRF+(K_base, princ, keylen(K_base.etype)), and lift it, kvno, keylen(etype)) */ +static krb5_error_code +derive_Key(krb5_context context, + const char *princ, + krb5uint32 kvno, + EncryptionKey *base, + krb5int32 etype, + Key *nk) +{ + krb5_error_code ret = 0; + EncryptionKey intermediate; + krb5_data pad; + + nk->salt = NULL; + nk->mkvno = NULL; + nk->key.keytype = 0; + nk->key.keyvalue.data = 0; + nk->key.keyvalue.length = 0; + + intermediate.keytype = 0; + intermediate.keyvalue.data = 0; + intermediate.keyvalue.length = 0; + if (princ) { + /* Derive intermediate key for the given principal */ + /* XXX Lift to optimize? */ + pad.data = (void *)(uintptr_t)princ; + pad.length = strlen(princ); + ret = derive_Key1(context, &pad, base, etype, &intermediate); + if (ret == 0) + base = &intermediate; + } /* else `base' is already an intermediate key for the desired princ */ + + /* Derive final key for `kvno' from intermediate key */ + kvno = htonl(kvno); + pad.data = &kvno; + pad.length = sizeof(kvno); + if (ret == 0) + ret = derive_Key1(context, &pad, base, etype, &nk->key); + free_EncryptionKey(&intermediate); + return ret; +} + +/* + * PRF+(PRF+(K_base, princ, keylen(etype)), kvno, keylen(etype)) for one + * enctype. + */ +static krb5_error_code +derive_Keys(krb5_context context, + const char *princ, + krb5uint32 kvno, + krb5int32 etype, + const Keys *base, + Keys *dk) + +{ + krb5_error_code ret = 0; + size_t i; + Key nk; + + dk->len = 0; + dk->val = 0; + + /* + * The enctypes of the base keys is the list of enctypes to derive keys + * for. Still, we derive all keys from the first base key. + */ + for (i = 0; ret == 0 && i < base->len; i++) { + if (etype != KRB5_ENCTYPE_NULL && etype != base->val[i].key.keytype) + continue; + ret = derive_Key(context, princ, kvno, &base->val[0].key, + base->val[i].key.keytype, &nk); + if (ret) + break; + ret = add_Keys(dk, &nk); + free_Key(&nk); /* - * We don't check that we can delete the aliases because we - * assume that the DB is consistent. If we did check for alias - * consistency we'd also have to provide a way to fsck the DB, - * otherwise admins would have no way to recover -- papering - * over this here is less work, but we really ought to provide - * an HDB fsck. + * FIXME We need to finish kdc/kadm5/kadmin support for the `etypes' so + * we can reduce the number of keys in keytabs to just those in current + * use and only of *one* enctype. + * + * What we could do is derive *one* key and for the others output a + * one-byte key of the intended enctype (which will never work). + * + * We'll never need any keys but the first one... */ - code = db->hdb__get(context, db, key, &value); - krb5_data_free(&key); - if (code == 0) { - krb5_data_free(&value); + } + + if (ret) + free_Keys(dk); + return ret; +} + +/* Helper for derive_keys_for_kr() */ +static krb5_error_code +derive_keyset(krb5_context context, + const Keys *base_keys, + const char *princ, + krb5int32 etype, + krb5uint32 kvno, + KerberosTime set_time, /* "now" */ + hdb_keyset *dks) +{ + dks->kvno = kvno; + dks->keys.val = 0; + dks->set_time = malloc(sizeof(*(dks->set_time))); + if (dks->set_time == NULL) + return krb5_enomem(context); + *dks->set_time = set_time; + return derive_Keys(context, princ, kvno, etype, base_keys, &dks->keys); +} + +/* Possibly derive and install in `h' a keyset identified by `t' */ +static krb5_error_code +derive_keys_for_kr(krb5_context context, + hdb_entry *h, + HDB_Ext_KeySet *base_keys, + int is_current_keyset, + int rotation_period_offset, + const char *princ, + krb5int32 etype, + krb5uint32 kvno_wanted, + KerberosTime t, + struct KeyRotation *krp) +{ + krb5_error_code ret; + hdb_keyset dks; + KerberosTime set_time, n; + krb5uint32 kvno; + size_t i; + + if (rotation_period_offset < -1 || rotation_period_offset > 1) + return EINVAL; /* wat */ + + /* + * Compute `kvno' and `set_time' given `t' and `krp'. + * + * There be signed 32-bit time_t dragons here. + * + * (t - krp->epoch < 0) is better than (krp->epoch < t), making us more + * tolerant of signed 32-bit time_t here near 2038. Of course, we have + * signed 32-bit time_t dragons elsewhere. + * + * We don't need to check for n == 0 && rotation_period_offset < 0 because + * only derive_keys_for_current_kr() calls us with non-zero rotation period + * offsets, and it will never call us in that case. + */ + if (t - krp->epoch < 0) + return 0; /* This KR is not relevant yet */ + n = (t - krp->epoch) / krp->period; + n += rotation_period_offset; + set_time = krp->epoch + krp->period * n; + kvno = krp->base_kvno + n; + + /* + * Since this principal is virtual, or has virtual keys, we're going to + * derive a "password expiration time" for it in order to help httpkadmind + * and other tools figure out when to request keys again. + * + * The kadm5 representation of principals does not include the set_time of + * keys/keysets, so we can't have httpkadmind derive a Cache-Control from + * that without adding yet another "TL data". Since adding TL data is a + * huge pain, we'll just use the `pw_end' field of `HDB_entry' to + * communicate when this principal's keys will change next. + */ + if (h->pw_end[0] == 0) { + KerberosTime used = (t - krp->epoch) % krp->period; + KerberosTime left = krp->period - used; + + /* + * If `h->pw_end[0]' == 0 then this must be the current period of the + * current KR we're deriving keys for. See upstairs. + * + * If there's more than a quarter of this time period left, then we'll + * set `h->pw_end[0]' to one quarter before the end of this time + * period. Else we'll set it to 1/4 after (we'll be including the next + * set of derived keys, so there's no harm in waiting that long to + * refetch). + */ + if (left > krp->period >> 2) + h->pw_end[0] = set_time + krp->period - (krp->period >> 2); + else + h->pw_end[0] = set_time + krp->period + (krp->period >> 2); + } + + + /* + * Do not waste cycles computing keys not wanted or needed. + * A past kvno is too old if its set_time + rotation period is in the past + * by more than half a rotation period, since then no service ticket + * encrypted with keys of that kvno can still be extant. + * + * A future kvno is not coming up soon enough if we're more than a quarter + * of the rotation period away from it. + * + * Recall: the assumption for virtually-keyed principals is that services + * fetch their future keys frequently enough that they'll never miss having + * the keys they need. + */ + if (!is_current_keyset || rotation_period_offset != 0) { + if ((kvno_wanted && kvno != kvno_wanted) || + t - (set_time + krp->period + (krp->period >> 1)) > 0 || + (set_time - t > 0 && (set_time - t) > (krp->period >> 2))) return 0; + } + + for (i = 0; i < base_keys->len; i++) { + if (base_keys->val[i].kvno == krp->base_key_kvno) + break; + } + if (i == base_keys->len) { + /* Base key not found! */ + if (kvno_wanted || is_current_keyset) { + krb5_set_error_message(context, ret = HDB_ERR_KVNO_NOT_FOUND, + "Base key version %u not found for %s", + krp->base_key_kvno, princ); + return ret; } - return code; + return 0; } - code = hdb_remove_aliases(context, db, &key); - if (code) { - krb5_data_free(&key); - return code; + ret = derive_keyset(context, &base_keys->val[i].keys, princ, etype, kvno, + set_time, &dks); + if (ret == 0) + ret = hdb_install_keyset(context, h, is_current_keyset, &dks); + + free_HDB_keyset(&dks); + return ret; +} + +/* Derive and install current keys, and possibly preceding or next keys */ +static krb5_error_code +derive_keys_for_current_kr(krb5_context context, + hdb_entry *h, + HDB_Ext_KeySet *base_keys, + const char *princ, + unsigned int flags, + krb5int32 etype, + krb5uint32 kvno_wanted, + KerberosTime t, + struct KeyRotation *krp, + KerberosTime future_epoch) +{ + krb5_error_code ret; + + /* derive_keys_for_kr() for current kvno and install as the current keys */ + ret = derive_keys_for_kr(context, h, base_keys, 1, 0, princ, etype, + kvno_wanted, t, krp); + if (!(flags & HDB_F_ALL_KVNOS)) + return ret; + + /* */ + + + /* + * derive_keys_for_kr() for prev kvno if still needed -- it can only be + * needed if the prev kvno's start time is within this KR's epoch. + * + * Note that derive_keys_for_kr() can return without doing anything if this + * is isn't the current keyset. So these conditions need not be + * sufficiently narrow. + */ + if (ret == 0 && t - krp->epoch >= krp->period) + ret = derive_keys_for_kr(context, h, base_keys, 0, -1, princ, etype, + kvno_wanted, t, krp); + /* + * derive_keys_for_kr() for next kvno if near enough, but only if it + * doesn't start after the next KR's epoch. + */ + if (future_epoch && + t - krp->epoch >= 0 /* We know! Hint to the compiler */) { + KerberosTime next_kvno_start, n; + + n = (t - krp->epoch) / krp->period; + next_kvno_start = krp->epoch + krp->period * (n + 1); + if (future_epoch - next_kvno_start <= 0) + return ret; } - code = db->hdb__del(context, db, key); - krb5_data_free(&key); - return code; + if (ret == 0) + ret = derive_keys_for_kr(context, h, base_keys, 0, 1, princ, etype, + kvno_wanted, t, krp); + return ret; +} + +/* + * Derive and install all keysets in `h' that `princ' needs at time `now'. + * + * This mutates the entry `h' to + * + * a) not have base keys, + * b) have keys derived from the base keys according to + * c) the key rotation periods for the base principal (possibly the same + * principal if it's a concrete principal with virtual keys), and the + * requested time, enctype, and kvno (all of which are optional, with zero + * implying some default). + * + * Arguments: + * + * - `flags' is the flags passed to `hdb_fetch_kvno()' + * - `princ' is the name of the principal we'll end up with in `entry' + * - `h_is_namespace' indicates whether `h' is for a namespace or a concrete + * principal (that might nonetheless have virtual/derived keys) + * - `t' is the time such that the derived keys are for kvnos needed at `t' + * - `etype' indicates what enctype to derive keys for (0 for all enctypes in + * `entry->etypes') + * - `kvno' requests a particular kvno, or all if zero + * + * The caller doesn't know if the principal needs key derivation -- we make + * that determination in this function. + * + * Note that this function is fully deterministic for any given set of + * arguments and HDB contents. + * + * Definitions: + * + * - A keyset is a set of keys for a single kvno. + * - A keyset is relevant IFF: + * - it is the keyset for a time period identified by `t' in a + * corresponding KR + * - it is a keyset for a past time period for which there may be extant, + * not-yet-expired tickets that a service may need to decrypt + * - it is a keyset for an upcoming time period that a service will need to + * fetch before that time period becomes current, that way the service + * can have keytab entries for those keys in time for when the KDC starts + * encrypting service tickets to those keys + * + * This function derives the keyset(s) for the current KR first. The idea is + * to optimize the order of resulting keytabs so that the most likely keys to + * be used come first. + * + * Invariants: + * + * - KR metadata is sane because sanity is checked for when storing HDB + * entries + * - KRs are sorted by epoch in descending order; KR #0's epoch is the most + * recent + * - KR periods are non-zero (we divide by period) + * - kvnos are numerically ordered and correspond to time periods + * - within each KR, the kvnos for larger times are larger than (or equal + * to) the kvnos of earlier times + * - at KR boundaries, the first kvno of the newer boundary is larger than + * the kvno of the last time period of the previous KR + * - the time `t' must fall into exactly one KR period + * - the time `t' must fall into exactly one period within a KR period + * - at most two kvnos will be relevant from the KR that `t' falls into + * (the current kvno for `t', and possibly either the preceding, or the + * next) + * - at most one kvno from non-current KRs will be derived: possibly one for a + * preceding KR, and possibly one from an upcoming KR + * + * There can be: + * + * - no KR extension (not a namespace principal, and no virtual keys) + * - 1, 2, or 3 KRs (see above) + * - the newest KR may have the `deleted' flag, meaning "does not exist after + * this epoch" + * + * Note that the last time period in any older KR can be partial. + * + * Timeline diagram: + * + * .......|--+--+...+--|---+---+---+...+--|----+... + * T20 T10 T11 RT12 T1n T01 + * ^ ^ ^ ^ ^ ^ ^ T00 + * | | | T22 T2n | | ^ + * ^ | T21 | | | + * princ | | epoch of | epoch of + * did | | middle KR | newest epoch + * not | | | + * exist! | start of Note that T1n + * | second kvno is shown as shorter + * | in 1st epoch than preceding periods + * | + * ^ + * first KR's + * epoch, and start + * of its first kvno + * + * Tmn == the start of the Mth KR's Nth time period. + * (higher M -> older KR; lower M -> newer KR) + * (N is the reverse: lower N -> older time period in KR) + * T20 == start of oldest KR -- no keys before this time will be derived. + * T2n == last time period in oldest KR + * T10 == start of middle KR + * T1n == last time period in middle KR + * T00 == start of newest KR + * T0n == current time period in newest KR for wall clock time + */ +static krb5_error_code +derive_keys(krb5_context context, + unsigned flags, + krb5_const_principal princ, + int h_is_namespace, + krb5_timestamp t, + krb5int32 etype, + krb5uint32 kvno, + hdb_entry *h) +{ + HDB_Ext_KeyRotation kr; + HDB_Ext_KeySet base_keys; + krb5_error_code ret = 0; + size_t current_kr, future_kr, past_kr, i; + char *p = NULL; + int valid = 1; + + if (!h_is_namespace && !h->flags.virtual_keys) + return 0; + h->flags.virtual = 1; + + kr.len = 0; + kr.val = 0; + if (ret == 0) { + const HDB_Ext_KeyRotation *ckr; + + /* Installing keys invalidates `ckr', so we copy it */ + ret = hdb_entry_get_key_rotation(context, h, &ckr); + if (!ckr) + return ret; + if (ret == 0) + ret = copy_HDB_Ext_KeyRotation(ckr, &kr); + } + + /* Get the base keys from the entry, and remove them */ + base_keys.val = 0; + base_keys.len = 0; + if (ret == 0) + ret = _hdb_remove_base_keys(context, h, &base_keys, &kr); + + /* Make sure we have h->etypes */ + if (ret == 0 && !h->etypes) + ret = hdb_derive_etypes(context, h, &base_keys); + + /* Keys not desired? Don't derive them! */ + if (ret || !(flags & HDB_F_DECRYPT)) { + free_HDB_Ext_KeyRotation(&kr); + free_HDB_Ext_KeySet(&base_keys); + return ret; + } + + /* The principal name will be used in key derivation and error messages */ + if (ret == 0) + ret = krb5_unparse_name(context, princ, &p); + + /* Sanity check key rotations, determine current & last kr */ + if (ret == 0 && kr.len < 1) + krb5_set_error_message(context, ret = HDB_ERR_NOENTRY, + "no key rotation periods for %s", p); + if (ret == 0) + current_kr = future_kr = past_kr = kr.len; + else + current_kr = future_kr = past_kr = 1; + + /* + * Identify a current, next, and previous KRs if there are any. + * + * There can be up to three KRs, ordered by epoch, descending, making up a + * timeline like: + * + * ...|---------|--------|------> + * ^ | | | + * | | | | + * | | | Newest KR (kr.val[0]) + * | | Middle KR (kr.val[1]) + * | Oldest (last) KR (kr.val[2]) + * | + * Before the begging of time for this namespace + * + * We step through these from future towards past looking for the best + * future, current, and past KRs. The best current KR is one that has its + * epoch nearest to `t' but in the past of `t'. + * + * We validate KRs before storing HDB entries with the KR extension, so we + * can assume they are valid here. However, we do some validity checking, + * and if they're not valid, we pick the best current KR and ignore the + * others. + * + * In principle there cannot be two future KRs, but this function is + * deterministic and takes a time value, so it should not enforce this just + * so we can test. Enforcement of such rules should be done at store time. + */ + for (i = 0; ret == 0 && i < kr.len; i++) { + /* Minimal validation: order and period */ + if (i && kr.val[i - 1].epoch - kr.val[i].epoch <= 0) { + future_kr = past_kr = kr.len; + valid = 0; + } + if (!kr.val[i].period) { + future_kr = past_kr = kr.len; + valid = 0; + continue; + } + if (t - kr.val[i].epoch >= 0) { + /* + * `t' is in the future of this KR's epoch, so it's a candidate for + * either current or past KR. + */ + if (current_kr == kr.len) + current_kr = i; /* First curr KR candidate; should be best */ + else if (kr.val[current_kr].epoch - kr.val[i].epoch < 0) + current_kr = i; /* Invalid KRs, but better curr KR cand. */ + else if (valid && past_kr == kr.len) + past_kr = i; + } else if (valid) { + /* This KR is in the future of `t', a candidate for next KR */ + future_kr = i; + } + } + if (ret == 0 && current_kr == kr.len) + /* No current KR -> too soon */ + krb5_set_error_message(context, ret = HDB_ERR_NOENTRY, + "Too soon for virtual principal to exist"); + + /* Check that the principal has not been marked deleted */ + if (ret == 0 && current_kr < kr.len && kr.val[current_kr].flags.deleted) + krb5_set_error_message(context, ret = HDB_ERR_NOENTRY, + "virtual principal %s does not exist " + "because last key rotation period " + "marks deletion", p); + + /* See `derive_keys_for_kr()' */ + if (h->pw_end == NULL && + (h->pw_end = calloc(1, sizeof(h->pw_end[0]))) == NULL) + ret = krb5_enomem(context); + + /* + * Derive and set in `h' its current kvno and current keys. + * + * This will set h->kvno as well. + * + * This may set up to TWO keysets for the current key rotation period: + * - current keys (h->keys and h->kvno) + * - possibly one future + * OR + * possibly one past keyset in hist_keys for the current_kr + */ + if (ret == 0 && current_kr < kr.len) + ret = derive_keys_for_current_kr(context, h, &base_keys, p, flags, + etype, kvno, t, &kr.val[current_kr], + current_kr ? kr.val[0].epoch : 0); + + /* + * Derive and set in `h' its future keys for next KR if it is soon to be + * current. + * + * We want to derive keys for the first kvno of the next (future) KR if + * it's sufficiently close to `t', meaning within 1 period of the current + * KR, but we want these keys to be available sooner, so 1.5 of the current + * period. + */ + if (ret == 0 && future_kr < kr.len && (flags & HDB_F_ALL_KVNOS)) + ret = derive_keys_for_kr(context, h, &base_keys, 0, 0, p, etype, kvno, + kr.val[future_kr].epoch, &kr.val[future_kr]); + + /* + * Derive and set in `h' its past keys for the previous KR if its last time + * period could still have extant, unexpired service tickets encrypted in + * its keys. + */ + if (ret == 0 && past_kr < kr.len && (flags & HDB_F_ALL_KVNOS)) + ret = derive_keys_for_kr(context, h, &base_keys, 0, 0, p, etype, kvno, + kr.val[current_kr].epoch - 1, &kr.val[past_kr]); + + /* + * Impose a bound on h->max_life so that [when the KDC is the caller] + * the KDC won't issue tickets longer lived than this. + */ + if (ret == 0 && !h->max_life && + (h->max_life = calloc(1, sizeof(h->max_life[0]))) == NULL) + ret = krb5_enomem(context); + if (ret == 0 && *h->max_life > kr.val[current_kr].period >> 1) + *h->max_life = kr.val[current_kr].period >> 1; + + if (ret == 0 && h->pw_end[0] == 0) + /* Shouldn't happen */ + h->pw_end[0] = kr.val[current_kr].epoch + + kr.val[current_kr].period * + (1 + (t - kr.val[current_kr].epoch) / kr.val[current_kr].period); + + free_HDB_Ext_KeyRotation(&kr); + free_HDB_Ext_KeySet(&base_keys); + free(p); + return ret; } +/* + * Pick a best kvno for the given principal at the given time. + * + * Implements the [hdb] new_service_key_delay configuration parameter. + * + * In order for disparate keytab provisioning systems such as OSKT and our own + * kadmin ext_keytab and httpkadmind's get-keys to coexist, we need to be able + * to force keys set by the former to not become current keys until users of + * the latter have had a chance to fetch those keys into their keytabs. To do + * this we have to search the list of keys in the entry looking for the newest + * keys older than `now - db->new_service_key_delay'. + * + * The context is that OSKT's krb5_keytab is very happy to change keys in a way + * that requires all members of a cluster to rekey together. If one also + * wishes to have cluster members that opt out of this and just fetch current, + * past, and future keys periodically, then the keys set by OSKT must not come + * into effect until all the opt-out members have had a chance to fetch the new + * keys. + * + * The assumption is that services will fetch new keys periodically, say, every + * four hours. Then one can set `[hdb] new_service_key_delay = 8h' in the + * configuration and new keys set by OSKT will not be used until 8h after they + * are set. + * + * Naturally, this applies only to concrete principals with concrete keys. + */ +static krb5_error_code +pick_kvno(krb5_context context, + HDB *db, + unsigned flags, + krb5_timestamp now, + krb5uint32 kvno, + hdb_entry *h) +{ + HDB_extension *ext; + HDB_Ext_KeySet keys; + time_t current = 0; + time_t best; + size_t i; + + /* + * If we want a specific kvno, or if the caller doesn't want new keys + * delayed, or if there's no new-key delay configured, or we're not + * fetching for use as a service principal, then we're out. + */ + if (!(flags & HDB_F_DELAY_NEW_KEYS) || kvno || h->flags.virtual || + h->flags.virtual_keys || db->new_service_key_delay <= 0) + return 0; + + /* No history -> current keyset is the only one and therefore the best */ + ext = hdb_find_extension(h, choice_HDB_extension_data_hist_keys); + if (!ext) + return 0; + + /* Assume the current keyset is the best to start with */ + (void) hdb_entry_get_pw_change_time(h, ¤t); + if (current == 0 && h->modified_by) + current = h->modified_by->time; + if (current == 0) + current = h->created_by.time; + + /* Current keyset starts out as best */ + best = current; + kvno = h->kvno; + + /* Look for a better keyset in the history */ + keys = ext->data.u.hist_keys; + for (i = 0; i < keys.len; i++) { + /* No set_time? Ignore. Too new? Ignore */ + if (!keys.val[i].set_time || + keys.val[i].set_time[0] + db->new_service_key_delay > now) + continue; + + /* + * Ignore the keyset with kvno 1 when the entry has better kvnos + * because kadmin's `ank -r' command immediately changes the keys. + */ + if (kvno > 1 && keys.val[i].kvno == 1) + continue; + + /* + * This keyset's set_time older than the previous best? Ignore. + * However, if the current best is the entry's current and that one + * is too new, then don't ignore this one. + */ + if (keys.val[i].set_time[0] < best && + (best != current || current + db->new_service_key_delay < now)) + continue; + + /* + * If two good enough keysets have the same set_time, take the keyset + * with the highest kvno. + */ + if (keys.val[i].set_time[0] == best && keys.val[i].kvno <= kvno) + continue; + + /* + * This keyset is clearly more current than the previous best keyset + * but still old enough to use for encrypting tickets with. + */ + best = keys.val[i].set_time[0]; + kvno = keys.val[i].kvno; + } + return hdb_change_kvno(context, kvno, h); +} + +/* + * Make a WELLKNOWN/HOSTBASED-NAMESPACE/${svc}/${hostname} or + * WELLKNOWN/HOSTBASED-NAMESPACE/${svc}/${hostname}/${domainname} principal + * object, with the service and hostname components take from `wanted', but if + * the service name is not in the list `db->virtual_hostbased_princ_svcs[]' + * then use "_" (wildcard) instead. This way we can have different attributes + * for different services in the same namespaces. + * + * For example, virtual hostbased service names for the "host" service might + * have ok-as-delegate set, but ones for the "HTTP" service might not. + */ +static krb5_error_code +make_namespace_princ(krb5_context context, + HDB *db, + krb5_const_principal wanted, + krb5_principal *namespace) +{ + krb5_error_code ret = 0; + const char *realm = krb5_principal_get_realm(context, wanted); + const char *comp0 = krb5_principal_get_comp_string(context, wanted, 0); + const char *comp1 = krb5_principal_get_comp_string(context, wanted, 1); + const char *comp2 = krb5_principal_get_comp_string(context, wanted, 2); + char * const *svcs = db->virtual_hostbased_princ_svcs; + size_t i; + + *namespace = NULL; + if (comp0 == NULL || comp1 == NULL) + return EINVAL; + if (strcmp(comp0, "krbtgt") == 0) + return 0; + + for (i = 0; svcs && svcs[i]; i++) { + if (strcmp(comp0, svcs[i]) == 0) { + comp0 = svcs[i]; + break; + } + } + if (!svcs || !svcs[i]) + comp0 = "_"; + + /* First go around, need a namespace princ. Make it! */ + ret = krb5_build_principal(context, namespace, strlen(realm), + realm, KRB5_WELLKNOWN_NAME, + HDB_WK_NAMESPACE, comp0, NULL); + if (ret == 0) + ret = krb5_principal_set_comp_string(context, *namespace, 3, comp1); + if (ret == 0 && comp2) + /* Support domain-based names */ + ret = krb5_principal_set_comp_string(context, *namespace, 4, comp2); + /* Caller frees `*namespace' on error */ + return ret; +} + +static int +is_namespace_princ_p(krb5_context context, + krb5_const_principal princ) +{ + return + krb5_principal_get_num_comp(context, princ) >= 4 + && strcmp(krb5_principal_get_comp_string(context, princ, 0), + KRB5_WELLKNOWN_NAME) == 0 + && strcmp(krb5_principal_get_comp_string(context, princ, 1), + HDB_WK_NAMESPACE) == 0; +} + +/* See call site */ +static krb5_error_code +rewrite_hostname(krb5_context context, + krb5_const_principal wanted_princ, + krb5_const_principal ns_princ, + krb5_const_principal found_ns_princ, + char **s) +{ + const char *ns_host_part, *wanted_host_part, *found_host_part; + const char *p, *r; + size_t ns_host_part_len, wanted_host_part_len; + + wanted_host_part = krb5_principal_get_comp_string(context, wanted_princ, 1); + wanted_host_part_len = strlen(wanted_host_part); + if (wanted_host_part_len > 256) { + krb5_set_error_message(context, HDB_ERR_NOENTRY, + "Aliases of host-based principals longer than " + "256 bytes not supported"); + return HDB_ERR_NOENTRY; + } + + ns_host_part = krb5_principal_get_comp_string(context, ns_princ, 3); + ns_host_part_len = strlen(ns_host_part); + + /* Find `ns_host_part' as the tail of `wanted_host_part' */ + for (r = p = strstr(wanted_host_part, ns_host_part); + r && strnlen(r, ns_host_part_len + 1) > ns_host_part_len; + p = (r = strstr(r, ns_host_part)) ? r : p) + ; + if (!p || strnlen(p, ns_host_part_len + 1) != ns_host_part_len) + return HDB_ERR_NOENTRY; /* Can't happen */ + if (p == wanted_host_part || p[-1] != '.') + return HDB_ERR_NOENTRY; + + found_host_part = + krb5_principal_get_comp_string(context, found_ns_princ, 3); + return + asprintf(s, "%.*s%s", (int)(p - wanted_host_part), wanted_host_part, + found_host_part) < 0 || + *s == NULL ? krb5_enomem(context) : 0; +} + +/* + * Fix `h->principal' to match the desired `princ' in the namespace + * `nsprinc' (which is either the same as `h->principal' or an alias + * of it). + */ +static krb5_error_code +fix_princ_name(krb5_context context, + krb5_const_principal princ, + krb5_const_principal nsprinc, + hdb_entry *h) +{ + krb5_error_code ret = 0; + char *s = NULL; + + if (!nsprinc) + return 0; + if (krb5_principal_get_num_comp(context, princ) < 2) + return HDB_ERR_NOENTRY; + + /* `nsprinc' must be a namespace principal */ + + if (krb5_principal_compare(context, nsprinc, h->principal)) { + /* + * `h' is the HDB entry for `nsprinc', and `nsprinc' is its canonical + * name. + * + * Set the entry's principal name to the desired name. The keys will + * be fixed next (upstairs, but don't forget to!). + */ + free_Principal(h->principal); + return copy_Principal(princ, h->principal); + } + + if (!is_namespace_princ_p(context, h->principal)) { + /* + * The alias is a namespace, but the canonical name is not. WAT. + * + * Well, the KDC will just issue a referral anyways, so we can leave + * `h->principal' as is... + * + * Remove all of `h's keys just in case, and leave + * `h->principal' as-is. + */ + free_Keys(&h->keys); + (void) hdb_entry_clear_password(context, h); + return hdb_clear_extension(context, h, + choice_HDB_extension_data_hist_keys); + } + + /* + * A namespace alias of a namespace entry. + * + * We'll want to rewrite the original principal accordingly. + * + * E.g., if the caller wanted host/foo.ns.test.h5l.se and we + * found WELLKNOWN/HOSTBASED-NAMESPACE/ns.test.h5l.se is an + * alias of WELLKNOWN/HOSTBASED-NAMESPACE/ns.example.org, then + * we'll want to treat host/foo.ns.test.h5l.se as an alias of + * host/foo.ns.example.org. + */ + if (krb5_principal_get_num_comp(context, h->principal) != + 2 + krb5_principal_get_num_comp(context, princ)) + ret = HDB_ERR_NOENTRY; /* Only host-based services for now */ + if (ret == 0) + ret = rewrite_hostname(context, princ, nsprinc, h->principal, &s); + if (ret == 0) { + krb5_free_principal(context, h->principal); + h->principal = NULL; + ret = krb5_make_principal(context, &h->principal, + krb5_principal_get_realm(context, princ), + krb5_principal_get_comp_string(context, + princ, 0), + s, + NULL); + } + free(s); + return ret; +} + +/* Wrapper around db->hdb_fetch_kvno() that implements virtual princs/keys */ +static krb5_error_code +fetch_it(krb5_context context, + HDB *db, + krb5_const_principal princ, + unsigned flags, + krb5_timestamp t, + krb5int32 etype, + krb5uint32 kvno, + hdb_entry *ent) +{ + krb5_const_principal tmpprinc = princ; + krb5_principal nsprinc = NULL; + krb5_error_code ret = 0; + const char *comp0 = krb5_principal_get_comp_string(context, princ, 0); + const char *comp1 = krb5_principal_get_comp_string(context, princ, 1); + const char *tmp; + size_t mindots = db->virtual_hostbased_princ_ndots; + size_t maxdots = db->virtual_hostbased_princ_maxdots; + size_t hdots = 0; + char *host = NULL; + int do_search = 0; + + if (!db->enable_virtual_hostbased_princs) + maxdots = mindots = 0; + if (db->enable_virtual_hostbased_princs && comp1 && + strcmp("krbtgt", comp0) != 0 && strcmp(KRB5_WELLKNOWN_NAME, comp0) != 0) { + char *htmp; + + if ((host = strdup(comp1)) == NULL) + return krb5_enomem(context); + + /* Strip out any :port */ + htmp = strchr(host, ':'); + if (htmp) { + if (strchr(htmp + 1, ':')) { + /* Extra ':'s? No virtualization for you! */ + free(host); + host = NULL; + htmp = NULL; + } else { + *htmp = '\0'; + } + } + /* Count dots in `host' */ + for (hdots = 0, htmp = host; htmp && *htmp; htmp++) + if (*htmp == '.') + hdots++; + + do_search = 1; + } + + tmp = host ? host : comp1; + for (ret = HDB_ERR_NOENTRY; ret == HDB_ERR_NOENTRY; tmpprinc = nsprinc) { + krb5_error_code ret2 = 0; + + /* + * We break out of this loop with ret == 0 only if we found the HDB + * entry we were looking for or the HDB entry for a matching namespace. + * + * Otherwise we break out with ret != 0, typically HDB_ERR_NOENTRY. + * + * First time through we lookup the principal as given. + * + * Next we lookup a namespace principal, stripping off hostname labels + * from the left until we find one or get tired of looking or run out + * of labels. + */ + ret = db->hdb_fetch_kvno(context, db, tmpprinc, flags, kvno, ent); + if (ret == 0 && nsprinc && ent->flags.invalid) { + free_HDB_entry(ent); + ret = HDB_ERR_NOENTRY; + } + if (ret != HDB_ERR_NOENTRY || hdots == 0 || hdots < mindots || !tmp || + !do_search) + break; + + /* + * Breadcrumb: + * + * - if we found a concrete principal, but it's been marked + * as now-virtual, then we must keep going + * + * But this will be coded in the future. + * + * Maybe we can take attributes from the concrete principal... + */ + + /* + * The namespace's hostname will not have more labels than maxdots + 1. + * Thus we truncate immediately down to maxdots + 1 if we haven't yet. + * + * Example: with maxdots == 3, + * foo.bar.baz.app.blah.example -> baz.app.blah.example + */ + while (maxdots && hdots > maxdots && tmp) { + tmp = strchr(tmp, '.'); + /* tmp != NULL because maxdots > 0; we check to quiet linters */ + if (tmp == NULL) { + ret = HDB_ERR_NOENTRY; + goto out; + } + tmp++; + hdots--; + } + + if (nsprinc == NULL) + /* First go around, need a namespace princ. Make it! */ + ret2 = make_namespace_princ(context, db, tmpprinc, &nsprinc); + + /* Update the hostname component of the namespace principal */ + if (ret2 == 0) + ret2 = krb5_principal_set_comp_string(context, nsprinc, 3, tmp); + if (ret2) + ret = ret2; + + if (tmp) { + /* Strip off left-most label for the next go-around */ + if ((tmp = strchr(tmp, '.'))) + tmp++; + hdots--; + } /* else we'll break out after the next db->hdb_fetch_kvno() call */ + } + + /* + * If unencrypted keys were requested, derive them. There may not be any + * key derivation to do, but that's decided in derive_keys(). + */ + if (ret == 0 || ret == HDB_ERR_WRONG_REALM) { + krb5_error_code save_ret = ret; + + /* Fix the principal name if namespaced */ + ret = fix_princ_name(context, princ, nsprinc, ent); + + /* Derive keys if namespaced or virtual */ + if (ret == 0) + ret = derive_keys(context, flags, princ, !!nsprinc, t, etype, kvno, + ent); + /* Pick the best kvno for this principal at the given time */ + if (ret == 0) + ret = pick_kvno(context, db, flags, t, kvno, ent); + if (ret == 0) + ret = save_ret; + } + +out: + if (ret != 0 && ret != HDB_ERR_WRONG_REALM) + hdb_free_entry(context, db, ent); + krb5_free_principal(context, nsprinc); + free(host); + return ret; +} + +/** + * Fetch a principal's HDB entry, possibly generating virtual keys from base + * keys according to strict key rotation schedules. If a time is given, other + * than HDB I/O, this function is pure, thus usable for testing. + * + * HDB writers should use `db->hdb_fetch_kvno()' to avoid materializing virtual + * principals. + * + * HDB readers should use this function rather than `db->hdb_fetch_kvno()' + * unless they only want to see concrete principals and not bother generating + * any virtual keys. + * + * @param context Context + * @param db HDB + * @param principal Principal name + * @param flags Fetch flags + * @param t For virtual keys, use this as the point in time (use zero to mean "now") + * @param etype Key enctype (use KRB5_ENCTYPE_NULL to mean "preferred") + * @param kvno Key version number (use zero to mean "current") + * @param h Output HDB entry + * + * @return Zero or HDB_ERR_WRONG_REALM on success, an error code otherwise. + */ +krb5_error_code +hdb_fetch_kvno(krb5_context context, + HDB *db, + krb5_const_principal principal, + unsigned int flags, + krb5_timestamp t, + krb5int32 etype, + krb5uint32 kvno, + hdb_entry *h) +{ + krb5_error_code ret; + krb5_timestamp now; + + krb5_timeofday(context, &now); + + flags |= kvno ? HDB_F_KVNO_SPECIFIED : 0; /* XXX is this needed */ + ret = fetch_it(context, db, principal, flags, t ? t : now, etype, kvno, h); + if (ret == 0 && t == 0 && h->flags.virtual && + h->pw_end && h->pw_end[0] < now) { + /* + * This shouldn't happen! + * + * Do not allow h->pw_end[0] to be in the past for virtual principals + * outside testing. This is just to prevent the AS/TGS from failing. + */ + h->pw_end[0] = now + 3600; + } + if (ret == HDB_ERR_NOENTRY) + krb5_set_error_message(context, ret, "no such entry found in hdb"); + return ret; +} + +size_t ASN1CALL +length_hdb_keyset(HDB_keyset *data) +{ + return length_HDB_keyset(data); +} + +size_t ASN1CALL +length_hdb_entry(HDB_entry *data) +{ + return length_HDB_entry(data); +} + +size_t ASN1CALL +length_hdb_entry_alias(HDB_entry_alias *data) +{ + return length_HDB_entry_alias(data); +} + +void ASN1CALL +free_hdb_keyset(HDB_keyset *data) +{ + free_HDB_keyset(data); +} + +void ASN1CALL +free_hdb_entry(HDB_entry *data) +{ + free_HDB_entry(data); +} + +void ASN1CALL +free_hdb_entry_alias(HDB_entry_alias *data) +{ + free_HDB_entry_alias(data); +} + +size_t ASN1CALL +copy_hdb_keyset(const HDB_keyset *from, HDB_keyset *to) +{ + return copy_HDB_keyset(from, to); +} + +size_t ASN1CALL +copy_hdb_entry(const HDB_entry *from, HDB_entry *to) +{ + return copy_HDB_entry(from, to); +} + +size_t ASN1CALL +copy_hdb_entry_alias(const HDB_entry_alias *from, HDB_entry_alias *to) +{ + return copy_HDB_entry_alias(from, to); +} + +int ASN1CALL +decode_hdb_keyset(const unsigned char *p, + size_t len, + HDB_keyset *data, + size_t *size) +{ + return decode_HDB_keyset(p, len, data, size); +} + +int ASN1CALL +decode_hdb_entry(const unsigned char *p, + size_t len, + HDB_entry *data, + size_t *size) +{ + return decode_HDB_entry(p, len, data, size); +} + +int ASN1CALL +decode_hdb_entry_alias(const unsigned char *p, + size_t len, + HDB_entry_alias *data, + size_t *size) +{ + return decode_HDB_entry_alias(p, len, data, size); +} + +int ASN1CALL +encode_hdb_keyset(unsigned char *p, + size_t len, + const HDB_keyset *data, + size_t *size) +{ + return encode_HDB_keyset(p, len, data, size); +} + +int ASN1CALL +encode_hdb_entry(unsigned char *p, + size_t len, + const HDB_entry *data, + size_t *size) +{ + return encode_HDB_entry(p, len, data, size); +} + +int ASN1CALL +encode_hdb_entry_alias(unsigned char *p, + size_t len, + const HDB_entry_alias *data, + size_t *size) +{ + return encode_HDB_entry_alias(p, len, data, size); +} diff --git a/lib/hdb/db.c b/lib/hdb/db.c index 4cee8d0097a1..5fcce7b8e8b3 100644 --- a/lib/hdb/db.c +++ b/lib/hdb/db.c @@ -71,7 +71,8 @@ DB_destroy(krb5_context context, HDB *db) { krb5_error_code ret; - ret = hdb_clear_master_key (context, db); + ret = hdb_clear_master_key(context, db); + krb5_config_free_strings(db->virtual_hostbased_princ_svcs); free(db->hdb_name); free(db); return ret; @@ -113,7 +114,7 @@ DB_unlock(krb5_context context, HDB *db) static krb5_error_code DB_seq(krb5_context context, HDB *db, - unsigned flags, hdb_entry_ex *entry, int flag) + unsigned flags, hdb_entry *entry, int flag) { DB *d = (DB*)db->hdb_db; DBT key, value; @@ -137,21 +138,21 @@ DB_seq(krb5_context context, HDB *db, data.data = value.data; data.length = value.size; memset(entry, 0, sizeof(*entry)); - if (hdb_value2entry(context, &data, &entry->entry)) + if (hdb_value2entry(context, &data, entry)) return DB_seq(context, db, flags, entry, R_NEXT); if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { - code = hdb_unseal_keys (context, db, &entry->entry); + code = hdb_unseal_keys (context, db, entry); if (code) - hdb_free_entry (context, entry); + hdb_free_entry (context, db, entry); } - if (code == 0 && entry->entry.principal == NULL) { - entry->entry.principal = malloc(sizeof(*entry->entry.principal)); - if (entry->entry.principal == NULL) { + if (code == 0 && entry->principal == NULL) { + entry->principal = malloc(sizeof(*entry->principal)); + if (entry->principal == NULL) { code = ENOMEM; krb5_set_error_message(context, code, "malloc: out of memory"); - hdb_free_entry (context, entry); + hdb_free_entry (context, db, entry); } else { - hdb_key2principal(context, &key_data, entry->entry.principal); + hdb_key2principal(context, &key_data, entry->principal); } } return code; @@ -159,14 +160,14 @@ DB_seq(krb5_context context, HDB *db, static krb5_error_code -DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) { return DB_seq(context, db, flags, entry, R_FIRST); } static krb5_error_code -DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) { return DB_seq(context, db, flags, entry, R_NEXT); } diff --git a/lib/hdb/db3.c b/lib/hdb/db3.c index 0d41369d7101..9d0c0a97d9ab 100644 --- a/lib/hdb/db3.c +++ b/lib/hdb/db3.c @@ -86,7 +86,8 @@ DB_destroy(krb5_context context, HDB *db) { krb5_error_code ret; - ret = hdb_clear_master_key (context, db); + ret = hdb_clear_master_key(context, db); + krb5_config_free_strings(db->virtual_hostbased_princ_svcs); free(db->hdb_name); free(db); return ret; @@ -135,7 +136,7 @@ DB_unlock(krb5_context context, HDB *db) static krb5_error_code DB_seq(krb5_context context, HDB *db, - unsigned flags, hdb_entry_ex *entry, int flag) + unsigned flags, hdb_entry *entry, int flag) { DBT key, value; DBC *dbcp = db->hdb_dbc; @@ -155,21 +156,21 @@ DB_seq(krb5_context context, HDB *db, data.data = value.data; data.length = value.size; memset(entry, 0, sizeof(*entry)); - if (hdb_value2entry(context, &data, &entry->entry)) + if (hdb_value2entry(context, &data, entry)) return DB_seq(context, db, flags, entry, DB_NEXT); if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { - code = hdb_unseal_keys (context, db, &entry->entry); + code = hdb_unseal_keys (context, db, entry); if (code) - hdb_free_entry (context, entry); + hdb_free_entry (context, db, entry); } - if (entry->entry.principal == NULL) { - entry->entry.principal = malloc(sizeof(*entry->entry.principal)); - if (entry->entry.principal == NULL) { - hdb_free_entry (context, entry); + if (entry->principal == NULL) { + entry->principal = malloc(sizeof(*entry->principal)); + if (entry->principal == NULL) { + hdb_free_entry (context, db, entry); krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } else { - hdb_key2principal(context, &key_data, entry->entry.principal); + hdb_key2principal(context, &key_data, entry->principal); } } return 0; @@ -177,14 +178,14 @@ DB_seq(krb5_context context, HDB *db, static krb5_error_code -DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) { return DB_seq(context, db, flags, entry, DB_FIRST); } static krb5_error_code -DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) { return DB_seq(context, db, flags, entry, DB_NEXT); } diff --git a/lib/hdb/dbinfo.c b/lib/hdb/dbinfo.c index e2890255b2ef..60b11f8cd663 100644 --- a/lib/hdb/dbinfo.c +++ b/lib/hdb/dbinfo.c @@ -271,5 +271,21 @@ hdb_db_dir(krb5_context context) const char * hdb_default_db(krb5_context context) { - return HDB_DEFAULT_DB; + static char *default_hdb = NULL; + struct hdb_dbinfo *dbinfo = NULL; + struct hdb_dbinfo *d = NULL; + const char *s; + + if (default_hdb) + return default_hdb; + + (void) hdb_get_dbinfo(context, &dbinfo); + while ((d = hdb_dbinfo_get_next(dbinfo, d)) != NULL) { + if ((s = hdb_dbinfo_get_dbname(context, d)) && + (default_hdb = strdup(s))) + break; + } + + hdb_free_dbinfo(context, &dbinfo); + return default_hdb ? default_hdb : HDB_DEFAULT_DB; } diff --git a/lib/hdb/ext.c b/lib/hdb/ext.c index ecefe931b74f..48683ef1607f 100644 --- a/lib/hdb/ext.c +++ b/lib/hdb/ext.c @@ -86,7 +86,6 @@ hdb_replace_extension(krb5_context context, const HDB_extension *ext) { HDB_extension *ext2; - HDB_extension *es; int ret; ext2 = NULL; @@ -157,22 +156,7 @@ hdb_replace_extension(krb5_context context, return ret; } - es = realloc(entry->extensions->val, - (entry->extensions->len+1)*sizeof(entry->extensions->val[0])); - if (es == NULL) { - krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); - return ENOMEM; - } - entry->extensions->val = es; - - ret = copy_HDB_extension(ext, - &entry->extensions->val[entry->extensions->len]); - if (ret == 0) - entry->extensions->len++; - else - krb5_set_error_message(context, ret, "hdb: failed to copy new extension"); - - return ret; + return add_HDB_extensions(entry->extensions, ext); } krb5_error_code @@ -185,14 +169,11 @@ hdb_clear_extension(krb5_context context, if (entry->extensions == NULL) return 0; - for (i = 0; i < entry->extensions->len; i++) { - if (entry->extensions->val[i].data.element == (unsigned)type) { - free_HDB_extension(&entry->extensions->val[i]); - memmove(&entry->extensions->val[i], - &entry->extensions->val[i + 1], - sizeof(entry->extensions->val[i]) * (entry->extensions->len - i - 1)); - entry->extensions->len--; - } + for (i = 0; i < entry->extensions->len; ) { + if (entry->extensions->val[i].data.element == (unsigned)type) + (void) remove_HDB_extensions(entry->extensions, i); + else + i++; } if (entry->extensions->len == 0) { free(entry->extensions->val); @@ -247,6 +228,33 @@ hdb_entry_get_pkinit_cert(const hdb_entry *entry, const HDB_Ext_PKINIT_cert **a) } krb5_error_code +hdb_entry_get_krb5_config(const hdb_entry *entry, heim_octet_string *c) +{ + const HDB_extension *ext; + + c->data = NULL; + c->length = 0; + ext = hdb_find_extension(entry, choice_HDB_extension_data_krb5_config); + if (ext) + *c = ext->data.u.krb5_config; + return 0; +} + +krb5_error_code +hdb_entry_set_krb5_config(krb5_context context, + hdb_entry *entry, + heim_octet_string *s) +{ + HDB_extension ext; + + ext.mandatory = FALSE; + ext.data.element = choice_HDB_extension_data_last_pw_change; + /* hdb_replace_extension() copies this, so no need to copy it here */ + ext.data.u.krb5_config = *s; + return hdb_replace_extension(context, entry, &ext); +} + +krb5_error_code hdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t) { const HDB_extension *ext; @@ -530,3 +538,249 @@ hdb_set_last_modified_by(krb5_context context, hdb_entry *entry, return 0; } +krb5_error_code +hdb_entry_get_key_rotation(krb5_context context, + const hdb_entry *entry, + const HDB_Ext_KeyRotation **kr) +{ + HDB_extension *ext = + hdb_find_extension(entry, choice_HDB_extension_data_key_rotation); + + *kr = ext ? &ext->data.u.key_rotation : NULL; + return 0; +} + +krb5_error_code +hdb_validate_key_rotation(krb5_context context, + const KeyRotation *past_kr, + const KeyRotation *new_kr) +{ + unsigned int last_kvno; + + if (new_kr->period < 1) { + krb5_set_error_message(context, EINVAL, + "Key rotation periods must be non-zero " + "and positive"); + return EINVAL; + } + if (new_kr->base_key_kvno < 1 || new_kr->base_kvno < 1) { + krb5_set_error_message(context, EINVAL, + "Key version number zero not allowed " + "for key rotation"); + return EINVAL; + } + if (!past_kr) + return 0; + + if (past_kr->base_key_kvno == new_kr->base_key_kvno) { + /* + * The new base keys can be the same as the old, but must have + * different kvnos. (Well, not must must. It's a convention for now.) + */ + krb5_set_error_message(context, EINVAL, + "Base key version numbers for KRs must differ"); + return EINVAL; + } + if (new_kr->epoch - past_kr->epoch <= 0) { + krb5_set_error_message(context, EINVAL, + "New key rotation periods must start later " + "than existing ones"); + return EINVAL; + } + + last_kvno = 1 + ((new_kr->epoch - past_kr->epoch) / past_kr->period); + if (new_kr->base_kvno <= last_kvno) { + krb5_set_error_message(context, EINVAL, + "New key rotation base kvno must be larger " + "the last kvno for the current key " + "rotation (%u)", last_kvno); + return EINVAL; + } + return 0; +} + +static int +kr_eq(const KeyRotation *a, const KeyRotation *b) +{ + return !!( + a->epoch == b->epoch && + a->period == b->period && + a->base_kvno == b->base_kvno && + a->base_key_kvno == b->base_key_kvno && + KeyRotationFlags2int(a->flags) == KeyRotationFlags2int(b->flags) + ); +} + +krb5_error_code +hdb_validate_key_rotations(krb5_context context, + const HDB_Ext_KeyRotation *existing, + const HDB_Ext_KeyRotation *krs) +{ + krb5_error_code ret = 0; + size_t added = 0; + size_t i; + + if ((!existing || !existing->len) && (!krs || !krs->len)) + return 0; /* Nothing to do; weird */ + + /* + * HDB_Ext_KeyRotation has to have 1..3 elements, and this is enforced by + * the ASN.1 compiler and the code it generates. Nonetheless we'll check + * that there's not zero elements. + */ + if ((!krs || !krs->len)) { + /* + * NOTE: We can clear this on concrete principals with virtual keys + * though. The caller can check for that case. + */ + krb5_set_error_message(context, EINVAL, + "Cannot clear key rotation metadata on " + "virtual principal namespaces"); + ret = EINVAL; + } + + /* Validate the new KRs by themselves */ + for (i = 0; ret == 0 && i < krs->len; i++) { + ret = hdb_validate_key_rotation(context, + i+1 < krs->len ? &krs->val[i+1] : 0, + &krs->val[i]); + } + if (ret || !existing || !existing->len) + return ret; + + if (existing->len == krs->len) { + /* Check for no change */ + for (i = 0; i < krs->len; i++) + if (!kr_eq(&existing->val[i], &krs->val[i])) + break; + if (i == krs->len) + return 0; /* No change */ + } + + /* + * Check that new KRs make sense in the context of the previous KRs. + * + * Permitted changes: + * + * - add one new KR in front + * - drop old KRs + * + * Start by checking if we're adding a KR, then go on to check for dropped + * KRs and/or last KR alteration. + */ + if (existing->val[0].epoch == krs->val[0].epoch || + existing->val[0].base_kvno == krs->val[0].base_kvno) { + if (!kr_eq(&existing->val[0], &krs->val[0])) { + krb5_set_error_message(context, EINVAL, + "Key rotation change not sensible"); + ret = EINVAL; + } + /* Key rotation *not* added */ + } else { + /* Key rotation added; check it first */ + ret = hdb_validate_key_rotation(context, + &existing->val[0], + &krs->val[0]); + added = 1; + } + for (i = 0; ret == 0 && i < existing->len && i + added < krs->len; i++) + if (!kr_eq(&existing->val[i], &krs->val[i + added])) + krb5_set_error_message(context, ret = EINVAL, + "Only last key rotation may be truncated"); + return ret; +} + +/* XXX We need a function to "revoke" the past */ + +/** + * This function adds a KeyRotation value to an entry, validating the + * change. One of `entry' and `krs' must be NULL, and the other non-NULL, and + * whichever is given will be altered. + * + * @param context Context + * @param entry An HDB entry + * @param krs A key rotation extension for hdb_entry + * @param kr A new KeyRotation value + * + * @return Zero on success, an error otherwise. + */ +krb5_error_code +hdb_entry_add_key_rotation(krb5_context context, + hdb_entry *entry, + HDB_Ext_KeyRotation *krs, + const KeyRotation *kr) +{ + krb5_error_code ret; + HDB_extension new_ext; + HDB_extension *ext = &new_ext; + KeyRotation tmp; + size_t i, sz; + + if (kr->period < 1) { + krb5_set_error_message(context, EINVAL, + "Key rotation period cannot be zero"); + return EINVAL; + } + + new_ext.mandatory = TRUE; + new_ext.data.element = choice_HDB_extension_data_key_rotation; + new_ext.data.u.key_rotation.len = 0; + new_ext.data.u.key_rotation.val = 0; + + if (entry && krs) + return EINVAL; + + if (entry) { + ext = hdb_find_extension(entry, choice_HDB_extension_data_key_rotation); + if (!ext) + ext = &new_ext; + } else { + const KeyRotation *prev_kr = &krs->val[0]; + unsigned int last_kvno = 0; + + if (kr->epoch - prev_kr->epoch <= 0) { + krb5_set_error_message(context, EINVAL, + "New key rotation periods must start later " + "than existing ones"); + return EINVAL; + } + + if (kr->base_kvno <= prev_kr->base_kvno || + kr->base_kvno - prev_kr->base_kvno <= + (last_kvno = 1 + + ((kr->epoch - prev_kr->epoch) / prev_kr->period))) { + krb5_set_error_message(context, EINVAL, + "New key rotation base kvno must be larger " + "the last kvno for the current key " + "rotation (%u)", last_kvno); + return EINVAL; + } + } + + /* First, append */ + ret = add_HDB_Ext_KeyRotation(&ext->data.u.key_rotation, kr); + if (ret) + return ret; + + /* Rotate new to front */ + tmp = ext->data.u.key_rotation.val[ext->data.u.key_rotation.len - 1]; + sz = sizeof(ext->data.u.key_rotation.val[0]); + memmove(&ext->data.u.key_rotation.val[1], &ext->data.u.key_rotation.val[0], + (ext->data.u.key_rotation.len - 1) * sz); + ext->data.u.key_rotation.val[0] = tmp; + + /* Drop too old entries */ + for (i = 3; i < ext->data.u.key_rotation.len; i++) + free_KeyRotation(&ext->data.u.key_rotation.val[i]); + ext->data.u.key_rotation.len = + ext->data.u.key_rotation.len > 3 ? 3 : ext->data.u.key_rotation.len; + + if (ext != &new_ext) + return 0; + + /* Install new extension */ + if (ret == 0 && entry) + ret = hdb_replace_extension(context, entry, ext); + free_HDB_extension(&new_ext); + return ret; +} diff --git a/lib/hdb/hdb-keytab.c b/lib/hdb/hdb-keytab.c index ab2afb5d74ba..c9b469cb1a85 100644 --- a/lib/hdb/hdb-keytab.c +++ b/lib/hdb/hdb-keytab.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Kungliga Tekniska Högskolan + * Copyright (c) 2009 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -65,7 +65,8 @@ hkt_destroy(krb5_context context, HDB *db) hdb_keytab k = (hdb_keytab)db->hdb_db; krb5_error_code ret; - ret = hdb_clear_master_key (context, db); + ret = hdb_clear_master_key(context, db); + krb5_config_free_strings(db->virtual_hostbased_princ_svcs); free(k->path); free(k); @@ -89,14 +90,14 @@ hkt_unlock(krb5_context context, HDB *db) static krb5_error_code hkt_firstkey(krb5_context context, HDB *db, - unsigned flags, hdb_entry_ex *entry) + unsigned flags, hdb_entry *entry) { return HDB_ERR_DB_INUSE; } static krb5_error_code hkt_nextkey(krb5_context context, HDB * db, unsigned flags, - hdb_entry_ex * entry) + hdb_entry * entry) { return HDB_ERR_DB_INUSE; } @@ -118,7 +119,7 @@ hkt_open(krb5_context context, HDB * db, int flags, mode_t mode) static krb5_error_code hkt_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal, - unsigned flags, krb5_kvno kvno, hdb_entry_ex * entry) + unsigned flags, krb5_kvno kvno, hdb_entry * entry) { hdb_keytab k = (hdb_keytab)db->hdb_db; krb5_error_code ret; @@ -131,13 +132,13 @@ hkt_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal, memset(&ktentry, 0, sizeof(ktentry)); - entry->entry.flags.server = 1; - entry->entry.flags.forwardable = 1; - entry->entry.flags.renewable = 1; + entry->flags.server = 1; + entry->flags.forwardable = 1; + entry->flags.renewable = 1; /* Not recorded in the OD backend, make something up */ ret = krb5_parse_name(context, "hdb/keytab@WELL-KNOWN:KEYTAB-BACKEND", - &entry->entry.created_by.principal); + &entry->created_by.principal); if (ret) goto out; @@ -154,7 +155,7 @@ hkt_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal, goto out; } - ret = krb5_copy_principal(context, principal, &entry->entry.principal); + ret = krb5_copy_principal(context, principal, &entry->principal); if (ret) goto out; @@ -162,8 +163,8 @@ hkt_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal, out: if (ret) { - free_hdb_entry(&entry->entry); - memset(&entry->entry, 0, sizeof(entry->entry)); + free_HDB_entry(entry); + memset(entry, 0, sizeof(*entry)); } krb5_kt_free_entry(context, &ktentry); @@ -172,7 +173,7 @@ hkt_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal, static krb5_error_code hkt_store(krb5_context context, HDB * db, unsigned flags, - hdb_entry_ex * entry) + hdb_entry * entry) { return HDB_ERR_DB_INUSE; } diff --git a/lib/hdb/hdb-ldap.c b/lib/hdb/hdb-ldap.c index 2ed7a6ba2596..5cd097f5b6bc 100644 --- a/lib/hdb/hdb-ldap.c +++ b/lib/hdb/hdb-ldap.c @@ -47,7 +47,7 @@ static krb5_error_code LDAP_close(krb5_context context, HDB *); static krb5_error_code LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, - int flags, hdb_entry_ex * ent); + int flags, hdb_entry * ent); static const char *default_structural_object = "account"; static char *structural_object; @@ -388,14 +388,14 @@ bervalstrcmp(struct berval *v, const char *str) static krb5_error_code -LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, - LDAPMessage * msg, LDAPMod *** pmods) +LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry * ent, + LDAPMessage * msg, LDAPMod *** pmods, krb5_boolean *pis_new_entry) { krb5_error_code ret; - krb5_boolean is_new_entry; + krb5_boolean is_new_entry = FALSE; char *tmp = NULL; LDAPMod **mods = NULL; - hdb_entry_ex orig; + hdb_entry orig; unsigned long oflags, nflags; int i; @@ -414,8 +414,6 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, if (ret) goto out; - is_new_entry = FALSE; - vals = ldap_get_values_len(HDB2LDAP(db), msg, "objectClass"); if (vals) { int num_objectclasses = ldap_count_values_len(vals); @@ -454,12 +452,12 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, * orig being intiialized to zero */ memset(&orig, 0, sizeof(orig)); - ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top"); - if (ret) - goto out; - /* account is the structural object class */ if (is_account == FALSE) { + ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top"); + if (ret) + goto out; + ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", structural_object); is_account = TRUE; @@ -479,12 +477,12 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, } if (is_new_entry || - krb5_principal_compare(context, ent->entry.principal, orig.entry.principal) + krb5_principal_compare(context, ent->principal, orig.principal) == FALSE) { if (is_heimdal_principal || is_heimdal_entry) { - ret = krb5_unparse_name(context, ent->entry.principal, &tmp); + ret = krb5_unparse_name(context, ent->principal, &tmp); if (ret) goto out; @@ -498,7 +496,7 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, } if (is_account || is_samba_account) { - ret = krb5_unparse_name_short(context, ent->entry.principal, &tmp); + ret = krb5_unparse_name_short(context, ent->principal, &tmp); if (ret) goto out; ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "uid", tmp); @@ -510,15 +508,15 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, } } - if (is_heimdal_entry && (ent->entry.kvno != orig.entry.kvno || is_new_entry)) { + if (is_heimdal_entry && (ent->kvno != orig.kvno || is_new_entry)) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "krb5KeyVersionNumber", - ent->entry.kvno); + ent->kvno); if (ret) goto out; } - if (is_heimdal_entry && ent->entry.extensions) { + if (is_heimdal_entry && ent->extensions) { if (!is_new_entry) { vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5ExtendedAttributes"); if (vals) { @@ -529,11 +527,11 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, } } - for (i = 0; i < ent->entry.extensions->len; i++) { + for (i = 0; i < ent->extensions->len; i++) { unsigned char *buf; size_t size, sz = 0; - ASN1_MALLOC_ENCODE(HDB_extension, buf, size, &ent->entry.extensions->val[i], &sz, ret); + ASN1_MALLOC_ENCODE(HDB_extension, buf, size, &ent->extensions->val[i], &sz, ret); if (ret) goto out; if (size != sz) @@ -545,42 +543,42 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, } } - if (is_heimdal_entry && ent->entry.valid_start) { - if (orig.entry.valid_end == NULL - || (*(ent->entry.valid_start) != *(orig.entry.valid_start))) { + if (is_heimdal_entry && ent->valid_start) { + if (orig.valid_end == NULL + || (*(ent->valid_start) != *(orig.valid_start))) { ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, "krb5ValidStart", - ent->entry.valid_start); + ent->valid_start); if (ret) goto out; } } - if (ent->entry.valid_end) { - if (orig.entry.valid_end == NULL || (*(ent->entry.valid_end) != *(orig.entry.valid_end))) { + if (ent->valid_end) { + if (orig.valid_end == NULL || (*(ent->valid_end) != *(orig.valid_end))) { if (is_heimdal_entry) { ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, "krb5ValidEnd", - ent->entry.valid_end); + ent->valid_end); if (ret) goto out; } if (is_samba_account) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "sambaKickoffTime", - *(ent->entry.valid_end)); + *(ent->valid_end)); if (ret) goto out; } } } - if (ent->entry.pw_end) { - if (orig.entry.pw_end == NULL || (*(ent->entry.pw_end) != *(orig.entry.pw_end))) { + if (ent->pw_end) { + if (orig.pw_end == NULL || (*(ent->pw_end) != *(orig.pw_end))) { if (is_heimdal_entry) { ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, "krb5PasswordEnd", - ent->entry.pw_end); + ent->pw_end); if (ret) goto out; } @@ -588,7 +586,7 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, if (is_samba_account) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "sambaPwdMustChange", - *(ent->entry.pw_end)); + *(ent->pw_end)); if (ret) goto out; } @@ -597,43 +595,43 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, #if 0 /* we we have last_pw_change */ - if (is_samba_account && ent->entry.last_pw_change) { - if (orig.entry.last_pw_change == NULL || (*(ent->entry.last_pw_change) != *(orig.entry.last_pw_change))) { + if (is_samba_account && ent->last_pw_change) { + if (orig.last_pw_change == NULL || (*(ent->last_pw_change) != *(orig.last_pw_change))) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "sambaPwdLastSet", - *(ent->entry.last_pw_change)); + *(ent->last_pw_change)); if (ret) goto out; } } #endif - if (is_heimdal_entry && ent->entry.max_life) { - if (orig.entry.max_life == NULL - || (*(ent->entry.max_life) != *(orig.entry.max_life))) { + if (is_heimdal_entry && ent->max_life) { + if (orig.max_life == NULL + || (*(ent->max_life) != *(orig.max_life))) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "krb5MaxLife", - *(ent->entry.max_life)); + *(ent->max_life)); if (ret) goto out; } } - if (is_heimdal_entry && ent->entry.max_renew) { - if (orig.entry.max_renew == NULL - || (*(ent->entry.max_renew) != *(orig.entry.max_renew))) { + if (is_heimdal_entry && ent->max_renew) { + if (orig.max_renew == NULL + || (*(ent->max_renew) != *(orig.max_renew))) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "krb5MaxRenew", - *(ent->entry.max_renew)); + *(ent->max_renew)); if (ret) goto out; } } - oflags = HDBFlags2int(orig.entry.flags); - nflags = HDBFlags2int(ent->entry.flags); + oflags = HDBFlags2int(orig.flags); + nflags = HDBFlags2int(ent->flags); if (is_heimdal_entry && oflags != nflags) { @@ -645,7 +643,7 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, } /* Remove keys if they exists, and then replace keys. */ - if (!is_new_entry && orig.entry.keys.len > 0) { + if (!is_new_entry && orig.keys.len > 0) { vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key"); if (vals) { ldap_value_free_len(vals); @@ -656,21 +654,21 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, } } - for (i = 0; i < ent->entry.keys.len; i++) { + for (i = 0; i < ent->keys.len; i++) { if (is_samba_account - && ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { + && ent->keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { char *ntHexPassword; char *nt; time_t now = time(NULL); /* the key might have been 'sealed', but samba passwords are clear in the directory */ - ret = hdb_unseal_key(context, db, &ent->entry.keys.val[i]); + ret = hdb_unseal_key(context, db, &ent->keys.val[i]); if (ret) goto out; - nt = ent->entry.keys.val[i].key.keyvalue.data; + nt = ent->keys.val[i].key.keyvalue.data; /* store in ntPassword, not krb5key */ ret = hex_encode(nt, 16, &ntHexPassword); if (ret < 0) { @@ -703,7 +701,7 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, unsigned char *buf; size_t len, buf_size; - ASN1_MALLOC_ENCODE(Key, buf, buf_size, &ent->entry.keys.val[i], &len, ret); + ASN1_MALLOC_ENCODE(Key, buf, buf_size, &ent->keys.val[i], &len, ret); if (ret) goto out; if(buf_size != len) @@ -716,7 +714,7 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, } } - if (ent->entry.etypes) { + if (ent->etypes) { int add_krb5EncryptionType = 0; /* @@ -738,15 +736,15 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, add_krb5EncryptionType = 1; if (add_krb5EncryptionType) { - for (i = 0; i < ent->entry.etypes->len; i++) { + for (i = 0; i < ent->etypes->len; i++) { if (is_samba_account && - ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) + ent->keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { ; } else if (is_heimdal_entry) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_ADD, "krb5EncryptionType", - ent->entry.etypes->val[i]); + ent->etypes->val[i]); if (ret) goto out; } @@ -759,6 +757,8 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, out: + *pis_new_entry = is_new_entry; + if (ret == 0) *pmods = mods; else if (mods != NULL) { @@ -767,7 +767,7 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, } if (msg) - hdb_free_entry(context, &orig); + hdb_free_entry(context, db, &orig); return ret; } @@ -1005,7 +1005,7 @@ LDAP_principal2message(krb5_context context, HDB * db, */ static krb5_error_code LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, - int flags, hdb_entry_ex * ent) + int flags, hdb_entry * ent) { char *unparsed_name = NULL, *dn = NULL, *ntPasswordIN = NULL; char *samba_acct_flags = NULL; @@ -1015,18 +1015,18 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, int tmp, tmp_time, i, ret, have_arcfour = 0; memset(ent, 0, sizeof(*ent)); - ent->entry.flags = int2HDBFlags(0); + ent->flags = int2HDBFlags(0); ret = LDAP_get_string_value(db, msg, "krb5PrincipalName", &unparsed_name); if (ret == 0) { - ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal); + ret = krb5_parse_name(context, unparsed_name, &ent->principal); if (ret) goto out; } else { ret = LDAP_get_string_value(db, msg, "uid", &unparsed_name); if (ret == 0) { - ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal); + ret = krb5_parse_name(context, unparsed_name, &ent->principal); if (ret) goto out; } else { @@ -1042,25 +1042,25 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, ret = LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber", &integer); if (ret) - ent->entry.kvno = 0; + ent->kvno = 0; else - ent->entry.kvno = integer; + ent->kvno = integer; } keys = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key"); if (keys != NULL) { size_t l; - ent->entry.keys.len = ldap_count_values_len(keys); - ent->entry.keys.val = (Key *) calloc(ent->entry.keys.len, sizeof(Key)); - if (ent->entry.keys.val == NULL) { + ent->keys.len = ldap_count_values_len(keys); + ent->keys.val = (Key *) calloc(ent->keys.len, sizeof(Key)); + if (ent->keys.val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "calloc: out of memory"); goto out; } - for (i = 0; i < ent->entry.keys.len; i++) { + for (i = 0; i < ent->keys.len; i++) { decode_Key((unsigned char *) keys[i]->bv_val, - (size_t) keys[i]->bv_len, &ent->entry.keys.val[i], &l); + (size_t) keys[i]->bv_len, &ent->keys.val[i], &l); } ber_bvecfree(keys); } else { @@ -1070,8 +1070,8 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, * be related to a general directory entry without creating * the keys. Hopefully it's OK. */ - ent->entry.keys.len = 0; - ent->entry.keys.val = NULL; + ent->keys.len = 0; + ent->keys.val = NULL; #else ret = HDB_ERR_NOENTRY; goto out; @@ -1082,47 +1082,47 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, if (extensions != NULL) { size_t l; - ent->entry.extensions = calloc(1, sizeof(*(ent->entry.extensions))); - if (ent->entry.extensions == NULL) { + ent->extensions = calloc(1, sizeof(*(ent->extensions))); + if (ent->extensions == NULL) { ret = krb5_enomem(context); goto out; } - ent->entry.extensions->len = ldap_count_values_len(extensions); - ent->entry.extensions->val = (HDB_extension *) calloc(ent->entry.extensions->len, sizeof(HDB_extension)); - if (ent->entry.extensions->val == NULL) { - ent->entry.extensions->len = 0; + ent->extensions->len = ldap_count_values_len(extensions); + ent->extensions->val = (HDB_extension *) calloc(ent->extensions->len, sizeof(HDB_extension)); + if (ent->extensions->val == NULL) { + ent->extensions->len = 0; ret = krb5_enomem(context); goto out; } - for (i = 0; i < ent->entry.extensions->len; i++) { + for (i = 0; i < ent->extensions->len; i++) { ret = decode_HDB_extension((unsigned char *) extensions[i]->bv_val, - (size_t) extensions[i]->bv_len, &ent->entry.extensions->val[i], &l); + (size_t) extensions[i]->bv_len, &ent->extensions->val[i], &l); if (ret) krb5_set_error_message(context, ret, "decode_HDB_extension failed"); } ber_bvecfree(extensions); } else { - ent->entry.extensions = NULL; + ent->extensions = NULL; } vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType"); if (vals != NULL) { - ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes))); - if (ent->entry.etypes == NULL) { + ent->etypes = malloc(sizeof(*(ent->etypes))); + if (ent->etypes == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret,"malloc: out of memory"); goto out; } - ent->entry.etypes->len = ldap_count_values_len(vals); - ent->entry.etypes->val = calloc(ent->entry.etypes->len, - sizeof(ent->entry.etypes->val[0])); - if (ent->entry.etypes->val == NULL) { + ent->etypes->len = ldap_count_values_len(vals); + ent->etypes->val = calloc(ent->etypes->len, + sizeof(ent->etypes->val[0])); + if (ent->etypes->val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); - ent->entry.etypes->len = 0; + ent->etypes->len = 0; goto out; } - for (i = 0; i < ent->entry.etypes->len; i++) { + for (i = 0; i < ent->etypes->len; i++) { char *buf; buf = malloc(vals[i]->bv_len + 1); @@ -1133,14 +1133,14 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, } memcpy(buf, vals[i]->bv_val, vals[i]->bv_len); buf[vals[i]->bv_len] = '\0'; - ent->entry.etypes->val[i] = atoi(buf); + ent->etypes->val[i] = atoi(buf); free(buf); } ldap_value_free_len(vals); } - for (i = 0; i < ent->entry.keys.len; i++) { - if (ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { + for (i = 0; i < ent->keys.len; i++) { + if (ent->keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { have_arcfour = 1; break; } @@ -1152,146 +1152,151 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, unsigned *etypes; Key *ks; - ks = realloc(ent->entry.keys.val, - (ent->entry.keys.len + 1) * - sizeof(ent->entry.keys.val[0])); + ks = realloc(ent->keys.val, + (ent->keys.len + 1) * + sizeof(ent->keys.val[0])); if (ks == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } - ent->entry.keys.val = ks; - memset(&ent->entry.keys.val[ent->entry.keys.len], 0, sizeof(Key)); - ent->entry.keys.val[ent->entry.keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5; - ret = krb5_data_alloc (&ent->entry.keys.val[ent->entry.keys.len].key.keyvalue, 16); + ent->keys.val = ks; + memset(&ent->keys.val[ent->keys.len], 0, sizeof(Key)); + ent->keys.val[ent->keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5; + ret = krb5_data_alloc (&ent->keys.val[ent->keys.len].key.keyvalue, 16); if (ret) { krb5_set_error_message(context, ret, "malloc: out of memory"); ret = ENOMEM; goto out; } ret = hex_decode(ntPasswordIN, - ent->entry.keys.val[ent->entry.keys.len].key.keyvalue.data, 16); - ent->entry.keys.len++; - - if (ent->entry.etypes == NULL) { - ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes))); - if (ent->entry.etypes == NULL) { + ent->keys.val[ent->keys.len].key.keyvalue.data, 16); + ent->keys.len++; + if (ret == -1) { + krb5_set_error_message(context, ret = EINVAL, + "invalid hex encoding of password"); + goto out; + } + + if (ent->etypes == NULL) { + ent->etypes = malloc(sizeof(*(ent->etypes))); + if (ent->etypes == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } - ent->entry.etypes->val = NULL; - ent->entry.etypes->len = 0; + ent->etypes->val = NULL; + ent->etypes->len = 0; } - for (i = 0; i < ent->entry.etypes->len; i++) - if (ent->entry.etypes->val[i] == ETYPE_ARCFOUR_HMAC_MD5) + for (i = 0; i < ent->etypes->len; i++) + if (ent->etypes->val[i] == ETYPE_ARCFOUR_HMAC_MD5) break; /* If there is no ARCFOUR enctype, add one */ - if (i == ent->entry.etypes->len) { - etypes = realloc(ent->entry.etypes->val, - (ent->entry.etypes->len + 1) * - sizeof(ent->entry.etypes->val[0])); + if (i == ent->etypes->len) { + etypes = realloc(ent->etypes->val, + (ent->etypes->len + 1) * + sizeof(ent->etypes->val[0])); if (etypes == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } - ent->entry.etypes->val = etypes; - ent->entry.etypes->val[ent->entry.etypes->len] = + ent->etypes->val = etypes; + ent->etypes->val[ent->etypes->len] = ETYPE_ARCFOUR_HMAC_MD5; - ent->entry.etypes->len++; + ent->etypes->len++; } } ret = LDAP_get_generalized_time_value(db, msg, "createTimestamp", - &ent->entry.created_by.time); + &ent->created_by.time); if (ret) - ent->entry.created_by.time = time(NULL); + ent->created_by.time = time(NULL); - ent->entry.created_by.principal = NULL; + ent->created_by.principal = NULL; if (flags & HDB_F_ADMIN_DATA) { ret = LDAP_get_string_value(db, msg, "creatorsName", &dn); if (ret == 0) { - LDAP_dn2principal(context, db, dn, &ent->entry.created_by.principal); + LDAP_dn2principal(context, db, dn, &ent->created_by.principal); free(dn); } - ent->entry.modified_by = calloc(1, sizeof(*ent->entry.modified_by)); - if (ent->entry.modified_by == NULL) { + ent->modified_by = calloc(1, sizeof(*ent->modified_by)); + if (ent->modified_by == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_generalized_time_value(db, msg, "modifyTimestamp", - &ent->entry.modified_by->time); + &ent->modified_by->time); if (ret == 0) { ret = LDAP_get_string_value(db, msg, "modifiersName", &dn); if (ret == 0) { - LDAP_dn2principal(context, db, dn, &ent->entry.modified_by->principal); + LDAP_dn2principal(context, db, dn, &ent->modified_by->principal); free(dn); } else { - free(ent->entry.modified_by); - ent->entry.modified_by = NULL; + free(ent->modified_by); + ent->modified_by = NULL; } } } - ent->entry.valid_start = malloc(sizeof(*ent->entry.valid_start)); - if (ent->entry.valid_start == NULL) { + ent->valid_start = malloc(sizeof(*ent->valid_start)); + if (ent->valid_start == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidStart", - ent->entry.valid_start); + ent->valid_start); if (ret) { /* OPTIONAL */ - free(ent->entry.valid_start); - ent->entry.valid_start = NULL; + free(ent->valid_start); + ent->valid_start = NULL; } - ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end)); - if (ent->entry.valid_end == NULL) { + ent->valid_end = malloc(sizeof(*ent->valid_end)); + if (ent->valid_end == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd", - ent->entry.valid_end); + ent->valid_end); if (ret) { /* OPTIONAL */ - free(ent->entry.valid_end); - ent->entry.valid_end = NULL; + free(ent->valid_end); + ent->valid_end = NULL; } ret = LDAP_get_integer_value(db, msg, "sambaKickoffTime", &tmp_time); if (ret == 0) { - if (ent->entry.valid_end == NULL) { - ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end)); - if (ent->entry.valid_end == NULL) { + if (ent->valid_end == NULL) { + ent->valid_end = malloc(sizeof(*ent->valid_end)); + if (ent->valid_end == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } } - *ent->entry.valid_end = tmp_time; + *ent->valid_end = tmp_time; } - ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end)); - if (ent->entry.pw_end == NULL) { + ent->pw_end = malloc(sizeof(*ent->pw_end)); + if (ent->pw_end == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd", - ent->entry.pw_end); + ent->pw_end); if (ret) { /* OPTIONAL */ - free(ent->entry.pw_end); - ent->entry.pw_end = NULL; + free(ent->pw_end); + ent->pw_end = NULL; } ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time); @@ -1305,76 +1310,76 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, NULL); if (delta) { - if (ent->entry.pw_end == NULL) { - ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end)); - if (ent->entry.pw_end == NULL) { + if (ent->pw_end == NULL) { + ent->pw_end = malloc(sizeof(*ent->pw_end)); + if (ent->pw_end == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } } - *ent->entry.pw_end = tmp_time + delta; + *ent->pw_end = tmp_time + delta; } } ret = LDAP_get_integer_value(db, msg, "sambaPwdMustChange", &tmp_time); if (ret == 0) { - if (ent->entry.pw_end == NULL) { - ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end)); - if (ent->entry.pw_end == NULL) { + if (ent->pw_end == NULL) { + ent->pw_end = malloc(sizeof(*ent->pw_end)); + if (ent->pw_end == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } } - *ent->entry.pw_end = tmp_time; + *ent->pw_end = tmp_time; } /* OPTIONAL */ ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time); if (ret == 0) - hdb_entry_set_pw_change_time(context, &ent->entry, tmp_time); + hdb_entry_set_pw_change_time(context, ent, tmp_time); { int max_life; - ent->entry.max_life = malloc(sizeof(*ent->entry.max_life)); - if (ent->entry.max_life == NULL) { + ent->max_life = malloc(sizeof(*ent->max_life)); + if (ent->max_life == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", &max_life); if (ret) { - free(ent->entry.max_life); - ent->entry.max_life = NULL; + free(ent->max_life); + ent->max_life = NULL; } else - *ent->entry.max_life = max_life; + *ent->max_life = max_life; } { int max_renew; - ent->entry.max_renew = malloc(sizeof(*ent->entry.max_renew)); - if (ent->entry.max_renew == NULL) { + ent->max_renew = malloc(sizeof(*ent->max_renew)); + if (ent->max_renew == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", &max_renew); if (ret) { - free(ent->entry.max_renew); - ent->entry.max_renew = NULL; + free(ent->max_renew); + ent->max_renew = NULL; } else - *ent->entry.max_renew = max_renew; + *ent->max_renew = max_renew; } ret = LDAP_get_integer_value(db, msg, "krb5KDCFlags", &tmp); if (ret) tmp = 0; - ent->entry.flags = int2HDBFlags(tmp); + ent->flags = int2HDBFlags(tmp); /* Try and find Samba flags to put into the mix */ ret = LDAP_get_string_value(db, msg, "sambaAcctFlags", &samba_acct_flags); @@ -1406,7 +1411,7 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, /* Allow forwarding */ if (samba_forwardable) - ent->entry.flags.forwardable = TRUE; + ent->flags.forwardable = TRUE; for (i=0; i < flags_len; i++) { switch (samba_acct_flags[i]) { @@ -1418,36 +1423,36 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, /* how to handle no password in kerberos? */ break; case 'D': - ent->entry.flags.invalid = TRUE; + ent->flags.invalid = TRUE; break; case 'H': break; case 'T': /* temp duplicate */ - ent->entry.flags.invalid = TRUE; + ent->flags.invalid = TRUE; break; case 'U': - ent->entry.flags.client = TRUE; + ent->flags.client = TRUE; break; case 'M': break; case 'W': case 'S': - ent->entry.flags.server = TRUE; - ent->entry.flags.client = TRUE; + ent->flags.server = TRUE; + ent->flags.client = TRUE; break; case 'L': - ent->entry.flags.invalid = TRUE; + ent->flags.invalid = TRUE; break; case 'X': - if (ent->entry.pw_end) { - free(ent->entry.pw_end); - ent->entry.pw_end = NULL; + if (ent->pw_end) { + free(ent->pw_end); + ent->pw_end = NULL; } break; case 'I': - ent->entry.flags.server = TRUE; - ent->entry.flags.client = TRUE; + ent->flags.server = TRUE; + ent->flags.client = TRUE; break; } } @@ -1462,7 +1467,7 @@ out: free(ntPasswordIN); if (ret) - hdb_free_entry(context, ent); + hdb_free_entry(context, db, ent); return ret; } @@ -1491,7 +1496,7 @@ LDAP_unlock(krb5_context context, HDB * db) } static krb5_error_code -LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry) +LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry * entry) { int msgid, rc, parserc; krb5_error_code ret; @@ -1545,9 +1550,9 @@ LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry) if (ret == 0) { if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { - ret = hdb_unseal_keys(context, db, &entry->entry); + ret = hdb_unseal_keys(context, db, entry); if (ret) - hdb_free_entry(context, entry); + hdb_free_entry(context, db, entry); } } @@ -1556,7 +1561,7 @@ LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry) static krb5_error_code LDAP_firstkey(krb5_context context, HDB *db, unsigned flags, - hdb_entry_ex *entry) + hdb_entry *entry) { krb5_error_code ret; int msgid; @@ -1584,7 +1589,7 @@ LDAP_firstkey(krb5_context context, HDB *db, unsigned flags, static krb5_error_code LDAP_nextkey(krb5_context context, HDB * db, unsigned flags, - hdb_entry_ex * entry) + hdb_entry * entry) { return LDAP_seq(context, db, flags, entry); } @@ -1687,7 +1692,7 @@ LDAP_open(krb5_context context, HDB * db, int flags, mode_t mode) static krb5_error_code LDAP_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal, - unsigned flags, krb5_kvno kvno, hdb_entry_ex * entry) + unsigned flags, krb5_kvno kvno, hdb_entry * entry) { LDAPMessage *msg, *e; krb5_error_code ret; @@ -1705,9 +1710,9 @@ LDAP_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal, ret = LDAP_message2entry(context, db, e, flags, entry); if (ret == 0) { if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { - ret = hdb_unseal_keys(context, db, &entry->entry); + ret = hdb_unseal_keys(context, db, entry); if (ret) - hdb_free_entry(context, entry); + hdb_free_entry(context, db, entry); } } @@ -1720,7 +1725,7 @@ LDAP_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal, #if 0 static krb5_error_code LDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal, - unsigned flags, hdb_entry_ex * entry) + unsigned flags, hdb_entry * entry) { return LDAP_fetch_kvno(context, db, principal, flags & (~HDB_F_KVNO_SPECIFIED), 0, entry); @@ -1729,7 +1734,7 @@ LDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal, static krb5_error_code LDAP_store(krb5_context context, HDB * db, unsigned flags, - hdb_entry_ex * entry) + hdb_entry * entry) { LDAPMod **mods = NULL; krb5_error_code ret; @@ -1737,26 +1742,27 @@ LDAP_store(krb5_context context, HDB * db, unsigned flags, int rc; LDAPMessage *msg = NULL, *e = NULL; char *dn = NULL, *name = NULL; + krb5_boolean is_new_entry; if ((flags & HDB_F_PRECHECK)) return 0; /* we can't guarantee whether we'll be able to perform it */ - ret = LDAP_principal2message(context, db, entry->entry.principal, &msg); + ret = LDAP_principal2message(context, db, entry->principal, &msg); if (ret == 0) e = ldap_first_entry(HDB2LDAP(db), msg); - ret = krb5_unparse_name(context, entry->entry.principal, &name); + ret = krb5_unparse_name(context, entry->principal, &name); if (ret) { free(name); return ret; } - ret = hdb_seal_keys(context, db, &entry->entry); + ret = hdb_seal_keys(context, db, entry); if (ret) goto out; /* turn new entry into LDAPMod array */ - ret = LDAP_entry2mods(context, db, entry, e, &mods); + ret = LDAP_entry2mods(context, db, entry, e, &mods, &is_new_entry); if (ret) goto out; @@ -1767,8 +1773,9 @@ LDAP_store(krb5_context context, HDB * db, unsigned flags, krb5_set_error_message(context, ret, "asprintf: out of memory"); goto out; } - } else if (flags & HDB_F_REPLACE) { + } else if ((flags & HDB_F_REPLACE) || (is_new_entry)) { /* Entry exists, and we're allowed to replace it. */ + /* Entry may also exist but need to be modified to create a new principal. */ dn = ldap_get_dn(HDB2LDAP(db), e); } else { /* Entry exists, but we're not allowed to replace it. Bail. */ @@ -1847,6 +1854,8 @@ LDAP_remove(krb5_context context, HDB *db, goto out; } + /* HACK: This should check if we need to delete the object or just some attributes */ + rc = ldap_delete_ext_s(HDB2LDAP(db), dn, NULL, NULL ); if (check_ldap(context, db, rc)) { ret = HDB_ERR_CANT_LOCK_DB; @@ -1878,6 +1887,7 @@ LDAP_destroy(krb5_context context, HDB * db) free(HDB2CREATE(db)); if (HDB2URL(db)) free(HDB2URL(db)); + krb5_config_free_strings(db->virtual_hostbased_princ_svcs); if (db->hdb_name) free(db->hdb_name); free(db->hdb_db); @@ -2089,6 +2099,7 @@ struct hdb_method hdb_ldap_interface = { HDB_INTERFACE_VERSION, init, fini, + 0 /*is_file_based*/, 0 /*can_taste*/, "ldap", hdb_ldap_create }; @@ -2097,6 +2108,7 @@ struct hdb_method hdb_ldapi_interface = { HDB_INTERFACE_VERSION, init, fini, + 0 /*is_file_based*/, 0 /*can_taste*/, "ldapi", hdb_ldapi_create }; diff --git a/lib/hdb/hdb-mdb.c b/lib/hdb/hdb-mdb.c index 52d9aed7ac1f..dd1f27453d5e 100644 --- a/lib/hdb/hdb-mdb.c +++ b/lib/hdb/hdb-mdb.c @@ -42,13 +42,289 @@ #define KILO 1024 +#define E(sym, kret) case sym: ret = kret; ename = #sym; break + +/* Note: calls krb5_set_error_message() */ +static krb5_error_code +mdb2krb5_code(krb5_context context, int code) +{ + krb5_error_code ret = 0; + const char *ename = "UNKNOWN"; + const char *estr = mdb_strerror(code); + + switch (code) { + case MDB_SUCCESS: return 0; + E(MDB_KEYEXIST, HDB_ERR_EXISTS); + E(MDB_NOTFOUND, HDB_ERR_NOENTRY); + E(MDB_PAGE_NOTFOUND, HDB_ERR_UK_SERROR); + E(MDB_CORRUPTED, HDB_ERR_UK_SERROR); + E(MDB_PANIC, HDB_ERR_UK_SERROR); + E(MDB_VERSION_MISMATCH, HDB_ERR_UK_SERROR); + E(MDB_INVALID, HDB_ERR_UK_SERROR); + E(MDB_MAP_FULL, HDB_ERR_UK_SERROR); + E(MDB_DBS_FULL, HDB_ERR_UK_SERROR); + E(MDB_READERS_FULL, HDB_ERR_UK_SERROR); + E(MDB_TLS_FULL, HDB_ERR_UK_SERROR); + E(MDB_TXN_FULL, HDB_ERR_UK_SERROR); + E(MDB_CURSOR_FULL, HDB_ERR_UK_SERROR); + E(MDB_PAGE_FULL, HDB_ERR_UK_SERROR); + E(MDB_MAP_RESIZED, HDB_ERR_UK_SERROR); + E(MDB_INCOMPATIBLE, HDB_ERR_UK_SERROR); + E(MDB_BAD_RSLOT, HDB_ERR_UK_SERROR); + E(MDB_BAD_TXN, HDB_ERR_UK_SERROR); + E(MDB_BAD_VALSIZE, HDB_ERR_UK_SERROR); + E(MDB_BAD_DBI, HDB_ERR_UK_SERROR); + default: + if (code > 0 && code < 100) + ret = code; + else + ret = HDB_ERR_UK_SERROR; + break; + } + if (ret) + krb5_set_error_message(context, ret, "MDB error %s (%d): %s", + ename, code, estr); + return ret; +} + typedef struct mdb_info { MDB_env *e; MDB_txn *t; MDB_dbi d; MDB_cursor *c; + int oflags; + mode_t mode; + size_t mapsize; + unsigned int in_tx:1; } mdb_info; +/* See below */ +struct keep_it_open { + char *path; + MDB_env *env; + MDB_dbi d; + unsigned int oflags; + size_t refs; + size_t mapsize; + unsigned int valid:1; + struct keep_it_open *next; +} *keep_them_open; +HEIMDAL_MUTEX keep_them_open_lock = HEIMDAL_MUTEX_INITIALIZER; + +/* + * On Unix LMDB uses fcntl() byte-range locks, and unlike SQLite3 (which also + * uses fcntl() byte-range locks) LMDB takes no precautions to avoid early + * first-close()s that cause other threads' locks to get dropped. No, LMDB + * requires the caller to take such precautions. For us that means opening one + * mdb env per-{HDB, mode} (where mode is read-write or read-only), never + * closing it, and sharing it with all threads. + * + * Sharing an MDB_env * across multiple threads is documented to be safe, and + * internally LMDB uses pread(2), pwrite(2), and mmap(2) for I/O, using + * read(2)/write(2) only in the DB copy routines that we don't use. + * + * On WIN32 we don't have to do any of this, however, to avoid ifdef spaghetti, + * we share this code on all platforms, even if it isn't strictly needed. + * + * Also, one must call mdb_open() (aka mdb_dbi_open()) only once per call to + * mdb_env_open() and per B-tree. We only use one B-tree in each LMDB: the + * main one. + * + * On success this outputs an `MDB_env *' (the handle for the LMDB) and an + * `MDB_dbi' (the handle for the main B-tree in the LMDB). + * + * ALSO, LMDB requires that we re-open the `MDB_env' when the database grows + * larger than the mmap size. We handle this by finding in `keep_them_open' + * the env we already have, marking it unusable, and the finding some other + * better one or opening a new one and adding it to the list. + */ +static krb5_error_code +my_mdb_env_create_and_open(krb5_context context, + mdb_info *mi, + const char *path, + int mapfull) +{ + struct keep_it_open *p, *n; + MDB_txn *txn = NULL; + unsigned int flags = MDB_NOSUBDIR | MDB_NOTLS; + struct stat st; + size_t mapsize = 0; + int max_readers; + int locked = 0; + int code = 0; + + mi->oflags &= O_ACCMODE; + flags |= (mi->oflags == O_RDONLY) ? MDB_RDONLY : 0; + + mi->e = NULL; + + /* + * Allocate a new object, in case we don't already have one in + * `keep_them_open'; if we don't need it, we'll free it. This way we do + * some of the work of creating one while not holding a lock. + */ + if ((n = calloc(1, sizeof(*n))) == NULL || + (n->path = strdup(path)) == NULL) { + free(n); + return krb5_enomem(context); + } + n->oflags = mi->oflags; + + max_readers = krb5_config_get_int_default(context, NULL, 0, "kdc", + "hdb-mdb-maxreaders", NULL); + mapsize = krb5_config_get_int_default(context, NULL, 0, "kdc", "hdb-mdb-mapsize", + NULL); + if (mapsize > INT_MAX) + mapsize = 0; + + memset(&st, 0, sizeof(st)); + if (stat(path, &st) == 0 && st.st_size > mapsize * KILO) + mapsize += (st.st_size + (st.st_size >> 2)) / KILO; + if (mapsize < 100 * 1024) + mapsize = 100 * 1024; /* 100MB */ + if (mapsize < mi->mapsize) + mapsize = mi->mapsize; + if (mapfull) + mapsize += 10 * 1024; + if ((code = mdb_env_create(&n->env)) || + (max_readers && (code = mdb_env_set_maxreaders(n->env, max_readers)))) + goto out; + + /* Look for an existing env */ + HEIMDAL_MUTEX_lock(&keep_them_open_lock); + locked = 1; + for (p = keep_them_open; p; p = p->next) { + if (strcmp(p->path, path) != 0) + continue; + if (p->mapsize > mapsize) + /* Always increase mapsize */ + mapsize = p->mapsize + (p->mapsize >> 1); + if (!p->valid || p->oflags != mi->oflags) + continue; + /* Found one; output it and get out */ + mi->e = p->env; + mi->d = p->d; + p->refs++; + goto out; + } + + /* Did not find one, so open and add this one to the list */ + + /* Open the LMDB itself */ + n->refs = 1; + n->valid = 1; + krb5_debug(context, 5, "Opening HDB LMDB %s with mapsize %llu", + path, (unsigned long long)mapsize * KILO); + code = mdb_env_set_mapsize(n->env, mapsize * KILO); + if (code == 0) + code = mdb_env_open(n->env, path, flags, mi->mode); + if (code == 0) + /* Open a transaction so we can resolve the main B-tree */ + code = mdb_txn_begin(n->env, NULL, MDB_RDONLY, &txn); + if (code == 0) + /* Resolve the main B-tree */ + code = mdb_open(txn, NULL, 0, &n->d); + if (code) + goto out; + + /* Successfully opened the LMDB; output the two handles */ + mi->mapsize = n->mapsize = mapsize; + mi->e = n->env; + mi->d = n->d; + + /* Add this keep_it_open to the front of the list */ + n->next = keep_them_open; + keep_them_open = n; + n = NULL; + +out: + if (locked) + HEIMDAL_MUTEX_unlock(&keep_them_open_lock); + if (n) { + if (n->env) + mdb_env_close(n->env); + free(n->path); + free(n); + } + (void) mdb_txn_commit(txn); /* Safe when `txn == NULL' */ + return mdb2krb5_code(context, code); +} + +static void +my_mdb_env_close(krb5_context context, + const char *db_name, + MDB_env **envp) +{ + struct keep_it_open **prev; + struct keep_it_open *p, *old; + size_t refs_seen = 0; + size_t slen = strlen(db_name); + MDB_env *env = *envp; + + if (env == NULL) + return; + + HEIMDAL_MUTEX_lock(&keep_them_open_lock); + for (p = keep_them_open; p; p = p->next) { + /* + * We can have multiple open ones and we need to know if this is the + * last one, so we can't break out early. + */ + if (p->env == env) + refs_seen += (--(p->refs)); + else if (strncmp(db_name, p->path, slen) == 0 && + strcmp(p->path + slen, ".mdb") == 0) + refs_seen += p->refs; + } + krb5_debug(context, 6, "Closing HDB LMDB %s / %p; refs %llu", db_name, env, + (unsigned long long)refs_seen); + prev = &keep_them_open; + for (p = keep_them_open; !refs_seen && p; ) { + /* We're the last close */ + if (p->refs || + strncmp(db_name, p->path, slen) != 0 || + strcmp(p->path + slen, ".mdb") != 0) { + + /* Not us; this keep_it_open stays */ + prev = &p->next; + p = p->next; + continue; + } + + /* Close and remove this one */ + krb5_debug(context, 6, "Closing HDB LMDB %s (mapsize was %llu)", + db_name, (unsigned long long)p->mapsize * KILO); + old = p; + *prev = (p = p->next); /* prev stays */ + mdb_env_close(old->env); + free(old->path); + free(old); + } + HEIMDAL_MUTEX_unlock(&keep_them_open_lock); +} + +/* + * This is a wrapper around my_mdb_env_create_and_open(). It may close an + * existing MDB_env in mi->e if it's there. If we need to reopen because the + * MDB grew too much, then we call this. + */ +static krb5_error_code +my_reopen_mdb(krb5_context context, HDB *db, int mapfull) +{ + mdb_info *mi = (mdb_info *)db->hdb_db; + char *fn; + krb5_error_code ret = 0; + + /* No-op if we don't have an open one */ + my_mdb_env_close(context, db->hdb_name, &mi->e); + if (asprintf(&fn, "%s.mdb", db->hdb_name) == -1) + ret = krb5_enomem(context); + if (ret == 0) + ret = my_mdb_env_create_and_open(context, mi, fn, mapfull); + free(fn); + return ret; +} + static krb5_error_code DB_close(krb5_context context, HDB *db) { @@ -56,7 +332,7 @@ DB_close(krb5_context context, HDB *db) mdb_cursor_close(mi->c); mdb_txn_abort(mi->t); - mdb_env_close(mi->e); + my_mdb_env_close(context, db->hdb_name, &mi->e); mi->c = 0; mi->t = 0; mi->e = 0; @@ -68,7 +344,8 @@ DB_destroy(krb5_context context, HDB *db) { krb5_error_code ret; - ret = hdb_clear_master_key (context, db); + ret = hdb_clear_master_key(context, db); + krb5_config_free_strings(db->virtual_hostbased_princ_svcs); free(db->hdb_name); free(db->hdb_db); free(db); @@ -106,41 +383,44 @@ DB_unlock(krb5_context context, HDB *db) static krb5_error_code DB_seq(krb5_context context, HDB *db, - unsigned flags, hdb_entry_ex *entry, int flag) + unsigned flags, hdb_entry *entry, int flag) { mdb_info *mi = db->hdb_db; MDB_val key, value; krb5_data key_data, data; int code; + /* + * No need to worry about MDB_MAP_FULL when we're scanning the DB since we + * have snapshot semantics, and any DB growth from other transactions + * should not affect us. + */ key.mv_size = 0; value.mv_size = 0; code = mdb_cursor_get(mi->c, &key, &value, flag); - if (code == MDB_NOTFOUND) - return HDB_ERR_NOENTRY; if (code) - return code; + return mdb2krb5_code(context, code); key_data.data = key.mv_data; key_data.length = key.mv_size; data.data = value.mv_data; data.length = value.mv_size; memset(entry, 0, sizeof(*entry)); - if (hdb_value2entry(context, &data, &entry->entry)) + if (hdb_value2entry(context, &data, entry)) return DB_seq(context, db, flags, entry, MDB_NEXT); if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { - code = hdb_unseal_keys (context, db, &entry->entry); + code = hdb_unseal_keys (context, db, entry); if (code) - hdb_free_entry (context, entry); + hdb_free_entry (context, db, entry); } - if (entry->entry.principal == NULL) { - entry->entry.principal = malloc(sizeof(*entry->entry.principal)); - if (entry->entry.principal == NULL) { - hdb_free_entry (context, entry); + if (entry->principal == NULL) { + entry->principal = malloc(sizeof(*entry->principal)); + if (entry->principal == NULL) { + hdb_free_entry (context, db, entry); krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } else { - hdb_key2principal(context, &key_data, entry->entry.principal); + hdb_key2principal(context, &key_data, entry->principal); } } return 0; @@ -148,29 +428,41 @@ DB_seq(krb5_context context, HDB *db, static krb5_error_code -DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) { + krb5_error_code ret = 0; mdb_info *mi = db->hdb_db; - int code; + int tries = 3; + int code = 0; /* Always start with a fresh cursor to pick up latest DB state */ - if (mi->t) - mdb_txn_abort(mi->t); - code = mdb_txn_begin(mi->e, NULL, MDB_RDONLY, &mi->t); - if (code) - return code; - - code = mdb_cursor_open(mi->t, mi->d, &mi->c); - if (code) - return code; - - return DB_seq(context, db, flags, entry, MDB_FIRST); + do { + if (mi->t) + mdb_txn_abort(mi->t); + mi->t = NULL; + if (code) + code = my_reopen_mdb(context, db, 1); + if (code == 0) + code = mdb_txn_begin(mi->e, NULL, MDB_RDONLY, &mi->t); + if (code == 0) + code = mdb_cursor_open(mi->t, mi->d, &mi->c); + if (code == 0) { + ret = DB_seq(context, db, flags, entry, MDB_FIRST); + break; + } + } while (code == MDB_MAP_FULL && --tries > 0); + + if (code || ret) { + mdb_txn_abort(mi->t); + mi->t = NULL; + } + return ret ? ret : mdb2krb5_code(context, code); } static krb5_error_code -DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) { return DB_seq(context, db, flags, entry, MDB_NEXT); } @@ -206,24 +498,34 @@ static krb5_error_code DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply) { mdb_info *mi = (mdb_info*)db->hdb_db; - MDB_txn *txn; + MDB_txn *txn = NULL; MDB_val k, v; - int code; + int tries = 3; + int code = 0; k.mv_data = key.data; k.mv_size = key.length; - code = mdb_txn_begin(mi->e, NULL, MDB_RDONLY, &txn); - if (code) - return code; + do { + if (txn) { + mdb_txn_abort(txn); + txn = NULL; + } + if (code) + code = my_reopen_mdb(context, db, 1); + if (code == 0) + code = mdb_txn_begin(mi->e, NULL, MDB_RDONLY, &txn); + if (code == 0) + code = mdb_get(txn, mi->d, &k, &v); + if (code == 0) + krb5_data_copy(reply, v.mv_data, v.mv_size); + } while (code == MDB_MAP_FULL && --tries > 0); - code = mdb_get(txn, mi->d, &k, &v); - if (code == 0) - krb5_data_copy(reply, v.mv_data, v.mv_size); - mdb_txn_abort(txn); - if(code == MDB_NOTFOUND) - return HDB_ERR_NOENTRY; - return code; + if (code) + mdb_txn_abort(txn); + else + (void) mdb_txn_commit(txn); /* Empty transaction? -> commit */ + return mdb2krb5_code(context, code); } static krb5_error_code @@ -231,137 +533,110 @@ DB__put(krb5_context context, HDB *db, int replace, krb5_data key, krb5_data value) { mdb_info *mi = (mdb_info*)db->hdb_db; - MDB_txn *txn; + MDB_txn *txn = NULL; MDB_val k, v; - int code; + int tries = 3; + int code = 0; k.mv_data = key.data; k.mv_size = key.length; v.mv_data = value.data; v.mv_size = value.length; - code = mdb_txn_begin(mi->e, NULL, 0, &txn); - if (code) - return code; - - code = mdb_put(txn, mi->d, &k, &v, replace ? 0 : MDB_NOOVERWRITE); - if (code) - mdb_txn_abort(txn); - else - code = mdb_txn_commit(txn); - /* - * No need to call mdb_env_sync(); it's done automatically if MDB_NOSYNC is - * not set. - */ - if(code == MDB_KEYEXIST) - return HDB_ERR_EXISTS; - return code; + do { + if (txn) { + mdb_txn_abort(txn); + txn = NULL; + } + if (code) + code = my_reopen_mdb(context, db, 1); + if (code == 0) + code = mdb_txn_begin(mi->e, NULL, 0, &txn); + if (code == 0) + code = mdb_put(txn, mi->d, &k, &v, replace ? 0 : MDB_NOOVERWRITE); + if (code == 0) { + /* + * No need to call mdb_env_sync(); it's done automatically if + * MDB_NOSYNC is not set. + */ + code = mdb_txn_commit(txn); + txn = NULL; + } + } while (code == MDB_MAP_FULL && --tries > 0); + if (txn) + mdb_txn_abort(txn); + return mdb2krb5_code(context, code); } static krb5_error_code DB__del(krb5_context context, HDB *db, krb5_data key) { mdb_info *mi = (mdb_info*)db->hdb_db; - MDB_txn *txn; + MDB_txn *txn = NULL; MDB_val k; - krb5_error_code code; + int tries = 3; + int code = 0; k.mv_data = key.data; k.mv_size = key.length; - code = mdb_txn_begin(mi->e, NULL, 0, &txn); - if (code) - return code; - - code = mdb_del(txn, mi->d, &k, NULL); - if (code) - mdb_txn_abort(txn); - else - code = mdb_txn_commit(txn); - /* - * No need to call mdb_env_sync(); it's done automatically if MDB_NOSYNC is - * not set. - */ - if(code == MDB_NOTFOUND) - return HDB_ERR_NOENTRY; - return code; + do { + if (txn) { + mdb_txn_abort(txn); + txn = NULL; + } + if (code) + code = my_reopen_mdb(context, db, 1); + if (code == 0) + code = mdb_txn_begin(mi->e, NULL, 0, &txn); + if (code == 0) + code = mdb_del(txn, mi->d, &k, NULL); + if (code == 0) { + /* + * No need to call mdb_env_sync(); it's done automatically if + * MDB_NOSYNC is not set. + */ + code = mdb_txn_commit(txn); + txn = NULL; + } + } while (code == MDB_MAP_FULL && --tries > 0); + + if (txn) + mdb_txn_abort(txn); + return mdb2krb5_code(context, code); } static krb5_error_code -DB_open(krb5_context context, HDB *db, int flags, mode_t mode) +DB_open(krb5_context context, HDB *db, int oflags, mode_t mode) { mdb_info *mi = (mdb_info *)db->hdb_db; - MDB_txn *txn; - char *fn; krb5_error_code ret; - int myflags = MDB_NOSUBDIR, tmp; - - if((flags & O_ACCMODE) == O_RDONLY) - myflags |= MDB_RDONLY; - if (asprintf(&fn, "%s.mdb", db->hdb_name) == -1) - return krb5_enomem(context); - if (mdb_env_create(&mi->e)) { - free(fn); - return krb5_enomem(context); - } - - tmp = krb5_config_get_int_default(context, NULL, 0, "kdc", - "hdb-mdb-maxreaders", NULL); - if (tmp) { - ret = mdb_env_set_maxreaders(mi->e, tmp); - if (ret) { - free(fn); - krb5_set_error_message(context, ret, "setting maxreaders on %s: %s", - db->hdb_name, mdb_strerror(ret)); - return ret; - } - } - - tmp = krb5_config_get_int_default(context, NULL, 0, "kdc", - "hdb-mdb-mapsize", NULL); - if (tmp) { - size_t maps = tmp; - maps *= KILO; - ret = mdb_env_set_mapsize(mi->e, maps); - if (ret) { - free(fn); - krb5_set_error_message(context, ret, "setting mapsize on %s: %s", - db->hdb_name, mdb_strerror(ret)); - return ret; - } - } - - ret = mdb_env_open(mi->e, fn, myflags, mode); - free(fn); + mi->e = NULL; + mi->mode = mode; + mi->oflags = oflags & O_ACCMODE; + ret = my_reopen_mdb(context, db, 0); if (ret) { -fail: - mdb_env_close(mi->e); - mi->e = 0; - krb5_set_error_message(context, ret, "opening %s: %s", - db->hdb_name, mdb_strerror(ret)); + krb5_prepend_error_message(context, ret, "opening %s:", db->hdb_name); return ret; } - ret = mdb_txn_begin(mi->e, NULL, MDB_RDONLY, &txn); - if (ret) - goto fail; - - ret = mdb_open(txn, NULL, 0, &mi->d); - mdb_txn_abort(txn); - if (ret) - goto fail; - - if((flags & O_ACCMODE) == O_RDONLY) + if ((oflags & O_ACCMODE) == O_RDONLY) { ret = hdb_check_db_format(context, db); - else + /* + * Dubious: if the DB is not initialized, shouldn't we tell the + * caller?? + */ + if (ret == HDB_ERR_NOENTRY) + return 0; + } else { + /* hdb_init_db() calls hdb_check_db_format() */ ret = hdb_init_db(context, db); - if(ret == HDB_ERR_NOENTRY) - return 0; + } if (ret) { DB_close(context, db); krb5_set_error_message(context, ret, "hdb_open: failed %s database %s", - (flags & O_ACCMODE) == O_RDONLY ? + (oflags & O_ACCMODE) == O_RDONLY ? "checking format of" : "initialize", db->hdb_name); } diff --git a/lib/hdb/hdb-mitdb.c b/lib/hdb/hdb-mitdb.c index 7a9438cbe1ea..ae315cd831d9 100644 --- a/lib/hdb/hdb-mitdb.c +++ b/lib/hdb/hdb-mitdb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan + * Copyright (c) 1997 - 2017 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -108,6 +108,7 @@ attr_to_flags(unsigned attr, HDBFlags *flags) flags->invalid = !!(attr & KRB5_KDB_DISALLOW_ALL_TIX); flags->require_preauth = !!(attr & KRB5_KDB_REQUIRES_PRE_AUTH); flags->require_hwauth = !!(attr & KRB5_KDB_REQUIRES_HW_AUTH); + flags->require_pwchange = !!(attr & KRB5_KDB_REQUIRES_PWCHANGE); flags->server = !(attr & KRB5_KDB_DISALLOW_SVR); flags->change_pw = !!(attr & KRB5_KDB_PWCHANGE_SERVICE); flags->client = 1; /* XXX */ @@ -554,7 +555,7 @@ _hdb_mdb_value2entry(krb5_context context, krb5_data *data, goto out; } CHECK(ret = krb5_parse_name(context, p, &modby)); - ret = hdb_set_last_modified_by(context, entry, modby, u32); + CHECK(ret = hdb_set_last_modified_by(context, entry, modby, u32)); krb5_free_principal(context, modby); free(p); break; @@ -661,7 +662,7 @@ out: if (ret == HEIM_ERR_EOF) /* Better error code than "end of file" */ ret = HEIM_ERR_BAD_HDBENT_ENCODING; - free_hdb_entry(entry); + free_HDB_entry(entry); free_Key(&k); return ret; } @@ -696,7 +697,8 @@ mdb_destroy(krb5_context context, HDB *db) { krb5_error_code ret; - ret = hdb_clear_master_key (context, db); + ret = hdb_clear_master_key(context, db); + krb5_config_free_strings(db->virtual_hostbased_princ_svcs); free(db->hdb_name); free(db); return ret; @@ -763,11 +765,11 @@ mdb_unlock(krb5_context context, HDB *db) static krb5_error_code mdb_seq(krb5_context context, HDB *db, - unsigned flags, hdb_entry_ex *entry, int flag) + unsigned flags, hdb_entry *entry, int flag) { DB *d = (DB*)db->hdb_db; DBT key, value; - krb5_data key_data, data; + krb5_data data; int code; code = db->hdb_lock(context, db, HDB_RLOCK); @@ -788,19 +790,17 @@ mdb_seq(krb5_context context, HDB *db, return HDB_ERR_NOENTRY; } - key_data.data = key.data; - key_data.length = key.size; data.data = value.data; data.length = value.size; memset(entry, 0, sizeof(*entry)); - if (_hdb_mdb_value2entry(context, &data, 0, &entry->entry)) + if (_hdb_mdb_value2entry(context, &data, 0, entry)) return mdb_seq(context, db, flags, entry, R_NEXT); if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { - code = hdb_unseal_keys (context, db, &entry->entry); + code = hdb_unseal_keys (context, db, entry); if (code) - hdb_free_entry (context, entry); + hdb_free_entry (context, db, entry); } return code; @@ -808,14 +808,14 @@ mdb_seq(krb5_context context, HDB *db, static krb5_error_code -mdb_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +mdb_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) { return mdb_seq(context, db, flags, entry, R_FIRST); } static krb5_error_code -mdb_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +mdb_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) { return mdb_seq(context, db, flags, entry, R_NEXT); } @@ -939,7 +939,7 @@ mdb__del(krb5_context context, HDB *db, krb5_data key) static krb5_error_code mdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, - unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry) + unsigned flags, krb5_kvno kvno, hdb_entry *entry) { krb5_data key, value; krb5_error_code ret; @@ -951,15 +951,15 @@ mdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, krb5_data_free(&key); if(ret) return ret; - ret = _hdb_mdb_value2entry(context, &value, kvno, &entry->entry); + ret = _hdb_mdb_value2entry(context, &value, kvno, entry); krb5_data_free(&value); if (ret) return ret; if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { - ret = hdb_unseal_keys (context, db, &entry->entry); + ret = hdb_unseal_keys (context, db, entry); if (ret) { - hdb_free_entry(context, entry); + hdb_free_entry(context, db, entry); return ret; } } @@ -968,7 +968,7 @@ mdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, } static krb5_error_code -mdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +mdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) { krb5_error_code ret; krb5_storage *sp = NULL; @@ -977,13 +977,13 @@ mdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) krb5_data kdb_ent = { 0, 0 }; krb5_data key = { 0, 0 }; krb5_data value = { 0, 0 }; - ssize_t sz; + krb5_ssize_t sz; if ((flags & HDB_F_PRECHECK) && (flags & HDB_F_REPLACE)) return 0; if ((flags & HDB_F_PRECHECK)) { - ret = mdb_principal2key(context, entry->entry.principal, &key); + ret = mdb_principal2key(context, entry->principal, &key); if (ret) return ret; ret = db->hdb__get(context, db, key, &value); krb5_data_free(&key); @@ -997,13 +997,13 @@ mdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) sp = krb5_storage_emem(); if (!sp) return ENOMEM; ret = _hdb_set_master_key_usage(context, db, 0); /* MIT KDB uses KU 0 */ - ret = hdb_seal_keys(context, db, &entry->entry); + ret = hdb_seal_keys(context, db, entry); if (ret) return ret; - ret = entry2mit_string_int(context, sp, &entry->entry); + ret = entry2mit_string_int(context, sp, entry); if (ret) goto out; sz = krb5_storage_write(sp, "\n", 2); /* NUL-terminate */ ret = ENOMEM; - if (sz == -1) goto out; + if (sz != 2) goto out; ret = krb5_storage_to_data(sp, &line); if (ret) goto out; @@ -1014,7 +1014,7 @@ mdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) if (ret) goto out; ret = krb5_storage_to_data(spent, &kdb_ent); if (ret) goto out; - ret = mdb_principal2key(context, entry->entry.principal, &key); + ret = mdb_principal2key(context, entry->principal, &key); if (ret) goto out; ret = mdb__put(context, db, 1, key, kdb_ent); @@ -1038,9 +1038,8 @@ mdb_remove(krb5_context context, HDB *db, krb5_data key; krb5_data value = { 0, 0 }; - code = mdb_principal2key(context, principal, &key); - if (code) - return code; + mdb_principal2key(context, principal, &key); + if ((flags & HDB_F_PRECHECK)) { code = db->hdb__get(context, db, key, &value); krb5_data_free(&key); @@ -1252,17 +1251,16 @@ getdata(char **p, unsigned char *buf, size_t len, const char *what) } static int -getint(char **p, const char *what) +getint(char **p, const char *what, int *val) { - int val; char *q = nexttoken(p, 0, what); if (!q) { warnx("Failed to find a signed integer (%s) in dump", what); - return -1; + return 1; } - if (sscanf(q, "%d", &val) != 1) - return -1; - return val; + if (sscanf(q, "%d", val) != 1) + return 1; + return 0; } static unsigned int @@ -1309,7 +1307,7 @@ _hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp) krb5_error_code ret = EINVAL; char *p = line, *q; char *princ; - ssize_t sz; + krb5_ssize_t sz; size_t i; size_t princ_len; unsigned int num_tl_data; @@ -1326,7 +1324,7 @@ _hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp) "'policy', nor 'princ'"); return -1; } - if (getint(&p, "constant '38'") != 38) { + if (getint(&p, "constant '38'", &tmp) || tmp != 38) { warnx("Dump entry does not start with '38<TAB>'"); return EINVAL; } @@ -1342,7 +1340,7 @@ _hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp) } num_tl_data = getuint(&p, "number of TL data"); num_key_data = getuint(&p, "number of key data"); - getint(&p, "5th field, length of 'extra data'"); + (void) getint(&p, "5th field, length of 'extra data'", &tmp); princ = nexttoken(&p, (int)princ_len, "principal name"); if (princ == NULL) { warnx("Failed to read principal name (expected length %llu)", @@ -1354,38 +1352,31 @@ _hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp) ret = krb5_store_uint32(sp, attributes); if (ret) return ret; - tmp = getint(&p, "max life"); - CHECK_UINT(tmp); + if (getint(&p, "max life", &tmp)) return EINVAL; ret = krb5_store_uint32(sp, tmp); if (ret) return ret; - tmp = getint(&p, "max renewable life"); - CHECK_UINT(tmp); + if (getint(&p, "max renewable life", &tmp)) return EINVAL; ret = krb5_store_uint32(sp, tmp); if (ret) return ret; - tmp = getint(&p, "expiration"); - CHECK_UINT(tmp); + if (getint(&p, "expiration", &tmp)) return EINVAL; ret = krb5_store_uint32(sp, tmp); if (ret) return ret; - tmp = getint(&p, "pw expiration"); - CHECK_UINT(tmp); + if (getint(&p, "pw expiration", &tmp)) return EINVAL; ret = krb5_store_uint32(sp, tmp); if (ret) return ret; - tmp = getint(&p, "last auth"); - CHECK_UINT(tmp); + if (getint(&p, "last auth", &tmp)) return EINVAL; ret = krb5_store_uint32(sp, tmp); if (ret) return ret; - tmp = getint(&p, "last failed auth"); - CHECK_UINT(tmp); + if (getint(&p, "last failed auth", &tmp)) return EINVAL; ret = krb5_store_uint32(sp, tmp); if (ret) return ret; - tmp = getint(&p,"fail auth count"); - CHECK_UINT(tmp); + if (getint(&p,"fail auth count", &tmp)) return EINVAL; ret = krb5_store_uint32(sp, tmp); if (ret) return ret; @@ -1405,7 +1396,7 @@ _hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp) ret = krb5_store_uint16(sp, princ_len); if (ret) return ret; sz = krb5_storage_write(sp, princ, princ_len); - if (sz == -1) return ENOMEM; + if (sz != princ_len) return ENOMEM; /* scan and write TL data */ for (i = 0; i < num_tl_data; i++) { @@ -1413,8 +1404,9 @@ _hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp) int tl_type, tl_length; unsigned char *buf; - tl_type = getint(&p, "TL data type"); - tl_length = getint(&p, "data length"); + if (getint(&p, "TL data type", &tl_type) || + getint(&p, "data length", &tl_length)) + return EINVAL; if (asprintf(&reading_what, "TL data type %d (length %d)", tl_type, tl_length) < 0) @@ -1434,11 +1426,13 @@ _hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp) if (tl_length) { buf = malloc(tl_length); if (!buf) return ENOMEM; - if (getdata(&p, buf, tl_length, reading_what) != tl_length) + if (getdata(&p, buf, tl_length, reading_what) != tl_length) { + free(buf); return EINVAL; + } sz = krb5_storage_write(sp, buf, tl_length); free(buf); - if (sz == -1) return ENOMEM; + if (sz != tl_length) return ENOMEM; } else { if (strcmp(nexttoken(&p, 0, "'-1' field"), "-1") != 0) return EINVAL; } @@ -1453,23 +1447,23 @@ _hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp) int keylen; size_t k; - key_versions = getint(&p, "key data 'version'"); + if (getint(&p, "key data 'version'", &key_versions)) return EINVAL; CHECK_UINT16(key_versions); ret = krb5_store_int16(sp, key_versions); if (ret) return ret; - kvno = getint(&p, "kvno"); + if (getint(&p, "kvno", &kvno)) return EINVAL; CHECK_UINT16(kvno); ret = krb5_store_int16(sp, kvno); if (ret) return ret; for (k = 0; k < key_versions; k++) { - keytype = getint(&p, "enctype"); + if (getint(&p, "enctype", &keytype)) return EINVAL; CHECK_UINT16(keytype); ret = krb5_store_int16(sp, keytype); if (ret) return ret; - keylen = getint(&p, "encrypted key length"); + if (getint(&p, "encrypted key length", &keylen)) return EINVAL; CHECK_UINT16(keylen); ret = krb5_store_int16(sp, keylen); if (ret) return ret; @@ -1477,11 +1471,13 @@ _hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp) if (keylen) { buf = malloc(keylen); if (!buf) return ENOMEM; - if (getdata(&p, buf, keylen, "key (or salt) data") != keylen) + if (getdata(&p, buf, keylen, "key (or salt) data") != keylen) { + free(buf); return EINVAL; + } sz = krb5_storage_write(sp, buf, keylen); free(buf); - if (sz == -1) return ENOMEM; + if (sz != keylen) return ENOMEM; } else { if (strcmp(nexttoken(&p, 0, "'-1' zero-length key/salt field"), diff --git a/lib/hdb/hdb-private.h b/lib/hdb/hdb-private.h deleted file mode 100644 index 82681997681f..000000000000 --- a/lib/hdb/hdb-private.h +++ /dev/null @@ -1,81 +0,0 @@ -/* This is a generated file */ -#ifndef __hdb_private_h__ -#define __hdb_private_h__ - -#include <stdarg.h> - -krb5_error_code -_hdb_fetch_kvno ( - krb5_context /*context*/, - HDB */*db*/, - krb5_const_principal /*principal*/, - unsigned /*flags*/, - krb5_kvno /*kvno*/, - hdb_entry_ex */*entry*/); - -hdb_master_key -_hdb_find_master_key ( - unsigned int */*mkvno*/, - hdb_master_key /*mkey*/); - -krb5_error_code -_hdb_keytab2hdb_entry ( - krb5_context /*context*/, - const krb5_keytab_entry */*ktentry*/, - hdb_entry_ex */*entry*/); - -krb5_error_code -_hdb_mdb_value2entry ( - krb5_context /*context*/, - krb5_data */*data*/, - krb5_kvno /*target_kvno*/, - hdb_entry */*entry*/); - -int -_hdb_mit_dump2mitdb_entry ( - krb5_context /*context*/, - char */*line*/, - krb5_storage */*sp*/); - -int -_hdb_mkey_decrypt ( - krb5_context /*context*/, - hdb_master_key /*key*/, - krb5_key_usage /*usage*/, - void */*ptr*/, - size_t /*size*/, - krb5_data */*res*/); - -int -_hdb_mkey_encrypt ( - krb5_context /*context*/, - hdb_master_key /*key*/, - krb5_key_usage /*usage*/, - const void */*ptr*/, - size_t /*size*/, - krb5_data */*res*/); - -int -_hdb_mkey_version (hdb_master_key /*mkey*/); - -krb5_error_code -_hdb_remove ( - krb5_context /*context*/, - HDB */*db*/, - unsigned /*flags*/, - krb5_const_principal /*principal*/); - -krb5_error_code -_hdb_set_master_key_usage ( - krb5_context /*context*/, - HDB */*db*/, - unsigned int /*key_usage*/); - -krb5_error_code -_hdb_store ( - krb5_context /*context*/, - HDB */*db*/, - unsigned /*flags*/, - hdb_entry_ex */*entry*/); - -#endif /* __hdb_private_h__ */ diff --git a/lib/hdb/hdb-protos.h b/lib/hdb/hdb-protos.h deleted file mode 100644 index 76855095beed..000000000000 --- a/lib/hdb/hdb-protos.h +++ /dev/null @@ -1,592 +0,0 @@ -/* This is a generated file */ -#ifndef __hdb_protos_h__ -#define __hdb_protos_h__ -#ifndef DOXY - -#include <stdarg.h> - -#ifdef __cplusplus -extern "C" { -#endif - -krb5_error_code -entry2mit_string_int ( - krb5_context /*context*/, - krb5_storage */*sp*/, - hdb_entry */*ent*/); - -/** - * This function adds an HDB entry's current keyset to the entry's key - * history. The current keyset is left alone; the caller is responsible - * for freeing it. - * - * @param context Context - * @param entry HDB entry - */ - -krb5_error_code -hdb_add_current_keys_to_history ( - krb5_context /*context*/, - hdb_entry */*entry*/); - -/** - * This function adds a key to an HDB entry's key history. - * - * @param context Context - * @param entry HDB entry - * @param kvno Key version number of the key to add to the history - * @param key The Key to add - */ - -krb5_error_code -hdb_add_history_key ( - krb5_context /*context*/, - hdb_entry */*entry*/, - krb5_kvno /*kvno*/, - Key */*key*/); - -krb5_error_code -hdb_add_master_key ( - krb5_context /*context*/, - krb5_keyblock */*key*/, - hdb_master_key */*inout*/); - -/** - * This function changes an hdb_entry's kvno, swapping the current key - * set with a historical keyset. If no historical keys are found then - * an error is returned (the caller can still set entry->kvno directly). - * - * @param context krb5_context - * @param new_kvno New kvno for the entry - * @param entry hdb_entry to modify - */ - -krb5_error_code -hdb_change_kvno ( - krb5_context /*context*/, - krb5_kvno /*new_kvno*/, - hdb_entry */*entry*/); - -krb5_error_code -hdb_check_db_format ( - krb5_context /*context*/, - HDB */*db*/); - -krb5_error_code -hdb_clear_extension ( - krb5_context /*context*/, - hdb_entry */*entry*/, - int /*type*/); - -krb5_error_code -hdb_clear_master_key ( - krb5_context /*context*/, - HDB */*db*/); - -/** - * Create a handle for a Kerberos database - * - * Create a handle for a Kerberos database backend specified by a - * filename. Doesn't create a file if its doesn't exists, you have to - * use O_CREAT to tell the backend to create the file. - */ - -krb5_error_code -hdb_create ( - krb5_context /*context*/, - HDB **/*db*/, - const char */*filename*/); - -krb5_error_code -hdb_db1_create ( - krb5_context /*context*/, - HDB **/*db*/, - const char */*filename*/); - -krb5_error_code -hdb_db3_create ( - krb5_context /*context*/, - HDB **/*db*/, - const char */*filename*/); - -/** - * Return the directory where the hdb database resides. - * - * @param context Kerberos 5 context. - * - * @return string pointing to directory. - */ - -const char * -hdb_db_dir (krb5_context /*context*/); - -const char * -hdb_dbinfo_get_acl_file ( - krb5_context /*context*/, - struct hdb_dbinfo */*dbp*/); - -const krb5_config_binding * -hdb_dbinfo_get_binding ( - krb5_context /*context*/, - struct hdb_dbinfo */*dbp*/); - -const char * -hdb_dbinfo_get_dbname ( - krb5_context /*context*/, - struct hdb_dbinfo */*dbp*/); - -const char * -hdb_dbinfo_get_label ( - krb5_context /*context*/, - struct hdb_dbinfo */*dbp*/); - -const char * -hdb_dbinfo_get_log_file ( - krb5_context /*context*/, - struct hdb_dbinfo */*dbp*/); - -const char * -hdb_dbinfo_get_mkey_file ( - krb5_context /*context*/, - struct hdb_dbinfo */*dbp*/); - -struct hdb_dbinfo * -hdb_dbinfo_get_next ( - struct hdb_dbinfo */*dbp*/, - struct hdb_dbinfo */*dbprevp*/); - -const char * -hdb_dbinfo_get_realm ( - krb5_context /*context*/, - struct hdb_dbinfo */*dbp*/); - -/** - * Return the default hdb database resides. - * - * @param context Kerberos 5 context. - * - * @return string pointing to directory. - */ - -const char * -hdb_default_db (krb5_context /*context*/); - -krb5_error_code -hdb_enctype2key ( - krb5_context /*context*/, - hdb_entry */*e*/, - const Keys */*keyset*/, - krb5_enctype /*enctype*/, - Key **/*key*/); - -krb5_error_code -hdb_entry2string ( - krb5_context /*context*/, - hdb_entry */*ent*/, - char **/*str*/); - -int -hdb_entry2value ( - krb5_context /*context*/, - const hdb_entry */*ent*/, - krb5_data */*value*/); - -int -hdb_entry_alias2value ( - krb5_context /*context*/, - const hdb_entry_alias */*alias*/, - krb5_data */*value*/); - -krb5_error_code -hdb_entry_check_mandatory ( - krb5_context /*context*/, - const hdb_entry */*ent*/); - -krb5_error_code -hdb_entry_clear_kvno_diff_clnt ( - krb5_context /*context*/, - hdb_entry */*entry*/); - -krb5_error_code -hdb_entry_clear_kvno_diff_svc ( - krb5_context /*context*/, - hdb_entry */*entry*/); - -int -hdb_entry_clear_password ( - krb5_context /*context*/, - hdb_entry */*entry*/); - -krb5_error_code -hdb_entry_get_ConstrainedDelegACL ( - const hdb_entry */*entry*/, - const HDB_Ext_Constrained_delegation_acl **/*a*/); - -krb5_error_code -hdb_entry_get_aliases ( - const hdb_entry */*entry*/, - const HDB_Ext_Aliases **/*a*/); - -unsigned int -hdb_entry_get_kvno_diff_clnt (const hdb_entry */*entry*/); - -unsigned int -hdb_entry_get_kvno_diff_svc (const hdb_entry */*entry*/); - -int -hdb_entry_get_password ( - krb5_context /*context*/, - HDB */*db*/, - const hdb_entry */*entry*/, - char **/*p*/); - -krb5_error_code -hdb_entry_get_pkinit_acl ( - const hdb_entry */*entry*/, - const HDB_Ext_PKINIT_acl **/*a*/); - -krb5_error_code -hdb_entry_get_pkinit_cert ( - const hdb_entry */*entry*/, - const HDB_Ext_PKINIT_cert **/*a*/); - -krb5_error_code -hdb_entry_get_pkinit_hash ( - const hdb_entry */*entry*/, - const HDB_Ext_PKINIT_hash **/*a*/); - -krb5_error_code -hdb_entry_get_pw_change_time ( - const hdb_entry */*entry*/, - time_t */*t*/); - -krb5_error_code -hdb_entry_set_kvno_diff_clnt ( - krb5_context /*context*/, - hdb_entry */*entry*/, - unsigned int /*diff*/); - -krb5_error_code -hdb_entry_set_kvno_diff_svc ( - krb5_context /*context*/, - hdb_entry */*entry*/, - unsigned int /*diff*/); - -int -hdb_entry_set_password ( - krb5_context /*context*/, - HDB */*db*/, - hdb_entry */*entry*/, - const char */*p*/); - -krb5_error_code -hdb_entry_set_pw_change_time ( - krb5_context /*context*/, - hdb_entry */*entry*/, - time_t /*t*/); - -HDB_extension * -hdb_find_extension ( - const hdb_entry */*entry*/, - int /*type*/); - -krb5_error_code -hdb_foreach ( - krb5_context /*context*/, - HDB */*db*/, - unsigned /*flags*/, - hdb_foreach_func_t /*func*/, - void */*data*/); - -void -hdb_free_dbinfo ( - krb5_context /*context*/, - struct hdb_dbinfo **/*dbp*/); - -void -hdb_free_entry ( - krb5_context /*context*/, - hdb_entry_ex */*ent*/); - -void -hdb_free_key (Key */*key*/); - -void -hdb_free_keys ( - krb5_context /*context*/, - int /*len*/, - Key */*keys*/); - -void -hdb_free_master_key ( - krb5_context /*context*/, - hdb_master_key /*mkey*/); - -krb5_error_code -hdb_generate_key_set ( - krb5_context /*context*/, - krb5_principal /*principal*/, - krb5_key_salt_tuple */*ks_tuple*/, - int /*n_ks_tuple*/, - Key **/*ret_key_set*/, - size_t */*nkeyset*/, - int /*no_salt*/); - -krb5_error_code -hdb_generate_key_set_password ( - krb5_context /*context*/, - krb5_principal /*principal*/, - const char */*password*/, - Key **/*keys*/, - size_t */*num_keys*/); - -krb5_error_code -hdb_generate_key_set_password_with_ks_tuple ( - krb5_context /*context*/, - krb5_principal /*principal*/, - const char */*password*/, - krb5_key_salt_tuple */*ks_tuple*/, - int /*n_ks_tuple*/, - Key **/*keys*/, - size_t */*num_keys*/); - -int -hdb_get_dbinfo ( - krb5_context /*context*/, - struct hdb_dbinfo **/*dbp*/); - -krb5_error_code -hdb_init_db ( - krb5_context /*context*/, - HDB */*db*/); - -int -hdb_key2principal ( - krb5_context /*context*/, - krb5_data */*key*/, - krb5_principal /*p*/); - -krb5_error_code -hdb_keytab_create ( - krb5_context /*context*/, - HDB ** /*db*/, - const char */*arg*/); - -const Keys * -hdb_kvno2keys ( - krb5_context /*context*/, - const hdb_entry */*e*/, - krb5_kvno /*kvno*/); - -krb5_error_code -hdb_ldap_create ( - krb5_context /*context*/, - HDB ** /*db*/, - const char */*arg*/); - -krb5_error_code -hdb_ldapi_create ( - krb5_context /*context*/, - HDB ** /*db*/, - const char */*arg*/); - -krb5_error_code -hdb_list_builtin ( - krb5_context /*context*/, - char **/*list*/); - -krb5_error_code -hdb_lock ( - int /*fd*/, - int /*operation*/); - -krb5_error_code -hdb_mdb_create ( - krb5_context /*context*/, - HDB **/*db*/, - const char */*filename*/); - -krb5_error_code -hdb_mitdb_create ( - krb5_context /*context*/, - HDB **/*db*/, - const char */*filename*/); - -krb5_error_code -hdb_ndbm_create ( - krb5_context /*context*/, - HDB **/*db*/, - const char */*filename*/); - -krb5_error_code -hdb_next_enctype2key ( - krb5_context /*context*/, - const hdb_entry */*e*/, - const Keys */*keyset*/, - krb5_enctype /*enctype*/, - Key **/*key*/); - -int -hdb_principal2key ( - krb5_context /*context*/, - krb5_const_principal /*p*/, - krb5_data */*key*/); - -krb5_error_code -hdb_print_entry ( - krb5_context /*context*/, - HDB */*db*/, - hdb_entry_ex */*entry*/, - void */*data*/); - -krb5_error_code -hdb_process_master_key ( - krb5_context /*context*/, - int /*kvno*/, - krb5_keyblock */*key*/, - krb5_enctype /*etype*/, - hdb_master_key */*mkey*/); - -/** - * This function prunes an HDB entry's keys that are too old to have been used - * to mint still valid tickets (based on the entry's maximum ticket lifetime). - * - * @param context Context - * @param entry HDB entry - */ - -krb5_error_code -hdb_prune_keys ( - krb5_context /*context*/, - hdb_entry */*entry*/); - -krb5_error_code -hdb_read_master_key ( - krb5_context /*context*/, - const char */*filename*/, - hdb_master_key */*mkey*/); - -krb5_error_code -hdb_replace_extension ( - krb5_context /*context*/, - hdb_entry */*entry*/, - const HDB_extension */*ext*/); - -krb5_error_code -hdb_seal_key ( - krb5_context /*context*/, - HDB */*db*/, - Key */*k*/); - -krb5_error_code -hdb_seal_key_mkey ( - krb5_context /*context*/, - Key */*k*/, - hdb_master_key /*mkey*/); - -krb5_error_code -hdb_seal_keys ( - krb5_context /*context*/, - HDB */*db*/, - hdb_entry */*ent*/); - -krb5_error_code -hdb_seal_keys_mkey ( - krb5_context /*context*/, - hdb_entry */*ent*/, - hdb_master_key /*mkey*/); - -krb5_error_code -hdb_set_last_modified_by ( - krb5_context /*context*/, - hdb_entry */*entry*/, - krb5_principal /*modby*/, - time_t /*modtime*/); - -krb5_error_code -hdb_set_master_key ( - krb5_context /*context*/, - HDB */*db*/, - krb5_keyblock */*key*/); - -krb5_error_code -hdb_set_master_keyfile ( - krb5_context /*context*/, - HDB */*db*/, - const char */*keyfile*/); - -/** - * Create SQLITE object, and creates the on disk database if its doesn't exists. - * - * @param context A Kerberos 5 context. - * @param db a returned database handle. - * @param filename filename - * - * @return 0 on success, an error code if not - */ - -krb5_error_code -hdb_sqlite_create ( - krb5_context /*context*/, - HDB **/*db*/, - const char */*filename*/); - -krb5_error_code -hdb_unlock (int /*fd*/); - -krb5_error_code -hdb_unseal_key ( - krb5_context /*context*/, - HDB */*db*/, - Key */*k*/); - -krb5_error_code -hdb_unseal_key_mkey ( - krb5_context /*context*/, - Key */*k*/, - hdb_master_key /*mkey*/); - -krb5_error_code -hdb_unseal_keys ( - krb5_context /*context*/, - HDB */*db*/, - hdb_entry */*ent*/); - -krb5_error_code -hdb_unseal_keys_kvno ( - krb5_context /*context*/, - HDB */*db*/, - krb5_kvno /*kvno*/, - unsigned /*flags*/, - hdb_entry */*ent*/); - -krb5_error_code -hdb_unseal_keys_mkey ( - krb5_context /*context*/, - hdb_entry */*ent*/, - hdb_master_key /*mkey*/); - -int -hdb_value2entry ( - krb5_context /*context*/, - krb5_data */*value*/, - hdb_entry */*ent*/); - -int -hdb_value2entry_alias ( - krb5_context /*context*/, - krb5_data */*value*/, - hdb_entry_alias */*ent*/); - -krb5_error_code -hdb_write_master_key ( - krb5_context /*context*/, - const char */*filename*/, - hdb_master_key /*mkey*/); - -#ifdef __cplusplus -} -#endif - -#endif /* DOXY */ -#endif /* __hdb_protos_h__ */ diff --git a/lib/hdb/hdb-sqlite.c b/lib/hdb/hdb-sqlite.c index d5eb3f184e84..4bb2f8e8553c 100644 --- a/lib/hdb/hdb-sqlite.c +++ b/lib/hdb/hdb-sqlite.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Kungliga Tekniska Högskolan + * Copyright (c) 2009 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -41,6 +41,7 @@ typedef struct hdb_sqlite_db { sqlite3 *db; char *db_file; + sqlite3_stmt *connect; sqlite3_stmt *get_version; sqlite3_stmt *fetch; sqlite3_stmt *get_ids; @@ -82,6 +83,8 @@ typedef struct hdb_sqlite_db { " DELETE FROM Principal" \ " WHERE entry = OLD.id;" \ " END" +#define HDBSQLITE_CONNECT \ + " PRAGMA journal_mode = WAL" #define HDBSQLITE_GET_VERSION \ " SELECT number FROM Version" #define HDBSQLITE_FETCH \ @@ -156,6 +159,11 @@ prep_stmts(krb5_context context, hdb_sqlite_db *hsdb) int ret; ret = hdb_sqlite_prepare_stmt(context, hsdb->db, + &hsdb->connect, + HDBSQLITE_CONNECT); + if (ret) + return ret; + ret = hdb_sqlite_prepare_stmt(context, hsdb->db, &hsdb->get_version, HDBSQLITE_GET_VERSION); if (ret) @@ -209,6 +217,10 @@ prep_stmts(krb5_context context, hdb_sqlite_db *hsdb) static void finalize_stmts(krb5_context context, hdb_sqlite_db *hsdb) { + if (hsdb->connect != NULL) + sqlite3_finalize(hsdb->connect); + hsdb->connect = NULL; + if (hsdb->get_version != NULL) sqlite3_finalize(hsdb->get_version); hsdb->get_version = NULL; @@ -316,6 +328,8 @@ bind_principal(krb5_context context, krb5_const_principal principal, sqlite3_stm return 0; } +static int hdb_sqlite_step(krb5_context, sqlite3 *, sqlite3_stmt *); + /** * Opens an sqlite3 database handle to a file, may create the * database file depending on flags. @@ -346,7 +360,6 @@ hdb_sqlite_open_database(krb5_context context, HDB *db, int flags) ret = krb5_enomem(context); return ret; } - return 0; } @@ -436,6 +449,10 @@ hdb_sqlite_make_database(krb5_context context, HDB *db, const char *filename) ret = prep_stmts(context, hsdb); if (ret) goto out; + sqlite3_reset(hsdb->connect); + (void) hdb_sqlite_step(context, hsdb->db, hsdb->connect); + sqlite3_reset(hsdb->connect); + ret = hdb_sqlite_step(context, hsdb->db, hsdb->get_version); if(ret == SQLITE_ROW) { hsdb->version = sqlite3_column_double(hsdb->get_version, 0); @@ -478,7 +495,7 @@ hdb_sqlite_make_database(krb5_context context, HDB *db, const char *filename) */ static krb5_error_code hdb_sqlite_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, - unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry) + unsigned flags, krb5_kvno kvno, hdb_entry *entry) { int sqlite_error; krb5_error_code ret; @@ -524,14 +541,14 @@ hdb_sqlite_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal princi value.length = sqlite3_column_bytes(fetch, 0); value.data = (void *) sqlite3_column_blob(fetch, 0); - ret = hdb_value2entry(context, &value, &entry->entry); + ret = hdb_value2entry(context, &value, entry); if(ret) goto out; if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { - ret = hdb_unseal_keys(context, db, &entry->entry); + ret = hdb_unseal_keys(context, db, entry); if(ret) { - hdb_free_entry(context, entry); + hdb_free_entry(context, db, entry); goto out; } } @@ -583,7 +600,7 @@ hdb_sqlite_step_once(krb5_context context, HDB *db, sqlite3_stmt *statement) */ static krb5_error_code hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags, - hdb_entry_ex *entry) + hdb_entry *entry) { int ret; int i; @@ -607,17 +624,17 @@ hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags, goto rollback; } - ret = hdb_seal_keys(context, db, &entry->entry); + ret = hdb_seal_keys(context, db, entry); if(ret) { goto rollback; } - ret = hdb_entry2value(context, &entry->entry, &value); + ret = hdb_entry2value(context, entry, &value); if(ret) { goto rollback; } - ret = bind_principal(context, entry->entry.principal, get_ids, 1); + ret = bind_principal(context, entry->principal, get_ids, 1); if (ret) goto rollback; @@ -639,7 +656,7 @@ hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags, goto rollback; } - ret = bind_principal(context, entry->entry.principal, hsdb->add_principal, 1); + ret = bind_principal(context, entry->principal, hsdb->add_principal, 1); if (ret) goto rollback; @@ -667,8 +684,10 @@ hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags, } else if(ret == SQLITE_ROW) { /* Found a principal */ - if(! (flags & HDB_F_REPLACE)) /* Not allowed to replace it */ + if(!(flags & HDB_F_REPLACE)) { + ret = HDB_ERR_EXISTS; goto rollback; + } entry_id = sqlite3_column_int64(get_ids, 1); @@ -694,7 +713,7 @@ hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags, goto rollback; } - ret = hdb_entry_get_aliases(&entry->entry, &aliases); + ret = hdb_entry_get_aliases(entry, &aliases); if(ret || aliases == NULL) goto commit; @@ -800,7 +819,9 @@ hdb_sqlite_destroy(krb5_context context, HDB *db) hsdb = (hdb_sqlite_db*)(db->hdb_db); + krb5_config_free_strings(db->virtual_hostbased_princ_svcs); free(hsdb->db_file); + free(db->hdb_name); free(db->hdb_db); free(db); @@ -843,7 +864,7 @@ hdb_sqlite_unlock(krb5_context context, HDB *db) */ static krb5_error_code hdb_sqlite_nextkey(krb5_context context, HDB *db, unsigned flags, - hdb_entry_ex *entry) + hdb_entry *entry) { krb5_error_code ret = 0; int sqlite_error; @@ -857,7 +878,7 @@ hdb_sqlite_nextkey(krb5_context context, HDB *db, unsigned flags, value.length = sqlite3_column_bytes(hsdb->get_all_entries, 0); value.data = (void *) sqlite3_column_blob(hsdb->get_all_entries, 0); memset(entry, 0, sizeof(*entry)); - ret = hdb_value2entry(context, &value, &entry->entry); + ret = hdb_value2entry(context, &value, entry); } else if(sqlite_error == SQLITE_DONE) { /* No more entries */ @@ -881,7 +902,7 @@ hdb_sqlite_nextkey(krb5_context context, HDB *db, unsigned flags, */ static krb5_error_code hdb_sqlite_firstkey(krb5_context context, HDB *db, unsigned flags, - hdb_entry_ex *entry) + hdb_entry *entry) { hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; krb5_error_code ret; @@ -931,11 +952,12 @@ hdb_sqlite_remove(krb5_context context, HDB *db, sqlite3_stmt *get_ids = hsdb->get_ids; sqlite3_stmt *rm = hsdb->remove; - bind_principal(context, principal, rm, 1); + ret = bind_principal(context, principal, rm, 1); - ret = hdb_sqlite_exec_stmt(context, hsdb, - "BEGIN IMMEDIATE TRANSACTION", - HDB_ERR_UK_SERROR); + if (ret == 0) + ret = hdb_sqlite_exec_stmt(context, hsdb, + "BEGIN IMMEDIATE TRANSACTION", + HDB_ERR_UK_SERROR); if (ret != SQLITE_OK) { ret = HDB_ERR_UK_SERROR; (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0); @@ -1024,13 +1046,13 @@ hdb_sqlite_create(krb5_context context, HDB **db, const char *filename) if (ret) { free((*db)->hdb_db); free(*db); - + *db = NULL; return ret; } (*db)->hdb_master_key_set = 0; (*db)->hdb_openp = 0; - (*db)->hdb_capability_flags = 0; + (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL; (*db)->hdb_open = hdb_sqlite_open; (*db)->hdb_close = hdb_sqlite_close; diff --git a/lib/hdb/hdb.asn1 b/lib/hdb/hdb.asn1 index 333ccb064196..49318c811336 100644 --- a/lib/hdb/hdb.asn1 +++ b/lib/hdb/hdb.asn1 @@ -4,7 +4,7 @@ BEGIN IMPORTS EncryptionKey, KerberosTime, Principal FROM krb5; -HDB_DB_FORMAT INTEGER ::= 2 -- format of database, +hdb_db_format INTEGER ::= 2 -- format of database, -- update when making changes -- these must have the same value as the pa-* counterparts @@ -49,6 +49,19 @@ HDBFlags ::= BIT STRING { locked-out(17), -- Account is locked out, -- authentication will be denied require-pwchange(18), -- require a passwd change + + materialize(19), -- store even if within virtual namespace + virtual-keys(20), -- entry stored; keys mostly derived + virtual(21), -- entry not stored; keys always derived + synthetic(22), -- entry not stored; for PKINIT + no-auth-data-reqd(23), -- omit PAC from service tickets + auth-data-reqd(24), -- include PAC in service tickets + + force-canonicalize(30), -- force the KDC to return the canonical + -- principal irrespective of the setting + -- of the canonicalize KDC option + -- (principals cannot have this flag + -- set when stored into the HDB) do-not-store(31) -- Not to be modified and stored in HDB } @@ -91,15 +104,93 @@ HDB-Ext-Aliases ::= SEQUENCE { Keys ::= SEQUENCE OF Key -hdb_keyset ::= SEQUENCE { +HDB_keyset ::= SEQUENCE { kvno[0] INTEGER (0..4294967295), keys[1] Keys, set-time[2] KerberosTime OPTIONAL, -- time this keyset was created/set ... } -HDB-Ext-KeySet ::= SEQUENCE OF hdb_keyset +HDB-Ext-KeySet ::= SEQUENCE OF HDB_keyset + +-- +-- We need a function of current (or given, but it will always be current) time +-- and a base hdb_entry or its HDB-Ext-KeyRotation and service ticket lifetime, +-- that outputs a sequence of {kvno, set_time, max_life} representing past keys +-- (up to one per past and current KeyRotation), current keys (for the current +-- KeyRotation), up to one future key for the current KeyRotation, and up to +-- one future key for the _next_ (future) KeyRotation if there is one. +-- +-- We have to impose constraints on new KeyRotation elements of +-- HDB-Ext-KeyRotation. +-- +-- So virtual keysets (keytabs) will contain: +-- +-- - up to one past keyset for all KeyRotation periods that are "applicable" +-- - the current keyset for all KeyRotation periods that are "applicable" +-- - up to one future keyset for all KeyRotation periods that are "applicable" +-- +-- An applicable KeyRotation period is: +-- +-- - the KeyRotation whose `epoch` is a) in the past and b) nearest to the +-- current time - we call this the current KeyRotation +-- - a KeyRotation whose `epoch` is nearest but in the past of the current +-- one +-- - a KeyRotation whose `epoch` is nearest but in the future of the current +-- one +-- +-- A service principal's max ticket life will be bounded by half the current +-- key rotation period. +-- +-- Note: There can be more than one applicable past KeyRotation, and more than +-- one applicable KeyRotation. We might not want to permit this. +-- However, it's probably easier to permit it, though we might not test +-- end-to-end. +-- +-- Testing: +-- +-- - We should have standalone unit tests for all these pure functions. +-- +-- - We should have a test that uses kadm5 and GSS to test against a KDC using +-- small key rotation periods on the order of seconds, with back-off in case +-- of losing a race condition. +-- +KeyRotationFlags ::= BIT STRING { + deleted(0), -- if set on a materialized principal, this will mean + -- the principal does not exist + -- if set on a namespace, this will mean that + -- only materialized principal below it exist + parent(1) -- if set on a materialized principal, this will mean + -- that the keys for kvnos in this KeyRotation spec + -- will be derived from the parent's base keys and + -- corresponding KeyRotation spec + -- if set on a namespace, this flag will be ignored + -- (or we could support nested namespaces?) +} +KeyRotation ::= SEQUENCE { + -- base-kvno is always computed at set time and set for the principal, + -- and is never subject to admin choice. The base-kvno is that of the + -- current kvno at that period's `from` given the previous period. + -- + -- Also, insertion of KeyRotation elements before existing ones (in + -- time) is never permitted, and all new KeyRotation elements must be + -- in the future relative to existing ones. + -- + -- HDB-Ext-KeyRotation will always be sorted (as stored) by `from`, in + -- descending order. + -- + -- Max service ticket lifetime will be constrained to no more than half + -- the period of the the applicable KeyRotation elements. + -- + flags[0] KeyRotationFlags, + epoch[1] KerberosTime, -- start of this period + period[2] INTEGER(0..4294967295), -- key rotation seconds + base-kvno[3] INTEGER(0..4294967295), -- starting from this kvno + base-key-kvno[4]INTEGER(0..4294967295), -- kvno of base-key + ... +} +HDB-Ext-KeyRotation ::= SEQUENCE SIZE (1..3) OF KeyRotation HDB-extension ::= SEQUENCE { mandatory[0] BOOLEAN, -- kdc MUST understand this extension, @@ -108,7 +199,7 @@ HDB-extension ::= SEQUENCE { 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, + allowed-to-delegate-to[2] HDB-Ext-Constrained-delegation-acl, -- referral-info[3] HDB-Ext-Referrals, lm-owf[4] HDB-Ext-Lan-Manager-OWF, password[5] HDB-Ext-Password, @@ -120,6 +211,8 @@ HDB-extension ::= SEQUENCE { hist-kvno-diff-svc[11] INTEGER (0..4294967295), policy[12] UTF8String, principal-id[13] INTEGER(-9223372036854775808..9223372036854775807), + key-rotation[14] HDB-Ext-KeyRotation, + krb5-config[15] OCTET STRING, ... }, ... @@ -127,7 +220,10 @@ HDB-extension ::= SEQUENCE { HDB-extensions ::= SEQUENCE OF HDB-extension -hdb_entry ::= SEQUENCE { +-- Just for convenience, for encoding this as TL data in lib/kadm5 +HDB-EncTypeList ::= SEQUENCE OF INTEGER (0..4294967295) + +HDB_entry ::= SEQUENCE { principal[0] Principal OPTIONAL, -- this is optional only -- for compatibility with libkrb5 kvno[1] INTEGER (0..4294967295), @@ -140,13 +236,18 @@ hdb_entry ::= SEQUENCE { max-life[8] INTEGER (0..4294967295) OPTIONAL, max-renew[9] INTEGER (0..4294967295) OPTIONAL, flags[10] HDBFlags, - etypes[11] SEQUENCE OF INTEGER (0..4294967295) OPTIONAL, + etypes[11] HDB-EncTypeList OPTIONAL, generation[12] GENERATION OPTIONAL, extensions[13] HDB-extensions OPTIONAL } -hdb_entry_alias ::= [APPLICATION 0] SEQUENCE { +HDB_entry_alias ::= [APPLICATION 0] SEQUENCE { principal[0] Principal OPTIONAL } +HDB-EntryOrAlias ::= CHOICE { + entry HDB_entry, + alias HDB_entry_alias +} + END diff --git a/lib/hdb/hdb.c b/lib/hdb/hdb.c index 1cb33df0885c..9947254b18ec 100644 --- a/lib/hdb/hdb.c +++ b/lib/hdb/hdb.c @@ -66,59 +66,53 @@ const int hdb_interface_version = HDB_INTERFACE_VERSION; static struct hdb_method methods[] = { /* "db:" should be db3 if we have db3, or db1 if we have db1 */ #if HAVE_DB3 - { HDB_INTERFACE_VERSION, NULL, NULL, "db:", hdb_db3_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 1 /*is_file_based*/, 1 /*can_taste*/, + "db:", hdb_db3_create}, #elif HAVE_DB1 - { HDB_INTERFACE_VERSION, NULL, NULL, "db:", hdb_db1_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "db:", hdb_db1_create}, #endif #if HAVE_DB1 - { HDB_INTERFACE_VERSION, NULL, NULL, "db1:", hdb_db1_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "db1:", hdb_db1_create}, #endif #if HAVE_DB3 - { HDB_INTERFACE_VERSION, NULL, NULL, "db3:", hdb_db3_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "db3:", hdb_db3_create}, #endif #if HAVE_DB1 - { HDB_INTERFACE_VERSION, NULL, NULL, "mit-db:", hdb_mitdb_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "mit-db:", hdb_mitdb_create}, #endif #if HAVE_LMDB - { HDB_INTERFACE_VERSION, NULL, NULL, "mdb:", hdb_mdb_create}, - { HDB_INTERFACE_VERSION, NULL, NULL, "lmdb:", hdb_mdb_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "mdb:", hdb_mdb_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "lmdb:", hdb_mdb_create}, #endif #if HAVE_NDBM - { HDB_INTERFACE_VERSION, NULL, NULL, "ndbm:", hdb_ndbm_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 0, "ndbm:", hdb_ndbm_create}, #endif - { HDB_INTERFACE_VERSION, NULL, NULL, "keytab:", hdb_keytab_create}, +#ifdef HAVE_SQLITE3 + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "sqlite:", hdb_sqlite_create}, +#endif + /* The keytab interface can't use its hdb_open() method to "taste" a DB */ + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 0, "keytab:", hdb_keytab_create}, + /* The rest are not file-based */ #if defined(OPENLDAP) && !defined(OPENLDAP_MODULE) - { HDB_INTERFACE_VERSION, NULL, NULL, "ldap:", hdb_ldap_create}, - { HDB_INTERFACE_VERSION, NULL, NULL, "ldapi:", hdb_ldapi_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldap:", hdb_ldap_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldapi:", hdb_ldapi_create}, #elif defined(OPENLDAP) - { HDB_INTERFACE_VERSION, NULL, NULL, "ldap:", NULL}, - { HDB_INTERFACE_VERSION, NULL, NULL, "ldapi:", NULL}, + { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldap:", NULL}, + { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldapi:", NULL}, #endif -#ifdef HAVE_SQLITE3 - { HDB_INTERFACE_VERSION, NULL, NULL, "sqlite:", hdb_sqlite_create}, -#endif - { 0, NULL, NULL, NULL, NULL} + { 0, NULL, NULL, 0, 0, NULL, NULL} }; -/* - * It'd be nice if we could try opening an HDB with each supported - * backend until one works or all fail. It may not be possible for all - * flavors, but where it's possible we should. +/** + * Returns the Keys of `e' for `kvno', or NULL if not found. The Keys will + * remain valid provided that the entry is not mutated. + * + * @param context Context + * @param e The HDB entry + * @param kvno The kvno + * + * @return A pointer to the Keys for the requested kvno. */ -#if defined(HAVE_LMDB) -static struct hdb_method default_dbmethod = - { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_mdb_create }; -#elif defined(HAVE_DB3) -static struct hdb_method default_dbmethod = - { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_db3_create }; -#elif defined(HAVE_DB1) -static struct hdb_method default_dbmethod = - { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_db1_create }; -#elif defined(HAVE_NDBM) -static struct hdb_method default_dbmethod = - { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_ndbm_create }; -#endif - const Keys * hdb_kvno2keys(krb5_context context, const hdb_entry *e, @@ -128,7 +122,7 @@ hdb_kvno2keys(krb5_context context, HDB_extension *extp; size_t i; - if (kvno == 0) + if (kvno == 0 || e->kvno == kvno) return &e->keys; extp = hdb_find_extension(e, choice_HDB_extension_data_hist_keys); @@ -144,6 +138,177 @@ hdb_kvno2keys(krb5_context context, return NULL; } +/* Based on remove_HDB_Ext_KeySet(), generated by the ASN.1 compiler */ +static int +dequeue_HDB_Ext_KeySet(HDB_Ext_KeySet *data, unsigned int element, hdb_keyset *ks) +{ + if (element >= data->len) { + ks->kvno = 0; + ks->keys.len = 0; + ks->keys.val = 0; + ks->set_time = 0; + return ASN1_OVERRUN; + } + *ks = data->val[element]; + data->len--; + /* Swap instead of memmove()... changes the order of elements */ + if (element < data->len) + data->val[element] = data->val[data->len]; + if (data->len == 0) { + free(data->val); + data->val = 0; + } + return 0; +} + + +/** + * Removes from `e' and optionally outputs the keyset for the requested `kvno'. + * + * @param context Context + * @param e The HDB entry + * @param kvno The key version number + * @param ks A pointer to a variable of type hdb_keyset (may be NULL) + * + * @return Zero on success, an error code otherwise. + */ +krb5_error_code +hdb_remove_keys(krb5_context context, + hdb_entry *e, + krb5_kvno kvno, + hdb_keyset *ks) +{ + HDB_Ext_KeySet *hist_keys; + HDB_extension *extp; + size_t i; + + if (kvno == 0 || e->kvno == kvno) { + if (ks) { + KerberosTime t; + + (void) hdb_entry_get_pw_change_time(e, &t); + if (t) { + if ((ks->set_time = malloc(sizeof(*ks->set_time))) == NULL) + return krb5_enomem(context); + *ks->set_time = t; + } + ks->kvno = e->kvno; + ks->keys = e->keys; + e->keys.len = 0; + e->keys.val = NULL; + e->kvno = 0; + } else { + free_Keys(&e->keys); + } + return 0; + } + + if (ks) { + ks->kvno = 0; + ks->keys.len = 0; + ks->keys.val = 0; + ks->set_time = 0; + } + + extp = hdb_find_extension(e, choice_HDB_extension_data_hist_keys); + if (extp == NULL) + return 0; + + hist_keys = &extp->data.u.hist_keys; + for (i = 0; i < hist_keys->len; i++) { + if (hist_keys->val[i].kvno != kvno) + continue; + if (ks) + return dequeue_HDB_Ext_KeySet(hist_keys, i, ks); + return remove_HDB_Ext_KeySet(hist_keys, i); + } + return HDB_ERR_NOENTRY; +} + +/** + * Removes from `e' and outputs all the base keys for virtual principal and/or + * key derivation. + * + * @param context Context + * @param e The HDB entry + * @param ks A pointer to a variable of type HDB_Ext_KeySet + * @param ckr A pointer to stable (copied) HDB_Ext_KeyRotation + * + * @return Zero on success, an error code otherwise. + */ +krb5_error_code +_hdb_remove_base_keys(krb5_context context, + hdb_entry *e, + HDB_Ext_KeySet *base_keys, + const HDB_Ext_KeyRotation *ckr) +{ + krb5_error_code ret = 0; + size_t i, k; + + base_keys->len = 0; + if ((base_keys->val = calloc(ckr->len, sizeof(base_keys->val[0]))) == NULL) + ret = krb5_enomem(context); + + for (k = i = 0; ret == 0 && i < ckr->len; i++) { + const KeyRotation *krp = &ckr->val[i]; + + /* + * WARNING: O(N * M) where M is number of keysets and N is the number + * of base keysets. + * + * In practice N will never be > 3 because the ASN.1 module imposes + * that as a constraint, and M will generally be the same as N, so this + * will be O(1) after all. + */ + ret = hdb_remove_keys(context, e, krp->base_key_kvno, + &base_keys->val[k]); + if (ret == 0) + k++; + else if (ret == HDB_ERR_NOENTRY) + ret = 0; + } + if (ret == 0) + base_keys->len = k; + else + free_HDB_Ext_KeySet(base_keys); + return 0; +} + +/** + * Removes from `e' and outputs all the base keys for virtual principal and/or + * key derivation. + * + * @param context Context + * @param e The HDB entry + * @param is_current_keyset Whether to make the keys the current keys for `e' + * @param ks A pointer to an hdb_keyset containing the keys to set + * + * @return Zero on success, an error code otherwise. + */ +krb5_error_code +hdb_install_keyset(krb5_context context, + hdb_entry *e, + int is_current_keyset, + const hdb_keyset *ks) +{ + krb5_error_code ret = 0; + + if (is_current_keyset) { + if (e->keys.len && + (ret = hdb_add_current_keys_to_history(context, e))) + return ret; + free_Keys(&e->keys); + e->kvno = ks->kvno; + if (ret == 0) + ret = copy_Keys(&ks->keys, &e->keys); + if (ret == 0 && ks->set_time) + ret = hdb_entry_set_pw_change_time(context, e, *ks->set_time); + return ret; + } + return hdb_add_history_keyset(context, e, ks); +} + + krb5_error_code hdb_next_enctype2key(krb5_context context, const hdb_entry *e, @@ -180,9 +345,10 @@ hdb_enctype2key(krb5_context context, void hdb_free_key(Key *key) { - memset(key->key.keyvalue.data, - 0, - key->key.keyvalue.length); + memset_s(key->key.keyvalue.data, + key->key.keyvalue.length, + 0, + key->key.keyvalue.length); free_Key(key); free(key); } @@ -217,20 +383,23 @@ hdb_unlock(int fd) } void -hdb_free_entry(krb5_context context, hdb_entry_ex *ent) +hdb_free_entry(krb5_context context, HDB *db, hdb_entry *ent) { Key *k; size_t i; - if (ent->free_entry) - (*ent->free_entry)(context, ent); + if (db && db->hdb_free_entry_context) + db->hdb_free_entry_context(context, db, ent); - for(i = 0; i < ent->entry.keys.len; i++) { - k = &ent->entry.keys.val[i]; + for(i = 0; i < ent->keys.len; i++) { + k = &ent->keys.val[i]; - memset (k->key.keyvalue.data, 0, k->key.keyvalue.length); + memset_s(k->key.keyvalue.data, + k->key.keyvalue.length, + 0, + k->key.keyvalue.length); } - free_hdb_entry(&ent->entry); + free_HDB_entry(ent); } krb5_error_code @@ -241,13 +410,13 @@ hdb_foreach(krb5_context context, void *data) { krb5_error_code ret; - hdb_entry_ex entry; + hdb_entry entry; ret = db->hdb_firstkey(context, db, flags, &entry); if (ret == 0) krb5_clear_error_message(context); while(ret == 0){ ret = (*func)(context, db, &entry, data); - hdb_free_entry(context, &entry); + hdb_free_entry(context, db, &entry); if(ret == 0) ret = db->hdb_nextkey(context, db, flags, &entry); } @@ -318,40 +487,70 @@ hdb_init_db(krb5_context context, HDB *db) } /* - * find the relevant method for `filename', returning a pointer to the - * rest in `rest'. - * return NULL if there's no such method. + * `default_dbmethod' is the last resort default. + * + * In hdb_create() we may try all the `methods[]' until one succeeds or all + * fail. */ +#if defined(HAVE_LMDB) +static struct hdb_method default_dbmethod = + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "", hdb_mdb_create }; +#elif defined(HAVE_DB3) +static struct hdb_method default_dbmethod = + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "", hdb_db3_create }; +#elif defined(HAVE_DB1) +static struct hdb_method default_dbmethod = + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "", hdb_db1_create }; +#elif defined(HAVE_NDBM) +static struct hdb_method default_dbmethod = + { HDB_INTERFACE_VERSION, NULL, NULL, 0, 1, "", hdb_ndbm_create }; +#else +static struct hdb_method default_dbmethod = + { 0, NULL, NULL, 0, 0, NULL, NULL}; +#endif + +static int +is_pathish(const char *s) +{ + if (s[0] == '/' || + strncmp(s, "./", sizeof("./") - 1) == 0 || + strncmp(s, "../", sizeof("../") - 1) == 0) + return 1; +#ifdef WIN32 + if (s[0] == '\\' || (isalpha((unsigned char)s[0]) && s[0] == ':') || + strncmp(s, ".\\", sizeof(".\\") - 1) == 0 || + strncmp(s, "\\\\", sizeof("\\\\") - 1) == 0) + return 1; +#endif + return 0; +} static const struct hdb_method * -find_method (const char *filename, const char **rest) +has_method_prefix(const char *filename) { const struct hdb_method *h; - for (h = methods; h->prefix != NULL; ++h) { - if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) { - *rest = filename + strlen(h->prefix); + for (h = methods; h->prefix != NULL; ++h) + if (strncmp(filename, h->prefix, strlen(h->prefix)) == 0) return h; - } - } -#if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_LMDB) || defined(HAVE_NDBM) - if (strncmp(filename, "/", sizeof("/") - 1) == 0 - || strncmp(filename, "./", sizeof("./") - 1) == 0 - || strncmp(filename, "../", sizeof("../") - 1) == 0 -#ifdef WIN32 - || strncmp(filename, "\\\\", sizeof("\\\\") - 1) - || (isalpha(filename[0]) && filename[1] == ':') -#endif - ) - { - *rest = filename; - return &default_dbmethod; - } -#endif - return NULL; } +/* + * find the relevant method for `filename', returning a pointer to the + * rest in `rest'. + * return NULL if there's no such method. + */ + +static const struct hdb_method * +find_method(const char *filename, const char **rest) +{ + const struct hdb_method *h = has_method_prefix(filename); + + *rest = h ? filename + strlen(h->prefix) : filename; + return h; +} + struct cb_s { const char *residual; const char *filename; @@ -390,6 +589,8 @@ make_sym(const char *prefix) return sym; } +static const char *hdb_plugin_deps[] = { "hdb", "krb5", NULL }; + krb5_error_code hdb_list_builtin(krb5_context context, char **list) { @@ -414,12 +615,17 @@ hdb_list_builtin(krb5_context context, char **list) if (h->create == NULL) { struct cb_s cb_ctx; char *f; - char *sym; + struct heim_plugin_data hdb_plugin_data; + + hdb_plugin_data.module = "krb5"; + hdb_plugin_data.min_version = HDB_INTERFACE_VERSION; + hdb_plugin_data.deps = hdb_plugin_deps; + hdb_plugin_data.get_instance = hdb_get_instance; /* Try loading the plugin */ if (asprintf(&f, "%sfoo", h->prefix) == -1) f = NULL; - if ((sym = make_sym(h->prefix)) == NULL) { + if ((hdb_plugin_data.name = make_sym(h->prefix)) == NULL) { free(buf); free(f); return krb5_enomem(context); @@ -427,11 +633,10 @@ hdb_list_builtin(krb5_context context, char **list) cb_ctx.filename = f; cb_ctx.residual = NULL; cb_ctx.h = NULL; - (void)_krb5_plugin_run_f(context, "krb5", sym, - HDB_INTERFACE_VERSION, 0, &cb_ctx, - callback); + (void)_krb5_plugin_run_f(context, &hdb_plugin_data, 0, + &cb_ctx, callback); free(f); - free(sym); + free(rk_UNCONST(hdb_plugin_data.name)); if (cb_ctx.h == NULL || cb_ctx.h->create == NULL) continue; } @@ -446,54 +651,198 @@ hdb_list_builtin(krb5_context context, char **list) krb5_error_code _hdb_keytab2hdb_entry(krb5_context context, const krb5_keytab_entry *ktentry, - hdb_entry_ex *entry) + hdb_entry *entry) { - entry->entry.kvno = ktentry->vno; - entry->entry.created_by.time = ktentry->timestamp; + entry->kvno = ktentry->vno; + entry->created_by.time = ktentry->timestamp; - entry->entry.keys.val = calloc(1, sizeof(entry->entry.keys.val[0])); - if (entry->entry.keys.val == NULL) + entry->keys.val = calloc(1, sizeof(entry->keys.val[0])); + if (entry->keys.val == NULL) return ENOMEM; - entry->entry.keys.len = 1; + entry->keys.len = 1; - entry->entry.keys.val[0].mkvno = NULL; - entry->entry.keys.val[0].salt = NULL; + entry->keys.val[0].mkvno = NULL; + entry->keys.val[0].salt = NULL; return krb5_copy_keyblock_contents(context, &ktentry->keyblock, - &entry->entry.keys.val[0].key); + &entry->keys.val[0].key); +} + +static krb5_error_code +load_config(krb5_context context, HDB *db) +{ + db->enable_virtual_hostbased_princs = + krb5_config_get_bool_default(context, NULL, FALSE, "hdb", + "enable_virtual_hostbased_princs", + NULL); + db->virtual_hostbased_princ_ndots = + krb5_config_get_int_default(context, NULL, 1, "hdb", + "virtual_hostbased_princ_mindots", + NULL); + db->virtual_hostbased_princ_maxdots = + krb5_config_get_int_default(context, NULL, 0, "hdb", + "virtual_hostbased_princ_maxdots", + NULL); + db->new_service_key_delay = + krb5_config_get_time_default(context, NULL, 0, "hdb", + "new_service_key_delay", NULL); + /* + * XXX Needs freeing in the HDB backends because we don't have a + * first-class hdb_close() :( + */ + db->virtual_hostbased_princ_svcs = + krb5_config_get_strings(context, NULL, "hdb", + "virtual_hostbased_princ_svcs", NULL); + /* Check for ENOMEM */ + if (db->virtual_hostbased_princ_svcs == NULL + && krb5_config_get_string(context, NULL, "hdb", + "virtual_hostbased_princ_svcs", NULL)) { + return krb5_enomem(context); + } + return 0; } /** * Create a handle for a Kerberos database * * Create a handle for a Kerberos database backend specified by a - * filename. Doesn't create a file if its doesn't exists, you have to - * use O_CREAT to tell the backend to create the file. + * filename. Doesn't actually create or even open an HDB file(s); + * you have to call the hdb_open() open method of the resulting HDB + * to open the database, and you have to use O_CREAT to create it. + * + * If `filename' does not have a backend type prefix, all file-based + * backends will be tried until one succeeds or all fail, and if the + * HDB exists for some backend, that will be used. A build-time + * default backend type will be used if the `filename' does not exist. + * + * Note that the actual filename may have a suffix added, such as + * ".db". Also, for backends such as "ldap:" and "ldapi:" the + * `filename' is more like a URI. + * + * @param [in] context Context + * @param [out] db HDB handle output + * @param [in] filename The name of the HDB + * + * @return Zero on success else a krb5 error code. */ krb5_error_code hdb_create(krb5_context context, HDB **db, const char *filename) { + krb5_error_code ret = ENOTSUP; struct cb_s cb_ctx; + *db = NULL; if (filename == NULL) - filename = HDB_DEFAULT_DB; - cb_ctx.h = find_method (filename, &cb_ctx.residual); + filename = hdb_default_db(context); + + cb_ctx.h = find_method(filename, &cb_ctx.residual); cb_ctx.filename = filename; if (cb_ctx.h == NULL || cb_ctx.h->create == NULL) { - char *sym; - - if ((sym = make_sym(filename)) == NULL) + struct heim_plugin_data hdb_plugin_data; + + /* + * `filename' does not start with a known HDB backend prefix. + * + * Try plugins. + */ + hdb_plugin_data.module = "krb5"; + hdb_plugin_data.min_version = HDB_INTERFACE_VERSION; + hdb_plugin_data.deps = hdb_plugin_deps; + hdb_plugin_data.get_instance = hdb_get_instance; + + if ((hdb_plugin_data.name = make_sym(filename)) == NULL) return krb5_enomem(context); - (void)_krb5_plugin_run_f(context, "krb5", sym, HDB_INTERFACE_VERSION, - 0, &cb_ctx, callback); + (void)_krb5_plugin_run_f(context, &hdb_plugin_data, 0 /* flags */, + &cb_ctx, callback); - free(sym); + free(rk_UNCONST(hdb_plugin_data.name)); + } + + if (cb_ctx.h == NULL || cb_ctx.h->create == NULL) { + int pathish = is_pathish(filename); + /* + * `filename' does not start with a known HDB backend prefix and it + * wasn't handled by any plugin. + * + * If it's "filename-ish", try all builtin HDB backends that are + * local-file-ish, but use hdb_open() to see if the HDB exists and stop + * when a backend is found for which the HDB exists. + */ + if (!pathish) { + krb5_set_error_message(context, ret = ENOTSUP, + "No database support for %s", + cb_ctx.filename); + return ret; + } + for (cb_ctx.h = methods; cb_ctx.h->prefix != NULL; cb_ctx.h++) { + if (cb_ctx.h->is_file_based) + continue; + if (!cb_ctx.h->can_taste) + continue; + /* Taste the file */ + ret = (*cb_ctx.h->create)(context, db, filename); + if (ret == 0) + ret = (*db)->hdb_open(context, *db, O_RDONLY, 0); + if (ret == 0) { + (void) (*db)->hdb_close(context, *db); + break; + } + if (*db) + (*db)->hdb_destroy(context, *db); + *db = NULL; + } + if (cb_ctx.h->prefix == NULL) + cb_ctx.h = NULL; } +#ifdef HDB_DEFAULT_DB_TYPE + if (cb_ctx.h == NULL) { + /* + * If still we've not picked a backend, use a build configuration time + * default. + */ + for (cb_ctx.h = methods; cb_ctx.h->prefix != NULL; cb_ctx.h++) + if (strcmp(cb_ctx.h->prefix, HDB_DEFAULT_DB_TYPE) == 0) + break; + if (cb_ctx.h->prefix == NULL) + cb_ctx.h = NULL; + } +#endif if (cb_ctx.h == NULL) - krb5_errx(context, 1, "No database support for %s", cb_ctx.filename); - return (*cb_ctx.h->create)(context, db, cb_ctx.residual); + /* Last resort default */ + cb_ctx.h = &default_dbmethod; + if (cb_ctx.h->prefix == NULL) { + krb5_set_error_message(context, ENOTSUP, + "Could not determine default DB backend for %s", + filename); + return ENOTSUP; + } + if (!*db) { + ret = (*cb_ctx.h->create)(context, db, cb_ctx.residual); + if (ret == 0) + (*db)->hdb_method_name = cb_ctx.h->prefix; + } + if (ret == 0 && *db) + ret = load_config(context, *db); + if (ret && *db) { + (*db)->hdb_destroy(context, *db); + *db = NULL; + } + return ret; +} + +uintptr_t KRB5_CALLCONV +hdb_get_instance(const char *libname) +{ + static const char *instance = "libhdb"; + + if (strcmp(libname, "hdb") == 0) + return (uintptr_t)instance; + else if (strcmp(libname, "krb5") == 0) + return krb5_get_instance(libname); + + return 0; } diff --git a/lib/hdb/hdb.h b/lib/hdb/hdb.h index 7c6dfcf9d8d2..87377513d549 100644 --- a/lib/hdb/hdb.h +++ b/lib/hdb/hdb.h @@ -42,30 +42,42 @@ #include <hdb_err.h> +#include <heimbase-svc.h> #include <heim_asn1.h> #include <hdb_asn1.h> +#define HDB_DB_FORMAT hdb_db_format + +typedef HDB_keyset hdb_keyset; +typedef HDB_entry hdb_entry; +typedef HDB_entry_alias hdb_entry_alias; + 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 */ -#define HDB_F_ADMIN_DATA 64 /* want data that kdc don't use */ -#define HDB_F_KVNO_SPECIFIED 128 /* we want a particular KVNO */ -#define HDB_F_CURRENT_KVNO 256 /* we want the current KVNO */ -#define HDB_F_LIVE_CLNT_KVNOS 512 /* we want all live keys for pre-auth */ -#define HDB_F_LIVE_SVC_KVNOS 1024 /* we want all live keys for tix */ -#define HDB_F_ALL_KVNOS 2048 /* we want all the keys, live or not */ -#define HDB_F_FOR_AS_REQ 4096 /* fetch is for a AS REQ */ -#define HDB_F_FOR_TGS_REQ 8192 /* fetch is for a TGS REQ */ -#define HDB_F_PRECHECK 16384 /* check that the operation would succeed */ +#define HDB_F_DECRYPT 0x00001 /* decrypt keys */ +#define HDB_F_REPLACE 0x00002 /* replace entry */ +#define HDB_F_GET_CLIENT 0x00004 /* fetch client */ +#define HDB_F_GET_SERVER 0x00008 /* fetch server */ +#define HDB_F_GET_KRBTGT 0x00010 /* fetch krbtgt */ +#define HDB_F_GET_ANY ( HDB_F_GET_CLIENT | \ + HDB_F_GET_SERVER | \ + HDB_F_GET_KRBTGT ) /* fetch any of client,server,krbtgt */ +#define HDB_F_CANON 0x00020 /* want canonicalition */ +#define HDB_F_ADMIN_DATA 0x00040 /* want data that kdc don't use */ +#define HDB_F_KVNO_SPECIFIED 0x00080 /* we want a particular KVNO */ +#define HDB_F_LIVE_CLNT_KVNOS 0x00200 /* we want all live keys for pre-auth */ +#define HDB_F_LIVE_SVC_KVNOS 0x00400 /* we want all live keys for tix */ +#define HDB_F_ALL_KVNOS 0x00800 /* we want all the keys, live or not */ +#define HDB_F_FOR_AS_REQ 0x01000 /* fetch is for a AS REQ */ +#define HDB_F_FOR_TGS_REQ 0x02000 /* fetch is for a TGS REQ */ +#define HDB_F_PRECHECK 0x04000 /* check that the operation would succeed */ +#define HDB_F_DELAY_NEW_KEYS 0x08000 /* apply [hdb] new_service_key_delay */ +#define HDB_F_SYNTHETIC_OK 0x10000 /* synthetic principal for PKINIT or GSS preauth OK */ +#define HDB_F_GET_FAST_COOKIE 0x20000 /* fetch the FX-COOKIE key (not a normal principal) */ +#define HDB_F_ARMOR_PRINCIPAL 0x40000 /* fetch is for the client of an armor ticket */ /* hdb_capability_flags */ #define HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL 1 @@ -73,29 +85,26 @@ enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK }; #define HDB_CAP_F_PASSWORD_UPDATE_KEYS 4 #define HDB_CAP_F_SHARED_DIRECTORY 8 -/* auth status values */ -#define HDB_AUTH_SUCCESS 0 -#define HDB_AUTH_WRONG_PASSWORD 1 -#define HDB_AUTH_INVALID_SIGNATURE 2 +#define heim_pcontext krb5_context +#define heim_pconfig void * + +typedef struct hdb_request_desc { + HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS; +} *hdb_request_t; + +#undef heim_pcontext +#undef heim_pconfig /* key usage for master key */ #define HDB_KU_MKEY 0x484442 -typedef struct hdb_master_key_data *hdb_master_key; - -/** - * hdb_entry_ex is a wrapper structure around the hdb_entry structure - * that allows backends to keep a pointer to the backing store, ie in - * ->hdb_fetch_kvno(), so that we the kadmin/kpasswd backend gets around to - * ->hdb_store(), the backend doesn't need to lookup the entry again. +/* + * Second component of WELLKNOWN namespace principals, the third component is + * the common DNS suffix of the implied virtual hosts. */ +#define HDB_WK_NAMESPACE "HOSTBASED-NAMESPACE" -typedef struct hdb_entry_ex { - void *ctx; - hdb_entry entry; - void (*free_entry)(krb5_context, struct hdb_entry_ex *); -} hdb_entry_ex; - +typedef struct hdb_master_key_data *hdb_master_key; /** * HDB backend function pointer structure @@ -107,6 +116,7 @@ typedef struct hdb_entry_ex { typedef struct HDB { void *hdb_db; void *hdb_dbc; /** don't use, only for DB3 */ + const char *hdb_method_name; char *hdb_name; int hdb_master_key_set; hdb_master_key hdb_master_key; @@ -114,6 +124,17 @@ typedef struct HDB { int hdb_capability_flags; int lock_count; int lock_type; + /* + * These fields cache config values. + * + * XXX Move these into a structure that we point to so that we + * don't need to break the ABI every time we add a field. + */ + int enable_virtual_hostbased_princs; + size_t virtual_hostbased_princ_ndots; /* Min. # of .s in hostname */ + size_t virtual_hostbased_princ_maxdots; /* Max. # of .s in namespace */ + char **virtual_hostbased_princ_svcs; /* Which svcs are not wildcarded */ + time_t new_service_key_delay; /* Delay for new keys */ /** * Open (or create) the a Kerberos database. * @@ -134,9 +155,9 @@ typedef struct HDB { */ krb5_error_code (*hdb_close)(krb5_context, struct HDB*); /** - * Free an entry after use. + * Free backend-specific entry context. */ - void (*hdb_free)(krb5_context, struct HDB*, hdb_entry_ex*); + void (*hdb_free_entry_context)(krb5_context, struct HDB*, hdb_entry*); /** * Fetch an entry from the backend * @@ -146,12 +167,12 @@ typedef struct HDB { */ krb5_error_code (*hdb_fetch_kvno)(krb5_context, struct HDB*, krb5_const_principal, unsigned, krb5_kvno, - hdb_entry_ex*); + hdb_entry*); /** * Store an entry to database */ krb5_error_code (*hdb_store)(krb5_context, struct HDB*, - unsigned, hdb_entry_ex*); + unsigned, hdb_entry*); /** * Remove an entry from the database. */ @@ -161,12 +182,12 @@ typedef struct HDB { * As part of iteration, fetch one entry */ krb5_error_code (*hdb_firstkey)(krb5_context, struct HDB*, - unsigned, hdb_entry_ex*); + unsigned, hdb_entry*); /** * As part of iteration, fetch next entry */ krb5_error_code (*hdb_nextkey)(krb5_context, struct HDB*, - unsigned, hdb_entry_ex*); + unsigned, hdb_entry*); /** * Lock database * @@ -245,32 +266,35 @@ typedef struct HDB { * The backend needs to call _kadm5_set_keys() and perform password * quality checks. */ - krb5_error_code (*hdb_password)(krb5_context, struct HDB*, hdb_entry_ex*, const char *, int); + krb5_error_code (*hdb_password)(krb5_context, struct HDB*, hdb_entry*, const char *, int); /** - * Auth feedback + * Authentication auditing. Note that this function is called by + * both the AS and TGS, but currently only the AS sets the auth + * event type. This may change in a future version. * - * This is a feedback call that allows backends that provides - * lockout functionality to register failure and/or successes. + * Event details are available by querying the request using + * heim_audit_getkv(HDB_REQUEST_KV_...). * * In case the entry is locked out, the backend should set the * hdb_entry.flags.locked-out flag. */ - krb5_error_code (*hdb_auth_status)(krb5_context, struct HDB *, hdb_entry_ex *, int); + krb5_error_code (*hdb_audit)(krb5_context, struct HDB *, hdb_entry *, hdb_request_t); + /** * Check if delegation is allowed. */ - krb5_error_code (*hdb_check_constrained_delegation)(krb5_context, struct HDB *, hdb_entry_ex *, krb5_const_principal); + krb5_error_code (*hdb_check_constrained_delegation)(krb5_context, struct HDB *, hdb_entry *, krb5_const_principal); /** * Check if this name is an alias for the supplied client for PKINIT userPrinicpalName logins */ - krb5_error_code (*hdb_check_pkinit_ms_upn_match)(krb5_context, struct HDB *, hdb_entry_ex *, krb5_const_principal); + krb5_error_code (*hdb_check_pkinit_ms_upn_match)(krb5_context, struct HDB *, hdb_entry *, krb5_const_principal); /** - * Check if s4u2self is allowed from this client to this server + * Check if s4u2self is allowed from this client to this server or the SPN is a valid SPN of this client (for user2user) */ - krb5_error_code (*hdb_check_s4u2self)(krb5_context, struct HDB *, hdb_entry_ex *, krb5_const_principal); + krb5_error_code (*hdb_check_client_matches_target_service)(krb5_context, struct HDB *, hdb_entry *, hdb_entry *); /** * Enable/disable synchronous updates @@ -281,12 +305,12 @@ typedef struct HDB { krb5_error_code (*hdb_set_sync)(krb5_context, struct HDB *, int); }HDB; -#define HDB_INTERFACE_VERSION 10 +#define HDB_INTERFACE_VERSION 11 struct hdb_method { - int version; - krb5_error_code (*init)(krb5_context, void **); - void (*fini)(void *); + HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(krb5_context); + unsigned int is_file_based:1; + unsigned int can_taste:1; const char *prefix; krb5_error_code (*create)(krb5_context, HDB **, const char *filename); }; @@ -303,7 +327,7 @@ struct hdb_print_entry_arg { }; typedef krb5_error_code (*hdb_foreach_func_t)(krb5_context, HDB*, - hdb_entry_ex*, void*); + hdb_entry*, void*); extern krb5_kt_ops hdb_kt_ops; extern krb5_kt_ops hdb_get_kt_ops; diff --git a/lib/hdb/hdb.opt b/lib/hdb/hdb.opt new file mode 100644 index 000000000000..a96eb632e527 --- /dev/null +++ b/lib/hdb/hdb.opt @@ -0,0 +1,9 @@ +--sequence=HDB-extensions +--sequence=HDB-Ext-KeyRotation +--sequence=HDB-Ext-KeySet +--sequence=Keys +--decorate=HDB_entry:void:context?::: +# The `aliased` field is for indicating whether a name used in an HDB +# lookup was an alias. This could be useful to applications when hard +# aliasing is implemented in an HDB backend, as it should be. +--decorate=HDB_entry:int:aliased::: diff --git a/lib/hdb/keys.c b/lib/hdb/keys.c index 96c221ed275e..457e5daf7a7b 100644 --- a/lib/hdb/keys.c +++ b/lib/hdb/keys.c @@ -31,21 +31,10 @@ * SUCH DAMAGE. */ +#include "krb5_locl.h" #include "hdb_locl.h" -struct hx509_certs_data; -struct krb5_pk_identity; -struct krb5_pk_cert; -struct ContentInfo; -struct AlgorithmIdentifier; -struct _krb5_krb_auth_data; -typedef struct krb5_pk_init_ctx_data *krb5_pk_init_ctx; -struct krb5_dh_moduli; -struct _krb5_key_data; -struct _krb5_encryption_type; -struct _krb5_key_type; #include <pkinit_asn1.h> -#include <krb5-private.h> #include <base64.h> /* @@ -212,18 +201,30 @@ parse_key_set(krb5_context context, const char *key, } /** - * This function prunes an HDB entry's keys that are too old to have been used - * to mint still valid tickets (based on the entry's maximum ticket lifetime). - * + * This function prunes an HDB entry's historic keys by kvno. + * * @param context Context * @param entry HDB entry + * @param kvno Keyset kvno to prune, or zero to prune all too-old keys */ krb5_error_code -hdb_prune_keys(krb5_context context, hdb_entry *entry) +hdb_prune_keys_kvno(krb5_context context, hdb_entry *entry, int kvno) { HDB_extension *ext; HDB_Ext_KeySet *keys; + hdb_keyset *elem; + time_t keep_time = 0; size_t nelem; + size_t i; + + /* + * XXX Pruning old keys for namespace principals may not be desirable, but! + * as long as the `set_time's of the base keys for a namespace principal + * match the `epoch's of the corresponding KeyRotation periods, it will be + * perfectly acceptable to prune old [base] keys for namespace principals + * just as for any other principal. Therefore, we may not need to make any + * changes here w.r.t. namespace principals. + */ ext = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys); if (ext == NULL) @@ -231,14 +232,12 @@ hdb_prune_keys(krb5_context context, hdb_entry *entry) keys = &ext->data.u.hist_keys; nelem = keys->len; - /* Optionally drop key history for keys older than now - max_life */ - if (entry->max_life != NULL && nelem > 0 - && krb5_config_get_bool_default(context, NULL, FALSE, - "kadmin", "prune-key-history", NULL)) { - hdb_keyset *elem; + /* + * Optionally drop key history for keys older than now - max_life, which is + * all the keys no longer needed to decrypt extant tickets. + */ + if (kvno == 0 && entry->max_life != NULL && nelem > 0) { time_t ceiling = time(NULL) - *entry->max_life; - time_t keep_time = 0; - size_t i; /* * Compute most recent key timestamp that predates the current time @@ -250,94 +249,123 @@ hdb_prune_keys(krb5_context context, hdb_entry *entry) && (keep_time == 0 || *elem->set_time > keep_time)) keep_time = *elem->set_time; } + } - /* Drop obsolete entries */ - if (keep_time) { - for (i = 0; i < nelem; /* see below */) { - elem = &keys->val[i]; - if (elem->set_time && *elem->set_time < keep_time) { - remove_HDB_Ext_KeySet(keys, i); - /* - * Removing the i'th element shifts the tail down, continue - * at same index with reduced upper bound. - */ - --nelem; - continue; - } - ++i; - } - } + if (kvno == 0 && keep_time == 0) + return 0; + + for (i = 0; i < nelem; /* see below */) { + elem = &keys->val[i]; + if ((kvno && kvno == elem->kvno) || + (keep_time && elem->set_time && *elem->set_time < keep_time)) { + remove_HDB_Ext_KeySet(keys, i); + /* + * Removing the i'th element shifts the tail down, continue + * at same index with reduced upper bound. + */ + --nelem; + continue; + } + ++i; } return 0; } /** + * This function prunes an HDB entry's keys that are too old to have been used + * to mint still valid tickets (based on the entry's maximum ticket lifetime). + * + * @param context Context + * @param entry HDB entry + */ +krb5_error_code +hdb_prune_keys(krb5_context context, hdb_entry *entry) +{ + if (!krb5_config_get_bool_default(context, NULL, FALSE, + "kadmin", "prune-key-history", NULL)) + return 0; + return hdb_prune_keys_kvno(context, entry, 0); +} + +/** + * This function adds a keyset to an HDB entry's key history. + * + * @param context Context + * @param entry HDB entry + * @param kvno Key version number of the key to add to the history + * @param key The Key to add + */ +krb5_error_code +hdb_add_history_keyset(krb5_context context, + hdb_entry *entry, + const hdb_keyset *ks) +{ + size_t i; + HDB_Ext_KeySet *hist_keys; + HDB_extension ext; + HDB_extension *extp; + krb5_error_code ret = 0; + + memset(&ext, 0, sizeof (ext)); + + extp = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys); + if (extp == NULL) { + ext.mandatory = FALSE; + ext.data.element = choice_HDB_extension_data_hist_keys; + ext.data.u.hist_keys.len = 0; + ext.data.u.hist_keys.val = 0; + extp = &ext; + } + hist_keys = &extp->data.u.hist_keys; + + for (i = 0; i < hist_keys->len; i++) { + if (hist_keys->val[i].kvno == ks->kvno) { + /* Replace existing */ + free_HDB_keyset(&hist_keys->val[i]); + ret = copy_HDB_keyset(ks, &hist_keys->val[i]); + break; + } + } + if (i >= hist_keys->len) + ret = add_HDB_Ext_KeySet(hist_keys, ks); /* Append new */ + if (ret == 0 && extp == &ext) + ret = hdb_replace_extension(context, entry, &ext); + free_HDB_extension(&ext); + return ret; +} + +/** * This function adds an HDB entry's current keyset to the entry's key * history. The current keyset is left alone; the caller is responsible * for freeing it. * * @param context Context * @param entry HDB entry + * + * @return Zero on success, or an error code otherwise. */ krb5_error_code hdb_add_current_keys_to_history(krb5_context context, hdb_entry *entry) { - krb5_boolean replace = FALSE; krb5_error_code ret; - HDB_extension *ext; - HDB_Ext_KeySet *keys; - hdb_keyset newkeyset; + hdb_keyset ks; time_t newtime; if (entry->keys.len == 0) return 0; /* nothing to do */ - ext = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys); - if (ext == NULL) { - replace = TRUE; - ext = calloc(1, sizeof (*ext)); - if (ext == NULL) - return krb5_enomem(context); - - ext->data.element = choice_HDB_extension_data_hist_keys; - } - keys = &ext->data.u.hist_keys; - - ext->mandatory = FALSE; - - /* - * Copy in newest old keyset - */ ret = hdb_entry_get_pw_change_time(entry, &newtime); if (ret) - goto out; - - memset(&newkeyset, 0, sizeof(newkeyset)); - newkeyset.keys = entry->keys; - newkeyset.kvno = entry->kvno; - newkeyset.set_time = &newtime; - - ret = add_HDB_Ext_KeySet(keys, &newkeyset); - if (ret) - goto out; - - if (replace) { - /* hdb_replace_extension() deep-copies ext; what a waste */ - ret = hdb_replace_extension(context, entry, ext); - if (ret) - goto out; - } + return ret; - ret = hdb_prune_keys(context, entry); - if (ret) - goto out; + ks.keys = entry->keys; + ks.kvno = entry->kvno; + ks.set_time = &newtime; - out: - if (replace && ext) { - free_HDB_extension(ext); - free(ext); - } + ret = hdb_add_history_keyset(context, entry, &ks); + if (ret == 0) + ret = hdb_prune_keys(context, entry); return ret; } @@ -348,6 +376,8 @@ hdb_add_current_keys_to_history(krb5_context context, hdb_entry *entry) * @param entry HDB entry * @param kvno Key version number of the key to add to the history * @param key The Key to add + * + * @return Zero on success, or an error code otherwise. */ krb5_error_code hdb_add_history_key(krb5_context context, hdb_entry *entry, krb5_kvno kvno, Key *key) @@ -392,12 +422,11 @@ hdb_add_history_key(krb5_context context, hdb_entry *entry, krb5_kvno kvno, Key } out: - free_hdb_keyset(&keyset); + free_HDB_keyset(&keyset); free_HDB_extension(&ext); return ret; } - /** * This function changes an hdb_entry's kvno, swapping the current key * set with a historical keyset. If no historical keys are found then @@ -433,7 +462,7 @@ hdb_change_kvno(krb5_context context, krb5_kvno new_kvno, hdb_entry *entry) for (i = 0; i < hist_keys->len; i++) { if (hist_keys->val[i].kvno == new_kvno) { found = 1; - ret = copy_hdb_keyset(&hist_keys->val[i], &keyset); + ret = copy_HDB_keyset(&hist_keys->val[i], &keyset); if (ret) goto out; ret = remove_HDB_Ext_KeySet(hist_keys, i); @@ -456,7 +485,7 @@ hdb_change_kvno(krb5_context context, krb5_kvno new_kvno, hdb_entry *entry) memset(&keyset.keys, 0, sizeof (keyset.keys)); out: - free_hdb_keyset(&keyset); + free_HDB_keyset(&keyset); return ret; } @@ -654,7 +683,8 @@ hdb_generate_key_set(krb5_context context, krb5_principal principal, ktypes = ks_tuple_strs; if (ktypes == NULL) { - ktypes = glob_rules_keys(context, principal); + config_ktypes = glob_rules_keys(context, principal); + ktypes = config_ktypes; } if (ktypes == NULL) { config_ktypes = krb5_config_get_strings(context, NULL, "kadmin", diff --git a/lib/hdb/keytab.c b/lib/hdb/keytab.c index 81cf2b287261..df16cb782faa 100644 --- a/lib/hdb/keytab.c +++ b/lib/hdb/keytab.c @@ -42,7 +42,7 @@ struct hdb_data { struct hdb_cursor { HDB *db; - hdb_entry_ex hdb_entry; + hdb_entry hdb_entry; int first, next; int key_idx; }; @@ -160,7 +160,7 @@ find_db (krb5_context context, } hdb_free_dbinfo(context, &head); if (*dbname == NULL && - (*dbname = strdup(HDB_DEFAULT_DB)) == NULL) { + (*dbname = strdup(hdb_default_db(context))) == NULL) { free(*mkey); *mkey = NULL; return krb5_enomem(context); @@ -181,7 +181,7 @@ hdb_get_entry(krb5_context context, krb5_enctype enctype, krb5_keytab_entry *entry) { - hdb_entry_ex ent; + hdb_entry ent; krb5_error_code ret; struct hdb_data *d = id->data; const char *dbname = d->dbname; @@ -190,6 +190,9 @@ hdb_get_entry(krb5_context context, HDB *db; size_t i; + if (!principal) + return KRB5_KT_NOTFOUND; + memset(&ent, 0, sizeof(ent)); if (dbname == NULL) { @@ -215,38 +218,37 @@ hdb_get_entry(krb5_context context, goto out2; } - ret = (*db->hdb_fetch_kvno)(context, db, principal, - HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED| - HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, - kvno, &ent); + ret = hdb_fetch_kvno(context, db, principal, + HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED| + HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, + 0, 0, kvno, &ent); - if(ret == HDB_ERR_NOENTRY) { + if (ret == HDB_ERR_WRONG_REALM || ret == HDB_ERR_NOENTRY) ret = KRB5_KT_NOTFOUND; - goto out; - }else if(ret) + if (ret) goto out; - if(kvno && (krb5_kvno)ent.entry.kvno != kvno) { - hdb_free_entry(context, &ent); + if(kvno && (krb5_kvno)ent.kvno != kvno) { + hdb_free_entry(context, db, &ent); ret = KRB5_KT_NOTFOUND; goto out; } if(enctype == 0) - if(ent.entry.keys.len > 0) - enctype = ent.entry.keys.val[0].key.keytype; + if(ent.keys.len > 0) + enctype = ent.keys.val[0].key.keytype; ret = KRB5_KT_NOTFOUND; - for(i = 0; i < ent.entry.keys.len; i++) { - if(ent.entry.keys.val[i].key.keytype == enctype) { + for(i = 0; i < ent.keys.len; i++) { + if(ent.keys.val[i].key.keytype == enctype) { krb5_copy_principal(context, principal, &entry->principal); - entry->vno = ent.entry.kvno; + entry->vno = ent.kvno; krb5_copy_keyblock_contents(context, - &ent.entry.keys.val[i].key, + &ent.keys.val[i].key, &entry->keyblock); ret = 0; break; } } - hdb_free_entry(context, &ent); + hdb_free_entry(context, db, &ent); out: (*db->hdb_close)(context, db); (*db->hdb_destroy)(context, db); @@ -336,8 +338,8 @@ hdb_next_entry(krb5_context context, else if (ret) return ret; - if (c->hdb_entry.entry.keys.len == 0) - hdb_free_entry(context, &c->hdb_entry); + if (c->hdb_entry.keys.len == 0) + hdb_free_entry(context, c->db, &c->hdb_entry); else c->next = FALSE; } @@ -353,8 +355,8 @@ hdb_next_entry(krb5_context context, return ret; /* If no keys on this entry, try again */ - if (c->hdb_entry.entry.keys.len == 0) - hdb_free_entry(context, &c->hdb_entry); + if (c->hdb_entry.keys.len == 0) + hdb_free_entry(context, c->db, &c->hdb_entry); else c->next = FALSE; } @@ -365,14 +367,14 @@ hdb_next_entry(krb5_context context, */ ret = krb5_copy_principal(context, - c->hdb_entry.entry.principal, + c->hdb_entry.principal, &entry->principal); if (ret) return ret; - entry->vno = c->hdb_entry.entry.kvno; + entry->vno = c->hdb_entry.kvno; ret = krb5_copy_keyblock_contents(context, - &c->hdb_entry.entry.keys.val[c->key_idx].key, + &c->hdb_entry.keys.val[c->key_idx].key, &entry->keyblock); if (ret) { krb5_free_principal(context, entry->principal); @@ -386,8 +388,8 @@ hdb_next_entry(krb5_context context, * next entry */ - if ((size_t)c->key_idx == c->hdb_entry.entry.keys.len) { - hdb_free_entry(context, &c->hdb_entry); + if ((size_t)c->key_idx == c->hdb_entry.keys.len) { + hdb_free_entry(context, c->db, &c->hdb_entry); c->next = TRUE; c->key_idx = 0; } @@ -404,7 +406,7 @@ hdb_end_seq_get(krb5_context context, struct hdb_cursor *c = cursor->data; if (!c->next) - hdb_free_entry(context, &c->hdb_entry); + hdb_free_entry(context, c->db, &c->hdb_entry); (c->db->hdb_close)(context, c->db); (c->db->hdb_destroy)(context, c->db); diff --git a/lib/hdb/libhdb-exports.def b/lib/hdb/libhdb-exports.def index 67c0e33e0357..72a7fb7aaad7 100644 --- a/lib/hdb/libhdb-exports.def +++ b/lib/hdb/libhdb-exports.def @@ -1,7 +1,12 @@ EXPORTS - encode_hdb_keyset - hdb_add_master_key + encode_HDB_keyset + _hdb_fetch_kvno + _hdb_remove + _hdb_store hdb_add_current_keys_to_history + hdb_add_history_key + hdb_add_history_keyset + hdb_add_master_key hdb_change_kvno hdb_check_db_format hdb_clear_extension @@ -16,22 +21,28 @@ EXPORTS hdb_dbinfo_get_mkey_file hdb_dbinfo_get_next hdb_dbinfo_get_realm + hdb_derive_etypes hdb_default_db hdb_enctype2key hdb_entry2string hdb_entry2value + hdb_entry_add_key_rotation hdb_entry_alias2value hdb_entry_check_mandatory hdb_entry_clear_password hdb_entry_get_ConstrainedDelegACL hdb_entry_get_aliases + hdb_entry_get_key_rotation + hdb_entry_get_krb5_config hdb_entry_get_password hdb_entry_get_pkinit_acl hdb_entry_get_pkinit_cert hdb_entry_get_pkinit_hash hdb_entry_get_pw_change_time + hdb_entry_set_krb5_config hdb_entry_set_password hdb_entry_set_pw_change_time + hdb_fetch_kvno hdb_find_extension hdb_foreach hdb_free_dbinfo @@ -43,7 +54,9 @@ EXPORTS hdb_generate_key_set_password hdb_generate_key_set_password_with_ks_tuple hdb_get_dbinfo + hdb_get_instance hdb_init_db + hdb_install_keyset hdb_interface_version DATA hdb_key2principal hdb_kvno2keys @@ -54,7 +67,9 @@ EXPORTS hdb_print_entry hdb_process_master_key hdb_prune_keys + hdb_prune_keys_kvno hdb_read_master_key + hdb_remove_keys hdb_replace_extension hdb_seal_key hdb_seal_key_mkey @@ -67,54 +82,99 @@ EXPORTS hdb_unseal_key hdb_unseal_key_mkey hdb_unseal_keys + hdb_unseal_keys_kvno hdb_unseal_keys_mkey + hdb_validate_key_rotation + hdb_validate_key_rotations hdb_value2entry hdb_value2entry_alias hdb_write_master_key - length_hdb_keyset + length_HDB_keyset initialize_hdb_error_table_r hdb_kt_ops hdb_get_kt_ops ; MIT KDB related entries - _hdb_mdb_value2entry - _hdb_mit_dump2mitdb_entry + _hdb_mdb_value2entry + _hdb_mit_dump2mitdb_entry ; some random bits needed for libkadm - HDBFlags2int + add_HDB_Ext_KeyRotation + add_HDB_Ext_KeySet + add_Keys asn1_HDBFlags_units copy_Event + copy_HDB_EncTypeList + copy_hdb_entry + copy_HDB_entry + copy_hdb_entry_alias + copy_HDB_entry_alias + copy_HDB_EntryOrAlias copy_HDB_extensions + copy_HDB_Ext_KeyRotation copy_Key - copy_Keys + copy_Keys copy_Salt + decode_HDB_EncTypeList + decode_hdb_entry + decode_HDB_entry + decode_hdb_entry_alias + decode_HDB_entry_alias + decode_HDB_EntryOrAlias decode_HDB_Ext_Aliases - decode_HDB_Ext_PKINIT_acl decode_HDB_extension - decode_Key - decode_Keys + decode_HDB_Ext_KeyRotation + decode_HDB_Ext_PKINIT_acl + decode_Key + decode_Keys + encode_HDB_EncTypeList + encode_hdb_entry + encode_HDB_entry + encode_hdb_entry_alias + encode_HDB_entry_alias + encode_HDB_EntryOrAlias encode_HDB_Ext_Aliases - encode_HDB_Ext_PKINIT_acl encode_HDB_extension - encode_Key - encode_Keys + encode_HDB_Ext_KeyRotation + encode_HDB_Ext_PKINIT_acl + encode_hdb_keyset + encode_Key + encode_Keys free_Event + free_HDB_EncTypeList + free_hdb_entry + free_HDB_entry + free_hdb_entry_alias + free_HDB_entry_alias + free_HDB_EntryOrAlias free_HDB_Ext_Aliases - free_HDB_Ext_PKINIT_acl free_HDB_extension free_HDB_extensions + free_HDB_Ext_KeyRotation + free_HDB_Ext_KeySet + free_HDB_Ext_PKINIT_acl + free_hdb_keyset + free_HDB_keyset free_Key free_Keys - free_Salt - free_hdb_entry - free_hdb_keyset + free_Salt + HDBFlags2int int2HDBFlags + int2KeyRotationFlags + KeyRotationFlags2int + length_HDB_EncTypeList + length_hdb_entry + length_HDB_entry + length_hdb_entry_alias + length_HDB_entry_alias + length_HDB_EntryOrAlias length_HDB_Ext_Aliases - length_HDB_Ext_PKINIT_acl length_HDB_extension - length_Key - length_Keys - add_Keys - add_HDB_Ext_KeySet - remove_Keys + length_HDB_Ext_KeyRotation + length_HDB_Ext_PKINIT_acl + length_hdb_keyset + length_Key + length_Keys + remove_HDB_Ext_KeyRotation + remove_Keys diff --git a/lib/hdb/ndbm.c b/lib/hdb/ndbm.c index 5b2c07e5f644..52c52c890dc0 100644 --- a/lib/hdb/ndbm.c +++ b/lib/hdb/ndbm.c @@ -53,7 +53,8 @@ struct ndbm_db { static krb5_error_code NDBM_destroy(krb5_context context, HDB *db) { - hdb_clear_master_key (context, db); + hdb_clear_master_key(context, db); + krb5_config_free_strings(db->virtual_hostbased_princ_svcs); free(db->hdb_name); free(db); return 0; @@ -75,7 +76,7 @@ NDBM_unlock(krb5_context context, HDB *db) static krb5_error_code NDBM_seq(krb5_context context, HDB *db, - unsigned flags, hdb_entry_ex *entry, int first) + unsigned flags, hdb_entry *entry, int first) { struct ndbm_db *d = (struct ndbm_db *)db->hdb_db; @@ -98,21 +99,21 @@ NDBM_seq(krb5_context context, HDB *db, data.data = value.dptr; data.length = value.dsize; memset(entry, 0, sizeof(*entry)); - if(hdb_value2entry(context, &data, &entry->entry)) + if(hdb_value2entry(context, &data, entry)) return NDBM_seq(context, db, flags, entry, 0); if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { - ret = hdb_unseal_keys (context, db, &entry->entry); + ret = hdb_unseal_keys (context, db, entry); if (ret) - hdb_free_entry (context, entry); + hdb_free_entry (context, db, entry); } - if (ret == 0 && entry->entry.principal == NULL) { - entry->entry.principal = malloc (sizeof(*entry->entry.principal)); - if (entry->entry.principal == NULL) { - hdb_free_entry (context, entry); + if (ret == 0 && entry->principal == NULL) { + entry->principal = malloc (sizeof(*entry->principal)); + if (entry->principal == NULL) { + hdb_free_entry (context, db, entry); ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); } else { - hdb_key2principal (context, &key_data, entry->entry.principal); + hdb_key2principal (context, &key_data, entry->principal); } } return ret; @@ -120,14 +121,14 @@ NDBM_seq(krb5_context context, HDB *db, static krb5_error_code -NDBM_firstkey(krb5_context context, HDB *db,unsigned flags,hdb_entry_ex *entry) +NDBM_firstkey(krb5_context context, HDB *db,unsigned flags,hdb_entry *entry) { return NDBM_seq(context, db, flags, entry, 1); } static krb5_error_code -NDBM_nextkey(krb5_context context, HDB *db, unsigned flags,hdb_entry_ex *entry) +NDBM_nextkey(krb5_context context, HDB *db, unsigned flags,hdb_entry *entry) { return NDBM_seq(context, db, flags, entry, 0); } @@ -136,23 +137,22 @@ static krb5_error_code open_lock_file(krb5_context context, const char *db_name, int *fd) { char *lock_file; + int ret = 0; /* lock old and new databases */ - asprintf(&lock_file, "%s.lock", db_name); - if(lock_file == NULL) { + if (asprintf(&lock_file, "%s.lock", db_name) == -1) { krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } *fd = open(lock_file, O_RDWR | O_CREAT, 0600); - free(lock_file); if(*fd < 0) { - int ret = errno; + ret = errno; krb5_set_error_message(context, ret, "open(%s): %s", lock_file, strerror(ret)); - return ret; } - return 0; + free(lock_file); + return ret; } @@ -160,7 +160,8 @@ static krb5_error_code NDBM_rename(krb5_context context, HDB *db, const char *new_name) { int ret; - char *old_dir, *old_pag, *new_dir, *new_pag; + char *old_dir = NULL, *old_pag = NULL; + char *new_dir = NULL, *new_pag = NULL; int old_lock_fd, new_lock_fd; /* lock old and new databases */ @@ -189,10 +190,26 @@ NDBM_rename(krb5_context context, HDB *db, const char *new_name) return ret; } - asprintf(&old_dir, "%s.dir", db->hdb_name); - asprintf(&old_pag, "%s.pag", db->hdb_name); - asprintf(&new_dir, "%s.dir", new_name); - asprintf(&new_pag, "%s.pag", new_name); + if (asprintf(&old_dir, "%s.dir", db->hdb_name) == -1) { + old_dir = NULL; + ret = ENOMEM; + goto out; + } + if (asprintf(&old_pag, "%s.pag", db->hdb_name) == -1) { + old_pag = NULL; + ret = ENOMEM; + goto out; + } + if (asprintf(&new_dir, "%s.dir", new_name) == -1) { + new_dir = NULL; + ret = ENOMEM; + goto out; + } + if (asprintf(&new_pag, "%s.pag", new_name) == -1) { + new_pag = NULL; + ret = ENOMEM; + goto out; + } ret = rename(old_dir, new_dir) || rename(old_pag, new_pag); if (ret) { @@ -202,6 +219,7 @@ NDBM_rename(krb5_context context, HDB *db, const char *new_name) krb5_set_error_message(context, ret, "rename: %s", strerror(ret)); } + out: free(old_dir); free(old_pag); free(new_dir); diff --git a/lib/hdb/print.c b/lib/hdb/print.c index 0d1e2855217d..7f2535881892 100644 --- a/lib/hdb/print.c +++ b/lib/hdb/print.c @@ -453,7 +453,8 @@ entry2mit_string_int(krb5_context context, krb5_storage *sp, hdb_entry *ent) unsigned char *ptr; ptr = (unsigned char *)&last_pw_chg; - val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); + val = ((unsigned long)ptr[3] << 24) | (ptr[2] << 16) + | (ptr[1] << 8) | ptr[0]; d.data = &val; d.length = sizeof (last_pw_chg); sz = append_string(context, sp, "\t%u\t%u\t", @@ -474,12 +475,16 @@ entry2mit_string_int(krb5_context context, krb5_storage *sp, hdb_entry *ent) d.data = &val; d.length = sizeof (ent->modified_by->time); ret = krb5_unparse_name(context, ent->modified_by->principal, &modby_p); - if (ret) return ret; + if (ret) + return ret; plen = strlen(modby_p); sz = append_string(context, sp, "\t%u\t%u\t", mit_KRB5_TL_MOD_PRINC, d.length + plen + 1 /* NULL counted */); - if (sz == -1) return ENOMEM; + if (sz == -1) { + free(modby_p); + return ENOMEM; + } sz = append_hex(context, sp, 1, 1, &d); if (sz == -1) { free(modby_p); @@ -489,7 +494,8 @@ entry2mit_string_int(krb5_context context, krb5_storage *sp, hdb_entry *ent) d.length = plen + 1; sz = append_hex(context, sp, 1, 1, &d); free(modby_p); - if (sz == -1) return ENOMEM; + if (sz == -1) + return ENOMEM; } /* * Dump keys (remembering to not include any with kvno higher than @@ -556,7 +562,7 @@ hdb_entry2string(krb5_context context, hdb_entry *ent, char **str) /* print a hdb_entry to (FILE*)data; suitable for hdb_foreach */ krb5_error_code -hdb_print_entry(krb5_context context, HDB *db, hdb_entry_ex *entry, +hdb_print_entry(krb5_context context, HDB *db, hdb_entry *entry, void *data) { struct hdb_print_entry_arg *parg = data; @@ -572,10 +578,10 @@ hdb_print_entry(krb5_context context, HDB *db, hdb_entry_ex *entry, switch (parg->fmt) { case HDB_DUMP_HEIMDAL: - ret = entry2string_int(context, sp, &entry->entry); + ret = entry2string_int(context, sp, entry); break; case HDB_DUMP_MIT: - ret = entry2mit_string_int(context, sp, &entry->entry); + ret = entry2mit_string_int(context, sp, entry); break; default: heim_abort("Only two dump formats supported: Heimdal and MIT"); diff --git a/lib/hdb/test_concurrency.c b/lib/hdb/test_concurrency.c new file mode 100644 index 000000000000..35c01f59f594 --- /dev/null +++ b/lib/hdb/test_concurrency.c @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2005 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This test tries to test reader/writer concurrency for the SQLite3 and LMDB + * HDB backends. We're hoping to find that one thread or process can dump the + * HDB while another writes -- this way backups and ipropd-master need not + * block write transactions when dumping a huge HDB. + * + * It has two modes: threaded, and forked. + * + * Apparently, neither LMDB nor SQLite3 give us the desired level of + * concurrency in threaded mode, with this test not making progress. This is + * surprising, at least for SQLite3, which is supposed to support N readers, 1 + * writer and be thread-safe. LMDB also is supposed to support N readers, 1 + * writers, but perhaps not all in one process? + */ + +#include "hdb_locl.h" +#include <sys/types.h> +#include <sys/wait.h> +#include <pthread.h> +#include <getarg.h> + +struct tsync { + pthread_mutex_t lock; + pthread_cond_t rcv; + pthread_cond_t wcv; + const char *hdb_name; + const char *fname; + volatile int writer_go; + volatile int reader_go; + int writer_go_pipe[2]; + int reader_go_pipe[2]; +}; + +static void * +threaded_reader(void *d) +{ + krb5_error_code ret; + krb5_context context; + struct tsync *s = d; + hdb_entry entr; + HDB *dbr = NULL; + + printf("Reader thread opening HDB\n"); + + if ((krb5_init_context(&context))) + errx(1, "krb5_init_context failed"); + + printf("Reader thread waiting for writer to create the HDB\n"); + (void) pthread_mutex_lock(&s->lock); + s->writer_go = 1; + (void) pthread_cond_signal(&s->wcv); + while (!s->reader_go) + (void) pthread_cond_wait(&s->rcv, &s->lock); + s->reader_go = 0; + (void) pthread_mutex_unlock(&s->lock); + + /* Open a new HDB handle to read */ + if ((ret = hdb_create(context, &dbr, s->hdb_name))) { + //(void) unlink(s->fname); + krb5_err(context, 1, ret, "Could not get a handle for HDB %s (read)", + s->hdb_name); + } + if ((ret = dbr->hdb_open(context, dbr, O_RDONLY, 0))) { + //(void) unlink(s->fname); + krb5_err(context, 1, ret, "Could not open HDB %s", s->hdb_name); + } + if ((ret = dbr->hdb_firstkey(context, dbr, 0, &entr))) { + //(void) unlink(s->fname); + krb5_err(context, 1, ret, "Could not iterate HDB %s", s->hdb_name); + } + free_HDB_entry(&entr); + + /* Tell the writer to go ahead and write */ + printf("Reader thread iterated one entry; telling writer to write more\n"); + s->writer_go = 1; + (void) pthread_mutex_lock(&s->lock); + (void) pthread_cond_signal(&s->wcv); + + /* Wait for the writer to have written one more entry to the HDB */ + printf("Reader thread waiting for writer\n"); + while (!s->reader_go) + (void) pthread_cond_wait(&s->rcv, &s->lock); + s->reader_go = 0; + (void) pthread_mutex_unlock(&s->lock); + + /* Iterate the rest */ + printf("Reader thread iterating another entry\n"); + if ((ret = dbr->hdb_nextkey(context, dbr, 0, &entr))) { + //(void) unlink(s->fname); + krb5_err(context, 1, ret, + "Could not iterate while writing to HDB %s", s->hdb_name); + } + printf("Reader thread iterated another entry\n"); + free_HDB_entry(&entr); + if ((ret = dbr->hdb_nextkey(context, dbr, 0, &entr)) == 0) { + //(void) unlink(s->fname); + krb5_warn(context, ret, + "HDB %s sees writes committed since starting iteration", + s->hdb_name); + } else if (ret != HDB_ERR_NOENTRY) { + //(void) unlink(s->fname); + krb5_err(context, 1, ret, + "Could not iterate while writing to HDB %s (2)", s->hdb_name); + } + + /* Tell the writer we're done */ + printf("Reader thread telling writer to go\n"); + s->writer_go = 1; + (void) pthread_cond_signal(&s->wcv); + (void) pthread_mutex_unlock(&s->lock); + + dbr->hdb_close(context, dbr); + dbr->hdb_destroy(context, dbr); + krb5_free_context(context); + printf("Reader thread exiting\n"); + return 0; +} + +static void +forked_reader(struct tsync *s) +{ + krb5_error_code ret; + krb5_context context; + hdb_entry entr; + ssize_t bytes; + char b[1]; + HDB *dbr = NULL; + + printf("Reader process opening HDB\n"); + + (void) close(s->writer_go_pipe[0]); + (void) close(s->reader_go_pipe[1]); + s->writer_go_pipe[0] = -1; + s->reader_go_pipe[1] = -1; + if ((krb5_init_context(&context))) + errx(1, "krb5_init_context failed"); + + printf("Reader process waiting for writer\n"); + while ((bytes = read(s->reader_go_pipe[0], b, sizeof(b))) == -1 && + errno == EINTR) + ; + if (bytes == -1) + err(1, "Could not read from reader-go pipe (error)"); + + /* Open a new HDB handle to read */ + if ((ret = hdb_create(context, &dbr, s->hdb_name))) { + //(void) unlink(s->fname); + krb5_err(context, 1, ret, "Could not get a handle for HDB %s (read)", + s->hdb_name); + } + if ((ret = dbr->hdb_open(context, dbr, O_RDONLY, 0))) { + //(void) unlink(s->fname); + krb5_err(context, 1, ret, "Could not open HDB %s", s->hdb_name); + } + if ((ret = dbr->hdb_firstkey(context, dbr, 0, &entr))) { + //(void) unlink(s->fname); + krb5_err(context, 1, ret, "Could not iterate HDB %s", s->hdb_name); + } + printf("Reader process iterated one entry\n"); + free_HDB_entry(&entr); + + /* Tell the writer to go ahead and write */ + printf("Reader process iterated one entry; telling writer to write more\n"); + while ((bytes = write(s->writer_go_pipe[1], "", sizeof(""))) == -1 && + errno == EINTR) + ; + if (bytes == -1) + err(1, "Could not write to writer-go pipe (error)"); + + + /* Wait for the writer to have written one more entry to the HDB */ + printf("Reader process waiting for writer\n"); + while ((bytes = read(s->reader_go_pipe[0], b, sizeof(b))) == -1 && + errno == EINTR) + ; + if (bytes == -1) + err(1, "Could not read from reader-go pipe (error)"); + if (bytes == 0) + errx(1, "Could not read from reader-go pipe (EOF)"); + + /* Iterate the rest */ + if ((ret = dbr->hdb_nextkey(context, dbr, 0, &entr))) { + //(void) unlink(s->fname); + krb5_err(context, 1, ret, + "Could not iterate while writing to HDB %s", s->hdb_name); + } + free_HDB_entry(&entr); + printf("Reader process iterated another entry\n"); + if ((ret = dbr->hdb_nextkey(context, dbr, 0, &entr)) == 0) { + //(void) unlink(s->fname); + krb5_warn(context, ret, + "HDB %s sees writes committed since starting iteration (%s)", + s->hdb_name, entr.principal->name.name_string.val[0]); + } else if (ret != HDB_ERR_NOENTRY) { + //(void) unlink(s->fname); + krb5_err(context, 1, ret, + "Could not iterate while writing to HDB %s (2)", s->hdb_name); + } + + /* Tell the writer we're done */ + printf("Reader process done; telling writer to go\n"); + while ((bytes = write(s->writer_go_pipe[1], "", sizeof(""))) == -1 && + errno == EINTR) + ; + if (bytes == -1) + err(1, "Could not write to writer-go pipe (error)"); + + dbr->hdb_close(context, dbr); + dbr->hdb_destroy(context, dbr); + krb5_free_context(context); + (void) close(s->writer_go_pipe[1]); + (void) close(s->reader_go_pipe[0]); + printf("Reader process exiting\n"); + _exit(0); +} + +static krb5_error_code +make_entry(krb5_context context, hdb_entry *entry, const char *name) +{ + krb5_error_code ret; + + memset(entry, 0, sizeof(*entry)); + entry->kvno = 2; + entry->keys.len = 0; + entry->keys.val = NULL; + entry->created_by.time = time(NULL); + entry->modified_by = NULL; + entry->valid_start = NULL; + entry->valid_end = NULL; + entry->max_life = NULL; + entry->max_renew = NULL; + entry->etypes = NULL; + entry->generation = NULL; + entry->extensions = NULL; + if ((ret = krb5_make_principal(context, &entry->principal, + "TEST.H5L.SE", name, NULL))) + return ret; + if ((ret = krb5_make_principal(context, &entry->created_by.principal, + "TEST.H5L.SE", "tester", NULL))) + return ret; + return 0; +} + +static void +readers_turn(struct tsync *s, pid_t child, int threaded) +{ + if (threaded) { + (void) pthread_mutex_lock(&s->lock); + s->reader_go = 1; + (void) pthread_cond_signal(&s->rcv); + + while (!s->writer_go) + (void) pthread_cond_wait(&s->wcv, &s->lock); + s->writer_go = 0; + (void) pthread_mutex_unlock(&s->lock); + } else { + ssize_t bytes; + char b[1]; + + while ((bytes = write(s->reader_go_pipe[1], "", sizeof(""))) == -1 && + errno == EINTR) + ; + if (bytes == -1) { + kill(child, SIGKILL); + err(1, "Could not write to reader-go pipe (error)"); + } + if (bytes == 0) { + kill(child, SIGKILL); + err(1, "Could not write to reader-go pipe (EOF?)"); + } + + while ((bytes = read(s->writer_go_pipe[0], b, sizeof(b))) == -1 && + errno == EINTR) + ; + if (bytes == -1) { + kill(child, SIGKILL); + err(1, "Could not read from writer-go pipe"); + } + if (bytes == 0) { + kill(child, SIGKILL); + errx(1, "Child errored"); + } + s->writer_go = 0; + } +} + +static void +test_hdb_concurrency(char *name, const char *ext, int threaded) +{ + krb5_error_code ret; + krb5_context context; + char *fname = strchr(name, ':') + 1; + char *fname_ext = NULL; + pthread_t reader_thread; + struct tsync ts; + hdb_entry entw; + pid_t child = getpid(); + HDB *dbw = NULL; + int status; + int fd; + + memset(&ts, 0, sizeof(ts)); + (void) pthread_cond_init(&ts.rcv, NULL); + (void) pthread_cond_init(&ts.wcv, NULL); + (void) pthread_mutex_init(&ts.lock, NULL); + + if ((krb5_init_context(&context))) + errx(1, "krb5_init_context failed"); + + /* Use mkstemp() then unlink() to avoid warnings about mktemp(); ugh */ + if ((fd = mkstemp(fname)) == -1) + err(1, "mkstemp(%s)", fname); + (void) close(fd); + (void) unlink(fname); + if (asprintf(&fname_ext, "%s%s", fname, ext ? ext : "") == -1 || + fname_ext == NULL) + err(1, "Out of memory"); + ts.hdb_name = name; + ts.fname = fname_ext; + + if (threaded) { + printf("Starting reader thread\n"); + (void) pthread_mutex_lock(&ts.lock); + if ((errno = pthread_create(&reader_thread, NULL, threaded_reader, &ts))) { + (void) unlink(fname_ext); + krb5_err(context, 1, errno, "Could not create a thread to read HDB"); + } + + /* Wait for reader */ + while (!ts.writer_go) + (void) pthread_cond_wait(&ts.wcv, &ts.lock); + (void) pthread_mutex_unlock(&ts.lock); + } else { + printf("Starting reader process\n"); + if (pipe(ts.writer_go_pipe) == -1) + err(1, "Could not create a pipe"); + if (pipe(ts.reader_go_pipe) == -1) + err(1, "Could not create a pipe"); + switch ((child = fork())) { + case -1: err(1, "Could not fork a child"); + case 0: forked_reader(&ts); _exit(0); + default: break; + } + (void) close(ts.writer_go_pipe[1]); + ts.writer_go_pipe[1] = -1; + } + + printf("Writing two entries into HDB\n"); + if ((ret = hdb_create(context, &dbw, name))) + krb5_err(context, 1, ret, "Could not get a handle for HDB %s (write)", + name); + if ((ret = dbw->hdb_open(context, dbw, O_RDWR | O_CREAT, 0600))) + krb5_err(context, 1, ret, "Could not create HDB %s", name); + + /* Add two entries */ + memset(&entw, 0, sizeof(entw)); + if ((ret = make_entry(context, &entw, "foo")) || + (ret = dbw->hdb_store(context, dbw, 0, &entw))) { + (void) unlink(fname_ext); + krb5_err(context, 1, ret, + "Could not store entry for \"foo\" in HDB %s", name); + } + free_HDB_entry(&entw); + if ((ret = make_entry(context, &entw, "bar")) || + (ret = dbw->hdb_store(context, dbw, 0, &entw))) { + (void) unlink(fname_ext); + krb5_err(context, 1, ret, + "Could not store entry for \"foo\" in HDB %s", name); + } + free_HDB_entry(&entw); + + /* Tell the reader to start reading */ + readers_turn(&ts, child, threaded); + + /* Store one more entry */ + if ((ret = make_entry(context, &entw, "foobar")) || + (ret = dbw->hdb_store(context, dbw, 0, &entw))) { + (void) unlink(fname_ext); + krb5_err(context, 1, ret, + "Could not store entry for \"foobar\" in HDB %s " + "while iterating it", name); + } + free_HDB_entry(&entw); + + /* Tell the reader to go again */ + readers_turn(&ts, child, threaded); + + dbw->hdb_close(context, dbw); + dbw->hdb_destroy(context, dbw); + if (threaded) { + (void) pthread_join(reader_thread, NULL); + } else { + (void) close(ts.writer_go_pipe[1]); + (void) close(ts.reader_go_pipe[0]); + (void) close(ts.reader_go_pipe[1]); + while (wait(&status) == -1 && errno == EINTR) + ; + (void) close(ts.writer_go_pipe[0]); + if (!WIFEXITED(status)) + errx(1, "Child reader died"); + if (WEXITSTATUS(status) != 0) + errx(1, "Child reader errored"); + } + (void) unlink(fname_ext); + krb5_free_context(context); +} + +static int use_fork; +static int use_threads; +static int help_flag; +static int version_flag; + +struct getargs args[] = { + { "use-fork", 'f', arg_flag, &use_fork, NULL, NULL }, + { "use-threads", 't', arg_flag, &use_threads, NULL, NULL }, + { "help", 'h', arg_flag, &help_flag, NULL, NULL }, + { "version", 0, arg_flag, &version_flag, NULL, NULL } +}; + +static int num_args = sizeof(args) / sizeof(args[0]); + +int +main(int argc, char **argv) +{ + char stemplate[sizeof("sqlite:testhdb-XXXXXX")]; +#ifdef HAVE_LMDB + char ltemplate[sizeof("lmdb:testhdb-XXXXXX")]; +#endif + int o = 0; + + setprogname(argv[0]); + + if (getarg(args, num_args, argc, argv, &o)) + krb5_std_usage(1, args, num_args); + + if (help_flag) + krb5_std_usage(0, args, num_args); + + if (version_flag){ + print_version(NULL); + return 0; + } + + if (!use_fork && !use_threads) + use_threads = use_fork = 1; + +#ifdef HAVE_FORK + if (use_fork) { + printf("Testing SQLite3 HDB backend (multi-process)\n"); + memcpy(stemplate, "sqlite:testhdb-XXXXXX", sizeof("sqlite:testhdb-XXXXXX")); + test_hdb_concurrency(stemplate, "", 0); + +#ifdef HAVE_LMDB + printf("Testing LMDB HDB backend (multi-process)\n"); + memcpy(ltemplate, "lmdb:testhdb-XXXXXX", sizeof("lmdb:testhdb-XXXXXX")); + test_hdb_concurrency(ltemplate, ".lmdb", 0); +#endif + } +#endif + + if (use_threads) { + printf("Testing SQLite3 HDB backend (multi-process)\n"); + memcpy(stemplate, "sqlite:testhdb-XXXXXX", sizeof("sqlite:testhdb-XXXXXX")); + test_hdb_concurrency(stemplate, "", 1); + +#ifdef HAVE_LMDB + printf("Testing LMDB HDB backend (multi-process)\n"); + memcpy(ltemplate, "lmdb:testhdb-XXXXXX", sizeof("lmdb:testhdb-XXXXXX")); + test_hdb_concurrency(ltemplate, ".lmdb", 1); +#endif + } + return 0; +} diff --git a/lib/hdb/test_dbinfo.c b/lib/hdb/test_dbinfo.c index b94b75bb3746..195fd4151dc4 100644 --- a/lib/hdb/test_dbinfo.c +++ b/lib/hdb/test_dbinfo.c @@ -44,6 +44,65 @@ struct getargs args[] = { static int num_args = sizeof(args) / sizeof(args[0]); +/* + * Prove that HDB_EntryOrAlias being a CHOICE of hdb_entry or hdb_entry_alias + * adds nothing to the encoding of those types. + */ +static +void +check_HDB_EntryOrAlias(krb5_context context) +{ + HDB_EntryOrAlias eoa; + hdb_entry entry; + hdb_entry_alias alias; + krb5_data v; + size_t len; + int ret; + + memset(&entry, 0, sizeof(entry)); + memset(&alias, 0, sizeof(alias)); + memset(&eoa, 0, sizeof(eoa)); + krb5_data_zero(&v); + + ret = krb5_make_principal(context, &alias.principal, "KTH.SE", "foo", + NULL); + if (ret) + krb5_err(context, 1, ret, "krb5_make_principal"); + ASN1_MALLOC_ENCODE(HDB_entry_alias, v.data, v.length, &alias, &len, ret); + if (ret) + krb5_err(context, 1, ret, "encode_HDB_EntryOrAlias"); + if (v.length != len) + abort(); + ret = decode_HDB_EntryOrAlias(v.data, v.length, &eoa, &len); + if (ret) + krb5_err(context, 1, ret, "decode_HDB_EntryOrAlias"); + if (v.length != len) + abort(); + free_HDB_EntryOrAlias(&eoa); + free_HDB_entry_alias(&alias); + krb5_data_free(&v); + + ret = krb5_make_principal(context, &entry.principal, "KTH.SE", "foo", + NULL); + if (ret) + krb5_err(context, 1, ret, "krb5_make_principal"); + entry.kvno = 5; + entry.flags.initial = 1; + ASN1_MALLOC_ENCODE(HDB_entry, v.data, v.length, &entry, &len, ret); + if (ret) + krb5_err(context, 1, ret, "encode_HDB_EntryOrAlias"); + if (v.length != len) + abort(); + ret = decode_HDB_EntryOrAlias(v.data, v.length, &eoa, &len); + if (ret) + krb5_err(context, 1, ret, "decode_HDB_EntryOrAlias"); + if (v.length != len) + abort(); + free_HDB_EntryOrAlias(&eoa); + free_HDB_entry(&entry); + krb5_data_free(&v); +} + int main(int argc, char **argv) { @@ -68,6 +127,8 @@ main(int argc, char **argv) if (ret) errx (1, "krb5_init_context failed: %d", ret); + check_HDB_EntryOrAlias(context); + ret = hdb_get_dbinfo(context, &info); if (ret) krb5_err(context, 1, ret, "hdb_get_dbinfo"); diff --git a/lib/hdb/test_hdbkeys.c b/lib/hdb/test_hdbkeys.c index 7cf4629b6ce0..d6bc31d4caf3 100644 --- a/lib/hdb/test_hdbkeys.c +++ b/lib/hdb/test_hdbkeys.c @@ -104,7 +104,7 @@ main(int argc, char **argv) krb5_free_principal (context, principal); - ASN1_MALLOC_ENCODE(hdb_keyset, data, length, &keyset, &len, ret); + ASN1_MALLOC_ENCODE(HDB_keyset, data, length, &keyset, &len, ret); if (ret) krb5_errx(context, 1, "encode keyset"); if (len != length) diff --git a/lib/hdb/test_hdbplugin.c b/lib/hdb/test_hdbplugin.c deleted file mode 100644 index 4ebceb8da072..000000000000 --- a/lib/hdb/test_hdbplugin.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2013 Jeffrey Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "hdb_locl.h" - -struct hdb_called { - int create; - int init; - int fini; -}; -struct hdb_called testresult; - -static krb5_error_code -hdb_test_create(krb5_context context, struct HDB **db, const char *arg) -{ - testresult.create = 1; - return 0; -} - -static krb5_error_code -hdb_test_init(krb5_context context, void **ctx) -{ - *ctx = NULL; - testresult.init = 1; - return 0; -} - -static void hdb_test_fini(void *ctx) -{ - testresult.fini = 1; -} - -struct hdb_method hdb_test = -{ -#ifdef WIN32 - /* Not c99 */ - HDB_INTERFACE_VERSION, - hdb_test_init, - hdb_test_fini, - "test", - hdb_test_create -#else - .version = HDB_INTERFACE_VERSION, - .init = hdb_test_init, - .fini = hdb_test_fini, - .prefix = "test", - .create = hdb_test_create -#endif -}; - -int -main(int argc, char **argv) -{ - krb5_error_code ret; - krb5_context context; - HDB *db; - - setprogname(argv[0]); - - ret = krb5_init_context(&context); - if (ret) - errx(1, "krb5_init_contex"); - - ret = krb5_plugin_register(context, - PLUGIN_TYPE_DATA, "hdb_test_interface", - &hdb_test); - if(ret) { - krb5_err(context, 1, ret, "krb5_plugin_register"); - } - - ret = hdb_create(context, &db, "test:test&1234"); - if(ret) { - krb5_err(context, 1, ret, "hdb_create"); - } - - krb5_free_context(context); - return 0; -} diff --git a/lib/hdb/test_namespace.c b/lib/hdb/test_namespace.c new file mode 100644 index 000000000000..f9b4cdbdde89 --- /dev/null +++ b/lib/hdb/test_namespace.c @@ -0,0 +1,941 @@ +/* + * Copyright (c) 2020 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. + */ + +/* + * This program implements an ephemeral, memory-based HDB backend, stores into + * it just one HDB entry -one for a namespace- then checks that virtual + * principals are returned below that namespace by hdb_fetch_kvno(), and that + * the logic for automatic key rotation of virtual principals is correct. + */ + +#include "hdb_locl.h" +#include <hex.h> + +static KeyRotation krs[2]; +static const char *base_pw[2] = { "Testing123...", "Tested123..." }; + +typedef struct { + HDB hdb; /* generic members */ + /* + * Make this dict a global, add a mutex lock around it, and a .finit and/or + * atexit() handler to free it, and we'd have a first-class MEMORY HDB. + * + * What would a first-class MEMORY HDB be good for though, besides testing? + * + * However, we could move this dict into `HDB' and then have _hdb_store() + * and friends support it as a cache for frequently-used & seldom-changing + * entries, such as: K/M, namespaces, and krbtgt principals. That would + * speed up lookups, especially for backends with poor reader-writer + * concurrency (DB, LMDB) and LDAP. Such entries could be cached for a + * minute or three at a time. + */ + heim_dict_t dict; +} TEST_HDB; + +struct hdb_called { + int create; + int init; + int fini; +}; + +static krb5_error_code +TDB_close(krb5_context context, HDB *db) +{ + return 0; +} + +static krb5_error_code +TDB_destroy(krb5_context context, HDB *db) +{ + TEST_HDB *tdb = (void *)db; + + heim_release(tdb->dict); + free(tdb->hdb.hdb_name); + free(tdb); + return 0; +} + +static krb5_error_code +TDB_set_sync(krb5_context context, HDB *db, int on) +{ + return 0; +} + +static krb5_error_code +TDB_lock(krb5_context context, HDB *db, int operation) +{ + + return 0; +} + +static krb5_error_code +TDB_unlock(krb5_context context, HDB *db) +{ + + return 0; +} + +static krb5_error_code +TDB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) +{ + /* XXX Implement */ + /* Tricky thing: heim_dict_iterate_f() is inconvenient here */ + /* We need this to check that virtual principals aren't created */ + return 0; +} + +static krb5_error_code +TDB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) +{ + /* XXX Implement */ + /* Tricky thing: heim_dict_iterate_f() is inconvenient here */ + /* We need this to check that virtual principals aren't created */ + return 0; +} + +static krb5_error_code +TDB_rename(krb5_context context, HDB *db, const char *new_name) +{ + return EEXIST; +} + +static krb5_error_code +TDB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply) +{ + krb5_error_code ret = 0; + TEST_HDB *tdb = (void *)db; + heim_object_t k, v = NULL; + + if ((k = heim_data_create(key.data, key.length)) == NULL) + ret = krb5_enomem(context); + if (ret == 0 && (v = heim_dict_get_value(tdb->dict, k)) == NULL) + ret = HDB_ERR_NOENTRY; + if (ret == 0) + ret = krb5_data_copy(reply, heim_data_get_ptr(v), heim_data_get_length(v)); + heim_release(k); + return ret; +} + +static krb5_error_code +TDB__put(krb5_context context, HDB *db, int rplc, krb5_data kd, krb5_data vd) +{ + krb5_error_code ret = 0; + TEST_HDB *tdb = (void *)db; + heim_object_t k = NULL; + heim_object_t v = NULL; + + if ((k = heim_data_create(kd.data, kd.length)) == NULL || + (v = heim_data_create(vd.data, vd.length)) == NULL) + ret = krb5_enomem(context); + if (ret == 0 && !rplc && heim_dict_get_value(tdb->dict, k) != NULL) + ret = HDB_ERR_EXISTS; + if (ret == 0 && heim_dict_set_value(tdb->dict, k, v)) + ret = krb5_enomem(context); + heim_release(k); + heim_release(v); + return ret; +} + +static krb5_error_code +TDB__del(krb5_context context, HDB *db, krb5_data key) +{ + krb5_error_code ret = 0; + TEST_HDB *tdb = (void *)db; + heim_object_t k; + + if ((k = heim_data_create(key.data, key.length)) == NULL) + ret = krb5_enomem(context); + if (ret == 0 && heim_dict_get_value(tdb->dict, k) == NULL) + ret = HDB_ERR_NOENTRY; + if (ret == 0) + heim_dict_delete_key(tdb->dict, k); + heim_release(k); + return ret; +} + +static krb5_error_code +TDB_open(krb5_context context, HDB *db, int flags, mode_t mode) +{ + return 0; +} + +static krb5_error_code +hdb_test_create(krb5_context context, struct HDB **db, const char *arg) +{ + TEST_HDB *tdb; + + if ((tdb = calloc(1, sizeof(tdb[0]))) == NULL || + (tdb->hdb.hdb_name = strdup(arg)) == NULL || + (tdb->dict = heim_dict_create(10)) == NULL) { + if (tdb) + free(tdb->hdb.hdb_name); + free(tdb); + return krb5_enomem(context); + } + + tdb->hdb.hdb_db = NULL; + tdb->hdb.hdb_master_key_set = 0; + tdb->hdb.hdb_openp = 0; + tdb->hdb.hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL; + tdb->hdb.hdb_open = TDB_open; + tdb->hdb.hdb_close = TDB_close; + tdb->hdb.hdb_fetch_kvno = _hdb_fetch_kvno; + tdb->hdb.hdb_store = _hdb_store; + tdb->hdb.hdb_remove = _hdb_remove; + tdb->hdb.hdb_firstkey = TDB_firstkey; + tdb->hdb.hdb_nextkey= TDB_nextkey; + tdb->hdb.hdb_lock = TDB_lock; + tdb->hdb.hdb_unlock = TDB_unlock; + tdb->hdb.hdb_rename = TDB_rename; + tdb->hdb.hdb__get = TDB__get; + tdb->hdb.hdb__put = TDB__put; + tdb->hdb.hdb__del = TDB__del; + tdb->hdb.hdb_destroy = TDB_destroy; + tdb->hdb.hdb_set_sync = TDB_set_sync; + *db = &tdb->hdb; + + return 0; +} + +static krb5_error_code +hdb_test_init(krb5_context context, void **ctx) +{ + *ctx = NULL; + return 0; +} + +static void hdb_test_fini(void *ctx) +{ +} + +struct hdb_method hdb_test = +{ +#ifdef WIN32 + /* Not c99 */ + HDB_INTERFACE_VERSION, + hdb_test_init, + hdb_test_fini, + 1 /*is_file_based*/, 1 /*can_taste*/, + "test", + hdb_test_create +#else + .minor_version = HDB_INTERFACE_VERSION, + .init = hdb_test_init, + .fini = hdb_test_fini, + .is_file_based = 1, + .can_taste = 1, + .prefix = "test", + .create = hdb_test_create +#endif +}; + +static krb5_error_code +make_base_key(krb5_context context, + krb5_const_principal p, + const char *pw, + krb5_keyblock *k) +{ + return krb5_string_to_key(context, KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + pw, p, k); +} + +static krb5_error_code +tderive_key(krb5_context context, + const char *p, + KeyRotation *kr, + int toffset, + krb5_keyblock *base, + krb5int32 etype, + krb5_keyblock *k, + uint32_t *kvno, + time_t *set_time) +{ + krb5_error_code ret = 0; + krb5_crypto crypto = NULL; + EncryptionKey intermediate; + krb5_data pad, out; + size_t len; + int n; + + n = toffset / kr->period; + *set_time = kr->epoch + kr->period * n; + *kvno = kr->base_kvno + n; + + out.data = 0; + out.length = 0; + + /* Derive intermediate key */ + pad.data = (void *)(uintptr_t)p; + pad.length = strlen(p); + ret = krb5_enctype_keysize(context, base->keytype, &len); + if (ret == 0) + ret = krb5_crypto_init(context, base, 0, &crypto); + if (ret == 0) + ret = krb5_crypto_prfplus(context, crypto, &pad, len, &out); + if (crypto) + krb5_crypto_destroy(context, crypto); + crypto = NULL; + if (ret == 0) + ret = krb5_random_to_key(context, etype, out.data, out.length, + &intermediate); + krb5_data_free(&out); + + /* Derive final key */ + pad.data = kvno; + pad.length = sizeof(*kvno); + if (ret == 0) + ret = krb5_enctype_keysize(context, etype, &len); + if (ret == 0) + ret = krb5_crypto_init(context, &intermediate, 0, &crypto); + if (ret == 0) { + *kvno = htonl(*kvno); + ret = krb5_crypto_prfplus(context, crypto, &pad, len, &out); + *kvno = ntohl(*kvno); + } + if (crypto) + krb5_crypto_destroy(context, crypto); + if (ret == 0) + ret = krb5_random_to_key(context, etype, out.data, out.length, k); + krb5_data_free(&out); + + free_EncryptionKey(&intermediate); + return ret; +} + +/* Create a namespace principal */ +static void +make_namespace(krb5_context context, HDB *db, const char *name) +{ + krb5_error_code ret = 0; + hdb_entry e; + Key k; + + memset(&k, 0, sizeof(k)); + k.mkvno = 0; + k.salt = 0; + + /* Setup the HDB entry */ + memset(&e, 0, sizeof(e)); + e.created_by.time = krs[0].epoch; + e.valid_start = e.valid_end = e.pw_end = 0; + e.generation = 0; + e.flags = int2HDBFlags(0); + e.flags.server = e.flags.client = 1; + e.flags.virtual = 1; + + /* Setup etypes */ + if (ret == 0 && + (e.etypes = malloc(sizeof(*e.etypes))) == NULL) + ret = krb5_enomem(context); + if (ret == 0) + e.etypes->len = 3; + if (ret == 0 && + (e.etypes->val = calloc(e.etypes->len, + sizeof(e.etypes->val[0]))) == NULL) + ret = krb5_enomem(context); + if (ret == 0) { + e.etypes->val[0] = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128; + e.etypes->val[1] = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192; + e.etypes->val[2] = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96; + } + + /* Setup max_life and max_renew */ + if (ret == 0 && + (e.max_life = malloc(sizeof(*e.max_life))) == NULL) + ret = krb5_enomem(context); + if (ret == 0 && + (e.max_renew = malloc(sizeof(*e.max_renew))) == NULL) + ret = krb5_enomem(context); + if (ret == 0) + /* Make it long, so we see the clamped max */ + *e.max_renew = 2 * ((*e.max_life = 15 * 24 * 3600)); + + /* Setup principal name and created_by */ + if (ret == 0) + ret = krb5_parse_name(context, name, &e.principal); + if (ret == 0) + ret = krb5_parse_name(context, "admin@BAR.EXAMPLE", + &e.created_by.principal); + + /* Make base keys for first epoch */ + if (ret == 0) + ret = make_base_key(context, e.principal, base_pw[0], &k.key); + if (ret == 0) + add_Keys(&e.keys, &k); + if (ret == 0) + ret = hdb_entry_set_pw_change_time(context, &e, krs[0].epoch); + free_Key(&k); + e.kvno = krs[0].base_key_kvno; + + /* Move them to history */ + if (ret == 0) + ret = hdb_add_current_keys_to_history(context, &e); + free_Keys(&e.keys); + + /* Make base keys for second epoch */ + if (ret == 0) + ret = make_base_key(context, e.principal, base_pw[1], &k.key); + if (ret == 0) + add_Keys(&e.keys, &k); + e.kvno = krs[1].base_key_kvno; + if (ret == 0) + ret = hdb_entry_set_pw_change_time(context, &e, krs[1].epoch); + + /* Add the key rotation metadata */ + if (ret == 0) + ret = hdb_entry_add_key_rotation(context, &e, 0, &krs[0]); + if (ret == 0) + ret = hdb_entry_add_key_rotation(context, &e, 0, &krs[1]); + + if (ret == 0) + ret = db->hdb_store(context, db, 0, &e); + if (ret) + krb5_err(context, 1, ret, "failed to setup a namespace principal"); + free_Key(&k); + hdb_free_entry(context, db, &e); +} + +#define WK_PREFIX "WELLKNOWN/" HDB_WK_NAMESPACE "/" + +static const char *expected[] = { + WK_PREFIX "_/bar.example@BAR.EXAMPLE", + "HTTP/bar.example@BAR.EXAMPLE", + "HTTP/foo.bar.example@BAR.EXAMPLE", + "host/foo.bar.example@BAR.EXAMPLE", + "HTTP/blah.foo.bar.example@BAR.EXAMPLE", +}; +static const char *unexpected[] = { + WK_PREFIX "_/no.example@BAZ.EXAMPLE", + "HTTP/no.example@BAR.EXAMPLE", + "HTTP/foo.no.example@BAR.EXAMPLE", + "HTTP/blah.foo.no.example@BAR.EXAMPLE", +}; + +/* + * We'll fetch as many entries as we have principal names in `expected[]', for + * as many KeyRotation periods as we have (between 1 and 3), and for up to 5 + * different time offsets in each period. + */ +#define NUM_OFFSETS 5 +static hdb_entry e[ + (sizeof(expected) / sizeof(expected[0])) * + (sizeof(krs) / sizeof(krs[0])) * + NUM_OFFSETS +]; + +static int +hist_key_compar(const void *va, const void *vb) +{ + const hdb_keyset *a = va; + const hdb_keyset *b = vb; + + return a->kvno - b->kvno; +} + +/* + * Fetch keys for some decent time in the given kr. + * + * `kr' is an index into the global `krs[]'. + * `t' is a number 0..4 inclusive that identifies a time period relative to the + * epoch of `krs[kr]' (see code below). + */ +static void +fetch_entries(krb5_context context, + HDB *db, + size_t kr, + size_t t, + int must_fail) +{ + krb5_error_code ret = 0; + krb5_principal p = NULL; + krb5_keyblock base_key, dk; + hdb_entry *ep; + hdb_entry no; + size_t i, b; + int toffset = 0; + + memset(&base_key, 0, sizeof(base_key)); + + /* Work out offset of first entry in `e[]' */ + assert(kr < sizeof(krs) / sizeof(krs[0])); + assert(t < NUM_OFFSETS); + b = (kr * NUM_OFFSETS + t) * (sizeof(expected) / sizeof(expected[0])); + assert(b < sizeof(e) / sizeof(e[0])); + assert(sizeof(e) / sizeof(e[0]) - b >= + (sizeof(expected) / sizeof(expected[0]))); + + switch (t) { + case 0: toffset = 1; break; /* epoch + 1s */ + case 1: toffset = 1 + (krs[kr].period >> 1); break; /* epoch + period/2 */ + case 2: toffset = 1 + (krs[kr].period >> 2); break; /* epoch + period/4 */ + case 3: toffset = 1 + (krs[kr].period >> 3); break; /* epoch + period/8 */ + case 4: toffset = 1 - (krs[kr].period >> 3); break; /* epoch - period/8 */ + } + + for (i = 0; ret == 0 && i < sizeof(expected) / sizeof(expected[0]); i++) { + ep = &e[b + i]; + memset(ep, 0, sizeof(*ep)); + if (ret == 0) + ret = krb5_parse_name(context, expected[i], &p); + if (ret == 0 && i == 0) { + if (toffset < 0 && kr) + ret = make_base_key(context, p, base_pw[kr - 1], &base_key); + else + ret = make_base_key(context, p, base_pw[kr], &base_key); + } + if (ret == 0) + ret = hdb_fetch_kvno(context, db, p, + HDB_F_DECRYPT | HDB_F_ALL_KVNOS, + krs[kr].epoch + toffset, 0, 0, ep); + if (i && must_fail && ret == 0) + krb5_errx(context, 1, + "virtual principal that shouldn't exist does"); + if (kr == 0 && toffset < 0 && ret == HDB_ERR_NOENTRY) + continue; + if (kr == 0 && toffset < 0) { + /* + * Virtual principals don't exist before their earliest key + * rotation epoch's start time. + */ + if (i == 0) { + if (ret) + krb5_errx(context, 1, + "namespace principal does not exist before its time"); + } else if (i != 0) { + if (ret == 0) + krb5_errx(context, 1, + "virtual principal exists before its time"); + if (ret != HDB_ERR_NOENTRY) + krb5_errx(context, 1, "wrong error code"); + ret = 0; + } + } else { + if (ret == 0 && + !krb5_principal_compare(context, p, ep->principal)) + krb5_errx(context, 1, "wrong principal in fetched entry"); + } + + { + HDB_Ext_KeySet *hist_keys; + HDB_extension *ext; + ext = hdb_find_extension(ep, + choice_HDB_extension_data_hist_keys); + if (ext) { + /* Sort key history by kvno, why not */ + hist_keys = &ext->data.u.hist_keys; + qsort(hist_keys->val, hist_keys->len, + sizeof(hist_keys->val[0]), hist_key_compar); + } + } + + krb5_free_principal(context, p); + } + if (ret && must_fail) { + free_EncryptionKey(&base_key); + return; + } + if (ret) + krb5_err(context, 1, ret, "virtual principal test failed"); + + for (i = 0; i < sizeof(unexpected) / sizeof(unexpected[0]); i++) { + memset(&no, 0, sizeof(no)); + if (ret == 0) + ret = krb5_parse_name(context, unexpected[i], &p); + if (ret == 0) + ret = hdb_fetch_kvno(context, db, p, HDB_F_DECRYPT, + krs[kr].epoch + toffset, 0, 0, &no); + if (ret == 0) + krb5_errx(context, 1, "bogus principal exists, wat"); + krb5_free_principal(context, p); + ret = 0; + } + + if (kr == 0 && toffset < 0) + return; + + /* + * XXX + * + * Add check that derived keys are a) different, b) as expected, using a + * set of test vectors or else by computing the expected keys here with + * code that's not shared with lib/hdb/common.c. + * + * Add check that we get expected past and/or future keys, not just current + * keys. + */ + for (i = 1; ret == 0 && i < sizeof(expected) / sizeof(expected[0]); i++) { + uint32_t kvno; + time_t set_time, chg_time; + + ep = &e[b + i]; + if (toffset > 0) { + ret = tderive_key(context, expected[i], &krs[kr], toffset, + &base_key, base_key.keytype, &dk, &kvno, &set_time); + } else /* XXX */{ + /* XXX */ + assert(kr); + ret = tderive_key(context, expected[i], &krs[kr - 1], + krs[kr].epoch - krs[kr - 1].epoch + toffset, + &base_key, base_key.keytype, &dk, &kvno, &set_time); + } + if (ret) + krb5_err(context, 1, ret, "deriving keys for comparison"); + + if (kvno != ep->kvno) + krb5_errx(context, 1, "kvno mismatch (%u != %u)", kvno, ep->kvno); + (void) hdb_entry_get_pw_change_time(ep, &chg_time); + if (set_time != chg_time) + krb5_errx(context, 1, "key change time mismatch"); + if (ep->keys.len == 0) + krb5_errx(context, 1, "no keys!"); + if (ep->keys.val[0].key.keytype != dk.keytype) + krb5_errx(context, 1, "enctype mismatch!"); + if (ep->keys.val[0].key.keyvalue.length != + dk.keyvalue.length) + krb5_errx(context, 1, "key length mismatch!"); + if (memcmp(ep->keys.val[0].key.keyvalue.data, + dk.keyvalue.data, dk.keyvalue.length) != 0) + krb5_errx(context, 1, "key mismatch!"); + if (memcmp(ep->keys.val[0].key.keyvalue.data, + e[b + i - 1].keys.val[0].key.keyvalue.data, + dk.keyvalue.length) == 0) + krb5_errx(context, 1, "different virtual principals have the same keys!"); + /* XXX Add check that we have the expected number of history keys */ + free_EncryptionKey(&dk); + } + free_EncryptionKey(&base_key); +} + +static void +check_kvnos(krb5_context context) +{ + HDB_Ext_KeySet keysets; + size_t i, k, m, p; /* iterator indices */ + + keysets.len = 0; + keysets.val = 0; + + /* For every principal name */ + for (i = 0; i < sizeof(expected)/sizeof(expected[0]); i++) { + free_HDB_Ext_KeySet(&keysets); + + /* For every entry we've fetched for it */ + for (k = 0; k < sizeof(e)/sizeof(e[0]); k++) { + HDB_Ext_KeySet *hist_keys; + HDB_extension *ext; + hdb_entry *ep; + int match = 0; + + if ((k % NUM_OFFSETS) != i) + continue; + + ep = &e[k]; + if (ep->principal == NULL) + continue; /* Didn't fetch this one */ + + /* + * Check that the current keys for it match what we've seen already + * or else add them to `keysets'. + */ + for (m = 0; m < keysets.len; m++) { + if (ep->kvno == keysets.val[m].kvno) { + /* Check the key is the same */ + if (ep->keys.val[0].key.keytype != + keysets.val[m].keys.val[0].key.keytype || + ep->keys.val[0].key.keyvalue.length != + keysets.val[m].keys.val[0].key.keyvalue.length || + memcmp(ep->keys.val[0].key.keyvalue.data, + keysets.val[m].keys.val[0].key.keyvalue.data, + ep->keys.val[0].key.keyvalue.length) != 0) + krb5_errx(context, 1, + "key mismatch for same princ & kvno"); + match = 1; + } + } + if (m == keysets.len) { + hdb_keyset ks; + + ks.kvno = ep->kvno; + ks.keys = ep->keys; + ks.set_time = 0; + if (add_HDB_Ext_KeySet(&keysets, &ks)) + krb5_err(context, 1, ENOMEM, "out of memory"); + match = 1; + } + if (match) + continue; + + /* For all non-current keysets, repeat the above */ + ext = hdb_find_extension(ep, + choice_HDB_extension_data_hist_keys); + if (!ext) + continue; + hist_keys = &ext->data.u.hist_keys; + for (p = 0; p < hist_keys->len; p++) { + for (m = 0; m < keysets.len; m++) { + if (keysets.val[m].kvno == hist_keys->val[p].kvno) + if (ep->keys.val[0].key.keytype != + keysets.val[m].keys.val[0].key.keytype || + ep->keys.val[0].key.keyvalue.length != + keysets.val[m].keys.val[0].key.keyvalue.length || + memcmp(ep->keys.val[0].key.keyvalue.data, + keysets.val[m].keys.val[0].key.keyvalue.data, + ep->keys.val[0].key.keyvalue.length) != 0) + krb5_errx(context, 1, + "key mismatch for same princ & kvno"); + } + if (m == keysets.len) { + hdb_keyset ks; + ks.kvno = ep->kvno; + ks.keys = ep->keys; + ks.set_time = 0; + if (add_HDB_Ext_KeySet(&keysets, &ks)) + krb5_err(context, 1, ENOMEM, "out of memory"); + } + } + } + } + free_HDB_Ext_KeySet(&keysets); +} + +static void +print_em(krb5_context context) +{ + HDB_Ext_KeySet *hist_keys; + HDB_extension *ext; + size_t i, p; + + for (i = 0; i < sizeof(e)/sizeof(e[0]); i++) { + const char *name = expected[i % (sizeof(expected)/sizeof(expected[0]))]; + char *x; + + if (0 == i % (sizeof(expected)/sizeof(expected[0]))) + continue; + if (e[i].principal == NULL) + continue; + hex_encode(e[i].keys.val[0].key.keyvalue.data, + e[i].keys.val[0].key.keyvalue.length, &x); + printf("%s %u %s\n", x, e[i].kvno, name); + free(x); + + ext = hdb_find_extension(&e[i], choice_HDB_extension_data_hist_keys); + if (!ext) + continue; + hist_keys = &ext->data.u.hist_keys; + for (p = 0; p < hist_keys->len; p++) { + hex_encode(hist_keys->val[p].keys.val[0].key.keyvalue.data, + hist_keys->val[p].keys.val[0].key.keyvalue.length, &x); + printf("%s %u %s\n", x, hist_keys->val[p].kvno, name); + free(x); + } + } +} + +#if 0 +static void +check_expected_kvnos(krb5_context context) +{ + HDB_Ext_KeySet *hist_keys; + HDB_extension *ext; + size_t i, k, m, p; + + for (i = 0; i < sizeof(expected)/sizeof(expected[0]); i++) { + for (k = 0; k < sizeof(krs)/sizeof(krs[0]); k++) { + hdb_entry *ep = &e[k * sizeof(expected)/sizeof(expected[0]) + i]; + + if (ep->principal == NULL) + continue; + for (m = 0; m < NUM_OFFSETS; m++) { + ext = hdb_find_extension(ep, + choice_HDB_extension_data_hist_keys); + if (!ext) + continue; + hist_keys = &ext->data.u.hist_keys; + for (p = 0; p < hist_keys->len; p++) { + fprintf(stderr, "%s at %lu, %lu: history kvno %u\n", + expected[i], k, m, hist_keys->val[p].kvno); + } + } + fprintf(stderr, "%s at %lu: kvno %u\n", expected[i], k, + ep->kvno); + } + } +} +#endif + +#define SOME_TIME 1596318329 +#define SOME_BASE_KVNO 150 +#define SOME_EPOCH (SOME_TIME - (7 * 24 * 3600) - (SOME_TIME % (7 * 24 * 3600))) +#define SOME_PERIOD 3600 + +#define CONF \ + "[hdb]\n" \ + "\tenable_virtual_hostbased_princs = true\n" \ + "\tvirtual_hostbased_princ_mindots = 1\n" \ + "\tvirtual_hostbased_princ_maxdots = 3\n" \ + +int +main(int argc, char **argv) +{ + krb5_error_code ret; + krb5_context context; + size_t i; + HDB *db = NULL; + + setprogname(argv[0]); + memset(e, 0, sizeof(e)); + ret = krb5_init_context(&context); + if (ret == 0) + ret = krb5_set_config(context, CONF); + if (ret == 0) + ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA, "hdb_test_interface", + &hdb_test); + if (ret == 0) + ret = hdb_create(context, &db, "test:mem"); + if (ret) + krb5_err(context, 1, ret, "failed to setup HDB driver and test"); + + assert(db->enable_virtual_hostbased_princs); + assert(db->virtual_hostbased_princ_ndots == 1); + assert(db->virtual_hostbased_princ_maxdots == 3); + + /* Setup key rotation metadata in a convenient way */ + /* + * FIXME Reorder these two KRs to match how we store them to avoid + * confusion. #0 should be future-most, #1 should past-post. + */ + krs[0].flags = krs[1].flags = int2KeyRotationFlags(0); + krs[0].epoch = SOME_EPOCH - 20 * 24 * 3600; + krs[0].period = SOME_PERIOD >> 1; + krs[0].base_kvno = 150; + krs[0].base_key_kvno = 1; + krs[1].epoch = SOME_TIME; + krs[1].period = SOME_PERIOD; + krs[1].base_kvno = krs[0].base_kvno + 1 + (krs[1].epoch + (krs[0].period - 1) - krs[0].epoch) / krs[0].period; + krs[1].base_key_kvno = 2; + + { + HDB_Ext_KeyRotation existing_krs, new_krs; + KeyRotation ordered_krs[2]; + + ordered_krs[0] = krs[1]; + ordered_krs[1] = krs[0]; + existing_krs.len = 0; + existing_krs.val = 0; + new_krs.len = 1; + new_krs.val = &ordered_krs[1]; + if ((ret = hdb_validate_key_rotations(context, NULL, &new_krs)) || + (ret = hdb_validate_key_rotations(context, &existing_krs, + &new_krs))) + krb5_err(context, 1, ret, "Valid KeyRotation thought invalid"); + new_krs.len = 1; + new_krs.val = &ordered_krs[0]; + if ((ret = hdb_validate_key_rotations(context, NULL, &new_krs)) || + (ret = hdb_validate_key_rotations(context, &existing_krs, + &new_krs))) + krb5_err(context, 1, ret, "Valid KeyRotation thought invalid"); + new_krs.len = 2; + new_krs.val = &ordered_krs[0]; + if ((ret = hdb_validate_key_rotations(context, NULL, &new_krs)) || + (ret = hdb_validate_key_rotations(context, &existing_krs, + &new_krs))) + krb5_err(context, 1, ret, "Valid KeyRotation thought invalid"); + existing_krs.len = 1; + existing_krs.val = &ordered_krs[1]; + if ((ret = hdb_validate_key_rotations(context, &existing_krs, + &new_krs))) + krb5_err(context, 1, ret, "Valid KeyRotation thought invalid"); + existing_krs.len = 2; + existing_krs.val = &ordered_krs[0]; + if ((ret = hdb_validate_key_rotations(context, &existing_krs, + &new_krs))) + krb5_err(context, 1, ret, "Valid KeyRotation thought invalid"); + + new_krs.len = 2; + new_krs.val = &krs[0]; + if ((ret = hdb_validate_key_rotations(context, &existing_krs, + &new_krs)) == 0) + krb5_errx(context, 1, "Invalid KeyRotation thought valid"); + } + + make_namespace(context, db, WK_PREFIX "_/bar.example@BAR.EXAMPLE"); + + fetch_entries(context, db, 1, 0, 0); + fetch_entries(context, db, 1, 1, 0); + fetch_entries(context, db, 1, 2, 0); + fetch_entries(context, db, 1, 3, 0); + fetch_entries(context, db, 1, 4, 0); /* Just before newest KR */ + + fetch_entries(context, db, 0, 0, 0); + fetch_entries(context, db, 0, 1, 0); + fetch_entries(context, db, 0, 2, 0); + fetch_entries(context, db, 0, 3, 0); + fetch_entries(context, db, 0, 4, 1); /* Must fail: just before 1st KR */ + + /* + * Check that for every virtual principal in `expected[]', all the keysets + * with the same kvno, in all the entries fetched for different times, + * match. + */ + check_kvnos(context); + +#if 0 + /* + * Check that for every virtual principal in `expected[]' we have the + * expected key history. + */ + check_expected_kvnos(context); +#endif + + /* + * XXX Add various tests here, checking `e[]': + * + * - Extract all {principal, kvno, key} for all keys, current and + * otherwise, then sort by {key, kvno, principal}, then check that the + * only time we have matching keys is when the kvno and principal also + * match. + */ + + print_em(context); + + /* + * XXX Test adding a third KR, a 4th KR, dropping KRs... + */ + + /* Cleanup */ + for (i = 0; ret == 0 && i < sizeof(e) / sizeof(e[0]); i++) + hdb_free_entry(context, db, &e[i]); + db->hdb_destroy(context, db); + krb5_free_context(context); + return 0; +} diff --git a/lib/hdb/version-script.map b/lib/hdb/version-script.map index 579a2efe7c41..058060dae0c1 100644 --- a/lib/hdb/version-script.map +++ b/lib/hdb/version-script.map @@ -2,9 +2,13 @@ HEIMDAL_HDB_1.0 { global: - encode_hdb_keyset; - hdb_add_master_key; + _hdb_fetch_kvno; + _hdb_remove; + _hdb_store; hdb_add_current_keys_to_history; + hdb_add_history_key; + hdb_add_history_keyset; + hdb_add_master_key; hdb_change_kvno; hdb_check_db_format; hdb_clear_extension; @@ -20,21 +24,27 @@ HEIMDAL_HDB_1.0 { hdb_dbinfo_get_next; hdb_dbinfo_get_realm; hdb_default_db; + hdb_derive_etypes; hdb_enctype2key; hdb_entry2string; hdb_entry2value; + hdb_entry_add_key_rotation; hdb_entry_alias2value; hdb_entry_check_mandatory; hdb_entry_clear_password; hdb_entry_get_ConstrainedDelegACL; hdb_entry_get_aliases; + hdb_entry_get_key_rotation; + hdb_entry_get_krb5_config; hdb_entry_get_password; hdb_entry_get_pkinit_acl; hdb_entry_get_pkinit_cert; hdb_entry_get_pkinit_hash; hdb_entry_get_pw_change_time; + hdb_entry_set_krb5_config; hdb_entry_set_password; hdb_entry_set_pw_change_time; + hdb_fetch_kvno; hdb_find_extension; hdb_foreach; hdb_free_dbinfo; @@ -46,7 +56,9 @@ HEIMDAL_HDB_1.0 { hdb_generate_key_set_password; hdb_generate_key_set_password_with_ks_tuple; hdb_get_dbinfo; + hdb_get_instance; hdb_init_db; + hdb_install_keyset; hdb_key2principal; hdb_kvno2keys; hdb_list_builtin; @@ -56,7 +68,9 @@ HEIMDAL_HDB_1.0 { hdb_print_entry; hdb_process_master_key; hdb_prune_keys; + hdb_prune_keys_kvno; hdb_read_master_key; + hdb_remove_keys; hdb_replace_extension; hdb_seal_key; hdb_seal_key_mkey; @@ -69,11 +83,15 @@ HEIMDAL_HDB_1.0 { hdb_unseal_key; hdb_unseal_key_mkey; hdb_unseal_keys; + hdb_unseal_keys_kvno; hdb_unseal_keys_mkey; + hdb_validate_key_rotation; + hdb_validate_key_rotations; hdb_value2entry; hdb_value2entry_alias; hdb_write_master_key; length_hdb_keyset; + length_HDB_keyset; hdb_interface_version; initialize_hdb_error_table_r; @@ -85,44 +103,86 @@ HEIMDAL_HDB_1.0 { hdb_get_kt_ops; # some random bits needed for libkadm + add_HDB_Ext_KeyRotation; + add_HDB_Ext_KeySet; add_HDB_Ext_KeySet; add_Keys; + add_Keys; asn1_HDBFlags_units; copy_Event; + copy_HDB_EncTypeList; + copy_hdb_entry; + copy_hdb_entry_alias; + copy_HDB_entry; + copy_HDB_entry_alias; + copy_HDB_EntryOrAlias; copy_HDB_extensions; + copy_HDB_Ext_KeyRotation; copy_Key; copy_Keys; copy_Salt; + decode_HDB_EncTypeList; + decode_hdb_entry; + decode_hdb_entry_alias; + decode_HDB_entry; + decode_HDB_entry_alias; + decode_HDB_EntryOrAlias; decode_HDB_Ext_Aliases; decode_HDB_extension; + decode_HDB_Ext_KeyRotation; decode_HDB_Ext_PKINIT_acl; decode_Key; decode_Keys; + encode_HDB_EncTypeList; + encode_hdb_entry; + encode_hdb_entry_alias; + encode_HDB_entry; + encode_HDB_entry_alias; + encode_HDB_EntryOrAlias; encode_HDB_Ext_Aliases; encode_HDB_extension; + encode_HDB_Ext_KeyRotation; encode_HDB_Ext_PKINIT_acl; + encode_hdb_keyset; + encode_HDB_keyset; encode_Key; encode_Keys; free_Event; + free_HDB_EncTypeList; free_hdb_entry; + free_hdb_entry_alias; + free_HDB_entry; + free_HDB_entry_alias; + free_HDB_EntryOrAlias; free_HDB_Ext_Aliases; free_HDB_extension; free_HDB_extensions; + free_HDB_Ext_KeyRotation; + free_HDB_Ext_KeySet; free_HDB_Ext_PKINIT_acl; free_hdb_keyset; + free_HDB_keyset; free_Key; free_Keys; free_Salt; HDBFlags2int; int2HDBFlags; + int2KeyRotationFlags; + KeyRotationFlags2int; + length_HDB_EncTypeList; + length_hdb_entry; + length_hdb_entry_alias; + length_HDB_entry; + length_HDB_entry_alias; + length_HDB_EntryOrAlias; length_HDB_Ext_Aliases; length_HDB_extension; + length_HDB_Ext_KeyRotation; length_HDB_Ext_PKINIT_acl; length_Key; length_Keys; + remove_HDB_Ext_KeyRotation; remove_Keys; - add_Keys; - add_HDB_Ext_KeySet; local: *; |
