diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2023-06-26 22:56:52 +0000 |
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2023-06-26 22:56:52 +0000 |
| commit | b6a943f7197af1a5eb6bb028b9b808ec5016e30c (patch) | |
| tree | cfbb91e940dd89d0e1d46095f43c228d7d079fa0 /lib/base | |
| parent | 6f4e10db3298f6d65e1e646fe52aaafc3682b788 (diff) | |
Diffstat (limited to 'lib/base')
| -rw-r--r-- | lib/base/Makefile.am | 47 | ||||
| -rw-r--r-- | lib/base/Makefile.in | 1567 | ||||
| -rw-r--r-- | lib/base/NTMakefile | 60 | ||||
| -rw-r--r-- | lib/base/array.c | 4 | ||||
| -rw-r--r-- | lib/base/baselocl.h | 107 | ||||
| -rw-r--r-- | lib/base/common_plugin.h | 106 | ||||
| -rw-r--r-- | lib/base/config_file.c | 1472 | ||||
| -rw-r--r-- | lib/base/config_reg.c | 658 | ||||
| -rw-r--r-- | lib/base/context.c | 394 | ||||
| -rw-r--r-- | lib/base/data.c | 9 | ||||
| -rw-r--r-- | lib/base/db.c | 31 | ||||
| -rw-r--r-- | lib/base/dict.c | 8 | ||||
| -rw-r--r-- | lib/base/error.c | 7 | ||||
| -rw-r--r-- | lib/base/error_string.c | 177 | ||||
| -rw-r--r-- | lib/base/expand_path.c | 725 | ||||
| -rw-r--r-- | lib/base/heim_err.et | 57 | ||||
| -rw-r--r-- | lib/base/heimbase-atomics.h | 386 | ||||
| -rw-r--r-- | lib/base/heimbase-svc.h | 83 | ||||
| -rw-r--r-- | lib/base/heimbase.c | 111 | ||||
| -rw-r--r-- | lib/base/heimbase.h | 301 | ||||
| -rw-r--r-- | lib/base/heimbasepriv.h | 28 | ||||
| -rw-r--r-- | lib/base/heimqueue.h | 167 | ||||
| -rw-r--r-- | lib/base/json.c | 864 | ||||
| -rw-r--r-- | lib/base/log.c | 1079 | ||||
| -rw-r--r-- | lib/base/number.c | 22 | ||||
| -rw-r--r-- | lib/base/plugin.c | 785 | ||||
| -rw-r--r-- | lib/base/string.c | 13 | ||||
| -rw-r--r-- | lib/base/test_base.c | 437 | ||||
| -rw-r--r-- | lib/base/version-script.map | 137 | ||||
| -rw-r--r-- | lib/base/warn.c | 169 |
30 files changed, 7765 insertions, 2246 deletions
diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index c5f857f4daeb..606e00a975c4 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -11,7 +11,7 @@ IMPLEMENT_TLS += dll.c AM_CPPFLAGS += -DHEIM_BASE_MAINTAINER endif -AM_CPPFLAGS += $(ROKEN_RENAME) +AM_CPPFLAGS += $(ROKEN_RENAME) -I../com_err -I$(srcdir)/../com_err lib_LTLIBRARIES = libheimbase.la check_PROGRAMS = test_base @@ -20,36 +20,61 @@ test_base_CFLAGS = -Wno-string-concatenation libheimbase_la_LDFLAGS = -version-info 1:0:0 +if FRAMEWORK_COREFOUNDATION +libheimbase_la_LDFLAGS += -framework CoreFoundation +endif + TESTS = test_base if versionscript libheimbase_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map endif -libheimbase_la_LIBADD = $(PTHREAD_LIBADD) +libheimbase_la_LIBADD = $(PTHREAD_LIBADD) $(LIB_dlopen) $(LIB_com_err) + +include_HEADERS = heimbase.h common_plugin.h heimbase-atomics.h heimbase-svc.h -include_HEADERS = heimbase.h +ERR_FILES = heim_err.c dist_libheimbase_la_SOURCES = \ array.c \ baselocl.h \ bsearch.c \ bool.c \ + common_plugin.h \ + config_file.c \ + context.c \ data.c \ db.c \ dict.c \ $(IMPLEMENT_TLS) \ error.c \ + error_string.c \ + expand_path.c \ heimbase.c \ heimbasepriv.h \ - heimqueue.h \ json.c \ + log.c \ null.c \ number.c \ + plugin.c \ roken_rename.h \ - string.c + string.c \ + warn.c + +nodist_libheimbase_la_SOURCES = $(ES) $(ERR_FILES) + +$(libheimbase_la_OBJECTS): $(srcdir)/heimbase-protos.h heim_err.h -nodist_libheimbase_la_SOURCES = $(ES) +$(srcdir)/heimbase-protos.h: $(dist_libheimbase_la_SOURCES) + cd $(srcdir) && \ + perl ../../cf/make-proto.pl -q -P comment -o heimbase-protos.h $(dist_libheimbase_la_SOURCES) || \ + rm -f heimbase-protos.h + +$(srcdir)/heimbase-private.h: $(dist_libheimbase_la_SOURCES) + cd $(srcdir) && \ + perl ../../cf/make-proto.pl -q -P comment -p heimbase-private.h $(dist_libheimbase_la_SOURCES) || \ + rm -f heimbase-private.h # install these? @@ -57,9 +82,15 @@ libheimbase_la_DEPENDENCIES = version-script.map test_base_LDADD = libheimbase.la $(LIB_roken) -CLEANFILES = base64.c test_db.json +CLEANFILES = base64.c test_db.json heim_err.c heim_err.h + +EXTRA_DIST = NTMakefile version-script.map config_reg.c heim_err.et + +dist_include_HEADERS = heimbase-protos.h + +nodist_include_HEADERS = heim_err.h -EXTRA_DIST = NTMakefile version-script.map +heim_err.h: heim_err.et base64.c: rm -f base64.c diff --git a/lib/base/Makefile.in b/lib/base/Makefile.in deleted file mode 100644 index b46043ee2c56..000000000000 --- a/lib/base/Makefile.in +++ /dev/null @@ -1,1567 +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$ - - -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@ -@MAINTAINER_MODE_TRUE@am__append_1 = dll.c -@MAINTAINER_MODE_TRUE@am__append_2 = -DHEIM_BASE_MAINTAINER -check_PROGRAMS = test_base$(EXEEXT) -TESTS = test_base$(EXEEXT) -@versionscript_TRUE@am__append_3 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map -subdir = lib/base -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) \ - $(am__DIST_COMMON) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/include/config.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -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)" -LTLIBRARIES = $(lib_LTLIBRARIES) -am__DEPENDENCIES_1 = -am__dist_libheimbase_la_SOURCES_DIST = array.c baselocl.h bsearch.c \ - bool.c data.c db.c dict.c dll.c error.c heimbase.c \ - heimbasepriv.h heimqueue.h json.c null.c number.c \ - roken_rename.h string.c -@MAINTAINER_MODE_TRUE@am__objects_1 = dll.lo -am__objects_2 = $(am__objects_1) -dist_libheimbase_la_OBJECTS = array.lo bsearch.lo bool.lo data.lo \ - db.lo dict.lo $(am__objects_2) error.lo heimbase.lo json.lo \ - null.lo number.lo string.lo -@do_roken_rename_TRUE@am__objects_3 = base64.lo -nodist_libheimbase_la_OBJECTS = $(am__objects_3) -libheimbase_la_OBJECTS = $(dist_libheimbase_la_OBJECTS) \ - $(nodist_libheimbase_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 = -libheimbase_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ - $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ - $(AM_CFLAGS) $(CFLAGS) $(libheimbase_la_LDFLAGS) $(LDFLAGS) -o \ - $@ -test_base_SOURCES = test_base.c -test_base_OBJECTS = test_base-test_base.$(OBJEXT) -test_base_DEPENDENCIES = libheimbase.la $(am__DEPENDENCIES_1) -test_base_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_base_CFLAGS) \ - $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -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)/array.Plo ./$(DEPDIR)/base64.Plo \ - ./$(DEPDIR)/bool.Plo ./$(DEPDIR)/bsearch.Plo \ - ./$(DEPDIR)/data.Plo ./$(DEPDIR)/db.Plo ./$(DEPDIR)/dict.Plo \ - ./$(DEPDIR)/dll.Plo ./$(DEPDIR)/error.Plo \ - ./$(DEPDIR)/heimbase.Plo ./$(DEPDIR)/json.Plo \ - ./$(DEPDIR)/null.Plo ./$(DEPDIR)/number.Plo \ - ./$(DEPDIR)/string.Plo ./$(DEPDIR)/test_base-test_base.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 = $(dist_libheimbase_la_SOURCES) \ - $(nodist_libheimbase_la_SOURCES) test_base.c -DIST_SOURCES = $(am__dist_libheimbase_la_SOURCES_DIST) test_base.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) -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__tty_colors_dummy = \ - mgn= red= grn= lgn= blu= brg= std=; \ - am__color_tests=no -am__tty_colors = { \ - $(am__tty_colors_dummy); \ - if test "X$(AM_COLOR_TESTS)" = Xno; then \ - am__color_tests=no; \ - elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ - am__color_tests=yes; \ - elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ - am__color_tests=yes; \ - fi; \ - if test $$am__color_tests = yes; then \ - red='[0;31m'; \ - grn='[0;32m'; \ - lgn='[1;32m'; \ - blu='[1;34m'; \ - mgn='[0;35m'; \ - brg='[1m'; \ - std='[m'; \ - fi; \ -} -am__recheck_rx = ^[ ]*:recheck:[ ]* -am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* -am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* -# A command that, given a newline-separated list of test names on the -# standard input, print the name of the tests that are to be re-run -# upon "make recheck". -am__list_recheck_tests = $(AWK) '{ \ - recheck = 1; \ - while ((rc = (getline line < ($$0 ".trs"))) != 0) \ - { \ - if (rc < 0) \ - { \ - if ((getline line2 < ($$0 ".log")) < 0) \ - recheck = 0; \ - break; \ - } \ - else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ - { \ - recheck = 0; \ - break; \ - } \ - else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ - { \ - break; \ - } \ - }; \ - if (recheck) \ - print $$0; \ - close ($$0 ".trs"); \ - close ($$0 ".log"); \ -}' -# A command that, given a newline-separated list of test names on the -# standard input, create the global log from their .trs and .log files. -am__create_global_log = $(AWK) ' \ -function fatal(msg) \ -{ \ - print "fatal: making $@: " msg | "cat >&2"; \ - exit 1; \ -} \ -function rst_section(header) \ -{ \ - print header; \ - len = length(header); \ - for (i = 1; i <= len; i = i + 1) \ - printf "="; \ - printf "\n\n"; \ -} \ -{ \ - copy_in_global_log = 1; \ - global_test_result = "RUN"; \ - while ((rc = (getline line < ($$0 ".trs"))) != 0) \ - { \ - if (rc < 0) \ - fatal("failed to read from " $$0 ".trs"); \ - if (line ~ /$(am__global_test_result_rx)/) \ - { \ - sub("$(am__global_test_result_rx)", "", line); \ - sub("[ ]*$$", "", line); \ - global_test_result = line; \ - } \ - else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ - copy_in_global_log = 0; \ - }; \ - if (copy_in_global_log) \ - { \ - rst_section(global_test_result ": " $$0); \ - while ((rc = (getline line < ($$0 ".log"))) != 0) \ - { \ - if (rc < 0) \ - fatal("failed to read from " $$0 ".log"); \ - print line; \ - }; \ - printf "\n"; \ - }; \ - close ($$0 ".trs"); \ - close ($$0 ".log"); \ -}' -# Restructured Text title. -am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } -# Solaris 10 'make', and several other traditional 'make' implementations, -# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it -# by disabling -e (using the XSI extension "set +e") if it's set. -am__sh_e_setup = case $$- in *e*) set +e;; esac -# Default flags passed to test drivers. -am__common_driver_flags = \ - --color-tests "$$am__color_tests" \ - --enable-hard-errors "$$am__enable_hard_errors" \ - --expect-failure "$$am__expect_failure" -# To be inserted before the command running the test. Creates the -# directory for the log if needed. Stores in $dir the directory -# containing $f, in $tst the test, in $log the log. Executes the -# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and -# passes TESTS_ENVIRONMENT. Set up options for the wrapper that -# will run the test scripts (or their associated LOG_COMPILER, if -# thy have one). -am__check_pre = \ -$(am__sh_e_setup); \ -$(am__vpath_adj_setup) $(am__vpath_adj) \ -$(am__tty_colors); \ -srcdir=$(srcdir); export srcdir; \ -case "$@" in \ - */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ - *) am__odir=.;; \ -esac; \ -test "x$$am__odir" = x"." || test -d "$$am__odir" \ - || $(MKDIR_P) "$$am__odir" || exit $$?; \ -if test -f "./$$f"; then dir=./; \ -elif test -f "$$f"; then dir=; \ -else dir="$(srcdir)/"; fi; \ -tst=$$dir$$f; log='$@'; \ -if test -n '$(DISABLE_HARD_ERRORS)'; then \ - am__enable_hard_errors=no; \ -else \ - am__enable_hard_errors=yes; \ -fi; \ -case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ - am__expect_failure=yes;; \ - *) \ - am__expect_failure=no;; \ -esac; \ -$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) -# A shell command to get the names of the tests scripts with any registered -# extension removed (i.e., equivalently, the names of the test logs, with -# the '.log' extension removed). The result is saved in the shell variable -# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, -# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", -# since that might cause problem with VPATH rewrites for suffix-less tests. -# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. -am__set_TESTS_bases = \ - bases='$(TEST_LOGS)'; \ - bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ - bases=`echo $$bases` -AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' -RECHECK_LOGS = $(TEST_LOGS) -AM_RECURSIVE_TARGETS = check recheck -TEST_SUITE_LOG = test-suite.log -TEST_EXTENSIONS = @EXEEXT@ .test -LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver -LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) -am__set_b = \ - case '$@' in \ - */*) \ - case '$*' in \ - */*) b='$*';; \ - *) b=`echo '$@' | sed 's/\.log$$//'`; \ - esac;; \ - *) \ - b='$*';; \ - esac -am__test_logs1 = $(TESTS:=.log) -am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) -TEST_LOGS = $(am__test_logs2:.test.log=.log) -TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver -TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ - $(TEST_LOG_FLAGS) -am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/Makefile.am.common \ - $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \ - $(top_srcdir)/test-driver -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) $(am__append_2) $(ROKEN_RENAME) -@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 "$@; -@do_roken_rename_TRUE@ES = base64.c -IMPLEMENT_TLS = $(am__append_1) -lib_LTLIBRARIES = libheimbase.la -test_base_CFLAGS = -Wno-string-concatenation -libheimbase_la_LDFLAGS = -version-info 1:0:0 $(am__append_3) -libheimbase_la_LIBADD = $(PTHREAD_LIBADD) -include_HEADERS = heimbase.h -dist_libheimbase_la_SOURCES = \ - array.c \ - baselocl.h \ - bsearch.c \ - bool.c \ - data.c \ - db.c \ - dict.c \ - $(IMPLEMENT_TLS) \ - error.c \ - heimbase.c \ - heimbasepriv.h \ - heimqueue.h \ - json.c \ - null.c \ - number.c \ - roken_rename.h \ - string.c - -nodist_libheimbase_la_SOURCES = $(ES) - -# install these? -libheimbase_la_DEPENDENCIES = version-script.map -test_base_LDADD = libheimbase.la $(LIB_roken) -CLEANFILES = base64.c test_db.json -EXTRA_DIST = NTMakefile version-script.map -all: all-am - -.SUFFIXES: -.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .log .o .obj .test .test$(EXEEXT) .trs -$(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/base/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --foreign lib/base/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-checkPROGRAMS: - @list='$(check_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}; \ - } - -libheimbase.la: $(libheimbase_la_OBJECTS) $(libheimbase_la_DEPENDENCIES) $(EXTRA_libheimbase_la_DEPENDENCIES) - $(AM_V_CCLD)$(libheimbase_la_LINK) -rpath $(libdir) $(libheimbase_la_OBJECTS) $(libheimbase_la_LIBADD) $(LIBS) - -test_base$(EXEEXT): $(test_base_OBJECTS) $(test_base_DEPENDENCIES) $(EXTRA_test_base_DEPENDENCIES) - @rm -f test_base$(EXEEXT) - $(AM_V_CCLD)$(test_base_LINK) $(test_base_OBJECTS) $(test_base_LDADD) $(LIBS) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -distclean-compile: - -rm -f *.tab.c - -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bool.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bsearch.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data.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)/dict.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dll.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heimbase.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/null.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/number.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_base-test_base.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 $@ $< - -test_base-test_base.o: test_base.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_base_CFLAGS) $(CFLAGS) -MT test_base-test_base.o -MD -MP -MF $(DEPDIR)/test_base-test_base.Tpo -c -o test_base-test_base.o `test -f 'test_base.c' || echo '$(srcdir)/'`test_base.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_base-test_base.Tpo $(DEPDIR)/test_base-test_base.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_base.c' object='test_base-test_base.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_base_CFLAGS) $(CFLAGS) -c -o test_base-test_base.o `test -f 'test_base.c' || echo '$(srcdir)/'`test_base.c - -test_base-test_base.obj: test_base.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_base_CFLAGS) $(CFLAGS) -MT test_base-test_base.obj -MD -MP -MF $(DEPDIR)/test_base-test_base.Tpo -c -o test_base-test_base.obj `if test -f 'test_base.c'; then $(CYGPATH_W) 'test_base.c'; else $(CYGPATH_W) '$(srcdir)/test_base.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_base-test_base.Tpo $(DEPDIR)/test_base-test_base.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_base.c' object='test_base-test_base.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_base_CFLAGS) $(CFLAGS) -c -o test_base-test_base.obj `if test -f 'test_base.c'; then $(CYGPATH_W) 'test_base.c'; else $(CYGPATH_W) '$(srcdir)/test_base.c'; fi` - -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) - -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 - -# Recover from deleted '.trs' file; this should ensure that -# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create -# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells -# to avoid problems with "make -n". -.log.trs: - rm -f $< $@ - $(MAKE) $(AM_MAKEFLAGS) $< - -# Leading 'am--fnord' is there to ensure the list of targets does not -# expand to empty, as could happen e.g. with make check TESTS=''. -am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) -am--force-recheck: - @: - -$(TEST_SUITE_LOG): $(TEST_LOGS) - @$(am__set_TESTS_bases); \ - am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ - redo_bases=`for i in $$bases; do \ - am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ - done`; \ - if test -n "$$redo_bases"; then \ - redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ - redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ - if $(am__make_dryrun); then :; else \ - rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ - fi; \ - fi; \ - if test -n "$$am__remaking_logs"; then \ - echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ - "recursion detected" >&2; \ - elif test -n "$$redo_logs"; then \ - am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ - fi; \ - if $(am__make_dryrun); then :; else \ - st=0; \ - errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ - for i in $$redo_bases; do \ - test -f $$i.trs && test -r $$i.trs \ - || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ - test -f $$i.log && test -r $$i.log \ - || { echo "$$errmsg $$i.log" >&2; st=1; }; \ - done; \ - test $$st -eq 0 || exit 1; \ - fi - @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ - ws='[ ]'; \ - results=`for b in $$bases; do echo $$b.trs; done`; \ - test -n "$$results" || results=/dev/null; \ - all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ - pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ - fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ - skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ - xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ - xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ - error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ - if test `expr $$fail + $$xpass + $$error` -eq 0; then \ - success=true; \ - else \ - success=false; \ - fi; \ - br='==================='; br=$$br$$br$$br$$br; \ - result_count () \ - { \ - if test x"$$1" = x"--maybe-color"; then \ - maybe_colorize=yes; \ - elif test x"$$1" = x"--no-color"; then \ - maybe_colorize=no; \ - else \ - echo "$@: invalid 'result_count' usage" >&2; exit 4; \ - fi; \ - shift; \ - desc=$$1 count=$$2; \ - if test $$maybe_colorize = yes && test $$count -gt 0; then \ - color_start=$$3 color_end=$$std; \ - else \ - color_start= color_end=; \ - fi; \ - echo "$${color_start}# $$desc $$count$${color_end}"; \ - }; \ - create_testsuite_report () \ - { \ - result_count $$1 "TOTAL:" $$all "$$brg"; \ - result_count $$1 "PASS: " $$pass "$$grn"; \ - result_count $$1 "SKIP: " $$skip "$$blu"; \ - result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ - result_count $$1 "FAIL: " $$fail "$$red"; \ - result_count $$1 "XPASS:" $$xpass "$$red"; \ - result_count $$1 "ERROR:" $$error "$$mgn"; \ - }; \ - { \ - echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ - $(am__rst_title); \ - create_testsuite_report --no-color; \ - echo; \ - echo ".. contents:: :depth: 2"; \ - echo; \ - for b in $$bases; do echo $$b; done \ - | $(am__create_global_log); \ - } >$(TEST_SUITE_LOG).tmp || exit 1; \ - mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ - if $$success; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ - fi; \ - echo "$${col}$$br$${std}"; \ - echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ - echo "$${col}$$br$${std}"; \ - create_testsuite_report --maybe-color; \ - echo "$$col$$br$$std"; \ - if $$success; then :; else \ - echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ - if test -n "$(PACKAGE_BUGREPORT)"; then \ - echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ - fi; \ - echo "$$col$$br$$std"; \ - fi; \ - $$success || exit 1 - -check-TESTS: $(check_PROGRAMS) - @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list - @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list - @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) - @set +e; $(am__set_TESTS_bases); \ - log_list=`for i in $$bases; do echo $$i.log; done`; \ - trs_list=`for i in $$bases; do echo $$i.trs; done`; \ - log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ - $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ - exit $$?; -recheck: all $(check_PROGRAMS) - @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) - @set +e; $(am__set_TESTS_bases); \ - bases=`for i in $$bases; do echo $$i; done \ - | $(am__list_recheck_tests)` || exit 1; \ - log_list=`for i in $$bases; do echo $$i.log; done`; \ - log_list=`echo $$log_list`; \ - $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ - am__force_recheck=am--force-recheck \ - TEST_LOGS="$$log_list"; \ - exit $$? -test_base.log: test_base$(EXEEXT) - @p='test_base$(EXEEXT)'; \ - b='test_base'; \ - $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ - --log-file $$b.log --trs-file $$b.trs \ - $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ - "$$tst" $(AM_TESTS_FD_REDIRECT) -.test.log: - @p='$<'; \ - $(am__set_b); \ - $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ - --log-file $$b.log --trs-file $$b.trs \ - $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ - "$$tst" $(AM_TESTS_FD_REDIRECT) -@am__EXEEXT_TRUE@.test$(EXEEXT).log: -@am__EXEEXT_TRUE@ @p='$<'; \ -@am__EXEEXT_TRUE@ $(am__set_b); \ -@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ -@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ -@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ -@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) -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_PROGRAMS) - $(MAKE) $(AM_MAKEFLAGS) check-TESTS check-local -check: check-am -all-am: Makefile $(LTLIBRARIES) $(HEADERS) all-local -install-checkPROGRAMS: install-libLTLIBRARIES - -installdirs: - for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: install-am -install-exec: 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: - -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) - -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) - -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) - -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." -clean: clean-am - -clean-am: clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \ - clean-libtool mostlyclean-am - -distclean: distclean-am - -rm -f ./$(DEPDIR)/array.Plo - -rm -f ./$(DEPDIR)/base64.Plo - -rm -f ./$(DEPDIR)/bool.Plo - -rm -f ./$(DEPDIR)/bsearch.Plo - -rm -f ./$(DEPDIR)/data.Plo - -rm -f ./$(DEPDIR)/db.Plo - -rm -f ./$(DEPDIR)/dict.Plo - -rm -f ./$(DEPDIR)/dll.Plo - -rm -f ./$(DEPDIR)/error.Plo - -rm -f ./$(DEPDIR)/heimbase.Plo - -rm -f ./$(DEPDIR)/json.Plo - -rm -f ./$(DEPDIR)/null.Plo - -rm -f ./$(DEPDIR)/number.Plo - -rm -f ./$(DEPDIR)/string.Plo - -rm -f ./$(DEPDIR)/test_base-test_base.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 - @$(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)/array.Plo - -rm -f ./$(DEPDIR)/base64.Plo - -rm -f ./$(DEPDIR)/bool.Plo - -rm -f ./$(DEPDIR)/bsearch.Plo - -rm -f ./$(DEPDIR)/data.Plo - -rm -f ./$(DEPDIR)/db.Plo - -rm -f ./$(DEPDIR)/dict.Plo - -rm -f ./$(DEPDIR)/dll.Plo - -rm -f ./$(DEPDIR)/error.Plo - -rm -f ./$(DEPDIR)/heimbase.Plo - -rm -f ./$(DEPDIR)/json.Plo - -rm -f ./$(DEPDIR)/null.Plo - -rm -f ./$(DEPDIR)/number.Plo - -rm -f ./$(DEPDIR)/string.Plo - -rm -f ./$(DEPDIR)/test_base-test_base.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 - @$(NORMAL_INSTALL) - $(MAKE) $(AM_MAKEFLAGS) uninstall-hook -.MAKE: check-am install-am install-data-am install-strip uninstall-am - -.PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \ - check-TESTS check-am check-local clean clean-checkPROGRAMS \ - clean-generic clean-libLTLIBRARIES clean-libtool 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-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 recheck tags tags-am \ - uninstall uninstall-am uninstall-hook uninstall-includeHEADERS \ - uninstall-libLTLIBRARIES - -.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 - -base64.c: - rm -f base64.c - $(LN_S) $(srcdir)/../roken/base64.c . - -# 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/base/NTMakefile b/lib/base/NTMakefile index e5bda31dd722..49cd8b865199 100644 --- a/lib/base/NTMakefile +++ b/lib/base/NTMakefile @@ -35,31 +35,83 @@ intcflags=-I$(SRCDIR) -I$(OBJ) !include ../../windows/NTMakefile.w32 -INCFILES=$(INCDIR)\heimbase.h +INCFILES= \ + $(INCDIR)\heimbase.h \ + $(INCDIR)\heimbase-protos.h \ + $(INCDIR)\heimbase-atomics.h \ + $(INCDIR)\heimbase-svc.h \ + $(INCDIR)\heim_err.h \ + $(INCDIR)\common_plugin.h test_binaries = $(OBJ)\test_base.exe +libheimbase_SOURCES = \ + array.c \ + bool.c \ + bsearch.c \ + config_file.c \ + config_reg.c \ + context.c \ + data.c \ + db.c \ + dict.c \ + dll.c \ + error.c \ + error_string.c \ + expand_path.c \ + heimbase.c \ + json.c \ + log.c \ + null.c \ + number.c \ + plugin.c \ + string.c \ + warn.c + libheimbase_OBJS = \ $(OBJ)\array.obj \ $(OBJ)\bool.obj \ $(OBJ)\bsearch.obj \ + $(OBJ)\config_file.obj \ + $(OBJ)\config_reg.obj \ + $(OBJ)\context.obj \ $(OBJ)\data.obj \ $(OBJ)\db.obj \ $(OBJ)\dict.obj \ $(OBJ)\dll.obj \ $(OBJ)\error.obj \ + $(OBJ)\error_string.obj \ + $(OBJ)\expand_path.obj \ $(OBJ)\heimbase.obj \ $(OBJ)\json.obj \ + $(OBJ)\log.obj \ $(OBJ)\null.obj \ $(OBJ)\number.obj \ - $(OBJ)\string.obj + $(OBJ)\plugin.obj \ + $(OBJ)\string.obj \ + $(OBJ)\warn.obj + +libheimbase_gen_OBJS= $(OBJ)\heim_err.obj -$(LIBHEIMBASE): $(libheimbase_OBJS) - $(LIBCON_C) -OUT:$@ $(LIBROKEN) @<< +$(LIBHEIMBASE): $(libheimbase_OBJS) $(libheimbase_gen_OBJS) + $(LIBCON_C) -OUT:$@ $(LIBROKEN) $(LIBCOMERR) $(PTHREAD_LIB) Secur32.lib Shell32.lib Advapi32.lib Shlwapi.lib @<< $(libheimbase_OBJS: = ) +$(libheimbase_gen_OBJS: = +) << +$(OBJ)\heimbase-protos.h: $(libheimbase_SOURCES) + $(PERL) ..\..\cf\make-proto.pl -E KRB5_LIB -q -P remove -o $(OBJ)\heimbase-protos.h $(libheimbase_SOURCES) || $(RM) -f $(OBJ)\heimbase-protos.h + +$(OBJ)\heimbase-private.h: $(libheimbase_SOURCES) + $(PERL) ..\..\cf\make-proto.pl -q -P remove -p $(OBJ)\heimbase-private.h $(libheimbase_SOURCES) || $(RM) -f $(OBJ)\heimbase-private.h + +$(OBJ)\heim_err.c $(OBJ)\heim_err.h: heim_err.et + cd $(OBJ) + $(BINDIR)\compile_et.exe $(SRCDIR)\heim_err.et + cd $(SRCDIR) + test:: test-binaries test-run test-run: diff --git a/lib/base/array.c b/lib/base/array.c index b34f9de48800..994fa7d38e4c 100644 --- a/lib/base/array.c +++ b/lib/base/array.c @@ -46,7 +46,7 @@ struct heim_array_data { heim_object_t *allocated; }; -static void +static void HEIM_CALLCONV array_dealloc(heim_object_t ptr) { heim_array_t array = ptr; @@ -58,7 +58,7 @@ array_dealloc(heim_object_t ptr) struct heim_type_data array_object = { HEIM_TID_ARRAY, - "dict-object", + "array-object", NULL, array_dealloc, NULL, diff --git a/lib/base/baselocl.h b/lib/base/baselocl.h index b24c13d4fb24..7ca6439b33b3 100644 --- a/lib/base/baselocl.h +++ b/lib/base/baselocl.h @@ -37,6 +37,13 @@ #include <roken.h> +#define ISTILDE(x) (x == '~') +#ifdef _WIN32 +# define ISPATHSEP(x) (x == '/' || x =='\\') +#else +# define ISPATHSEP(x) (x == '/') +#endif + #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif @@ -60,94 +67,6 @@ #include <dispatch/dispatch.h> #endif -#if defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH) - -#define heim_base_atomic_inc(x) __sync_add_and_fetch((x), 1) -#define heim_base_atomic_dec(x) __sync_sub_and_fetch((x), 1) -#define heim_base_atomic_type unsigned int -#define heim_base_atomic_max UINT_MAX - -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - -#if __has_builtin(__sync_swap) -#define heim_base_exchange_pointer(t,v) __sync_swap((t), (v)) -#else -#define heim_base_exchange_pointer(t,v) __sync_lock_test_and_set((t), (v)) -#endif - -#elif defined(__sun) - -#include <sys/atomic.h> - -#define heim_base_atomic_inc(x) atomic_inc_uint_nv((volatile uint_t *)(x)) -#define heim_base_atomic_dec(x) atomic_dec_uint_nv((volatile uint_t *)(x)) -#define heim_base_atomic_type uint_t -#define heim_base_atomic_max UINT_MAX - -#define heim_base_exchange_pointer(t,v) atomic_swap_ptr((volatile void *)(t), (void *)(v)) - -#elif defined(_AIX) - -#include <sys/atomic_op.h> - -#define heim_base_atomic_inc(x) (fetch_and_add((atomic_p)(x)) + 1) -#define heim_base_atomic_dec(x) (fetch_and_add((atomic_p)(x)) - 1) -#define heim_base_atomic_type unsigned int -#define heim_base_atomic_max UINT_MAX - -static inline void * -heim_base_exchange_pointer(void *p, void *newval) -{ - void *val = *(void **)p; - - while (!compare_and_swaplp((atomic_l)p, (long *)&val, (long)newval)) - ; - - return val; -} - -#elif defined(_WIN32) - -#define heim_base_atomic_inc(x) InterlockedIncrement(x) -#define heim_base_atomic_dec(x) InterlockedDecrement(x) -#define heim_base_atomic_type LONG -#define heim_base_atomic_max MAXLONG - -#define heim_base_exchange_pointer(t,v) InterlockedExchangePointer((t),(v)) - -#else - -#define HEIM_BASE_NEED_ATOMIC_MUTEX 1 -extern HEIMDAL_MUTEX _heim_base_mutex; - -#define heim_base_atomic_type unsigned int - -static inline heim_base_atomic_type -heim_base_atomic_inc(heim_base_atomic_type *x) -{ - heim_base_atomic_type t; - HEIMDAL_MUTEX_lock(&_heim_base_mutex); - t = ++(*x); - HEIMDAL_MUTEX_unlock(&_heim_base_mutex); - return t; -} - -static inline heim_base_atomic_type -heim_base_atomic_dec(heim_base_atomic_type *x) -{ - heim_base_atomic_type t; - HEIMDAL_MUTEX_lock(&_heim_base_mutex); - t = --(*x); - HEIMDAL_MUTEX_unlock(&_heim_base_mutex); - return t; -} - -#define heim_base_atomic_max UINT_MAX - -#endif - /* tagged strings/object/XXX */ #define heim_base_is_tagged(x) (((uintptr_t)(x)) & 0x3) @@ -165,3 +84,15 @@ heim_base_atomic_dec(heim_base_atomic_type *x) #define HEIMDAL_NORETURN_ATTRIBUTE #undef HEIMDAL_PRINTF_ATTRIBUTE #define HEIMDAL_PRINTF_ATTRIBUTE(x) + +struct heim_context_s { + heim_log_facility *log_dest; + heim_log_facility *warn_dest; + heim_log_facility *debug_dest; + char *time_fmt; + unsigned int log_utc:1; + unsigned int homedir_access:1; + struct et_list *et_list; + char *error_string; + heim_error_code error_code; +}; diff --git a/lib/base/common_plugin.h b/lib/base/common_plugin.h new file mode 100644 index 000000000000..d7b6bcae48d1 --- /dev/null +++ b/lib/base/common_plugin.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2006 - 2020 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2018 AuriStor, Inc. + * + * 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. + */ + +#ifndef HEIMDAL_BASE_COMMON_PLUGIN_H +#define HEIMDAL_BASE_COMMON_PLUGIN_H + +#include <heimbase-svc.h> + +#ifdef _WIN32 +# ifndef HEIM_CALLCONV +# define HEIM_CALLCONV __stdcall +# endif +# ifndef HEIM_LIB_CALL +# define HEIM_LIB_CALL __stdcall +# endif +#else +# ifndef HEIM_CALLCONV +# define HEIM_CALLCONV +# endif +# ifndef HEIM_LIB_CALL +# define HEIM_LIB_CALL +# endif +#endif +#ifndef KRB5_CALLCONV +# define KRB5_CALLCONV HEIM_CALLCONV +#endif +#ifndef KRB5_LIB_CALL +# define KRB5_LIB_CALL HEIM_LIB_CALL +#endif + +/* For krb5 plugins, this is a krb5_context */ +typedef struct heim_pcontext_s *heim_pcontext; + +typedef uintptr_t +(HEIM_LIB_CALL *heim_get_instance_func_t)(const char *); +typedef heim_get_instance_func_t krb5_get_instance_t; + +/* + * All plugin function tables extend the following structure. + */ +struct heim_plugin_common_ftable_desc { + HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(heim_pcontext); +}; +typedef struct heim_plugin_common_ftable_desc heim_plugin_common_ftable; +typedef struct heim_plugin_common_ftable_desc *heim_plugin_common_ftable_p; +typedef const struct heim_plugin_common_ftable_desc *heim_plugin_common_ftable_const_p; +typedef struct heim_plugin_common_ftable_desc * const heim_plugin_common_ftable_cp; + +typedef int +(HEIM_CALLCONV heim_plugin_load_ft)(heim_pcontext context, + heim_get_instance_func_t *func, + size_t *n_ftables, + heim_plugin_common_ftable_cp **ftables); + +typedef heim_plugin_load_ft *heim_plugin_load_t; + +/* For source backwards-compatibility */ +typedef struct heim_plugin_common_ftable_desc krb5_plugin_common_ftable; +typedef struct heim_plugin_common_ftable_desc *krb5_plugin_common_ftable_p; +typedef struct heim_plugin_common_ftable_desc * const krb5_plugin_common_ftable_cp; +typedef heim_plugin_load_ft krb5_plugin_load_ft; +typedef heim_plugin_load_ft *krb5_plugin_load_t; + +/* + * All plugins must export a function named "<type>_plugin_load" with + * a signature of: + * + * int HEIM_CALLCONV + * <type>_plugin_load(heim_pcontext context, + * heim_get_instance_func_t *func, + * size_t *n_ftables, + * const heim_plugin_common_ftable *const **ftables); + */ +#endif /* HEIMDAL_BASE_COMMON_PLUGIN_H */ diff --git a/lib/base/config_file.c b/lib/base/config_file.c new file mode 100644 index 000000000000..b1675ea5f141 --- /dev/null +++ b/lib/base/config_file.c @@ -0,0 +1,1472 @@ +/* + * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2009 Apple Inc. 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 "baselocl.h" +#include <assert.h> +#include <ctype.h> +#include <parse_time.h> + +#if defined(HAVE_FRAMEWORK_COREFOUNDATION) +#include <CoreFoundation/CoreFoundation.h> +#endif + +/* Gaah! I want a portable funopen */ +struct fileptr { + heim_context context; + const char *s; + FILE *f; +}; + +static char * +config_fgets(char *str, size_t len, struct fileptr *ptr) +{ + /* XXX this is not correct, in that they don't do the same if the + line is longer than len */ + if(ptr->f != NULL) + return fgets(str, len, ptr->f); + else { + /* this is almost strsep_copy */ + const char *p; + ssize_t l; + if(*ptr->s == '\0') + return NULL; + p = ptr->s + strcspn(ptr->s, "\n"); + if(*p == '\n') + p++; + l = min(len, (size_t)(p - ptr->s)); + if(len > 0) { + memcpy(str, ptr->s, l); + str[l] = '\0'; + } + ptr->s = p; + return str; + } +} + +static heim_error_code parse_section(char *p, heim_config_section **s, + heim_config_section **res, + const char **err_message); +static heim_error_code parse_binding(struct fileptr *f, unsigned *lineno, char *p, + heim_config_binding **b, + heim_config_binding **parent, + const char **err_message); +static heim_error_code parse_list(struct fileptr *f, unsigned *lineno, + heim_config_binding **parent, + const char **err_message); + +heim_config_section * +heim_config_get_entry(heim_config_section **parent, const char *name, int type) +{ + heim_config_section **q; + + for (q = parent; *q != NULL; q = &(*q)->next) + if (type == heim_config_list && + (unsigned)type == (*q)->type && + strcmp(name, (*q)->name) == 0) + return *q; + *q = calloc(1, sizeof(**q)); + if (*q == NULL) + return NULL; + (*q)->name = strdup(name); + (*q)->type = type; + if ((*q)->name == NULL) { + free(*q); + *q = NULL; + return NULL; + } + return *q; +} + +/* + * Parse a section: + * + * [section] + * foo = bar + * b = { + * a + * } + * ... + * + * starting at the line in `p', storing the resulting structure in + * `s' and hooking it into `parent'. + * Store the error message in `err_message'. + */ + +static heim_error_code +parse_section(char *p, heim_config_section **s, heim_config_section **parent, + const char **err_message) +{ + char *p1; + heim_config_section *tmp; + + p1 = strchr (p + 1, ']'); + if (p1 == NULL) { + *err_message = "missing ]"; + return HEIM_ERR_CONFIG_BADFORMAT; + } + *p1 = '\0'; + tmp = heim_config_get_entry(parent, p + 1, heim_config_list); + if(tmp == NULL) { + *err_message = "out of memory"; + return HEIM_ERR_CONFIG_BADFORMAT; + } + *s = tmp; + return 0; +} + +/* + * Parse a brace-enclosed list from `f', hooking in the structure at + * `parent'. + * Store the error message in `err_message'. + */ + +static heim_error_code +parse_list(struct fileptr *f, unsigned *lineno, heim_config_binding **parent, + const char **err_message) +{ + char buf[2048]; + heim_error_code ret; + heim_config_binding *b = NULL; + unsigned beg_lineno = *lineno; + + while(config_fgets(buf, sizeof(buf), f) != NULL) { + char *p; + + ++*lineno; + buf[strcspn(buf, "\r\n")] = '\0'; + p = buf; + while(isspace((unsigned char)*p)) + ++p; + if (*p == '#' || *p == ';' || *p == '\0') + continue; + while(isspace((unsigned char)*p)) + ++p; + if (*p == '}') + return 0; + if (*p == '\0') + continue; + ret = parse_binding (f, lineno, p, &b, parent, err_message); + if (ret) + return ret; + } + *lineno = beg_lineno; + *err_message = "unclosed {"; + return HEIM_ERR_CONFIG_BADFORMAT; +} + +/* + * + */ + +static heim_error_code +parse_binding(struct fileptr *f, unsigned *lineno, char *p, + heim_config_binding **b, heim_config_binding **parent, + const char **err_message) +{ + heim_config_binding *tmp; + char *p1, *p2; + heim_error_code ret = 0; + + p1 = p; + while (*p && *p != '=' && !isspace((unsigned char)*p)) + ++p; + if (*p == '\0') { + *err_message = "missing ="; + return HEIM_ERR_CONFIG_BADFORMAT; + } + p2 = p; + while (isspace((unsigned char)*p)) + ++p; + if (*p != '=') { + *err_message = "missing ="; + return HEIM_ERR_CONFIG_BADFORMAT; + } + ++p; + while(isspace((unsigned char)*p)) + ++p; + *p2 = '\0'; + if (*p == '{') { + tmp = heim_config_get_entry(parent, p1, heim_config_list); + if (tmp == NULL) { + *err_message = "out of memory"; + return HEIM_ERR_CONFIG_BADFORMAT; + } + ret = parse_list (f, lineno, &tmp->u.list, err_message); + } else { + tmp = heim_config_get_entry(parent, p1, heim_config_string); + if (tmp == NULL) { + *err_message = "out of memory"; + return HEIM_ERR_CONFIG_BADFORMAT; + } + p1 = p; + p = p1 + strlen(p1); + while(p > p1 && isspace((unsigned char)*(p-1))) + --p; + *p = '\0'; + tmp->u.string = strdup(p1); + } + *b = tmp; + return ret; +} + +#if defined(HAVE_FRAMEWORK_COREFOUNDATION) + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 +#define HAVE_CFPROPERTYLISTCREATEWITHSTREAM 1 +#endif + +static char * +cfstring2cstring(CFStringRef string) +{ + CFIndex len; + char *str; + + str = (char *) CFStringGetCStringPtr(string, kCFStringEncodingUTF8); + if (str) + return strdup(str); + + len = CFStringGetLength(string); + len = 1 + CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8); + str = malloc(len); + if (str == NULL) + return NULL; + + if (!CFStringGetCString (string, str, len, kCFStringEncodingUTF8)) { + free (str); + return NULL; + } + return str; +} + +static void +convert_content(const void *key, const void *value, void *context) +{ + heim_config_section *tmp, **parent = context; + char *k; + + if (CFGetTypeID(key) != CFStringGetTypeID()) + return; + + k = cfstring2cstring(key); + if (k == NULL) + return; + + if (CFGetTypeID(value) == CFStringGetTypeID()) { + tmp = heim_config_get_entry(parent, k, heim_config_string); + tmp->u.string = cfstring2cstring(value); + } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) { + tmp = heim_config_get_entry(parent, k, heim_config_list); + CFDictionaryApplyFunction(value, convert_content, &tmp->u.list); + } else { + /* log */ + } + free(k); +} + +static heim_error_code +parse_plist_config(heim_context context, const char *path, heim_config_section **parent) +{ + CFReadStreamRef s; + CFDictionaryRef d; + CFURLRef url; + + url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8 *)path, strlen(path), 0); + if (url == NULL) { + heim_clear_error_message(context); + return ENOMEM; + } + + s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url); + CFRelease(url); + if (s == NULL) { + heim_clear_error_message(context); + return ENOMEM; + } + + if (!CFReadStreamOpen(s)) { + CFRelease(s); + heim_clear_error_message(context); + return ENOENT; + } + +#ifdef HAVE_CFPROPERTYLISTCREATEWITHSTREAM + d = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL); +#else + d = (CFDictionaryRef)CFPropertyListCreateFromStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL); +#endif + CFRelease(s); + if (d == NULL) { + heim_clear_error_message(context); + return ENOENT; + } + + CFDictionaryApplyFunction(d, convert_content, parent); + CFRelease(d); + + return 0; +} + +#endif + +static int +is_absolute_path(const char *path) +{ + /* + * An absolute path is one that refers to an explicit object + * without ambiguity. + */ +#ifdef WIN32 + size_t len = strlen(path); + + /* UNC path is by definition absolute */ + if (len > 2 + && ISPATHSEP(path[0]) + && ISPATHSEP(path[1])) + return 1; + + /* A drive letter path might be absolute */ + if (len > 3 + && isalpha((unsigned char)path[0]) + && path[1] == ':' + && ISPATHSEP(path[2])) + return 1; + + /* + * if no drive letter but first char is a path + * separator then the drive letter must be obtained + * from the including file. + */ +#else + /* UNIX is easy, first char '/' is absolute */ + if (ISPATHSEP(path[0])) + return 1; +#endif + return 0; +} + +/* + * Parse the config file `fname', generating the structures into `res' + * returning error messages in `err_message' + */ + +static heim_error_code +heim_config_parse_debug(struct fileptr *f, + heim_config_section **res, + unsigned *lineno, + const char **err_message) +{ + heim_config_section *s = NULL; + heim_config_binding *b = NULL; + char buf[2048]; + heim_error_code ret; + + *lineno = 0; + *err_message = ""; + + while (config_fgets(buf, sizeof(buf), f) != NULL) { + char *p; + + ++*lineno; + buf[strcspn(buf, "\r\n")] = '\0'; + p = buf; + while(isspace((unsigned char)*p)) + ++p; + if (*p == '#' || *p == ';') + continue; + if (*p == '[') { + ret = parse_section(p, &s, res, err_message); + if (ret) + return ret; + b = NULL; + } else if (*p == '}') { + *err_message = "unmatched }"; + return 2048; + } else if (strncmp(p, "include", sizeof("include") - 1) == 0 && + isspace((unsigned char)p[sizeof("include") - 1])) { + p += sizeof("include"); + while (isspace((unsigned char)*p)) + p++; + if (!is_absolute_path(p)) { + heim_set_error_message(f->context, HEIM_ERR_CONFIG_BADFORMAT, + "Configuration include path must be " + "absolute"); + return HEIM_ERR_CONFIG_BADFORMAT; + } + ret = heim_config_parse_file_multi(f->context, p, res); + if (ret) + return ret; + } else if (strncmp(p, "includedir", sizeof("includedir") - 1) == 0 && + isspace((unsigned char)p[sizeof("includedir") - 1])) { + p += sizeof("includedir"); + while (isspace((unsigned char)*p)) + p++; + if (!is_absolute_path(p)) { + heim_set_error_message(f->context, HEIM_ERR_CONFIG_BADFORMAT, + "Configuration includedir path must be " + "absolute"); + return HEIM_ERR_CONFIG_BADFORMAT; + } + ret = heim_config_parse_dir_multi(f->context, p, res); + if (ret) + return ret; + } else if(*p != '\0') { + if (s == NULL) { + *err_message = "binding before section"; + return 2048; + } + ret = parse_binding(f, lineno, p, &b, &s->u.list, err_message); + if (ret) + return ret; + } + } + return 0; +} + +static int +is_plist_file(const char *fname) +{ + size_t len = strlen(fname); + char suffix[] = ".plist"; + if (len < sizeof(suffix)) + return 0; + if (strcasecmp(&fname[len - (sizeof(suffix) - 1)], suffix) != 0) + return 0; + return 1; +} + +/** + * Parse configuration files in the given directory and add the result + * into res. Only files whose names consist only of alphanumeric + * characters, hyphen, and underscore, will be parsed, though files + * ending in ".conf" will also be parsed. + * + * This interface can be used to parse several configuration directories + * into one resulting heim_config_section by calling it repeatably. + * + * @param context a Kerberos 5 context. + * @param dname a directory name to a Kerberos configuration file + * @param res the returned result, must be free with heim_free_config_files(). + * @return Return an error code or 0, see heim_get_error_message(). + * + * @ingroup heim_support + */ + +heim_error_code +heim_config_parse_dir_multi(heim_context context, + const char *dname, + heim_config_section **res) +{ + struct dirent *entry; + heim_error_code ret; + DIR *d; + + if ((d = opendir(dname)) == NULL) + return errno; + + while ((entry = readdir(d)) != NULL) { + char *p = entry->d_name; + char *path; + int is_valid = 1; + + while (*p) { + /* + * Here be dragons. The call to heim_config_parse_file_multi() + * below expands path tokens. Because of the limitations here + * on file naming, we can't have path tokens in the file name, + * so we're safe. Anyone changing this if condition here should + * be aware. + */ + if (!isalnum((unsigned char)*p) && *p != '_' && *p != '-' && + strcmp(p, ".conf") != 0) { + is_valid = 0; + break; + } + p++; + } + if (!is_valid) + continue; + + if (asprintf(&path, "%s/%s", dname, entry->d_name) == -1 || + path == NULL) { + (void) closedir(d); + return heim_enomem(context); + } + ret = heim_config_parse_file_multi(context, path, res); + free(path); + if (ret == ENOMEM) { + (void) closedir(d); + return ENOMEM; + } + /* Ignore malformed config files so we don't lock out admins, etc... */ + } + (void) closedir(d); + return 0; +} + +static int +is_devnull(struct stat *st) +{ +#ifdef WIN32 + return 0; +#else + struct stat devnullst; + + if (stat("/dev/null", &devnullst) == -1) + return 0; + return st->st_dev == devnullst.st_dev && st->st_ino == devnullst.st_ino; +#endif +} + +HEIMDAL_THREAD_LOCAL int config_include_depth = 0; + +/** + * Parse a configuration file and add the result into res. This + * interface can be used to parse several configuration files into one + * resulting heim_config_section by calling it repeatably. + * + * @param context a Kerberos 5 context. + * @param fname a file name to a Kerberos configuration file + * @param res the returned result, must be free with heim_free_config_files(). + * @return Return an error code or 0, see heim_get_error_message(). + * + * @ingroup heim_support + */ + +heim_error_code +heim_config_parse_file_multi(heim_context context, + const char *fname, + heim_config_section **res) +{ + const char *str; + char *newfname = NULL; + unsigned lineno = 0; + heim_error_code ret = 0; + struct fileptr f; + struct stat st; + + if (config_include_depth > 5) { + heim_warnx(context, "Maximum config file include depth reached; " + "not including %s", fname); + return 0; + } + config_include_depth++; + + /** + * If the fname starts with "~/" parse configuration file in the + * current users home directory. The behavior can be disabled and + * enabled by calling heim_set_home_dir_access(). + */ + if (ISTILDE(fname[0]) && ISPATHSEP(fname[1])) { + if (!heim_context_get_homedir_access(context)) { + heim_set_error_message(context, EPERM, + "Access to home directory not allowed"); + ret = EPERM; + goto out; + } + if (asprintf(&newfname, "%%{USERCONFIG}%s", &fname[1]) < 0 || + newfname == NULL) { + ret = heim_enomem(context); + goto out; + } + fname = newfname; + } + + if (is_plist_file(fname)) { +#if defined(HAVE_FRAMEWORK_COREFOUNDATION) + ret = parse_plist_config(context, fname, res); + if (ret) { + heim_set_error_message(context, ret, + "Failed to parse plist %s", fname); + goto out; + } +#else + heim_set_error_message(context, ENOENT, + "no support for plist configuration files"); + ret = ENOENT; + goto out; +#endif + } else { + char *exp_fname = NULL; + + /* + * Note that heim_config_parse_dir_multi() doesn't want tokens + * expanded here, but it happens to limit the names of files to + * include such that there can be no tokens to expand. Don't + * add token expansion for tokens using _, say. + */ + ret = heim_expand_path_tokens(context, fname, 1, &exp_fname, NULL); + if (ret) + goto out; + free(newfname); + fname = newfname = exp_fname; + + f.context = context; + f.f = fopen(fname, "r"); + f.s = NULL; + if (f.f == NULL || fstat(fileno(f.f), &st) == -1) { + if (f.f != NULL) + (void) fclose(f.f); + ret = errno; + heim_set_error_message(context, ret, "open or stat %s: %s", + fname, strerror(ret)); + goto out; + } + + if (!S_ISREG(st.st_mode) && !is_devnull(&st)) { + (void) fclose(f.f); + heim_set_error_message(context, EISDIR, "not a regular file %s: %s", + fname, strerror(EISDIR)); + ret = EISDIR; + goto out; + } + + ret = heim_config_parse_debug(&f, res, &lineno, &str); + fclose(f.f); + if (ret) { + if (ret != HEIM_ERR_CONFIG_BADFORMAT) + ret = HEIM_ERR_CONFIG_BADFORMAT; + heim_set_error_message(context, ret, "%s:%u: %s", + fname, lineno, str); + goto out; + } + } + + out: + config_include_depth--; + if (ret == HEIM_ERR_CONFIG_BADFORMAT || (ret && config_include_depth > 0)) { + heim_warn(context, ret, "Ignoring"); + if (config_include_depth > 0) + ret = 0; + } + free(newfname); + return ret; +} + +heim_error_code +heim_config_parse_file(heim_context context, + const char *fname, + heim_config_section **res) +{ + *res = NULL; + return heim_config_parse_file_multi(context, fname, res); +} + +static void +free_binding(heim_context context, heim_config_binding *b) +{ + heim_config_binding *next_b; + + while (b) { + free (b->name); + assert(b->type == heim_config_string || b->type == heim_config_list); + if (b->type == heim_config_string) + free (b->u.string); + else + free_binding (context, b->u.list); + next_b = b->next; + free (b); + b = next_b; + } +} + +/** + * Free configuration file section, the result of + * heim_config_parse_file() and heim_config_parse_file_multi(). + * + * @param context A Kerberos 5 context + * @param s the configuration section to free + * + * @return returns 0 on successes, otherwise an error code, see + * heim_get_error_message() + * + * @ingroup heim_support + */ + +heim_error_code +heim_config_file_free(heim_context context, heim_config_section *s) +{ + free_binding (context, s); + return 0; +} + +#ifndef HEIMDAL_SMALLER + +heim_error_code +heim_config_copy(heim_context context, + heim_config_section *c, + heim_config_section **head) +{ + heim_config_binding *d, *previous = NULL; + + *head = NULL; + + while (c) { + d = calloc(1, sizeof(*d)); + + if (*head == NULL) + *head = d; + + d->name = strdup(c->name); + d->type = c->type; + assert(d->type == heim_config_string || d->type == heim_config_list); + if (d->type == heim_config_string) + d->u.string = strdup(c->u.string); + else + heim_config_copy (context, c->u.list, &d->u.list); + if (previous) + previous->next = d; + + previous = d; + c = c->next; + } + return 0; +} + +#endif /* HEIMDAL_SMALLER */ + +const void * +heim_config_get_next(heim_context context, + const heim_config_section *c, + const heim_config_binding **pointer, + int type, + ...) +{ + const char *ret; + va_list args; + + va_start(args, type); + ret = heim_config_vget_next(context, c, pointer, type, args); + va_end(args); + return ret; +} + +static const void * +vget_next(heim_context context, + const heim_config_binding *b, + const heim_config_binding **pointer, + int type, + const char *name, + va_list args) +{ + const char *p = va_arg(args, const char *); + + while (b != NULL) { + if (strcmp(b->name, name) == 0) { + if (b->type == (unsigned)type && p == NULL) { + *pointer = b; + return b->u.generic; + } else if (b->type == heim_config_list && p != NULL) { + return vget_next(context, b->u.list, pointer, type, p, args); + } + } + b = b->next; + } + return NULL; +} + +const void * +heim_config_vget_next(heim_context context, + const heim_config_section *c, + const heim_config_binding **pointer, + int type, + va_list args) +{ + const heim_config_binding *b; + const char *p; + + if (c == NULL) + return NULL; + + if (*pointer == NULL) { + /* first time here, walk down the tree looking for the right + section */ + p = va_arg(args, const char *); + if (p == NULL) + return NULL; + return vget_next(context, c, pointer, type, p, args); + } + + /* we were called again, so just look for more entries with the + same name and type */ + for (b = (*pointer)->next; b != NULL; b = b->next) { + if(strcmp(b->name, (*pointer)->name) == 0 && b->type == (unsigned)type) { + *pointer = b; + return b->u.generic; + } + } + return NULL; +} + +const void * +heim_config_get(heim_context context, + const heim_config_section *c, + int type, + ...) +{ + const void *ret; + va_list args; + + va_start(args, type); + ret = heim_config_vget(context, c, type, args); + va_end(args); + return ret; +} + + +const void * +heim_config_vget(heim_context context, + const heim_config_section *c, + int type, + va_list args) +{ + const heim_config_binding *foo = NULL; + + return heim_config_vget_next(context, c, &foo, type, args); +} + +/** + * Get a list of configuration binding list for more processing + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param ... a list of names, terminated with NULL. + * + * @return NULL if configuration list is not found, a list otherwise + * + * @ingroup heim_support + */ + +const heim_config_binding * +heim_config_get_list(heim_context context, + const heim_config_section *c, + ...) +{ + const heim_config_binding *ret; + va_list args; + + va_start(args, c); + ret = heim_config_vget_list(context, c, args); + va_end(args); + return ret; +} + +/** + * Get a list of configuration binding list for more processing + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param args a va_list of arguments + * + * @return NULL if configuration list is not found, a list otherwise + * + * @ingroup heim_support + */ + +const heim_config_binding * +heim_config_vget_list(heim_context context, + const heim_config_section *c, + va_list args) +{ + return heim_config_vget(context, c, heim_config_list, args); +} + +/** + * Returns a "const char *" to a string in the configuration database. + * The string may not be valid after a reload of the configuration + * database so a caller should make a local copy if it needs to keep + * the string. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param ... a list of names, terminated with NULL. + * + * @return NULL if configuration string not found, a string otherwise + * + * @ingroup heim_support + */ + +const char * +heim_config_get_string(heim_context context, + const heim_config_section *c, + ...) +{ + const char *ret; + va_list args; + + va_start(args, c); + ret = heim_config_vget_string(context, c, args); + va_end(args); + return ret; +} + +/** + * Like heim_config_get_string(), but uses a va_list instead of ... + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param args a va_list of arguments + * + * @return NULL if configuration string not found, a string otherwise + * + * @ingroup heim_support + */ + +const char * +heim_config_vget_string(heim_context context, + const heim_config_section *c, + va_list args) +{ + return heim_config_vget(context, c, heim_config_string, args); +} + +/** + * Like heim_config_vget_string(), but instead of returning NULL, + * instead return a default value. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param args a va_list of arguments + * + * @return a configuration string + * + * @ingroup heim_support + */ + +const char * +heim_config_vget_string_default(heim_context context, + const heim_config_section *c, + const char *def_value, + va_list args) +{ + const char *ret; + + ret = heim_config_vget_string(context, c, args); + if (ret == NULL) + ret = def_value; + return ret; +} + +/** + * Like heim_config_get_string(), but instead of returning NULL, + * instead return a default value. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param ... a list of names, terminated with NULL. + * + * @return a configuration string + * + * @ingroup heim_support + */ + +const char * +heim_config_get_string_default(heim_context context, + const heim_config_section *c, + const char *def_value, + ...) +{ + const char *ret; + va_list args; + + va_start(args, def_value); + ret = heim_config_vget_string_default (context, c, def_value, args); + va_end(args); + return ret; +} + +static char * +next_component_string(char * begin, const char * delims, char **state) +{ + char * end; + + if (begin == NULL) + begin = *state; + + if (*begin == '\0') + return NULL; + + end = begin; + while (*end == '"') { + char * t = strchr(end + 1, '"'); + + if (t) + end = ++t; + else + end += strlen(end); + } + + if (*end != '\0') { + size_t pos; + + pos = strcspn(end, delims); + end = end + pos; + } + + if (*end != '\0') { + *end = '\0'; + *state = end + 1; + if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) { + begin++; *(end - 1) = '\0'; + } + return begin; + } + + *state = end; + if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) { + begin++; *(end - 1) = '\0'; + } + return begin; +} + +/** + * Get a list of configuration strings, free the result with + * heim_config_free_strings(). + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param args a va_list of arguments + * + * @return TRUE or FALSE + * + * @ingroup heim_support + */ + +char ** +heim_config_vget_strings(heim_context context, + const heim_config_section *c, + va_list args) +{ + char **strings = NULL; + size_t nstr = 0; + const heim_config_binding *b = NULL; + const char *p; + + while((p = heim_config_vget_next(context, c, &b, + heim_config_string, args))) { + char *tmp = strdup(p); + char *pos = NULL; + char *s; + if(tmp == NULL) + goto cleanup; + s = next_component_string(tmp, " \t", &pos); + while(s){ + char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings)); + if(tmp2 == NULL) { + free(tmp); + goto cleanup; + } + strings = tmp2; + strings[nstr] = strdup(s); + nstr++; + if(strings[nstr-1] == NULL) { + free(tmp); + goto cleanup; + } + s = next_component_string(NULL, " \t", &pos); + } + free(tmp); + } + if(nstr){ + char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings)); + if(tmp == NULL) + goto cleanup; + strings = tmp; + strings[nstr] = NULL; + } + return strings; +cleanup: + while(nstr--) + free(strings[nstr]); + free(strings); + return NULL; + +} + +/** + * Get a list of configuration strings, free the result with + * heim_config_free_strings(). + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param ... a list of names, terminated with NULL. + * + * @return TRUE or FALSE + * + * @ingroup heim_support + */ + +char ** +heim_config_get_strings(heim_context context, + const heim_config_section *c, + ...) +{ + va_list ap; + char **ret; + va_start(ap, c); + ret = heim_config_vget_strings(context, c, ap); + va_end(ap); + return ret; +} + +/** + * Free the resulting strings from heim_config-get_strings() and + * heim_config_vget_strings(). + * + * @param strings strings to free + * + * @ingroup heim_support + */ + +void +heim_config_free_strings(char **strings) +{ + char **s = strings; + + while (s && *s) { + free(*s); + s++; + } + free(strings); +} + +/** + * Like heim_config_get_bool_default() but with a va_list list of + * configuration selection. + * + * Configuration value to a boolean value, where yes/true and any + * non-zero number means TRUE and other value is FALSE. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param args a va_list of arguments + * + * @return TRUE or FALSE + * + * @ingroup heim_support + */ + +int +heim_config_vget_bool_default(heim_context context, + const heim_config_section *c, + int def_value, + va_list args) +{ + const char *str; + str = heim_config_vget_string(context, c, args); + if (str == NULL) + return def_value; + return !!(strcasecmp(str, "yes") == 0 || + strcasecmp(str, "true") == 0 || + atoi(str)); +} + +/** + * heim_config_get_bool() will convert the configuration + * option value to a boolean value, where yes/true and any non-zero + * number means TRUE and other value is FALSE. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param args a va_list of arguments + * + * @return TRUE or FALSE + * + * @ingroup heim_support + */ + +int +heim_config_vget_bool(heim_context context, + const heim_config_section *c, + va_list args) +{ + return heim_config_vget_bool_default(context, c, 0, args); +} + +/** + * heim_config_get_bool_default() will convert the configuration + * option value to a boolean value, where yes/true and any non-zero + * number means TRUE and other value is FALSE. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param ... a list of names, terminated with NULL. + * + * @return TRUE or FALSE + * + * @ingroup heim_support + */ + +int +heim_config_get_bool_default(heim_context context, + const heim_config_section *c, + int def_value, + ...) +{ + va_list ap; + int ret; + + va_start(ap, def_value); + ret = heim_config_vget_bool_default(context, c, def_value, ap); + va_end(ap); + return ret; +} + +/** + * Like heim_config_get_bool() but with a va_list list of + * configuration selection. + * + * Configuration value to a boolean value, where yes/true and any + * non-zero number means TRUE and other value is FALSE. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param ... a list of names, terminated with NULL. + * + * @return TRUE or FALSE + * + * @ingroup heim_support + */ + +int +heim_config_get_bool(heim_context context, + const heim_config_section *c, + ...) +{ + va_list ap; + int ret; + va_start(ap, c); + ret = heim_config_vget_bool (context, c, ap); + va_end(ap); + return ret; +} + +/** + * Get the time from the configuration file using a relative time. + * + * Like heim_config_get_time_default() but with a va_list list of + * configuration selection. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param args a va_list of arguments + * + * @return parsed the time (or def_value on parse error) + * + * @ingroup heim_support + */ + +time_t +heim_config_vget_time_default(heim_context context, + const heim_config_section *c, + int def_value, + va_list args) +{ + const char *str; + time_t t = -1; + + if ((str = heim_config_vget_string(context, c, args))) + t = parse_time(str, "s"); + return t != -1 ? t : def_value; +} + +/** + * Get the time from the configuration file using a relative time, for example: 1h30s + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param args a va_list of arguments + * + * @return parsed the time or -1 on error + * + * @ingroup heim_support + */ + +time_t +heim_config_vget_time(heim_context context, + const heim_config_section *c, + va_list args) +{ + return heim_config_vget_time_default(context, c, -1, args); +} + +/** + * Get the time from the configuration file using a relative time, for example: 1h30s + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param ... a list of names, terminated with NULL. + * + * @return parsed the time (or def_value on parse error) + * + * @ingroup heim_support + */ + +time_t +heim_config_get_time_default(heim_context context, + const heim_config_section *c, + int def_value, + ...) +{ + va_list ap; + time_t ret; + + va_start(ap, def_value); + ret = heim_config_vget_time_default(context, c, def_value, ap); + va_end(ap); + return ret; +} + +/** + * Get the time from the configuration file using a relative time, for example: 1h30s + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param ... a list of names, terminated with NULL. + * + * @return parsed the time or -1 on error + * + * @ingroup heim_support + */ + +time_t +heim_config_get_time(heim_context context, + const heim_config_section *c, + ...) +{ + va_list ap; + int ret; + va_start(ap, c); + ret = heim_config_vget_time(context, c, ap); + va_end(ap); + return ret; +} + + +int +heim_config_vget_int_default(heim_context context, + const heim_config_section *c, + int def_value, + va_list args) +{ + const char *str; + str = heim_config_vget_string (context, c, args); + if(str == NULL) + return def_value; + else { + char *endptr; + long l; + l = strtol(str, &endptr, 0); + if (endptr == str) + return def_value; + else + return l; + } +} + +int +heim_config_vget_int(heim_context context, + const heim_config_section *c, + va_list args) +{ + return heim_config_vget_int_default(context, c, -1, args); +} + +int +heim_config_get_int_default(heim_context context, + const heim_config_section *c, + int def_value, + ...) +{ + va_list ap; + int ret; + + va_start(ap, def_value); + ret = heim_config_vget_int_default(context, c, def_value, ap); + va_end(ap); + return ret; +} + +int +heim_config_get_int(heim_context context, + const heim_config_section *c, + ...) +{ + va_list ap; + int ret; + va_start(ap, c); + ret = heim_config_vget_int (context, c, ap); + va_end(ap); + return ret; +} + +#ifndef HEIMDAL_SMALLER +heim_error_code +heim_config_parse_string_multi(heim_context context, + const char *string, + heim_config_section **res) +{ + const char *str; + unsigned lineno = 0; + heim_error_code ret; + struct fileptr f; + + f.context = context; + f.f = NULL; + f.s = string; + + ret = heim_config_parse_debug(&f, res, &lineno, &str); + if (ret) { + if (ret != HEIM_ERR_CONFIG_BADFORMAT) { + ret = HEIM_ERR_CONFIG_BADFORMAT; + heim_set_error_message(context, ret, "%s:%u: %s", + "<constant>", lineno, str); + } + return ret; + } + return 0; +} +#endif diff --git a/lib/base/config_reg.c b/lib/base/config_reg.c new file mode 100644 index 000000000000..cb24e5043d64 --- /dev/null +++ b/lib/base/config_reg.c @@ -0,0 +1,658 @@ +/*********************************************************************** + * Copyright (c) 2010, Secure Endpoints Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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 "baselocl.h" + +#ifndef _WIN32 +#error config_reg.c is only for Windows +#endif + +#include <shlwapi.h> + +#ifndef MAX_DWORD +#define MAX_DWORD 0xFFFFFFFF +#endif + +/** + * Store a string as a registry value of the specified type + * + * The following registry types are handled: + * + * - REG_DWORD: The string is converted to a number. + * + * - REG_SZ: The string is stored as is. + * + * - REG_EXPAND_SZ: The string is stored as is. + * + * - REG_MULTI_SZ: + * + * . If a separator is specified, the input string is broken + * up into multiple strings and stored as a multi-sz. + * + * . If no separator is provided, the input string is stored + * as a multi-sz. + * + * - REG_NONE: + * + * . If the string is all numeric, it will be stored as a + * REG_DWORD. + * + * . Otherwise, the string is stored as a REG_SZ. + * + * Other types are rejected. + * + * If cb_data is MAX_DWORD, the string pointed to by data must be nul-terminated + * otherwise a buffer overrun will occur. + * + * @param [in]valuename Name of the registry value to be modified or created + * @param [in]type Type of the value. REG_NONE if unknown + * @param [in]data The input string to be stored in the registry. + * @param [in]cb_data Size of the input string in bytes. MAX_DWORD if unknown. + * @param [in]separator Separator character for parsing strings. + * + * @retval 0 if success or non-zero on error. + * If non-zero is returned, an error message has been set using + * heim_set_error_message(). + * + */ +int +heim_store_string_to_reg_value(heim_context context, + HKEY key, const char *valuename, + DWORD type, const char *data, DWORD cb_data, + const char *separator) +{ + LONG rcode; + int dwData; + BYTE static_buffer[16384]; + + if (data == NULL) + { + if (context) + heim_set_error_message(context, 0, + "'data' must not be NULL"); + return -1; + } + + if (cb_data == MAX_DWORD) + { + cb_data = (DWORD)strlen(data) + 1; + } + else if ((type == REG_MULTI_SZ && cb_data >= sizeof(static_buffer) - 1) || + cb_data >= sizeof(static_buffer)) + { + if (context) + heim_set_error_message(context, 0, "cb_data too big"); + return -1; + } + else if (data[cb_data-1] != '\0') + { + memcpy(static_buffer, data, cb_data); + static_buffer[cb_data++] = '\0'; + if (type == REG_MULTI_SZ) + static_buffer[cb_data++] = '\0'; + data = static_buffer; + } + + if (type == REG_NONE) + { + /* + * If input is all numeric, convert to DWORD and save as REG_DWORD. + * Otherwise, store as REG_SZ. + */ + if ( StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) ) + { + type = REG_DWORD; + } else { + type = REG_SZ; + } + } + + switch (type) { + case REG_SZ: + case REG_EXPAND_SZ: + rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data); + if (rcode) + { + if (context) + heim_set_error_message(context, 0, + "Unexpected error when setting registry value %s gle 0x%x", + valuename, + GetLastError()); + return -1; + } + break; + case REG_MULTI_SZ: + if (separator && *separator) + { + char *cp; + + if (data != static_buffer) + static_buffer[cb_data++] = '\0'; + + for ( cp = static_buffer; cp < static_buffer+cb_data; cp++) + { + if (*cp == *separator) + *cp = '\0'; + } + + rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data); + if (rcode) + { + if (context) + heim_set_error_message(context, 0, + "Unexpected error when setting registry value %s gle 0x%x", + valuename, + GetLastError()); + return -1; + } + } + break; + case REG_DWORD: + if ( !StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) ) + { + if (context) + heim_set_error_message(context, 0, + "Unexpected error when parsing %s as number gle 0x%x", + data, + GetLastError()); + } + + rcode = RegSetValueEx(key, valuename, 0, type, (BYTE *)&dwData, sizeof(DWORD)); + if (rcode) + { + if (context) + heim_set_error_message(context, 0, + "Unexpected error when setting registry value %s gle 0x%x", + valuename, + GetLastError()); + return -1; + } + break; + default: + return -1; + } + + return 0; +} + +/** + * Parse a registry value as a string + * + * @see heim_parse_reg_value_as_multi_string() + */ +char * +heim_parse_reg_value_as_string(heim_context context, + HKEY key, const char * valuename, + DWORD type, DWORD cb_data) +{ + return heim_parse_reg_value_as_multi_string(context, key, valuename, + type, cb_data, " "); +} + +/** + * Parse a registry value as a multi string + * + * The following registry value types are handled: + * + * - REG_DWORD: The decimal string representation is used as the + * value. + * + * - REG_SZ: The string is used as-is. + * + * - REG_EXPAND_SZ: Environment variables in the string are expanded + * and the result is used as the value. + * + * - REG_MULTI_SZ: The list of strings is concatenated using the + * separator. No quoting is performed. + * + * Any other value type is rejected. + * + * @param [in]valuename Name of the registry value to be queried + * @param [in]type Type of the value. REG_NONE if unknown + * @param [in]cbdata Size of value. 0 if unknown. + * @param [in]separator Separator character for concatenating strings. + * + * @a type and @a cbdata are only considered valid if both are + * specified. + * + * @retval The registry value string, or NULL if there was an error. + * If NULL is returned, an error message has been set using + * heim_set_error_message(). + */ +char * +heim_parse_reg_value_as_multi_string(heim_context context, + HKEY key, const char * valuename, + DWORD type, DWORD cb_data, char *separator) +{ + LONG rcode = ERROR_MORE_DATA; + + BYTE static_buffer[16384]; + BYTE *pbuffer = &static_buffer[0]; + DWORD cb_alloc = sizeof(static_buffer); + char *ret_string = NULL; + + /* If we know a type and cb_data from a previous call to + * RegEnumValue(), we use it. Otherwise we use the + * static_buffer[] and query directly. We do this to minimize the + * number of queries. */ + + if (type == REG_NONE || cb_data == 0) { + + pbuffer = &static_buffer[0]; + cb_alloc = cb_data = sizeof(static_buffer); + rcode = RegQueryValueExA(key, valuename, NULL, &type, pbuffer, &cb_data); + + if (rcode == ERROR_SUCCESS && + + ((type != REG_SZ && + type != REG_EXPAND_SZ) || cb_data + 1 <= sizeof(static_buffer)) && + + (type != REG_MULTI_SZ || cb_data + 2 <= sizeof(static_buffer))) + goto have_data; + + if (rcode != ERROR_MORE_DATA && rcode != ERROR_SUCCESS) + return NULL; + } + + /* Either we don't have the data or we aren't sure of the size + * (due to potentially missing terminating NULs). */ + + switch (type) { + case REG_DWORD: + if (cb_data != sizeof(DWORD)) { + if (context) + heim_set_error_message(context, 0, + "Unexpected size while reading registry value %s", + valuename); + return NULL; + } + break; + + case REG_SZ: + case REG_EXPAND_SZ: + + if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0') + goto have_data; + + cb_data += sizeof(char); /* Accout for potential missing NUL + * terminator. */ + break; + + case REG_MULTI_SZ: + + if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0' && + (cb_data == 1 || pbuffer[cb_data - 2] == '\0')) + goto have_data; + + cb_data += sizeof(char) * 2; /* Potential missing double NUL + * terminator. */ + break; + + default: + if (context) + heim_set_error_message(context, 0, + "Unexpected type while reading registry value %s", + valuename); + return NULL; + } + + if (cb_data <= sizeof(static_buffer)) + pbuffer = &static_buffer[0]; + else { + pbuffer = malloc(cb_data); + if (pbuffer == NULL) + return NULL; + } + + cb_alloc = cb_data; + rcode = RegQueryValueExA(key, valuename, NULL, NULL, pbuffer, &cb_data); + + if (rcode != ERROR_SUCCESS) { + + /* This can potentially be from a race condition. I.e. some + * other process or thread went and modified the registry + * value between the time we queried its size and queried for + * its value. Ideally we would retry the query in a loop. */ + + if (context) + heim_set_error_message(context, 0, + "Unexpected error while reading registry value %s", + valuename); + goto done; + } + + if (cb_data > cb_alloc || cb_data == 0) { + if (context) + heim_set_error_message(context, 0, + "Unexpected size while reading registry value %s", + valuename); + goto done; + } + +have_data: + switch (type) { + case REG_DWORD: + asprintf(&ret_string, "%d", *((DWORD *) pbuffer)); + break; + + case REG_SZ: + { + char * str = (char *) pbuffer; + + if (str[cb_data - 1] != '\0') { + if (cb_data < cb_alloc) + str[cb_data] = '\0'; + else + break; + } + + if (pbuffer != static_buffer) { + ret_string = (char *) pbuffer; + pbuffer = NULL; + } else { + ret_string = strdup((char *) pbuffer); + } + } + break; + + case REG_EXPAND_SZ: + { + char *str = (char *) pbuffer; + char expsz[32768]; /* Size of output buffer for + * ExpandEnvironmentStrings() is + * limited to 32K. */ + + if (str[cb_data - 1] != '\0') { + if (cb_data < cb_alloc) + str[cb_data] = '\0'; + else + break; + } + + if (ExpandEnvironmentStrings(str, expsz, sizeof(expsz)/sizeof(char)) != 0) { + ret_string = strdup(expsz); + } else { + if (context) + heim_set_error_message(context, 0, + "Overflow while expanding environment strings " + "for registry value %s", valuename); + } + } + break; + + case REG_MULTI_SZ: + { + char * str = (char *) pbuffer; + char * iter; + + str[cb_alloc - 1] = '\0'; + str[cb_alloc - 2] = '\0'; + + for (iter = str; *iter;) { + size_t len = strlen(iter); + + iter += len; + if (iter[1] != '\0') + *iter++ = *separator; + else + break; + } + + if (pbuffer != static_buffer) { + ret_string = str; + pbuffer = NULL; + } else { + ret_string = strdup(str); + } + } + break; + + default: + if (context) + heim_set_error_message(context, 0, + "Unexpected type while reading registry value %s", + valuename); + } + +done: + if (pbuffer != static_buffer && pbuffer != NULL) + free(pbuffer); + + return ret_string; +} + +/** + * Parse a registry value as a configuration value + * + * @see parse_reg_value_as_string() + */ +static heim_error_code +parse_reg_value(heim_context context, + HKEY key, const char * valuename, + DWORD type, DWORD cbdata, heim_config_section ** parent) +{ + char *reg_string = NULL; + heim_config_section *value; + heim_error_code code = 0; + + reg_string = heim_parse_reg_value_as_string(context, key, valuename, type, cbdata); + + if (reg_string == NULL) + return HEIM_ERR_CONFIG_BADFORMAT; + + value = heim_config_get_entry(parent, valuename, heim_config_string); + if (value == NULL) { + code = ENOMEM; + goto done; + } + + if (value->u.string != NULL) + free(value->u.string); + + value->u.string = reg_string; + reg_string = NULL; + +done: + if (reg_string != NULL) + free(reg_string); + + return code; +} + +static heim_error_code +parse_reg_values(heim_context context, + HKEY key, + heim_config_section ** parent) +{ + DWORD index; + LONG rcode; + + for (index = 0; ; index ++) { + char name[16385]; + DWORD cch = sizeof(name)/sizeof(name[0]); + DWORD type; + DWORD cbdata = 0; + heim_error_code code; + + rcode = RegEnumValue(key, index, name, &cch, NULL, + &type, NULL, &cbdata); + if (rcode != ERROR_SUCCESS) + break; + + if (cbdata == 0) + continue; + + code = parse_reg_value(context, key, name, type, cbdata, parent); + if (code != 0) + return code; + } + + return 0; +} + +static heim_error_code +parse_reg_subkeys(heim_context context, + HKEY key, + heim_config_section ** parent) +{ + DWORD index; + LONG rcode; + + for (index = 0; ; index ++) { + HKEY subkey = NULL; + char name[256]; + DWORD cch = sizeof(name)/sizeof(name[0]); + heim_config_section *section = NULL; + heim_error_code code; + + rcode = RegEnumKeyEx(key, index, name, &cch, NULL, NULL, NULL, NULL); + if (rcode != ERROR_SUCCESS) + break; + + rcode = RegOpenKeyEx(key, name, 0, KEY_READ, &subkey); + if (rcode != ERROR_SUCCESS) + continue; + + section = heim_config_get_entry(parent, name, heim_config_list); + if (section == NULL) { + RegCloseKey(subkey); + return ENOMEM; + } + + code = parse_reg_values(context, subkey, §ion->u.list); + if (code) { + RegCloseKey(subkey); + return code; + } + + code = parse_reg_subkeys(context, subkey, §ion->u.list); + if (code) { + RegCloseKey(subkey); + return code; + } + + RegCloseKey(subkey); + } + + return 0; +} + +static heim_error_code +parse_reg_root(heim_context context, + HKEY key, + heim_config_section ** parent) +{ + heim_config_section *libdefaults = NULL; + heim_error_code code = 0; + + libdefaults = heim_config_get_entry(parent, "libdefaults", heim_config_list); + if (libdefaults == NULL) + return heim_enomem(context); + + code = parse_reg_values(context, key, &libdefaults->u.list); + if (code) + return code; + + return parse_reg_subkeys(context, key, parent); +} + +static heim_error_code +load_config_from_regpath(heim_context context, + HKEY hk_root, + const char* key_path, + heim_config_section ** res) +{ + HKEY key = NULL; + LONG rcode; + heim_error_code code = 0; + + rcode = RegOpenKeyEx(hk_root, key_path, 0, KEY_READ, &key); + if (rcode == ERROR_SUCCESS) { + code = parse_reg_root(context, key, res); + RegCloseKey(key); + key = NULL; + } + + return code; +} + +/** + * Load configuration from registry + * + * The registry keys 'HKCU\Software\Heimdal' and + * 'HKLM\Software\Heimdal' are treated as krb5.conf files. Each + * registry key corresponds to a configuration section (or bound list) + * and each value in a registry key is treated as a bound value. The + * set of values that are directly under the Heimdal key are treated + * as if they were defined in the [libdefaults] section. + * + * @see parse_reg_value() for details about how each type of value is handled. + */ +heim_error_code +heim_load_config_from_registry(heim_context context, + const char *path0, + const char *path1, + heim_config_section **res) +{ + heim_error_code code; + + if (!path0 && !path1) + return EINVAL; + + if (path0) { + code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE, + path0, res); + if (code) + return code; + } + + if (path1) { + code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE, + path1, res); + if (code) + return code; + } + + if (path0) { + code = load_config_from_regpath(context, HKEY_CURRENT_USER, + path0, res); + if (code) + return code; + } + + if (path0) { + code = load_config_from_regpath(context, HKEY_CURRENT_USER, + path1, res); + if (code) + return code; + } + return 0; +} diff --git a/lib/base/context.c b/lib/base/context.c new file mode 100644 index 000000000000..f22ce9459f44 --- /dev/null +++ b/lib/base/context.c @@ -0,0 +1,394 @@ +/* + * 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. + */ + +#include "baselocl.h" + +#undef __attribute__ +#define __attribute__(X) + +heim_context +heim_context_init(void) +{ + heim_context context; + + if ((context = calloc(1, sizeof(*context))) == NULL) + return NULL; + + context->homedir_access = !issuid(); + context->log_utc = 1; + context->error_string = NULL; + context->debug_dest = NULL; + context->warn_dest = NULL; + context->log_dest = NULL; + context->time_fmt = NULL; + context->et_list = NULL; + return context; +} + +void +heim_context_free(heim_context *contextp) +{ + heim_context context = *contextp; + + *contextp = NULL; + if (!context) + return; + heim_closelog(context, context->debug_dest); + heim_closelog(context, context->warn_dest); + heim_closelog(context, context->log_dest); + free_error_table(context->et_list); + free(context->time_fmt); + free(context->error_string); + free(context); +} + +heim_error_code +heim_add_et_list(heim_context context, void (*func)(struct et_list **)) +{ + (*func)(&context->et_list); + return 0; +} + +heim_error_code +heim_context_set_time_fmt(heim_context context, const char *fmt) +{ + char *s; + + if (fmt == NULL) { + free(context->time_fmt); + return 0; + } + if ((s = strdup(fmt)) == NULL) + return heim_enomem(context); + free(context->time_fmt); + context->time_fmt = s; + return 0; +} + +const char * +heim_context_get_time_fmt(heim_context context) +{ + return context->time_fmt ? context->time_fmt : "%Y-%m-%dT%H:%M:%S"; +} + +unsigned int +heim_context_set_log_utc(heim_context context, unsigned int log_utc) +{ + unsigned int old = context->log_utc; + + context->log_utc = log_utc ? 1 : 0; + return old; +} + +int +heim_context_get_log_utc(heim_context context) +{ + return context->log_utc; +} + +unsigned int +heim_context_set_homedir_access(heim_context context, unsigned int homedir_access) +{ + unsigned int old = context->homedir_access; + + context->homedir_access = homedir_access ? 1 : 0; + return old; +} + +unsigned int +heim_context_get_homedir_access(heim_context context) +{ + return context->homedir_access; +} + +heim_error_code +heim_enomem(heim_context context) +{ + heim_set_error_message(context, ENOMEM, "malloc: out of memory"); + return ENOMEM; +} + +heim_log_facility * +heim_get_log_dest(heim_context context) +{ + return context->log_dest; +} + +heim_log_facility * +heim_get_warn_dest(heim_context context) +{ + return context->warn_dest; +} + +heim_log_facility * +heim_get_debug_dest(heim_context context) +{ + return context->debug_dest; +} + +heim_error_code +heim_set_log_dest(heim_context context, heim_log_facility *fac) +{ + context->log_dest = heim_log_ref(fac); + return 0; +} + +heim_error_code +heim_set_warn_dest(heim_context context, heim_log_facility *fac) +{ + context->warn_dest = fac; + return 0; +} + +heim_error_code +heim_set_debug_dest(heim_context context, heim_log_facility *fac) +{ + context->debug_dest = fac; + return 0; +} + +#ifndef PATH_SEP +# define PATH_SEP ":" +#endif + +static heim_error_code +add_file(char ***pfilenames, int *len, char *file) +{ + char **pp = *pfilenames; + int i; + + for(i = 0; i < *len; i++) { + if(strcmp(pp[i], file) == 0) { + free(file); + return 0; + } + } + + pp = realloc(*pfilenames, (*len + 2) * sizeof(*pp)); + if (pp == NULL) { + free(file); + return ENOMEM; + } + + pp[*len] = file; + pp[*len + 1] = NULL; + *pfilenames = pp; + *len += 1; + return 0; +} + +#ifdef WIN32 +static char * +get_default_config_config_files_from_registry(const char *envvar) +{ + static const char *KeyName = "Software\\Heimdal"; /* XXX #define this */ + const char *ValueName; + char *config_file = NULL; + LONG rcode; + HKEY key; + + if (stricmp(envvar, "KRB5_CONFIG") == 0) + ValueName = "config"; + else + ValueName = envvar; + + rcode = RegOpenKeyEx(HKEY_CURRENT_USER, KeyName, 0, KEY_READ, &key); + if (rcode == ERROR_SUCCESS) { + config_file = heim_parse_reg_value_as_multi_string(NULL, key, ValueName, + REG_NONE, 0, PATH_SEP); + RegCloseKey(key); + } + + if (config_file) + return config_file; + + rcode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KeyName, 0, KEY_READ, &key); + if (rcode == ERROR_SUCCESS) { + config_file = heim_parse_reg_value_as_multi_string(NULL, key, ValueName, + REG_NONE, 0, PATH_SEP); + RegCloseKey(key); + } + + return config_file; +} +#endif + +heim_error_code +heim_prepend_config_files(const char *filelist, + char **pq, + char ***ret_pp) +{ + heim_error_code ret; + const char *p, *q; + char **pp; + int len; + char *fn; + + pp = NULL; + + len = 0; + p = filelist; + while(1) { + ssize_t l; + q = p; + l = strsep_copy(&q, PATH_SEP, NULL, 0); + if(l == -1) + break; + fn = malloc(l + 1); + if(fn == NULL) { + heim_free_config_files(pp); + return ENOMEM; + } + (void) strsep_copy(&p, PATH_SEP, fn, l + 1); + ret = add_file(&pp, &len, fn); + if (ret) { + heim_free_config_files(pp); + return ret; + } + } + + if (pq != NULL) { + int i; + + for (i = 0; pq[i] != NULL; i++) { + fn = strdup(pq[i]); + if (fn == NULL) { + heim_free_config_files(pp); + return ENOMEM; + } + ret = add_file(&pp, &len, fn); + if (ret) { + heim_free_config_files(pp); + return ret; + } + } + } + + *ret_pp = pp; + return 0; +} + +heim_error_code +heim_prepend_config_files_default(const char *prepend, + const char *def, + const char *envvar, + char ***pfilenames) +{ + heim_error_code ret; + char **defpp, **pp = NULL; + + ret = heim_get_default_config_files(def, envvar, &defpp); + if (ret) + return ret; + + ret = heim_prepend_config_files(prepend, defpp, &pp); + heim_free_config_files(defpp); + if (ret) { + return ret; + } + *pfilenames = pp; + return 0; +} + +heim_error_code +heim_get_default_config_files(const char *def, + const char *envvar, + char ***pfilenames) +{ + const char *files = NULL; + + files = secure_getenv(envvar); + +#ifdef _WIN32 + if (files == NULL) { + char * reg_files; + reg_files = get_default_config_config_files_from_registry(envvar); + if (reg_files != NULL) { + heim_error_code code; + + code = heim_prepend_config_files(reg_files, NULL, pfilenames); + free(reg_files); + + return code; + } + } +#endif + + if (files == NULL) + files = def; + return heim_prepend_config_files(files, NULL, pfilenames); +} + +#ifdef _WIN32 +#define REGPATH_KERBEROS "SOFTWARE\\Kerberos" +#define REGPATH_HEIMDAL "SOFTWARE\\Heimdal" +#endif + +heim_error_code +heim_set_config_files(heim_context context, char **filenames, + heim_config_binding **res) +{ + heim_error_code ret = 0; + + *res = NULL; + while (filenames != NULL && *filenames != NULL && **filenames != '\0') { + ret = heim_config_parse_file_multi(context, *filenames, res); + if (ret != 0 && ret != ENOENT && ret != EACCES && ret != EPERM + && ret != HEIM_ERR_CONFIG_BADFORMAT) { + heim_config_file_free(context, *res); + *res = NULL; + return ret; + } + filenames++; + } + +#ifdef _WIN32 + /* + * We always ignored errors from loading from the registry, so we still do. + */ + heim_load_config_from_registry(context, REGPATH_KERBEROS, + REGPATH_HEIMDAL, res); + +#endif + return 0; +} + +void +heim_free_config_files(char **filenames) +{ + char **p; + + for (p = filenames; p && *p != NULL; p++) + free(*p); + free(filenames); +} diff --git a/lib/base/data.c b/lib/base/data.c index 4aa6efc66774..cefdde0c1bbe 100644 --- a/lib/base/data.c +++ b/lib/base/data.c @@ -34,7 +34,7 @@ #include "baselocl.h" #include <string.h> -static void +static void HEIM_CALLCONV data_dealloc(void *ptr) { heim_data_t d = ptr; @@ -61,7 +61,7 @@ data_cmp(void *a, void *b) return memcmp(osa->data, osb->data, osa->length); } -static unsigned long +static uintptr_t data_hash(void *ptr) { heim_octet_string *os = ptr; @@ -69,8 +69,9 @@ data_hash(void *ptr) if (os->length < 4) return os->length; - return s[0] | (s[1] << 8) | - (s[os->length - 2] << 16) | (s[os->length - 1] << 24); + + return ((unsigned long)s[os->length - 1] << 24) + | (s[os->length - 2] << 16) | (s[1] << 8) | s[0]; } struct heim_type_data _heim_data_object = { diff --git a/lib/base/db.c b/lib/base/db.c index 944091684882..e6f6af41a203 100644 --- a/lib/base/db.c +++ b/lib/base/db.c @@ -47,6 +47,8 @@ * memory-based rollback log is used). */ +#include "config.h" + #include <errno.h> #include <stdio.h> #include <stdlib.h> @@ -84,7 +86,7 @@ static int open_file(const char *, int , int, int *, heim_error_t *); static int read_json(const char *, heim_object_t *, heim_error_t *); static struct heim_db_type json_dbt; -static void db_dealloc(void *ptr); +static void HEIM_CALLCONV db_dealloc(void *ptr); struct heim_type_data db_object = { HEIM_TID_DB, @@ -150,7 +152,7 @@ db_init_plugins_once(void *arg) db_plugins = heim_retain(arg); } -static void +static void HEIM_CALLCONV plugin_dealloc(void *arg) { db_plugin plug = arg; @@ -242,7 +244,7 @@ heim_db_register(const char *dbtype, return ret; } -static void +static void HEIM_CALLCONV db_dealloc(void *arg) { heim_db_t db = arg; @@ -577,7 +579,7 @@ heim_db_commit(heim_db_t db, heim_error_t *error) goto done; } - if (db->options == NULL) + if (db->options) journal_fname = heim_dict_get_value(db->options, HSTR("journal-filename")); if (journal_fname != NULL) { @@ -1144,21 +1146,15 @@ enomem: static heim_data_t from_base64(heim_string_t s, heim_error_t *error) { + ssize_t len = -1; void *buf; - size_t len; heim_data_t d; buf = malloc(strlen(heim_string_get_utf8(s))); - if (buf == NULL) - goto enomem; - - len = rk_base64_decode(heim_string_get_utf8(s), buf); - d = heim_data_ref_create(buf, len, free); - if (d == NULL) - goto enomem; - return d; - -enomem: + if (buf) + len = rk_base64_decode(heim_string_get_utf8(s), buf); + if (len > -1 && (d = heim_data_ref_create(buf, len, free))) + return d; free(buf); if (error) *error = heim_error_create_enomem(); @@ -1327,7 +1323,7 @@ json_db_open(void *plug, const char *dbtype, const char *dbname, if (error) *error = NULL; - if (dbtype && *dbtype && strcmp(dbtype, "json")) + if (dbtype && *dbtype && strcmp(dbtype, "json") != 0) return HEIM_ERROR(error, EINVAL, (EINVAL, N_("Wrong DB type", ""))); if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0) { char *ext = strrchr(dbname, '.'); @@ -1511,11 +1507,12 @@ json_db_sync(void *db, heim_error_t *error) json = heim_json_copy_serialize(jsondb->dict, 0, &e); if (json == NULL) { + ret = heim_error_get_code(e); if (error) *error = e; else heim_release(e); - return heim_error_get_code(e); + return ret; } json_text = heim_string_get_utf8(json); diff --git a/lib/base/dict.c b/lib/base/dict.c index 8d73846b2bbb..86be109ffc5e 100644 --- a/lib/base/dict.c +++ b/lib/base/dict.c @@ -47,7 +47,7 @@ struct heim_dict_data { struct hashentry **tab; }; -static void +static void HEIM_CALLCONV dict_dealloc(void *ptr) { heim_dict_t dict = ptr; @@ -115,6 +115,8 @@ heim_dict_create(size_t size) heim_dict_t dict; dict = _heim_alloc_object(&dict_object, sizeof(*dict)); + if (dict == NULL) + return NULL; dict->size = findprime(size); if (dict->size == 0) { @@ -149,7 +151,7 @@ heim_dict_get_type_id(void) static struct hashentry * _search(heim_dict_t dict, heim_object_t ptr) { - unsigned long v = heim_get_hash(ptr); + uintptr_t v = heim_get_hash(ptr); struct hashentry *p; for (p = dict->tab[v % dict->size]; p != NULL; p = p->next) @@ -219,7 +221,7 @@ heim_dict_set_value(heim_dict_t dict, heim_object_t key, heim_object_t value) heim_release(h->value); h->value = heim_retain(value); } else { - unsigned long v; + uintptr_t v; h = malloc(sizeof(*h)); if (h == NULL) diff --git a/lib/base/error.c b/lib/base/error.c index 8ae65de4981e..bc289d3d910a 100644 --- a/lib/base/error.c +++ b/lib/base/error.c @@ -41,7 +41,7 @@ struct heim_error { struct heim_error *next; }; -static void +static void HEIM_CALLCONV error_dealloc(void *ptr) { struct heim_error *p = ptr; @@ -58,7 +58,7 @@ error_cmp(void *a, void *b) return heim_cmp(ap->msg, bp->msg); } -static unsigned long +static uintptr_t error_hash(void *ptr) { struct heim_error *p = ptr; @@ -86,6 +86,7 @@ heim_error_create_enomem(void) void heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...) + HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 3, 4)) { if (error) { va_list ap; @@ -97,6 +98,7 @@ heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...) heim_error_t heim_error_create(int error_code, const char *fmt, ...) + HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 3)) { heim_error_t e; va_list ap; @@ -110,6 +112,7 @@ heim_error_create(int error_code, const char *fmt, ...) heim_error_t heim_error_createv(int error_code, const char *fmt, va_list ap) + HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 0)) { heim_error_t e; char *str; diff --git a/lib/base/error_string.c b/lib/base/error_string.c new file mode 100644 index 000000000000..a562833a91a0 --- /dev/null +++ b/lib/base/error_string.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2001, 2003, 2005 - 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. + */ + +#include "baselocl.h" + +#undef __attribute__ +#define __attribute__(x) + +void +heim_clear_error_message(heim_context context) +{ + if (!context) + return; + if (context->error_string) + free(context->error_string); + context->error_code = 0; + context->error_string = NULL; +} + +void +heim_set_error_message(heim_context context, heim_error_code ret, + const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 3, 4))) +{ + va_list ap; + + va_start(ap, fmt); + if (context) + heim_vset_error_message(context, ret, fmt, ap); + va_end(ap); +} + +void +heim_vset_error_message(heim_context context, heim_error_code ret, + const char *fmt, va_list args) + __attribute__ ((__format__ (__printf__, 3, 0))) +{ + int r; + + if (context == NULL) + return; + if (context->error_string) { + free(context->error_string); + context->error_string = NULL; + } + context->error_code = ret; + r = vasprintf(&context->error_string, fmt, args); + if (r < 0) + context->error_string = NULL; + if (context->error_string) + heim_debug(context, 200, "error message: %s: %d", context->error_string, ret); +} + +void +heim_prepend_error_message(heim_context context, heim_error_code ret, + const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 3, 4))) +{ + va_list ap; + + va_start(ap, fmt); + heim_vprepend_error_message(context, ret, fmt, ap); + va_end(ap); +} + +void +heim_vprepend_error_message(heim_context context, heim_error_code ret, + const char *fmt, va_list args) + __attribute__ ((__format__ (__printf__, 3, 0))) +{ + char *str = NULL, *str2 = NULL; + + if (context == NULL || context->error_code != ret || + vasprintf(&str, fmt, args) < 0 || str == NULL) + return; + if (context->error_string) { + int e; + + e = asprintf(&str2, "%s: %s", str, context->error_string); + free(context->error_string); + if (e < 0 || str2 == NULL) + context->error_string = NULL; + else + context->error_string = str2; + free(str); + } else + context->error_string = str; +} + +const char * +heim_get_error_message(heim_context context, heim_error_code code) +{ + const char *cstr = NULL; + char *str = NULL; + char buf[128]; + int free_context = 0; + + if (code == 0) + return strdup("Success"); + + /* + * The MIT version of this function ignores the krb5_context + * and several widely deployed applications call krb5_get_error_message() + * with a NULL context in order to translate an error code as a + * replacement for error_message(). Another reason a NULL context + * might be provided is if the krb5_init_context() call itself + * failed. + */ + if (context && + context->error_string && + (code == context->error_code || context->error_code == 0) && + (cstr = strdup(context->error_string))) + return cstr; + + if (context == NULL && (context = heim_context_init())) + free_context = 1; + if (context) + cstr = com_right_r(context->et_list, code, buf, sizeof(buf)); + if (free_context) + heim_context_free(&context); + + if (cstr || (cstr = error_message(code))) + return strdup(cstr); + if (asprintf(&str, "<unknown error: %d>", (int)code) == -1 || str == NULL) + return NULL; + return str; +} + +const char * +heim_get_error_string(heim_context context) +{ + if (context && context->error_string) + return strdup(context->error_string); + return NULL; +} + +int +heim_have_error_string(heim_context context) +{ + return context && context->error_string != NULL; +} + +void +heim_free_error_message(heim_context context, const char *msg) +{ + free(rk_UNCONST(msg)); +} diff --git a/lib/base/expand_path.c b/lib/base/expand_path.c new file mode 100644 index 000000000000..cf249917e8fe --- /dev/null +++ b/lib/base/expand_path.c @@ -0,0 +1,725 @@ + +/*********************************************************************** + * Copyright (c) 2009-2020, Secure Endpoints Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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 "baselocl.h" + +#include <stdarg.h> + +typedef int PTYPE; + +#ifdef _WIN32 +#include <shlobj.h> +#include <sddl.h> + +/* + * Expand a %{TEMP} token + * + * The %{TEMP} token expands to the temporary path for the current + * user as returned by GetTempPath(). + * + * @note: Since the GetTempPath() function relies on the TMP or TEMP + * environment variables, this function will failover to the system + * temporary directory until the user profile is loaded. In addition, + * the returned path may or may not exist. + */ +static heim_error_code +expand_temp_folder(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **ret) +{ + TCHAR tpath[MAX_PATH]; + size_t len; + + if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) { + heim_set_error_message(context, EINVAL, + "Failed to get temporary path (GLE=%d)", + GetLastError()); + return EINVAL; + } + + len = strlen(tpath); + + if (len > 0 && tpath[len - 1] == '\\') + tpath[len - 1] = '\0'; + + *ret = strdup(tpath); + + if (*ret == NULL) + return heim_enomem(context); + + return 0; +} + +EXTERN_C IMAGE_DOS_HEADER __ImageBase; + +/* + * Expand a %{BINDIR} token + * + * This is also used to expand a few other tokens on Windows, since + * most of the executable binaries end up in the same directory. The + * "bin" directory is considered to be the directory in which the + * containing DLL is located. + */ +static heim_error_code +expand_bin_dir(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **ret) +{ + TCHAR path[MAX_PATH]; + TCHAR *lastSlash; + DWORD nc; + + nc = GetModuleFileName((HINSTANCE)&__ImageBase, path, + sizeof(path)/sizeof(path[0])); + if (nc == 0 || + nc == sizeof(path)/sizeof(path[0])) { + return EINVAL; + } + + lastSlash = strrchr(path, '\\'); + if (lastSlash != NULL) { + TCHAR *fslash = strrchr(lastSlash, '/'); + + if (fslash != NULL) + lastSlash = fslash; + + *lastSlash = '\0'; + } + + if (postfix) { + if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0])) + return EINVAL; + } + + *ret = strdup(path); + if (*ret == NULL) + return heim_enomem(context); + + return 0; +} + +/* + * Expand a %{USERID} token + * + * The %{USERID} token expands to the string representation of the + * user's SID. The user account that will be used is the account + * corresponding to the current thread's security token. This means + * that: + * + * - If the current thread token has the anonymous impersonation + * level, the call will fail. + * + * - If the current thread is impersonating a token at + * SecurityIdentification level the call will fail. + * + */ +static heim_error_code +expand_userid(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **ret) +{ + int rv = EINVAL; + HANDLE hThread = NULL; + HANDLE hToken = NULL; + PTOKEN_OWNER pOwner = NULL; + DWORD len = 0; + LPTSTR strSid = NULL; + + hThread = GetCurrentThread(); + + if (!OpenThreadToken(hThread, TOKEN_QUERY, + FALSE, /* Open the thread token as the + current thread user. */ + &hToken)) { + + DWORD le = GetLastError(); + + if (le == ERROR_NO_TOKEN) { + HANDLE hProcess = GetCurrentProcess(); + + le = 0; + if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) + le = GetLastError(); + } + + if (le != 0) { + heim_set_error_message(context, rv, + "Can't open thread token (GLE=%d)", le); + goto _exit; + } + } + + if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + heim_set_error_message(context, rv, + "Unexpected error reading token information (GLE=%d)", + GetLastError()); + goto _exit; + } + + if (len == 0) { + heim_set_error_message(context, rv, + "GetTokenInformation() returned truncated buffer"); + goto _exit; + } + + pOwner = malloc(len); + if (pOwner == NULL) { + heim_set_error_message(context, rv, "Out of memory"); + goto _exit; + } + } else { + heim_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer"); + goto _exit; + } + + if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) { + heim_set_error_message(context, rv, + "GetTokenInformation() failed. GLE=%d", + GetLastError()); + goto _exit; + } + + if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) { + heim_set_error_message(context, rv, + "Can't convert SID to string. GLE=%d", + GetLastError()); + goto _exit; + } + + *ret = strdup(strSid); + if (*ret == NULL) + heim_set_error_message(context, rv, "Out of memory"); + + rv = 0; + + _exit: + if (hToken != NULL) + CloseHandle(hToken); + + if (pOwner != NULL) + free (pOwner); + + if (strSid != NULL) + LocalFree(strSid); + + return rv; +} + +/* + * Expand a folder identified by a CSIDL + */ + +static heim_error_code +expand_csidl(heim_context context, PTYPE folder, const char *postfix, + const char *arg, char **ret) +{ + TCHAR path[MAX_PATH]; + size_t len; + + if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) { + heim_set_error_message(context, EINVAL, "Unable to determine folder path"); + return EINVAL; + } + + len = strlen(path); + + if (len > 0 && path[len - 1] == '\\') + path[len - 1] = '\0'; + + if (postfix && + strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0])) + return heim_enomem(context); + + *ret = strdup(path); + if (*ret == NULL) + return heim_enomem(context); + return 0; +} + +#else + +static heim_error_code +expand_path(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **ret) +{ + *ret = strdup(postfix); + if (*ret == NULL) + return heim_enomem(context); + return 0; +} + +static heim_error_code +expand_temp_folder(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **ret) +{ + const char *p = NULL; + + p = secure_getenv("TEMP"); + + if (p) + *ret = strdup(p); + else + *ret = strdup("/tmp"); + if (*ret == NULL) + return heim_enomem(context); + return 0; +} + +static heim_error_code +expand_userid(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **str) +{ + int ret = asprintf(str, "%ld", (unsigned long)getuid()); + if (ret < 0 || *str == NULL) + return heim_enomem(context); + return 0; +} + +static heim_error_code +expand_euid(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **str) +{ + int ret = asprintf(str, "%ld", (unsigned long)geteuid()); + if (ret < 0 || *str == NULL) + return heim_enomem(context); + return 0; +} +#endif /* _WIN32 */ + +static heim_error_code +expand_home(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **str) +{ + char homedir[MAX_PATH]; + int ret; + + if (roken_get_homedir(homedir, sizeof(homedir))) + ret = asprintf(str, "%s", homedir); + else + ret = asprintf(str, "/unknown"); + if (ret < 0 || *str == NULL) + return heim_enomem(context); + return 0; +} + +static heim_error_code +expand_username(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **str) +{ + char user[128]; + const char *username = roken_get_username(user, sizeof(user)); + + if (username == NULL) { + heim_set_error_message(context, ENOTTY, + N_("unable to figure out current principal", + "")); + return ENOTTY; /* XXX */ + } + + *str = strdup(username); + if (*str == NULL) + return heim_enomem(context); + + return 0; +} + +static heim_error_code +expand_loginname(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **str) +{ + char user[128]; + const char *username = roken_get_loginname(user, sizeof(user)); + + if (username == NULL) { + heim_set_error_message(context, ENOTTY, + N_("unable to figure out current principal", + "")); + return ENOTTY; /* XXX */ + } + + *str = strdup(username); + if (*str == NULL) + return heim_enomem(context); + + return 0; +} + +static heim_error_code +expand_strftime(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **ret) +{ + size_t len; + time_t t; + char buf[1024]; + + t = time(NULL); + len = strftime(buf, sizeof(buf), arg, localtime(&t)); + if (len == 0 || len >= sizeof(buf)) + return heim_enomem(context); + *ret = strdup(buf); + return 0; +} + +/** + * Expand an extra token + */ + +static heim_error_code +expand_extra_token(heim_context context, const char *value, char **ret) +{ + *ret = strdup(value); + if (*ret == NULL) + return heim_enomem(context); + return 0; +} + +/** + * Expand a %{null} token + * + * The expansion of a %{null} token is always the empty string. + */ + +static heim_error_code +expand_null(heim_context context, PTYPE param, const char *postfix, + const char *arg, char **ret) +{ + *ret = strdup(""); + if (*ret == NULL) + return heim_enomem(context); + return 0; +} + + +static const struct { + const char * tok; + int ftype; +#define FTYPE_CSIDL 0 +#define FTYPE_SPECIAL 1 + + PTYPE param; + const char * postfix; + + int (*exp_func)(heim_context, PTYPE, const char *, const char *, char **); + +#define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f +#define SPECIAL(f) SPECIALP(f, NULL) + +} tokens[] = { +#ifdef _WIN32 +#define CSIDLP(C,P) FTYPE_CSIDL, C, P, expand_csidl +#define CSIDL(C) CSIDLP(C, NULL) + + {"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */ + {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */ + {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */ + {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */ + {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */ + {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */ + {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */ + {"LIBDIR", SPECIAL(expand_bin_dir)}, + {"BINDIR", SPECIAL(expand_bin_dir)}, + {"LIBEXEC", SPECIAL(expand_bin_dir)}, + {"SBINDIR", SPECIAL(expand_bin_dir)}, +#else + {"LOCALSTATEDIR", FTYPE_SPECIAL, 0, LOCALSTATEDIR, expand_path}, + {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, expand_path}, + {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, expand_path}, + {"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, expand_path}, + {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, expand_path}, + {"USERCONFIG", SPECIAL(expand_home)}, /* same as %{HOME} on not-Windows */ + {"euid", SPECIAL(expand_euid)}, + {"ruid", SPECIAL(expand_userid)}, + {"loginname", SPECIAL(expand_loginname)}, +#endif + {"username", SPECIAL(expand_username)}, + {"TEMP", SPECIAL(expand_temp_folder)}, + {"USERID", SPECIAL(expand_userid)}, + {"uid", SPECIAL(expand_userid)}, + {"null", SPECIAL(expand_null)}, + {"strftime", SPECIAL(expand_strftime)}, + {"HOME", SPECIAL(expand_home)}, +}; + +static heim_error_code +expand_token(heim_context context, + const char *token, + const char *token_end, + char **extra_tokens, + char **ret) +{ + heim_error_code errcode; + size_t i; + char **p; + const char *colon; + + *ret = NULL; + + if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' || + token_end - token <= 2) { + heim_set_error_message(context, EINVAL,"Invalid token."); + return EINVAL; + } + + for (p = extra_tokens; p && p[0]; p += 2) { + if (strncmp(token+2, p[0], (token_end - token) - 2) == 0) + return expand_extra_token(context, p[1], ret); + } + + for (colon=token+2; colon < token_end; colon++) + if (*colon == ':') + break; + + for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++) + if (!strncmp(token+2, tokens[i].tok, (colon - token) - 2)) { + char *arg = NULL; + + errcode = 0; + if (*colon == ':') { + int asprintf_ret = asprintf(&arg, "%.*s", + (int)(token_end - colon - 1), + colon + 1); + if (asprintf_ret < 0 || !arg) + errcode = ENOMEM; + } + if (!errcode) + errcode = tokens[i].exp_func(context, tokens[i].param, + tokens[i].postfix, arg, ret); + free(arg); + return errcode; + } + + heim_set_error_message(context, EINVAL, "Invalid token."); + return EINVAL; +} + +/** + * Internal function to expand tokens in paths. + * + * Params: + * + * @context A heim_context + * @path_in The path to expand tokens from + * @filepath True if this is a filesystem path (converts slashes to + * backslashes on Windows) + * @ppath_out The expanded path + * @... Variable number of pairs of strings, the first of each + * being a token (e.g., "luser") and the second a string to + * replace it with. The list is terminated by a NULL. + */ +heim_error_code +heim_expand_path_tokens(heim_context context, + const char *path_in, + int filepath, + char **ppath_out, + ...) +{ + heim_error_code ret; + va_list ap; + + va_start(ap, ppath_out); + ret = heim_expand_path_tokensv(context, path_in, filepath, ppath_out, ap); + va_end(ap); + + return ret; +} + +static void +free_extra_tokens(char **extra_tokens) +{ + char **p; + + for (p = extra_tokens; p && *p; p++) + free(*p); + free(extra_tokens); +} + +/** + * Internal function to expand tokens in paths. + * + * Inputs: + * + * @context A heim_context + * @path_in The path to expand tokens from + * @filepath True if this is a filesystem path (converts slashes to + * backslashes on Windows) + * @ppath_out The expanded path + * @ap A NULL-terminated va_list of pairs of strings, the first of each + * being a token (e.g., "luser") and the second a string to replace + * it with. + * + * Outputs: + * + * @ppath_out Path with expanded tokens (caller must free() this) + */ +heim_error_code +heim_expand_path_tokensv(heim_context context, + const char *path_in, + int filepath, + char **ppath_out, va_list ap) +{ + char *tok_begin, *tok_end, *append; + char **extra_tokens = NULL; + const char *path_left; + size_t nargs = 0; + size_t len = 0; + va_list ap2; + + if (path_in == NULL || *path_in == '\0') { + *ppath_out = strdup(""); + return 0; + } + + *ppath_out = NULL; + +#if defined(_MSC_VER) + ap2 = ap; /* Come on! See SO #558223 */ +#else + va_copy(ap2, ap); +#endif + while (va_arg(ap2, const char *)) { + nargs++; + va_arg(ap2, const char *); + } + va_end(ap2); + nargs *= 2; + + /* Get extra tokens */ + if (nargs) { + size_t i; + + extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens)); + if (extra_tokens == NULL) + return heim_enomem(context); + for (i = 0; i < nargs; i++) { + const char *s = va_arg(ap, const char *); /* token key */ + if (s == NULL) + break; + extra_tokens[i] = strdup(s); + if (extra_tokens[i++] == NULL) { + free_extra_tokens(extra_tokens); + return heim_enomem(context); + } + s = va_arg(ap, const char *); /* token value */ + if (s == NULL) + s = ""; + extra_tokens[i] = strdup(s); + if (extra_tokens[i] == NULL) { + free_extra_tokens(extra_tokens); + return heim_enomem(context); + } + } + } + + for (path_left = path_in; path_left && *path_left; ) { + + tok_begin = strstr(path_left, "%{"); + + if (tok_begin && tok_begin != path_left) { + + append = malloc((tok_begin - path_left) + 1); + if (append) { + memcpy(append, path_left, tok_begin - path_left); + append[tok_begin - path_left] = '\0'; + } + path_left = tok_begin; + + } else if (tok_begin) { + + tok_end = strchr(tok_begin, '}'); + if (tok_end == NULL) { + free_extra_tokens(extra_tokens); + if (*ppath_out) + free(*ppath_out); + *ppath_out = NULL; + heim_set_error_message(context, EINVAL, "variable missing }"); + return EINVAL; + } + + if (expand_token(context, tok_begin, tok_end, extra_tokens, + &append)) { + free_extra_tokens(extra_tokens); + if (*ppath_out) + free(*ppath_out); + *ppath_out = NULL; + return EINVAL; + } + + path_left = tok_end + 1; + } else { + + append = strdup(path_left); + path_left = NULL; + + } + + if (append == NULL) { + + free_extra_tokens(extra_tokens); + if (*ppath_out) + free(*ppath_out); + *ppath_out = NULL; + return heim_enomem(context); + + } + + { + size_t append_len = strlen(append); + char * new_str = realloc(*ppath_out, len + append_len + 1); + + if (new_str == NULL) { + free_extra_tokens(extra_tokens); + free(append); + if (*ppath_out) + free(*ppath_out); + *ppath_out = NULL; + return heim_enomem(context); + } + + *ppath_out = new_str; + memcpy(*ppath_out + len, append, append_len + 1); + len = len + append_len; + free(append); + } + } + +#ifdef _WIN32 + /* Also deal with slashes */ + if (filepath && *ppath_out) { + char * c; + + for (c = *ppath_out; *c; c++) + if (*c == '/') + *c = '\\'; + } +#endif + + free_extra_tokens(extra_tokens); + return 0; +} diff --git a/lib/base/heim_err.et b/lib/base/heim_err.et new file mode 100644 index 000000000000..5532d9b84dbb --- /dev/null +++ b/lib/base/heim_err.et @@ -0,0 +1,57 @@ +# +# Error messages for the krb5 library +# +# This might look like a com_err file, but is not +# +id "$Id$" + +error_table heim + +prefix HEIM_ERR + +error_code LOG_PARSE, "Error parsing log destination" +error_code V4_PRINC_NO_CONV, "Failed to convert v4 principal" +error_code SALTTYPE_NOSUPP, "Salt type is not supported by enctype" +error_code NOHOST, "Host not found" +error_code OPNOTSUPP, "Operation not supported" +error_code EOF, "End of file" +error_code BAD_MKEY, "Failed to get the master key" +error_code SERVICE_NOMATCH, "Unacceptable service used" +error_code NOT_SEEKABLE, "File descriptor not seekable" +error_code TOO_BIG, "Offset too large" +error_code BAD_HDBENT_ENCODING, "Invalid HDB entry encoding" +error_code RANDOM_OFFLINE, "No random source available" +error_code CONFIG_BADFORMAT, "Improper format of configuration file" +error_code PA_CONTINUE_NEEDED, "Need to continue preauth stepping" +error_code PA_CANT_CONTINUE, "Can't continue with this preauth" +error_code NO_MORE_PA_MECHS, "No more PA mechanisms available" + +index 64 +prefix HEIM_PKINIT +error_code NO_CERTIFICATE, "Certificate missing" +error_code NO_PRIVATE_KEY, "Private key missing" +error_code NO_VALID_CA, "No valid certificate authority" +error_code CERTIFICATE_INVALID, "Certificate invalid" +error_code PRIVATE_KEY_INVALID, "Private key invalid" + +index 128 +prefix HEIM_EAI +#error_code NOERROR, "no error" +error_code UNKNOWN, "unknown error from getaddrinfo" +error_code ADDRFAMILY, "address family for nodename not supported" +error_code AGAIN, "temporary failure in name resolution" +error_code BADFLAGS, "invalid value for ai_flags" +error_code FAIL, "non-recoverable failure in name resolution" +error_code FAMILY, "ai_family not supported" +error_code MEMORY, "memory allocation failure" +error_code NODATA, "no address associated with nodename" +error_code NONAME, "nodename nor servname provided, or not known" +error_code SERVICE, "servname not supported for ai_socktype" +error_code SOCKTYPE, "ai_socktype not supported" +error_code SYSTEM, "system error returned in errno" + +index 192 +prefix HEIM_NET +error_code CONN_REFUSED, "connection refused" + +end diff --git a/lib/base/heimbase-atomics.h b/lib/base/heimbase-atomics.h new file mode 100644 index 000000000000..e468a36bda9a --- /dev/null +++ b/lib/base/heimbase-atomics.h @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2010 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2010 Apple Inc. 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. + */ + +#ifndef HEIM_BASE_ATOMICS_H +#define HEIM_BASE_ATOMICS_H 1 + +#include <stdint.h> + +/* + * Atomic operations + * + * (#define HEIM_BASE_ATOMICS_FALLBACK to test fallbacks.) + */ + +#if !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(HAVE_STDATOMIC_H) + +#include <stdatomic.h> + +#define heim_base_atomic_init(t, v) atomic_init(t, v) +#define heim_base_atomic_load(x) atomic_load((x)) +#define heim_base_atomic_store(t, v) atomic_store((t), (v)) + +#define heim_base_atomic(T) _Atomic(T) + +#define heim_base_atomic_inc_32(x) (atomic_fetch_add((x), 1) + 1) +#define heim_base_atomic_dec_32(x) (atomic_fetch_sub((x), 1) - 1) +#define heim_base_atomic_inc_64(x) (atomic_fetch_add((x), 1) + 1) +#define heim_base_atomic_dec_64(x) (atomic_fetch_sub((x), 1) - 1) + +#define heim_base_exchange_pointer(t,v) atomic_exchange((t), (v)) +#define heim_base_exchange_32(t,v) atomic_exchange((t), (v)) +#define heim_base_exchange_64(t,v) atomic_exchange((t), (v)) + +/* + * <stdatomic.h>'s and AIX's CAS functions take a pointer to an expected value + * and return a boolean, setting the pointed-to variable to the old value of + * the target. + * + * Other CAS functions, like GCC's, Solaris'/Illumos', and Windows', return the + * old value and don't take a pointer to an expected value. + * + * We implement the latter semantics. + */ +static inline void * +heim_base_cas_pointer_(heim_base_atomic(void *)*t, void *e, void *d) +{ + return atomic_compare_exchange_strong(t, &e, d), e; +} + +static inline uint32_t +heim_base_cas_32_(heim_base_atomic(uint32_t)*t, uint32_t e, uint32_t d) +{ + return atomic_compare_exchange_strong(t, &e, d), e; +} + +static inline uint64_t +heim_base_cas_64_(heim_base_atomic(uint64_t)*t, uint64_t e, uint64_t d) +{ + return atomic_compare_exchange_strong(t, &e, d), e; +} + +#define heim_base_cas_pointer(t,e,d) heim_base_cas_pointer_((t), (e), (d)) +#define heim_base_cas_32(t,e,d) heim_base_cas_32_((t), (e), (d)) +#define heim_base_cas_64(t,e,d) heim_base_cas_64_((t), (e), (d)) + +#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH) + +#define heim_base_atomic_barrier() __sync_synchronize() + +#define heim_base_atomic_inc_32(x) __sync_add_and_fetch((x), 1) +#define heim_base_atomic_dec_32(x) __sync_sub_and_fetch((x), 1) +#define heim_base_atomic_inc_64(x) __sync_add_and_fetch((x), 1) +#define heim_base_atomic_dec_64(x) __sync_sub_and_fetch((x), 1) + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if __has_builtin(__sync_swap) +#define heim_base_exchange_pointer(t,v) __sync_swap((t), (v)) +#else +/* FIXME: some targets may only write the value 1 into *t */ +#define heim_base_exchange_pointer(t,v) __sync_lock_test_and_set((t), (v)) +#endif + +#define heim_base_exchange_32(t,v) heim_base_exchange_pointer((t), (v)) +#define heim_base_exchange_64(t,v) heim_base_exchange_pointer((t), (v)) + +#define heim_base_cas_pointer(t,e,d) __sync_val_compare_and_swap((t), (e), (d)) +#define heim_base_cas_32(t,e,d) __sync_val_compare_and_swap((t), (e), (d)) +#define heim_base_cas_64(t,e,d) __sync_val_compare_and_swap((t), (e), (d)) + +#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(__sun) + +#include <sys/atomic.h> +#include <mbarrier.h> + +static inline void __heim_base_atomic_barrier(void) +{ + __machine_rw_barrier(); +} + +#define heim_base_atomic_barrier() __heim_base_atomic_barrier() + +#define heim_base_atomic(T) volatile T + +#define heim_base_atomic_inc_32(x) atomic_inc_32_nv((x)) +#define heim_base_atomic_dec_32(x) atomic_dec_32_nv((x)) +#define heim_base_atomic_inc_64(x) atomic_inc_64_nv((x)) +#define heim_base_atomic_dec_64(x) atomic_dec_64_nv((x)) + +#define heim_base_exchange_pointer(t,v) atomic_swap_ptr((t), (void *)(v)) +#define heim_base_exchange_32(t,v) atomic_swap_32((t), (v)) +#define heim_base_exchange_64(t,v) atomic_swap_64((t), (v)) + +#define heim_base_cas_pointer(t,e,d) atomic_cas_ptr((t), (e), (d)) +#define heim_base_cas_32(t,e,d) atomic_cas_32((t), (e), (d)) +#define heim_base_cas_64(t,e,d) atomic_cas_64((t), (e), (d)) + +#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(_AIX) + +#include <sys/atomic_op.h> + +#define heim_base_atomic_barrier() __isync() + +#define heim_base_atomic_inc_32(x) (fetch_and_add((atomic_p)(x), 1) + 1) +#define heim_base_atomic_dec_32(x) (fetch_and_add((atomic_p)(x), -1) - 1) +#define heim_base_atomic_inc_64(x) (fetch_and_addlp((atomic_l)(x), 1) + 1) +#define heim_base_atomic_dec_64(x) (fetch_and_addlp((atomic_l)(x), -1) - 1) + +static inline void * +heim_base_exchange_pointer(void *p, void *newval) +{ + void *val = *(void **)p; + + while (!compare_and_swaplp((atomic_l)p, (long *)&val, (long)newval)) + ; + + return val; +} + +static inline uint32_t +heim_base_exchange_32(uint32_t *p, uint32_t newval) +{ + uint32_t val = *p; + + while (!compare_and_swap((atomic_p)p, (int *)&val, (int)newval)) + ; + + return val; +} + +static inline uint64_t +heim_base_exchange_64(uint64_t *p, uint64_t newval) +{ + uint64_t val = *p; + + while (!compare_and_swaplp((atomic_l)p, (long *)&val, (long)newval)) + ; + + return val; +} + +static inline void * +heim_base_cas_pointer_(heim_base_atomic(void *)*t, void *e, void *d) +{ + return compare_and_swaplp((atomic_l)t, &e, d), e; +} + +static inline uint32_t +heim_base_cas_32_(heim_base_atomic(uint32_t)*t, uint32_t e, uint32_t d) +{ + return compare_and_swap((atomic_p)t, &e, d), e; +} + +static inline uint64_t +heim_base_cas_64_(heim_base_atomic(uint64_t)*t, uint64_t e, uint64_t d) +{ + return compare_and_swaplp((atomic_l)t, &e, d), e; +} + +#define heim_base_cas_pointer(t,e,d) heim_base_cas_pointer_((t), (e), (d)) +#define heim_base_cas_32(t,e,d) heim_base_cas_32_((t), (e), (d)) +#define heim_base_cas_64(t,e,d) heim_base_cas_64_((t), (e), (d)) + +#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(_WIN32) + +#define heim_base_atomic_barrier() MemoryBarrier() + +#define heim_base_atomic_inc_32(x) InterlockedIncrement(x) +#define heim_base_atomic_dec_32(x) InterlockedDecrement(x) +#define heim_base_atomic_inc_64(x) InterlockedIncrement64(x) +#define heim_base_atomic_dec_64(x) InterlockedDecrement64(x) + +#define heim_base_exchange_pointer(t,v) InterlockedExchangePointer((PVOID volatile *)(t), (PVOID)(v)) +#define heim_base_exchange_32(t,v) ((ULONG)InterlockedExchange((LONG volatile *)(t), (LONG)(v))) +#define heim_base_exchange_64(t,v) ((ULONG64)InterlockedExchange64((ULONG64 volatile *)(t), (LONG64)(v))) + +#define heim_base_cas_pointer(t,e,d) InterlockedCompareExchangePointer((PVOID volatile *)(t), (d), (e)) +#define heim_base_cas_32(t,e,d) InterlockedCompareExchange ((LONG volatile *)(t), (d), (e)) +#define heim_base_cas_64(t,e,d) InterlockedCompareExchange64((ULONG64 volatile *)(t), (d), (e)) + +#else + +#define heim_base_atomic(T) volatile T +#define heim_base_atomic_barrier() +#define heim_base_atomic_load(x) (*(x)) +#define heim_base_atomic_init(t, v) do { (*(t) = (v)); } while (0) +#define heim_base_atomic_store(t, v) do { (*(t) = (v)); } while (0) + +#include <heim_threads.h> + +#define HEIM_BASE_NEED_ATOMIC_MUTEX 1 + +static inline uint32_t +heim_base_atomic_inc_32(heim_base_atomic(uint32_t) *x) +{ + uint32_t t; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + t = ++(*x); + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return t; +} + +static inline uint32_t +heim_base_atomic_dec_32(heim_base_atomic(uint32_t) *x) +{ + uint32_t t; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + t = --(*x); + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return t; +} + +static inline uint64_t +heim_base_atomic_inc_64(heim_base_atomic(uint64_t) *x) +{ + uint64_t t; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + t = ++(*x); + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return t; +} + +static inline uint64_t +heim_base_atomic_dec_64(heim_base_atomic(uint64_t) *x) +{ + uint64_t t; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + t = --(*x); + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return t; +} + +static inline void * +heim_base_exchange_pointer(heim_base_atomic(void *)target, void *value) +{ + void *old; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + old = *(void **)target; + *(void **)target = value; + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return old; +} + +static inline uint32_t +heim_base_exchange_32(heim_base_atomic(uint32_t) *target, uint32_t newval) +{ + uint32_t old; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + old = *target; + *target = newval; + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return old; +} + +static inline uint64_t +heim_base_exchange_64(heim_base_atomic(uint64_t) *target, uint64_t newval) +{ + uint64_t old; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + old = *target; + *target = newval; + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return old; +} + +static inline void * +heim_base_cas_pointer(heim_base_atomic(void *)target, void *expected, void *desired) +{ + void *old; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + if ((old = *(void **)target) == expected) + *(void **)target = desired; + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return old; +} + +static inline uint32_t +heim_base_cas_32(heim_base_atomic(uint32_t) *target, uint32_t expected, uint32_t desired) +{ + uint32_t old; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + if ((old = *(uint32_t *)target) == expected) + *target = desired; + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return old; +} + +static inline uint64_t +heim_base_cas_64(heim_base_atomic(uint64_t) *target, uint64_t expected,uint64_t desired) +{ + uint64_t old; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + if ((old = *(uint64_t *)target) == expected) + *target = desired; + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return old; +} + +#endif /* defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH) */ + +#ifndef heim_base_atomic +#define heim_base_atomic(T) T +#endif + +#ifndef heim_base_atomic_barrier +static inline void heim_base_atomic_barrier(void) { return; } +#endif + +#ifndef heim_base_atomic_load +#define heim_base_atomic_load(x) (heim_base_atomic_barrier(), *(x)) +#endif + +#ifndef heim_base_atomic_init +#define heim_base_atomic_init(t, v) do { (*(t) = (v)); } while (0) +#endif + +#ifndef heim_base_atomic_store +#define heim_base_atomic_store(t, v) do { \ + (*(t) = (v)); \ + heim_base_atomic_barrier(); \ + } while (0) +#endif + +#if SIZEOF_TIME_T == 8 +#define heim_base_exchange_time_t(t,v) heim_base_exchange_64((t), (v)) +#elif SIZEOF_TIME_T == 4 +#define heim_base_exchange_time_t(t,v) heim_base_exchange_32((t), (v)) +#else +#error set SIZEOF_TIME_T for your platform +#endif + +#endif /* HEIM_BASE_ATOMICS_H */ diff --git a/lib/base/heimbase-svc.h b/lib/base/heimbase-svc.h new file mode 100644 index 000000000000..0e7454c8382c --- /dev/null +++ b/lib/base/heimbase-svc.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2010 Apple Inc. 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. + */ + +#ifndef HEIMBASE_SVC_H +#define HEIMBASE_SVC_H 1 + +#include <heimbase.h> + +/* + * This file is meant to be included in services, which can + * + * #define heim_pcontext krb5_context + * + * or whatever is appropriate. + */ + +#define HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS \ + /* Input */ \ + heim_pcontext context; \ + heim_pconfig config; \ + heim_context hcontext; \ + heim_log_facility *logf; \ + const char *from; \ + struct sockaddr *addr; \ + int datagram_reply; \ + heim_octet_string request; \ + \ + /* Output */ \ + heim_octet_string *reply; \ + unsigned int use_request_t:1; \ + \ + /* Common state, to be freed in process.c */ \ + struct timeval tv_start; \ + struct timeval tv_end; \ + const char *reqtype; \ + char *cname; \ + char *sname; \ + const char *e_text; \ + char *e_text_buf; \ + heim_string_t reason; \ + /* auditing key/value store */ \ + heim_dict_t kv; \ + heim_dict_t attributes; \ + int32_t error_code + +#define HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(CONTEXT_TYPE) \ + int minor_version; \ + int (HEIM_LIB_CALL *init)(CONTEXT_TYPE, void **); \ + void (HEIM_LIB_CALL *fini)(void *) + +#endif /* HEIMBASE_SVC_H */ diff --git a/lib/base/heimbase.c b/lib/base/heimbase.c index 9a1120b68368..4e9ea1b4b5f6 100644 --- a/lib/base/heimbase.c +++ b/lib/base/heimbase.c @@ -34,13 +34,14 @@ */ #include "baselocl.h" +#include "heimbase-atomics.h" #include <syslog.h> -static heim_base_atomic_type tidglobal = HEIM_TID_USER; +static heim_base_atomic(uint32_t) tidglobal = HEIM_TID_USER; struct heim_base { - heim_type_t isa; - heim_base_atomic_type ref_cnt; + heim_const_type_t isa; + heim_base_atomic(uint32_t) ref_cnt; HEIM_TAILQ_ENTRY(heim_base) autorel; heim_auto_release_t autorelpool; uintptr_t isaextra[3]; @@ -48,21 +49,24 @@ struct heim_base { /* specialized version of base */ struct heim_base_mem { - heim_type_t isa; - heim_base_atomic_type ref_cnt; + heim_const_type_t isa; + heim_base_atomic(uint32_t) ref_cnt; HEIM_TAILQ_ENTRY(heim_base) autorel; heim_auto_release_t autorelpool; const char *name; - void (*dealloc)(void *); + void (HEIM_CALLCONV *dealloc)(void *); uintptr_t isaextra[1]; }; #define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1) #define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1)) -#ifdef HEIM_BASE_NEED_ATOMIC_MUTEX -HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER; -#endif +HEIMDAL_MUTEX * HEIM_CALLCONV +heim_base_mutex(void) +{ + static HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER; + return &_heim_base_mutex; +} /* * Auto release structure @@ -83,8 +87,8 @@ struct heim_auto_release { * @return the same object as passed in */ -void * -heim_retain(void *ptr) +heim_object_t +heim_retain(heim_object_t ptr) { struct heim_base *p; @@ -92,10 +96,11 @@ heim_retain(void *ptr) return ptr; p = PTR2BASE(ptr); - if (p->ref_cnt == heim_base_atomic_max) + + if (heim_base_atomic_load(&p->ref_cnt) == UINT32_MAX) return ptr; - if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0) + if ((heim_base_atomic_inc_32(&p->ref_cnt) - 1) == 0) heim_abort("resurection"); return ptr; } @@ -109,17 +114,18 @@ heim_retain(void *ptr) void heim_release(void *ptr) { - heim_base_atomic_type old; + heim_base_atomic(uint32_t) old; struct heim_base *p; if (ptr == NULL || heim_base_is_tagged(ptr)) return; p = PTR2BASE(ptr); - if (p->ref_cnt == heim_base_atomic_max) + + if (heim_base_atomic_load(&p->ref_cnt) == UINT32_MAX) return; - old = heim_base_atomic_dec(&p->ref_cnt) + 1; + old = heim_base_atomic_dec_32(&p->ref_cnt) + 1; if (old > 1) return; @@ -158,7 +164,7 @@ void _heim_make_permanent(heim_object_t ptr) { struct heim_base *p = PTR2BASE(ptr); - p->ref_cnt = heim_base_atomic_max; + heim_base_atomic_store(&p->ref_cnt, UINT32_MAX); } @@ -176,7 +182,7 @@ static heim_type_t tagged_isa[9] = { NULL }; -heim_type_t +heim_const_type_t _heim_get_isa(heim_object_t ptr) { struct heim_base *p; @@ -200,7 +206,7 @@ _heim_get_isa(heim_object_t ptr) heim_tid_t heim_get_tid(heim_object_t ptr) { - heim_type_t isa = _heim_get_isa(ptr); + heim_const_type_t isa = _heim_get_isa(ptr); return isa->tid; } @@ -212,13 +218,13 @@ heim_get_tid(heim_object_t ptr) * @return a hash value */ -unsigned long +uintptr_t heim_get_hash(heim_object_t ptr) { - heim_type_t isa = _heim_get_isa(ptr); + heim_const_type_t isa = _heim_get_isa(ptr); if (isa->hash) return isa->hash(ptr); - return (unsigned long)ptr; + return (uintptr_t)ptr; } /** @@ -235,7 +241,7 @@ int heim_cmp(heim_object_t a, heim_object_t b) { heim_tid_t ta, tb; - heim_type_t isa; + heim_const_type_t isa; ta = heim_get_tid(a); tb = heim_get_tid(b); @@ -255,7 +261,7 @@ heim_cmp(heim_object_t a, heim_object_t b) * Private - allocates an memory object */ -static void +static void HEIM_CALLCONV memory_dealloc(void *ptr) { if (ptr) { @@ -266,7 +272,7 @@ memory_dealloc(void *ptr) } } -struct heim_type_data memory_object = { +static const struct heim_type_data memory_object = { HEIM_TID_MEMORY, "memory-object", NULL, @@ -319,7 +325,7 @@ _heim_create_type(const char *name, if (type == NULL) return NULL; - type->tid = heim_base_atomic_inc(&tidglobal); + type->tid = heim_base_atomic_inc_32(&tidglobal); type->name = name; type->init = init; type->dealloc = dealloc; @@ -332,7 +338,7 @@ _heim_create_type(const char *name, } heim_object_t -_heim_alloc_object(heim_type_t type, size_t size) +_heim_alloc_object(heim_const_type_t type, size_t size) { /* XXX should use posix_memalign */ struct heim_base *p = calloc(1, size + sizeof(*p)); @@ -347,9 +353,10 @@ _heim_alloc_object(heim_type_t type, size_t size) void * _heim_get_isaextra(heim_object_t ptr, size_t idx) { - struct heim_base *p = (struct heim_base *)PTR2BASE(ptr); + struct heim_base *p; heim_assert(ptr != NULL, "internal error"); + p = (struct heim_base *)PTR2BASE(ptr); if (p->isa == &memory_object) return NULL; heim_assert(idx < 3, "invalid private heim_base extra data index"); @@ -500,6 +507,8 @@ heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *)) void heim_abort(const char *fmt, ...) + HEIMDAL_NORETURN_ATTRIBUTE + HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 2)) { va_list ap; va_start(ap, fmt); @@ -513,6 +522,8 @@ heim_abort(const char *fmt, ...) void heim_abortv(const char *fmt, va_list ap) + HEIMDAL_NORETURN_ATTRIBUTE + HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 0)) { static char str[1024]; @@ -585,7 +596,7 @@ autorel_tls(void) } -static void +static void HEIM_CALLCONV autorel_dealloc(void *ptr) { heim_auto_release_t ar = ptr; @@ -614,10 +625,10 @@ autorel_cmp(void *a, void *b) return (a == b); } -static unsigned long +static uintptr_t autorel_hash(void *ptr) { - return (unsigned long)ptr; + return (uintptr_t)ptr; } @@ -764,9 +775,10 @@ heim_path_vget2(heim_object_t ptr, heim_object_t *parent, heim_object_t *key, next_node = heim_dict_get_value(node, path_element); } else if (node_type == HEIM_TID_DB) { next_node = _heim_db_get_value(node, NULL, path_element, NULL); - } else if (node_type == HEIM_TID_ARRAY) { + } else { int idx = -1; + /* node_type == HEIM_TID_ARRAY */ if (heim_get_tid(path_element) == HEIM_TID_NUMBER) idx = heim_number_get_int(path_element); if (idx < 0) { @@ -778,12 +790,6 @@ heim_path_vget2(heim_object_t ptr, heim_object_t *parent, heim_object_t *key, return NULL; } next_node = heim_array_get_value(node, idx); - } else { - if (error) - *error = heim_error_create(EINVAL, - "heim_path_get() node in path " - "not a container type"); - return NULL; } node = next_node; } @@ -919,14 +925,14 @@ heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf, heim_abort("heim_path_vcreate() does not create root nodes"); while (path_element != NULL) { + int idx = -1; + next_path_element = va_arg(ap, heim_object_t); node_type = heim_get_tid(node); if (node_type == HEIM_TID_DICT) { next_node = heim_dict_get_value(node, path_element); } else if (node_type == HEIM_TID_ARRAY) { - int idx = -1; - if (heim_get_tid(path_element) == HEIM_TID_NUMBER) idx = heim_number_get_int(path_element); if (idx < 0) { @@ -937,10 +943,16 @@ heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf, "and positive"); return EINVAL; } - if (idx < heim_array_get_length(node)) + if (idx < heim_array_get_length(node)) { next_node = heim_array_get_value(node, idx); - else + } else if (idx == heim_array_get_length(node)) { next_node = NULL; + } else { + if (error) + *error = heim_error_create(EINVAL, + "Index for array in path is too large"); + return EINVAL; + } } else if (node_type == HEIM_TID_DB && next_path_element != NULL) { if (error) *error = heim_error_create(EINVAL, "Interior node is a DB"); @@ -952,26 +964,31 @@ heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf, /* Create missing interior node */ if (next_node == NULL) { - next_node = heim_dict_create(size); /* no arrays or DBs, just dicts */ - if (next_node == NULL) { + heim_dict_t new_node; + + new_node = heim_dict_create(size); /* no arrays or DBs, just dicts */ + if (new_node == NULL) { ret = ENOMEM; goto err; } if (node_type == HEIM_TID_DICT) { - ret = heim_dict_set_value(node, path_element, next_node); + ret = heim_dict_set_value(node, path_element, new_node); + next_node = heim_dict_get_value(node, path_element); } else if (node_type == HEIM_TID_ARRAY && heim_number_get_int(path_element) <= heim_array_get_length(node)) { ret = heim_array_insert_value(node, heim_number_get_int(path_element), - next_node); + new_node); + next_node = heim_array_get_value(node, idx); } else { ret = EINVAL; if (error) *error = heim_error_create(ret, "Node in path not a " "container"); } - heim_release(next_node); + + heim_release(new_node); if (ret) goto err; } diff --git a/lib/base/heimbase.h b/lib/base/heimbase.h index 157cbd4c1105..4546df94699c 100644 --- a/lib/base/heimbase.h +++ b/lib/base/heimbase.h @@ -37,11 +37,17 @@ #define HEIM_BASE_H 1 #include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#endif #if !defined(WIN32) && !defined(HAVE_DISPATCH_DISPATCH_H) && defined(ENABLE_PTHREAD_SUPPORT) #include <pthread.h> #endif #include <krb5-types.h> #include <stdarg.h> +#ifdef _WIN32 +#include <winsock2.h> +#endif #ifdef HAVE_STDBOOL_H #include <stdbool.h> #else @@ -53,8 +59,101 @@ #endif #endif +#include <stdint.h> + +#include <heim_err.h> + +#ifdef _WIN32 +#define HEIM_CALLCONV __stdcall +#define HEIM_LIB_CALL __stdcall +#else +#define HEIM_CALLCONV +#define HEIM_LIB_CALL +#endif + +#if !defined(__GNUC__) && !defined(__attribute__) +#define __attribute__(x) +#endif + #define HEIM_BASE_API_VERSION 20130210 +/* + * Generic facilities (moved from lib/krb5/. + */ + +typedef int32_t heim_error_code; +typedef struct heim_context_s *heim_context; +typedef struct heim_pcontext_s *heim_pcontext; + +typedef void (HEIM_CALLCONV *heim_log_log_func_t)(heim_context, + const char *, + const char *, + void *); +typedef void (HEIM_CALLCONV *heim_log_close_func_t)(void *); + +typedef struct heim_log_facility_s heim_log_facility; + +typedef uintptr_t +(HEIM_LIB_CALL *heim_get_instance_func_t)(const char *); + +#define HEIM_PLUGIN_INVOKE_ALL 1 + +struct heim_plugin_data { + const char *module; + const char *name; + int min_version; + const char *const *deps; + heim_get_instance_func_t get_instance; +}; + +/* + * heim_config_binding is identical to struct krb5_config_binding + * within krb5.h. Its format is public and used by callers of + * krb5_config_get_list() and krb5_config_vget_list(). + */ +enum heim_config_type { + heim_config_string, + heim_config_list, +}; +struct heim_config_binding { + enum heim_config_type type; + char *name; + struct heim_config_binding *next; + union { + char *string; + struct heim_config_binding *list; + void *generic; + } u; +}; +typedef struct heim_config_binding heim_config_binding; +typedef struct heim_config_binding heim_config_section; + +/* + * CF-like, JSON APIs + */ + +typedef enum heim_tid_enum { + HEIM_TID_NUMBER = 0, + HEIM_TID_NULL = 1, + HEIM_TID_BOOL = 2, + HEIM_TID_TAGGED_UNUSED2 = 3, /* reserved for tagged object types */ + HEIM_TID_TAGGED_UNUSED3 = 4, /* reserved for tagged object types */ + HEIM_TID_TAGGED_UNUSED4 = 5, /* reserved for tagged object types */ + HEIM_TID_TAGGED_UNUSED5 = 6, /* reserved for tagged object types */ + HEIM_TID_TAGGED_UNUSED6 = 7, /* reserved for tagged object types */ + HEIM_TID_MEMORY = 128, + HEIM_TID_ARRAY = 129, + HEIM_TID_DICT = 130, + HEIM_TID_STRING = 131, + HEIM_TID_AUTORELEASE = 132, + HEIM_TID_ERROR = 133, + HEIM_TID_DATA = 134, + HEIM_TID_DB = 135, + HEIM_TID_PA_AUTH_MECH = 136, + HEIM_TID_PAC = 137, + HEIM_TID_USER = 255 +} heim_tid; + typedef void * heim_object_t; typedef unsigned int heim_tid_t; typedef heim_object_t heim_bool_t; @@ -89,37 +188,7 @@ typedef long heim_base_once_t; /* XXX arch dependant */ #endif -void * heim_retain(heim_object_t); -void heim_release(heim_object_t); - -void heim_show(heim_object_t); - -typedef void (*heim_type_dealloc)(void *); - -void * -heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc); - -heim_tid_t -heim_get_tid(heim_object_t object); - -int -heim_cmp(heim_object_t a, heim_object_t b); - -unsigned long -heim_get_hash(heim_object_t ptr); - -void -heim_base_once_f(heim_base_once_t *, void *, void (*)(void *)); - -void -heim_abort(const char *fmt, ...) - HEIMDAL_NORETURN_ATTRIBUTE - HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 2)); - -void -heim_abortv(const char *fmt, va_list ap) - HEIMDAL_NORETURN_ATTRIBUTE - HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 0)); +typedef void (HEIM_CALLCONV *heim_type_dealloc)(void *); #define heim_assert(e,t) \ (heim_builtin_expect(!(e), 0) ? heim_abort(t ":" #e) : (void)0) @@ -128,70 +197,23 @@ heim_abortv(const char *fmt, va_list ap) * */ -heim_null_t -heim_null_create(void); - -heim_bool_t -heim_bool_create(int); - -int -heim_bool_val(heim_bool_t); - /* * Array */ typedef struct heim_array_data *heim_array_t; -heim_array_t heim_array_create(void); -heim_tid_t heim_array_get_type_id(void); - typedef void (*heim_array_iterator_f_t)(heim_object_t, void *, int *); typedef int (*heim_array_filter_f_t)(heim_object_t, void *); -int heim_array_append_value(heim_array_t, heim_object_t); -int heim_array_insert_value(heim_array_t, size_t idx, heim_object_t); -void heim_array_iterate_f(heim_array_t, void *, heim_array_iterator_f_t); -void heim_array_iterate_reverse_f(heim_array_t, void *, heim_array_iterator_f_t); -#ifdef __BLOCKS__ -void heim_array_iterate(heim_array_t, void (^)(heim_object_t, int *)); -void heim_array_iterate_reverse(heim_array_t, void (^)(heim_object_t, int *)); -#endif -size_t heim_array_get_length(heim_array_t); -heim_object_t - heim_array_get_value(heim_array_t, size_t); -heim_object_t - heim_array_copy_value(heim_array_t, size_t); -void heim_array_set_value(heim_array_t, size_t, heim_object_t); -void heim_array_delete_value(heim_array_t, size_t); -void heim_array_filter_f(heim_array_t, void *, heim_array_filter_f_t); -#ifdef __BLOCKS__ -void heim_array_filter(heim_array_t, int (^)(heim_object_t)); -#endif - /* * Dict */ typedef struct heim_dict_data *heim_dict_t; -heim_dict_t heim_dict_create(size_t size); -heim_tid_t heim_dict_get_type_id(void); - typedef void (*heim_dict_iterator_f_t)(heim_object_t, heim_object_t, void *); -int heim_dict_set_value(heim_dict_t, heim_object_t, heim_object_t); -void heim_dict_iterate_f(heim_dict_t, void *, heim_dict_iterator_f_t); -#ifdef __BLOCKS__ -void heim_dict_iterate(heim_dict_t, void (^)(heim_object_t, heim_object_t)); -#endif - -heim_object_t - heim_dict_get_value(heim_dict_t, heim_object_t); -heim_object_t - heim_dict_copy_value(heim_dict_t, heim_object_t); -void heim_dict_delete_key(heim_dict_t, heim_object_t); - /* * String */ @@ -199,15 +221,6 @@ void heim_dict_delete_key(heim_dict_t, heim_object_t); typedef struct heim_string_data *heim_string_t; typedef void (*heim_string_free_f_t)(void *); -heim_string_t heim_string_create(const char *); -heim_string_t heim_string_ref_create(const char *, heim_string_free_f_t); -heim_string_t heim_string_create_with_bytes(const void *, size_t); -heim_string_t heim_string_ref_create_with_bytes(const void *, size_t, - heim_string_free_f_t); -heim_string_t heim_string_create_with_format(const char *, ...); -heim_tid_t heim_string_get_type_id(void); -const char * heim_string_get_utf8(heim_string_t); - #define HSTR(_str) (__heim_string_constant("" _str "")) heim_string_t __heim_string_constant(const char *); @@ -217,41 +230,10 @@ heim_string_t __heim_string_constant(const char *); typedef struct heim_error * heim_error_t; -heim_error_t heim_error_create_enomem(void); - -heim_error_t heim_error_create(int, const char *, ...) - HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 3)); - -void heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...) - HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 3, 4)); - -heim_error_t heim_error_createv(int, const char *, va_list) - HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 0)); - -heim_string_t heim_error_copy_string(heim_error_t); -int heim_error_get_code(heim_error_t); - -heim_error_t heim_error_append(heim_error_t, heim_error_t); - /* * Path */ -heim_object_t heim_path_get(heim_object_t ptr, heim_error_t *error, ...); -heim_object_t heim_path_copy(heim_object_t ptr, heim_error_t *error, ...); -heim_object_t heim_path_vget(heim_object_t ptr, heim_error_t *error, - va_list ap); -heim_object_t heim_path_vcopy(heim_object_t ptr, heim_error_t *error, - va_list ap); - -int heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf, - heim_error_t *error, va_list ap); -int heim_path_create(heim_object_t ptr, size_t size, heim_object_t leaf, - heim_error_t *error, ...); - -void heim_path_vdelete(heim_object_t ptr, heim_error_t *error, va_list ap); -void heim_path_delete(heim_object_t ptr, heim_error_t *error, ...); - /* * Data (octet strings) */ @@ -268,14 +250,6 @@ typedef struct heim_base_data heim_octet_string; typedef struct heim_base_data * heim_data_t; typedef void (*heim_data_free_f_t)(void *); -heim_data_t heim_data_create(const void *, size_t); -heim_data_t heim_data_ref_create(const void *, size_t, heim_data_free_f_t); -heim_tid_t heim_data_get_type_id(void); -const heim_octet_string * - heim_data_get_data(heim_data_t); -const void * heim_data_get_ptr(heim_data_t); -size_t heim_data_get_length(heim_data_t); - /* * DB */ @@ -325,52 +299,18 @@ extern struct heim_db_type heim_sorted_text_file_dbtype; #define HEIM_DB_TYPE_VERSION_01 1 -int heim_db_register(const char *dbtype, - void *data, - struct heim_db_type *plugin); - -heim_db_t heim_db_create(const char *dbtype, const char *dbname, - heim_dict_t options, heim_error_t *error); -heim_db_t heim_db_clone(heim_db_t, heim_error_t *); -int heim_db_begin(heim_db_t, int, heim_error_t *); -int heim_db_commit(heim_db_t, heim_error_t *); -int heim_db_rollback(heim_db_t, heim_error_t *); -heim_tid_t heim_db_get_type_id(void); - -int heim_db_set_value(heim_db_t, heim_string_t, heim_data_t, heim_data_t, - heim_error_t *); -heim_data_t heim_db_copy_value(heim_db_t, heim_string_t, heim_data_t, - heim_error_t *); -int heim_db_delete_key(heim_db_t, heim_string_t, heim_data_t, - heim_error_t *); -void heim_db_iterate_f(heim_db_t, heim_string_t, void *, - heim_db_iterator_f_t, heim_error_t *); -#ifdef __BLOCKS__ -void heim_db_iterate(heim_db_t, heim_string_t, - void (^)(heim_data_t, heim_data_t), heim_error_t *); -#endif - - /* * Number */ typedef struct heim_number_data *heim_number_t; -heim_number_t heim_number_create(int); -heim_tid_t heim_number_get_type_id(void); -int heim_number_get_int(heim_number_t); - /* - * + * Autorelease */ typedef struct heim_auto_release * heim_auto_release_t; -heim_auto_release_t heim_auto_release_create(void); -void heim_auto_release_drain(heim_auto_release_t); -heim_object_t heim_auto_release(heim_object_t); - /* * JSON */ @@ -383,25 +323,19 @@ typedef enum heim_json_flags { HEIM_JSON_F_STRICT = 31, HEIM_JSON_F_CNULL2JSNULL = 32, HEIM_JSON_F_TRY_DECODE_DATA = 64, - HEIM_JSON_F_ONE_LINE = 128 + HEIM_JSON_F_ONE_LINE = 128, + HEIM_JSON_F_ESCAPE_NON_ASCII = 256, + HEIM_JSON_F_NO_ESCAPE_NON_ASCII = 512, + /* The default is to indent with one tab */ + HEIM_JSON_F_INDENT2 = 1024, + HEIM_JSON_F_INDENT4 = 2048, + HEIM_JSON_F_INDENT8 = 4096, } heim_json_flags_t; -heim_object_t heim_json_create(const char *, size_t, heim_json_flags_t, - heim_error_t *); -heim_object_t heim_json_create_with_bytes(const void *, size_t, size_t, - heim_json_flags_t, - heim_error_t *); -heim_string_t heim_json_copy_serialize(heim_object_t, heim_json_flags_t, - heim_error_t *); - - /* * Debug */ -heim_string_t -heim_description(heim_object_t ptr); - /* * Binary search. * @@ -422,10 +356,19 @@ void _bsearch_file_close(bsearch_file_handle *bfh); * Thread-specific keys */ -int heim_w32_key_create(unsigned long *, void (*)(void *)); -int heim_w32_delete_key(unsigned long); -int heim_w32_setspecific(unsigned long, void *); -void *heim_w32_getspecific(unsigned long); -void heim_w32_service_thread_detach(void *); +#include <heim_threads.h> +#include <com_err.h> + +/* + * Service logging facility (moved from kdc/). + */ + +#define HEIM_SVC_AUDIT_EATWHITE 0x1 +#define HEIM_SVC_AUDIT_VIS 0x2 +#define HEIM_SVC_AUDIT_VISLAST 0x4 + +typedef struct heim_svc_req_desc_common_s *heim_svc_req_desc; + +#include <heimbase-protos.h> #endif /* HEIM_BASE_H */ diff --git a/lib/base/heimbasepriv.h b/lib/base/heimbasepriv.h index eb155006fe0d..a431da478ad4 100644 --- a/lib/base/heimbasepriv.h +++ b/lib/base/heimbasepriv.h @@ -42,31 +42,11 @@ typedef void (*heim_type_init)(void *); typedef heim_object_t (*heim_type_copy)(void *); typedef int (*heim_type_cmp)(void *, void *); -typedef unsigned long (*heim_type_hash)(void *); +typedef uintptr_t (*heim_type_hash)(void *); typedef heim_string_t (*heim_type_description)(void *); typedef struct heim_type_data *heim_type_t; - -enum { - HEIM_TID_NUMBER = 0, - HEIM_TID_NULL = 1, - HEIM_TID_BOOL = 2, - HEIM_TID_TAGGED_UNUSED2 = 3, /* reserved for tagged object types */ - HEIM_TID_TAGGED_UNUSED3 = 4, /* reserved for tagged object types */ - HEIM_TID_TAGGED_UNUSED4 = 5, /* reserved for tagged object types */ - HEIM_TID_TAGGED_UNUSED5 = 6, /* reserved for tagged object types */ - HEIM_TID_TAGGED_UNUSED6 = 7, /* reserved for tagged object types */ - HEIM_TID_MEMORY = 128, - HEIM_TID_ARRAY = 129, - HEIM_TID_DICT = 130, - HEIM_TID_STRING = 131, - HEIM_TID_AUTORELEASE = 132, - HEIM_TID_ERROR = 133, - HEIM_TID_DATA = 134, - HEIM_TID_DB = 135, - HEIM_TID_USER = 255 - -}; +typedef const struct heim_type_data *heim_const_type_t; struct heim_type_data { heim_tid_t tid; @@ -79,7 +59,7 @@ struct heim_type_data { heim_type_description desc; }; -heim_type_t _heim_get_isa(heim_object_t); +heim_const_type_t _heim_get_isa(heim_object_t); heim_type_t _heim_create_type(const char *name, @@ -91,7 +71,7 @@ _heim_create_type(const char *name, heim_type_description desc); heim_object_t -_heim_alloc_object(heim_type_t type, size_t size); +_heim_alloc_object(heim_const_type_t type, size_t size); void * _heim_get_isaextra(heim_object_t o, size_t idx); diff --git a/lib/base/heimqueue.h b/lib/base/heimqueue.h deleted file mode 100644 index 423a68478792..000000000000 --- a/lib/base/heimqueue.h +++ /dev/null @@ -1,167 +0,0 @@ -/* $NetBSD: queue.h,v 1.38 2004/04/18 14:12:05 lukem Exp $ */ -/* $Id$ */ - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef _HEIM_QUEUE_H_ -#define _HEIM_QUEUE_H_ - -/* - * Tail queue definitions. - */ -#define HEIM_TAILQ_HEAD(name, type) \ -struct name { \ - struct type *tqh_first; /* first element */ \ - struct type **tqh_last; /* addr of last next element */ \ -} - -#define HEIM_TAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } -#define HEIM_TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; /* next element */ \ - struct type **tqe_prev; /* address of previous next element */ \ -} - -/* - * Tail queue functions. - */ -#if defined(_KERNEL) && defined(QUEUEDEBUG) -#define QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD(head, elm, field) \ - if ((head)->tqh_first && \ - (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ - panic("HEIM_TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); -#define QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL(head, elm, field) \ - if (*(head)->tqh_last != NULL) \ - panic("HEIM_TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__); -#define QUEUEDEBUG_HEIM_TAILQ_OP(elm, field) \ - if ((elm)->field.tqe_next && \ - (elm)->field.tqe_next->field.tqe_prev != \ - &(elm)->field.tqe_next) \ - panic("HEIM_TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ - if (*(elm)->field.tqe_prev != (elm)) \ - panic("HEIM_TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__); -#define QUEUEDEBUG_HEIM_TAILQ_PREREMOVE(head, elm, field) \ - if ((elm)->field.tqe_next == NULL && \ - (head)->tqh_last != &(elm)->field.tqe_next) \ - panic("HEIM_TAILQ_PREREMOVE head %p elm %p %s:%d", \ - (head), (elm), __FILE__, __LINE__); -#define QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE(elm, field) \ - (elm)->field.tqe_next = (void *)1L; \ - (elm)->field.tqe_prev = (void *)1L; -#else -#define QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD(head, elm, field) -#define QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL(head, elm, field) -#define QUEUEDEBUG_HEIM_TAILQ_OP(elm, field) -#define QUEUEDEBUG_HEIM_TAILQ_PREREMOVE(head, elm, field) -#define QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE(elm, field) -#endif - -#define HEIM_TAILQ_INIT(head) do { \ - (head)->tqh_first = NULL; \ - (head)->tqh_last = &(head)->tqh_first; \ -} while (/*CONSTCOND*/0) - -#define HEIM_TAILQ_INSERT_HEAD(head, elm, field) do { \ - QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD((head), (elm), field) \ - if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ - (head)->tqh_first->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (head)->tqh_first = (elm); \ - (elm)->field.tqe_prev = &(head)->tqh_first; \ -} while (/*CONSTCOND*/0) - -#define HEIM_TAILQ_INSERT_TAIL(head, elm, field) do { \ - QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL((head), (elm), field) \ - (elm)->field.tqe_next = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &(elm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define HEIM_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - QUEUEDEBUG_HEIM_TAILQ_OP((listelm), field) \ - if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ - (elm)->field.tqe_next->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (listelm)->field.tqe_next = (elm); \ - (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define HEIM_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - QUEUEDEBUG_HEIM_TAILQ_OP((listelm), field) \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define HEIM_TAILQ_REMOVE(head, elm, field) do { \ - QUEUEDEBUG_HEIM_TAILQ_PREREMOVE((head), (elm), field) \ - QUEUEDEBUG_HEIM_TAILQ_OP((elm), field) \ - if (((elm)->field.tqe_next) != NULL) \ - (elm)->field.tqe_next->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ - QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE((elm), field); \ -} while (/*CONSTCOND*/0) - -#define HEIM_TAILQ_FOREACH(var, head, field) \ - for ((var) = ((head)->tqh_first); \ - (var); \ - (var) = ((var)->field.tqe_next)) - -#define HEIM_TAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ - (var); \ - (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) - -/* - * Tail queue access methods. - */ -#define HEIM_TAILQ_EMPTY(head) ((head)->tqh_first == NULL) -#define HEIM_TAILQ_FIRST(head) ((head)->tqh_first) -#define HEIM_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) - -#define HEIM_TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) -#define HEIM_TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) - - -#endif /* !_HEIM_QUEUE_H_ */ diff --git a/lib/base/json.c b/lib/base/json.c index 2ef371b975ea..4fa0f2d5aff0 100644 --- a/lib/base/json.c +++ b/lib/base/json.c @@ -37,10 +37,12 @@ #include <ctype.h> #include <base64.h> +#ifndef WIN32 +#include <langinfo.h> +#endif + static heim_base_once_t heim_json_once = HEIM_BASE_ONCE_INIT; static heim_string_t heim_tid_data_uuid_key = NULL; -static const char base64_chars[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static void json_init_once(void *arg) @@ -66,7 +68,7 @@ struct heim_strbuf { }; static int -base2json(heim_object_t, struct twojson *); +base2json(heim_object_t, struct twojson *, int); static void indent(struct twojson *j) @@ -74,8 +76,18 @@ indent(struct twojson *j) size_t i = j->indent; if (j->flags & HEIM_JSON_F_ONE_LINE) return; - while (i--) - j->out(j->ctx, "\t"); + if (j->flags & HEIM_JSON_F_INDENT2) + while (i--) + j->out(j->ctx, " "); + else if (j->flags & HEIM_JSON_F_INDENT4) + while (i--) + j->out(j->ctx, " "); + else if (j->flags & HEIM_JSON_F_INDENT8) + while (i--) + j->out(j->ctx, " "); + else + while (i--) + j->out(j->ctx, "\t"); } static void @@ -90,7 +102,7 @@ array2json(heim_object_t value, void *ctx, int *stop) j->out(j->ctx, NULL); /* eat previous '\n' if possible */ j->out(j->ctx, ",\n"); } - j->ret = base2json(value, j); + j->ret = base2json(value, j, 0); } static void @@ -105,19 +117,77 @@ dict2json(heim_object_t key, heim_object_t value, void *ctx) j->out(j->ctx, NULL); /* eat previous '\n' if possible */ j->out(j->ctx, ",\n"); } - j->ret = base2json(key, j); - if (j->ret) - return; - j->out(j->ctx, " : \n"); - j->indent++; - j->ret = base2json(value, j); + j->ret = base2json(key, j, 0); if (j->ret) return; - j->indent--; + switch (heim_get_tid(value)) { + case HEIM_TID_ARRAY: + case HEIM_TID_DICT: + case HEIM_TID_DATA: + j->out(j->ctx, ":\n"); + j->indent++; + j->ret = base2json(value, j, 0); + if (j->ret) + return; + j->indent--; + break; + default: + j->out(j->ctx, ": "); + j->ret = base2json(value, j, 1); + break; + } +} + +#ifndef WIN32 +static void +init_is_utf8(void *ptr) +{ + *(int *)ptr = strcasecmp("utf-8", nl_langinfo(CODESET)) == 0; +} +#endif + +int +heim_locale_is_utf8(void) +{ +#ifdef WIN32 + return 0; /* XXX Implement */ +#else + static int locale_is_utf8 = -1; + static heim_base_once_t once = HEIM_BASE_ONCE_INIT; + + heim_base_once_f(&once, &locale_is_utf8, init_is_utf8); + return locale_is_utf8; +#endif +} + +static void +out_escaped_bmp(struct twojson *j, const unsigned char *p, int nbytes) +{ + unsigned char e[sizeof("\\u0000")]; + unsigned codepoint; + + if (nbytes == 2) + codepoint = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f); + else if (nbytes == 3) + codepoint = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); + else + abort(); + e[0] = '\\'; + e[1] = 'u'; + e[2] = codepoint >> 12; + e[2] += (e[2] < 10) ? '0' : ('A' - 10); + e[3] = (codepoint >> 8) & 0x0f; + e[3] += (e[3] < 10) ? '0' : ('A' - 10); + e[4] = (codepoint >> 4) & 0x0f; + e[4] += (e[4] < 10) ? '0' : ('A' - 10); + e[5] = codepoint & 0x0f; + e[5] += (e[5] < 10) ? '0' : ('A' - 10); + e[6] = '\0'; + j->out(j->ctx, (char *)e); } static int -base2json(heim_object_t obj, struct twojson *j) +base2json(heim_object_t obj, struct twojson *j, int skip_indent) { heim_tid_t type; int first = 0; @@ -166,12 +236,186 @@ base2json(heim_object_t obj, struct twojson *j) j->first = first; break; - case HEIM_TID_STRING: - indent(j); + case HEIM_TID_STRING: { + const unsigned char *s = (const unsigned char *)heim_string_get_utf8(obj); + const unsigned char *p; + unsigned int c, cp, ctop, cbot; + char e[sizeof("\\u0123\\u3210")]; + int good; + size_t i; + + if (!skip_indent) + indent(j); j->out(j->ctx, "\""); - j->out(j->ctx, heim_string_get_utf8(obj)); + for (p = s; (c = *p); p++) { + switch (c) { + /* ASCII control characters w/ C-like escapes */ + case '\b': j->out(j->ctx, "\\b"); continue; + case '\f': j->out(j->ctx, "\\f"); continue; + case '\n': j->out(j->ctx, "\\n"); continue; + case '\r': j->out(j->ctx, "\\r"); continue; + case '\t': j->out(j->ctx, "\\t"); continue; + /* Other must-escape non-control ASCII characters */ + case '"': j->out(j->ctx, "\\\""); continue; + case '\\': j->out(j->ctx, "\\\\"); continue; + default: break; + } + + /* + * JSON string encoding is... complex. + * + * Invalid UTF-8 w/ HEIM_JSON_F_STRICT_STRINGS set -> return 1 + * + * Invalid UTF-8 w/o HEIM_JSON_F_STRICT_STRINGS set -> pass + * through, a sort of Heimdal WTF-8, but not _the_ WTF-8. + */ + if (c < 0x20) { + /* ASCII control character w/o C-like escape */ + e[0] = '\\'; + e[1] = 'u'; + e[2] = '0'; + e[3] = '0'; + e[4] = "0123456789ABCDEF"[c>>4]; + e[5] = "0123456789ABCDEF"[c & 0x0f]; + e[6] = '\0'; + j->out(j->ctx, e); + continue; + } + if (c < 0x80) { + /* ASCII */ + e[0] = c; + e[1] = '\0'; + j->out(j->ctx, e); + continue; + } + if ((c & 0xc0) == 0x80) { + /* UTF-8 bare non-leading byte */ + if (!(j->flags & HEIM_JSON_F_STRICT_STRINGS)) { + e[0] = c; + e[1] = '\0'; + j->out(j->ctx, e); + continue; + } + return 1; + } + if ((c & 0xe0) == 0xc0) { + /* UTF-8 leading byte of two-byte sequence */ + good = 1; + for (i = 1; i < 2 && good && p[i]; i++) { + if ((p[i] & 0xc0) != 0x80) + good = 0; + } + if (i != 2) + good = 0; + if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) { + e[0] = c; + e[1] = '\0'; + j->out(j->ctx, e); + continue; + } else if (!good) { + return 1; + } + if (j->flags & HEIM_JSON_F_ESCAPE_NON_ASCII) { + out_escaped_bmp(j, p, 2); + p += 1; + continue; + } + e[0] = c; + e[1] = p[1]; + e[2] = '\0'; + j->out(j->ctx, e); + p += 1; + continue; + } + if ((c & 0xf0) == 0xe0) { + /* UTF-8 leading byte of three-byte sequence */ + good = 1; + for (i = 1; i < 3 && good && p[i]; i++) { + if ((p[i] & 0xc0) != 0x80) + good = 0; + } + if (i != 3) + good = 0; + if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) { + e[0] = c; + e[1] = '\0'; + j->out(j->ctx, e); + continue; + } else if (!good) { + return 1; + } + if (j->flags & HEIM_JSON_F_ESCAPE_NON_ASCII) { + out_escaped_bmp(j, p, 3); + p += 2; + continue; + } + e[0] = c; + e[1] = p[1]; + e[2] = p[2]; + e[3] = '\0'; + j->out(j->ctx, e); + p += 2; + continue; + } + + if (c > 0xf7) { + /* Invalid UTF-8 leading byte */ + if (!(j->flags & HEIM_JSON_F_STRICT_STRINGS)) { + e[0] = c; + e[1] = '\0'; + j->out(j->ctx, e); + continue; + } + return 1; + } + + /* + * A codepoint > U+FFFF, needs encoding a la UTF-16 surrogate + * pair because JSON takes after JS which uses UTF-16. Ugly. + */ + cp = c & 0x7; + good = 1; + for (i = 1; i < 4 && good && p[i]; i++) { + if ((p[i] & 0xc0) == 0x80) + cp = (cp << 6) | (p[i] & 0x3f); + else + good = 0; + } + if (i != 4) + good = 0; + if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) { + e[0] = c; + e[1] = '\0'; + j->out(j->ctx, e); + continue; + } else if (!good) { + return 1; + } + p += 3; + + cp -= 0x10000; + ctop = 0xD800 + (cp >> 10); + cbot = 0xDC00 + (cp & 0x3ff); + + e[0 ] = '\\'; + e[1 ] = 'u'; + e[2 ] = "0123456789ABCDEF"[(ctop ) >> 12]; + e[3 ] = "0123456789ABCDEF"[(ctop & 0x0f00) >> 8]; + e[4 ] = "0123456789ABCDEF"[(ctop & 0x00f0) >> 4]; + e[5 ] = "0123456789ABCDEF"[(ctop & 0x000f) ]; + e[6 ] = '\\'; + e[7 ] = 'u'; + e[8 ] = "0123456789ABCDEF"[(cbot ) >> 12]; + e[9 ] = "0123456789ABCDEF"[(cbot & 0x0f00) >> 8]; + e[10] = "0123456789ABCDEF"[(cbot & 0x00f0) >> 4]; + e[11] = "0123456789ABCDEF"[(cbot & 0x000f) ]; + e[12] = '\0'; + j->out(j->ctx, e); + continue; + } j->out(j->ctx, "\""); break; + } case HEIM_TID_DATA: { heim_dict_t d; @@ -220,7 +464,7 @@ base2json(heim_object_t obj, struct twojson *j) heim_release(d); return ENOMEM; } - ret = base2json(d, j); + ret = base2json(d, j, 0); heim_release(d); if (ret) return ret; @@ -230,17 +474,20 @@ base2json(heim_object_t obj, struct twojson *j) case HEIM_TID_NUMBER: { char num[32]; - indent(j); + if (!skip_indent) + indent(j); snprintf(num, sizeof (num), "%d", heim_number_get_int(obj)); j->out(j->ctx, num); break; } case HEIM_TID_NULL: - indent(j); + if (!skip_indent) + indent(j); j->out(j->ctx, "null"); break; case HEIM_TID_BOOL: - indent(j); + if (!skip_indent) + indent(j); j->out(j->ctx, heim_bool_val(obj) ? "true" : "false"); break; default: @@ -255,9 +502,6 @@ heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags, { struct twojson j; - if (flags & HEIM_JSON_F_STRICT_STRINGS) - return ENOTSUP; /* Sorry, not yet! */ - heim_base_once_f(&heim_json_once, NULL, json_init_once); j.indent = 0; @@ -267,7 +511,11 @@ heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags, j.ret = 0; j.first = 1; - return base2json(obj, &j); + if (!(flags & HEIM_JSON_F_NO_ESCAPE_NON_ASCII) && + !heim_locale_is_utf8()) + j.flags |= HEIM_JSON_F_ESCAPE_NON_ASCII; + + return base2json(obj, &j, 0); } @@ -342,93 +590,425 @@ parse_number(struct parse_ctx *ctx) return heim_number_create(number * neg); } +/* + * Read 4 hex digits from ctx->p. + * + * If we don't have enough, rewind ctx->p and return -1 . + */ +static int +unescape_unicode(struct parse_ctx *ctx) +{ + int c = 0; + int i; + + for (i = 0; i < 4 && ctx->p < ctx->pend; i++, ctx->p++) { + if (*ctx->p >= '0' && *ctx->p <= '9') { + c = (c << 4) + (*ctx->p - '0'); + } else if (*ctx->p >= 'A' && *ctx->p <= 'F') { + c = (c << 4) + (10 + *ctx->p - 'A'); + } else if (*ctx->p >= 'a' && *ctx->p <= 'f') { + c = (c << 4) + (10 + *ctx->p - 'a'); + } else { + ctx->p -= i; + return -1; + } + } + return c; +} + +static int +encode_utf8(struct parse_ctx *ctx, char **pp, char *pend, int c) +{ + char *p = *pp; + + if (c < 0x80) { + /* ASCII */ + if (p >= pend) return 0; + *(p++) = c; + *pp = p; + return 1; + } + if (c < 0x800) { + /* 2 code unit UTF-8 sequence */ + if (p >= pend) return 0; + *(p++) = 0xc0 | ((c >> 6) ); + if (p == pend) return 0; + *(p++) = 0x80 | ((c ) & 0x3f); + *pp = p; + return 1; + } + if (c < 0x10000) { + /* 3 code unit UTF-8 sequence */ + if (p >= pend) return 0; + *(p++) = 0xe0 | ((c >> 12) ); + if (p == pend) return 0; + *(p++) = 0x80 | ((c >> 6) & 0x3f); + if (p == pend) return 0; + *(p++) = 0x80 | ((c) & 0x3f); + *pp = p; + return 1; + } + if (c < 0x110000) { + /* 4 code unit UTF-8 sequence */ + if (p >= pend) return 0; + *(p++) = 0xf0 | ((c >> 18) ); + if (p == pend) return 0; + *(p++) = 0x80 | ((c >> 12) & 0x3f); + if (p == pend) return 0; + *(p++) = 0x80 | ((c >> 6) & 0x3f); + if (p == pend) return 0; + *(p++) = 0x80 | ((c) & 0x3f); + *pp = p; + return 1; + } + return 0; +} + +static heim_string_t +parse_string_error(struct parse_ctx *ctx, + char *freeme, + const char *msg) +{ + free(freeme); + ctx->error = heim_error_create(EINVAL, "%s at %lu", msg, ctx->lineno); + return NULL; +} + static heim_string_t parse_string(struct parse_ctx *ctx) { const uint8_t *start; - int quote = 0; - - if (ctx->flags & HEIM_JSON_F_STRICT_STRINGS) { - ctx->error = heim_error_create(EINVAL, "Strict JSON string encoding " - "not yet supported"); - return NULL; + heim_object_t o; + size_t alloc_len = 0; + size_t need = 0; + char *p0, *p, *pend; + int strict = ctx->flags & HEIM_JSON_F_STRICT_STRINGS; + int binary = 0; + + if (*ctx->p != '"') + return parse_string_error(ctx, NULL, + "Expected a JSON string but found " + "something else"); + start = ++(ctx->p); + + /* Estimate how many bytes we need to allocate */ + p0 = p = pend = NULL; + for (need = 1; ctx->p < ctx->pend; ctx->p++) { + need++; + if (*ctx->p == '\\') + ctx->p++; + else if (*ctx->p == '"') + break; } + if (ctx->p == ctx->pend) + return parse_string_error(ctx, NULL, "Unterminated JSON string"); - if (*ctx->p != '"') { - ctx->error = heim_error_create(EINVAL, "Expected a JSON string but " - "found something else at line %lu", - ctx->lineno); - return NULL; + ctx->p = start; + while (ctx->p < ctx->pend) { + const unsigned char *p_save; + int32_t ctop, cbot; + + if (*ctx->p == '"') { + ctx->p++; + break; + } + + /* Allocate or resize our output buffer if need be */ + if (need || p == pend) { + char *tmp; + + /* + * Work out how far p is into p0 to re-esablish p after + * the realloc() + */ + size_t p0_to_p_len = (p - p0); + + tmp = realloc(p0, alloc_len + need + 5 /* slop? */); + + if (tmp == NULL) { + ctx->error = heim_error_create_enomem(); + free(p0); + return NULL; + } + alloc_len += need + 5; + + /* + * We have two pointers, p and p0, we want to keep them + * pointing into the same memory after the realloc() + */ + p = tmp + p0_to_p_len; + p0 = tmp; + pend = p0 + alloc_len; + + need = 0; + } + + if (*ctx->p != '\\') { + unsigned char c = *ctx->p; + + /* + * Not backslashed -> consume now. + * + * NOTE: All cases in this block must continue or return w/ error. + */ + + /* Check for unescaped ASCII control characters */ + if (c == '\n') { + if (strict) + return parse_string_error(ctx, p0, + "Unescaped newline in JSON string"); + /* Count the newline but don't add it to the decoding */ + ctx->lineno++; + } else if (strict && *ctx->p <= 0x1f) { + return parse_string_error(ctx, p0, "Unescaped ASCII control character"); + } else if (c == 0) { + binary = 1; + } + if (!strict || c < 0x80) { + /* ASCII, or not strict -> no need to validate */ + *(p++) = c; + ctx->p++; + continue; + } + + /* + * Being strict for parsing means we want to detect malformed UTF-8 + * sequences. + * + * If not strict then we just go on below and add to `p' whatever + * bytes we find in `ctx->p' as we find them. + * + * For each two-byte sequence we need one more byte in `p[]'. For + * each three-byte sequence we need two more bytes in `p[]'. + * + * Setting `need' and looping will cause `p0' to be grown. + * + * NOTE: All cases in this block must continue or return w/ error. + */ + if ((c & 0xe0) == 0xc0) { + /* Two-byte UTF-8 encoding */ + if (pend - p < 2) { + need = 2; + continue; /* realloc p0 */ + } + + *(p++) = c; + ctx->p++; + if (ctx->p == ctx->pend) + return parse_string_error(ctx, p0, "Truncated UTF-8"); + c = *(ctx->p++); + if ((c & 0xc0) != 0x80) + return parse_string_error(ctx, p0, "Truncated UTF-8"); + *(p++) = c; + continue; + } + if ((c & 0xf0) == 0xe0) { + /* Three-byte UTF-8 encoding */ + if (pend - p < 3) { + need = 3; + continue; /* realloc p0 */ + } + + *(p++) = c; + ctx->p++; + if (ctx->p == ctx->pend) + return parse_string_error(ctx, p0, "Truncated UTF-8"); + c = *(ctx->p++); + if ((c & 0xc0) != 0x80) + return parse_string_error(ctx, p0, "Truncated UTF-8"); + *(p++) = c; + c = *(ctx->p++); + if ((c & 0xc0) != 0x80) + return parse_string_error(ctx, p0, "Truncated UTF-8"); + *(p++) = c; + continue; + } + if ((c & 0xf8) == 0xf0) + return parse_string_error(ctx, p0, "UTF-8 sequence not " + "encoded as escaped UTF-16"); + if ((c & 0xc0) == 0x80) + return parse_string_error(ctx, p0, + "Invalid UTF-8 " + "(bare continuation code unit)"); + + return parse_string_error(ctx, p0, "Not UTF-8"); + } + + /* Backslash-quoted character */ + ctx->p++; + if (ctx->p == ctx->pend) { + ctx->error = + heim_error_create(EINVAL, + "Unterminated JSON string at line %lu", + ctx->lineno); + free(p0); + return NULL; + } + switch (*ctx->p) { + /* Simple escapes */ + case 'b': *(p++) = '\b'; ctx->p++; continue; + case 'f': *(p++) = '\f'; ctx->p++; continue; + case 'n': *(p++) = '\n'; ctx->p++; continue; + case 'r': *(p++) = '\r'; ctx->p++; continue; + case 't': *(p++) = '\t'; ctx->p++; continue; + case '"': *(p++) = '"'; ctx->p++; continue; + case '\\': *(p++) = '\\'; ctx->p++; continue; + /* Escaped Unicode handled below */ + case 'u': + /* + * Worst case for !strict we need 11 bytes for a truncated non-BMP + * codepoint escape. Call it 12. + */ + if (strict) + need = 4; + else + need = 12; + if (pend - p < need) { + /* Go back to the backslash, realloc, try again */ + ctx->p--; + continue; + } + + need = 0; + ctx->p++; + break; + default: + if (!strict) { + *(p++) = *ctx->p; + ctx->p++; + continue; + } + ctx->error = + heim_error_create(EINVAL, + "Invalid backslash escape at line %lu", + ctx->lineno); + free(p0); + return NULL; + } + + /* Unicode code point */ + if (pend - p < 12) { + need = 12; + ctx->p -= 2; /* for "\\u" */ + continue; /* This will cause p0 to be realloc'ed */ + } + p_save = ctx->p; + cbot = -3; + ctop = unescape_unicode(ctx); + if (ctop == -1 && strict) + return parse_string_error(ctx, p0, "Invalid escaped Unicode"); + if (ctop == -1) { + /* + * Not strict; tolerate bad input. + * + * Output "\\u" and then loop to treat what we expected to be four + * digits as if they were not part of an escaped Unicode codepoint. + */ + ctx->p = p_save; + if (p < pend) + *(p++) = '\\'; + if (p < pend) + *(p++) = 'u'; + continue; + } + if (ctop == 0) { + *(p++) = '\0'; + binary = 1; + continue; + } + if (ctop < 0xd800) { + if (!encode_utf8(ctx, &p, pend, ctop)) + return parse_string_error(ctx, p0, + "Internal JSON string parse error"); + continue; + } + + /* + * We parsed the top escaped codepoint of a surrogate pair encoding + * of a non-BMP Unicode codepoint. What follows must be another + * escaped codepoint. + */ + if (ctx->p < ctx->pend && ctx->p[0] == '\\') + ctx->p++; + else + ctop = -1; + if (ctop > -1 && ctx->p < ctx->pend && ctx->p[0] == 'u') + ctx->p++; + else + ctop = -1; + if (ctop > -1) { + /* Parse the hex digits of the bottom half of the surrogate pair */ + cbot = unescape_unicode(ctx); + if (cbot == -1 || cbot < 0xdc00) + ctop = -1; + } + if (ctop == -1) { + if (strict) + return parse_string_error(ctx, p0, + "Invalid surrogate pair"); + + /* + * Output "\\u", rewind, output the digits of `ctop'. + * + * When we get to what should have been the bottom half of the + * pair we'll necessarily fail to parse it as a normal escaped + * Unicode codepoint, and once again, rewind and output its digits. + */ + if (p < pend) + *(p++) = '\\'; + if (p < pend) + *(p++) = 'u'; + ctx->p = p_save; + continue; + } + + /* Finally decode the surrogate pair then encode as UTF-8 */ + ctop -= 0xd800; + cbot -= 0xdc00; + if (!encode_utf8(ctx, &p, pend, 0x10000 + ((ctop << 10) | (cbot & 0x3ff)))) + return parse_string_error(ctx, p0, + "Internal JSON string parse error"); } - start = ++ctx->p; - while (ctx->p < ctx->pend) { - if (*ctx->p == '\n') { - ctx->lineno++; - } else if (*ctx->p == '\\') { - if (ctx->p + 1 == ctx->pend) - goto out; - ctx->p++; - quote = 1; - } else if (*ctx->p == '"') { - heim_object_t o; - - if (quote) { - char *p0, *p; - p = p0 = malloc(ctx->p - start); - if (p == NULL) - goto out; - while (start < ctx->p) { - if (*start == '\\') { - start++; - /* XXX validate quoted char */ - } - *p++ = *start++; - } - o = heim_string_create_with_bytes(p0, p - p0); - free(p0); - } else { - o = heim_string_create_with_bytes(start, ctx->p - start); - if (o == NULL) { - ctx->error = heim_error_create_enomem(); - return NULL; - } - - /* If we can decode as base64, then let's */ - if (ctx->flags & HEIM_JSON_F_TRY_DECODE_DATA) { - void *buf; - size_t len; - const char *s; - - s = heim_string_get_utf8(o); - len = strlen(s); - - if (len >= 4 && strspn(s, base64_chars) >= len - 2) { - buf = malloc(len); - if (buf == NULL) { - heim_release(o); - ctx->error = heim_error_create_enomem(); - return NULL; - } - len = rk_base64_decode(s, buf); - if (len == -1) { - free(buf); - return o; - } - heim_release(o); - o = heim_data_ref_create(buf, len, free); - } - } - } - ctx->p += 1; + if (p0 == NULL) + return heim_string_create(""); - return o; - } - ctx->p += 1; + /* NUL-terminate for rk_base64_decode() and plain paranoia */ + if (p0 != NULL && p == pend) { + /* + * Work out how far p is into p0 to re-esablish p after + * the realloc() + */ + size_t p0_to_pend_len = (pend - p0); + char *tmp = realloc(p0, 1 + p0_to_pend_len); + + if (tmp == NULL) { + ctx->error = heim_error_create_enomem(); + free(p0); + return NULL; + } + /* + * We have three pointers, p, pend (which are the same) + * and p0, we want to keep them pointing into the same + * memory after the realloc() + */ + p = tmp + p0_to_pend_len; + + pend = p + 1; + p0 = tmp; } - out: - ctx->error = heim_error_create(EINVAL, "ran out of string"); - return NULL; + *(p++) = '\0'; + + /* If there's embedded NULs, it's not a C string */ + if (binary) { + o = heim_data_ref_create(p0, (p - 1) - p0, free); + return o; + } + + /* Sadly this will copy `p0' */ + o = heim_string_create_with_bytes(p0, p - p0); + free(p0); + return o; } static int @@ -809,3 +1389,83 @@ heim_json_copy_serialize(heim_object_t obj, heim_json_flags_t flags, heim_error_ } return str; } + +struct heim_eq_f_ctx { + heim_dict_t other; + int ret; +}; + +static void +heim_eq_dict_iter_f(heim_object_t key, heim_object_t val, void *d) +{ + struct heim_eq_f_ctx *ctx = d; + heim_object_t other_val; + + if (!ctx->ret) + return; + + /* + * This doesn't work if the key is an array or a dict, which, anyways, + * isn't allowed in JSON, though we allow it. + */ + other_val = heim_dict_get_value(ctx->other, key); + ctx->ret = heim_json_eq(val, other_val); +} + +int +heim_json_eq(heim_object_t a, heim_object_t b) +{ + heim_tid_t atid, btid; + + if (a == b) + return 1; + if (a == NULL || b == NULL) + return 0; + atid = heim_get_tid(a); + btid = heim_get_tid(b); + if (atid != btid) + return 0; + switch (atid) { + case HEIM_TID_ARRAY: { + size_t len = heim_array_get_length(b); + size_t i; + + if (heim_array_get_length(a) != len) + return 0; + for (i = 0; i < len; i++) { + if (!heim_json_eq(heim_array_get_value(a, i), + heim_array_get_value(b, i))) + return 0; + } + return 1; + } + case HEIM_TID_DICT: { + struct heim_eq_f_ctx ctx; + + ctx.other = b; + ctx.ret = 1; + heim_dict_iterate_f(a, &ctx, heim_eq_dict_iter_f); + + if (ctx.ret) { + ctx.other = a; + heim_dict_iterate_f(b, &ctx, heim_eq_dict_iter_f); + } + return ctx.ret; + } + case HEIM_TID_STRING: + return strcmp(heim_string_get_utf8(a), heim_string_get_utf8(b)) == 0; + case HEIM_TID_DATA: { + return heim_data_get_length(a) == heim_data_get_length(b) && + memcmp(heim_data_get_ptr(a), heim_data_get_ptr(b), + heim_data_get_length(a)) == 0; + } + case HEIM_TID_NUMBER: + return heim_number_get_long(a) == heim_number_get_long(b); + case HEIM_TID_NULL: + case HEIM_TID_BOOL: + return heim_bool_val(a) == heim_bool_val(b); + default: + break; + } + return 0; +} diff --git a/lib/base/log.c b/lib/base/log.c new file mode 100644 index 000000000000..9a97276e1534 --- /dev/null +++ b/lib/base/log.c @@ -0,0 +1,1079 @@ +/* + * Copyright (c) 1997-2020 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2009 Apple Inc. 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 "baselocl.h" +#include "heim_threads.h" +#include "heimbase-atomics.h" +#include "heimbase.h" +#include "heimbase-svc.h" +#include <assert.h> +#include <stdarg.h> +#include <vis.h> +#include <base64.h> + +struct heim_log_facility_internal { + int min; + int max; + heim_log_log_func_t log_func; + heim_log_close_func_t close_func; + void *data; +}; + +struct heim_log_facility_s { + char *program; + heim_base_atomic(uint32_t) refs; + size_t len; + struct heim_log_facility_internal *val; +}; + +typedef struct heim_pcontext_s *heim_pcontext; +typedef struct heim_pconfig *heim_pconfig; +struct heim_svc_req_desc_common_s { + HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS; +}; + +static struct heim_log_facility_internal * +log_realloc(heim_log_facility *f) +{ + struct heim_log_facility_internal *fp; + fp = realloc(f->val, (f->len + 1) * sizeof(*f->val)); + if (fp == NULL) + return NULL; + f->len++; + f->val = fp; + fp += f->len - 1; + return fp; +} + +struct s2i { + const char *s; + int val; +}; + +#define L(X) { #X, LOG_ ## X } + +static struct s2i syslogvals[] = { + L(EMERG), + L(ALERT), + L(CRIT), + L(ERR), + L(WARNING), + L(NOTICE), + L(INFO), + L(DEBUG), + + L(AUTH), +#ifdef LOG_AUTHPRIV + L(AUTHPRIV), +#endif +#ifdef LOG_CRON + L(CRON), +#endif + L(DAEMON), +#ifdef LOG_FTP + L(FTP), +#endif + L(KERN), + L(LPR), + L(MAIL), +#ifdef LOG_NEWS + L(NEWS), +#endif + L(SYSLOG), + L(USER), +#ifdef LOG_UUCP + L(UUCP), +#endif + L(LOCAL0), + L(LOCAL1), + L(LOCAL2), + L(LOCAL3), + L(LOCAL4), + L(LOCAL5), + L(LOCAL6), + L(LOCAL7), + { NULL, -1 } +}; + +static int +find_value(const char *s, struct s2i *table) +{ + while (table->s && strcasecmp(table->s, s) != 0) + table++; + return table->val; +} + +heim_error_code +heim_initlog(heim_context context, + const char *program, + heim_log_facility **fac) +{ + heim_log_facility *f = calloc(1, sizeof(*f)); + if (f == NULL) + return heim_enomem(context); + f->refs = 1; + f->program = strdup(program); + if (f->program == NULL) { + free(f); + return heim_enomem(context); + } + *fac = f; + return 0; +} + +heim_log_facility * +heim_log_ref(heim_log_facility *fac) +{ + if (fac) + (void) heim_base_atomic_inc_32(&fac->refs); + return fac; +} + +heim_error_code +heim_addlog_func(heim_context context, + heim_log_facility *fac, + int min, + int max, + heim_log_log_func_t log_func, + heim_log_close_func_t close_func, + void *data) +{ + struct heim_log_facility_internal *fp = log_realloc(fac); + if (fp == NULL) + return heim_enomem(context); + fp->min = min; + fp->max = max; + fp->log_func = log_func; + fp->close_func = close_func; + fp->data = data; + return 0; +} + + +struct _heimdal_syslog_data{ + int priority; +}; + +static void HEIM_CALLCONV +log_syslog(heim_context context, const char *timestr, + const char *msg, void *data) +{ + struct _heimdal_syslog_data *s = data; + syslog(s->priority, "%s", msg); +} + +static void HEIM_CALLCONV +close_syslog(void *data) +{ + free(data); + closelog(); +} + +static heim_error_code +open_syslog(heim_context context, + heim_log_facility *facility, int min, int max, + const char *sev, const char *fac) +{ + struct _heimdal_syslog_data *sd; + heim_error_code ret; + int i; + + if (facility == NULL) + return EINVAL; + if ((sd = calloc(1, sizeof(*sd))) == NULL) + return heim_enomem(context); + i = find_value(sev, syslogvals); + if (i == -1) + i = LOG_ERR; + sd->priority = i; + i = find_value(fac, syslogvals); + if (i == -1) + i = LOG_AUTH; + sd->priority |= i; + roken_openlog(facility->program, LOG_PID | LOG_NDELAY, i); + ret = heim_addlog_func(context, facility, min, max, log_syslog, + close_syslog, sd); + if (ret) + free(sd); + else + sd = NULL; + return ret; +} + +struct file_data { + char *filename; + const char *mode; + struct timeval tv; + FILE *fd; + int disp; +#define FILEDISP_KEEPOPEN 0x1 +#define FILEDISP_REOPEN 0x2 +#define FILEDISP_IFEXISTS 0x4 +}; + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +static void HEIM_CALLCONV +log_file(heim_context context, const char *timestr, const char *msg, void *data) +{ + struct timeval tv; + struct file_data *f = data; + FILE *logf = f->fd; + char *msgclean; + size_t i = 0; + size_t j; + + if (f->filename && (logf == NULL || (f->disp & FILEDISP_REOPEN))) { + int flags = O_WRONLY|O_APPEND; + int fd; + + if (f->mode[0] == 'e') { + flags |= O_CLOEXEC; + i = 1; + } + if (f->mode[i] == 'w') + flags |= O_TRUNC; + if (f->mode[i + 1] == '+') + flags |= O_RDWR; + + if (f->disp & FILEDISP_IFEXISTS) { + /* Cache failure for 1s */ + gettimeofday(&tv, NULL); + if (tv.tv_sec == f->tv.tv_sec) + return; + } else { + flags |= O_CREAT; + } + + fd = open(f->filename, flags, 0666); /* umask best be set */ + if (fd == -1) { + if (f->disp & FILEDISP_IFEXISTS) + gettimeofday(&f->tv, NULL); + return; + } + rk_cloexec(fd); + logf = fdopen(fd, f->mode); + } + if (f->fd == NULL && (f->disp & FILEDISP_KEEPOPEN)) + f->fd = logf; + if (logf == NULL) + return; + /* + * make sure the log doesn't contain special chars: + * we used to use strvisx(3) to encode the log, but this is + * inconsistent with our syslog(3) code which does not do this. + * It also makes it inelegant to write data which has already + * been quoted such as what krb5_unparse_principal() gives us. + * So, we change here to eat the special characters, instead. + */ + if (msg && (msgclean = strdup(msg))) { + for (i = 0, j = 0; msg[i]; i++) + if (msg[i] >= 32 || msg[i] == '\t') + msgclean[j++] = msg[i]; + fprintf(logf, "%s %s\n", timestr ? timestr : "", msgclean); + free(msgclean); + } + if (logf != f->fd) + fclose(logf); +} + +static void HEIM_CALLCONV +close_file(void *data) +{ + struct file_data *f = data; + if (f->fd && f->fd != stdout && f->fd != stderr) + fclose(f->fd); + free(f->filename); + free(data); +} + +static heim_error_code +open_file(heim_context context, heim_log_facility *fac, int min, int max, + const char *filename, const char *mode, FILE *f, int disp, + int exp_tokens) +{ + heim_error_code ret = 0; + struct file_data *fd; + + if ((fd = calloc(1, sizeof(*fd))) == NULL) + return heim_enomem(context); + + fd->filename = NULL; + fd->mode = mode; + fd->fd = f; + fd->disp = disp; + + if (filename) { + if (exp_tokens) + ret = heim_expand_path_tokens(context, filename, 1, &fd->filename, NULL); + else if ((fd->filename = strdup(filename)) == NULL) + ret = heim_enomem(context); + } + if (ret == 0) + ret = heim_addlog_func(context, fac, min, max, log_file, close_file, fd); + if (ret) { + free(fd->filename); + free(fd); + } else if (disp & FILEDISP_KEEPOPEN) { + log_file(context, NULL, NULL, fd); + fd = NULL; + } + return ret; +} + +heim_error_code +heim_addlog_dest(heim_context context, heim_log_facility *f, const char *orig) +{ + heim_error_code ret = 0; + int min = 0, max = 3, n; + char c; + const char *p = orig; +#ifdef _WIN32 + const char *q; +#endif + + n = sscanf(p, "%d%c%d/", &min, &c, &max); + if (n == 2) { + if (ISPATHSEP(c)) { + if (min < 0) { + max = -min; + min = 0; + } else { + max = min; + } + } + if (c == '-') + max = -1; + } + if (n) { +#ifdef _WIN32 + q = strrchr(p, '\\'); + if (q != NULL) + p = q; + else +#endif + p = strchr(p, '/'); + if (p == NULL) { + heim_set_error_message(context, EINVAL /*XXX HEIM_ERR_LOG_PARSE*/, + N_("failed to parse \"%s\"", ""), orig); + return EINVAL /*XXX HEIM_ERR_LOG_PARSE*/; + } + p++; + } + if (strcmp(p, "STDERR") == 0) { + ret = open_file(context, f, min, max, NULL, "a", stderr, + FILEDISP_KEEPOPEN, 0); + } else if (strcmp(p, "CONSOLE") == 0) { + /* XXX WIN32 */ + ret = open_file(context, f, min, max, "/dev/console", "w", NULL, + FILEDISP_KEEPOPEN, 0); + } else if (strncmp(p, "EFILE:", 5) == 0) { + ret = open_file(context, f, min, max, p + sizeof("EFILE:") - 1, "a", + NULL, FILEDISP_IFEXISTS | FILEDISP_REOPEN, 1); + } else if (strncmp(p, "EFILE=", 5) == 0) { + ret = open_file(context, f, min, max, p + sizeof("EFILE=") - 1, "a", + NULL, FILEDISP_IFEXISTS | FILEDISP_KEEPOPEN, 1); + } else if (strncmp(p, "FILE:", sizeof("FILE:") - 1) == 0) { + ret = open_file(context, f, min, max, p + sizeof("FILE:") - 1, "a", + NULL, FILEDISP_REOPEN, 1); + } else if (strncmp(p, "FILE=", sizeof("FILE=") - 1) == 0) { + ret = open_file(context, f, min, max, p + sizeof("FILE=") - 1, "a", + NULL, FILEDISP_KEEPOPEN, 1); + } else if (strncmp(p, "DEVICE:", sizeof("DEVICE:") - 1) == 0) { + ret = open_file(context, f, min, max, p + sizeof("DEVICE:") - 1, "a", + NULL, FILEDISP_REOPEN, 0); + } else if (strncmp(p, "DEVICE=", sizeof("DEVICE=") - 1) == 0) { + ret = open_file(context, f, min, max, p + sizeof("DEVICE=") - 1, "a", + NULL, FILEDISP_KEEPOPEN, 0); + } else if (strncmp(p, "SYSLOG", 6) == 0 && (p[6] == '\0' || p[6] == ':')) { + char severity[128] = ""; + char facility[128] = ""; + p += 6; + if (*p != '\0') + p++; + if (strsep_copy(&p, ":", severity, sizeof(severity)) != -1) + strsep_copy(&p, ":", facility, sizeof(facility)); + if (*severity == '\0') + strlcpy(severity, "ERR", sizeof(severity)); + if (*facility == '\0') + strlcpy(facility, "AUTH", sizeof(facility)); + ret = open_syslog(context, f, min, max, severity, facility); + } else { + ret = EINVAL; /*XXX HEIM_ERR_LOG_PARSE*/ + heim_set_error_message(context, ret, + N_("unknown log type: %s", ""), p); + } + return ret; +} + +heim_error_code +heim_openlog(heim_context context, + const char *program, + const char **specs, + heim_log_facility **fac) +{ + heim_error_code ret; + + ret = heim_initlog(context, program, fac); + if (ret) + return ret; + + if (specs) { + size_t i; + for (i = 0; specs[i] && ret == 0; i++) + ret = heim_addlog_dest(context, *fac, specs[i]); + } else { + ret = heim_addlog_dest(context, *fac, "SYSLOG"); + } + return ret; +} + +void +heim_closelog(heim_context context, heim_log_facility *fac) +{ + int i; + + if (!fac || heim_base_atomic_dec_32(&fac->refs)) + return; + for (i = 0; i < fac->len; i++) + (*fac->val[i].close_func)(fac->val[i].data); + free(fac->val); + free(fac->program); + fac->val = NULL; + fac->len = 0; + fac->program = NULL; + free(fac); + return; +} + +static void +format_time(heim_context context, time_t t, char *s, size_t len) +{ + struct tm *tm = heim_context_get_log_utc(context) ? + gmtime(&t) : localtime(&t); + if (tm && strftime(s, len, heim_context_get_time_fmt(context), tm)) + return; + snprintf(s, len, "%ld", (long)t); +} + +#undef __attribute__ +#define __attribute__(X) + +heim_error_code +heim_vlog_msg(heim_context context, + heim_log_facility *fac, + char **reply, + int level, + const char *fmt, + va_list ap) +__attribute__ ((__format__ (__printf__, 5, 0))) +{ + + char *msg = NULL; + const char *actual = NULL; + char buf[64]; + time_t t = 0; + int i; + + if (!fac) + fac = context->log_dest; + for (i = 0; fac && i < fac->len; i++) + if (fac->val[i].min <= level && + (fac->val[i].max < 0 || fac->val[i].max >= level)) { + if (t == 0) { + t = time(NULL); + format_time(context, t, buf, sizeof(buf)); + } + if (actual == NULL) { + int ret = vasprintf(&msg, fmt, ap); + if (ret < 0 || msg == NULL) + actual = fmt; + else + actual = msg; + } + (*fac->val[i].log_func)(context, buf, actual, fac->val[i].data); + } + if (reply == NULL) + free(msg); + else + *reply = msg; + return 0; +} + +heim_error_code +heim_vlog(heim_context context, + heim_log_facility *fac, + int level, + const char *fmt, + va_list ap) +__attribute__ ((__format__ (__printf__, 4, 0))) +{ + return heim_vlog_msg(context, fac, NULL, level, fmt, ap); +} + +heim_error_code +heim_log_msg(heim_context context, + heim_log_facility *fac, + int level, + char **reply, + const char *fmt, + ...) +__attribute__ ((__format__ (__printf__, 5, 6))) +{ + va_list ap; + heim_error_code ret; + + va_start(ap, fmt); + ret = heim_vlog_msg(context, fac, reply, level, fmt, ap); + va_end(ap); + return ret; +} + + +heim_error_code +heim_log(heim_context context, + heim_log_facility *fac, + int level, + const char *fmt, + ...) +__attribute__ ((__format__ (__printf__, 4, 5))) +{ + va_list ap; + heim_error_code ret; + + va_start(ap, fmt); + ret = heim_vlog(context, fac, level, fmt, ap); + va_end(ap); + return ret; +} + +void +heim_debug(heim_context context, + int level, + const char *fmt, + ...) +__attribute__ ((__format__ (__printf__, 3, 4))) +{ + heim_log_facility *fac; + va_list ap; + + if (context == NULL || + (fac = heim_get_debug_dest(context)) == NULL) + return; + + va_start(ap, fmt); + heim_vlog(context, fac, level, fmt, ap); + va_end(ap); +} + +void +heim_vdebug(heim_context context, + int level, + const char *fmt, + va_list ap) +__attribute__ ((__format__ (__printf__, 3, 0))) +{ + heim_log_facility *fac; + + if (context == NULL || + (fac = heim_get_debug_dest(context)) == NULL) + return; + + heim_vlog(context, fac, level, fmt, ap); +} + +heim_error_code +heim_have_debug(heim_context context, int level) +{ + return (context != NULL && heim_get_debug_dest(context) != NULL); +} + +heim_error_code +heim_add_warn_dest(heim_context context, const char *program, + const char *log_spec) +{ + heim_log_facility *fac; + + heim_error_code ret; + + if ((fac = heim_get_warn_dest(context)) == NULL) { + ret = heim_initlog(context, program, &fac); + if (ret) + return ret; + heim_set_warn_dest(context, fac); + } + + ret = heim_addlog_dest(context, fac, log_spec); + if (ret) + return ret; + return 0; +} + +heim_error_code +heim_add_debug_dest(heim_context context, const char *program, + const char *log_spec) +{ + heim_log_facility *fac; + heim_error_code ret; + + if ((fac = heim_get_debug_dest(context)) == NULL) { + ret = heim_initlog(context, program, &fac); + if (ret) + return ret; + heim_set_debug_dest(context, fac); + } + + ret = heim_addlog_dest(context, fac, log_spec); + if (ret) + return ret; + return 0; +} + +struct heim_audit_kv_tuple { + heim_string_t key; + heim_object_t value; +}; + +static struct heim_audit_kv_tuple zero_tuple; + +static struct heim_audit_kv_tuple +fmtkv(int flags, const char *k, const char *fmt, va_list ap) + __attribute__ ((__format__ (__printf__, 3, 0))) +{ + size_t i; + ssize_t j; + struct heim_audit_kv_tuple kv; + char *value; + char *value_vis; + + j = vasprintf(&value, fmt, ap); + if (j < 0 || value == NULL) + return zero_tuple; + + /* We optionally eat the whitespace. */ + + if (flags & HEIM_SVC_AUDIT_EATWHITE) { + for (i=0, j=0; value[i]; i++) + if (value[i] != ' ' && value[i] != '\t') + value[j++] = value[i]; + value[j] = '\0'; + } + + if (flags & (HEIM_SVC_AUDIT_VIS | HEIM_SVC_AUDIT_VISLAST)) { + int vis_flags = VIS_CSTYLE | VIS_OCTAL | VIS_NL; + + if (flags & HEIM_SVC_AUDIT_VIS) + vis_flags |= VIS_WHITE; + value_vis = malloc((j + 1) * 4 + 1); + if (value_vis) + strvisx(value_vis, value, j, vis_flags); + free(value); + if (value_vis == NULL) + return zero_tuple; + } else + value_vis = value; + + if (k) + kv.key = heim_string_create(k); + else + kv.key = NULL; + kv.value = heim_string_ref_create(value_vis, free); + + return kv; +} + +void +heim_audit_vaddreason(heim_svc_req_desc r, const char *fmt, va_list ap) + __attribute__ ((__format__ (__printf__, 2, 0))) +{ + struct heim_audit_kv_tuple kv; + + kv = fmtkv(HEIM_SVC_AUDIT_VISLAST, NULL, fmt, ap); + if (kv.value == NULL) { + heim_log(r->hcontext, r->logf, 1, "heim_audit_vaddreason: " + "failed to add reason (out of memory)"); + return; + } + + heim_log(r->hcontext, r->logf, 7, "heim_audit_vaddreason(): " + "adding reason %s", heim_string_get_utf8(kv.value)); + if (r->reason) { + heim_string_t str2; + + str2 = heim_string_create_with_format("%s: %s", + heim_string_get_utf8(kv.value), + heim_string_get_utf8(r->reason)); + if (str2) { + heim_release(kv.value); + kv.value = str2; + } + } + heim_release(r->reason); + r->reason = kv.value; +} + +void +heim_audit_addreason(heim_svc_req_desc r, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))) +{ + va_list ap; + + va_start(ap, fmt); + heim_audit_vaddreason(r, fmt, ap); + va_end(ap); +} + +size_t +addkv(heim_svc_req_desc r, heim_object_t key, heim_object_t value) +{ + size_t index; + heim_object_t obj; + + obj = heim_dict_get_value(r->kv, key); + if (obj) { + if (heim_get_tid(obj) == HEIM_TID_ARRAY) { + index = heim_array_get_length(obj); + heim_array_append_value(obj, value); + } else { + heim_array_t array = heim_array_create(); + + index = 1; + heim_array_append_value(array, obj); + heim_array_append_value(array, value); + heim_dict_set_value(r->kv, key, array); + heim_release(array); /* retained by r->kv */ + } + } else { + index = 0; + heim_dict_set_value(r->kv, key, value); + } + + return index; +} + +/* + * add a key-value token. if the key already exists, the value is + * promoted to an array of values. + */ + +void +heim_audit_vaddkv(heim_svc_req_desc r, int flags, const char *k, + const char *fmt, va_list ap) + __attribute__ ((__format__ (__printf__, 4, 0))) +{ + struct heim_audit_kv_tuple kv; + size_t index; + + kv = fmtkv(flags, k, fmt, ap); + if (kv.key == NULL || kv.value == NULL) { + heim_log(r->hcontext, r->logf, 1, "heim_audit_vaddkv: " + "failed to add kv pair (out of memory)"); + heim_release(kv.key); + heim_release(kv.value); + return; + } + + index = addkv(r, kv.key, kv.value); + + heim_log(r->hcontext, r->logf, 7, "heim_audit_vaddkv(): " + "kv pair[%zu] %s=%s", index, + heim_string_get_utf8(kv.key), heim_string_get_utf8(kv.value)); + + heim_release(kv.key); + heim_release(kv.value); +} + +void +heim_audit_addkv(heim_svc_req_desc r, int flags, const char *k, + const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 4, 5))) +{ + va_list ap; + + va_start(ap, fmt); + heim_audit_vaddkv(r, flags, k, fmt, ap); + va_end(ap); +} + +void +heim_audit_addkv_timediff(heim_svc_req_desc r, const char *k, + const struct timeval *start, + const struct timeval *end) +{ + time_t sec; + int usec; + const char *sign = ""; + + if (end->tv_sec > start->tv_sec || + (end->tv_sec == start->tv_sec && end->tv_usec >= start->tv_usec)) { + sec = end->tv_sec - start->tv_sec; + usec = end->tv_usec - start->tv_usec; + } else { + sec = start->tv_sec - end->tv_sec; + usec = start->tv_usec - end->tv_usec; + sign = "-"; + } + + if (usec < 0) { + usec += 1000000; + sec -= 1; + } + + heim_audit_addkv(r, 0, k, "%s%ld.%06d", sign, (long)sec, usec); +} + +void +heim_audit_setkv_bool(heim_svc_req_desc r, const char *k, int v) +{ + heim_string_t key = heim_string_create(k); + heim_number_t value; + + if (key == NULL) + return; + + heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_bool(): " + "setting kv pair %s=%s", k, v ? "true" : "false"); + + value = heim_bool_create(v); + heim_dict_set_value(r->kv, key, value); + heim_release(key); + heim_release(value); +} + +void +heim_audit_addkv_number(heim_svc_req_desc r, const char *k, int64_t v) +{ + heim_string_t key = heim_string_create(k); + heim_number_t value; + + if (key == NULL) + return; + + heim_log(r->hcontext, r->logf, 7, "heim_audit_addkv_number(): " + "adding kv pair %s=%lld", k, (long long)v); + + value = heim_number_create(v); + addkv(r, key, value); + heim_release(key); + heim_release(value); +} + +void +heim_audit_setkv_number(heim_svc_req_desc r, const char *k, int64_t v) +{ + heim_string_t key = heim_string_create(k); + heim_number_t value; + + if (key == NULL) + return; + + heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_number(): " + "setting kv pair %s=%lld", k, (long long)v); + + value = heim_number_create(v); + heim_dict_set_value(r->kv, key, value); + heim_release(key); + heim_release(value); +} + +void +heim_audit_addkv_object(heim_svc_req_desc r, const char *k, heim_object_t value) +{ + heim_string_t key = heim_string_create(k); + heim_string_t descr; + + if (key == NULL) + return; + + descr = heim_json_copy_serialize(value, HEIM_JSON_F_NO_DATA_DICT, NULL); + heim_log(r->hcontext, r->logf, 7, "heim_audit_addkv_object(): " + "adding kv pair %s=%s", + k, descr ? heim_string_get_utf8(descr) : "<unprintable>"); + addkv(r, key, value); + heim_release(key); + heim_release(descr); +} + +void +heim_audit_setkv_object(heim_svc_req_desc r, const char *k, heim_object_t value) +{ + heim_string_t key = heim_string_create(k); + heim_string_t descr; + + if (key == NULL) + return; + + descr = heim_json_copy_serialize(value, HEIM_JSON_F_NO_DATA_DICT, NULL); + heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_object(): " + "setting kv pair %s=%s", + k, descr ? heim_string_get_utf8(descr) : "<unprintable>"); + heim_dict_set_value(r->kv, key, value); + heim_release(key); + heim_release(descr); +} + +heim_object_t +heim_audit_getkv(heim_svc_req_desc r, const char *k) +{ + heim_string_t key; + heim_object_t value; + + key = heim_string_create(k); + if (key == NULL) + return NULL; + + value = heim_dict_get_value(r->kv, key); + heim_release(key); + return value; +} + +struct heim_audit_kv_buf { + char buf[1024]; + size_t pos; + heim_object_t iter; +}; + +static void +audit_trail_iterator(heim_object_t key, heim_object_t value, void *arg); + +static void +audit_trail_iterator_array(heim_object_t value, void *arg, int *stop) +{ + struct heim_audit_kv_buf *kvb = arg; + + audit_trail_iterator(kvb->iter, value, kvb); +} + +static void +audit_trail_iterator(heim_object_t key, heim_object_t value, void *arg) +{ + struct heim_audit_kv_buf *kvb = arg; + char num[32]; + const char *k = heim_string_get_utf8(key), *v = NULL; + char *b64 = NULL; + + if (k == NULL || *k == '#') /* # keys are hidden */ + return; + + switch (heim_get_tid(value)) { + case HEIM_TID_STRING: + v = heim_string_get_utf8(value); + break; + case HEIM_TID_NUMBER: + snprintf(num, sizeof(num), "%lld", (long long)heim_number_get_long(value)); + v = num; + break; + case HEIM_TID_NULL: + v = "null"; + break; + case HEIM_TID_BOOL: + v = heim_bool_val(value) ? "true" : "false"; + break; + case HEIM_TID_ARRAY: + if (kvb->iter) + break; /* arrays cannot be nested */ + + kvb->iter = key; + heim_array_iterate_f(value, kvb, audit_trail_iterator_array); + kvb->iter = NULL; + break; + case HEIM_TID_DATA: { + const heim_octet_string *data = heim_data_get_data(value); + if (rk_base64_encode(data->data, data->length, &b64) >= 0) + v = b64; + break; + } + default: + break; + } + + if (v == NULL) + return; + + if (kvb->pos < sizeof(kvb->buf) - 1) + kvb->buf[kvb->pos++] = ' '; + for (; *k && kvb->pos < sizeof(kvb->buf) - 1; kvb->pos++) + kvb->buf[kvb->pos] = *k++; + if (kvb->pos < sizeof(kvb->buf) - 1) + kvb->buf[kvb->pos++] = '='; + for (; *v && kvb->pos < sizeof(kvb->buf) - 1; kvb->pos++) + kvb->buf[kvb->pos] = *v++; + + free(b64); +} + +void +heim_audit_trail(heim_svc_req_desc r, heim_error_code ret, const char *retname) +{ + const char *retval; + struct heim_audit_kv_buf kvb; + char retvalbuf[30]; /* Enough for UNKNOWN-%d */ + +#define CASE(x) case x : retval = #x; break + if (retname) { + retval = retname; + } else switch (ret ? ret : r->error_code) { + CASE(ENOMEM); + CASE(ENOENT); + CASE(EACCES); + case 0: + retval = "SUCCESS"; + break; + default: + /* Wish we had a com_err number->symbolic name function */ + (void) snprintf(retvalbuf, sizeof(retvalbuf), "UNKNOWN-%d", + ret ? ret : r->error_code); + retval = retvalbuf; + break; + } + + heim_audit_addkv_timediff(r, "elapsed", &r->tv_start, &r->tv_end); + if (r->e_text && r->kv) + heim_audit_addkv(r, HEIM_SVC_AUDIT_VIS, "e-text", "%s", r->e_text); + + memset(&kvb, 0, sizeof(kvb)); + if (r->kv) + heim_dict_iterate_f(r->kv, &kvb, audit_trail_iterator); + kvb.buf[kvb.pos] = '\0'; + + heim_log(r->hcontext, r->logf, 3, "%s %s %s %s %s%s%s%s", + r->reqtype, retval, r->from, + r->cname ? r->cname : "<unknown>", + r->sname ? r->sname : "<unknown>", + kvb.buf, r->reason ? " reason=" : "", + r->reason ? heim_string_get_utf8(r->reason) : ""); +} diff --git a/lib/base/number.c b/lib/base/number.c index c259f69971d0..8833c8b15233 100644 --- a/lib/base/number.c +++ b/lib/base/number.c @@ -35,7 +35,7 @@ #include "baselocl.h" -static void +static void HEIM_CALLCONV number_dealloc(void *ptr) { } @@ -58,12 +58,12 @@ number_cmp(void *a, void *b) return na - nb; } -static unsigned long +static uintptr_t number_hash(void *ptr) { if (heim_base_is_tagged_object(ptr)) return heim_base_tagged_object_value(ptr); - return (unsigned long)*(int *)ptr; + return (uintptr_t)*(int64_t *)ptr; } struct heim_type_data _heim_number_object = { @@ -86,16 +86,16 @@ struct heim_type_data _heim_number_object = { */ heim_number_t -heim_number_create(int number) +heim_number_create(int64_t number) { heim_number_t n; if (number < 0xffffff && number >= 0) return heim_base_make_tagged_object(number, HEIM_TID_NUMBER); - n = _heim_alloc_object(&_heim_number_object, sizeof(int)); + n = _heim_alloc_object(&_heim_number_object, sizeof(int64_t)); if (n) - *((int *)n) = number; + *((int64_t *)n) = number; return n; } @@ -124,5 +124,13 @@ heim_number_get_int(heim_number_t number) { if (heim_base_is_tagged_object(number)) return heim_base_tagged_object_value(number); - return *(int *)number; + return (int)(*(int64_t *)number); +} + +int64_t +heim_number_get_long(heim_number_t number) +{ + if (heim_base_is_tagged_object(number)) + return heim_base_tagged_object_value(number); + return *(int64_t *)number; } diff --git a/lib/base/plugin.c b/lib/base/plugin.c new file mode 100644 index 000000000000..87bb5f7ac640 --- /dev/null +++ b/lib/base/plugin.c @@ -0,0 +1,785 @@ +/* + * Copyright (c) 2006 - 2020 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2018 AuriStor, Inc. + * + * 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 "baselocl.h" +#include "common_plugin.h" + +/* + * Documentation for the Heimdal plugin system is in lib/krb5/plugin.c and + * lib/krb5/krb5-plugin.7. + */ + +/* + * Definitions: + * + * module - a category of plugin module, identified by subsystem + * (e.g., "krb5") + * dso - a library for a module containing a map of plugin + * types to plugins (e.g. "service_locator") + * plugin - a set of callbacks and state that follows the + * common plugin module definition (version, init, fini) + * + * Obviously it would have been clearer to use the term "module" rather than + * "DSO" given there is an internal "DSO", but "module" was already taken... + * + * modules := { module: dsos } + * dsos := { path, dsohandle, plugins-by-name } + * plugins-by-name := { plugin-name: [plug] } + * plug := { ftable, ctx } + */ + +/* global module use, use copy_modules() accessor to access */ +static heim_dict_t __modules; + +static HEIMDAL_MUTEX modules_mutex = HEIMDAL_MUTEX_INITIALIZER; + +static void +copy_modules_once(void *context) +{ + heim_dict_t *modules = (heim_dict_t *)context; + + *modules = heim_dict_create(11); + heim_assert(*modules, "plugin modules array allocation failure"); +} + +/* returns global modules list, refcount +1 */ +static heim_dict_t +copy_modules(void) +{ + static heim_base_once_t modules_once = HEIM_BASE_ONCE_INIT; + + heim_base_once_f(&modules_once, &__modules, copy_modules_once); + + return heim_retain(__modules); +} + +/* returns named module, refcount +1 */ +static heim_dict_t +copy_module(const char *name) +{ + heim_string_t module_name = heim_string_create(name); + heim_dict_t modules = copy_modules(); + heim_dict_t module; + + module = heim_dict_copy_value(modules, module_name); + if (module == NULL) { + module = heim_dict_create(11); + heim_dict_set_value(modules, module_name, module); + } + + heim_release(modules); + heim_release(module_name); + + return module; +} + +/* DSO helpers */ +struct heim_dso { + heim_string_t path; + heim_dict_t plugins_by_name; + void *dsohandle; +}; + +static void HEIM_CALLCONV +dso_dealloc(void *ptr) +{ + struct heim_dso *p = ptr; + + heim_release(p->path); + heim_release(p->plugins_by_name); +#ifdef HAVE_DLOPEN + if (p->dsohandle) + dlclose(p->dsohandle); +#endif +} + +/* returns internal "DSO" for name, refcount +1 */ +static struct heim_dso * +copy_internal_dso(const char *name) +{ + heim_string_t dso_name = HSTR("__HEIMDAL_INTERNAL_DSO__"); + heim_dict_t module = copy_module(name); + struct heim_dso *dso; + + if (module == NULL) + return NULL; + + dso = heim_dict_copy_value(module, dso_name); + if (dso == NULL) { + dso = heim_alloc(sizeof(*dso), "heim-dso", dso_dealloc); + + dso->path = dso_name; + dso->plugins_by_name = heim_dict_create(11); + + heim_dict_set_value(module, dso_name, dso); + } + + heim_release(module); + + return dso; +} + +struct heim_plugin { + heim_plugin_common_ftable_const_p ftable; + void *ctx; +}; + +static void HEIM_CALLCONV +plugin_free(void *ptr) +{ + struct heim_plugin *pl = ptr; + + if (pl->ftable && pl->ftable->fini) + pl->ftable->fini(pl->ctx); +} + +struct heim_plugin_register_ctx { + const void *symbol; + int is_dup; +}; + +static void +plugin_register_check_dup(heim_object_t value, void *ctx, int *stop) +{ + struct heim_plugin_register_ctx *pc = ctx; + struct heim_plugin *pl = value; + + if (pl->ftable == pc->symbol) { + pc->is_dup = 1; + *stop = 1; + } +} + +/** + * Register a plugin symbol name of specific type. + * @param context a Keberos context + * @param module name of plugin module (e.g., "krb5") + * @param name name of plugin symbol (e.g., "krb5_plugin_kuserok") + * @param ftable a pointer to a function pointer table + * @return In case of error a non zero error com_err error is returned + * and the Kerberos error string is set. + * + * @ingroup heim_support + */ + +heim_error_code +heim_plugin_register(heim_context context, + heim_pcontext pcontext, + const char *module, + const char *name, + const void *ftable) +{ + heim_error_code ret; + heim_array_t plugins; + heim_string_t hname; + struct heim_dso *dso; + struct heim_plugin_register_ctx ctx; + + ctx.symbol = ftable; + ctx.is_dup = 0; + + HEIMDAL_MUTEX_lock(&modules_mutex); + + dso = copy_internal_dso(module); + hname = heim_string_create(name); + plugins = heim_dict_copy_value(dso->plugins_by_name, hname); + if (plugins != NULL) + heim_array_iterate_f(plugins, &ctx, plugin_register_check_dup); + else { + plugins = heim_array_create(); + heim_dict_set_value(dso->plugins_by_name, hname, plugins); + } + + ret = 0; + if (!ctx.is_dup) { + /* Note: refactored plugin API only supports common plugin layout */ + struct heim_plugin *pl; + + pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free); + if (pl == NULL) { + ret = heim_enomem(context); + } else { + pl->ftable = ftable; + ret = pl->ftable->init(pcontext, &pl->ctx); + if (ret == 0) { + heim_array_append_value(plugins, pl); + heim_debug(context, 5, "Registered %s plugin", name); + } + heim_release(pl); + } + } + + HEIMDAL_MUTEX_unlock(&modules_mutex); + + heim_release(dso); + heim_release(hname); + heim_release(plugins); + + return ret; +} + +#ifdef HAVE_DLOPEN + +static char * +resolve_origin(const char *di, const char *module) +{ +#ifdef HAVE_DLADDR + Dl_info dl_info; + const char *dname; + char *path, *p; + + if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) != 0 && + strcmp(di, "$ORIGIN") != 0) + return strdup(di); + + di += sizeof("$ORIGIN") - 1; + + if (dladdr(heim_plugin_register, &dl_info) == 0) { + char *s = NULL; + + /* dladdr() failed */ + if (asprintf(&s, LIBDIR "/plugin/%s", module) == -1) + return NULL; + return s; + } + + dname = dl_info.dli_fname; +#ifdef _WIN32 + p = strrchr(dname, '\\'); + if (p == NULL) +#endif + p = strrchr(dname, '/'); + if (p) { + if (asprintf(&path, "%.*s%s", (int) (p - dname), dname, di) == -1) + return NULL; + } else { + if (asprintf(&path, "%s%s", dname, di) == -1) + return NULL; + } + + return path; +#else + char *s = NULL; + + if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) != 0 && + strcmp(di, "$ORIGIN") != 0) + return strdup(di); + if (asprintf(&s, LIBDIR "/plugin/%s", module) == -1) + return NULL; + return s; +#endif /* HAVE_DLADDR */ +} + +#endif /* HAVE_DLOPEN */ + +/** + * Load plugins (new system) for the given module @module from the given + * directory @paths. + * + * Inputs: + * + * @context A heim_context + * @module Name of plugin module (typically "krb5") + * @paths Array of directory paths where to look + */ +void +heim_load_plugins(heim_context context, + const char *module, + const char **paths) +{ +#ifdef HAVE_DLOPEN + heim_string_t s = heim_string_create(module); + heim_dict_t mod, modules; + struct dirent *entry; + heim_error_code ret; + const char **di; + char *dirname = NULL; + DIR *d; +#ifdef _WIN32 + char *plugin_prefix; + size_t plugin_prefix_len; + + if (asprintf(&plugin_prefix, "plugin_%s_", module) == -1) + return; + plugin_prefix_len = (plugin_prefix ? strlen(plugin_prefix) : 0); +#endif + + HEIMDAL_MUTEX_lock(&modules_mutex); + + modules = copy_modules(); + + mod = heim_dict_copy_value(modules, s); + if (mod == NULL) { + mod = heim_dict_create(11); + if (mod == NULL) { + HEIMDAL_MUTEX_unlock(&modules_mutex); + heim_release(s); + heim_release(modules); + heim_debug(context, 5, "Load plugins for module %s failed", module); + return; + } + heim_dict_set_value(modules, s, mod); + } + heim_release(s); + heim_release(modules); + + for (di = paths; *di != NULL; di++) { + free(dirname); + dirname = resolve_origin(*di, module); + if (dirname == NULL) { + heim_debug(context, 10, "Could not resolve %s", *di); + continue; + } + d = opendir(dirname); + if (d == NULL) { + heim_debug(context, 10, "No such directory %s", dirname); + continue; + } + rk_cloexec_dir(d); + + heim_debug(context, 10, "Load plugins for module %s; search %s (%s)", + module, *di, dirname); + + while ((entry = readdir(d)) != NULL) { + char *n = entry->d_name; + char *path = NULL; + heim_string_t spath; + struct heim_dso *p; + + /* skip . and .. */ + if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0'))) + continue; + + ret = 0; +#ifdef _WIN32 + /* + * On Windows, plugins must be loaded from the same directory as + * heimdal.dll (typically the assembly directory) and must have + * the name form "plugin_<module>_<name>.dll". + */ + { + char *ext; + + if (strnicmp(n, plugin_prefix, plugin_prefix_len) != 0) + continue; + ext = strrchr(n, '.'); + if (ext == NULL || stricmp(ext, ".dll") != 0) + continue; + + ret = asprintf(&path, "%s\\%s", dirname, n); + if (ret < 0 || path == NULL) + continue; + } +#endif +#ifdef __APPLE__ + { /* support loading bundles on MacOS */ + size_t len = strlen(n); + if (len > 7 && strcmp(&n[len - 7], ".bundle") == 0) + ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", dirname, n, (int)(len - 7), n); + } +#endif + if (ret < 0 || path == NULL) + ret = asprintf(&path, "%s/%s", dirname, n); + + if (ret < 0 || path == NULL) + continue; + + spath = heim_string_create(n); + if (spath == NULL) { + free(path); + continue; + } + + /* check if already cached */ + p = heim_dict_copy_value(mod, spath); + if (p == NULL) { + p = heim_alloc(sizeof(*p), "heim-dso", dso_dealloc); + if (p) + p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY|RTLD_GROUP); + if (p && p->dsohandle) { + p->path = heim_retain(spath); + p->plugins_by_name = heim_dict_create(11); + heim_dict_set_value(mod, spath, p); + heim_debug(context, 10, "Load plugins for module %s; " + "found DSO %s", module, path); + } + } + heim_release(p); + heim_release(spath); + free(path); + } + closedir(d); + } + free(dirname); + HEIMDAL_MUTEX_unlock(&modules_mutex); + heim_release(mod); +#ifdef _WIN32 + if (plugin_prefix) + free(plugin_prefix); +#endif +#endif /* HAVE_DLOPEN */ +} + +/** + * Unload plugins of the given @module name. + * + * Params: + * + * @module Name of module whose plusins to unload. + */ +void +heim_unload_plugins(heim_context context, const char *module) +{ + heim_string_t sname = heim_string_create(module); + heim_dict_t modules; + + HEIMDAL_MUTEX_lock(&modules_mutex); + + modules = copy_modules(); + heim_dict_delete_key(modules, sname); + + HEIMDAL_MUTEX_unlock(&modules_mutex); + + heim_release(modules); + heim_release(sname); +} + +struct iter_ctx { + heim_context context; + heim_pcontext pcontext; + heim_string_t n; + const struct heim_plugin_data *caller; + int flags; + heim_array_t result; + int32_t (HEIM_LIB_CALL *func)(void *, const void *, void *, void *); + void *userctx; + int32_t ret; + int32_t plugin_no_handle_retval; +}; + +#ifdef HAVE_DLOPEN +/* + * Add plugin from a DSO that exports the plugin structure directly. This is + * provided for backwards compatibility with prior versions of Heimdal, but it + * does not allow a module to export multiple plugins, nor does it allow + * instance validation. + */ +static heim_array_t +add_dso_plugin_struct(heim_context context, + heim_pcontext pcontext, + const char *dsopath, + void *dsohandle, + const char *name) +{ + heim_error_code ret; + heim_plugin_common_ftable_p cpm; + struct heim_plugin *pl; + heim_array_t plugins; + + if (dsohandle == NULL) + return NULL; + + /* suppress error here because we may be looking for a different plugin type */ + cpm = (heim_plugin_common_ftable_p)dlsym(dsohandle, name); + if (cpm == NULL) { + heim_debug(context, 15, "Symbol %s not found in %s", name, dsopath); + return NULL; + } + + heim_warnx(context, "plugin %s uses deprecated loading mechanism", dsopath); + + pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free); + + ret = cpm->init(pcontext, &pl->ctx); + if (ret) { + heim_warn(context, ret, "plugin %s failed to initialize", dsopath); + heim_release(pl); + return NULL; + } + + pl->ftable = cpm; + + plugins = heim_array_create(); + heim_array_append_value(plugins, pl); + heim_release(pl); + + return plugins; +} + +static int +validate_plugin_deps(heim_context context, + const struct heim_plugin_data *caller, + const char *dsopath, + heim_get_instance_func_t get_instance) +{ + size_t i; + + if (get_instance == NULL) { + heim_warnx(context, "plugin %s omitted instance callback", + dsopath); + return FALSE; + } + + for (i = 0; caller->deps[i] != NULL; i++) { + uintptr_t heim_instance, plugin_instance; + + heim_instance = caller->get_instance(caller->deps[i]); + plugin_instance = get_instance(caller->deps[i]); + + if (heim_instance == 0 || plugin_instance == 0) + continue; + + if (heim_instance != plugin_instance) { + heim_warnx(context, "plugin %s library %s linked against different " + "instance of Heimdal (got %zu, us %zu)", + dsopath, caller->deps[i], + plugin_instance, heim_instance); + return FALSE; + } + heim_debug(context, 10, "Validated plugin library dependency %s for %s", + caller->deps[i], dsopath); + } + + return TRUE; +} + +/* + * New interface from Heimdal 8 where a DSO can export a load function + * that can return both a Heimdal instance identifier along with an + * array of plugins. + */ +static heim_array_t +add_dso_plugins_load_fn(heim_context context, + heim_pcontext pcontext, + const struct heim_plugin_data *caller, + const char *dsopath, + void *dsohandle) +{ + heim_error_code ret; + heim_array_t plugins; + heim_plugin_load_t load_fn; + char *sym = NULL; + size_t i; + heim_get_instance_func_t get_instance; + size_t n_ftables; + heim_plugin_common_ftable_cp *ftables; + + if (asprintf(&sym, "%s_plugin_load", caller->name) == -1 || sym == NULL) + return NULL; + + /* suppress error here because we may be looking for a different plugin type */ + load_fn = (heim_plugin_load_t)dlsym(dsohandle, sym); + if (load_fn == NULL) { + heim_debug(context, 15, "Symbol %s not found in %s", sym, dsopath); + free(sym); + return NULL; + } + + ret = load_fn(pcontext, &get_instance, &n_ftables, &ftables); + if (ret) { + heim_warn(context, ret, "plugin %s failed to load", dsopath); + free(sym); + + /* fallback to loading structure directly */ + return add_dso_plugin_struct(context, pcontext, dsopath, + dsohandle, caller->name); + } + + if (!validate_plugin_deps(context, caller, dsopath, get_instance)) { + free(sym); + return NULL; + } + + plugins = heim_array_create(); + + for (i = 0; i < n_ftables; i++) { + heim_plugin_common_ftable_cp cpm = ftables[i]; + struct heim_plugin *pl; + + pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free); + + ret = cpm->init(pcontext, &pl->ctx); + if (ret) { + heim_warn(context, ret, "plugin %s[%zu] failed to initialize", + dsopath, i); + } else { + pl->ftable = cpm; + heim_array_append_value(plugins, pl); + } + heim_release(pl); + } + + heim_debug(context, 15, "DSO %s loaded (%s)", dsopath, sym); + free(sym); + return plugins; +} +#endif /* HAVE_DLOPEN */ + +static void +reduce_by_version(heim_object_t value, void *ctx, int *stop) +{ + struct iter_ctx *s = ctx; + struct heim_plugin *pl = value; + + if (pl->ftable && pl->ftable->minor_version >= s->caller->min_version) + heim_array_append_value(s->result, pl); +} + +static void +search_modules(heim_object_t key, heim_object_t value, void *ctx) +{ + struct iter_ctx *s = ctx; + struct heim_dso *p = value; + heim_array_t plugins = heim_dict_copy_value(p->plugins_by_name, s->n); + +#ifdef HAVE_DLOPEN + if (plugins == NULL && p->dsohandle) { + const char *path = heim_string_get_utf8(p->path); + + plugins = add_dso_plugins_load_fn(s->context, + s->pcontext, + s->caller, + path, + p->dsohandle); + if (plugins) { + heim_dict_set_value(p->plugins_by_name, s->n, plugins); + heim_debug(s->context, 5, "Loaded %zu %s %s plugin%s from %s", + heim_array_get_length(plugins), + s->caller->module, s->caller->name, + heim_array_get_length(plugins) > 1 ? "s" : "", + path); + } + } +#endif /* HAVE_DLOPEN */ + + if (plugins) { + heim_array_iterate_f(plugins, s, reduce_by_version); + heim_release(plugins); + } +} + +static void +eval_results(heim_object_t value, void *ctx, int *stop) +{ + struct heim_plugin *pl = value; + struct iter_ctx *s = ctx; + + if (s->ret != s->plugin_no_handle_retval) + return; + + s->ret = s->func(s->pcontext, pl->ftable, pl->ctx, s->userctx); + if (s->ret != s->plugin_no_handle_retval + && !(s->flags & HEIM_PLUGIN_INVOKE_ALL)) + *stop = 1; +} + +/** + * Run plugins for the given @module (e.g., "krb5") and @name (e.g., + * "kuserok"). Specifically, the @func is invoked once per-plugin with + * four arguments: the @context, the plugin symbol value (a pointer to a + * struct whose first three fields are the same as common_plugin_ftable), + * a context value produced by the plugin's init method, and @userctx. + * + * @func should unpack arguments for a plugin function and invoke it + * with arguments taken from @userctx. @func should save plugin + * outputs, if any, in @userctx. + * + * All loaded and registered plugins are invoked via @func until @func + * returns something other than @nohandle. Plugins that have nothing to + * do for the given arguments should return the same value as @nohandle. + * + * Inputs: + * + * @context A heim_context + * @pcontext A context for the plugin, such as a krb5_context + * @module Name of module (typically "krb5") + * @name Name of pluggable interface (e.g., "kuserok") + * @min_version Lowest acceptable plugin minor version number + * @flags Flags (none defined at this time) + * @nohandle Flags (none defined at this time) + * @userctx Callback data for the callback function @func + * @func A callback function, invoked once per-plugin + * + * Outputs: None, other than the return value and such outputs as are + * gathered by @func. + */ +heim_error_code +heim_plugin_run_f(heim_context context, + heim_pcontext pcontext, + const struct heim_plugin_data *caller, + int flags, + int32_t nohandle, + void *userctx, + int32_t (HEIM_LIB_CALL *func)(void *, const void *, void *, void *)) +{ + heim_string_t m = heim_string_create(caller->module); + heim_dict_t modules, dict = NULL; + struct iter_ctx s; + + s.context = context; + s.pcontext = pcontext; + s.caller = caller; + s.n = heim_string_create(caller->name); + s.flags = flags; + s.result = heim_array_create(); + s.func = func; + s.userctx = userctx; + s.plugin_no_handle_retval = nohandle; + s.ret = nohandle; + + HEIMDAL_MUTEX_lock(&modules_mutex); + + /* Get loaded plugins */ + modules = copy_modules(); + dict = heim_dict_copy_value(modules, m); + + /* Add loaded plugins to s.result array */ + if (dict) + heim_dict_iterate_f(dict, &s, search_modules); + + /* We don't need to hold modules_mutex during plugin invocation */ + HEIMDAL_MUTEX_unlock(&modules_mutex); + + /* Invoke loaded plugins */ + heim_array_iterate_f(s.result, &s, eval_results); + + heim_release(s.result); + heim_release(s.n); + heim_release(dict); + heim_release(m); + heim_release(modules); + + return s.ret; +} diff --git a/lib/base/string.c b/lib/base/string.c index 35ea2182ba3e..5e79e00b18cb 100644 --- a/lib/base/string.c +++ b/lib/base/string.c @@ -36,7 +36,7 @@ #include "baselocl.h" #include <string.h> -static void +static void HEIM_CALLCONV string_dealloc(void *ptr) { heim_string_t s = ptr; @@ -73,11 +73,11 @@ string_cmp(void *a, void *b) return strcmp(a, b); } -static unsigned long +static uintptr_t string_hash(void *ptr) { const char *s = ptr; - unsigned long n; + uintptr_t n; for (n = 0; *s; ++s) n += *s; @@ -153,7 +153,8 @@ heim_string_create_with_bytes(const void *data, size_t len) s = _heim_alloc_object(&_heim_string_object, len + 1); if (s) { - memcpy(s, data, len); + if (len) + memcpy(s, data, len); ((char *)s)[len] = '\0'; } return s; @@ -182,7 +183,7 @@ heim_string_create_with_format(const char *fmt, ...) if (ret < 0 || str == NULL) return NULL; - s = heim_string_ref_create(str, string_dealloc); + s = heim_string_ref_create(str, free); if (s == NULL) free(str); return s; @@ -238,7 +239,7 @@ heim_string_t __heim_string_constant(const char *_str) { static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER; - static heim_base_once_t once; + static heim_base_once_t once = HEIM_BASE_ONCE_INIT; static heim_dict_t dict = NULL; heim_string_t s, s2; diff --git a/lib/base/test_base.c b/lib/base/test_base.c index 250708df2ef4..cc875d7f8b60 100644 --- a/lib/base/test_base.c +++ b/lib/base/test_base.c @@ -53,6 +53,7 @@ #include <sys/stat.h> #ifndef WIN32 #include <sys/file.h> +#include <locale.h> #endif #ifdef HAVE_IO_H #include <io.h> @@ -63,8 +64,9 @@ #include <fcntl.h> #include "baselocl.h" +#include "heimbase-atomics.h" -static void +static void HEIM_CALLCONV memory_free(heim_object_t obj) { } @@ -238,32 +240,324 @@ test_json(void) "{ \"k1\" : \"s1\", \"k2\" : \"s2\" }", "{ \"k1\" : [\"s1\", \"s2\", \"s3\"], \"k2\" : \"s3\" }", "{ \"k1\" : {\"k2\":\"s1\",\"k3\":\"s2\",\"k4\":\"s3\"}, \"k5\" : \"s4\" }", - "[ \"v1\", \"v2\", [\"v3\",\"v4\",[\"v 5\",\" v 7 \"]], -123456789, " - "null, true, false, 123456789, \"\"]", + ("[ \"v1\", \"v2\", [\"v3\",\"v4\",[\"v 5\",\" v 7 \"]], -123456789, " + "null, true, false, 123456789, \"\"]"), " -1" }; char *s; size_t i, k; - heim_object_t o, o2; + heim_object_t o, o2, o3; heim_string_t k1 = heim_string_create("k1"); o = heim_json_create("\"string\"", 10, 0, NULL); heim_assert(o != NULL, "string"); heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid"); heim_assert(strcmp("string", heim_string_get_utf8(o)) == 0, "wrong string"); + o2 = heim_json_copy_serialize(o, 0, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); heim_release(o); + /* + * Test string escaping: + * + * - C-like must-escapes + * - ASCII control character must-escapes + * - surrogate pairs + * + * We test round-tripping. First we parse, then we serialize, then parse, + * then compare the second parse to the first for equality. + * + * We do compare serialized forms in spite of their not being canonical. + * That means that some changes to serialization can cause failures here. + */ + o = heim_json_create("\"" + "\\b\\f\\n\\r\\t" /* ASCII C-like escapes */ + "\x1e" /* ASCII control character w/o C-like escape */ + "\\u00e1" /* á */ + "\\u07ff" + "\\u0801" + "\\u8001" + "\\uD834\\udd1e" /* U+1D11E, as shown in RFC 7159 */ + "\"", 10, 0, NULL); + heim_assert(o != NULL, "string"); + heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid"); + heim_assert(strcmp( + "\b\f\n\r\t" + "\x1e" + "\xc3\xa1" + "\xdf\xbf" + "\xe0\xA0\x81" + "\xe8\x80\x81" + "\xf0\x9d\x84\x9e", heim_string_get_utf8(o)) == 0, "wrong string"); + o2 = heim_json_copy_serialize(o, + HEIM_JSON_F_STRICT | + HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL); + heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001Eá߿ࠁ老\\uD834\\uDD1E\"", + heim_string_get_utf8(o2)) == 0, + "JSON encoding changed; please check that it is till valid"); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); + heim_release(o); + + o = heim_json_create("\"" + "\\b\\f\\n\\r\\t" /* ASCII C-like escapes */ + "\x1e" /* ASCII control character w/o C-like escape */ + "\xc3\xa1" + "\xdf\xbf" + "\xe0\xa0\x81" + "\xE8\x80\x81" + "\\uD834\\udd1e" /* U+1D11E, as shown in RFC 7159 */ + "\"", 10, 0, NULL); + heim_assert(o != NULL, "string"); + heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid"); + heim_assert(strcmp( + "\b\f\n\r\t" + "\x1e" + "\xc3\xa1" + "\xdf\xbf" + "\xe0\xA0\x81" + "\xe8\x80\x81" + "\xf0\x9d\x84\x9e", heim_string_get_utf8(o)) == 0, "wrong string"); + o2 = heim_json_copy_serialize(o, + HEIM_JSON_F_STRICT | + HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL); + heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001Eá߿ࠁ老\\uD834\\uDD1E\"", + heim_string_get_utf8(o2)) == 0, + "JSON encoding changed; please check that it is till valid"); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); + heim_release(o); + + /* + * Test HEIM_JSON_F_ESCAPE_NON_ASCII. + * + * Also test that we get escaped non-ASCII because we're in a not-UTF-8 + * locale, since we setlocale(LC_ALL, "C"), so we should escape non-ASCII + * by default. + */ + o = heim_json_create("\"" + "\\b\\f\\n\\r\\t" /* ASCII C-like escapes */ + "\x1e" /* ASCII control character w/o C-like escape */ + "\xc3\xa1" + "\xdf\xbf" + "\xe0\xa0\x81" + "\xE8\x80\x81" + "\\uD834\\udd1e" /* U+1D11E, as shown in RFC 7159 */ + "\"", 10, 0, NULL); + heim_assert(o != NULL, "string"); + heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid"); + heim_assert(strcmp( + "\b\f\n\r\t" + "\x1e" + "\xc3\xa1" + "\xdf\xbf" + "\xe0\xA0\x81" + "\xe8\x80\x81" + "\xf0\x9d\x84\x9e", heim_string_get_utf8(o)) == 0, "wrong string"); + o2 = heim_json_copy_serialize(o, + HEIM_JSON_F_STRICT | + HEIM_JSON_F_ESCAPE_NON_ASCII, NULL); + heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001E\\u00E1\\u07FF\\u0801\\u8001" + "\\uD834\\uDD1E\"", + heim_string_get_utf8(o2)) == 0, + "JSON encoding changed; please check that it is till valid"); + heim_release(o2); + o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL); + heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001E\\u00E1\\u07FF\\u0801\\u8001" + "\\uD834\\uDD1E\"", + heim_string_get_utf8(o2)) == 0, + "JSON encoding changed; please check that it is till valid"); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); + heim_release(o); + + /* Test rejection of unescaped ASCII control characters */ + o = heim_json_create("\"\b\\f\"", 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(o == NULL, "strict parse accepted bad input"); + o = heim_json_create("\"\b\x1e\"", 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(o == NULL, "strict parse accepted bad input"); + + o = heim_json_create("\"\b\\f\"", 10, 0, NULL); + heim_assert(o != NULL, "string"); + heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid"); + heim_assert(strcmp("\b\f", heim_string_get_utf8(o)) == 0, "wrong string"); + o2 = heim_json_copy_serialize(o, + HEIM_JSON_F_STRICT | + HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL); + heim_assert(strcmp("\"\\b\\f\"", heim_string_get_utf8(o2)) == 0, + "JSON encoding changed; please check that it is till valid"); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); + heim_release(o); + + /* Test bogus backslash escape */ + o = heim_json_create("\"" + "\\ " + "\"", 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(o == NULL, "malformed string accepted"); + o = heim_json_create("\"" + "\\ " + "\"", 10, 0, NULL); + heim_assert(o != NULL, "malformed string rejected (not strict)"); + heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid"); + heim_assert(strcmp(" ", heim_string_get_utf8(o)) == 0, "wrong string"); + o2 = heim_json_copy_serialize(o, + HEIM_JSON_F_STRICT | + HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL); + heim_assert(strcmp("\" \"", heim_string_get_utf8(o2)) == 0, + "JSON encoding changed; please check that it is till valid"); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); + heim_release(o); + + /* Test truncated surrogate encoding (bottom code unit) */ + o = heim_json_create("\"" + "\xE8\x80\x81" + "\\uD834\\udd" + "\"", 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(o == NULL, "malformed string accepted"); + o = heim_json_create("\"" + "\xE8\x80\x81" + "\\uD834\\udd" + "\"", 10, 0, NULL); + heim_assert(o != NULL, "malformed string rejected (not strict)"); + heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid"); + heim_assert(strcmp( + "\xe8\x80\x81" + "\\uD834\\udd", heim_string_get_utf8(o)) == 0, "wrong string"); + o2 = heim_json_copy_serialize(o, + HEIM_JSON_F_STRICT | + HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL); + heim_assert(strcmp("\"老\\\\uD834\\\\udd\"", + heim_string_get_utf8(o2)) == 0, + "JSON encoding changed; please check that it is till valid"); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); + heim_release(o); + + /* Test truncated surrogate encodings (top code unit) */ + o = heim_json_create("\"" + "\xE8\x80\x81" + "\\uD83" + "\"", 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(o == NULL, "malformed string accepted"); + o = heim_json_create("\"" + "\xE8\x80\x81" + "\\uD83" + "\"", 10, 0, NULL); + heim_assert(o != NULL, "malformed string rejected (not strict)"); + heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid"); + heim_assert(strcmp( + "\xe8\x80\x81" + "\\uD83", heim_string_get_utf8(o)) == 0, "wrong string"); + o2 = heim_json_copy_serialize(o, + HEIM_JSON_F_STRICT | + HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL); + heim_assert(strcmp("\"老\\\\uD83\"", + heim_string_get_utf8(o2)) == 0, + "JSON encoding changed; please check that it is till valid"); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); + heim_release(o); + + /* + * Test handling of truncated UTF-8 multi-byte sequences. + */ + o = heim_json_create("\"" + "\xE8\x80" + "\"", 10, 0, NULL); + heim_assert(o != NULL, "malformed string rejected (not strict)"); + heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid"); + heim_assert(strcmp("\xe8\x80", + heim_string_get_utf8(o)) == 0, "wrong string"); + o2 = heim_json_copy_serialize(o, + HEIM_JSON_F_STRICT | + HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL); + heim_assert(o2 == NULL, "malformed string serialized"); + o2 = heim_json_copy_serialize(o, HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(o3 == NULL, "malformed string accepted (not strict)"); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(strcmp("\xe8\x80", + heim_string_get_utf8(o3)) == 0, "wrong string"); + heim_release(o3); + heim_release(o2); + heim_release(o); + + /* Test handling of unescaped / embedded newline */ + o = heim_json_create("\"\n\"", 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(o == NULL, "malformed string accepted (strict)"); + o = heim_json_create("\"\n\"", 10, 0, NULL); + heim_assert(o != NULL, "malformed string rejected (not strict)"); + heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid"); + heim_assert(strcmp("\n", heim_string_get_utf8(o)) == 0, "wrong string"); + o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL); + heim_assert(o2 != NULL, "string not serialized"); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(o3 != NULL, "string not accepted"); + heim_assert(strcmp("\n", heim_string_get_utf8(o3)) == 0, "wrong string"); + heim_release(o3); + heim_release(o2); + heim_release(o); + + /* Test handling of embedded NULs (must decode as data, not string) */ + o = heim_json_create("\"\\u0000\"", 10, HEIM_JSON_F_STRICT, NULL); + heim_assert(o != NULL, "string with NULs rejected"); + heim_assert(heim_get_tid(o) == heim_data_get_type_id(), "data-tid"); + heim_assert(heim_data_get_length(o) == 1, "wrong data length"); + heim_assert(((const char *)heim_data_get_ptr(o))[0] == '\0', + "wrong data NUL"); + o2 = heim_json_copy_serialize(o, 0, NULL); + heim_assert(o2 != NULL, "data not serialized"); + heim_release(o2); + heim_release(o); + + /* + * Note that the trailing ']' is not part of the JSON text (which is just a + * string). + */ o = heim_json_create(" \"foo\\\"bar\" ]", 10, 0, NULL); heim_assert(o != NULL, "string"); heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid"); heim_assert(strcmp("foo\"bar", heim_string_get_utf8(o)) == 0, "wrong string"); + o2 = heim_json_copy_serialize(o, 0, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); heim_release(o); o = heim_json_create(" { \"key\" : \"value\" }", 10, 0, NULL); heim_assert(o != NULL, "dict"); heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid"); + o2 = heim_json_copy_serialize(o, 0, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); heim_release(o); + /* + * heim_json_eq() can't handle dicts with dicts as keys, so we don't check + * for round-tripping here + */ o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", " "{ \"k3\" : \"s4\" } : -1 }", 10, 0, NULL); heim_assert(o != NULL, "dict"); @@ -281,6 +575,11 @@ test_json(void) o2 = heim_dict_copy_value(o, k1); heim_assert(heim_get_tid(o2) == heim_string_get_type_id(), "string-tid"); heim_release(o2); + o2 = heim_json_copy_serialize(o, 0, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); heim_release(o); o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", 10, 0, NULL); @@ -289,6 +588,11 @@ test_json(void) o2 = heim_dict_copy_value(o, k1); heim_assert(heim_get_tid(o2) == heim_dict_get_type_id(), "dict-tid"); heim_release(o2); + o2 = heim_json_copy_serialize(o, 0, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); heim_release(o); o = heim_json_create("{ \"k1\" : 1 }", 10, 0, NULL); @@ -297,26 +601,51 @@ test_json(void) o2 = heim_dict_copy_value(o, k1); heim_assert(heim_get_tid(o2) == heim_number_get_type_id(), "number-tid"); heim_release(o2); + o2 = heim_json_copy_serialize(o, 0, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); heim_release(o); o = heim_json_create("-10", 10, 0, NULL); heim_assert(o != NULL, "number"); heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid"); + o2 = heim_json_copy_serialize(o, 0, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); heim_release(o); o = heim_json_create("99", 10, 0, NULL); heim_assert(o != NULL, "number"); heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid"); + o2 = heim_json_copy_serialize(o, 0, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); heim_release(o); o = heim_json_create(" [ 1 ]", 10, 0, NULL); heim_assert(o != NULL, "array"); heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid"); + o2 = heim_json_copy_serialize(o, 0, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); heim_release(o); o = heim_json_create(" [ -1 ]", 10, 0, NULL); heim_assert(o != NULL, "array"); heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid"); + o2 = heim_json_copy_serialize(o, 0, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); heim_release(o); for (i = 0; i < (sizeof (j) / sizeof (j[0])); i++) { @@ -325,6 +654,11 @@ test_json(void) fprintf(stderr, "Failed to parse this JSON: %s\n", j[i]); return 1; } + o2 = heim_json_copy_serialize(o, 0, NULL); + o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL); + heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip"); + heim_release(o3); + heim_release(o2); heim_release(o); /* Simple fuzz test */ for (k = strlen(j[i]) - 1; k > 0; k--) { @@ -494,7 +828,7 @@ dict_db_open(void *plug, const char *dbtype, const char *dbname, if (error) *error = NULL; - if (dbtype && *dbtype && strcmp(dbtype, "dictdb")) + if (dbtype && *dbtype && strcmp(dbtype, "dictdb") != 0) return EINVAL; if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0) return EINVAL; @@ -646,12 +980,13 @@ test_db_iter(heim_data_t k, heim_data_t v, void *arg) vptr = heim_data_get_ptr(v); vlen = heim_data_get_length(v); - if (klen == strlen("msg") && !strncmp(kptr, "msg", strlen("msg")) && - vlen == strlen("abc") && !strncmp(vptr, "abc", strlen("abc"))) + if (klen == strlen("msg") && strncmp(kptr, "msg", strlen("msg")) == 0 && + vlen == strlen("abc") && strncmp(vptr, "abc", strlen("abc")) == 0) *ret &= ~(1); else if (klen == strlen("msg2") && - !strncmp(kptr, "msg2", strlen("msg2")) && - vlen == strlen("FooBar") && !strncmp(vptr, "FooBar", strlen("FooBar"))) + strncmp(kptr, "msg2", strlen("msg2")) == 0 && + vlen == strlen("FooBar") && + strncmp(vptr, "FooBar", strlen("FooBar")) == 0) *ret &= ~(2); else *ret |= 4; @@ -939,11 +1274,94 @@ test_array() return 0; } +/* This function tests only that heimbase-atomics.h compiles */ +static int +test_atomics(void) +{ + heim_base_atomic(void *) tptr; + heim_base_atomic(uint32_t) tu32; + heim_base_atomic(uint64_t) tu64; + + heim_base_atomic_init(&tptr, NULL); + heim_base_atomic_init(&tu32, 0); + heim_base_atomic_init(&tu64, 0); + + if (heim_base_atomic_load(&tptr)) + return 1; + if (heim_base_atomic_load(&tu32)) + return 1; + if (heim_base_atomic_load(&tu64)) + return 1; + + heim_base_atomic_store(&tptr, &tptr); + heim_base_atomic_store(&tu32, 1); + heim_base_atomic_store(&tu64, 1); + + if (heim_base_atomic_load(&tptr) != &tptr) + return 1; + if (heim_base_atomic_load(&tu32) != 1) + return 1; + if (heim_base_atomic_load(&tu64) != 1) + return 1; + + if (heim_base_atomic_inc_32(&tu32) != 2 || + heim_base_atomic_load(&tu32) != 2) + return 1; + if (heim_base_atomic_inc_64(&tu64) != 2 || + heim_base_atomic_load(&tu64) != 2) + return 1; + + if (heim_base_atomic_dec_32(&tu32) != 1 || + heim_base_atomic_load(&tu32) != 1) + return 1; + if (heim_base_atomic_dec_64(&tu64) != 1 || + heim_base_atomic_load(&tu64) != 1) + return 1; + + heim_base_exchange_pointer(&tptr, (void *)&tu32); + if (heim_base_atomic_load(&tptr) != &tu32) + return 1; + heim_base_exchange_32(&tu32, 32); + if (heim_base_atomic_load(&tu32) != 32) + return 1; + heim_base_exchange_64(&tu64, 64); + if (heim_base_atomic_load(&tu64) != 64) + return 1; + + if (heim_base_cas_pointer(&tptr, (void *)&tu32, (void *)&tu64) != &tu32) + return 1; + if (heim_base_cas_pointer(&tptr, (void *)&tu32, (void *)&tptr) != &tu64) + return 1; + if (heim_base_atomic_load(&tptr) != (void *)&tu64) + return 1; + + if (heim_base_cas_32(&tu32, 32, 4) != 32) + return 1; + if (heim_base_cas_32(&tu32, 32, 4) != 4) + return 1; + if (heim_base_atomic_load(&tu32) != 4) + return 1; + + if (heim_base_cas_64(&tu64, 64, 4) != 64) + return 1; + if (heim_base_cas_64(&tu64, 64, 4) != 4) + return 1; + if (heim_base_atomic_load(&tu64) != 4) + return 1; + + return 0; +} + int main(int argc, char **argv) { int res = 0; +#ifndef WIN32 + setlocale(LC_ALL, "C"); + heim_assert(!heim_locale_is_utf8(), "setlocale(LC_ALL, \"C\") failed?"); +#endif + res |= test_memory(); res |= test_mutex(); res |= test_rwlock(); @@ -956,6 +1374,7 @@ main(int argc, char **argv) res |= test_db(NULL, NULL); res |= test_db("json", argc > 1 ? argv[1] : "test_db.json"); res |= test_array(); + res |= test_atomics(); return res ? 1 : 0; } diff --git a/lib/base/version-script.map b/lib/base/version-script.map index 656277e37da2..1ba88992985b 100644 --- a/lib/base/version-script.map +++ b/lib/base/version-script.map @@ -1,4 +1,3 @@ - HEIMDAL_BASE_1.0 { global: _bsearch_file; @@ -6,11 +5,17 @@ HEIMDAL_BASE_1.0 { _bsearch_file_info; _bsearch_file_open; _bsearch_text; - __heim_string_constant; DllMain; + et_heim_error_table; heim_abort; heim_abortv; + heim_add_debug_dest; + heim_add_et_list; + heim_addlog_dest; + heim_addlog_func; + heim_add_warn_dest; heim_alloc; + _heim_alloc_object; heim_array_append_value; heim_array_copy_value; heim_array_create; @@ -19,35 +24,94 @@ HEIMDAL_BASE_1.0 { heim_array_get_length; heim_array_get_type_id; heim_array_get_value; + heim_array_insert_value; heim_array_iterate_f; heim_array_iterate_reverse_f; - heim_array_insert_value; heim_array_set_value; + heim_audit_addkv; + heim_audit_addkv_number; + heim_audit_addkv_object; + heim_audit_addkv_timediff; + heim_audit_setkv_bool; + heim_audit_setkv_number; + heim_audit_setkv_object; + heim_audit_addreason; + heim_audit_getkv; + heim_audit_trail; + heim_audit_vaddkv; + heim_audit_vaddreason; heim_auto_release; heim_auto_release_create; heim_auto_release_drain; heim_base_once_f; + heim_base_mutex; heim_bool_create; heim_bool_val; + heim_clear_error_message; + heim_closelog; heim_cmp; + heim_config_copy; + heim_config_file_free; + heim_config_free_strings; + heim_config_get; + heim_config_get_bool; + heim_config_get_bool_default; + heim_config_get_entry; + heim_config_get_int; + heim_config_get_int_default; + heim_config_get_list; + heim_config_get_next; + heim_config_get_string; + heim_config_get_string_default; + heim_config_get_strings; + heim_config_get_time; + heim_config_get_time_default; + heim_config_parse_dir_multi; + heim_config_parse_file; + heim_config_parse_file_multi; + heim_config_parse_string_multi; + heim_config_vget; + heim_config_vget_bool; + heim_config_vget_bool_default; + heim_config_vget_int; + heim_config_vget_int_default; + heim_config_vget_list; + heim_config_vget_next; + heim_config_vget_string; + heim_config_vget_string_default; + heim_config_vget_strings; + heim_config_vget_time; + heim_config_vget_time_default; + heim_context_free; + heim_context_get_homedir_access; + heim_context_get_log_utc; + heim_context_get_time_fmt; + heim_context_init; + heim_context_set_homedir_access; + heim_context_set_log_utc; + heim_context_set_time_fmt; + _heim_create_type; heim_data_create; - heim_data_ref_create; heim_data_get_data; heim_data_get_length; heim_data_get_ptr; heim_data_get_type_id; + heim_data_ref_create; heim_data_ref_get_type_id; heim_db_begin; heim_db_clone; heim_db_commit; heim_db_copy_value; + heim_db_create; heim_db_delete_key; heim_db_get_type_id; + _heim_db_get_value; heim_db_iterate_f; - heim_db_create; heim_db_register; heim_db_rollback; heim_db_set_value; + heim_debug; + heim_description; heim_dict_copy_value; heim_dict_create; heim_dict_delete_key; @@ -55,40 +119,93 @@ HEIMDAL_BASE_1.0 { heim_dict_get_value; heim_dict_iterate_f; heim_dict_set_value; + heim_enomem; heim_error_append; heim_error_copy_string; - heim_error_create_opt; heim_error_create; - heim_error_createv; heim_error_create_enomem; + heim_error_create_opt; + heim_error_createv; heim_error_get_code; + heim_expand_path_tokens; + heim_expand_path_tokensv; + heim_free_config_files; + heim_free_error_message; + heim_get_debug_dest; + heim_get_default_config_files; + heim_get_error_message; + heim_get_error_string; heim_get_hash; + _heim_get_isa; + _heim_get_isaextra; + heim_get_log_dest; heim_get_tid; + heim_get_warn_dest; + heim_have_debug; + heim_have_error_string; + heim_initlog; + heim_json_copy_serialize; heim_json_create; heim_json_create_with_bytes; - heim_json_copy_serialize; + heim_json_eq; + heim_load_plugins; + heim_locale_is_utf8; + heim_log; + heim_log_msg; + _heim_make_permanent; heim_null_create; heim_number_create; heim_number_get_int; + heim_number_get_long; heim_number_get_type_id; + heim_openlog; + heim_path_copy; heim_path_create; heim_path_delete; heim_path_get; - heim_path_copy; + heim_path_vcopy; heim_path_vcreate; heim_path_vdelete; heim_path_vget; - heim_path_vcopy; + heim_plugin_register; + heim_plugin_run_f; + heim_prepend_config_files; + heim_prepend_config_files_default; + heim_prepend_error_message; heim_release; heim_retain; + heim_set_config_files; + heim_set_debug_dest; + heim_set_error_message; + heim_set_log_dest; + heim_set_warn_dest; heim_show; heim_sorted_text_file_dbtype; + __heim_string_constant; heim_string_create; heim_string_create_with_bytes; heim_string_create_with_format; heim_string_get_type_id; heim_string_get_utf8; heim_string_ref_create; + _heim_type_get_tid; + heim_unload_plugins; + heim_vdebug; + heim_vlog; + heim_vlog_msg; + heim_vprepend_error_message; + heim_vset_error_message; + heim_vwarn; + heim_vwarnx; + heim_w32_delete_key; + heim_w32_getspecific; + heim_w32_key_create; + heim_w32_service_thread_detach; + heim_w32_setspecific; + heim_warn; + heim_warnx; + initialize_heim_error_table; + initialize_heim_error_table_r; local: *; }; diff --git a/lib/base/warn.c b/lib/base/warn.c new file mode 100644 index 000000000000..3dfed48fdac8 --- /dev/null +++ b/lib/base/warn.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 1997 - 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. + */ + +#if defined(_MSC_VER) +# pragma warning(disable: 4646) +# pragma warning(disable: 4716) +#endif + +#include "baselocl.h" +#include <err.h> + +static heim_error_code _warnerr(heim_context context, int do_errtext, + heim_error_code code, int level, const char *fmt, va_list ap) + __attribute__ ((__format__ (__printf__, 5, 0))); + +static heim_error_code +_warnerr(heim_context context, int do_errtext, + heim_error_code code, int level, const char *fmt, va_list ap) +{ + char xfmt[7] = ""; + const char *args[2], **arg; + char *msg = NULL; + const char *err_str = NULL; + heim_error_code ret; + + args[0] = args[1] = NULL; + arg = args; + if(fmt){ + strlcat(xfmt, "%s", sizeof(xfmt)); + if(do_errtext) + strlcat(xfmt, ": ", sizeof(xfmt)); + ret = vasprintf(&msg, fmt, ap); + if(ret < 0 || msg == NULL) + return ENOMEM; + *arg++ = msg; + } + if (do_errtext) { + strlcat(xfmt, "%s", sizeof(xfmt)); + + err_str = heim_get_error_message(context, code); + if (err_str != NULL) { + *arg = err_str; + } else { + *arg= "<unknown error>"; + } + } + + if (context && heim_get_warn_dest(context)) + heim_log(context, heim_get_warn_dest(context), level, xfmt, args[0], + args[1]); + else + warnx(xfmt, args[0], args[1]); + free(msg); + heim_free_error_message(context, err_str); + return 0; +} + +#define FUNC(ETEXT, CODE, LEVEL) \ + heim_error_code ret; \ + va_list ap; \ + va_start(ap, fmt); \ + ret = _warnerr(context, ETEXT, CODE, LEVEL, fmt, ap); \ + va_end(ap); + +#undef __attribute__ +#define __attribute__(X) + +/** + * Log a warning to the log, default stderr, include the error from + * the last failure. + * + * @param context A Kerberos 5 context. + * @param code error code of the last error + * @param fmt message to print + * @param ap arguments + * + * @ingroup heim_error + */ + +heim_error_code +heim_vwarn(heim_context context, heim_error_code code, + const char *fmt, va_list ap) + __attribute__ ((__format__ (__printf__, 3, 0))) +{ + return _warnerr(context, 1, code, 1, fmt, ap); +} + +/** + * Log a warning to the log, default stderr, include the error from + * the last failure. + * + * @param context A Kerberos 5 context. + * @param code error code of the last error + * @param fmt message to print + * + * @ingroup heim_error + */ + +heim_error_code +heim_warn(heim_context context, heim_error_code code, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 3, 4))) +{ + FUNC(1, code, 1); + return ret; +} + +/** + * Log a warning to the log, default stderr. + * + * @param context A Kerberos 5 context. + * @param fmt message to print + * @param ap arguments + * + * @ingroup heim_error + */ + +heim_error_code +heim_vwarnx(heim_context context, const char *fmt, va_list ap) + __attribute__ ((__format__ (__printf__, 2, 0))) +{ + return _warnerr(context, 0, 0, 1, fmt, ap); +} + +/** + * Log a warning to the log, default stderr. + * + * @param context A Kerberos 5 context. + * @param fmt message to print + * + * @ingroup heim_error + */ + +heim_error_code +heim_warnx(heim_context context, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))) +{ + FUNC(0, 0, 1); + return ret; +} |
