gssproxy-v0.8.2/0000755000174300017420000000000013456641744013216 5ustar gitgit00000000000000gssproxy-v0.8.2/BUILD.txt0000644000174300017420000000220413456641744014614 0ustar gitgit00000000000000First off, run: $ autoreconf -f -i Then the usual $ ./configure # and $ make # In order to build gss-proxy the following development packages are needed additionally to a ISO C compiler: autoconf automake docbook-style-xsl doxygen findutils gettext-devel libini_config-devel keyutils-libs-devel krb5-devel libselinux-devel libtool libverto-devel libxml2 libxslt m4 make pkgconfig popt-devel For testing via $ make tests # you additionally need these packages: krb5-server krb5-server-ldap krb5-workstation nss_wrapper openldap-clients openldap-servers socket_wrapper valgrind NOTE: The minimum supported Kerberos version is MIT Kerberos 1.14 as it includes [1] for Microsoft interoperability. gssproxy relies on the interposer mechanism [1] and so there is no version of gssproxy which will work before 1.11. Various other features require newer versions of MIT krb5; patch out at your own risk. [1] https://github.com/krb5/krb5/commit/7e6965ae33338216650384ca559d49e90312087a [2] http://k5wiki.kerberos.org/wiki/Projects/Interposer_Mechanism gssproxy-v0.8.2/COPYING0000644000174300017420000000214013456641744014246 0ustar gitgit00000000000000GSS-PROXY Copyright (C) 2011 Red Hat, Inc. Copyright (C) 2011-2015 the GSS-PROXY contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gssproxy-v0.8.2/Makefile.am0000644000174300017420000002161613456641744015260 0ustar gitgit00000000000000SUBDIRS = . tests #SUBDIRS += po if HAVE_MANPAGES SUBDIRS += man endif # Some old versions of automake don't define builddir builddir ?= . DOXYGEN = @DOXYGEN@ DISTSETUPOPTS = if HAVE_DEBIAN DISTSETUPOPTS += --install-layout=deb endif gssplibdir = $(libdir)/gssproxy gsspconfdir = $(sysconfdir)/gssproxy gssconfdir = $(sysconfdir)/gss localedir = @localedir@ ccpath = @ccpath@ systemdunitdir = @systemdunitdir@ examplesdir= @datarootdir@/examples logpath = @logpath@ pubconfpath = @pubconfpath@ pkgconfigdir = $(libdir)/pkgconfig gpstatedir = @gpstatedir@ gpclidir = @gpstatedir@/clients AM_DISTCHECK_CONFIGURE_FLAGS = \ --with-systemdunitdir='$$(prefix)/$(systemdunitdir)' AM_CPPFLAGS = AM_CFLAGS = AM_LDFLAGS = if WANT_AUX_INFO AM_CFLAGS += -aux-info $@.X endif AM_CFLAGS += -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith \ -Wcast-qual -Wcast-align -Wwrite-strings \ -fstrict-aliasing -Wstrict-aliasing -Werror=strict-aliasing \ -Werror-implicit-function-declaration \ -Werror=format-security -Wextra if BUILD_HARDENING AM_CPPFLAGS += -D_FORTIFY_SOURCE=2 -Wdate-time AM_CFLAGS += -fPIE -fstack-protector-strong AM_LDFLAGS += -fPIE -pie -fPIC -Wl,-z,relro -Wl,-z,now endif dist_pkgconfig_DATA = ACLOCAL_AMFLAGS = -I m4 -I . sbin_PROGRAMS = \ gssproxy check_PROGRAMS = \ cli_srv_comm interposetest gssplib_LTLIBRARIES = \ proxymech.la dist_noinst_SCRIPTS = tests/scripts/dlopen.sh dist_noinst_DATA = ############################### # Global compilation settings # ############################### AM_CPPFLAGS += \ -Wall \ -Iinclude \ -I$(srcdir)/include \ -I$(srcdir) \ -Iinclude \ -I. \ $(POPT_CFLAGS) \ $(GSSAPI_CFLAGS) \ $(INI_CFLAGS) \ -DLIBDIR=\"$(libdir)\" \ -DVARDIR=\"$(localstatedir)\" \ -DSHLIBEXT=\"$(SHLIBEXT)\" \ -DSYSCONFDIR=\"$(sysconfdir)\" \ -DLOCALEDIR=\"$(localedir)\" GSS_PROXY_LIBS = $(POPT_LIBS) $(KRB5_LIBS) $(VERTO_LIBS) $(INI_LIBS) $(GSSAPI_LIBS) $(GSSRPC_LIBS) if BUILD_SELINUX GSS_PROXY_LIBS += $(SELINUX_LIBS) endif if HAVE_CAP GSS_PROXY_LIBS += $(CAP_LIBS) endif GP_RPCGEN_OBJ = rpcgen/gp_rpc_xdr.c rpcgen/gss_proxy_xdr.c rpcgen/gp_xdr.c GP_RPCCLI_OBJ = \ src/client/gpm_display_status.c \ src/client/gpm_accept_sec_context.c \ src/client/gpm_release_handle.c \ src/client/gpm_acquire_cred.c \ src/client/gpm_indicate_mechs.c \ src/client/gpm_import_and_canon_name.c \ src/client/gpm_init_sec_context.c \ src/client/gpm_inquire_context.c \ src/client/gpm_get_mic.c \ src/client/gpm_verify_mic.c \ src/client/gpm_wrap.c \ src/client/gpm_unwrap.c \ src/client/gpm_wrap_size_limit.c \ src/client/gpm_common.c \ src/gp_util.c GP_MECHGLUE_OBJ = \ src/mechglue/gpp_accept_sec_context.c \ src/mechglue/gpp_acquire_cred.c \ src/mechglue/gpp_creds.c \ src/mechglue/gpp_context.c \ src/mechglue/gpp_init_sec_context.c \ src/mechglue/gpp_display_status.c \ src/mechglue/gpp_import_and_canon_name.c \ src/mechglue/gpp_indicate_mechs.c \ src/mechglue/gpp_priv_integ.c \ src/mechglue/gpp_misc.c \ src/mechglue/gss_plugin.c dist_noinst_HEADERS = \ rpcgen/gp_rpc.h \ rpcgen/gp_xdr.h \ rpcgen/gss_proxy.h \ src/gp_rpc_process.h \ src/gp_proxy.h \ src/client/gssapi_gpm.h \ src/gp_common.h \ src/gp_rpc_debug.h \ src/gp_log.h \ src/gp_creds.h \ src/gp_export.h \ src/gp_conv.h \ src/gp_config.h \ src/gp_debug.h \ src/gp_rpc_creds.h \ src/gp_selinux.h \ src/mechglue/gss_plugin.h #################### # Program Binaries # #################### gssproxy_SOURCES = \ src/gp_config.c \ src/gp_init.c \ src/gp_socket.c \ src/gp_workers.c \ src/gp_creds.c \ $(GP_RPCGEN_OBJ) \ src/gp_rpc_debug.c \ src/gp_rpc_process.c \ src/gp_conv.c \ src/gp_export.c \ src/gp_debug.c \ src/gp_log.c \ src/gp_util.c \ src/gp_rpc_accept_sec_context.c \ src/gp_rpc_release_handle.c \ src/gp_rpc_acquire_cred.c \ src/gp_rpc_indicate_mechs.c \ src/gp_rpc_import_and_canon_name.c \ src/gp_rpc_init_sec_context.c \ src/gp_rpc_get_mic.c \ src/gp_rpc_verify_mic.c \ src/gp_rpc_wrap.c \ src/gp_rpc_unwrap.c \ src/gp_rpc_wrap_size_limit.c \ src/gssproxy.c proxymech_la_SOURCES = \ src/gp_conv.c \ $(GP_RPCGEN_OBJ) \ $(GP_RPCCLI_OBJ) \ $(GP_MECHGLUE_OBJ) proxymech_la_CFLAGS = \ $(AM_FLAGS) proxymech_la_LDFLAGS = \ -avoid-version \ -module cli_srv_comm_SOURCES = \ src/gp_conv.c \ src/gp_debug.c \ src/gp_log.c \ $(GP_RPCGEN_OBJ) \ $(GP_RPCCLI_OBJ) \ tests/t_utils.c \ tests/cli_srv_comm.c interposetest_SOURCES = \ src/gp_log.c \ src/gp_debug.c \ tests/t_utils.c \ tests/interposetest.c gssproxy_LDADD = \ $(GSS_PROXY_LIBS) cli_srv_comm_LDADD = \ $(GSS_PROXY_LIBS) interposetest_LDADD = \ $(GSS_PROXY_LIBS) dist_noinst_DATA += \ m4 noinst_PROGRAMS = cli_srv_comm interposetest ################ # TRANSLATIONS # ################ update-po: if HAVE_MANPAGES $(MAKE) -C man update-po endif # $(MAKE) -C po update-po ####################### # Installation Extras # ####################### systemdunit_DATA = if HAVE_SYSTEMD_UNIT systemdunit_DATA += \ systemd/gssproxy.service endif noinst_DATA = \ examples/gssproxy.conf \ examples/24-nfs-server.conf \ examples/80-httpd.conf \ examples/99-nfs-client.conf \ examples/mech edit_cmd = $(SED) \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' \ -e 's|@libdir[@]|$(libdir)|g' \ -e 's|@gpclidir[@]|$(gpclidir)|g' replace_script = \ @rm -f $@ $@.tmp; \ srcdir=''; \ test -f ./$@.in || srcdir=$(srcdir)/; \ $(edit_cmd) $${srcdir}$@.in >$@.tmp; \ mv $@.tmp $@ EXTRA_DIST = \ systemd/gssproxy.service.in \ examples/gssproxy.conf.in \ examples/24-nfs-server.conf.in \ examples/80-httpd.conf.in \ examples/99-nfs-client.conf.in \ examples/mech.in systemd/gssproxy.service: systemd/gssproxy.service.in Makefile @$(MKDIR_P) systemd/ $(replace_script) examples/%.conf: examples/%.conf.in Makefile @$(MKDIR_P) examples/ $(replace_script) examples/mech: examples/mech.in Makefile @$(MKDIR_P) examples/ $(replace_script) installgsspdirs:: mkdir -p \ $(DESTDIR)$(includedir) \ $(DESTDIR)$(libdir) \ $(DESTDIR)$(sbindir) \ $(DESTDIR)$(mandir) \ $(DESTDIR)$(gsspconfdir) \ $(DESTDIR)$(gssconfdir) \ $(DESTDIR)$(logpath) \ $(DESTDIR)$(gpstatedir) \ $(DESTDIR)$(gpclidir) \ $(DESTDIR)$(pubconfpath) if HAVE_DOXYGEN docs: $(DOXYGEN) doxy.config else docs: @echo "Doxygen not installed, cannot generate documentation" @exit 1 endif install-exec-hook: installgsspdirs mkdir -p doc $(DESTDIR)/$(docdir); cp -a doc $(DESTDIR)/$(docdir)/ if HAVE_SYSTEMD_UNIT mkdir -p $(DESTDIR)$(systemdunitdir) endif clean-local: rm -Rf doc rm -Rf testdir CLEANFILES = *.X */*.X */*/*.X \ examples/mech \ examples/gssproxy.conf \ examples/24-nfs-server.conf \ examples/80-httpd.conf \ examples/99-nfs-client.conf \ systemd/gssproxy.service check: all $(check_PROGRAMS) $(srcdir)/tests/runtests.py $(CHECKARGS) tests: check # RPM-related tasks RPMBUILD ?= $(PWD)/rpmbuild dist_noinst_DATA += \ m4 \ contrib/gssproxy.spec.in \ BUILD.txt \ COPYING rpmroot: mkdir -p $(RPMBUILD)/BUILD mkdir -p $(RPMBUILD)/RPMS mkdir -p $(RPMBUILD)/SOURCES mkdir -p $(RPMBUILD)/SPECS mkdir -p $(RPMBUILD)/SRPMS rpms: dist-gzip rpmroot cp $(builddir)/contrib/gssproxy.spec $(RPMBUILD)/SPECS cp $(distdir).tar.gz $(RPMBUILD)/SOURCES cd $(RPMBUILD); \ rpmbuild --define "_topdir $(RPMBUILD)" -ba SPECS/gssproxy.spec if GIT_CHECKOUT prerelease-rpms: cp $(srcdir)/version.m4 $(srcdir)/version.m4.orig sed -e "s/m4_define(\[PRERELEASE_VERSION_NUMBER\], \[.*\])/m4_define(\[PRERELEASE_VERSION_NUMBER\], \[.`date +%Y%m%d.%H%M`.git`git log -1 --pretty=format:%h`\])/" < $(srcdir)/version.m4.orig > $(srcdir)/version.m4 $(MAKE) rpms mv $(srcdir)/version.m4.orig $(srcdir)/version.m4 endif # make srpms will use the old digest algorithm to be compatible # with RHEL5 srpm: dist-gzip rpmroot cp $(builddir)/contrib/gssproxy.spec $(RPMBUILD)/SPECS cp $(distdir).tar.gz $(RPMBUILD)/SOURCES cd $(RPMBUILD); \ rpmbuild --define "_topdir $(RPMBUILD)" \ --define _source_filedigest_algorithm=1 \ -bs SPECS/gssproxy.spec if GIT_CHECKOUT prerelease-srpm: cp $(srcdir)/version.m4 $(srcdir)/version.m4.orig sed -e "s/m4_define(\[PRERELEASE_VERSION_NUMBER\], \[.*\])/m4_define(\[PRERELEASE_VERSION_NUMBER\], \[.`date +%Y%m%d.%H%M`.git`git log -1 --pretty=format:%h`\])/" < $(srcdir)/version.m4.orig > $(srcdir)/version.m4 $(MAKE) srpm mv $(srcdir)/version.m4.orig $(srcdir)/version.m4 endif #################### # Testing # #################### test_proxymech: TMPDIR=tests/scripts/ $(srcdir)/tests/scripts/dlopen.sh ./.libs/proxymech.so || exit 1 gssproxy-v0.8.2/NOTES0000644000174300017420000000365413456641744014041 0ustar gitgit00000000000000 - How to handle mixed proxied and non-proxied credentials for one process? Idea #0: Always use the proxy or no proxy. Period. Use an env var to select mechglue config. Idea #1: Some mechglue magic and minor constraints on the applications. - mechglue needs to allow multiple providers to provide same mechanisms, with GSS_Acquire/Add_cred*() trying all providers for the desired mechanism(s) in order till one works or all fail; - this does not work for GSS_Init/Accept_sec_context() when using the default credential; - for GSS_Init_sec_context() just pick one provider to be first for default credential and let apps that want the other provider acquire a credential handle instead of using the default one (e.g., ssh -o GSSAPIInitiatorCredential=...); - for GSS_Accept_sec_context() declare that all acceptor credentials for any given mechanism must be proxied or not; Idea #2: Use PGSS or GSS-APIv3 so we can have a caller context handle via which to specify mechglue configuration. - SPNEGO (any pseudo-mechanism) should not be proxied, as it will re-enter the mechglue and call the proxy(ies) if needed (or not) as appropriate. - How to pass around ccaches ? We simply don't. 1. For a user, we should probably deny init_sec_context initially, but if we allow it we need to create a ccache like /var/lib/gssproxy/cc/krb5cc_ The user will not have direct access to the cache. 2. For a normal service we will do the same, both accept and init contetx use the configured keytab and the ccache will be in /var/lib/gssproxy/cc/krb5cc_ 3. For a trusted service we do the same as in 2. except when the service asks us to init_sec_context as a user, in that case we will try to use the user's ccache in /run/user//krb5cc, erroring out if it does not exist or is expired. gssproxy-v0.8.2/README.md0000644000174300017420000000221313456641744014473 0ustar gitgit00000000000000This is the gss-proxy project. Documentation lives in the [docs folder of this repository](https://pagure.io/gssproxy/blob/master/f/docs). The goal is to have a GSS-API proxy, with standardizable protocol and a (somewhat portable) reference client and server implementation. There are several motivations for this some of which are: - Kernel-mode GSS-API applications (CIFS, NFS, AFS, ...) need to be able to leave all complexity of GSS\_Init/Accept\_sec\_context() out of the kernel by upcalling to a daemon that does all the dirty work. - Isolation and privilege separation for user-mode applications. For example: letting HTTP servers use but not see the keytabe entries for HTTP/* principals for accepting security contexts. - Possibly an ssh-agent-like SSH agent for GSS credentials -- a gss-agent. gss-proxy uses libverto for dealing with event loops. Note that you need to have at least one libverto event library installed (e.g. libverto-tevent). We have a [mailing list](https://lists.fedorahosted.org/archives/list/gss-proxy@lists.fedorahosted.org/) and an IRC channel (#gssproxy on [Freenode](https://webchat.freenode.net)). gssproxy-v0.8.2/STYLE.txt0000644000174300017420000000055113456641744014660 0ustar gitgit00000000000000We adopt a coding style derived from http://freeipa.org/page/Coding_Style There are minor differences including ut not limited to the following: - due to the fact we interface to GSSAPI which heavily used typedefs they are not actively discouraged in this code base - Initializations and NULL-checks should use the appropriate GSS_C_NO_XXX initializers gssproxy-v0.8.2/build_macros.m40000644000174300017420000000152513456641744016126 0ustar gitgit00000000000000AC_DEFUN([BUILD_WITH_SHARED_BUILD_DIR], [ AC_ARG_WITH([shared-build-dir], [AC_HELP_STRING([--with-shared-build-dir=DIR], [temporary build directory where libraries are installed [$srcdir/sharedbuild]])]) sharedbuilddir="$srcdir/sharedbuild" if test x"$with_shared_build_dir" != x; then sharedbuilddir=$with_shared_build_dir CFLAGS="$CFLAGS -I$with_shared_build_dir/include" CPPFLAGS="$CPPFLAGS -I$with_shared_build_dir/include" LDFLAGS="$LDFLAGS -L$with_shared_build_dir/lib" fi AC_SUBST(sharedbuilddir) ]) AC_DEFUN([BUILD_WITH_AUX_INFO], [ AC_ARG_WITH([aux-info], [AC_HELP_STRING([--with-aux-info], [Build with -aux-info output])]) ]) AM_CONDITIONAL([WANT_AUX_INFO], [test x$with_aux_info = xyes]) gssproxy-v0.8.2/conf_macros.m40000644000174300017420000002466713456641744015770 0ustar gitgit00000000000000AC_DEFUN([WITH_DISTRO_VERSION], [ AC_ARG_WITH([distro-version], [AC_HELP_STRING([--with-distro-version=VERSION], [Distro version number []] ) ] ) AC_DEFINE_UNQUOTED(DISTRO_VERSION, "$with_distro_version", [Distro version number]) ]) AC_DEFUN([WITH_LOG_PATH], [ AC_ARG_WITH([log-path], [AC_HELP_STRING([--with-log-path=PATH], [Where to store log files for gssproxy [/var/log/gssproxy]] ) ] ) config_logpath="\"VARDIR\"/log/gssproxy" logpath="${localstatedir}/log/gssproxy" if test x"$with_log_path" != x; then config_logpath=$with_log_path logpath=$with_log_path fi AC_SUBST(logpath) AC_DEFINE_UNQUOTED(LOG_PATH, "$config_logpath", [Where to store log files for gssproxy]) ]) AC_DEFUN([WITH_PUBCONF_PATH], [ AC_ARG_WITH([pubconf-path], [AC_HELP_STRING([--with-pubconf-path=PATH], [Where to store pubconf files for gssproxy [/etc/gssproxy]] ) ] ) config_pubconfpath="\"SYSCONFDIR\"/gssproxy" pubconfpath="${sysconfdir}/gssproxy" if test x"$with_pubconf_path" != x; then config_pubconfpath=$with_pubconf_path pubconfpath=$with_pubconf_path fi AC_SUBST(pubconfpath) AC_DEFINE_UNQUOTED(PUBCONF_PATH, "$config_pubconfpath", [Where to store pubconf files for gssproxy]) ]) AC_DEFUN([WITH_SOCKET_NAME], [ AC_ARG_WITH([socket-name], [AC_HELP_STRING([--with-socket-name=PATH], [Name of the GSS Proxy socket file [/var/lib/gssproxy/default.sock]] ) ] ) gp_socket_name="\"VARDIR\"/lib/gssproxy/default.sock" socketname="${localstatedir}/lib/gssproxy/default.sock" if test x"$with_socket_name" != x; then gp_socket_name=$with_socket_name socketname=$with_socket_name fi AC_SUBST(socketname) AC_DEFINE_UNQUOTED(GP_SOCKET_NAME, "$gp_socket_name", [The name of the GSS Proxy socket file]) ]) AC_DEFUN([WITH_PID_FILE], [ AC_ARG_WITH([pid-file], [AC_HELP_STRING([--with-pid-file=PATH], [Name of the GSS Proxy pid file [/var/run/gssproxy.pid]] ) ] ) gp_pid_file="\"VARDIR\"/run/gssproxy.pid" pidfile="${localstatedir}/run/gssproxy.pid" if test x"$with_pid_file" != x; then gp_pid_file=$with_pid_file pidfile=$with_pid_file fi AC_SUBST(pidfile) AC_DEFINE_UNQUOTED(GP_PID_FILE, "$gp_pid_file", [The name of the GSS Proxy pid file]) ]) AC_DEFUN([WITH_INITSCRIPT], [ AC_ARG_WITH([initscript], [AC_HELP_STRING([--with-initscript=INITSCRIPT_TYPE], [Type of your init script (systemd|none). [systemd]] ) ] ) default_initscript=systemd if test x"$with_initscript" = x; then with_initscript=$default_initscript fi if test x"$with_initscript" = xsystemd || \ test x"$with_initscript" = xnone; then initscript=$with_initscript else AC_MSG_ERROR([Illegal value -$with_initscript- for option --with-initscript]) fi AM_CONDITIONAL([HAVE_SYSTEMD_UNIT], [test x"$initscript" = xsystemd]) AC_MSG_NOTICE([Will use init script type: $initscript]) ]) dnl A macro to configure the directory to install the systemd unit files to AC_DEFUN([WITH_SYSTEMD_UNIT_DIR], [ AC_ARG_WITH([systemdunitdir], [ AC_HELP_STRING([--with-systemdunitdir=DIR], [Directory for systemd service files [Auto]] ), ], ) if test x"$with_systemdunitdir" != x; then systemdunitdir=$with_systemdunitdir else systemdunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) if test x"$systemdunitdir" = x; then AC_MSG_ERROR([Could not detect systemd unit directory]) fi fi AC_SUBST(systemdunitdir) ]) AC_DEFUN([WITH_MANPAGES], [ AC_ARG_WITH([manpages], [AC_HELP_STRING([--with-manpages], [Whether to regenerate man pages from DocBook sources [yes]] ) ], [], with_manpages=yes ) if test x"$with_manpages" = xyes; then HAVE_MANPAGES=1 AC_SUBST(HAVE_MANPAGES) fi ]) AM_CONDITIONAL([BUILD_MANPAGES], [test x$with_manpages = xyes]) AC_DEFUN([WITH_XML_CATALOG], [ AC_ARG_WITH([xml-catalog-path], [AC_HELP_STRING([--with-xml-catalog-path=PATH], [Where to look for XML catalog [/etc/xml/catalog]] ) ] ) SGML_CATALOG_FILES="/etc/xml/catalog" if test x"$with_xml_catalog_path" != x; then SGML_CATALOG_FILES="$with_xml_catalog_path" fi AC_SUBST([SGML_CATALOG_FILES]) ]) AC_DEFUN([WITH_SELINUX], [ AC_ARG_WITH([selinux], [AC_HELP_STRING([--with-selinux], [Whether to build with SELinux support [yes]] ) ], [], with_selinux=yes ) if test x"$with_selinux" = xyes; then HAVE_SELINUX=1 AC_SUBST(HAVE_SELINUX) AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, [Build with SELinux support]) fi AM_CONDITIONAL([BUILD_SELINUX], [test x"$with_selinux" = xyes]) ]) AC_DEFUN([WITH_TEST_DIR], [ AC_ARG_WITH([test-dir], [AC_HELP_STRING([--with-test-dir=PATH], [Directory used for make check temporary files [$builddir]] ) ] ) TEST_DIR=$with_test_dir AC_SUBST(TEST_DIR) AC_DEFINE_UNQUOTED(TEST_DIR, "$with_test_dir", [Directory used for 'make check' temporary files]) ]) AC_ARG_ENABLE([all-experimental-features], [AS_HELP_STRING([--enable-all-experimental-features], [build all experimental features])], [build_all_experimental_features=$enableval], [build_all_experimental_features=no]) AC_DEFUN([WITH_CC_PATH], [ AC_ARG_WITH([cc-path], [AC_HELP_STRING([--with-cc-path=PATH], [Where to store ccache files for gssproxy [/var/run/user/gssproxy]] ) ] ) config_ccpath="\"VARDIR\"/run/user/gssproxy" ccpath="${localstatedir}/run/user/gssproxy" if test x"$with_cc_path" != x; then config_ccpath=$with_cc_path ccpath=$with_cc_path fi AC_SUBST(ccpath) AC_DEFINE_UNQUOTED(CCACHE_PATH, "$config_ccpath", [Where to store ccache files for gssproxy]) ]) AC_DEFUN([WITH_GPSTATE_PATH], [ AC_ARG_WITH([gpstate-path], [AC_HELP_STRING([--with-gpstate-path=PATH], [Where to create default socket for gssproxy [/var/lib/gssproxy]] ) ] ) config_gpstatepath="\"VARDIR\"/lib/gssproxy" gpstatedir="${localstatedir}/lib/gssproxy" if test x"$with_gpstate_path" != x; then config_gpstatepath=$with_gpstate_path gpstatepath=$with_gpstate_path fi AC_SUBST(gpstatedir) AC_DEFINE_UNQUOTED(GPSTATE_PATH, "$config_gpstatepath", [Where to store ccache files for gssproxy]) ]) AC_DEFUN([WITH_GSSIDEBUG], [ AC_ARG_WITH([gssidebug], [AC_HELP_STRING([--with-gssidebug], [Whether to build with interposer debugging support [no]] ) ], [], with_gssidebug=no ) if test x"$with_gssidebug" = xyes; then AC_DEFINE_UNQUOTED(GSSI_DEBUGGING, 1, [Build with interposer debugging support]) fi ]) AC_DEFUN([WITH_GPP_DEFAULT_BEHAVIOR], [ AC_ARG_WITH([gpp_default_behavior], [AC_HELP_STRING([--with-gpp-default-behavior=LOCAL_FIRST|LOCAL_ONLY|REMOTE_FIRST|REMOTE_ONLY], [Which default behavior the gssproxy interposer plugin should use [LOCAL_FIRST]] ) ], [], ) default_behavior=GPP_LOCAL_FIRST default_behavior_env=LOCAL_FIRST if test x"$with_gpp_default_behavior" = x"LOCAL_FIRST"; then AC_MSG_RESULT([Using gssproxy interposer behavior LOCAL_FIRST]) default_behavior=GPP_LOCAL_FIRST default_behavior_env=LOCAL_FIRST elif test x"$with_gpp_default_behavior" = x"LOCAL_ONLY"; then AC_MSG_RESULT([Using gssproxy interposer behavior LOCAL_ONLY]) default_behavior=GPP_LOCAL_ONLY default_behavior_env=LOCAL_ONLY elif test x"$with_gpp_default_behavior" = x"REMOTE_FIRST"; then AC_MSG_RESULT([Using gssproxy interposer behavior REMOTE_FIRST]) default_behavior=GPP_REMOTE_FIRST default_behavior_env=REMOTE_FIRST elif test x"$with_gpp_default_behavior" = x"REMOTE_ONLY"; then AC_MSG_ERROR([REMOTE_ONLY currently not supported]) elif test x"$with_gpp_default_behavior" != x; then AC_MSG_ERROR([unknown gpp default behavior]) fi AC_SUBST(GPP_DEFAULT_BEHAVIOR, $default_behavior_env) AC_DEFINE_UNQUOTED(GPP_DEFAULT_BEHAVIOR, $default_behavior, [Default gssproxy interposer plugin behavior]) ]) AC_DEFUN([WITH_HARDENING], [ AC_ARG_WITH([hardening], [AC_HELP_STRING([--with-hardening], [Whether to add extra hardening flags [no]] ) ], [], with_hardening=no ) AM_CONDITIONAL([BUILD_HARDENING], [test x"$with_hardening" = xyes]) ]) AC_DEFUN([WITH_CAP], [ AC_ARG_WITH([cap], [AC_HELP_STRING([--with-cap], [Whether to build with libcap [no]] ) ], [], with_cap=no ) if test x"$with_cap" = xyes; then HAVE_CAP=1 AC_SUBST(HAVE_CAP) AC_DEFINE_UNQUOTED([HAVE_CAP], [1], [Build with capabilities support]) fi ]) AM_CONDITIONAL([HAVE_CAP], [test x$with_cap = xyes]) gssproxy-v0.8.2/configure.ac0000644000174300017420000002234013456641744015505 0ustar gitgit00000000000000AC_PREREQ(2.59) m4_include([version.m4]) AC_INIT([gssproxy], VERSION_NUMBER, [simo@redhat.com]) m4_ifdef([AC_USE_SYSTEM_EXTENSIONS], [AC_USE_SYSTEM_EXTENSIONS], [AC_GNU_SOURCE]) AC_PROG_CC_C99 CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE" AC_CONFIG_SRCDIR([BUILD.txt]) AM_INIT_AUTOMAKE([-Wall foreign subdir-objects tar-pax]) AM_PROG_CC_C_O AM_PROG_AR AC_PROG_INSTALL LT_INIT([disable-static]) AC_CONFIG_MACRO_DIR([m4]) AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION([0.14]) AC_SUBST([PRERELEASE_VERSION], PRERELEASE_VERSION_NUMBER) AC_DEFINE([PRERELEASE_VERSION], "PRERELEASE_VERSION_NUMBER", [Prerelease version number of package]) AM_CONDITIONAL([GIT_CHECKOUT], [git log -1 &>/dev/null]) m4_pattern_allow([AM_SILENT_RULES]) AM_SILENT_RULES AM_CONDITIONAL([HAVE_GCC], [test "$ac_cv_prog_gcc" = yes]) AC_CHECK_HEADERS(stdint.h dlfcn.h) AC_CONFIG_HEADER(config.h) AC_CHECK_TYPES([errno_t], [], [], [[#include ]]) m4_include([build_macros.m4]) BUILD_WITH_SHARED_BUILD_DIR AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;]])], [AC_DEFINE([HAVE_PTHREAD], [1], [Pthread mutexes available.])], [AC_MSG_WARN([Pthread library not found! Clients will not be thread safe...])]) m4_include([external/platform.m4]) m4_include(conf_macros.m4) WITH_DISTRO_VERSION WITH_CC_PATH WITH_LOG_PATH WITH_PUBCONF_PATH WITH_SOCKET_NAME WITH_PID_FILE WITH_TEST_DIR WITH_MANPAGES WITH_XML_CATALOG WITH_SELINUX WITH_GSSIDEBUG WITH_GPSTATE_PATH WITH_GPP_DEFAULT_BEHAVIOR WITH_HARDENING m4_include([external/pkg.m4]) m4_include([external/libpopt.m4]) m4_include([external/docbook.m4]) m4_include([external/sizes.m4]) m4_include([external/selinux.m4]) m4_include([external/libkeyutils.m4]) m4_include([external/systemd.m4]) m4_include([external/ax_pthread.m4]) PKG_CHECK_MODULES([VERTO], [libverto >= 0.2.2], [have_libverto=1], [have_libverto=]) if test x$have_libverto = x; then AC_MSG_ERROR([Could not find VERTO headers]) fi AC_CHECK_LIB(verto, verto_free,, AC_MSG_ERROR(["Error: libverto is not found or lacks verto_free"])) AC_CHECK_FUNCS(verto_cleanup,, AC_MSG_WARN(["Warning: libverto lacks verto_cleanup function"])) #Check for libini_config PKG_CHECK_MODULES([LIBINI_CONFIG], [ini_config >= 1.2.0], [have_libini_config=1], [have_libini_config=]) if test x$have_libini_config = x; then AC_MSG_WARN([Could not find LIBINI_CONFIG headers]) else INI_CONFIG_CFLAGS="`$PKG_CONFIG --cflags ini_config`" INI_CONFIG_LIBS="`$PKG_CONFIG --libs ini_config`" AC_CHECK_LIB(ini_config, ini_config_file_open, [], [AC_MSG_WARN([ini_config library must support ini_config_file_open])], [$INI_CONFIG_LIBS]) AC_CHECK_LIB(ini_config, ini_config_augment, [], [AC_MSG_WARN([ini_config library must support ini_config_augment])], [$INI_CONFIG_LIBS]) fi if test x$have_libini_config = x1; then INI_CFLAGS="$INI_CONFIG_CFLAGS" INI_LIBS="$INI_CONFIG_LIBS" else AC_MSG_ERROR([ini_config development packages not available]) fi AC_SUBST(INI_LIBS) AC_SUBST(INI_CFLAGS) AC_CHECK_LIB(ref_array, ref_array_destroy, [], [AC_MSG_WARN([ref_array library must support ref_array_destroy])], [$INI_CONFIG_LIBS]) AC_RUN_IFELSE([AC_LANG_SOURCE([[ /* See: https://pagure.io/SSSD/ding-libs/pull-request/3172 */ #include #include #include #include #include #include #include #include static int write_to_file(char *path, char *text) { FILE *f = fopen(path, "w"); int bytes = 0; if (f == NULL) return 1; bytes = fprintf(f, "%s", text); if (bytes != strlen(text)) return 1; return fclose(f); } int main(void) { char base_path[PATH_MAX]; char augment_path[PATH_MAX]; char config_base[] = "[section]\n" "key1 = first\n" "key2 = exists\n"; char config_augment[] = "[section]\n" "key1 = augment\n" "key3 = exists\n"; char *builddir; struct ini_cfgobj *in_cfg, *result_cfg; struct ini_cfgfile *file_ctx; uint32_t merge_flags = INI_MS_DETECT | INI_MS_PRESERVE; int ret; builddir = getenv("builddir"); if (builddir == NULL) { builddir = strdup("."); } snprintf(base_path, PATH_MAX, "%s/tmp_augment_base.conf", builddir); snprintf(augment_path, PATH_MAX, "%s/tmp_augment_augment.conf", builddir); ret = write_to_file(base_path, config_base); if (ret != 0) { ret = 1; goto cleanup; } ret = write_to_file(augment_path, config_augment); if (ret != 0) { goto cleanup; } /* Match only augment.conf */ const char *m_patterns[] = { "^tmp_augment_augment.conf$", NULL }; /* Match all sections */ const char *m_sections[] = { ".*", NULL }; /* Create config collection */ ret = ini_config_create(&in_cfg); if (ret != EOK) goto cleanup; /* Open base.conf */ ret = ini_config_file_open(base_path, 0, &file_ctx); if (ret != EOK) goto cleanup; /* Seed in_cfg with base.conf */ ret = ini_config_parse(file_ctx, 1, 0, 0, in_cfg); if (ret != EOK) goto cleanup; /* Update base.conf with augment.conf */ ret = ini_config_augment(in_cfg, builddir, m_patterns, m_sections, NULL, INI_STOP_ON_NONE, 0, INI_PARSE_NOSPACE|INI_PARSE_NOTAB, merge_flags, &result_cfg, NULL, NULL); /* We always expect EEXIST due to DETECT being set. */ if (ret != EEXIST) goto cleanup; ret = 0; cleanup: remove(base_path); remove(augment_path); /* Per autoconf guidelines */ if (ret != 0) ret = 1; return ret; } ]])] ,, [AC_MSG_ERROR(["ini_config library must support extended INI_MS_DETECT. See: https://pagure.io/SSSD/ding-libs/pull-request/3172"])]) AX_PTHREAD(,[AC_MSG_ERROR([Could not find Pthreads support])]) LIBS="$PTHREAD_LIBS $LIBS" LIBS="$PTHREAD_CFLAGS $CFLAGS" CC="$PTHREAD_CC" AC_CHECK_HEADERS([gssapi/gssapi.h],,[AC_MSG_ERROR([Could not find GSSAPI headers])]) PKG_CHECK_MODULES([KRB5_GSSAPI], [krb5-gssapi >= 1.12.0], [have_krb5_gssapi=1], [have_krb5_gssapi=]) if test x$have_krb5_gssapi = x; then AC_MSG_ERROR([Could not find Krb5 / GSSAPI development libraries]) else KRB5_CFLAGS="`$PKG_CONFIG --cflags krb5`" KRB5_LIBS="`$PKG_CONFIG --libs krb5`" GSSAPI_CFLAGS="`$PKG_CONFIG --cflags krb5-gssapi`" GSSAPI_LIBS="`$PKG_CONFIG --libs krb5-gssapi`" fi AC_CHECK_LIB(gssapi_krb5, gss_import_cred,, [AC_MSG_ERROR([GSSAPI library does not support gss_import_cred])], [$GSSAPI_LIBS]) AC_CHECK_LIB(gssapi_krb5, gss_export_cred,, [AC_MSG_ERROR([GSSAPI library does not support gss_export_cred])], [$GSSAPI_LIBS]) AC_CHECK_DECLS([GSS_KRB5_GET_CRED_IMPERSONATOR], [], [], [[#include ]]) AC_CHECK_DECLS([GSS_KRB5_CRED_NO_CI_FLAGS_X], [], [AC_MSG_ERROR([krb5 too old; missing GSS_KRB5_CRED_NO_CI_FLAGS_X (v1.14)])], [[#include ]]) AC_SUBST([KRB5_CFLAGS]) AC_SUBST([KRB5_LIBS]) AC_SUBST([GSSAPI_CFLAGS]) AC_SUBST([GSSAPI_LIBS]) AC_CHECK_HEADERS([gssrpc/rpc.h],,[AC_MSG_ERROR([Could not find GSSRPC headers])]) GSSRPC_LIBS="-lgssrpc" AC_CHECK_LIB(gssrpc, gssrpc_xdrmem_create,, [AC_MSG_ERROR([Failed to find GSSRPC symbols])], [$GSSAPI_LIBS $GSSRPC_LIBS]) AC_SUBST([GSSRPC_LIBS]) WITH_CAP if test x$HAVE_CAP != x; then AC_CHECK_FUNC([prctl],,[AC_MSG_ERROR([Failed to find prctl])]) AC_CHECK_LIB([cap], [cap_set_proc],[CAP_LIBS=-lcap], [AC_MSG_ERROR(["Failed to find libcap symbols"])]) AC_SUBST([CAP_LIBS]) AC_CHECK_HEADERS([sys/capability.h],, [AC_MSG_ERROR([Could not find libcap headers])]) fi AC_CHECK_FUNCS([__secure_getenv secure_getenv]) WITH_INITSCRIPT if test x$initscript = xsystemd; then WITH_SYSTEMD_UNIT_DIR fi if test x$HAVE_MANPAGES != x; then CHECK_XML_TOOLS CHECK_STYLESHEET([$SGML_CATALOG_FILES], [http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl], [Docbook XSL templates]) AC_CHECK_PROG([PO4A],[po4a],[po4a],[no]) fi AM_CONDITIONAL([HAVE_MANPAGES], [test "x$HAVE_MANPAGES" != "x"]) AM_CONDITIONAL([HAVE_PO4A], [test "x$PO4A" != "xno"]) if test x$HAVE_SELINUX != x; then AM_CHECK_SELINUX fi if test x$HAVE_SYSTEMD_UNIT != x; then AM_CHECK_SYSTEMD fi AC_PATH_PROG([DOXYGEN], [doxygen], [false]) AM_CONDITIONAL([HAVE_DOXYGEN], [test x$DOXYGEN != xfalse ]) abs_build_dir=`pwd` AC_DEFINE_UNQUOTED([ABS_BUILD_DIR], ["$abs_build_dir"], [Absolute path to the build directory]) AC_SUBST([abs_builddir], $abs_build_dir) AC_CONFIG_FILES([Makefile tests/Makefile man/Makefile contrib/gssproxy.spec man/gssproxy-mech.8.xml]) AC_OUTPUT gssproxy-v0.8.2/contrib/0000755000174300017420000000000013456641744014656 5ustar gitgit00000000000000gssproxy-v0.8.2/contrib/gssproxy.spec.in0000644000174300017420000000662313456641744020044 0ustar gitgit00000000000000Name: @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ Release: 0@PRERELEASE_VERSION@%{?dist} Summary: GSSAPI Proxy Group: System Environment/Libraries License: MIT URL: http://fedorahosted.org/gss-proxy Source0: http://fedorahosted.org/released/gss-proxy/%{name}-%{version}.tar.gz BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) %global servicename gssproxy %global pubconfpath %{_sysconfdir}/gssproxy %global gpstatedir %{_localstatedir}/lib/gssproxy ### Patches ### ### Dependencies ### Requires: krb5-libs >= 1.12.0 Requires: keyutils-libs Requires: libverto-module-base Requires: libini_config Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units ### Build Dependencies ### BuildRequires: autoconf BuildRequires: automake BuildRequires: libtool BuildRequires: m4 BuildRequires: libxslt BuildRequires: libxml2 BuildRequires: docbook-style-xsl BuildRequires: doxygen BuildRequires: gettext-devel BuildRequires: pkgconfig BuildRequires: krb5-devel >= 1.12.0 BuildRequires: libselinux-devel BuildRequires: keyutils-libs-devel BuildRequires: libini_config-devel >= 1.2.0 BuildRequires: libverto-devel BuildRequires: libcap-devel BuildRequires: popt-devel BuildRequires: findutils BuildRequires: systemd-units %description A proxy for GSSAPI credential handling %prep %setup -q # patch %build autoreconf -f -i %configure \ --with-pubconf-path=%{pubconfpath} \ --with-initscript=systemd \ --disable-static \ --disable-rpath \ --with-gpp-default-behavior=REMOTE_FIRST make %{?_smp_mflags} all make test_proxymech %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} rm -f %{buildroot}%{_libdir}/gssproxy/proxymech.la install -d -m755 %{buildroot}%{_sysconfdir}/gssproxy install -m644 examples/gssproxy.conf %{buildroot}%{_sysconfdir}/gssproxy/gssproxy.conf install -m644 examples/24-nfs-server.conf %{buildroot}%{_sysconfdir}/gssproxy/24-nfs-server.conf install -m644 examples/99-nfs-client.conf %{buildroot}%{_sysconfdir}/gssproxy/99-nfs-client.conf mkdir -p %{buildroot}%{_sysconfdir}/gss/mech.d install -m644 examples/mech %{buildroot}%{_sysconfdir}/gss/mech.d/gssproxy.conf mkdir -p %{buildroot}%{gpstatedir}/rcache %clean rm -rf %{buildroot} %files %defattr(-,root,root,-) %doc COPYING %{_unitdir}/gssproxy.service %{_sbindir}/gssproxy %attr(755,root,root) %dir %{pubconfpath} %attr(755,root,root) %dir %{gpstatedir} %attr(700,root,root) %dir %{gpstatedir}/clients %attr(700,root,root) %dir %{gpstatedir}/rcache %attr(0600,root,root) %config(noreplace) /%{_sysconfdir}/gssproxy/gssproxy.conf %attr(0600,root,root) %config(noreplace) /%{_sysconfdir}/gssproxy/24-nfs-server.conf %attr(0600,root,root) %config(noreplace) /%{_sysconfdir}/gssproxy/99-nfs-client.conf %attr(0644,root,root) %config(noreplace) /%{_sysconfdir}/gss/mech.d/gssproxy.conf %{_libdir}/gssproxy/proxymech.so %{_mandir}/man5/gssproxy.conf.5* %{_mandir}/man8/gssproxy.8* %{_mandir}/man8/gssproxy-mech.8* %post %systemd_post gssproxy.service %preun %systemd_preun gssproxy.service %postun %systemd_postun_with_restart gssproxy.service %changelog * Wed Jan 18 2017 Robbie Harwood - @PACKAGE_VERSION@-0@PRERELEASE_VERSION@ - Resync with fedora * Fri Mar 22 2013 Guenther Deschner - 0.5.1-3 - Various specfile and packaging fixes - Add systemd packaging * Mon Dec 12 2011 Simo Sorce - 0.0.1-0 - Automated build of the gssproxy daemon gssproxy-v0.8.2/docs/0000755000174300017420000000000013456641744014146 5ustar gitgit00000000000000gssproxy-v0.8.2/docs/Apache.md0000644000174300017420000000635313456641744015660 0ustar gitgit00000000000000# Using GSS-Proxy for Apache httpd operation The traditional approach for performing Kerberos authentication in Apache 2.* is to use the mod_auth_gssapi (historically, mod_auth_kerb would have been used) module. When using this module, the Apache process must have read access to a keytab (configured with the ```GssapiCredStore``` option, or the default ```/etc/krb5.keytab```) containing keys for the HTTP service. This is not optimal from a security point of view as all websites can potentially get access to the key material. GSS-Proxy allows to implement privilege separation for the Apache httpd server by removing access to the keytab while preserving Kerberos authentication functionality. This page describes a setup which works starting with Fedora 21 with gssproxy-0.4.1-1.fc21.x86_64, httpd-2.4.16-1.fc21.x86_64, and mod_auth_gssapi-1.3.0-2.fc21.x86_64. It works on similar versions of RHEL as well. ## Setting up GSS-Proxy The proxy will need access to the HTTP/server-name@realm's keytab. When using IPA server, command ``` # ipa service-add HTTP/server-name ``` will create the service principal. On an IPA-enrolled client machine, the ``` # ipa-getkeytab -s $(awk '/^server =/ {print $3}' /etc/ipa/default.conf) -k /etc/gssproxy/http.keytab -p HTTP/$(hostname -f) ``` will retrieve the keytab for the principal. In the following configuration snippet we assume it is stored in ```/etc/gssproxy/http.keytab```. The permissions are set to 400, owner root. The Apache user does not have access to the keytab. We need to know the Apache user numerical id to put it in the configuration file, because GSS-Proxy uses the effective uid to distinguish the services. On my installation, the uid is 48. Symbolic uids are also supported (e.g., "httpd" or "apache"). We add a new section to the gssproxy configuration. To do this, copy the ```examples/80-httpd.conf``` file to ```/etc/gssproxy/80-httpd.conf```. (If you are using a monolithic config file at ```/etc/gssproxy/gssproxy.conf```, make sure the HTTP stanza preceeds any ```allow_any_uid=yes``` sections.) We then start the service: ``` # systemctl restart gssproxy.service # systemctl enable gssproxy.service ``` ## Setting up Apache For Apache, we need to know the location or directory that we want to protect. For testing purposes, we can create a simple file ``` # echo OK > /var/www/html/private ``` and configure mod_auth_gssapi to protect that location: ``` AuthType GSSAPI AuthName "GSSAPI Login" Require valid-user ``` in some ```/etc/httpd/conf.d/*.conf``` file. Note that the path to the keytab is not configured here since it will not be needed -- communication with GSS-Proxy via ```/var/lib/gssproxy/default.sock``` will be used instead. Furthermore, we need to tell the libraries to use the GSS-Proxy - create ```/etc/systemd/system/httpd.service``` with content ``` .include /lib/systemd/system/httpd.service [Service] Environment=GSS_USE_PROXY=1 ``` Reload the configuration: ``` systemctl daemon-reload ``` When we now (re)start the Apache service ``` # systemctl restart httpd.service # systemctl enable httpd.service ``` we should be able to make HTTP requests against the server and they will be authenticated if the client has a valid Kerberos ticket. gssproxy-v0.8.2/docs/Behavior0000644000174300017420000001200513456641744015626 0ustar gitgit00000000000000This little document is about the current behavior of the GSSProxy and the libgssapi interposer plugin. Each is documented separately. Note that the GSSProxy act as server not only for the interposer plugin but also directly for the kernel and potentially for other clients, so the proxy behavior may include additional behaviors not directly available to the libgssapi interposer plugin. NOTE: This document should be upgraded every time we change the proxy or the plugin behavior, however it is my experience that developers forget to do so, so here we have also a timestamp from the last update: 20120910 If it is way too much in the past then something in the actual code may not reflect this document anymore, and please yell at the current maintainer to bring it up to date :-) GSS Proxy ----------------------------------------------------------------------------- Application based behavior: Currently GSS Proxy can be configure to behave differently for each 'user' connecting to it. By user here we really mean euid at this point (we are planning to make it possible to act on a per-application basis provided SELinux is used and each application has a different label that can be trasmitted via SM Rights like calls). The euid is obtained through a SCM Rigths call on the Unix Socket used to talk to the proxy. Each euid can have a config entry which can specify whether the euid is trusted or not and based on specific mechanism some options. The currently only supported mechanism is krb5. For this mechanism a euid specific keytab and ccache can be specified. When a 'user' is considered trusted it means it is allowed to command gss-proxy to act on behalf of another user (for example init a context as a user specified in an optional field in the protocol). The following table represent the current thinking around default/allowed behavior depending on the connecting peer: -------------------------------------------------------------------- | Operation | Initiate | Accept | |Peer | | | -------------------------------------------------------------------- | |With ccache available | Never allow to accept for | |euid not |always try to init | unconfigured euids | |explicitly | | | |configured in |Use default ccache | | |gssproxy.conf |defined in [global] | | | | | | | |Never use keytab | | ---------------|----------------------|----------------------------- | |With ccache available | If keytab is explicitly | |euid X |always try to init | configured always allow to | |(referenced | | try to accept via proxy | | explicitly |When keytab available | | | in a config |init with keytab only | | | section) |if following option | | | |is set to True: | | | |krb5_init_with_keytab | | | |defaults to False | | ---------------|----------------------|----------------------------- | | | If keytab is explicitly | |euid 0 | | configured always allow to | | | | try to accept via proxy | | | | | | | | Allow to fallback to host | | | | keytab if not configured ? | -------------------------------------------------------------------- Credentials: At the moment the GSS Proxy cannot be fully stateless due to limitations in GSSAPI (they are being addressed in MIT 1.11). The gss-proxy keeps a list of credential structs in a ring buffer and sends applications an encrypted token to reference them when the same credential needs to be used across multiple calls. Contexts: Context are always exported to the clients once obtained. Currently both lucid-type contexts and native MIT format contexts are supported. libgssapi Interposer Plugin ----------------------------------------------------------------------------- The Interposer plugin currently tries to perform local only operations first and falls back to try proxy communication if it can't obtain contexts/credentials using local calls. Also an environment variable can be used to change this behavior somewhat (NOTE: still inconsistent while developing the feature). The variable is called: GSSPROXY_BEHAVIOR and allowed values are: LOCAL_ONLY, LOCAL_FIRST, REMOTE_ONLY, REMOTE_FIRST. Currently only a hardcoded set of mechanism is supported, however in future it is planned that the supported set of mechanism to be queried from the gssproxy and/or the configuration file instead. gssproxy-v0.8.2/docs/NFS.md0000644000174300017420000002737013456641744015127 0ustar gitgit00000000000000# Introduction GSS-Proxy has been built as a way to provide an abstraction layer between a GSS client (typically an application) and the credentials being used. This is normally used to perform privilege separation, so that a client application can authenticate/use secure channels via GSSAPI without direct access to keying material. In the NFS server case we extended the GSS-Proxy protocol to be able to talk directly to the in kernel NFSD. The reason we did this was to allow the kernel NFS server to handle big tickets like those containing a MS-PAC payload that may be received by a Microsoft client. For the NFS client case GSS-Proxy allows to operate impersonation behind the scenes so that access to files is "always on" but network level security is maintained. ## NFS Server To use GSS-Proxy with the NFS server you need a recent enough kernel. Anything more recent than 3.10 should work just fine. At the time of writing the choice between using the classic rpc.scvgssd protocol and the new gssproxy protocol is determined *once* at runtime. Once the kernel "chooses" the method it cannot be changed. A reboot will be necessary. The kernel chooses what protocol to use on the first authentication request it receives. It checks if a gssproxy "client" has registered in which case it will bind to use the gssproxy protocol, otherwise it will select the classic rpc.svcgssd protocol and stick to that one. The gssproxy client registers to the kernel by performing 2 actions in the following order: - creates a unix socket for kernel communication in /var/run/gssproxy.sock (this path is hardcoded in the kernel and cannot be changed at this time) - writes 1 byte in the proc file /proc/net/rpc/use-gss-proxy (the client must be ready to accept a connection from the kernel when this is done, as the kernel we check that the socket is available) The simplest GSS-Proxy configuration file to act as a NFSD helper is the following: ``` [gssproxy] [service/nfs-server] mechs = krb5 socket = /run/gssproxy.sock cred_store = keytab:/etc/krb5.keytab trusted = yes kernel_nfsd = yes euid = 0 ``` Let's see what this means, line by line: - The service is named (an arbitrary name so that the admin has a hint of what it is used for) 'nfs-server'. - It limits the GSSAPI supported mechanisms to the 'krb5' mechanism (which is the only one that NFS supports). - It sets the kernel socket name /run/gssproxy.sock (on my system /var/run is a symlink to /run) - It defines that the server's keys are stored in /etc/krb5.keytab (the customary place, you should have both host/ and nfs/ keys in there). - It marks the peer as trusted, which means we will believe the kernel when it says it is connecting on behalf of a specific user even if the credentials (getpeercon) on the other side of the socket say otherwise. - It enables the kernel extensions to the protocol (the context is exported as a lucid context for example, and a list of resolved credentials is returned if authentication succeeds) - It prevents any user but root/kernel (i.e. anything matching euid = 0 as returned by getpeercon) from connecting to the socket. This is pretty much all that is needed to use GSS-Proxy as the user-space helper for the kernel NSFD daemon when GSSAPI authentication of a client is required. ## NFS Client The NFS client case is a little bit more complicated. For starter, at the time of writing the NFS client code in kernel still uses the classic protocol and can only talk with the rpc.gssd service. The interaction with GSS-Proxy, in the client case, is more subtle. The way GSS-Proxy is configured in the client case is pretty much the normal userspace interposition, performed at the libgssapi level. The GSSAPI library need to be configured (either in /etc/gss.conf or /etc/gss.conf.d/gssproxy.conf to load the GSS-Proxy interposer plugin which allows GSS-Proxy to intercept GSSAPI calls and sprinkle a little magic on the operations (as well as performing privilege separation). Example gss.conf ``` # GSS-API mechanism plugins # # Mechanism Name Object Identifier Shared Library Path Other Options gssproxy_v1 2.16.840.1.113730.3.8.15.1 @libdir@/gssproxy/proxymech.so ``` Once this is done rpc.gssd must be started with the following environment variable defined: ``` GSS_USE_PROXY="yes" ``` This instructs the interposer plugin to act, otherwise the interposer plugin will silently fallback to standard GSSAPI behavior. In the client case the GSS-Proxy is usually employed when special cases need to be handled. For example on unmanned systems people may need to use kerberized NFS but no human being is present to manually create a credential cache. In these cases there are 2 options that can be employed based on the admin preference and the local KDC capabilities. NOTE: we assume a modern version of rpc.gssd which drops privileges to the requesting uid before calling GSSAPI. ### Keytab based Client Initiation The GSS-Proxy daemon can easily initiate client credentials automatically based on a keytab. In this case services (for example an Apache server) on the NFS client machines need to access a krb5 protected mount unattended, but they can be given a Kerberos identity (principal) and matching set of keys (stored in keytabs). The following configuration allows this mode of operation: ``` [service/nfs-client] mechs = krb5 cred_store = keytab:/etc/krb5.keytab cred_store = ccache:FILE:/var/lib/gssproxy/clients/krb5cc_%U cred_store = client_keytab:/var/lib/gssproxy/clients/%U.keytab cred_usage = initiate allow_any_uid = yes euid = 0 ``` Let's see what this means, line by line: - The service is named (an arbitrary name so that the admin has a hint of what it is used for) 'nfs-client'. - It limits the GSSAPI supported mechanisms to the 'krb5' mechanism (which is the only one that NFS supports). - It defines that the client's nfs keys are stored in /etc/krb5.keytab (the customary place). - It defines that ccaches created by GSS-Proxy for user's credentials are stored under /var/lib/gssproxy/clients and use a format specifier to have a different name for each user. "%U" translated to the use UID Number. - It defines that keytabs with user's keys are stored under /var/lib/gssproxy/clients and use a format specifier to have a different name for each user. "%U" translated to the use UID Number. - It marks the service usable only for initiation (this is important to avoid allowing any user to receive connections using the system keytab). - It marks the service accessible by any user on the system. Each user will only be able to use their own credentials if any are available. You may notice that an explicit UNIX socket is not configured, this mean this service is exposed on the default socket that is available at /var/lib/gssproxy/default.sock by default. The important bit here is the user's keytabs which are stored under /var/lib/gssproxy/clients. If you have a HTTP service running as user Apache (uid=48) that needs to connect unattended then keys for this user can be stored under /var/lib/gssproxy/clients/48.keytab and this user can now always successfully use kerberized NFS w/o any additional configuration (no cronjobs or other wrapper services necessary). The necessary credentials will be fetched when needed from the KDC, on request. Note that if credentials are not available GSS-Proxy will return control to rpc.gssd and the usual crawling for credentials will be attempted. Note that if you need only one specific service to connect you may also use a more restricted service definition. Example for a system where only the Apache server uses unattended kerberized NFS mounts. ``` [service/apache] mechs = krb5 cred_store = ccache:FILE:/var/lib/gssproxy/clients/krb5cc_apache cred_store = client_keytab:/var/lib/gssproxy/clients/httpd.keytab cred_usage = initiate euid = 48 ``` In this example, only the Apache user (euid = 48) is permitted to attempt to use the GSS-Proxy service, and a fixed name for ccaches and keytab is used instead of a uid number dependent one. ### User Impersonation via Constrained Delegation The previous method works fine if you have a limited set of services running on the machine that need unattended access to a NFS file system, but what to do if you have actual physical users? Handling user's keytabs is annoying, both because it is a liability and because user's passwords can be changed by the users (and when that happens the keytab becomes invalid and needs to be regenerated). It also become cumbersome pretty quickly if there are very many users. Yet in many cases there are users that run long lasting jobs on relatively trusted (by the admin) machines and want to make sure their jobs won't fail after a day because their credentials expire and they suddenly lose access to the NFS share. (Or maybe the job is even batch scheduled so the user never even has a chance to leave credentials on the machine actually running the job). In this cases, if the KDC support Constrained Delegation and specifically the [s4u2self and s4u2proxy protocol extensions](https://ssimo.org/blog/id_011.html), then the administrator can "empower" the NFS client machine to impersonate arbitrary users. NOTE: not all KDCs support these extensions or have way to properly configure them. The 2 main products that do are Microsoft's [Active Directory](http://en.wikipedia.org/wiki/Active_Directory) and the Linux oriented [FreeIPA](http://www.freeipa.org). NOTE: The FreeIPA s4u2proxy implementation can also precisely limit which services can be reached via delegation, for example allow the NFS client machine to obtain tickets exclusively for a specific NFS server, and no other service at all. The following configuration allows this mode of operation: ``` [service/nfs-client] mechs = krb5 cred_store = keytab:/etc/krb5.keytab cred_store = ccache:FILE:/var/lib/gssproxy/clients/krb5cc_%U cred_usage = initiate allow_any_uid = yes impersonate = true euid = 0 ``` Let's see what this means, line by line: - The service is named (an arbitrary name so that the admin has a hint of what it is used for) 'nfs-client'. - It limits the GSSAPI supported mechanisms to the 'krb5' mechanism (which is the only one that NFS supports). - It defines that the client's nfs keys are stored in /etc/krb5.keytab (the customary place, this is required for impersonation). - It defines that ccaches created by GSS-Proxy for user's credentials are stored under /var/lib/gssproxy/clients and use a format specifier to have a different name for each user. "%U" translated to the use UID Number. - It marks the service usable only for initiation (this is important to avoid allowing any user to receive connections using the system keytab). - It marks the service accessible by any user on the system. Each user will only be able to use their own credentials if any are avilable. - It instructs gssproxy to attempt impersonation of the requesting user. So what happens here if a user process tries to walk a mount point ? - the kernel asks rpc.gssd to establish a secure context with the server - rpc.gssd asks GSSAPI for user credentials - the GSSAPI mechproxy module intercepts the requests and forwards it to GSS-Proxy - GSS-Proxy sees that a user requested initiation, it furthermore notices that the matching service allows impersonation. NOTE: that GSS-Proxy at this time uses a quite unsophisticated resolution mechanism to map the user uid to a principal name, it simply gets the user name from the system and tries to use that as the principal name (using the default realm). - GSS-Proxy gets initial credentials with the system keytab, then it tries to acquire a ticket for itself on behalf of the user (s4u2self). - GSS_Proxy finally tries to get a ticket for the target system using the previously obtained ticket as evidence (s4u2proxy). If all goes well the NFS Client now can impersonate the user and successfully connect to the NFS server. gssproxy-v0.8.2/docs/ProtocolDocumentation.md0000644000174300017420000000607113456641744021027 0ustar gitgit00000000000000# GSS-PROXY Protocol Documentation The GSS-PROXY protocol is an RPC protocol based on ONCRPC v2. ## Protocol Definition The protocol definition file is currently maintained in GIT here: https://pagure.io/gssproxy/blob/master/f/x-files/gss_proxy.x The protocol is not stable yet and it is being revised while we progress prototyping client and server code, however the parts used by the Linux kernel driver are considered final and will see no backward incompatible changes. Long term we will probably submit an actual RFC to the IETF to standardize it. ## Extensions There are 2 "extensions" currently being worked on for the Linux kernel client. ### Lucid context export type The Linux Kernel needs a special export and serialize Lucid context. To inform the proxy that this special format should be returned from the gss_accept_se_context call, the client needs to set a gssx_option in call_ctx structure. The option field is set to the string "exported_context_type" The value field is set to the string "linux_lucid_v1" When these fields are set the proxy will return a lucid context formatted accordingly to the Linux Kernel needs instead of a normal exported context buffer. ### Export credentials flag When the accept_sec_context call is completed the Linux Kernel needs to be given a credential set containing all the user uid, gid and secondary gids so that is can properly handle access control. To inform the proxy that this data is needed the clientneeds to set a gssx_option in call_ctx structure. The option field is set to the string "exported_creds_type" The value field is set to the string "linux_creds_v1" When these fields are set the proxy will return a buffer containing the credentials in the accpet_sec_context call response buffer as a gssx_option. The option field is set to the string "linux_creds_v1" The value field contains the creds buffer. This buffer is composed of the fixed fields of 32 bit size and an array of 32 bit values as follows: ` , , , ` ### Krb5 Set Allowed Enctypes Allows to send a krb5 mechanism specific option to set the allowed encryption types for a credential. When a client obtains valid credentials it can attach an option to a gssx_cred_element element of a gssx_cred credential to indicate to the server the desire to limit the allowed encryption types used on said credential before the following call. On the next call, when said credentials are transmitted to the Gss Proxy, the server will probe the received credentials and will attempt a call to gss_krb5_set_allowed_enctypes() using the data provided in the credentials received. The gssx_cred option should be placed in a gssx_cred_element whose mech type is one of the recognized krb5 mechanism OIDs. The option field is set to the string "krb5_set_allowed_enctype_values" The value field contains a buffer of 32bit integers The length of the buffer divided by 4 determines the number of enctypes conveyed. The array of integers is stored in machine dependent byte order, and is cast directly into an array of krb5_enctypes. gssproxy-v0.8.2/docs/README.md0000644000174300017420000001526413456641744015435 0ustar gitgit00000000000000# Getting started with gssproxy In this folder is detailed documentation on how gssproxy works internally (Behavior, ProtocolDocumentation) as well as configuration walkthroughs for specific services (Apache, NFS) and information on our releases (ReleaseProcess, Releases). This document attempts to cover how to use gssproxy with any simple, well-behaved service, and explain in broad strokes what each step accomplishes. All commands should be run as root. ## Background The GSSAPI (Generic Security Services API) is a RFC-standardized interface for applications that wish to use security libraries. Most commonly, GSSAPI is used as an interface to Kerberos, though there are other mechanisms provided as well (e.g., SPNEGO for Single-Sign On on the web; see the Apache docs in this folder for more information on that). When an application uses GSSAPI, typically it will have direct access to its security credentials, and all cryptographic operations are performed in the application's process. This is undesirable, but fortunately gssproxy can help in almost all use cases! gssproxy provides privilege separation to applications using the GSSAPI: the gssproxy daemon runs on the system, holds the application's credentials, and performs operations on behalf of the application. This is completely transparent to the application. However, some configuration is required, which we'll now go through. ## Configuring gssproxy (For a detailed explanation of any of the options described in this section, see the man page for gssproxy.conf (`man 5 gssproxy.conf`).) gssproxy configuration typically lives in **/etc/gssproxy**. On most systems, this directory will already contain a couple files, which can usually be ignored (but will be explained briefly anyway). - **gssproxy.conf** is the main gssproxy configuration file: options affecting operation of the daemon itself live here (e.g., logging levels). - **##-servicename.conf** are the service configuration files (where "##" is a two-digit number; the files are loaded in numeric order by gssproxy). We will be making one of the latter file for your application. Pick a useful name for your service - it will be used in log files. gssproxy configuration snippets are INI-style, so start with a service header: `[service/my_app]`. Then set the mechanisms we wish to allow: `mechs = krb5`. If the application has any keytabs, tell gssproxy about them: `cred_store = keytab:/etc/path/to.keytab`. Then change the user and group permissions on the keytab so that the application can't read them (but gssproxy can): `chown root:root /etc/path/to.keytab`. The next step is to figure out what sets your application apart from others that may be running on the system. gssproxy requires specifying a uid (`euid = appuser`), but if it runs as root, that may not be enough. Fortunately, gssproxy also allows executable name matching. This checks the canonical, full path to an executable. Your program must be a native ELF (or similar) - it cannot be an interpreted program (as that would allow all programs running under the interpreter to access gssproxy as if they were yours). This field is therefore optional. (If that's still not enough to distinguish your application, don't worry - just see the later section on using a custom socket.) In the end, we would end up with a config file that looks a bit like this: ```INI [service/my_app] mechs = krb5 cred_store = keytab:/etc/path/to.keytab euid = appuser program = /usr/local/bin/my_app ``` And tell gssproxy to use the new configuration file: `systemctl try-reload-or-restart gssproxy` ## Configuring the application In order for the application to attempt to use gssproxy, it needs an environment variable set: `GSS_USE_PROXY=yes`. (For more information on environment variable configuration, see the man page for gssproxy-mech - `man 8 gssproxy-mech`.) How this is configured will of course vary application to application. If launching by hand from a shell, just prepend it to the invocation: `GSS_USE_PROXY=yes my_app`. If launching using sysvinit, you'll probably need to edit the invocation in **/etc/init.d/my_app** (there may be a nicer way, but it's system-dependent). If launching using systemd (these instructions assume Fedora), create a new file at **/etc/systemd/system/my_app.service** and make it look like this: ```INI .include /lib/systemd/system/my_app.service [Service] Environment=GSS_USE_PROXY=1 ``` and then reload the systemd state: `systemctl daemon-reload`. From there, (re)start your application and you're off to the races! ## Aside: using a custom socket Normally, gssproxy traffic all runs over the same socket, which typically lives in **/var/lib/gssproxy/default.sock**. However, gssproxy can listen on many sockets, and can distinguish services this way. Using a custom socket for your service is a two part configuration: configure the client, and configure gssproxy. To configure gssproxy, we need to tell the daemon to open another socket. Pick a path (**/var/lib/gssproxy/my_app.sock** is good) and modify the service configuration to use it: `socket = /var/lib/gssproxy/my_app.sock`. Then reload gssproxy's configuration: `systemctl try-reload-or-restart gssproxy`. To configure the client, we need to set another environment variable: `GSSPROXY_SOCKET`. So, we set that in the same way we set `GSS_USE_PROXY` (i.e., `GSSPROXY_SOCKET=/var/lib/gssproxy/my_app.sock`), and launch. ## How to know it's working By far the easiest way to tell is to have a configuration working *without* gssproxy, and then migrate to one *with* gssproxy. If the environment variable is set in the process, and everything keeps working, then you know everything is all set. A similar thing is true of the keytabs: if the application can't read them, but continues to function, then everything is working correctly. That's not always possible, though. In that case, you can look at gssproxy's logs to see connections from your service. First, set `debug_level = 1` in **/etc/gssproxy/gssproxy.conf**, and then have gssproxy reload its configuration (`systemctl reload gssproxy`). Then, in the gssproxy logs (for systemd, this is `journalctl -xfu gssproxy`), you should see lines like: may 08 12:48:52 freeipa.rharwood.biz gssproxy[27144]: [CID 13][2018/05/08 16:48:52]: gp_rpc_execute: executing 6 (GSSX_ACQUIRE_CRED) for service "ipa-httpd", euid: 48,socket: (null) This happens to be taken from a user connecting to the webui on a freeipa instance. It was preceeded at some point by a line announcing the conection. This line says that it's executing a call to (something like) `gss_acquire_cred()` on behalf of the "ipa-httpd" configured service, which is uid 48 (i.e., apache), and that it's assigned ID 13 to this session. gssproxy-v0.8.2/docs/ReleaseProcess.md0000644000174300017420000000232513456641744017411 0ustar gitgit00000000000000# Release Process for GSS-Proxy The process is currently quite simple and requires write access to the project's git repository. # Prepare the sources ## Version and Tag the release - Change in version.m4 with the new version number (ex. 0.1.0) - Create a docs/Releases page with highlights and a full changelog with the command: ``` git shortlog .. ``` - Commit version.m4 and the new Releases page - Test locally with "make rpms" that everything builds fine - Tag the release in master like this: ``` git tag v0.1.0 ``` This will apply the tag to the last commit - Push the tag: ``` git push origin v0.1.0 ``` ## Create a release tarball and SHA hash - Run the following commands (on a git clean tree, please): ``` autoreconf -f -i ./configure make dist make distcheck ``` ... will generate a tarball named like: gssproxy-0.1.0.tar.gz ``` sha512sum gssproxy-0.1.0.tar.gz > gssproxy-0.1.0.tar.gz.sha512sum.txt ``` ... will generate a file with a sha512 checksum ## Publish the release - Upload tarball and checksum to Pagure release page - Copy the content of the new-release page into a mail and announce on the gssproxy mailinglist (https://lists.fedorahosted.org/mailman/listinfo/gss-proxy) gssproxy-v0.8.2/docs/Releases/0000755000174300017420000000000013456641744015711 5ustar gitgit00000000000000gssproxy-v0.8.2/docs/Releases/v0.1.0.md0000644000174300017420000000423413456641744017060 0ustar gitgit00000000000000# Highlights - Various critical fixes for the interposer plugin infrastructure - GSS-Proxy is now fully stateless through the use of new import/export credential functions avalable in MIT 1.11 - Enable new kernel interface to enable GSS-Proxy support in the rpcgss kernel module (still not mainline) - Add environment variables to influence interposer plugin behavior. - Improved tests - Packaging fixes, including systemd unit files fixes # Detailed Changeog Andreas Schneider (1): - Packaging fixes Günther Deschner (16): - build: check for gss_import_cred and gss_export_cred. - Change interposer usage, clients need to set GSS_USE_PROXY=1|YES. - Add example GSS-API mechanism plugins config file. - interposer-plugin: Fix MIT 1.11 gssi_import_sec_context_by_mech symbol name. - mechglue: fix gssi_set_cred_option() arguments. - mechglue: initialize gpp cred_handle in gssi_acquire_cred_with_password(). - Add debug statement when gp_rpc_execute is called. - interpostest: improve debug output when gss_context_time() fails. - mechglue: add trace debugging - Fix gssi_import_sec_context_by_mech() - Fix gssi_context_time for remote calls. - Add various fixes to gssproxy.spec. - Add systemd packaging to gssproxy spec file. - Fix systemd config file for gssproxy. - Make it easier to test gssproxy behavior settings. - Test all possible proxy mode combinations. Simo Sorce (18): - Use new gss_import/export_cred functions - Move master version to 0.0.99 - Fix includes - Add custom implementation of xdr_uint64_t - Use gssrpc instead of system rpc - Add support to get peer's SeLinux context - Remove gssproxy.service - Enable kernel support. - Make socket path a configure option - Write pid file at startup. - Create helper function to wrap token - Use token wrapper in gpp_remote_lo_local_ctx - Fix write_pid debug message - Improve ccache formatting. - Add helper function to check for krb5 oid - Add extension to set allowable enctypes - Add client side support to set allowed enctypes - Set version to 0.1 gssproxy-v0.8.2/docs/Releases/v0.1.2.md0000644000174300017420000000217413456641744017063 0ustar gitgit00000000000000# Highlights - Fixes for various issues found by Coverity - Switch default configuration backend to use libini_config # Detailed Changelog Günther Deschner (4): - Add missing newlines to GPDEBUG statements. - Abstract configuration layer for gssproxy. - Add dinglibs ini configuration detection and backend. - Prefer ini_config library support over iniparser support. Simo Sorce (16): - Fix unchecked return values found by Coverity - Fix copy and paste error found by Coverity - Fix locally dead code error found by coverity - Fix uninizialized variables found by Coverity - Fix dereference before null error fund by Coverity - Fix reporting of wrong error codes - Fix resource leaks found by Coverity - Fix infinite loop due to bad sign of variable - Fix missing break statement found by Coverity - Fix dereference after null checks found by Coverity - Use send() in client library to avoid SIGPIPE - Fix a few more resource leaks - Fix use of unintialized variable - Remove unused variables - Fix tabs in configure.ac - Release 0.1.1 gssproxy-v0.8.2/docs/Releases/v0.2.0.md0000644000174300017420000000346713456641744017070 0ustar gitgit00000000000000# Highlights - Switch to use cred store configuration options - Use the Credential Store and the Keytab Initiation features of Krb5 1.11 to handle trusted services # Detailed Changelog Günther Deschner (11): - Fix memory leak in gp_service_free(). - Silence a configure warning by adding AM_PROG_AR. - When checking for gssrpc libs also add gssapi library paths. - Add --all option to interposetest - Fix potential double-frees in load_services(). - Add gp_config_get_string_array() and an implementation in dinglibs backend. - Use mutivalued "cred_store" parameter, deprecate unused parameters. - Convert gp_config_get_* to return an error. - Add documentation for -d!|--debug in gssproxy manpage. - Add more documentation in the gssproxy.conf manpage. - Add new gssproxy-mech.8 manpage to describe the interposer plugin Simo Sorce (18): - Release 0.1.1 - Replace deprecated libtool macros - Import names as remote name by default. - Add krb5_client_keytab config option - Carefully process desired name based on service - Move string formatting in a separate function - Add generic function to get creds defaults - Use new GSSAPI Credential Store API - Special case client_keytab for root user - Treat credential store as opaquely as possible. - Improve default configuration. - Add support for per-service sockets - Make config functions return actual error codes. - Use const string in config functions - Fix typo in gssproxy.8 manpage - Require nothing less than MIT krb5 1.11.2 - Add gssproxy-mech.8 manpage to spec file - Bump version for 0.2.0 release # Note In order to use all features of this release MIt Krb5 1.11.2 is the minimum required.[[BR]] Additionally a [patch](https://github.com/krb5/krb5/commit/38cc076579888695a5820ceb44fe43020f5b61e1) only available in the development branch and targeted to 1.12 is required gssproxy-v0.8.2/docs/Releases/v0.2.1.md0000644000174300017420000000061613456641744017062 0ustar gitgit00000000000000## Highlights - Minor bugfix release. No new features. ## Detailed Changelog Günther Deschner (6): - Rename option_is_set to gp_boolean_is_true. - Make gp_boolean_is_true non-static. - Use gp_boolean_is_true from interposer plugin's GSS_USE_PROXY check. - Make sure non-root users can access gpstatedir. - Add --with-gpstate-path=PATH configure switch. - Bump version for 0.2.1 release. gssproxy-v0.8.2/docs/Releases/v0.2.2.md0000644000174300017420000000173013456641744017061 0ustar gitgit00000000000000## Highlights - Minor bugfix release. No new features. ## Detailed Changelog Günther Deschner (14): - Add --with-gpp-default-behavior configure switch. - Make error message in read_config() more precise, we fail in that case. - Fix unresolved symbol gp_boolean_is_true() in mechglue plugin. - Fix reallocation in gp_dinglibs_get_string_array(). - Fix typo in gssi_import_name_by_mech(). - Use counter when freeing cred_store configuration. - Don't forget to free gp_ini_context struct in load_config(). - Fix two memleaks in the configuration code. - Add dlopen script to check for unresolved symbols. - Disable gss_export_name_composite() for now. - Overwrite existing GSS_USE_PROXY variable in the server. - Fix documentation of "mechs" parameter in gssproxy.conf(5). - Bump version for 0.2.2 release. - Make sure dlopen.sh is part of the tarball Simo Sorce (3): - Neutralize gssi_export_name. - Fix secondary socket detection at runtime. - Fix socket error handling. gssproxy-v0.8.2/docs/Releases/v0.2.3.md0000644000174300017420000000127013456641744017061 0ustar gitgit00000000000000## Highlights - Minor bugfix release. No new features. ## Detailed Changelog Günther Deschner (10): - Make sure dlopen.sh is part of the tarball - Fix realloc size in gp_get_cred_environment(). - Add "make test_proxymech" to provided specfile. - systemd: Make sure we start before nfs-secure services. - systemd: add require for the nfs kernel modules. - gssproxy: report an error message on event loop failure. - Require libverto-tevent to make sure libverto initialization succeeds - Use verbose ding-libs error reporting when config parsing failed. - Add Requires: libini_config >= 1.0.0.1 to the rpm spec file. - Bump version for 0.2.3 release. Simo Sorce (1): - Fix nfsd socket gssproxy-v0.8.2/docs/Releases/v0.3.0.md0000644000174300017420000000205013456641744017054 0ustar gitgit00000000000000## Highlights - Add support for impersonation (depends on s4u2self/s4u2proxy on the KDC) - Add support for new rpc.gssd mode of operation that forks and changes uid - Add 2 new options allow_any_uid and cred_usage ## Detailed Changelog Günther Deschner (5): - Further improve debugging, mention servicename, socket and euid. - Use right signedness for creds buffer. - Fix resource leak in gpm_accept_sec_context(). - docs: autogenerate proxymech manpage. - docs: Fill in GSSPROXY_BEHAVIOR default setting from configure option. Simo Sorce (14): - Split nfs server and client services - Properly check socket for connection matching. - Coverity fixes. - Add service match using SeLinux Context - Fix selinux option check - Fix LOCAL_FIRST behavior - Fix documentation to match reality - Allow arbitrary users to connect to a service - Add option to specify allowed usage. - Add man page entry for allow_any_uid - Add man page entry for cred_usage - Move uid to name resolution in its own function. - Add impersonation support - Change version to 0.3.0 gssproxy-v0.8.2/docs/Releases/v0.3.1.md0000644000174300017420000000162313456641744017062 0ustar gitgit00000000000000## Highlights - Fix use of gssproxy for client initiation - Add new enforcing and filtering options for context initialization - Fix potential thread safety issues ## Detailed Changelog Günther Deschner (1): - Change version to 0.3.1 Simo Sorce (14): - Preserve requested flags and lifetime - Add way to return regular oid from special - Fix calling gpm_inquire_cred_by_mech - Fix continuations in context establishment calls - Autoinitialize creds on init_sec_context - Try impersonation even when a name is not provided - config: Add code to source flag filters - server: Implement flag filtering enforcement - man: Describe new flag filtering/enforcing options - config: Do not modify const strings - creds: Allow admins to define only client creds - Use secure_getenv in client and mechglue module - Add Thread-safe implementation of strerror() - Use gp_strerror() everywhere instead of strerror() gssproxy-v0.8.2/docs/Releases/v0.4.0.md0000644000174300017420000000326513456641744017066 0ustar gitgit00000000000000## Highlights - Added optional support for running GSS-Proxy as an unprivileged user - Uses new /etc/gss/mech.d configuration directory for gss mechanisms - Kernel related fixes - General bug fixing, many minor errors or incorrect behaviours have been corrected ## Detailed Changelog Günther Deschner (1): - Fix potential segfault in gssi_inquire_context(). Lukas Slebodnik (9): - BUILD: Fix building rpms - BUILD: Tests did not work from parallel directory - BUILD: Change ordering of invoking Makefiles - Suppress clang warning - Wrong coversion function was used - Use defined enum instead of constant - Fix memory leak - BUILD: Fix building with automake 1.15 - Fix few build issues Simo Sorce (24): - Fix config token parsing. - Add support for dropping privileges - Add zero termination when the buffer is a string - Make name conversion more robust to failure - Add utility functions to read()/write() safely - Block parent process until child is initialized. - Properly cleanup mutex on failure. - Add cmdline option to override default socket - Add a test framework for gss-proxy - Zero out the outputs of display_name - Install gssproxy mechanism config in mech.d - Generalize GSS Display Status logger code - Log why acquiring credentials failed. - Do not open logfile multiple times - Prevent a backtrace when a subprocess is not found - Use different env vars bases for gssapi tests - Really use gss-proxy in tests - Fix cast error - Fix error in compiling without SELinux - Default to systemd initscript in rpm spec file - Fix variable replacement in non config files - Properly handle security contexts on error - Suppress exported_composite_name for the kernel - Release version 0.4.0 gssproxy-v0.8.2/docs/Releases/v0.4.1.md0000644000174300017420000000111613456641744017060 0ustar gitgit00000000000000## Highlights - Fixes a bug in generating the example config files - Other minor !Coverity/Clang fixes for issues introduced by the last version ## Detailed Changelog Lukas Slebodnik (4): - Fix warning value stored to 'ret' is never read - Remove unused parameter from get_pipe_name - Include header file with prototypes in implementation module - Suppress warning: use after free Simo Sorce (5): - Switch to use pkg-config for krb5-gssapi - Use pkg-config for krb5 libs too - Fix handling of context initialization - Fix configuration file substitutions - Release version 0.4.1 gssproxy-v0.8.2/docs/Releases/v0.5.0.md0000644000174300017420000000457513456641744017074 0ustar gitgit00000000000000## Highlights - Added support for config directory and config snippets and reload on SIGHUP - Improved debug logging/tracing - Actually implemented the docuemnted krb5_principal option - Numerous bugfixes ## Detailed Changelog Lukas Slebodnik (1): - BUILD: Include gp_rpc_debug.h into tarball Robbie Harwood (24): - Extract generalized selinux context comparison function - Ensure ini_config >= 1.2.0 for `ini_config_augment()` - Fix formatting on noncompliant copyright lines - Allow configdir and configfile to be specified together - getpwman(3) can return NULL without setting errno - Reload config on SIGHUP - Use pkg-config to find libgssapi-krb5.so* - Correct handling of EINTR on read()/write() - Port test suite to python3 - Add tests for reloading on SIGHUP - Log useful message on kernel interface failure - Clear message structure before decoding into it - Fix potential deadlock on socket grab - Fix possible explicit NULL deref in gpm_accept_sec_context - Fix several leaks - Set C standard used to gnu99 - Fix for gss_inquire_attrs_for_mech accepting NULLs - Specify KRB5RCACHEDIR in systemd unit file - Allow symbolic euids in conf files - Remove one layer of abstraction over dinglibs - Add support for config directories - Update man pages for symbolic euids and config snippets - Error on `allow_any_uid` issues - Add HTTP service and move NFS into its own conf file Roland Mainz (3): - Turn on strict aliasing rules - Remove support for iniparse library - Update BUILD.txt Simo Sorce (23): - Fix make clean - Fix const warning in gp_creds.c - Fix const warning that can lead to issues - Improve code in gp_export_gssx_cred() - Add options to specify a debug level - Add higher level debugging for all rpc calls - Add helper to find options in rpc messages - Add gss_acquire_cred_impersonate_name support - Add helpers to store and retrieve encrypted creds - Fix acquiring default credentials - Add acquire test and generally improve tests - Use t_utils.h in all tests. - Appropriately resolve const warnings in tests. - Add impersonate test - Better naming for tests log files - Always use valgrind for gssproxy tests - Since krb5 1.14 inquire_context may return no name - Fix crash bug when reconfiguring service - Fix use after free bug - Implement the krb5_principal option - Add test to verify krb5_principal works - Allow to specify a client name for init tests - Release version 0.5.0 gssproxy-v0.8.2/docs/Releases/v0.5.1.md0000644000174300017420000000057513456641744017071 0ustar gitgit00000000000000## Highlights - Fix bug with export creds that can cause NFS failures - Fix bug with uid/pid/gid changes that can break autofs ## Detailed Changelog Andrew Elble (1): - Fix typo in gp_get_export_creds_type() Robbie Harwood (3): - Fix return check on gp_conv_gssx_to_name - Use new socket if uid, pid, or gid changes - Release version 0.5.1 gssproxy-v0.8.2/docs/Releases/v0.6.0.md0000644000174300017420000000327513456641744017071 0ustar gitgit00000000000000## Highlights - Test suite improvements - Improved tracing and debugging - s4u2proxy and s4u2self controls - Misc. leak fixes ## Detailed Changelog Robbie Harwood (24): - Deploy KDC on LDAP instead of db2 - Package runtests.py in dist tarball - Add configure option for build hardening - Fix NULL deref in gssi_release_name() - Fix NULL deref in gssi_release_cred() - Fix type of argument to gppint_get_def_creds() - Appease clang with memset instead of initializer - Fix two leaks in gp_get_cred_environment() - Fix leak of localname in gp_export_creds_linux() - Fix leak of ach in gp_accept_sec_context() - Remove sysvinit support - Cause `make check` to behave as expected - Split out test library functions - Split out each test into its own file - Separate out test return checking logic - Indent subtests - Lower 30-second timeouts to 10 - Add test for cred store extensions - Include new tests in `make dist` tarball - Default to a MEMORY cred_store ccache - Exercise default ccache behavior in test suite - Sync package spec file with fedora - Raise failures in test suite - Release version 0.6.0 Simo Sorce (15): - Add cred_store support for local calls. - Make sure to pass on request for delegated creds - In acquire_cred_from, probe for remote creds - Always initialize ccache when storing. - Trace the gssproxy process too. - Improved debug options - Fix print_return logic - Use a local keytab for creds encryption - Parse cred_store struct earlier - Make local call static - Add control to permit/deny protocol transition - Control access to constrained delegation - Add more impersonation tests - Fix impersonation tests to work properly - Fix make distcheck gssproxy-v0.8.2/docs/Releases/v0.6.1.md0000644000174300017420000000032413456641744017062 0ustar gitgit00000000000000Highlights: - Fix an error with missing keytab Robbie Harwood (1): - Release version 0.6.1 Simo Sorce (1): - Missing keytab should not be fatal gssproxy-v0.8.2/docs/Releases/v0.6.2.md0000644000174300017420000000021013456641744017055 0ustar gitgit00000000000000Highlights: - Fix a crash bug Robbie Harwood (2): - Fix allocation of cred_store to have two extra slots - Release version 0.6.2 gssproxy-v0.8.2/docs/Releases/v0.7.0.md0000644000174300017420000000257113456641744017070 0ustar gitgit00000000000000Highlights: - RHEL-7 support - Migration to Pagure completed - Support syncing changed credentials Robbie Harwood (13): - Clean up build flags - Detect kerberos.schema on RHEL - Fix behavior when not passed config\_dir on the command line - Document debug\_level option in gssproxy.conf(5) - Fix asprintf(3) call in ensure\_segregated\_ccache() - Appease Coverity - Remove unused variables in t\_acquire.c - Markdown conversion of docs from Trac - Pagure-ify release process - Remove outdated selinux issue from Apache docs - Update Apache docs to match latest releases - Fix broken link in protocol docs - Release version 0.7.0 Simo Sorce (16): - Fix incorrect use of non-null terminated string - Fix another incorrect use of non-null term. string - Always check if we have a remote credential - Fix potential memleak from gpm\_release\_cred - Local vs Remote cred check fixes - Add a helper function to pack options - Add ability to sync creds back on modification - Do not re-export unchanged creds - Rework gpp\_cred\_handle management - Add utility function to compare gssx\_creds - Always request cred sync on init\_sec\_context - If credentials changed try to store them - Change tests to always exercise ccache sycns - Add support for the NO\_CI\_FLAG credentials option - Add test to check setting cred options - Ensure test suite reports failure on traceback gssproxy-v0.8.2/docs/Releases/v0.8.0.md0000644000174300017420000000460313456641744017067 0ustar gitgit00000000000000Highlights: - Add capability to match services based on the name of the executable - Add support for reloading in our systemd unit file - Fix behavior with NFS setup Alexander Scheel (17): - Clarify test suite's logging - Create krb5 config files before setting up LDAP - Fix error reporting in init\_proc\_nfsd - Conditionally reload kernel interface on SIGHUP - Fix most memory leaks - Fix error handling in gp\_config\_from\_dir - Enable debugging for testsuites - Check for TERM in external environment before setting in tests - Update test and krb5 dependency list in BUILD.txt - [client] Switch to non-blocking sockets - [server] Add detailed request logging - Don't leak mech\_type when CONTINUE\_NEEDED from init\_sec\_context - Add call to verto\_cleanup() - Simplify setting NONBLOCK on socket - Fix handling of non-EPOLLIN/EPOLLOUT events - Fix error handling in gpm\_send\_buffer/gpm\_recv\_buffer - Fix silent crash with duplicate config sections Robbie Harwood (27): - Add mailing list and IRC links to README - Turn on -Wextra - Fix unused variables - Fix mismatched sign comparisons - Fix error checking on get\_impersonator\_fallback() - Remove gpm\_release\_ctx() to fix double unlock - Update systemd file - Mark selinux config attributes as deprecated - Require krb5 >= 1.14 for GSS\_KRB5\_CRED\_NO\_CI\_FLAGS\_X - Fix segfault when no config files are present - Include header for writev() - Make proc file failure loud but nonfatal - Appease gcc-7's fallthrough detection - Support matching based on executable name - Tolerate NULL pointers in gp\_same - Fix double free of popt context when querying version - Fix potential free of non-heap address - Prevent uninitialized read in error path of XDR contexts - Handle outdated encrypted ccaches - Update Apache docs to reflect config file split - Only empty FILE ccaches when storing remote creds - Fix a krb5 error code being returned as GSS major - Fix unused variable - Separate cred and ccache manipulation in gpp\_store\_remote\_creds() - Properly locate credentials in collection caches in mechglue - Properly initialize ccaches before storing into them - Update pagure release process Simo Sorce (8): - Properly renew expired credentials - Change impersonator check code - Allow connection to self when impersonator set - Add Client ID to debug messages - Fix memory leak - Style fixes - Emit debug on queue errors - Do not call gpm\_grab\_sock() twice gssproxy-v0.8.2/docs/Releases/v0.8.1.md0000644000174300017420000000224213456641744017065 0ustar gitgit00000000000000Highlights: - Fix explicit NULL derereference with tokens of certain enctypes - Always choose highest requested debug level - Fixes for running as unprivileged user Alexander Scheel (1): - Permit testing sans Valgrind Robbie Harwood (12): - Clarify debug and debug\_level in man pages - Always choose highest requested debug level - Add docs link to README.md - Add generic service README - Rename README.style -> STYLE.txt so pagure stops picking it up - Don't leak sock\_ctx if verto\_add\_io() fails - Update docs to reflect actual behavior of krb5\_principal - Sort options in man pages - Check for test-relevant executables early in suite - Always initialize out cred in gp\_import\_gssx\_cred() - Handle gss\_import\_cred() failure when importing gssx creds - Include length when using krb5\_c\_decrypt() Simo Sorce (5): - Always use the encype we selected - Use pthread keys for thread local storage - Close epoll fd within the lock - Add a safety timeout to epoll - Reorder functions Stanislav Levin (4): - Fix typo about pid-file - Retain CAP\_SYS\_PTRACE when running as unpriviliged - Make build with capabilities optional - Move run\_as\_user check out of drop\_privs() gssproxy-v0.8.2/docs/Releases/v0.8.2.md0000644000174300017420000000014613456641744017067 0ustar gitgit00000000000000Highlights: - Fix a crash bug in v0.8.1 Simo Sorce (1): - Change the way we handle encrypted buffers gssproxy-v0.8.2/examples/0000755000174300017420000000000013456641744015034 5ustar gitgit00000000000000gssproxy-v0.8.2/examples/24-nfs-server.conf.in0000644000174300017420000000023013456641744020620 0ustar gitgit00000000000000[service/nfs-server] mechs = krb5 socket = /run/gssproxy.sock cred_store = keytab:/etc/krb5.keytab trusted = yes kernel_nfsd = yes euid = 0 gssproxy-v0.8.2/examples/80-httpd.conf.in0000644000174300017420000000023013456641744017653 0ustar gitgit00000000000000[service/HTTP] mechs = krb5 cred_store = keytab:/etc/gssproxy/http.keytab cred_store = ccache:/var/lib/gssproxy/clients/krb5cc_%U euid = apache gssproxy-v0.8.2/examples/99-nfs-client.conf.in0000644000174300017420000000036613456641744020616 0ustar gitgit00000000000000[service/nfs-client] mechs = krb5 cred_store = keytab:/etc/krb5.keytab cred_store = ccache:FILE:@gpclidir@/krb5cc_%U cred_store = client_keytab:@gpclidir@/%U.keytab cred_usage = initiate allow_any_uid = yes trusted = yes euid = 0 gssproxy-v0.8.2/examples/gssproxy.conf.in0000644000174300017420000000001313456641744020200 0ustar gitgit00000000000000[gssproxy] gssproxy-v0.8.2/examples/mech.in0000644000174300017420000000027313456641744016302 0ustar gitgit00000000000000# GSS-API mechanism plugins # # Mechanism Name Object Identifier Shared Library Path Other Options gssproxy_v1 2.16.840.1.113730.3.8.15.1 @libdir@/gssproxy/proxymech.so gssproxy-v0.8.2/external/0000755000174300017420000000000013456641744015040 5ustar gitgit00000000000000gssproxy-v0.8.2/external/ax_pthread.m40000644000174300017420000003044013456641744017422 0ustar gitgit00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also link it with them as well. e.g. you should link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threads programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 17 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) AC_MSG_RESULT($ax_pthread_ok) if test x"$ax_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" ;; *-darwin*) ax_pthread_flags="-pthread $ax_pthread_flags" ;; esac if test x"$ax_pthread_ok" = xno; then for flag in $ax_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) if test x"$ax_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($ax_pthread_ok) if test "x$ax_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $attr; return attr /* ; */])], [attr_name=$attr; break], []) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *-osf* | *-hpux*) flag="-D_REENTRANT";; *solaris*) if test "$GCC" = "yes"; then flag="-D_REENTRANT" else flag="-mt -D_REENTRANT" fi ;; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], ax_cv_PTHREAD_PRIO_INHERIT, [ AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$ax_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD gssproxy-v0.8.2/external/docbook.m40000644000174300017420000000171013456641744016721 0ustar gitgit00000000000000dnl Checks for tools needed to generate manual pages AC_DEFUN([CHECK_XML_TOOLS], [ AC_PATH_PROG([XSLTPROC], [xsltproc]) if test ! -x "$XSLTPROC"; then AC_MSG_ERROR([Could not find xsltproc]) fi AC_PATH_PROG([XMLLINT], [xmllint]) if test ! -x "$XMLLINT"; then AC_MSG_ERROR([Could not find xmllint]) fi AC_PATH_PROG([XMLCATALOG], [xmlcatalog]) if test ! -x "$XMLCATALOG"; then AC_MSG_ERROR([Could not find xmlcatalog]) fi ]) dnl Usage: dnl CHECK_STYLESHEET_URI(FILE, URI, [FRIENDLY-NAME]) dnl Checks if the XML catalog given by FILE exists and dnl if a particular URI appears in the XML catalog AC_DEFUN([CHECK_STYLESHEET], [ AC_CHECK_FILE($1, [], [AC_MSG_ERROR([could not find XML catalog])]) AC_MSG_CHECKING([for ifelse([$3],,[$2],[$3]) in XML catalog]) if AC_RUN_LOG([$XMLCATALOG --noout "$1" "$2" >&2]); then AC_MSG_RESULT([yes]) else AC_MSG_ERROR([could not find ifelse([$3],,[$2],[$3]) in XML catalog]) fi ]) gssproxy-v0.8.2/external/libkeyutils.m40000644000174300017420000000075413456641744017650 0ustar gitgit00000000000000AC_SUBST(KEYUTILS_LIBS) AC_CHECK_HEADERS([keyutils.h], [AC_CHECK_LIB([keyutils], [add_key], [AC_DEFINE(USE_KEYRING, 1, [Define if the keyring should be used]) KEYUTILS_LIBS="-lkeyutils" ], [AC_MSG_WARN([No usable keyutils library found])] )], [AC_MSG_WARN([keyutils header files are not available])] ) gssproxy-v0.8.2/external/libpopt.m40000644000174300017420000000043413456641744016754 0ustar gitgit00000000000000POPT_OBJ="" AC_SUBST(POPT_OBJ) AC_SUBST(POPT_LIBS) AC_SUBST(POPT_CFLAGS) AC_CHECK_HEADERS([popt.h], [AC_CHECK_LIB(popt, poptGetContext, [ POPT_LIBS="-lpopt" ], [AC_MSG_ERROR([POPT must support poptGetContext])])], [AC_MSG_ERROR([POPT development libraries not installed])] ) gssproxy-v0.8.2/external/pkg.m40000644000174300017420000001205413456641744016065 0ustar gitgit00000000000000# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # # Copyright © 2004 Scott James Remnant . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # # Similar to PKG_CHECK_MODULES, make sure that the first instance of # this or PKG_CHECK_MODULES is called, or make sure to call # PKG_CHECK_EXISTS manually # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_ifval([$2], [$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- m4_define([_PKG_CONFIG], [if test -n "$PKG_CONFIG"; then if test -n "$$1"; then pkg_cv_[]$1="$$1" else PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], [pkg_failed=yes]) fi else pkg_failed=untried fi[]dnl ])# _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])# _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` else $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD ifelse([$4], , [AC_MSG_ERROR(dnl [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT ])], [AC_MSG_RESULT([no]) $4]) elif test $pkg_failed = untried; then ifelse([$4], , [AC_MSG_FAILURE(dnl [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])], [$4]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) ifelse([$3], , :, [$3]) fi[]dnl ])# PKG_CHECK_MODULES gssproxy-v0.8.2/external/platform.m40000644000174300017420000000315013456641744017125 0ustar gitgit00000000000000AC_ARG_WITH([os], [AC_HELP_STRING([--with-os=OS_TYPE], [Type of your operation system (fedora|redhat|suse|gentoo)])] ) osname="" if test x"$with_os" != x ; then if test x"$with_os" = xfedora || \ test x"$with_os" = xredhat || \ test x"$with_os" = xsuse || \ test x"$with_os" = xgentoo || \ test x"$with_os" = xdebian ; then osname=$with_os else AC_MSG_ERROR([Illegal value -$with_os- for option --with-os]) fi fi if test x"$osname" = x ; then if test -f /etc/fedora-release ; then osname="fedora" elif test -f /etc/redhat-release ; then osname="redhat" elif test -f /etc/SuSE-release ; then osname="suse" elif test -f /etc/debian_version ; then osname="debian" elif test -f /etc/gentoo-release ; then osname="gentoo" fi AC_MSG_NOTICE([Detected operating system type: $osname]) fi AM_CONDITIONAL([HAVE_FEDORA], [test x"$osname" = xfedora]) AM_CONDITIONAL([HAVE_REDHAT], [test x"$osname" = xredhat]) AM_CONDITIONAL([HAVE_SUSE], [test x"$osname" = xsuse]) AM_CONDITIONAL([HAVE_DEBIAN], [test x"$osname" = xdebian]) AM_CONDITIONAL([HAVE_GENTOO], [test x"$osname" = xgentoo]) AC_CHECK_MEMBERS([struct ucred.pid, struct ucred.uid, struct ucred.gid], , , [[#include ]]) if test x"$ac_cv_member_struct_ucred_pid" = xyes -a \ x"$ac_cv_member_struct_ucred_uid" = xyes -a \ x"$ac_cv_member_struct_ucred_gid" = xyes ; then AC_DEFINE([HAVE_UCRED], [1], [Define if struct ucred is available]) else AC_MSG_ERROR([struct ucred is not available]) fi gssproxy-v0.8.2/external/selinux.m40000644000174300017420000000200413456641744016765 0ustar gitgit00000000000000dnl A macro to check the availability of SELinux AC_DEFUN([AM_CHECK_SELINUX], [ AC_CHECK_HEADERS(selinux/selinux.h, [AC_CHECK_LIB(selinux, is_selinux_enabled, [SELINUX_LIBS="-lselinux"], [AC_MSG_ERROR([SELinux library is missing])] ) ], [AC_MSG_ERROR([SELinux headers are missing])]) AC_SUBST(SELINUX_LIBS) ]) dnl A macro to check the availability of SELinux management library AC_DEFUN([AM_CHECK_SEMANAGE], [ AC_CHECK_HEADERS(semanage/semanage.h, [AC_CHECK_LIB(semanage, semanage_handle_create, [SEMANAGE_LIBS="-lsemanage"], [AC_MSG_ERROR([libsemanage is missing])] ) ], [AC_MSG_ERROR([libsemanage is missing])]) AC_SUBST(SEMANAGE_LIBS) ]) gssproxy-v0.8.2/external/sizes.m40000644000174300017420000000205513456641744016441 0ustar gitgit00000000000000# Solaris needs HAVE_LONG_LONG defined AC_CHECK_TYPES(long long) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(char) AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) if test $ac_cv_sizeof_long_long -lt 8 ; then AC_MSG_ERROR([SSSD requires long long of 64-bits]) fi AC_CHECK_TYPE(uint_t, unsigned int) AC_CHECK_TYPE(int8_t, char) AC_CHECK_TYPE(uint8_t, unsigned char) AC_CHECK_TYPE(int16_t, short) AC_CHECK_TYPE(uint16_t, unsigned short) if test $ac_cv_sizeof_int -eq 4 ; then AC_CHECK_TYPE(int32_t, int) AC_CHECK_TYPE(uint32_t, unsigned int) elif test $ac_cv_size_long -eq 4 ; then AC_CHECK_TYPE(int32_t, long) AC_CHECK_TYPE(uint32_t, unsigned long) else AC_MSG_ERROR([LIBREPLACE no 32-bit type found]) fi AC_CHECK_TYPE(int64_t, long long) AC_CHECK_TYPE(uint64_t, unsigned long long) AC_CHECK_TYPE(size_t, unsigned int) AC_CHECK_TYPE(ssize_t, int) AC_CHECK_SIZEOF(off_t) AC_CHECK_SIZEOF(size_t) AC_CHECK_SIZEOF(ssize_t) AC_CHECK_TYPE(intptr_t, long long) AC_CHECK_TYPE(uintptr_t, unsigned long long) AC_CHECK_TYPE(ptrdiff_t, unsigned long long) gssproxy-v0.8.2/external/systemd.m40000644000174300017420000000042713456641744016775 0ustar gitgit00000000000000dnl A macro to check presence of systemd on the system AC_DEFUN([AM_CHECK_SYSTEMD], [ PKG_CHECK_EXISTS(systemd, [ HAVE_SYSTEMD=1, AC_SUBST(HAVE_SYSTEMD) ], [AC_MSG_ERROR([Could not detect systemd presence])] ) ]) gssproxy-v0.8.2/man/0000755000174300017420000000000013456641744013771 5ustar gitgit00000000000000gssproxy-v0.8.2/man/Makefile.am0000644000174300017420000001011313456641744016021 0ustar gitgit00000000000000# The following variable is dependent on placement of this file top_builddir = .. ############ # MANPAGES # ############ #Special Rules: export SGML_CATALOG_FILES DOCBOOK_XSLT = http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl XMLLINT_FLAGS = --catalogs --postvalid --nonet --xinclude --noout XSLTPROC_FLAGS = --catalogs --xinclude --nonet man_MANS = \ gssproxy.8 gssproxy.conf.5 gssproxy-mech.8 EXTRA_DIST = $(man_MANS:%=%.xml) $(wildcard $(srcdir)/include/*.xml) SUFFIXES = .1.xml .1 .3.xml .3 .5.xml .5 .8.xml .8 .1.xml.1: $(XMLLINT) $(XMLLINT_FLAGS) $< $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(DOCBOOK_XSLT) $< .3.xml.3: $(XMLLINT) $(XMLLINT_FLAGS) $< $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(DOCBOOK_XSLT) $< .5.xml.5: $(XMLLINT) $(XMLLINT_FLAGS) $< $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(DOCBOOK_XSLT) $< .8.xml.8: $(XMLLINT) $(XMLLINT_FLAGS) $< $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(DOCBOOK_XSLT) $< clean-local: rm -f $(man_MANS) ######################## # MANPAGE TRANSLATIONS # ######################## # #PO4A=@PO4A@ #SED=@SED@ # #PACKAGE_DOC=gssproxy-docs # #POTFILE = po/$(PACKAGE_DOC).pot #PO4A_CONFIG = po/po4a.cfg # ## Extract the list of languages from the po4a config file. #LINGUAS_DIST = `$(SED) -ne 's/^.*\[po4a_langs\] \(.*\)$$/\1/p' $(srcdir)/$(PO4A_CONFIG)` # ## If the user has not defined it let's use the default. #LINGUAS ?= $(LINGUAS_DIST) # #PO4A_COMMON_OPTS = --option doctype=docbook \ # --package-name $(PACKAGE_DOC) \ # --variable builddir=$(CURDIR) \ # --package-version $(PACKAGE_VERSION) \ # --msgid-bugs-address simo@redhat.com \ # --copyright-holder "Red Hat" # #PO4A_BUILD_OPTS = $(PO4A_COMMON_OPTS) --no-backups # #EXTRA_DIST += \ # $(POTFILE)\ # $(PO4A_CONFIG) # #XML_DOC = $(wildcard $(srcdir)/*.xml) $(wildcard $(srcdir)/include/*.xml) # #if HAVE_PO4A # ## FIXME: Use a stamp file until po4a supports them internally. #man.stamp: $(XML_DOC) $(POTFILE) $(PO4A_CONFIG) # cd $(srcdir) && \ # $(PO4A) $(PO4A_BUILD_OPTS) $(PO4A_CONFIG) # touch $@ # #update-po: # cd $(srcdir) && \ # $(PO4A) $(PO4A_BUILD_OPTS) --force $(PO4A_CONFIG) # #dist-hook: man.stamp # if [ -f man.stamp ]; then \ # cp man.stamp $(distdir); \ # for lang in $(LINGUAS_DIST); do \ # cp $(srcdir)/po/$$lang.po $(distdir)/po; \ # $(mkdir_p) $(distdir)/$$lang; \ # cp -r $(builddir)/$$lang $(distdir)/; \ # done; \ # else \ # cp $(srcdir)/man.stamp $(distdir); \ # for lang in $(LINGUAS_DIST); do \ # cp $(srcdir)/po/$$lang.po $(distdir)/po; \ # $(mkdir_p) $(distdir)/$$lang; \ # cp -r $(srcdir)/$$lang $(distdir)/; \ # done; \ # fi # # #clean-local-no: #clean-local-yes: # for lang in $(LINGUAS); do \ # if [ -d $$lang ]; then \ # rm -rf $$lang; \ # fi \ # done # rm -f $(man_MANS) # rm -f man.stamp # #else # #man.stamp: $(XML_DOC) # touch $@ # #clean-local-no: #clean-local-yes: # rm -f $(man_MANS) # rm -f man.stamp # #endif # #clean-local: clean-local-@USE_NLS@ #distclean-local: clean-local-@USE_NLS@ #mostlyclean-local: clean-local-@USE_NLS@ #maintainer-clean-local: clean-local-@USE_NLS@ # ## Generate translated manual pages #all-local: all-local-@USE_NLS@ #all-local-no: #all-local-yes: man.stamp # if [ -z $$recursion ]; then \ # for lang in $(LINGUAS); do \ # if [ -d $$lang ]; then \ # sources=$$(ls -1 $$lang/*.xml); \ # manpages=$$(echo $$sources | $(SED) 's/\.xml//g'); \ # $(MAKE) recursion=1 man_MANS="$$manpages"; \ # fi \ # done \ # fi # #install-data-local: install-data-local-@USE_NLS@ #install-data-local-no: #install-data-local-yes: # for lang in $(LINGUAS); do \ # if [ -d $$lang ]; then \ # sources=$$(ls -1 $$lang/*.xml); \ # manpages=$$(echo $$sources | $(SED) 's/\.xml//g'); \ # $(MAKE) install-man \ # mandir="$(mandir)/$$lang" \ # man_MANS="$$manpages"; \ # fi \ # done # #uninstall-local: uninstall-local-@USE_NLS@ #uninstall-local-no: #uninstall-local-yes: # for lang in $(LINGUAS); do \ # if [ -d $$lang ]; then \ # sources=$$(ls -1 $$lang/*.xml); \ # manpages=$$(echo $$sources | $(SED) 's/\.xml//g'); \ # $(MAKE) uninstall-man \ # mandir="$(mandir)/$$lang" \ # man_MANS="$$manpages"; \ # fi \ # done gssproxy-v0.8.2/man/gssproxy-mech.8.xml.in0000644000174300017420000001226613456641744020105 0ustar gitgit00000000000000 GssProxy GSSAPI mechanism manual page GSS Proxy GSS-Proxy - http://fedorahosted.org/gss-proxy gssproxy-mech 8 gssproxy-mech GssProxy GSSAPI mechanism plugin proxymech_v1 2.16.840.1.113730.3.8.15.1 /usr/lib64/gssproxy/proxymech.so options DESCRIPTION The gssproxy proxymech module is a interposer plugin that is loaded by GSSAPI. It is enabled by /etc/gss/mech configuration file. The interposer plugin allows to intercept the entire GSSAPI communication and detour to the gssproxy daemon. When the interposer plugin is installed two other conditions need to be met in order to activate it: a) interposer configuration file The plugin needs to be manually enabled in the /etc/gss/mech file. b) gssproxy environment variable The interposer plugin will not forward to the gssproxy daemon unless the environment variable named GSS_USE_PROXY=yes is set. Furthermore, the interposer plugin can be configured to behave in different ways when called from the GSSAPI. This behavior is controlled via the GSSPROXY_BEHAVIOR environment variable. It accepts four different values: LOCAL_ONLY All commands received with this setting will cause to immediately reenter the GSSAPI w/o any interaction with the gssproxy daemon. When the request cannot be processed it will just fail. LOCAL_FIRST All commands received with this setting will cause to immediately reenter the GSSAPI. When the local GSSAPI cannot process the request, it will resend the request to the gssproxy daemon. REMOTE_FIRST All commands received with this setting will be forwarded to the gssproxy daemon first. If the request cannot be handled there, the request will reenter the local GSSAPI. REMOTE_ONLY This setting is currently not fully implemented and therefor not supported. The default setting for GSSPROXY_BEHAVIOR is @GPP_DEFAULT_BEHAVIOR@. Finally the interposer may need to use a special per-service socket in order to communicate with gssproxy. The path to this socket is set via the GSSPROXY_SOCKET environment variable. SEE ALSO gssproxy.conf5 and gssproxy8 . gssproxy-v0.8.2/man/gssproxy.8.xml0000644000174300017420000001705713456641744016571 0ustar gitgit00000000000000 GssProxy Manual pages GSS Proxy GSS-Proxy - http://fedorahosted.org/gss-proxy gssproxy 8 gssproxy GssProxy Daemon gssproxy options DESCRIPTION gssproxy provides a daemon to manage access to GSSAPI credentials. gssproxy consists of the gssproxy daemon (configured by the gssproxy.conf 5 file) and a GSSAPI interposer plugin (gssproxy-mech8 ). OPTIONS , Become a daemon after starting up. , Specify a config file to use as the main config file (read before the rest of the config directory). The default is to use the file /etc/gssproxy/gssproxy.conf. For reference on the config file syntax and options, consult the gssproxy.conf 5 manual page. , Specify a non-default config dir. Files named of the form "##-foo.conf" (that is, beginning with two digits and a dash, and ending in ".conf") will be read in numeric order from this directory, in addition to the config file itself. The default is /etc/gssproxy. For reference on the config file syntax and options, consult the gssproxy.conf 5 manual page. , Turn on debugging. This option is identical to --debug-level=1. Turn on debugging at the specified level. 0 corresponds to no logging, while 1 turns on basic debug logging. Level 2 increases verbosity, including more detailed credential verification. At level 3 and above, KRB5_TRACE output is logged. If KRB5_TRACE was already set in the execution environment, trace output is sent to its value instead. , Run in the foreground, don't become a daemon. , Specify a custom default socket name. This socket will be used by all sections that do not define an explicit socket. Print version number and exit. Signals SIGTERM/SIGINT Informs the GssProxy to gracefully terminate all of its child processes and then shut down. SIGHUP Request a reload of all configuration for gssproxy. If there is an error in the configuration files, the existing configuration will not be replaced; if there is a problem applying the new configuration, gssproxy will exit. SEE ALSO gssproxy.conf5 and gssproxy-mech8 . gssproxy-v0.8.2/man/gssproxy.conf.5.xml0000644000174300017420000004425213456641744017507 0ustar gitgit00000000000000 GssProxy Manual pages GSS Proxy GSS-Proxy - http://fedorahosted.org/gss-proxy gssproxy.conf 5 gssproxy.conf GssProxy Daemon Configuration file DESCRIPTION Optional configuration directives for the gssproxy daemon. GSS-Proxy conf files are classic ini-style configuration files. Each option consist of a key = value pair. Any characters behind '#' will be treated as comments and will be ignored. Boolean parameters accept "1", "true", "yes" and "on" as positive values. All other values will be considered as negative values. GSS-Proxy conf files must either be named "gssproxy.conf", or be of the form "##-foo.conf" (that is, start with two numbers followed by a dash, and end in ".conf"). Files not conforming to this will be ignored unless specifically requested through command line parameters. SECTIONS A section in a GSS-Proxy conf file is identified by the sectionname in square brackets ([sectionname]). There is one special section for global gssproxy settings, called [gssproxy]. Services such as nfs, apache, ssh, etc. are represented by sections like [service/nfs], [service/apache], etc. and are identified by the "euid" setting (see below). VARIABLE SUBSTITUTIONS String parameters may contain substitution patterns. This allows gssproxy to deal with patterns for the storage location of keytabs or credential caches easier. The supported patterns are: %U substitutes to the user's numeric uid (e.g. 123) %u substitutes to the user's username (e.g. john). OPTIONS gssproxy supports the following options: allow_any_uid (boolean) Allow any process of any user to use this service. Note that absent a custom socket option this option may cause a service definition to mask access to following services. To avoid issues change the order of services in your configuation file so that services with allow_any_uid enabled are listed last, or define a custom socket for other services. Default: false allow_protocol_transition (boolean) Allow clients to request a ticket to self for an arbitrary user. This option controls whether s4u2self requests are allowed for the requesting client. The configured keytab is used as the service identity for which a ticket is requested. The KDC still needs to allow the operation for it to succeed. Default: false allow_constrained_delegation (boolean) Allow clients to request a ticket to another service using an evidence ticket. This option controls whether s4u2proxy requests are allowed for the requesting client. The KDC still needs to allow the operation for it to succeed. Default: false allow_client_ccache_sync (boolean) Allow clients to request credentials to be sent back for better caching. This option allows the proxy, in certain circumstances, to send back an additional option in the response structure of certain calls when it determines that a new ticket may have been added to the internal ccache. Clients can then replace their (encrypted) copy with the updated ccache. Default: false cred_store (string) This parameter allows to control in which way gssproxy should use the cred_store interface provided by GSSAPI. The parameter can be defined multiple times per service. The syntax of the cred_store parameter is as follows: :]]> Currently this interface supports the following options: keytab Defines the keytab the service should use. Example: cred_store = keytab:/path/to/keytab client_keytab Defines a client keytab the service should use. Example: cred_store = client_keytab:/path/to/client_keytab. ccache Defines a credential cache the service should use. Example: cred_store = ccache:/path/to/ccache. Notably the client_keytab and the ccache setting typically are used with variable substitution placeholders (see above). For example: cred_store = keytab:/etc/krb5.keytab cred_store = ccache:FILE:/var/lib/gssproxy/krb5cc_%U cred_store = client_keytab:/var/lib/gssproxy/%U.keytab Default: cred_store = cred_usage (string) Allow to restrict the kind of operations permitted for this service. The allowed options are: initiate, accept, both Default: cred_usage = both debug (boolean) Enable debugging to syslog. Setting to true is identical to setting debug_level to 1. Default: debug = false debug_level (integer) Detail level at which to log debugging messages. 0 corresponds to no logging, while 1 turns on basic debug logging. Level 2 increases verbosity, including more detailed credential verification. At level 3 and above, KRB5_TRACE output is logged. If KRB5_TRACE was already set in the execution environment, trace output is sent to its value instead. Default: 1 if debug is true, otherwise 0 enforce_flags (string) A list of GSS Request Flags that are added unconditionally to every context initialization call. Flags can only be added to the list or removed from the list by prepending a +/- sign to the flag name or value. Recognized flag names: DELEGATE, MUTUAL_AUTH, REPLAY_DETECT, SEQUENCE, CONFIDENTIALITY, INTEGRITY, ANONYMOUS Examples: enforce_flags = +REPLAY_DETECT enforce_flags = -0x0001 Default: enforce_flags = euid (integer or string) Either the numeric (e.g., 48) or symbolic (e.g., apache) effective uid of a running process, required to identify a service. The "euid" parameter is imperative, any section without it will be discarded. Default: euid = filter_flags (string) A list of GSS Request Flags that are filtered unconditionally from every context initialization call. Flags can only be added to the list or removed from the list by prepending a +/- sign to the flag name or value. NOTE: Because often gssproxy is used to withold access to credentials the Delegate Flag is filtered by default. To allow a service to delegate credentials use the first example below. Recognized flag names: DELEGATE, MUTUAL_AUTH, REPLAY_DETECT, SEQUENCE, CONFIDENTIALITY, INTEGRITY, ANONYMOUS Examples: filter_flags = -DELEGATE filter_flags = -0x0001 +ANONYMOUS Default: filter_flags = +DELEGATE impersonate (boolean) Use impersonation (s4u2self + s4u2proxy) to obtain credentials Default: impersonate = false kernel_nfsd (boolean) Boolean flag that allows the Linux kernel to check if gssproxy is running (via /proc/net/rpc/use-gss-proxy). Default: kernel_nfsd = false krb5_principal (string) The krb5 principal to be used preferred for this service, if one isn't requested by the application. Note that this does not enforce use of this specific name; it only sets a default. Default: krb5_principal = mechs (string) Currently only krb5 is supported. The "mechs" parameter is imperative, any section without it will be discarded. Default: mechs = program (string) If specified, this service will only match when the program being run is the specified string. Programs are assumed to be specified as canonical paths (i.e., no relative paths, no symlinks). Additionally, the '|' character is reserved for future use and therefore forbidden. run_as_user (string) The name of the user gssproxy will drop privileges to. This option is only available in the global section. Default: run_as_user = selinux_context (string) This option is deprecated. Use a custom socket or euid instead. socket (string) This parameter allows to create a per-service socket file over which gssproxy client and server components communicate. When this parameter is not set, gssproxy will use a compiled-in default. trusted (boolean) Defines whether this service is considered trusted. Use with caution, this enables impersonation. Default: trusted = false worker threads (integer) Defines the amount of worker threads gssproxy will create at startup. Default: worker threads = SEE ALSO gssproxy8 and gssproxy-mech8 . gssproxy-v0.8.2/po/0000755000174300017420000000000013456641744013634 5ustar gitgit00000000000000gssproxy-v0.8.2/po/README0000644000174300017420000000003213456641744014507 0ustar gitgit00000000000000PLACEHOLDER po files here gssproxy-v0.8.2/rpcgen/0000755000174300017420000000000013456641744014474 5ustar gitgit00000000000000gssproxy-v0.8.2/rpcgen/README0000644000174300017420000000055513456641744015361 0ustar gitgit00000000000000 This file have been generated from .x files in ../x-files Please do not directly edit these .c/.h files. Instead please edit the original .x files and then use rpcgen to re-generate the .c/.h files. NOTE: C files have been slightly edited to avoid compile warnings, please keep the changes if you regenerate them. Also note that gp_rpc.x must conform to RFC1831 gssproxy-v0.8.2/rpcgen/gp_rpc.h0000644000174300017420000001146213456641744016123 0ustar gitgit00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _GP_RPC_H_RPCGEN #define _GP_RPC_H_RPCGEN #include #ifdef __cplusplus extern "C" { #endif enum gp_rpc_auth_flavor { GP_RPC_AUTH_NONE = 0, GP_RPC_AUTH_SYS = 1, GP_RPC_AUTH_SHORT = 2, GP_RPC_AUTH_DH = 3, GP_RPC_RPCSEC_GSS = 6, }; typedef enum gp_rpc_auth_flavor gp_rpc_auth_flavor; struct gp_rpc_opaque_auth { gp_rpc_auth_flavor flavor; struct { u_int body_len; char *body_val; } body; }; typedef struct gp_rpc_opaque_auth gp_rpc_opaque_auth; enum gp_rpc_msg_type { GP_RPC_CALL = 0, GP_RPC_REPLY = 1, }; typedef enum gp_rpc_msg_type gp_rpc_msg_type; enum gp_rpc_reply_status { GP_RPC_MSG_ACCEPTED = 0, GP_RPC_MSG_DENIED = 1, }; typedef enum gp_rpc_reply_status gp_rpc_reply_status; enum gp_rpc_accept_status { GP_RPC_SUCCESS = 0, GP_RPC_PROG_UNAVAIL = 1, GP_RPC_PROG_MISMATCH = 2, GP_RPC_PROC_UNAVAIL = 3, GP_RPC_GARBAGE_ARGS = 4, GP_RPC_SYSTEM_ERR = 5, }; typedef enum gp_rpc_accept_status gp_rpc_accept_status; enum gp_rpc_reject_status { GP_RPC_RPC_MISMATCH = 0, GP_RPC_AUTH_ERROR = 1, }; typedef enum gp_rpc_reject_status gp_rpc_reject_status; enum gp_rpc_auth_status { GP_RPC_AUTH_OK = 0, GP_RPC_AUTH_BADCRED = 1, GP_RPC_AUTH_REJECTEDCRED = 2, GP_RPC_AUTH_BADVERF = 3, GP_RPC_AUTH_REJECTEDVERF = 4, GP_RPC_AUTH_TOOWEAK = 5, GP_RPC_AUTH_INVALIDRESP = 6, GP_RPC_AUTH_FAILED = 7, GP_RPC_AUTH_KERB_GENERIC = 8, GP_RPC_AUTH_TIMEEXPIRE = 9, GP_RPC_AUTH_TKT_FILE = 10, GP_RPC_AUTH_DECODE = 11, GP_RPC_AUTH_NET_ADDR = 12, GP_RPC_RPCSEC_GSS_CREDPROBLEM = 13, GP_RPC_RPCSEC_GSS_CTXPROBLEM = 14, }; typedef enum gp_rpc_auth_status gp_rpc_auth_status; struct gp_rpc_mismatch_info { u_int low; u_int high; }; typedef struct gp_rpc_mismatch_info gp_rpc_mismatch_info; struct gp_rpc_reply_union { gp_rpc_accept_status status; union { char results[0]; gp_rpc_mismatch_info mismatch_info; } gp_rpc_reply_union_u; }; typedef struct gp_rpc_reply_union gp_rpc_reply_union; struct gp_rpc_accepted_reply { gp_rpc_opaque_auth verf; gp_rpc_reply_union reply_data; }; typedef struct gp_rpc_accepted_reply gp_rpc_accepted_reply; struct gp_rpc_rejected_reply { gp_rpc_reject_status status; union { gp_rpc_mismatch_info mismatch_info; gp_rpc_auth_status status; } gp_rpc_rejected_reply_u; }; typedef struct gp_rpc_rejected_reply gp_rpc_rejected_reply; struct gp_rpc_call_header { u_int rpcvers; u_int prog; u_int vers; u_int proc; gp_rpc_opaque_auth cred; gp_rpc_opaque_auth verf; }; typedef struct gp_rpc_call_header gp_rpc_call_header; struct gp_rpc_reply_header { gp_rpc_reply_status status; union { gp_rpc_accepted_reply accepted; gp_rpc_rejected_reply rejected; } gp_rpc_reply_header_u; }; typedef struct gp_rpc_reply_header gp_rpc_reply_header; struct gp_rpc_msg_union { gp_rpc_msg_type type; union { gp_rpc_call_header chdr; gp_rpc_reply_header rhdr; } gp_rpc_msg_union_u; }; typedef struct gp_rpc_msg_union gp_rpc_msg_union; struct gp_rpc_msg { u_int xid; gp_rpc_msg_union header; }; typedef struct gp_rpc_msg gp_rpc_msg; /* the xdr functions */ #if defined(__STDC__) || defined(__cplusplus) extern bool_t xdr_gp_rpc_auth_flavor (XDR *, gp_rpc_auth_flavor*); extern bool_t xdr_gp_rpc_opaque_auth (XDR *, gp_rpc_opaque_auth*); extern bool_t xdr_gp_rpc_msg_type (XDR *, gp_rpc_msg_type*); extern bool_t xdr_gp_rpc_reply_status (XDR *, gp_rpc_reply_status*); extern bool_t xdr_gp_rpc_accept_status (XDR *, gp_rpc_accept_status*); extern bool_t xdr_gp_rpc_reject_status (XDR *, gp_rpc_reject_status*); extern bool_t xdr_gp_rpc_auth_status (XDR *, gp_rpc_auth_status*); extern bool_t xdr_gp_rpc_mismatch_info (XDR *, gp_rpc_mismatch_info*); extern bool_t xdr_gp_rpc_reply_union (XDR *, gp_rpc_reply_union*); extern bool_t xdr_gp_rpc_accepted_reply (XDR *, gp_rpc_accepted_reply*); extern bool_t xdr_gp_rpc_rejected_reply (XDR *, gp_rpc_rejected_reply*); extern bool_t xdr_gp_rpc_call_header (XDR *, gp_rpc_call_header*); extern bool_t xdr_gp_rpc_reply_header (XDR *, gp_rpc_reply_header*); extern bool_t xdr_gp_rpc_msg_union (XDR *, gp_rpc_msg_union*); extern bool_t xdr_gp_rpc_msg (XDR *, gp_rpc_msg*); #else /* K&R C */ extern bool_t xdr_gp_rpc_auth_flavor (); extern bool_t xdr_gp_rpc_opaque_auth (); extern bool_t xdr_gp_rpc_msg_type (); extern bool_t xdr_gp_rpc_reply_status (); extern bool_t xdr_gp_rpc_accept_status (); extern bool_t xdr_gp_rpc_reject_status (); extern bool_t xdr_gp_rpc_auth_status (); extern bool_t xdr_gp_rpc_mismatch_info (); extern bool_t xdr_gp_rpc_reply_union (); extern bool_t xdr_gp_rpc_accepted_reply (); extern bool_t xdr_gp_rpc_rejected_reply (); extern bool_t xdr_gp_rpc_call_header (); extern bool_t xdr_gp_rpc_reply_header (); extern bool_t xdr_gp_rpc_msg_union (); extern bool_t xdr_gp_rpc_msg (); #endif /* K&R C */ #ifdef __cplusplus } #endif #endif /* !_GP_RPC_H_RPCGEN */ gssproxy-v0.8.2/rpcgen/gp_rpc_xdr.c0000644000174300017420000001265613456641744017001 0ustar gitgit00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #include "rpcgen/gp_rpc.h" bool_t xdr_gp_rpc_auth_flavor (XDR *xdrs, gp_rpc_auth_flavor *objp) { if (!xdr_enum (xdrs, (enum_t *) objp)) return FALSE; return TRUE; } bool_t xdr_gp_rpc_opaque_auth (XDR *xdrs, gp_rpc_opaque_auth *objp) { if (!xdr_gp_rpc_auth_flavor (xdrs, &objp->flavor)) return FALSE; if (!xdr_bytes (xdrs, (char **)&objp->body.body_val, (u_int *) &objp->body.body_len, 400)) return FALSE; return TRUE; } bool_t xdr_gp_rpc_msg_type (XDR *xdrs, gp_rpc_msg_type *objp) { if (!xdr_enum (xdrs, (enum_t *) objp)) return FALSE; return TRUE; } bool_t xdr_gp_rpc_reply_status (XDR *xdrs, gp_rpc_reply_status *objp) { if (!xdr_enum (xdrs, (enum_t *) objp)) return FALSE; return TRUE; } bool_t xdr_gp_rpc_accept_status (XDR *xdrs, gp_rpc_accept_status *objp) { if (!xdr_enum (xdrs, (enum_t *) objp)) return FALSE; return TRUE; } bool_t xdr_gp_rpc_reject_status (XDR *xdrs, gp_rpc_reject_status *objp) { if (!xdr_enum (xdrs, (enum_t *) objp)) return FALSE; return TRUE; } bool_t xdr_gp_rpc_auth_status (XDR *xdrs, gp_rpc_auth_status *objp) { if (!xdr_enum (xdrs, (enum_t *) objp)) return FALSE; return TRUE; } bool_t xdr_gp_rpc_mismatch_info (XDR *xdrs, gp_rpc_mismatch_info *objp) { if (!xdr_u_int (xdrs, &objp->low)) return FALSE; if (!xdr_u_int (xdrs, &objp->high)) return FALSE; return TRUE; } bool_t xdr_gp_rpc_reply_union (XDR *xdrs, gp_rpc_reply_union *objp) { if (!xdr_gp_rpc_accept_status (xdrs, &objp->status)) return FALSE; switch (objp->status) { case GP_RPC_SUCCESS: if (!xdr_opaque (xdrs, objp->gp_rpc_reply_union_u.results, 0)) return FALSE; break; case GP_RPC_PROG_MISMATCH: if (!xdr_gp_rpc_mismatch_info (xdrs, &objp->gp_rpc_reply_union_u.mismatch_info)) return FALSE; break; default: break; } return TRUE; } bool_t xdr_gp_rpc_accepted_reply (XDR *xdrs, gp_rpc_accepted_reply *objp) { if (!xdr_gp_rpc_opaque_auth (xdrs, &objp->verf)) return FALSE; if (!xdr_gp_rpc_reply_union (xdrs, &objp->reply_data)) return FALSE; return TRUE; } bool_t xdr_gp_rpc_rejected_reply (XDR *xdrs, gp_rpc_rejected_reply *objp) { if (!xdr_gp_rpc_reject_status (xdrs, &objp->status)) return FALSE; switch (objp->status) { case GP_RPC_RPC_MISMATCH: if (!xdr_gp_rpc_mismatch_info (xdrs, &objp->gp_rpc_rejected_reply_u.mismatch_info)) return FALSE; break; case GP_RPC_AUTH_ERROR: if (!xdr_gp_rpc_auth_status (xdrs, &objp->gp_rpc_rejected_reply_u.status)) return FALSE; break; default: return FALSE; } return TRUE; } bool_t xdr_gp_rpc_call_header (XDR *xdrs, gp_rpc_call_header *objp) { register int32_t *buf; if (xdrs->x_op == XDR_ENCODE) { buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_u_int (xdrs, &objp->rpcvers)) return FALSE; if (!xdr_u_int (xdrs, &objp->prog)) return FALSE; if (!xdr_u_int (xdrs, &objp->vers)) return FALSE; if (!xdr_u_int (xdrs, &objp->proc)) return FALSE; } else { IXDR_PUT_U_LONG(buf, objp->rpcvers); IXDR_PUT_U_LONG(buf, objp->prog); IXDR_PUT_U_LONG(buf, objp->vers); IXDR_PUT_U_LONG(buf, objp->proc); } if (!xdr_gp_rpc_opaque_auth (xdrs, &objp->cred)) return FALSE; if (!xdr_gp_rpc_opaque_auth (xdrs, &objp->verf)) return FALSE; return TRUE; } else if (xdrs->x_op == XDR_DECODE) { buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_u_int (xdrs, &objp->rpcvers)) return FALSE; if (!xdr_u_int (xdrs, &objp->prog)) return FALSE; if (!xdr_u_int (xdrs, &objp->vers)) return FALSE; if (!xdr_u_int (xdrs, &objp->proc)) return FALSE; } else { objp->rpcvers = IXDR_GET_U_LONG(buf); objp->prog = IXDR_GET_U_LONG(buf); objp->vers = IXDR_GET_U_LONG(buf); objp->proc = IXDR_GET_U_LONG(buf); } if (!xdr_gp_rpc_opaque_auth (xdrs, &objp->cred)) return FALSE; if (!xdr_gp_rpc_opaque_auth (xdrs, &objp->verf)) return FALSE; return TRUE; } if (!xdr_u_int (xdrs, &objp->rpcvers)) return FALSE; if (!xdr_u_int (xdrs, &objp->prog)) return FALSE; if (!xdr_u_int (xdrs, &objp->vers)) return FALSE; if (!xdr_u_int (xdrs, &objp->proc)) return FALSE; if (!xdr_gp_rpc_opaque_auth (xdrs, &objp->cred)) return FALSE; if (!xdr_gp_rpc_opaque_auth (xdrs, &objp->verf)) return FALSE; return TRUE; } bool_t xdr_gp_rpc_reply_header (XDR *xdrs, gp_rpc_reply_header *objp) { if (!xdr_gp_rpc_reply_status (xdrs, &objp->status)) return FALSE; switch (objp->status) { case GP_RPC_MSG_ACCEPTED: if (!xdr_gp_rpc_accepted_reply (xdrs, &objp->gp_rpc_reply_header_u.accepted)) return FALSE; break; case GP_RPC_MSG_DENIED: if (!xdr_gp_rpc_rejected_reply (xdrs, &objp->gp_rpc_reply_header_u.rejected)) return FALSE; break; default: return FALSE; } return TRUE; } bool_t xdr_gp_rpc_msg_union (XDR *xdrs, gp_rpc_msg_union *objp) { if (!xdr_gp_rpc_msg_type (xdrs, &objp->type)) return FALSE; switch (objp->type) { case GP_RPC_CALL: if (!xdr_gp_rpc_call_header (xdrs, &objp->gp_rpc_msg_union_u.chdr)) return FALSE; break; case GP_RPC_REPLY: if (!xdr_gp_rpc_reply_header (xdrs, &objp->gp_rpc_msg_union_u.rhdr)) return FALSE; break; default: return FALSE; } return TRUE; } bool_t xdr_gp_rpc_msg (XDR *xdrs, gp_rpc_msg *objp) { if (!xdr_u_int (xdrs, &objp->xid)) return FALSE; if (!xdr_gp_rpc_msg_union (xdrs, &objp->header)) return FALSE; return TRUE; } gssproxy-v0.8.2/rpcgen/gp_xdr.c0000644000174300017420000000127013456641744016123 0ustar gitgit00000000000000/* Copyright (C) 2013 the GSS-PROXY contributors, see COPYING for license */ #include "rpcgen/gp_xdr.h" bool_t gp_xdr_uint64_t(XDR *xdrs, uint64_t *objp) { uint32_t h; uint32_t l; switch(xdrs->x_op) { case XDR_ENCODE: h = (uint32_t)((*objp) >> 32); l = (uint32_t)(*objp); if (!xdr_u_int32(xdrs, &h) || !xdr_u_int32(xdrs, &l)) { return FALSE; } return TRUE; case XDR_DECODE: if (!xdr_u_int32(xdrs, &h) || !xdr_u_int32(xdrs, &l)) { return FALSE; } *objp = (((uint64_t)h) << 32) | l; return TRUE; case XDR_FREE: return TRUE; default: return FALSE; } } gssproxy-v0.8.2/rpcgen/gp_xdr.h0000644000174300017420000000040013456641744016122 0ustar gitgit00000000000000/* Copyright (C) 2013 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GP_XDR_H_ #define _GP_XDR_H_ #include "gssrpc/rpc.h" #define xdr_u_quad_t gp_xdr_uint64_t bool_t gp_xdr_uint64_t(XDR *xdrs, uint64_t *objp); #endif /* _GP_XDR_H_ */ gssproxy-v0.8.2/rpcgen/gss_proxy.h0000644000174300017420000005442413456641744016713 0ustar gitgit00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _GSS_PROXY_H_RPCGEN #define _GSS_PROXY_H_RPCGEN #include #ifdef __cplusplus extern "C" { #endif typedef struct { u_int utf8string_len; char *utf8string_val; } utf8string; typedef struct { u_int octet_string_len; char *octet_string_val; } octet_string; typedef u_quad_t gssx_uint64; typedef u_quad_t gssx_qop; typedef octet_string gssx_buffer; typedef octet_string gssx_OID; typedef struct { u_int gssx_OID_set_len; gssx_OID *gssx_OID_set_val; } gssx_OID_set; enum gssx_cred_usage { GSSX_C_INITIATE = 1, GSSX_C_ACCEPT = 2, GSSX_C_BOTH = 3, }; typedef enum gssx_cred_usage gssx_cred_usage; typedef u_quad_t gssx_time; struct gssx_option { gssx_buffer option; gssx_buffer value; }; typedef struct gssx_option gssx_option; struct gssx_mech_attr { gssx_OID attr; gssx_buffer name; gssx_buffer short_desc; gssx_buffer long_desc; struct { u_int extensions_len; gssx_option *extensions_val; } extensions; }; typedef struct gssx_mech_attr gssx_mech_attr; struct gssx_mech_info { gssx_OID mech; gssx_OID_set name_types; gssx_OID_set mech_attrs; gssx_OID_set known_mech_attrs; gssx_OID_set cred_options; gssx_OID_set sec_ctx_options; gssx_buffer saslname_sasl_mech_name; gssx_buffer saslname_mech_name; gssx_buffer saslname_mech_desc; struct { u_int extensions_len; gssx_option *extensions_val; } extensions; }; typedef struct gssx_mech_info gssx_mech_info; struct gssx_name_attr { gssx_buffer attr; gssx_buffer value; struct { u_int extensions_len; gssx_option *extensions_val; } extensions; }; typedef struct gssx_name_attr gssx_name_attr; struct gssx_status { gssx_uint64 major_status; gssx_OID mech; gssx_uint64 minor_status; utf8string major_status_string; utf8string minor_status_string; octet_string server_ctx; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_status gssx_status; struct gssx_call_ctx { utf8string locale; octet_string server_ctx; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_call_ctx gssx_call_ctx; struct gssx_name { gssx_buffer display_name; gssx_OID name_type; gssx_buffer exported_name; gssx_buffer exported_composite_name; struct { u_int name_attributes_len; gssx_name_attr *name_attributes_val; } name_attributes; struct { u_int extensions_len; gssx_option *extensions_val; } extensions; }; typedef struct gssx_name gssx_name; struct gssx_cred_element { gssx_name MN; gssx_OID mech; gssx_cred_usage cred_usage; gssx_time initiator_time_rec; gssx_time acceptor_time_rec; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_cred_element gssx_cred_element; struct gssx_cred { gssx_name desired_name; struct { u_int elements_len; gssx_cred_element *elements_val; } elements; octet_string cred_handle_reference; bool_t needs_release; }; typedef struct gssx_cred gssx_cred; struct gssx_ctx { gssx_buffer exported_context_token; octet_string state; bool_t needs_release; gssx_OID mech; gssx_name src_name; gssx_name targ_name; gssx_time lifetime; gssx_uint64 ctx_flags; bool_t locally_initiated; bool_t open; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_ctx gssx_ctx; enum gssx_handle_type { GSSX_C_HANDLE_SEC_CTX = 0, GSSX_C_HANDLE_CRED = 1, }; typedef enum gssx_handle_type gssx_handle_type; struct gssx_handle { gssx_handle_type handle_type; union { gssx_cred cred_info; gssx_ctx sec_ctx_info; octet_string extensions; } gssx_handle_u; }; typedef struct gssx_handle gssx_handle; struct gssx_cb { gssx_uint64 initiator_addrtype; gssx_buffer initiator_address; gssx_uint64 acceptor_addrtype; gssx_buffer acceptor_address; gssx_buffer application_data; }; typedef struct gssx_cb gssx_cb; struct gssx_arg_release_handle { gssx_call_ctx call_ctx; gssx_handle cred_handle; }; typedef struct gssx_arg_release_handle gssx_arg_release_handle; struct gssx_res_release_handle { gssx_status status; }; typedef struct gssx_res_release_handle gssx_res_release_handle; struct gssx_arg_indicate_mechs { gssx_call_ctx call_ctx; }; typedef struct gssx_arg_indicate_mechs gssx_arg_indicate_mechs; struct gssx_res_indicate_mechs { gssx_status status; struct { u_int mechs_len; gssx_mech_info *mechs_val; } mechs; struct { u_int mech_attr_descs_len; gssx_mech_attr *mech_attr_descs_val; } mech_attr_descs; struct { u_int supported_extensions_len; gssx_buffer *supported_extensions_val; } supported_extensions; struct { u_int extensions_len; gssx_option *extensions_val; } extensions; }; typedef struct gssx_res_indicate_mechs gssx_res_indicate_mechs; struct gssx_arg_import_and_canon_name { gssx_call_ctx call_ctx; gssx_name input_name; gssx_OID mech; struct { u_int name_attributes_len; gssx_name_attr *name_attributes_val; } name_attributes; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_arg_import_and_canon_name gssx_arg_import_and_canon_name; struct gssx_res_import_and_canon_name { gssx_status status; gssx_name *output_name; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_res_import_and_canon_name gssx_res_import_and_canon_name; struct gssx_arg_get_call_context { gssx_call_ctx call_ctx; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_arg_get_call_context gssx_arg_get_call_context; struct gssx_res_get_call_context { gssx_status status; octet_string server_call_ctx; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_res_get_call_context gssx_res_get_call_context; struct gssx_arg_acquire_cred { gssx_call_ctx call_ctx; gssx_cred *input_cred_handle; bool_t add_cred_to_input_handle; gssx_name *desired_name; gssx_time time_req; gssx_OID_set desired_mechs; gssx_cred_usage cred_usage; gssx_time initiator_time_req; gssx_time acceptor_time_req; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_arg_acquire_cred gssx_arg_acquire_cred; struct gssx_res_acquire_cred { gssx_status status; gssx_cred *output_cred_handle; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_res_acquire_cred gssx_res_acquire_cred; struct gssx_arg_export_cred { gssx_call_ctx call_ctx; gssx_cred input_cred_handle; gssx_cred_usage cred_usage; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_arg_export_cred gssx_arg_export_cred; struct gssx_res_export_cred { gssx_status status; gssx_cred_usage usage_exported; octet_string *exported_handle; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_res_export_cred gssx_res_export_cred; struct gssx_arg_import_cred { gssx_call_ctx call_ctx; octet_string exported_handle; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_arg_import_cred gssx_arg_import_cred; struct gssx_res_import_cred { gssx_status status; gssx_cred *output_cred_handle; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_res_import_cred gssx_res_import_cred; struct gssx_arg_store_cred { gssx_call_ctx call_ctx; gssx_cred input_cred_handle; gssx_cred_usage cred_usage; gssx_OID desired_mech; bool_t overwrite_cred; bool_t default_cred; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_arg_store_cred gssx_arg_store_cred; struct gssx_res_store_cred { gssx_status status; gssx_OID_set elements_stored; gssx_cred_usage cred_usage_stored; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_res_store_cred gssx_res_store_cred; struct gssx_arg_init_sec_context { gssx_call_ctx call_ctx; gssx_ctx *context_handle; gssx_cred *cred_handle; gssx_name *target_name; gssx_OID mech_type; gssx_uint64 req_flags; gssx_time time_req; gssx_cb *input_cb; gssx_buffer *input_token; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_arg_init_sec_context gssx_arg_init_sec_context; struct gssx_res_init_sec_context { gssx_status status; gssx_ctx *context_handle; gssx_buffer *output_token; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_res_init_sec_context gssx_res_init_sec_context; struct gssx_arg_accept_sec_context { gssx_call_ctx call_ctx; gssx_ctx *context_handle; gssx_cred *cred_handle; gssx_buffer input_token; gssx_cb *input_cb; bool_t ret_deleg_cred; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_arg_accept_sec_context gssx_arg_accept_sec_context; struct gssx_res_accept_sec_context { gssx_status status; gssx_ctx *context_handle; gssx_buffer *output_token; gssx_cred *delegated_cred_handle; struct { u_int options_len; gssx_option *options_val; } options; }; typedef struct gssx_res_accept_sec_context gssx_res_accept_sec_context; struct gssx_arg_get_mic { gssx_call_ctx call_ctx; gssx_ctx context_handle; gssx_qop qop_req; gssx_buffer message_buffer; }; typedef struct gssx_arg_get_mic gssx_arg_get_mic; struct gssx_res_get_mic { gssx_status status; gssx_ctx *context_handle; gssx_buffer token_buffer; gssx_qop *qop_state; }; typedef struct gssx_res_get_mic gssx_res_get_mic; struct gssx_arg_verify_mic { gssx_call_ctx call_ctx; gssx_ctx context_handle; gssx_buffer message_buffer; gssx_buffer token_buffer; }; typedef struct gssx_arg_verify_mic gssx_arg_verify_mic; struct gssx_res_verify_mic { gssx_status status; gssx_ctx *context_handle; gssx_qop *qop_state; }; typedef struct gssx_res_verify_mic gssx_res_verify_mic; struct gssx_arg_wrap { gssx_call_ctx call_ctx; gssx_ctx context_handle; bool_t conf_req; struct { u_int message_buffer_len; gssx_buffer *message_buffer_val; } message_buffer; gssx_qop qop_state; }; typedef struct gssx_arg_wrap gssx_arg_wrap; struct gssx_res_wrap { gssx_status status; gssx_ctx *context_handle; struct { u_int token_buffer_len; gssx_buffer *token_buffer_val; } token_buffer; bool_t *conf_state; gssx_qop *qop_state; }; typedef struct gssx_res_wrap gssx_res_wrap; struct gssx_arg_unwrap { gssx_call_ctx call_ctx; gssx_ctx context_handle; struct { u_int token_buffer_len; gssx_buffer *token_buffer_val; } token_buffer; gssx_qop qop_state; }; typedef struct gssx_arg_unwrap gssx_arg_unwrap; struct gssx_res_unwrap { gssx_status status; gssx_ctx *context_handle; struct { u_int message_buffer_len; gssx_buffer *message_buffer_val; } message_buffer; bool_t *conf_state; gssx_qop *qop_state; }; typedef struct gssx_res_unwrap gssx_res_unwrap; struct gssx_arg_wrap_size_limit { gssx_call_ctx call_ctx; gssx_ctx context_handle; bool_t conf_req; gssx_qop qop_state; gssx_uint64 req_output_size; }; typedef struct gssx_arg_wrap_size_limit gssx_arg_wrap_size_limit; struct gssx_res_wrap_size_limit { gssx_status status; gssx_uint64 max_input_size; }; typedef struct gssx_res_wrap_size_limit gssx_res_wrap_size_limit; #define GSSPROXY 400112 #define GSSPROXYVERS 1 #define GSSX_PROC_MIN 1 #define GSSX_PROC_MAX 15 #if defined(__STDC__) || defined(__cplusplus) #define GSSX_INDICATE_MECHS 1 extern gssx_res_indicate_mechs * gssx_indicate_mechs_1(gssx_arg_indicate_mechs *, CLIENT *); extern gssx_res_indicate_mechs * gssx_indicate_mechs_1_svc(gssx_arg_indicate_mechs *, struct svc_req *); #define GSSX_GET_CALL_CONTEXT 2 extern gssx_res_get_call_context * gssx_get_call_context_1(gssx_arg_get_call_context *, CLIENT *); extern gssx_res_get_call_context * gssx_get_call_context_1_svc(gssx_arg_get_call_context *, struct svc_req *); #define GSSX_IMPORT_AND_CANON_NAME 3 extern gssx_res_import_and_canon_name * gssx_import_and_canon_name_1(gssx_arg_import_and_canon_name *, CLIENT *); extern gssx_res_import_and_canon_name * gssx_import_and_canon_name_1_svc(gssx_arg_import_and_canon_name *, struct svc_req *); #define GSSX_EXPORT_CRED 4 extern gssx_res_export_cred * gssx_export_cred_1(gssx_arg_export_cred *, CLIENT *); extern gssx_res_export_cred * gssx_export_cred_1_svc(gssx_arg_export_cred *, struct svc_req *); #define GSSX_IMPORT_CRED 5 extern gssx_res_import_cred * gssx_import_cred_1(gssx_arg_import_cred *, CLIENT *); extern gssx_res_import_cred * gssx_import_cred_1_svc(gssx_arg_import_cred *, struct svc_req *); #define GSSX_ACQUIRE_CRED 6 extern gssx_res_acquire_cred * gssx_acquire_cred_1(gssx_arg_acquire_cred *, CLIENT *); extern gssx_res_acquire_cred * gssx_acquire_cred_1_svc(gssx_arg_acquire_cred *, struct svc_req *); #define GSSX_STORE_CRED 7 extern gssx_res_store_cred * gssx_store_cred_1(gssx_arg_store_cred *, CLIENT *); extern gssx_res_store_cred * gssx_store_cred_1_svc(gssx_arg_store_cred *, struct svc_req *); #define GSSX_INIT_SEC_CONTEXT 8 extern gssx_res_init_sec_context * gssx_init_sec_context_1(gssx_arg_init_sec_context *, CLIENT *); extern gssx_res_init_sec_context * gssx_init_sec_context_1_svc(gssx_arg_init_sec_context *, struct svc_req *); #define GSSX_ACCEPT_SEC_CONTEXT 9 extern gssx_res_accept_sec_context * gssx_accept_sec_context_1(gssx_arg_accept_sec_context *, CLIENT *); extern gssx_res_accept_sec_context * gssx_accept_sec_context_1_svc(gssx_arg_accept_sec_context *, struct svc_req *); #define GSSX_RELEASE_HANDLE 10 extern gssx_res_release_handle * gssx_release_handle_1(gssx_arg_release_handle *, CLIENT *); extern gssx_res_release_handle * gssx_release_handle_1_svc(gssx_arg_release_handle *, struct svc_req *); #define GSSX_GET_MIC 11 extern gssx_res_get_mic * gssx_get_mic_1(gssx_arg_get_mic *, CLIENT *); extern gssx_res_get_mic * gssx_get_mic_1_svc(gssx_arg_get_mic *, struct svc_req *); #define GSSX_VERIFY 12 extern gssx_res_verify_mic * gssx_verify_1(gssx_arg_verify_mic *, CLIENT *); extern gssx_res_verify_mic * gssx_verify_1_svc(gssx_arg_verify_mic *, struct svc_req *); #define GSSX_WRAP 13 extern gssx_res_wrap * gssx_wrap_1(gssx_arg_wrap *, CLIENT *); extern gssx_res_wrap * gssx_wrap_1_svc(gssx_arg_wrap *, struct svc_req *); #define GSSX_UNWRAP 14 extern gssx_res_unwrap * gssx_unwrap_1(gssx_arg_unwrap *, CLIENT *); extern gssx_res_unwrap * gssx_unwrap_1_svc(gssx_arg_unwrap *, struct svc_req *); #define GSSX_WRAP_SIZE_LIMIT 15 extern gssx_res_wrap_size_limit * gssx_wrap_size_limit_1(gssx_arg_wrap_size_limit *, CLIENT *); extern gssx_res_wrap_size_limit * gssx_wrap_size_limit_1_svc(gssx_arg_wrap_size_limit *, struct svc_req *); extern int gssproxy_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); #else /* K&R C */ #define GSSX_INDICATE_MECHS 1 extern gssx_res_indicate_mechs * gssx_indicate_mechs_1(); extern gssx_res_indicate_mechs * gssx_indicate_mechs_1_svc(); #define GSSX_GET_CALL_CONTEXT 2 extern gssx_res_get_call_context * gssx_get_call_context_1(); extern gssx_res_get_call_context * gssx_get_call_context_1_svc(); #define GSSX_IMPORT_AND_CANON_NAME 3 extern gssx_res_import_and_canon_name * gssx_import_and_canon_name_1(); extern gssx_res_import_and_canon_name * gssx_import_and_canon_name_1_svc(); #define GSSX_EXPORT_CRED 4 extern gssx_res_export_cred * gssx_export_cred_1(); extern gssx_res_export_cred * gssx_export_cred_1_svc(); #define GSSX_IMPORT_CRED 5 extern gssx_res_import_cred * gssx_import_cred_1(); extern gssx_res_import_cred * gssx_import_cred_1_svc(); #define GSSX_ACQUIRE_CRED 6 extern gssx_res_acquire_cred * gssx_acquire_cred_1(); extern gssx_res_acquire_cred * gssx_acquire_cred_1_svc(); #define GSSX_STORE_CRED 7 extern gssx_res_store_cred * gssx_store_cred_1(); extern gssx_res_store_cred * gssx_store_cred_1_svc(); #define GSSX_INIT_SEC_CONTEXT 8 extern gssx_res_init_sec_context * gssx_init_sec_context_1(); extern gssx_res_init_sec_context * gssx_init_sec_context_1_svc(); #define GSSX_ACCEPT_SEC_CONTEXT 9 extern gssx_res_accept_sec_context * gssx_accept_sec_context_1(); extern gssx_res_accept_sec_context * gssx_accept_sec_context_1_svc(); #define GSSX_RELEASE_HANDLE 10 extern gssx_res_release_handle * gssx_release_handle_1(); extern gssx_res_release_handle * gssx_release_handle_1_svc(); #define GSSX_GET_MIC 11 extern gssx_res_get_mic * gssx_get_mic_1(); extern gssx_res_get_mic * gssx_get_mic_1_svc(); #define GSSX_VERIFY 12 extern gssx_res_verify_mic * gssx_verify_1(); extern gssx_res_verify_mic * gssx_verify_1_svc(); #define GSSX_WRAP 13 extern gssx_res_wrap * gssx_wrap_1(); extern gssx_res_wrap * gssx_wrap_1_svc(); #define GSSX_UNWRAP 14 extern gssx_res_unwrap * gssx_unwrap_1(); extern gssx_res_unwrap * gssx_unwrap_1_svc(); #define GSSX_WRAP_SIZE_LIMIT 15 extern gssx_res_wrap_size_limit * gssx_wrap_size_limit_1(); extern gssx_res_wrap_size_limit * gssx_wrap_size_limit_1_svc(); extern int gssproxy_1_freeresult (); #endif /* K&R C */ /* the xdr functions */ #if defined(__STDC__) || defined(__cplusplus) extern bool_t xdr_utf8string (XDR *, utf8string*); extern bool_t xdr_octet_string (XDR *, octet_string*); extern bool_t xdr_gssx_uint64 (XDR *, gssx_uint64*); extern bool_t xdr_gssx_qop (XDR *, gssx_qop*); extern bool_t xdr_gssx_buffer (XDR *, gssx_buffer*); extern bool_t xdr_gssx_OID (XDR *, gssx_OID*); extern bool_t xdr_gssx_OID_set (XDR *, gssx_OID_set*); extern bool_t xdr_gssx_cred_usage (XDR *, gssx_cred_usage*); extern bool_t xdr_gssx_time (XDR *, gssx_time*); extern bool_t xdr_gssx_option (XDR *, gssx_option*); extern bool_t xdr_gssx_mech_attr (XDR *, gssx_mech_attr*); extern bool_t xdr_gssx_mech_info (XDR *, gssx_mech_info*); extern bool_t xdr_gssx_name_attr (XDR *, gssx_name_attr*); extern bool_t xdr_gssx_status (XDR *, gssx_status*); extern bool_t xdr_gssx_call_ctx (XDR *, gssx_call_ctx*); extern bool_t xdr_gssx_name (XDR *, gssx_name*); extern bool_t xdr_gssx_cred_element (XDR *, gssx_cred_element*); extern bool_t xdr_gssx_cred (XDR *, gssx_cred*); extern bool_t xdr_gssx_ctx (XDR *, gssx_ctx*); extern bool_t xdr_gssx_handle_type (XDR *, gssx_handle_type*); extern bool_t xdr_gssx_handle (XDR *, gssx_handle*); extern bool_t xdr_gssx_cb (XDR *, gssx_cb*); extern bool_t xdr_gssx_cb (XDR *, gssx_cb*); extern bool_t xdr_gssx_arg_release_handle (XDR *, gssx_arg_release_handle*); extern bool_t xdr_gssx_res_release_handle (XDR *, gssx_res_release_handle*); extern bool_t xdr_gssx_arg_indicate_mechs (XDR *, gssx_arg_indicate_mechs*); extern bool_t xdr_gssx_res_indicate_mechs (XDR *, gssx_res_indicate_mechs*); extern bool_t xdr_gssx_arg_import_and_canon_name (XDR *, gssx_arg_import_and_canon_name*); extern bool_t xdr_gssx_res_import_and_canon_name (XDR *, gssx_res_import_and_canon_name*); extern bool_t xdr_gssx_arg_get_call_context (XDR *, gssx_arg_get_call_context*); extern bool_t xdr_gssx_res_get_call_context (XDR *, gssx_res_get_call_context*); extern bool_t xdr_gssx_arg_acquire_cred (XDR *, gssx_arg_acquire_cred*); extern bool_t xdr_gssx_res_acquire_cred (XDR *, gssx_res_acquire_cred*); extern bool_t xdr_gssx_arg_export_cred (XDR *, gssx_arg_export_cred*); extern bool_t xdr_gssx_res_export_cred (XDR *, gssx_res_export_cred*); extern bool_t xdr_gssx_arg_import_cred (XDR *, gssx_arg_import_cred*); extern bool_t xdr_gssx_res_import_cred (XDR *, gssx_res_import_cred*); extern bool_t xdr_gssx_arg_store_cred (XDR *, gssx_arg_store_cred*); extern bool_t xdr_gssx_res_store_cred (XDR *, gssx_res_store_cred*); extern bool_t xdr_gssx_arg_init_sec_context (XDR *, gssx_arg_init_sec_context*); extern bool_t xdr_gssx_res_init_sec_context (XDR *, gssx_res_init_sec_context*); extern bool_t xdr_gssx_arg_accept_sec_context (XDR *, gssx_arg_accept_sec_context*); extern bool_t xdr_gssx_res_accept_sec_context (XDR *, gssx_res_accept_sec_context*); extern bool_t xdr_gssx_arg_get_mic (XDR *, gssx_arg_get_mic*); extern bool_t xdr_gssx_res_get_mic (XDR *, gssx_res_get_mic*); extern bool_t xdr_gssx_arg_verify_mic (XDR *, gssx_arg_verify_mic*); extern bool_t xdr_gssx_res_verify_mic (XDR *, gssx_res_verify_mic*); extern bool_t xdr_gssx_arg_wrap (XDR *, gssx_arg_wrap*); extern bool_t xdr_gssx_res_wrap (XDR *, gssx_res_wrap*); extern bool_t xdr_gssx_arg_unwrap (XDR *, gssx_arg_unwrap*); extern bool_t xdr_gssx_res_unwrap (XDR *, gssx_res_unwrap*); extern bool_t xdr_gssx_arg_wrap_size_limit (XDR *, gssx_arg_wrap_size_limit*); extern bool_t xdr_gssx_res_wrap_size_limit (XDR *, gssx_res_wrap_size_limit*); #else /* K&R C */ extern bool_t xdr_utf8string (); extern bool_t xdr_octet_string (); extern bool_t xdr_gssx_uint64 (); extern bool_t xdr_gssx_qop (); extern bool_t xdr_gssx_buffer (); extern bool_t xdr_gssx_OID (); extern bool_t xdr_gssx_OID_set (); extern bool_t xdr_gssx_cred_usage (); extern bool_t xdr_gssx_time (); extern bool_t xdr_gssx_option (); extern bool_t xdr_gssx_mech_attr (); extern bool_t xdr_gssx_mech_info (); extern bool_t xdr_gssx_name_attr (); extern bool_t xdr_gssx_status (); extern bool_t xdr_gssx_call_ctx (); extern bool_t xdr_gssx_name (); extern bool_t xdr_gssx_cred_element (); extern bool_t xdr_gssx_cred (); extern bool_t xdr_gssx_ctx (); extern bool_t xdr_gssx_handle_type (); extern bool_t xdr_gssx_handle (); extern bool_t xdr_gssx_cb (); extern bool_t xdr_gssx_cb (); extern bool_t xdr_gssx_arg_release_handle (); extern bool_t xdr_gssx_res_release_handle (); extern bool_t xdr_gssx_arg_indicate_mechs (); extern bool_t xdr_gssx_res_indicate_mechs (); extern bool_t xdr_gssx_arg_import_and_canon_name (); extern bool_t xdr_gssx_res_import_and_canon_name (); extern bool_t xdr_gssx_arg_get_call_context (); extern bool_t xdr_gssx_res_get_call_context (); extern bool_t xdr_gssx_arg_acquire_cred (); extern bool_t xdr_gssx_res_acquire_cred (); extern bool_t xdr_gssx_arg_export_cred (); extern bool_t xdr_gssx_res_export_cred (); extern bool_t xdr_gssx_arg_import_cred (); extern bool_t xdr_gssx_res_import_cred (); extern bool_t xdr_gssx_arg_store_cred (); extern bool_t xdr_gssx_res_store_cred (); extern bool_t xdr_gssx_arg_init_sec_context (); extern bool_t xdr_gssx_res_init_sec_context (); extern bool_t xdr_gssx_arg_accept_sec_context (); extern bool_t xdr_gssx_res_accept_sec_context (); extern bool_t xdr_gssx_arg_get_mic (); extern bool_t xdr_gssx_res_get_mic (); extern bool_t xdr_gssx_arg_verify_mic (); extern bool_t xdr_gssx_res_verify_mic (); extern bool_t xdr_gssx_arg_wrap (); extern bool_t xdr_gssx_res_wrap (); extern bool_t xdr_gssx_arg_unwrap (); extern bool_t xdr_gssx_res_unwrap (); extern bool_t xdr_gssx_arg_wrap_size_limit (); extern bool_t xdr_gssx_res_wrap_size_limit (); #endif /* K&R C */ #ifdef __cplusplus } #endif #endif /* !_GSS_PROXY_H_RPCGEN */ gssproxy-v0.8.2/rpcgen/gss_proxy_xdr.c0000644000174300017420000005702013456641744017556 0ustar gitgit00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #include "rpcgen/gp_xdr.h" #include "rpcgen/gss_proxy.h" bool_t xdr_utf8string (XDR *xdrs, utf8string *objp) { if (!xdr_bytes (xdrs, (char **)&objp->utf8string_val, (u_int *) &objp->utf8string_len, ~0)) return FALSE; return TRUE; } bool_t xdr_octet_string (XDR *xdrs, octet_string *objp) { if (!xdr_bytes (xdrs, (char **)&objp->octet_string_val, (u_int *) &objp->octet_string_len, ~0)) return FALSE; return TRUE; } bool_t xdr_gssx_uint64 (XDR *xdrs, gssx_uint64 *objp) { if (!xdr_u_quad_t (xdrs, objp)) return FALSE; return TRUE; } bool_t xdr_gssx_qop (XDR *xdrs, gssx_qop *objp) { if (!xdr_u_quad_t (xdrs, objp)) return FALSE; return TRUE; } bool_t xdr_gssx_buffer (XDR *xdrs, gssx_buffer *objp) { if (!xdr_octet_string (xdrs, objp)) return FALSE; return TRUE; } bool_t xdr_gssx_OID (XDR *xdrs, gssx_OID *objp) { if (!xdr_octet_string (xdrs, objp)) return FALSE; return TRUE; } bool_t xdr_gssx_OID_set (XDR *xdrs, gssx_OID_set *objp) { if (!xdr_array (xdrs, (char **)&objp->gssx_OID_set_val, (u_int *) &objp->gssx_OID_set_len, ~0, sizeof (gssx_OID), (xdrproc_t) xdr_gssx_OID)) return FALSE; return TRUE; } bool_t xdr_gssx_cred_usage (XDR *xdrs, gssx_cred_usage *objp) { if (!xdr_enum (xdrs, (enum_t *) objp)) return FALSE; return TRUE; } bool_t xdr_gssx_time (XDR *xdrs, gssx_time *objp) { if (!xdr_u_quad_t (xdrs, objp)) return FALSE; return TRUE; } bool_t xdr_gssx_option (XDR *xdrs, gssx_option *objp) { if (!xdr_gssx_buffer (xdrs, &objp->option)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->value)) return FALSE; return TRUE; } bool_t xdr_gssx_mech_attr (XDR *xdrs, gssx_mech_attr *objp) { if (!xdr_gssx_OID (xdrs, &objp->attr)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->name)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->short_desc)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->long_desc)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->extensions.extensions_val, (u_int *) &objp->extensions.extensions_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_mech_info (XDR *xdrs, gssx_mech_info *objp) { if (!xdr_gssx_OID (xdrs, &objp->mech)) return FALSE; if (!xdr_gssx_OID_set (xdrs, &objp->name_types)) return FALSE; if (!xdr_gssx_OID_set (xdrs, &objp->mech_attrs)) return FALSE; if (!xdr_gssx_OID_set (xdrs, &objp->known_mech_attrs)) return FALSE; if (!xdr_gssx_OID_set (xdrs, &objp->cred_options)) return FALSE; if (!xdr_gssx_OID_set (xdrs, &objp->sec_ctx_options)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->saslname_sasl_mech_name)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->saslname_mech_name)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->saslname_mech_desc)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->extensions.extensions_val, (u_int *) &objp->extensions.extensions_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_name_attr (XDR *xdrs, gssx_name_attr *objp) { if (!xdr_gssx_buffer (xdrs, &objp->attr)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->value)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->extensions.extensions_val, (u_int *) &objp->extensions.extensions_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_status (XDR *xdrs, gssx_status *objp) { if (!xdr_gssx_uint64 (xdrs, &objp->major_status)) return FALSE; if (!xdr_gssx_OID (xdrs, &objp->mech)) return FALSE; if (!xdr_gssx_uint64 (xdrs, &objp->minor_status)) return FALSE; if (!xdr_utf8string (xdrs, &objp->major_status_string)) return FALSE; if (!xdr_utf8string (xdrs, &objp->minor_status_string)) return FALSE; if (!xdr_octet_string (xdrs, &objp->server_ctx)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_call_ctx (XDR *xdrs, gssx_call_ctx *objp) { if (!xdr_utf8string (xdrs, &objp->locale)) return FALSE; if (!xdr_octet_string (xdrs, &objp->server_ctx)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_name (XDR *xdrs, gssx_name *objp) { if (!xdr_gssx_buffer (xdrs, &objp->display_name)) return FALSE; if (!xdr_gssx_OID (xdrs, &objp->name_type)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->exported_name)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->exported_composite_name)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->name_attributes.name_attributes_val, (u_int *) &objp->name_attributes.name_attributes_len, ~0, sizeof (gssx_name_attr), (xdrproc_t) xdr_gssx_name_attr)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->extensions.extensions_val, (u_int *) &objp->extensions.extensions_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_cred_element (XDR *xdrs, gssx_cred_element *objp) { if (!xdr_gssx_name (xdrs, &objp->MN)) return FALSE; if (!xdr_gssx_OID (xdrs, &objp->mech)) return FALSE; if (!xdr_gssx_cred_usage (xdrs, &objp->cred_usage)) return FALSE; if (!xdr_gssx_time (xdrs, &objp->initiator_time_rec)) return FALSE; if (!xdr_gssx_time (xdrs, &objp->acceptor_time_rec)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_cred (XDR *xdrs, gssx_cred *objp) { if (!xdr_gssx_name (xdrs, &objp->desired_name)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->elements.elements_val, (u_int *) &objp->elements.elements_len, ~0, sizeof (gssx_cred_element), (xdrproc_t) xdr_gssx_cred_element)) return FALSE; if (!xdr_octet_string (xdrs, &objp->cred_handle_reference)) return FALSE; if (!xdr_bool (xdrs, &objp->needs_release)) return FALSE; return TRUE; } bool_t xdr_gssx_ctx (XDR *xdrs, gssx_ctx *objp) { if (!xdr_gssx_buffer (xdrs, &objp->exported_context_token)) return FALSE; if (!xdr_octet_string (xdrs, &objp->state)) return FALSE; if (!xdr_bool (xdrs, &objp->needs_release)) return FALSE; if (!xdr_gssx_OID (xdrs, &objp->mech)) return FALSE; if (!xdr_gssx_name (xdrs, &objp->src_name)) return FALSE; if (!xdr_gssx_name (xdrs, &objp->targ_name)) return FALSE; if (!xdr_gssx_time (xdrs, &objp->lifetime)) return FALSE; if (!xdr_gssx_uint64 (xdrs, &objp->ctx_flags)) return FALSE; if (!xdr_bool (xdrs, &objp->locally_initiated)) return FALSE; if (!xdr_bool (xdrs, &objp->open)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_handle_type (XDR *xdrs, gssx_handle_type *objp) { if (!xdr_enum (xdrs, (enum_t *) objp)) return FALSE; return TRUE; } bool_t xdr_gssx_handle (XDR *xdrs, gssx_handle *objp) { if (!xdr_gssx_handle_type (xdrs, &objp->handle_type)) return FALSE; switch (objp->handle_type) { case GSSX_C_HANDLE_CRED: if (!xdr_gssx_cred (xdrs, &objp->gssx_handle_u.cred_info)) return FALSE; break; case GSSX_C_HANDLE_SEC_CTX: if (!xdr_gssx_ctx (xdrs, &objp->gssx_handle_u.sec_ctx_info)) return FALSE; break; default: if (!xdr_octet_string (xdrs, &objp->gssx_handle_u.extensions)) return FALSE; break; } return TRUE; } bool_t xdr_gssx_cb (XDR *xdrs, gssx_cb *objp) { if (!xdr_gssx_uint64 (xdrs, &objp->initiator_addrtype)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->initiator_address)) return FALSE; if (!xdr_gssx_uint64 (xdrs, &objp->acceptor_addrtype)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->acceptor_address)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->application_data)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_release_handle (XDR *xdrs, gssx_arg_release_handle *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_gssx_handle (xdrs, &objp->cred_handle)) return FALSE; return TRUE; } bool_t xdr_gssx_res_release_handle (XDR *xdrs, gssx_res_release_handle *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_indicate_mechs (XDR *xdrs, gssx_arg_indicate_mechs *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; return TRUE; } bool_t xdr_gssx_res_indicate_mechs (XDR *xdrs, gssx_res_indicate_mechs *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->mechs.mechs_val, (u_int *) &objp->mechs.mechs_len, ~0, sizeof (gssx_mech_info), (xdrproc_t) xdr_gssx_mech_info)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->mech_attr_descs.mech_attr_descs_val, (u_int *) &objp->mech_attr_descs.mech_attr_descs_len, ~0, sizeof (gssx_mech_attr), (xdrproc_t) xdr_gssx_mech_attr)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->supported_extensions.supported_extensions_val, (u_int *) &objp->supported_extensions.supported_extensions_len, ~0, sizeof (gssx_buffer), (xdrproc_t) xdr_gssx_buffer)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->extensions.extensions_val, (u_int *) &objp->extensions.extensions_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_import_and_canon_name (XDR *xdrs, gssx_arg_import_and_canon_name *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_gssx_name (xdrs, &objp->input_name)) return FALSE; if (!xdr_gssx_OID (xdrs, &objp->mech)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->name_attributes.name_attributes_val, (u_int *) &objp->name_attributes.name_attributes_len, ~0, sizeof (gssx_name_attr), (xdrproc_t) xdr_gssx_name_attr)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_res_import_and_canon_name (XDR *xdrs, gssx_res_import_and_canon_name *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->output_name, sizeof (gssx_name), (xdrproc_t) xdr_gssx_name)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_get_call_context (XDR *xdrs, gssx_arg_get_call_context *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_res_get_call_context (XDR *xdrs, gssx_res_get_call_context *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_octet_string (xdrs, &objp->server_call_ctx)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_acquire_cred (XDR *xdrs, gssx_arg_acquire_cred *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->input_cred_handle, sizeof (gssx_cred), (xdrproc_t) xdr_gssx_cred)) return FALSE; if (!xdr_bool (xdrs, &objp->add_cred_to_input_handle)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->desired_name, sizeof (gssx_name), (xdrproc_t) xdr_gssx_name)) return FALSE; if (!xdr_gssx_time (xdrs, &objp->time_req)) return FALSE; if (!xdr_gssx_OID_set (xdrs, &objp->desired_mechs)) return FALSE; if (!xdr_gssx_cred_usage (xdrs, &objp->cred_usage)) return FALSE; if (!xdr_gssx_time (xdrs, &objp->initiator_time_req)) return FALSE; if (!xdr_gssx_time (xdrs, &objp->acceptor_time_req)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_res_acquire_cred (XDR *xdrs, gssx_res_acquire_cred *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->output_cred_handle, sizeof (gssx_cred), (xdrproc_t) xdr_gssx_cred)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_export_cred (XDR *xdrs, gssx_arg_export_cred *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_gssx_cred (xdrs, &objp->input_cred_handle)) return FALSE; if (!xdr_gssx_cred_usage (xdrs, &objp->cred_usage)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_res_export_cred (XDR *xdrs, gssx_res_export_cred *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_gssx_cred_usage (xdrs, &objp->usage_exported)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->exported_handle, sizeof (octet_string), (xdrproc_t) xdr_octet_string)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_import_cred (XDR *xdrs, gssx_arg_import_cred *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_octet_string (xdrs, &objp->exported_handle)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_res_import_cred (XDR *xdrs, gssx_res_import_cred *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->output_cred_handle, sizeof (gssx_cred), (xdrproc_t) xdr_gssx_cred)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_store_cred (XDR *xdrs, gssx_arg_store_cred *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_gssx_cred (xdrs, &objp->input_cred_handle)) return FALSE; if (!xdr_gssx_cred_usage (xdrs, &objp->cred_usage)) return FALSE; if (!xdr_gssx_OID (xdrs, &objp->desired_mech)) return FALSE; if (!xdr_bool (xdrs, &objp->overwrite_cred)) return FALSE; if (!xdr_bool (xdrs, &objp->default_cred)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_res_store_cred (XDR *xdrs, gssx_res_store_cred *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_gssx_OID_set (xdrs, &objp->elements_stored)) return FALSE; if (!xdr_gssx_cred_usage (xdrs, &objp->cred_usage_stored)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_init_sec_context (XDR *xdrs, gssx_arg_init_sec_context *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->context_handle, sizeof (gssx_ctx), (xdrproc_t) xdr_gssx_ctx)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->cred_handle, sizeof (gssx_cred), (xdrproc_t) xdr_gssx_cred)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->target_name, sizeof (gssx_name), (xdrproc_t) xdr_gssx_name)) return FALSE; if (!xdr_gssx_OID (xdrs, &objp->mech_type)) return FALSE; if (!xdr_gssx_uint64 (xdrs, &objp->req_flags)) return FALSE; if (!xdr_gssx_time (xdrs, &objp->time_req)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->input_cb, sizeof (gssx_cb), (xdrproc_t) xdr_gssx_cb)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->input_token, sizeof (gssx_buffer), (xdrproc_t) xdr_gssx_buffer)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_res_init_sec_context (XDR *xdrs, gssx_res_init_sec_context *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->context_handle, sizeof (gssx_ctx), (xdrproc_t) xdr_gssx_ctx)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->output_token, sizeof (gssx_buffer), (xdrproc_t) xdr_gssx_buffer)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_accept_sec_context (XDR *xdrs, gssx_arg_accept_sec_context *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->context_handle, sizeof (gssx_ctx), (xdrproc_t) xdr_gssx_ctx)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->cred_handle, sizeof (gssx_cred), (xdrproc_t) xdr_gssx_cred)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->input_token)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->input_cb, sizeof (gssx_cb), (xdrproc_t) xdr_gssx_cb)) return FALSE; if (!xdr_bool (xdrs, &objp->ret_deleg_cred)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_res_accept_sec_context (XDR *xdrs, gssx_res_accept_sec_context *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->context_handle, sizeof (gssx_ctx), (xdrproc_t) xdr_gssx_ctx)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->output_token, sizeof (gssx_buffer), (xdrproc_t) xdr_gssx_buffer)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->delegated_cred_handle, sizeof (gssx_cred), (xdrproc_t) xdr_gssx_cred)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->options.options_val, (u_int *) &objp->options.options_len, ~0, sizeof (gssx_option), (xdrproc_t) xdr_gssx_option)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_get_mic (XDR *xdrs, gssx_arg_get_mic *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_gssx_ctx (xdrs, &objp->context_handle)) return FALSE; if (!xdr_gssx_qop (xdrs, &objp->qop_req)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->message_buffer)) return FALSE; return TRUE; } bool_t xdr_gssx_res_get_mic (XDR *xdrs, gssx_res_get_mic *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->context_handle, sizeof (gssx_ctx), (xdrproc_t) xdr_gssx_ctx)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->token_buffer)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->qop_state, sizeof (gssx_qop), (xdrproc_t) xdr_gssx_qop)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_verify_mic (XDR *xdrs, gssx_arg_verify_mic *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_gssx_ctx (xdrs, &objp->context_handle)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->message_buffer)) return FALSE; if (!xdr_gssx_buffer (xdrs, &objp->token_buffer)) return FALSE; return TRUE; } bool_t xdr_gssx_res_verify_mic (XDR *xdrs, gssx_res_verify_mic *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->context_handle, sizeof (gssx_ctx), (xdrproc_t) xdr_gssx_ctx)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->qop_state, sizeof (gssx_qop), (xdrproc_t) xdr_gssx_qop)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_wrap (XDR *xdrs, gssx_arg_wrap *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_gssx_ctx (xdrs, &objp->context_handle)) return FALSE; if (!xdr_bool (xdrs, &objp->conf_req)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->message_buffer.message_buffer_val, (u_int *) &objp->message_buffer.message_buffer_len, ~0, sizeof (gssx_buffer), (xdrproc_t) xdr_gssx_buffer)) return FALSE; if (!xdr_gssx_qop (xdrs, &objp->qop_state)) return FALSE; return TRUE; } bool_t xdr_gssx_res_wrap (XDR *xdrs, gssx_res_wrap *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->context_handle, sizeof (gssx_ctx), (xdrproc_t) xdr_gssx_ctx)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->token_buffer.token_buffer_val, (u_int *) &objp->token_buffer.token_buffer_len, ~0, sizeof (gssx_buffer), (xdrproc_t) xdr_gssx_buffer)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->conf_state, sizeof (bool_t), (xdrproc_t) xdr_bool)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->qop_state, sizeof (gssx_qop), (xdrproc_t) xdr_gssx_qop)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_unwrap (XDR *xdrs, gssx_arg_unwrap *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_gssx_ctx (xdrs, &objp->context_handle)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->token_buffer.token_buffer_val, (u_int *) &objp->token_buffer.token_buffer_len, ~0, sizeof (gssx_buffer), (xdrproc_t) xdr_gssx_buffer)) return FALSE; if (!xdr_gssx_qop (xdrs, &objp->qop_state)) return FALSE; return TRUE; } bool_t xdr_gssx_res_unwrap (XDR *xdrs, gssx_res_unwrap *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->context_handle, sizeof (gssx_ctx), (xdrproc_t) xdr_gssx_ctx)) return FALSE; if (!xdr_array (xdrs, (char **)&objp->message_buffer.message_buffer_val, (u_int *) &objp->message_buffer.message_buffer_len, ~0, sizeof (gssx_buffer), (xdrproc_t) xdr_gssx_buffer)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->conf_state, sizeof (bool_t), (xdrproc_t) xdr_bool)) return FALSE; if (!xdr_pointer (xdrs, (char **)&objp->qop_state, sizeof (gssx_qop), (xdrproc_t) xdr_gssx_qop)) return FALSE; return TRUE; } bool_t xdr_gssx_arg_wrap_size_limit (XDR *xdrs, gssx_arg_wrap_size_limit *objp) { if (!xdr_gssx_call_ctx (xdrs, &objp->call_ctx)) return FALSE; if (!xdr_gssx_ctx (xdrs, &objp->context_handle)) return FALSE; if (!xdr_bool (xdrs, &objp->conf_req)) return FALSE; if (!xdr_gssx_qop (xdrs, &objp->qop_state)) return FALSE; if (!xdr_gssx_uint64 (xdrs, &objp->req_output_size)) return FALSE; return TRUE; } bool_t xdr_gssx_res_wrap_size_limit (XDR *xdrs, gssx_res_wrap_size_limit *objp) { if (!xdr_gssx_status (xdrs, &objp->status)) return FALSE; if (!xdr_gssx_uint64 (xdrs, &objp->max_input_size)) return FALSE; return TRUE; } gssproxy-v0.8.2/src/0000755000174300017420000000000013456641744014005 5ustar gitgit00000000000000gssproxy-v0.8.2/src/client/0000755000174300017420000000000013456641744015263 5ustar gitgit00000000000000gssproxy-v0.8.2/src/client/gpm_accept_sec_context.c0000644000174300017420000001073213456641744022132 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" #include "src/gp_conv.h" OM_uint32 gpm_accept_sec_context(OM_uint32 *minor_status, gssx_ctx **context_handle, gssx_cred *acceptor_cred_handle, gss_buffer_t input_token_buffer, gss_channel_bindings_t input_chan_bindings, gssx_name **src_name, gss_OID *mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec, gssx_cred **delegated_cred_handle) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_accept_sec_context *arg = &uarg.accept_sec_context; gssx_res_accept_sec_context *res = &ures.accept_sec_context; gssx_ctx *ctx = NULL; gssx_name *name = NULL; gss_OID_desc *mech = NULL; gss_buffer_t outbuf = NULL; uint32_t ret_maj; int ret; memset(&uarg, 0, sizeof(union gp_rpc_arg)); memset(&ures, 0, sizeof(union gp_rpc_res)); /* prepare proxy request */ if (*context_handle) { arg->context_handle = *context_handle; } if (acceptor_cred_handle) { arg->cred_handle = acceptor_cred_handle; } ret = gp_conv_buffer_to_gssx(input_token_buffer, &arg->input_token); if (ret) { goto done; } if (input_chan_bindings) { ret = gp_conv_cb_to_gssx_alloc(input_chan_bindings, &arg->input_cb); if (ret) { goto done; } } /* check if we want delegated creds */ if (delegated_cred_handle) { arg->ret_deleg_cred = true; } /* execute proxy request */ ret = gpm_make_call(GSSX_ACCEPT_SEC_CONTEXT, &uarg, &ures); if (ret) { goto done; } /* return values */ if (res->status.major_status) { gpm_save_status(&res->status); ret_maj = res->status.major_status; *minor_status = res->status.minor_status; ret = 0; goto done; } if (mech_type) { if (res->status.mech.octet_string_len) { ret = gp_conv_gssx_to_oid_alloc(&res->status.mech, &mech); if (ret) { goto done; } } } ctx = res->context_handle; /* we are stealing the delegated creds on success, so we do not want * it to be freed by xdr_free */ res->context_handle = NULL; if (ctx == NULL) { ret = EINVAL; goto done; } if (src_name) { ret = gp_copy_gssx_name_alloc(&ctx->src_name, &name); if (ret) { goto done; } } ret = gp_conv_gssx_to_buffer_alloc(res->output_token, &outbuf); if (ret) { goto done; } if (mech_type) { *mech_type = mech; } if (src_name) { *src_name = name; } if (outbuf) { *output_token = *outbuf; free(outbuf); } if (ret_flags) { *ret_flags = ctx->ctx_flags; } if (time_rec) { *time_rec = ctx->lifetime; } if (res->delegated_cred_handle) { if (delegated_cred_handle) { *delegated_cred_handle = res->delegated_cred_handle; } /* we are stealing the delegated creds on success, so we do not want * it to be freed by xdr_free */ res->delegated_cred_handle = NULL; } *minor_status = 0; ret_maj = GSS_S_COMPLETE; done: /* we are putting our copy of these structures in here, * and do not want it to be freed by xdr_free */ arg->context_handle = NULL; arg->cred_handle = NULL; gpm_free_xdrs(GSSX_ACCEPT_SEC_CONTEXT, &uarg, &ures); if (ret) { if (ctx) { xdr_free((xdrproc_t)xdr_gssx_ctx, (char *)ctx); free(ctx); } if (name) { xdr_free((xdrproc_t)xdr_gssx_name, (char *)name); free(name); } if (mech) { free(mech->elements); free(mech); } if (outbuf) { free(outbuf->value); free(outbuf); } *minor_status = ret; return GSS_S_FAILURE; } /* always replace old ctx handle and set new */ if (*context_handle) { xdr_free((xdrproc_t)xdr_gssx_ctx, (char *)*context_handle); free(*context_handle); } *context_handle = ctx; return ret_maj; } gssproxy-v0.8.2/src/client/gpm_acquire_cred.c0000644000174300017420000003207713456641744020731 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" static int gpmint_cred_to_actual_mechs(gssx_cred *c, gss_OID_set *a) { gssx_cred_element *e; gss_OID_set m = GSS_C_NO_OID_SET; if (c->elements.elements_len) { m = malloc(sizeof(gss_OID_set_desc)); if (!m) { return ENOMEM; } m->elements = calloc(c->elements.elements_len, sizeof(gss_OID_desc)); if (!m->elements) { free(m); return ENOMEM; } for (unsigned i = 0; i < c->elements.elements_len; i++) { e = &c->elements.elements_val[i]; m->elements[i].elements = gp_memdup(e->mech.octet_string_val, e->mech.octet_string_len); if (!m->elements[i].elements) { while (i > 0) { i--; free(m->elements[i].elements); } free(m->elements); free(m); return ENOMEM; } m->elements[i].length = e->mech.octet_string_len; } } *a = m; return 0; } OM_uint32 gpm_acquire_cred(OM_uint32 *minor_status, gssx_cred *in_cred_handle, gssx_name *desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, bool impersonate, gssx_cred **output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_acquire_cred *arg = &uarg.acquire_cred; gssx_res_acquire_cred *res = &ures.acquire_cred; uint32_t ret_min; uint32_t ret_maj; int ret = 0; memset(&uarg, 0, sizeof(union gp_rpc_arg)); memset(&ures, 0, sizeof(union gp_rpc_res)); if (output_cred_handle == NULL) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } /* ignore call_ctx for now */ arg->input_cred_handle = in_cred_handle; arg->desired_name = desired_name; if (desired_mechs) { ret = gp_conv_oid_set_to_gssx(desired_mechs, &arg->desired_mechs); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } } arg->time_req = time_req; arg->cred_usage = gp_conv_cred_usage_to_gssx(cred_usage); /* impersonate calls use input cred and a special option */ if (impersonate) { ret_min = gp_add_option(&arg->options.options_val, &arg->options.options_len, ACQUIRE_TYPE_OPTION, sizeof(ACQUIRE_TYPE_OPTION), ACQUIRE_IMPERSONATE_NAME, sizeof(ACQUIRE_IMPERSONATE_NAME)); if (ret_min) { ret_maj = GSS_S_FAILURE; goto done; } } /* execute proxy request */ ret = gpm_make_call(GSSX_ACQUIRE_CRED, &uarg, &ures); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } if (res->status.major_status) { gpm_save_status(&res->status); ret_min = res->status.minor_status; ret_maj = res->status.major_status; goto done; } if (actual_mechs) { ret = gpmint_cred_to_actual_mechs(res->output_cred_handle, actual_mechs); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } } if (time_rec) { gssx_cred_element *e; uint32_t t = 0; if (res->output_cred_handle->elements.elements_len) { e = &res->output_cred_handle->elements.elements_val[0]; if (e->initiator_time_rec < e->acceptor_time_rec) { t = e->initiator_time_rec; } else { t = e->acceptor_time_rec; } } *time_rec = t; } /* we steal the cred handler here */ *output_cred_handle = res->output_cred_handle; res->output_cred_handle = NULL; ret_maj = GSS_S_COMPLETE; ret_min = 0; done: /* don't let gpm_free_xdrs free variables passed in */ arg->desired_name = NULL; arg->input_cred_handle = NULL; gpm_free_xdrs(GSSX_ACQUIRE_CRED, &uarg, &ures); *minor_status = ret_min; return ret_maj; } OM_uint32 gpm_add_cred(OM_uint32 *minor_status, gssx_cred *input_cred_handle, gssx_name *desired_name, const gss_OID desired_mech, gss_cred_usage_t cred_usage, OM_uint32 initiator_time_req, OM_uint32 acceptor_time_req, gssx_cred **output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *initiator_time_rec, OM_uint32 *acceptor_time_rec) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_acquire_cred *arg = &uarg.acquire_cred; gssx_res_acquire_cred *res = &ures.acquire_cred; gss_OID_set_desc mechs; uint32_t ret_min; uint32_t ret_maj; int ret = 0; memset(&uarg, 0, sizeof(union gp_rpc_arg)); memset(&ures, 0, sizeof(union gp_rpc_res)); /* ignore call_ctx for now */ if (input_cred_handle) { arg->input_cred_handle = input_cred_handle; } if (output_cred_handle != NULL) { arg->add_cred_to_input_handle = true; } arg->desired_name = desired_name; if (desired_mech != GSS_C_NO_OID) { mechs.count = 1; mechs.elements = desired_mech; ret = gp_conv_oid_set_to_gssx(&mechs, &arg->desired_mechs); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } } arg->cred_usage = gp_conv_cred_usage_to_gssx(cred_usage); arg->initiator_time_req = initiator_time_req; arg->acceptor_time_req = acceptor_time_req; /* execute proxy request */ ret = gpm_make_call(GSSX_ACQUIRE_CRED, &uarg, &ures); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } if (res->status.major_status) { gpm_save_status(&res->status); ret_min = res->status.minor_status; ret_maj = res->status.major_status; goto done; } if (actual_mechs) { ret = gpmint_cred_to_actual_mechs(res->output_cred_handle, actual_mechs); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } } if (res->output_cred_handle->elements.elements_len) { gssx_cred_element *e; e = &res->output_cred_handle->elements.elements_val[0]; if (initiator_time_rec) { *initiator_time_rec = e->initiator_time_rec; } if (acceptor_time_rec) { *acceptor_time_rec = e->initiator_time_rec; } } else { if (initiator_time_rec) { *initiator_time_rec = 0; } if (acceptor_time_rec) { *acceptor_time_rec = 0; } } if (output_cred_handle) { /* we steal the cred handler here */ *output_cred_handle = res->output_cred_handle; res->output_cred_handle = NULL; } ret_maj = GSS_S_COMPLETE; ret_min = 0; done: gpm_free_xdrs(GSSX_ACQUIRE_CRED, &uarg, &ures); *minor_status = ret_min; return ret_maj; } OM_uint32 gpm_inquire_cred(OM_uint32 *minor_status, gssx_cred *cred, gssx_name **name, OM_uint32 *lifetime, gss_cred_usage_t *cred_usage, gss_OID_set *mechanisms) { gss_OID_set mechs = GSS_C_NO_OID_SET; gssx_name *dname = NULL; gssx_cred_element *e; gss_OID_desc tmp_oid; uint32_t ret_min = 0; uint32_t ret_maj = GSS_S_COMPLETE; uint32_t life; int cu; if (!cred) { *minor_status = 0; return GSS_S_CALL_INACCESSIBLE_READ; } if (cred->elements.elements_len == 0) { *minor_status = 0; return GSS_S_FAILURE; } if (name) { ret_min = gp_copy_gssx_name_alloc(&cred->desired_name, &dname); if (ret_min != 0) { return GSS_S_FAILURE; } } if (mechanisms) { ret_maj = gss_create_empty_oid_set(&ret_min, &mechs); if (ret_maj) { goto done; } } life = GSS_C_INDEFINITE; cu = -1; for (unsigned i = 0; i < cred->elements.elements_len; i++) { e = &cred->elements.elements_val[i]; switch (e->cred_usage) { case GSSX_C_INITIATE: if (e->initiator_time_rec != 0 && e->initiator_time_rec < life) { life = e->initiator_time_rec; } switch (cu) { case GSS_C_BOTH: break; case GSS_C_ACCEPT: cu = GSS_C_BOTH; break; default: cu = GSS_C_INITIATE; } break; case GSSX_C_ACCEPT: if (e->acceptor_time_rec != 0 && e->acceptor_time_rec < life) { life = e->acceptor_time_rec; } switch (cu) { case GSS_C_BOTH: break; case GSS_C_INITIATE: cu = GSS_C_BOTH; break; default: cu = GSS_C_ACCEPT; } break; case GSSX_C_BOTH: if (e->initiator_time_rec != 0 && e->initiator_time_rec < life) { life = e->initiator_time_rec; } if (e->acceptor_time_rec != 0 && e->acceptor_time_rec < life) { life = e->acceptor_time_rec; } cu = GSS_C_BOTH; break; } if (mechanisms) { gp_conv_gssx_to_oid(&e->mech, &tmp_oid); ret_maj = gss_add_oid_set_member(&ret_min, &tmp_oid, &mechs); if (ret_maj) { goto done; } } } if (lifetime) { *lifetime = life; } if (cred_usage) { *cred_usage = cu; } done: *minor_status = ret_min; if (ret_maj == GSS_S_COMPLETE) { if (name) { *name = dname; } if (mechanisms) { *mechanisms = mechs; } } else { (void)gpm_release_name(&ret_min, &dname); (void)gss_release_oid_set(&ret_min, &mechs); } return ret_maj; } OM_uint32 gpm_inquire_cred_by_mech(OM_uint32 *minor_status, gssx_cred *cred, gss_OID mech_type, gssx_name **name, OM_uint32 *initiator_lifetime, OM_uint32 *acceptor_lifetime, gss_cred_usage_t *cred_usage) { gssx_name *dname = NULL; gssx_cred_element *e; gss_OID_desc tmp_oid; uint32_t ret_min = 0; uint32_t ret_maj = GSS_S_COMPLETE; unsigned i; if (!cred) { *minor_status = 0; return GSS_S_CALL_INACCESSIBLE_READ; } if (cred->elements.elements_len == 0) { *minor_status = 0; return GSS_S_FAILURE; } for (i = 0; i < cred->elements.elements_len; i++) { e = &cred->elements.elements_val[i]; gp_conv_gssx_to_oid(&e->mech, &tmp_oid); if (!gss_oid_equal(&tmp_oid, mech_type)) { continue; } switch (e->cred_usage) { case GSSX_C_INITIATE: if (initiator_lifetime) { *initiator_lifetime = e->initiator_time_rec; } if (cred_usage) { *cred_usage = GSS_C_INITIATE; } break; case GSSX_C_ACCEPT: if (acceptor_lifetime) { *acceptor_lifetime = e->acceptor_time_rec; } if (cred_usage) { *cred_usage = GSS_C_ACCEPT; } break; case GSSX_C_BOTH: if (initiator_lifetime) { *initiator_lifetime = e->initiator_time_rec; } if (acceptor_lifetime) { *acceptor_lifetime = e->acceptor_time_rec; } if (cred_usage) { *cred_usage = GSS_C_BOTH; } break; } if (name) { ret_min = gp_copy_gssx_name_alloc(&e->MN, &dname); if (ret_min != 0) { ret_maj = GSS_S_FAILURE; goto done; } *name = dname; } goto done; } if (i >= cred->elements.elements_len) { ret_maj = GSS_S_FAILURE; } done: *minor_status = ret_min; if (ret_maj != GSS_S_COMPLETE) { (void)gpm_release_name(&ret_min, &dname); } return ret_maj; } gssproxy-v0.8.2/src/client/gpm_common.c0000644000174300017420000004716413456641744017576 0ustar gitgit00000000000000/* Copyright (C) 2011,2017 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" #include #include #include #include #include #include #include #include #include #define FRAGMENT_BIT (1 << 31) #define RESPONSE_TIMEOUT 15 #define SAFETY_TIMEOUT RESPONSE_TIMEOUT * 10 * 1000 #define MAX_TIMEOUT_RETRY 3 struct gpm_ctx { pthread_mutex_t lock; int fd; /* these are only meaningful if fd != -1 */ pid_t pid; uid_t uid; gid_t gid; int next_xid; int epollfd; int timerfd; }; /* a single global struct is not particularly efficient, * but will do for now */ struct gpm_ctx gpm_global_ctx; pthread_once_t gpm_init_once_control = PTHREAD_ONCE_INIT; static void gpm_init_once(void) { pthread_mutexattr_t attr; unsigned int seedp; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&gpm_global_ctx.lock, &attr); gpm_global_ctx.fd = -1; gpm_global_ctx.epollfd = -1; gpm_global_ctx.timerfd = -1; seedp = time(NULL) + getpid() + pthread_self(); gpm_global_ctx.next_xid = rand_r(&seedp); pthread_mutexattr_destroy(&attr); gpm_display_status_init_once(); } static int get_pipe_name(char *name) { const char *socket; int ret; socket = gp_getenv("GSSPROXY_SOCKET"); if (!socket) { socket = GP_SOCKET_NAME; } ret = snprintf(name, PATH_MAX, "%s", socket); if (ret < 0 || ret >= PATH_MAX) { return ENAMETOOLONG; } return 0; } static int gpm_open_socket(struct gpm_ctx *gpmctx) { struct sockaddr_un addr = {0}; char name[PATH_MAX]; int ret; int fd = -1; ret = get_pipe_name(name); if (ret) { return ret; } addr.sun_family = AF_UNIX; strncpy(addr.sun_path, name, sizeof(addr.sun_path)-1); addr.sun_path[sizeof(addr.sun_path)-1] = '\0'; fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); if (fd == -1) { ret = errno; goto done; } ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); if (ret == -1) { ret = errno; } done: if (ret) { if (fd != -1) { close(fd); fd = -1; } } gpmctx->fd = fd; gpmctx->pid = getpid(); gpmctx->uid = geteuid(); gpmctx->gid = getegid(); return ret; } static void gpm_close_socket(struct gpm_ctx *gpmctx) { int ret; do { ret = close(gpmctx->fd); /* in theory we should retry to close() on EINTR, * but on same system the fd will be invalid after * close() has been called, so closing again may * cause a race with another thread that just happend * to open an unrelated file descriptor. * So until POSIX finally amends language around close() * and at least the Linux kernel changes its behavior, * it is better to risk a leak than closing an unrelated * file descriptor */ ret = 0; } while (ret == EINTR); gpmctx->fd = -1; } static void gpm_timer_close(struct gpm_ctx *gpmctx) { if (gpmctx->timerfd < 0) { return; } close(gpmctx->timerfd); gpmctx->timerfd = -1; } static int gpm_timer_setup(struct gpm_ctx *gpmctx, int timeout_seconds) { int ret; struct itimerspec its; if (gpmctx->timerfd >= 0) { gpm_timer_close(gpmctx); } gpmctx->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); if (gpmctx->timerfd < 0) { return errno; } its.it_interval.tv_sec = timeout_seconds; its.it_interval.tv_nsec = 0; its.it_value.tv_sec = timeout_seconds; its.it_value.tv_nsec = 0; ret = timerfd_settime(gpmctx->timerfd, 0, &its, NULL); if (ret) { ret = errno; gpm_timer_close(gpmctx); return ret; } return 0; } static void gpm_epoll_close(struct gpm_ctx *gpmctx) { if (gpmctx->epollfd < 0) { return; } close(gpmctx->epollfd); gpmctx->epollfd = -1; } static int gpm_epoll_setup(struct gpm_ctx *gpmctx) { struct epoll_event ev; int ret; if (gpmctx->epollfd >= 0) { gpm_epoll_close(gpmctx); } gpmctx->epollfd = epoll_create1(EPOLL_CLOEXEC); if (gpmctx->epollfd == -1) { return errno; } /* Add timer */ ev.events = EPOLLIN; ev.data.fd = gpmctx->timerfd; ret = epoll_ctl(gpmctx->epollfd, EPOLL_CTL_ADD, gpmctx->timerfd, &ev); if (ret == -1) { ret = errno; gpm_epoll_close(gpmctx); return ret; } return ret; } static int gpm_release_sock(struct gpm_ctx *gpmctx) { gpm_epoll_close(gpmctx); gpm_timer_close(gpmctx); return pthread_mutex_unlock(&gpmctx->lock); } static int gpm_grab_sock(struct gpm_ctx *gpmctx) { int ret; pid_t p; uid_t u; gid_t g; ret = pthread_mutex_lock(&gpmctx->lock); if (ret) { return ret; } /* Detect fork / setresuid and friends */ p = getpid(); u = geteuid(); g = getegid(); if (gpmctx->fd != -1 && (p != gpmctx->pid || u != gpmctx->uid || g != gpmctx->gid)) { gpm_close_socket(gpmctx); } if (gpmctx->fd == -1) { ret = gpm_open_socket(gpmctx); if (ret) { goto done; } } /* setup timer */ ret = gpm_timer_setup(gpmctx, RESPONSE_TIMEOUT); if (ret) { goto done; } /* create epoll fd as well */ ret = gpm_epoll_setup(gpmctx); done: if (ret) { gpm_release_sock(gpmctx); } return ret; } static int gpm_epoll_wait(struct gpm_ctx *gpmctx, uint32_t event_flags) { int ret; int epoll_ret; struct epoll_event ev; struct epoll_event events[2]; uint64_t timer_read; if (gpmctx->epollfd < 0) { ret = gpm_epoll_setup(gpmctx); if (ret) return ret; } ev.events = event_flags; ev.data.fd = gpmctx->fd; epoll_ret = epoll_ctl(gpmctx->epollfd, EPOLL_CTL_ADD, gpmctx->fd, &ev); if (epoll_ret == -1) { ret = errno; gpm_epoll_close(gpmctx); return ret; } do { epoll_ret = epoll_wait(gpmctx->epollfd, events, 2, SAFETY_TIMEOUT); } while (epoll_ret < 0 && errno == EINTR); if (epoll_ret < 0) { /* Error while waiting that isn't EINTR */ ret = errno; gpm_epoll_close(gpmctx); } else if (epoll_ret == 0) { ret = ETIMEDOUT; gpm_epoll_close(gpmctx); } else if (epoll_ret == 1 && events[0].data.fd == gpmctx->timerfd) { /* Got an event which is only our timer */ if ((events[0].events & EPOLLIN) == 0) { /* We got an event which was not EPOLLIN; assume this is an error, * and exit with EBADF: epoll_wait said timerfd had an event, * but that event is not an EPOLIN event. */ ret = EBADF; } else { ret = read(gpmctx->timerfd, &timer_read, sizeof(uint64_t)); if (ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { /* In the case when reading from the timer failed, don't hide the * timer error behind ETIMEDOUT such that it isn't retried */ ret = errno; } else { /* If ret == 0, then we definitely timed out. Else, if ret == -1 * and errno == EAGAIN or errno == EWOULDBLOCK, we're in a weird * edge case where epoll thinks the timer can be read, but it * is blocking more; treat it like a TIMEOUT and retry, as * nothing around us would handle EAGAIN from timer and retry * it. */ ret = ETIMEDOUT; } } gpm_epoll_close(gpmctx); } else { /* If ret == 2, then we ignore the timerfd; that way if the next * operation cannot be performed immediately, we timeout and retry. * Always check the returned event of the socket fd. */ int fd_index = 0; if (epoll_ret == 2 && events[fd_index].data.fd != gpmctx->fd) { fd_index = 1; } if ((events[fd_index].events & event_flags) == 0) { /* We cannot call EPOLLIN/EPOLLOUT at this time; assume that this * is a fatal error; return with EBADFD to distinguish from * EBADF in timer_fd case. */ ret = EBADFD; gpm_epoll_close(gpmctx); } else { /* We definintely got a EPOLLIN/EPOLLOUT event; return success. */ ret = 0; } } epoll_ret = epoll_ctl(gpmctx->epollfd, EPOLL_CTL_DEL, gpmctx->fd, NULL); if (epoll_ret == -1) { /* If we previously had an error, expose that error instead of * clobbering it with errno; else if no error, then assume it is * better to notify of the error deleting the event than it is * to continue. */ if (ret == 0) ret = errno; gpm_epoll_close(gpmctx); } return ret; } static int gpm_retry_socket(struct gpm_ctx *gpmctx) { gpm_epoll_close(gpmctx); gpm_close_socket(gpmctx); return gpm_open_socket(gpmctx); } /* must be called after the lock has been grabbed */ static int gpm_send_buffer(struct gpm_ctx *gpmctx, char *buffer, uint32_t length) { uint32_t size; ssize_t wn; size_t pos; bool retry; int ret; if (length > MAX_RPC_SIZE) { return EINVAL; } size = length | FRAGMENT_BIT; size = htonl(size); retry = false; do { do { ret = gpm_epoll_wait(gpmctx, EPOLLOUT); if (ret != 0) { goto done; } ret = 0; wn = write(gpmctx->fd, &size, sizeof(uint32_t)); if (wn == -1) { ret = errno; } } while (ret == EINTR); if (wn != 4) { /* reopen and retry once */ if (retry == false) { ret = gpm_retry_socket(gpmctx); if (ret == 0) { retry = true; continue; } } else { ret = EIO; } goto done; } retry = false; } while (retry); pos = 0; while (length > pos) { ret = gpm_epoll_wait(gpmctx, EPOLLOUT); if (ret) { goto done; } wn = write(gpmctx->fd, buffer + pos, length - pos); if (wn == -1) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { continue; } ret = errno; goto done; } pos += wn; } ret = 0; done: /* we only need to return as gpm_retry_socket closes the socket */ return ret; } /* must be called after the lock has been grabbed */ static int gpm_recv_buffer(struct gpm_ctx *gpmctx, char **buffer, uint32_t *length) { uint32_t size; ssize_t rn; size_t pos; int ret; do { ret = gpm_epoll_wait(gpmctx, EPOLLIN); if (ret) { goto done; } ret = 0; rn = read(gpmctx->fd, &size, sizeof(uint32_t)); if (rn == -1) { ret = errno; } } while (ret == EINTR); if (rn != 4) { ret = EIO; goto done; } *length = ntohl(size); *length &= ~FRAGMENT_BIT; if (*length > MAX_RPC_SIZE) { ret = EMSGSIZE; goto done; } *buffer = malloc(*length); if (*buffer == NULL) { ret = ENOMEM; goto done; } pos = 0; while (*length > pos) { ret = gpm_epoll_wait(gpmctx, EPOLLIN); if (ret) { goto done; } rn = read(gpmctx->fd, *buffer + pos, *length - pos); if (rn == -1) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { continue; } ret = errno; goto done; } if (rn == 0) { ret = EIO; goto done; } pos += rn; } ret = 0; done: if (ret) { /* on errors, free the buffer to prevent calling * xdr_destroy(&xdr_reply_ctx); */ free(*buffer); *buffer = NULL; } return ret; } /* must be called after the lock has been grabbed */ static uint32_t gpm_next_xid(struct gpm_ctx *gpmctx) { uint32_t xid; if (gpmctx->next_xid < 0) { gpmctx->next_xid = 1; xid = 0; } else { xid = gpmctx->next_xid++; } return xid; } static struct gpm_ctx *gpm_get_ctx(void) { pthread_once(&gpm_init_once_control, gpm_init_once); return &gpm_global_ctx; } static int gpm_send_recv_loop(struct gpm_ctx *gpmctx, char *send_buffer, uint32_t send_length, char** recv_buffer, uint32_t *recv_length) { int ret; int retry_count; for (retry_count = 0; retry_count < MAX_TIMEOUT_RETRY; retry_count++) { /* send to proxy */ ret = gpm_send_buffer(gpmctx, send_buffer, send_length); if (ret == 0) { /* No error, continue to recv */ } else if (ret == ETIMEDOUT) { /* Close and reopen socket before trying again */ ret = gpm_retry_socket(gpmctx); if (ret != 0) return ret; ret = ETIMEDOUT; /* RETRY entire send */ continue; } else { /* Other error */ return ret; } /* receive answer */ ret = gpm_recv_buffer(gpmctx, recv_buffer, recv_length); if (ret == 0) { /* No error */ break; } else if (ret == ETIMEDOUT) { /* Close and reopen socket before trying again */ ret = gpm_retry_socket(gpmctx); if (ret != 0) return ret; ret = ETIMEDOUT; } else { /* Other error */ return ret; } } return ret; } OM_uint32 gpm_release_buffer(OM_uint32 *minor_status, gss_buffer_t buffer) { *minor_status = 0; if (buffer != GSS_C_NO_BUFFER) { if (buffer->value) { free(buffer->value); } buffer->length = 0; buffer->value = NULL; } return GSS_S_COMPLETE; } struct gpm_rpc_fn_set { xdrproc_t arg_fn; xdrproc_t res_fn; } gpm_xdr_set[] = { { /* NULLPROC */ (xdrproc_t)xdr_void, (xdrproc_t)xdr_void, }, { /* GSSX_INDICATE_MECHS */ (xdrproc_t)xdr_gssx_arg_indicate_mechs, (xdrproc_t)xdr_gssx_res_indicate_mechs, }, { /* GSSX_GET_CALL_CONTEXT */ (xdrproc_t)xdr_gssx_arg_get_call_context, (xdrproc_t)xdr_gssx_res_get_call_context, }, { /* GSSX_IMPORT_AND_CANON_NAME */ (xdrproc_t)xdr_gssx_arg_import_and_canon_name, (xdrproc_t)xdr_gssx_res_import_and_canon_name, }, { /* GSSX_EXPORT_CRED */ (xdrproc_t)xdr_gssx_arg_export_cred, (xdrproc_t)xdr_gssx_res_export_cred, }, { /* GSSX_IMPORT_CRED */ (xdrproc_t)xdr_gssx_arg_import_cred, (xdrproc_t)xdr_gssx_res_import_cred, }, { /* GSSX_ACQUIRE_CRED */ (xdrproc_t)xdr_gssx_arg_acquire_cred, (xdrproc_t)xdr_gssx_res_acquire_cred, }, { /* GSSX_STORE_CRED */ (xdrproc_t)xdr_gssx_arg_store_cred, (xdrproc_t)xdr_gssx_res_store_cred, }, { /* GSSX_INIT_SEC_CONTEXT */ (xdrproc_t)xdr_gssx_arg_init_sec_context, (xdrproc_t)xdr_gssx_res_init_sec_context, }, { /* GSSX_ACCEPT_SEC_CONTEXT */ (xdrproc_t)xdr_gssx_arg_accept_sec_context, (xdrproc_t)xdr_gssx_res_accept_sec_context, }, { /* GSSX_RELEASE_HANDLE */ (xdrproc_t)xdr_gssx_arg_release_handle, (xdrproc_t)xdr_gssx_res_release_handle, }, { /* GSSX_GET_MIC */ (xdrproc_t)xdr_gssx_arg_get_mic, (xdrproc_t)xdr_gssx_res_get_mic, }, { /* GSSX_VERIFY */ (xdrproc_t)xdr_gssx_arg_verify_mic, (xdrproc_t)xdr_gssx_res_verify_mic, }, { /* GSSX_WRAP */ (xdrproc_t)xdr_gssx_arg_wrap, (xdrproc_t)xdr_gssx_res_wrap, }, { /* GSSX_UNWRAP */ (xdrproc_t)xdr_gssx_arg_unwrap, (xdrproc_t)xdr_gssx_res_unwrap, }, { /* GSSX_WRAP_SIZE_LIMIT */ (xdrproc_t)xdr_gssx_arg_wrap_size_limit, (xdrproc_t)xdr_gssx_res_wrap_size_limit, } }; int gpm_make_call(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res) { struct gpm_ctx *gpmctx; gp_rpc_msg msg; XDR xdr_call_ctx = {0}; XDR xdr_reply_ctx = {0}; char *send_buffer = NULL; char *recv_buffer = NULL; uint32_t send_length; uint32_t recv_length; uint32_t xid; bool xdrok; bool sockgrab = false; int ret; send_buffer = malloc(MAX_RPC_SIZE); if (send_buffer == NULL) return ENOMEM; xdrmem_create(&xdr_call_ctx, send_buffer, MAX_RPC_SIZE, XDR_ENCODE); memset(&msg, 0, sizeof(gp_rpc_msg)); msg.header.type = GP_RPC_CALL; msg.header.gp_rpc_msg_union_u.chdr.rpcvers = 2; msg.header.gp_rpc_msg_union_u.chdr.prog = GSSPROXY; msg.header.gp_rpc_msg_union_u.chdr.vers = GSSPROXYVERS; msg.header.gp_rpc_msg_union_u.chdr.proc = proc; msg.header.gp_rpc_msg_union_u.chdr.cred.flavor = GP_RPC_AUTH_NONE; msg.header.gp_rpc_msg_union_u.chdr.cred.body.body_len = 0; msg.header.gp_rpc_msg_union_u.chdr.cred.body.body_val = NULL; msg.header.gp_rpc_msg_union_u.chdr.verf.flavor = GP_RPC_AUTH_NONE; msg.header.gp_rpc_msg_union_u.chdr.verf.body.body_len = 0; msg.header.gp_rpc_msg_union_u.chdr.verf.body.body_val = NULL; gpmctx = gpm_get_ctx(); if (!gpmctx) { return EINVAL; } /* grab the lock for the whole conversation */ ret = gpm_grab_sock(gpmctx); if (ret) { goto done; } sockgrab = true; msg.xid = xid = gpm_next_xid(gpmctx); /* encode header */ xdrok = xdr_gp_rpc_msg(&xdr_call_ctx, &msg); if (!xdrok) { ret = EINVAL; goto done; } /* encode data */ xdrok = gpm_xdr_set[proc].arg_fn(&xdr_call_ctx, (char *)arg); if (!xdrok) { ret = EINVAL; goto done; } /* set send_length */ send_length = xdr_getpos(&xdr_call_ctx); /* Send request, receive response with timeout */ ret = gpm_send_recv_loop(gpmctx, send_buffer, send_length, &recv_buffer, &recv_length); if (ret) goto done; /* release the lock */ gpm_release_sock(gpmctx); sockgrab = false; /* Create the reply context */ xdrmem_create(&xdr_reply_ctx, recv_buffer, recv_length, XDR_DECODE); /* decode header */ memset(&msg, 0, sizeof(gp_rpc_msg)); xdrok = xdr_gp_rpc_msg(&xdr_reply_ctx, &msg); if (!xdrok) { ret = EINVAL; goto done; } if (msg.xid != xid || msg.header.type != GP_RPC_REPLY || msg.header.gp_rpc_msg_union_u.rhdr.status != GP_RPC_MSG_ACCEPTED || msg.header.gp_rpc_msg_union_u.rhdr.gp_rpc_reply_header_u.accepted.reply_data.status != GP_RPC_SUCCESS) { ret = EINVAL; goto done; } /* decode answer */ xdrok = gpm_xdr_set[proc].res_fn(&xdr_reply_ctx, (char *)res); if (!xdrok) { ret = EINVAL; } done: if (sockgrab) { gpm_release_sock(gpmctx); } xdr_free((xdrproc_t)xdr_gp_rpc_msg, (char *)&msg); xdr_destroy(&xdr_call_ctx); if (recv_buffer != NULL) xdr_destroy(&xdr_reply_ctx); free(send_buffer); free(recv_buffer); return ret; } void gpm_free_xdrs(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res) { xdr_free(gpm_xdr_set[proc].arg_fn, (char *)arg); xdr_free(gpm_xdr_set[proc].res_fn, (char *)res); } gssproxy-v0.8.2/src/client/gpm_display_status.c0000644000174300017420000000763313456641744021353 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" #include static pthread_key_t gpm_last_status; static void gpm_destroy_last_status(void *arg) { gssx_status *status = (gssx_status *)arg; xdr_free((xdrproc_t)xdr_gssx_status, (char *)status); free(status); } void gpm_display_status_init_once(void) { (void)pthread_key_create(&gpm_last_status, gpm_destroy_last_status); } /* Portable thread local storage for return status. */ void gpm_save_status(gssx_status *status) { gssx_status *last_status; int ret; last_status = (gssx_status *)pthread_getspecific(gpm_last_status); if (last_status != NULL) { /* store NULL first so we do not risk a double free if we are * racing on a pthread_cancel */ pthread_setspecific(gpm_last_status, NULL); gpm_destroy_last_status(last_status); } ret = gp_copy_gssx_status_alloc(status, &last_status); if (ret == 0) { pthread_setspecific(gpm_last_status, last_status); } } gssx_status *gpm_get_saved_status(void) { return (gssx_status *)pthread_getspecific(gpm_last_status); } /* This funciton is used to record internal mech errors that are * generated by the proxy client code */ void gpm_save_internal_status(uint32_t err, char *err_str) { gssx_status status; memset(&status, 0, sizeof(gssx_status)); #define STD_MAJ_ERROR_STR "Internal gssproxy error" status.major_status = GSS_S_FAILURE; status.major_status_string.utf8string_val = strdup(STD_MAJ_ERROR_STR); status.major_status_string.utf8string_len = sizeof(STD_MAJ_ERROR_STR); status.minor_status = err; status.minor_status_string.utf8string_val = err_str; status.minor_status_string.utf8string_len = strlen(err_str) + 1; gpm_save_status(&status); } OM_uint32 gpm_display_status(OM_uint32 *minor_status, OM_uint32 status_value, int status_type, const gss_OID mech_type UNUSED, OM_uint32 *message_context, gss_buffer_t status_string) { gssx_status *last_status = gpm_get_saved_status(); utf8string tmp; int ret; switch(status_type) { case GSS_C_GSS_CODE: if (last_status && last_status->major_status == status_value && last_status->major_status_string.utf8string_len) { ret = gp_copy_utf8string(&last_status->major_status_string, &tmp); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } status_string->value = tmp.utf8string_val; status_string->length = tmp.utf8string_len; *minor_status = 0; return GSS_S_COMPLETE; } else { /* if we do not have it, make it clear */ return GSS_S_UNAVAILABLE; } case GSS_C_MECH_CODE: if (last_status && last_status->minor_status == status_value && last_status->minor_status_string.utf8string_len) { if (*message_context) { /* we do not support multiple messages for now */ *minor_status = EINVAL; return GSS_S_FAILURE; } ret = gp_copy_utf8string(&last_status->minor_status_string, &tmp); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } status_string->value = tmp.utf8string_val; status_string->length = tmp.utf8string_len; } else { /* if we do not have it, make it clear */ return GSS_S_UNAVAILABLE; } *minor_status = 0; return GSS_S_COMPLETE; default: *minor_status = EINVAL; return GSS_S_BAD_STATUS; } } gssproxy-v0.8.2/src/client/gpm_get_mic.c0000644000174300017420000000434313456641744017705 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" #include "src/gp_conv.h" OM_uint32 gpm_get_mic(OM_uint32 *minor_status, gssx_ctx *context_handle, gss_qop_t qop_req, gss_buffer_t message_buffer, gss_buffer_t message_token) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_get_mic *arg = &uarg.get_mic; gssx_res_get_mic *res = &ures.get_mic; uint32_t ret_min = 0; uint32_t ret_maj = 0; int ret = 0; memset(&uarg, 0, sizeof(union gp_rpc_arg)); memset(&ures, 0, sizeof(union gp_rpc_res)); if (!context_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* format request */ /* NOTE: the final free will also release the old context */ arg->context_handle = *context_handle; arg->qop_req = qop_req; ret = gp_conv_buffer_to_gssx(message_buffer, &arg->message_buffer); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } /* execute proxy request */ ret = gpm_make_call(GSSX_GET_MIC, &uarg, &ures); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } /* Check and save error status */ if (res->status.major_status) { gpm_save_status(&res->status); ret_min = res->status.minor_status; ret_maj = res->status.major_status; goto done; } ret = gp_copy_gssx_to_buffer(&res->token_buffer, message_token); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } done: /* Steal the new context if available. * NOTE: We do not want it to be freed by xdr_free, so copy the contents * and cear up the structure to be freed so contents are not freed. */ if (res->context_handle) { *context_handle = *res->context_handle; memset(res->context_handle, 0, sizeof(gssx_ctx)); } else { /* prevent the contexthandle from being destroyed in case of server * error. */ memset(&arg->context_handle, 0, sizeof(gssx_ctx)); } gpm_free_xdrs(GSSX_GET_MIC, &uarg, &ures); *minor_status = ret_min; return ret_maj; } gssproxy-v0.8.2/src/client/gpm_import_and_canon_name.c0000644000174300017420000002360013456641744022605 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" OM_uint32 gpm_display_name(OM_uint32 *minor_status, gssx_name *in_name, gss_buffer_t output_name_buffer, gss_OID *output_name_type) { gss_buffer_desc input_name_buffer = GSS_C_EMPTY_BUFFER; gssx_name *output_name = NULL; uint32_t ret_maj; uint32_t ret_min; uint32_t discard; int ret; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } *minor_status = 0; if (!in_name) { return GSS_S_CALL_INACCESSIBLE_READ; } if (!output_name_buffer) { return GSS_S_CALL_INACCESSIBLE_WRITE; } if (in_name->display_name.octet_string_len == 0) { if (in_name->exported_name.octet_string_len == 0) { return GSS_S_BAD_NAME; } gp_conv_gssx_to_buffer(&in_name->exported_name, &input_name_buffer); ret_maj = gpm_import_name(&ret_min, &input_name_buffer, GSS_C_NT_EXPORT_NAME, &output_name); if (ret_maj) { goto done; } /* steal display_name and name_type */ in_name->display_name = output_name->display_name; output_name->display_name.octet_string_len = 0; output_name->display_name.octet_string_val = NULL; in_name->name_type = output_name->name_type; output_name->name_type.octet_string_len = 0; output_name->name_type.octet_string_val = NULL; } ret = gp_copy_gssx_to_string_buffer(&in_name->display_name, output_name_buffer); if (ret) { ret_min = ret; ret_maj = GSS_S_FAILURE; goto done; } if (output_name_type) { ret = gp_conv_gssx_to_oid_alloc(&in_name->name_type, output_name_type); if (ret) { gss_release_buffer(&discard, output_name_buffer); ret_min = ret; ret_maj = GSS_S_FAILURE; goto done; } } ret_min = 0; ret_maj = GSS_S_COMPLETE; done: if (output_name) { xdr_free((xdrproc_t)xdr_gssx_name, (char *)output_name); free(output_name); } *minor_status = ret_min; return ret_maj; } OM_uint32 gpm_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer, gss_OID input_name_type, gssx_name **output_name) { gssx_name *name; uint32_t maj, min; int ret; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } *minor_status = 0; if (!input_name_buffer || !input_name_type) { return GSS_S_CALL_INACCESSIBLE_READ; } if (!output_name) { return GSS_S_CALL_INACCESSIBLE_WRITE; } /* ignore call_ctx for now */ maj = GSS_S_FAILURE; name = calloc(1, sizeof(gssx_name)); if (!name) { ret = ENOMEM; goto done; } ret = gp_conv_buffer_to_gssx(input_name_buffer, &name->display_name); if (ret) { goto done; } ret = gp_conv_oid_to_gssx(input_name_type, &name->name_type); if (ret) { goto done; } maj = GSS_S_COMPLETE; done: *minor_status = ret; if (maj == GSS_S_COMPLETE) { *output_name = name; } else { (void)gpm_release_name(&min, &name); } return maj; } OM_uint32 gpm_export_name(OM_uint32 *minor_status, gssx_name *input_name, gss_buffer_t exported_name) { int ret; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } *minor_status = 0; if (!input_name) { return GSS_S_CALL_INACCESSIBLE_READ; } if (input_name->exported_name.octet_string_len == 0) { return GSS_S_NAME_NOT_MN; } ret = gp_copy_gssx_to_buffer(&input_name->exported_name, exported_name); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } return GSS_S_COMPLETE; } OM_uint32 gpm_export_name_composite(OM_uint32 *minor_status, gssx_name *input_name, gss_buffer_t exported_composite_name) { int ret; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } *minor_status = 0; if (!input_name) { return GSS_S_CALL_INACCESSIBLE_READ; } if (input_name->exported_composite_name.octet_string_len == 0) { return GSS_S_NAME_NOT_MN; } ret = gp_copy_gssx_to_buffer(&input_name->exported_composite_name, exported_composite_name); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } return GSS_S_COMPLETE; } OM_uint32 gpm_duplicate_name(OM_uint32 *minor_status, gssx_name *input_name, gssx_name **dest_name) { int ret; ret = gp_copy_gssx_name_alloc(input_name, dest_name); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } return GSS_S_COMPLETE; } OM_uint32 gpm_canonicalize_name(OM_uint32 *minor_status, gssx_name *input_name, const gss_OID mech_type, gssx_name **output_name) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_import_and_canon_name *arg = &uarg.import_and_canon_name; gssx_res_import_and_canon_name *res = &ures.import_and_canon_name; uint32_t ret_maj; uint32_t ret_min; int ret; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } *minor_status = 0; if (!input_name || !mech_type) { return GSS_S_CALL_INACCESSIBLE_READ; } if (!output_name) { return GSS_S_CALL_INACCESSIBLE_WRITE; } memset(arg, 0, sizeof(gssx_arg_import_and_canon_name)); memset(res, 0, sizeof(gssx_res_import_and_canon_name)); /* ignore call_ctx for now */ ret = gp_copy_gssx_name(input_name, &arg->input_name); if (ret) { goto done; } ret = gp_conv_oid_to_gssx(mech_type, &arg->mech); if (ret) { goto done; } /* execute proxy request */ ret = gpm_make_call(GSSX_IMPORT_AND_CANON_NAME, &uarg, &ures); if (ret) { goto done; } ret_min = res->status.minor_status; ret_maj = res->status.major_status; if (res->status.major_status) { gpm_save_status(&res->status); ret = 0; goto done; } /* steal output_name */ *output_name = res->output_name; res->output_name = NULL; done: if (ret) { ret_min = ret; ret_maj = GSS_S_FAILURE; } gpm_free_xdrs(GSSX_IMPORT_AND_CANON_NAME, &uarg, &ures); *minor_status = ret_min; return ret_maj; } OM_uint32 gpm_inquire_name(OM_uint32 *minor_status, gssx_name *name, int *name_is_MN, gss_OID *MN_mech, gss_buffer_set_t *attrs) { gss_buffer_set_t xattrs = GSS_C_NO_BUFFER_SET; int ret; *minor_status = 0; if (name->exported_name.octet_string_len != 0) { if (name_is_MN != NULL) { *name_is_MN = 1; } } if (MN_mech != NULL) { ret = gp_conv_gssx_to_oid_alloc(&name->name_type, MN_mech); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } } if (name->name_attributes.name_attributes_len != 0) { xattrs = calloc(1, sizeof(gss_buffer_set_desc)); if (!xattrs) { *minor_status = ENOMEM; return GSS_S_FAILURE; } xattrs->count = name->name_attributes.name_attributes_len; xattrs->elements = calloc(xattrs->count, sizeof(gss_buffer_desc)); if (!xattrs->elements) { free(xattrs); *minor_status = ENOMEM; return GSS_S_FAILURE; } for (unsigned i = 0; i < xattrs->count; i++) { ret = gp_copy_gssx_to_buffer( &name->name_attributes.name_attributes_val[i].attr, &xattrs->elements[i]); if (ret) { for (; i > 0; i--) { free(xattrs->elements[i-1].value); } free(xattrs->elements); free(xattrs); *minor_status = ENOMEM; return GSS_S_FAILURE; } } } *attrs = xattrs; return GSS_S_COMPLETE; } OM_uint32 gpm_release_name(OM_uint32 *minor_status, gssx_name **input_name) { *minor_status = 0; if (*input_name != NULL) { xdr_free((xdrproc_t)xdr_gssx_name, (char *)(*input_name)); free(*input_name); *input_name = NULL; } return GSS_S_COMPLETE; } OM_uint32 gpm_compare_name(OM_uint32 *minor_status, gssx_name *name1, gssx_name *name2, int *name_equal) { gss_buffer_desc buf1 = {0}; gss_buffer_desc buf2 = {0}; gss_OID type1 = GSS_C_NO_OID; gss_OID type2 = GSS_C_NO_OID; uint32_t ret_maj; uint32_t ret_min; int c; *name_equal = 0; ret_maj = gpm_display_name(&ret_min, name1, &buf1, &type1); if (ret_maj != GSS_S_COMPLETE) { goto done; } ret_maj = gpm_display_name(&ret_min, name2, &buf2, &type2); if (ret_maj != GSS_S_COMPLETE) { goto done; } c = buf1.length - buf2.length; if (c == 0) { c = memcmp(buf1.value, buf2.value, buf1.length); if (c == 0) { c = gss_oid_equal(type1, type2); } } if (c != 0) { *name_equal = 1; } ret_min = 0; ret_maj = GSS_S_COMPLETE; done: *minor_status = ret_min; gss_release_buffer(&ret_min, &buf1); gss_release_buffer(&ret_min, &buf2); gss_release_oid(&ret_min, &type1); gss_release_oid(&ret_min, &type2); return ret_maj; } gssproxy-v0.8.2/src/client/gpm_indicate_mechs.c0000644000174300017420000005140113456641744021232 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" #include struct gpm_mech_info { gss_OID mech; gss_OID_set name_types; gss_OID_set mech_attrs; gss_OID_set known_mech_attrs; gss_OID_set cred_options; gss_OID_set sec_ctx_options; gss_buffer_t saslname_sasl_mech_name; gss_buffer_t saslname_mech_name; gss_buffer_t saslname_mech_desc; }; struct gpm_mech_attr { gss_OID attr; gss_buffer_t name; gss_buffer_t short_desc; gss_buffer_t long_desc; }; struct gpm_mechs { bool initialized; gss_OID_set mech_set; size_t info_len; struct gpm_mech_info *info; size_t desc_len; struct gpm_mech_attr *desc; }; pthread_mutex_t global_mechs_lock = PTHREAD_MUTEX_INITIALIZER; pthread_once_t indicate_mechs_once = PTHREAD_ONCE_INIT; struct gpm_mechs global_mechs = { .initialized = false, .mech_set = GSS_C_NO_OID_SET, .info_len = 0, .info = NULL, .desc_len = 0, .desc = NULL, }; static uint32_t gpm_copy_gss_OID_set(uint32_t *minor_status, gss_OID_set oldset, gss_OID_set *newset) { gss_OID_set n; uint32_t ret_maj; uint32_t ret_min; ret_maj = gss_create_empty_oid_set(&ret_min, &n); if (ret_maj) { *minor_status = ret_min; return ret_maj; } for (size_t i = 0; i < oldset->count; i++) { ret_maj = gss_add_oid_set_member(&ret_min, &oldset->elements[i], &n); if (ret_maj) { *minor_status = ret_min; gss_release_oid_set(&ret_min, &n); return ret_maj; } } *newset = n; *minor_status = 0; return GSS_S_COMPLETE; } static uint32_t gpm_copy_gss_buffer(uint32_t *minor_status, gss_buffer_t oldbuf, gss_buffer_t newbuf) { if (!oldbuf || oldbuf->length == 0) { newbuf->value = NULL; newbuf->length = 0; *minor_status = 0; return GSS_S_COMPLETE; } newbuf->value = malloc(oldbuf->length); if (!newbuf->value) { *minor_status = ENOMEM; return GSS_S_FAILURE; } memcpy(newbuf->value, oldbuf->value, oldbuf->length); newbuf->length = oldbuf->length; *minor_status = 0; return GSS_S_COMPLETE; } static bool gpm_equal_oids(gss_const_OID a, gss_const_OID b) { int ret; if (a->length == b->length) { ret = memcmp(a->elements, b->elements, a->length); if (ret == 0) { return true; } } return false; } static void gpmint_indicate_mechs(void) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_indicate_mechs *arg = &uarg.indicate_mechs; gssx_res_indicate_mechs *res = &ures.indicate_mechs; struct gpm_mech_info *gi; struct gpm_mech_attr *ga; gssx_mech_info *mi; gssx_mech_attr *ma; uint32_t discard; uint32_t ret_min; uint32_t ret_maj = 0; int ret = 0; memset(arg, 0, sizeof(gssx_arg_indicate_mechs)); memset(res, 0, sizeof(gssx_res_indicate_mechs)); /* ignore call_ctx for now */ /* execute proxy request */ ret = gpm_make_call(GSSX_INDICATE_MECHS, &uarg, &ures); if (ret) { goto done; } if (res->status.major_status) { gpm_save_status(&res->status); ret_min = res->status.minor_status; ret_maj = res->status.major_status; ret = 0; goto done; } ret_maj = gss_create_empty_oid_set(&ret_min, &global_mechs.mech_set); if (ret_maj) { goto done; } global_mechs.info = calloc(res->mechs.mechs_len, sizeof(struct gpm_mech_info)); if (!global_mechs.info) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } for (unsigned i = 0; i < res->mechs.mechs_len; i++) { mi = &res->mechs.mechs_val[i]; gi = &global_mechs.info[i]; ret = gp_conv_gssx_to_oid_alloc(&mi->mech, &gi->mech); if (ret) { goto done; } ret_maj = gss_add_oid_set_member(&ret_min, gi->mech, &global_mechs.mech_set); if (ret_maj) { goto done; } ret = gp_conv_gssx_to_oid_set(&mi->name_types, &gi->name_types); if (ret) { goto done; } ret = gp_conv_gssx_to_oid_set(&mi->mech_attrs, &gi->mech_attrs); if (ret) { goto done; } ret = gp_conv_gssx_to_oid_set(&mi->known_mech_attrs, &gi->known_mech_attrs); if (ret) { goto done; } ret = gp_conv_gssx_to_oid_set(&mi->cred_options, &gi->cred_options); if (ret) { goto done; } ret = gp_conv_gssx_to_oid_set(&mi->sec_ctx_options, &gi->sec_ctx_options); if (ret) { goto done; } ret = gp_conv_gssx_to_buffer_alloc(&mi->saslname_sasl_mech_name, &gi->saslname_sasl_mech_name); if (ret) { goto done; } ret = gp_conv_gssx_to_buffer_alloc(&mi->saslname_mech_name, &gi->saslname_mech_name); if (ret) { goto done; } ret = gp_conv_gssx_to_buffer_alloc(&mi->saslname_mech_desc, &gi->saslname_mech_desc); if (ret) { goto done; } } global_mechs.info_len = res->mechs.mechs_len; global_mechs.desc = calloc(res->mech_attr_descs.mech_attr_descs_len, sizeof(struct gpm_mech_attr)); if (!global_mechs.desc) { goto done; } for (unsigned i = 0; i < res->mech_attr_descs.mech_attr_descs_len; i++) { ma = &res->mech_attr_descs.mech_attr_descs_val[i]; ga = &global_mechs.desc[i]; ret = gp_conv_gssx_to_oid_alloc(&ma->attr, &ga->attr); if (ret) { goto done; } ret = gp_conv_gssx_to_buffer_alloc(&ma->name, &ga->name); if (ret) { goto done; } ret = gp_conv_gssx_to_buffer_alloc(&ma->short_desc, &ga->short_desc); if (ret) { goto done; } ret = gp_conv_gssx_to_buffer_alloc(&ma->long_desc, &ga->long_desc); if (ret) { goto done; } } global_mechs.desc_len = res->mech_attr_descs.mech_attr_descs_len; global_mechs.initialized = true; done: if (ret || ret_maj) { for (unsigned i = 0; i < global_mechs.desc_len; i++) { ga = &global_mechs.desc[i]; gss_release_oid(&discard, &ga->attr); gss_release_buffer(&discard, ga->name); gss_release_buffer(&discard, ga->short_desc); gss_release_buffer(&discard, ga->long_desc); } free(global_mechs.desc); global_mechs.desc = NULL; for (unsigned i = 0; i < global_mechs.info_len; i++) { gi = &global_mechs.info[i]; gss_release_oid(&discard, &gi->mech); gss_release_oid_set(&discard, &gi->name_types); gss_release_oid_set(&discard, &gi->mech_attrs); gss_release_oid_set(&discard, &gi->known_mech_attrs); gss_release_oid_set(&discard, &gi->cred_options); gss_release_oid_set(&discard, &gi->sec_ctx_options); gss_release_buffer(&discard, gi->saslname_sasl_mech_name); gss_release_buffer(&discard, gi->saslname_mech_name); gss_release_buffer(&discard, gi->saslname_mech_desc); } free(global_mechs.info); global_mechs.info = NULL; gss_release_oid_set(&discard, &global_mechs.mech_set); } gpm_free_xdrs(GSSX_INDICATE_MECHS, &uarg, &ures); } static int gpmint_init_global_mechs(void) { pthread_once(&indicate_mechs_once, gpmint_indicate_mechs); if (!global_mechs.initialized) { /* this is quite a corner case. It means the pthread_once() call * failed for some reason. In this case we need to use a mutex */ pthread_mutex_lock(&global_mechs_lock); /* need to recheck once we acquired the lock, to avoid redoing * if we were stuck after another thread that already did it */ if (!global_mechs.initialized) { gpmint_indicate_mechs(); } pthread_mutex_unlock(&global_mechs_lock); if (!global_mechs.initialized) { /* if still it is not initialized, give up */ return EIO; } } return 0; } OM_uint32 gpm_indicate_mechs(OM_uint32 *minor_status, gss_OID_set *mech_set) { uint32_t ret_min; uint32_t ret_maj; int ret; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } if (!mech_set) { *minor_status = 0; return GSS_S_CALL_INACCESSIBLE_WRITE; } ret= gpmint_init_global_mechs(); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret_maj = gpm_copy_gss_OID_set(&ret_min, global_mechs.mech_set, mech_set); *minor_status = ret_min; return ret_maj; } OM_uint32 gpm_inquire_names_for_mech(OM_uint32 *minor_status, gss_OID mech_type, gss_OID_set *mech_names) { uint32_t ret_min; uint32_t ret_maj; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } if (!mech_names) { *minor_status = 0; return GSS_S_CALL_INACCESSIBLE_WRITE; } ret_min = gpmint_init_global_mechs(); if (ret_min) { *minor_status = ret_min; return GSS_S_FAILURE; } for (unsigned i = 0; i < global_mechs.info_len; i++) { if (!gpm_equal_oids(global_mechs.info[i].mech, mech_type)) { continue; } ret_maj = gpm_copy_gss_OID_set(&ret_min, global_mechs.info[i].name_types, mech_names); *minor_status = ret_min; return ret_maj; } *minor_status = 0; return GSS_S_BAD_MECH; } OM_uint32 gpm_inquire_mechs_for_name(OM_uint32 *minor_status, gssx_name *input_name, gss_OID_set *mech_types) { uint32_t ret_min; uint32_t ret_maj; uint32_t discard; gss_OID name_type = GSS_C_NO_OID; int present; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } if (!input_name || !mech_types) { *minor_status = 0; return GSS_S_CALL_INACCESSIBLE_WRITE; } ret_min = gpmint_init_global_mechs(); if (ret_min) { *minor_status = ret_min; return GSS_S_FAILURE; } ret_min = gp_conv_gssx_to_oid_alloc(&input_name->name_type, &name_type); if (ret_min) { ret_maj = GSS_S_FAILURE; goto done; } ret_maj = gss_create_empty_oid_set(&ret_min, mech_types); if (ret_maj) { goto done; } for (unsigned i = 0; i < global_mechs.info_len; i++) { ret_maj = gss_test_oid_set_member(&ret_min, name_type, global_mechs.info[i].name_types, &present); if (ret_maj) { /* skip on error */ continue; } if (present) { ret_maj = gss_add_oid_set_member(&ret_min, global_mechs.info[i].mech, mech_types); } if (ret_maj) { goto done; } } done: gss_release_oid(&discard, &name_type); if (ret_maj) { gss_release_oid_set(&discard, mech_types); *minor_status = ret_min; return ret_maj; } *minor_status = 0; return GSS_S_COMPLETE; } OM_uint32 gpm_inquire_attrs_for_mech(OM_uint32 *minor_status, gss_OID mech, gss_OID_set *mech_attrs, gss_OID_set *known_mech_attrs) { uint32_t ret_min; uint32_t ret_maj; uint32_t discard; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } ret_min = gpmint_init_global_mechs(); if (ret_min) { *minor_status = ret_min; return GSS_S_FAILURE; } for (unsigned i = 0; i < global_mechs.info_len; i++) { if (!gpm_equal_oids(global_mechs.info[i].mech, mech)) { continue; } if (mech_attrs != NULL) { ret_maj = gpm_copy_gss_OID_set(&ret_min, global_mechs.info[i].mech_attrs, mech_attrs); if (ret_maj) { *minor_status = ret_min; return ret_maj; } } if (known_mech_attrs != NULL) { ret_maj = gpm_copy_gss_OID_set(&ret_min, global_mechs.info[i].known_mech_attrs, known_mech_attrs); if (ret_maj) { gss_release_oid_set(&discard, known_mech_attrs); } *minor_status = ret_min; return ret_maj; } /* all requested attributes copied successfully */ *minor_status = 0; return GSS_S_COMPLETE; } *minor_status = 0; return GSS_S_BAD_MECH; } OM_uint32 gpm_inquire_saslname_for_mech(OM_uint32 *minor_status, const gss_OID desired_mech, gss_buffer_t sasl_mech_name, gss_buffer_t mech_name, gss_buffer_t mech_description) { uint32_t ret_min; uint32_t ret_maj; uint32_t discard; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } if (!sasl_mech_name || !mech_name || !mech_description) { *minor_status = 0; return GSS_S_CALL_INACCESSIBLE_WRITE; } ret_min = gpmint_init_global_mechs(); if (ret_min) { *minor_status = ret_min; return GSS_S_FAILURE; } for (unsigned i = 0; i < global_mechs.info_len; i++) { if (!gpm_equal_oids(global_mechs.info[i].mech, desired_mech)) { continue; } ret_maj = gpm_copy_gss_buffer(&ret_min, global_mechs.info[i].saslname_sasl_mech_name, sasl_mech_name); if (ret_maj) { *minor_status = ret_min; return ret_maj; } ret_maj = gpm_copy_gss_buffer(&ret_min, global_mechs.info[i].saslname_mech_name, mech_name); if (ret_maj) { gss_release_buffer(&discard, sasl_mech_name); *minor_status = ret_min; return ret_maj; } ret_maj = gpm_copy_gss_buffer(&ret_min, global_mechs.info[i].saslname_mech_desc, mech_description); if (ret_maj) { gss_release_buffer(&discard, sasl_mech_name); gss_release_buffer(&discard, mech_name); } *minor_status = ret_min; return ret_maj; } *minor_status = 0; return GSS_S_BAD_MECH; } OM_uint32 gpm_display_mech_attr(OM_uint32 *minor_status, gss_const_OID mech_attr, gss_buffer_t name, gss_buffer_t short_desc, gss_buffer_t long_desc) { uint32_t ret_min; uint32_t ret_maj; uint32_t discard; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } if (!name || !short_desc || !long_desc) { *minor_status = 0; return GSS_S_CALL_INACCESSIBLE_WRITE; } ret_min = gpmint_init_global_mechs(); if (ret_min) { *minor_status = ret_min; return GSS_S_FAILURE; } for (unsigned i = 0; i < global_mechs.desc_len; i++) { if (!gpm_equal_oids(global_mechs.desc[i].attr, mech_attr)) { continue; } ret_maj = gpm_copy_gss_buffer(&ret_min, global_mechs.desc[i].name, name); if (ret_maj) { *minor_status = ret_min; return ret_maj; } ret_maj = gpm_copy_gss_buffer(&ret_min, global_mechs.desc[i].short_desc, short_desc); if (ret_maj) { gss_release_buffer(&discard, name); *minor_status = ret_min; return ret_maj; } ret_maj = gpm_copy_gss_buffer(&ret_min, global_mechs.desc[i].long_desc, long_desc); if (ret_maj) { gss_release_buffer(&discard, name); gss_release_buffer(&discard, short_desc); } *minor_status = ret_min; return ret_maj; } *minor_status = 0; return GSS_S_BAD_MECH; } OM_uint32 gpm_indicate_mechs_by_attrs(OM_uint32 *minor_status, gss_const_OID_set desired_mech_attrs, gss_const_OID_set except_mech_attrs, gss_const_OID_set critical_mech_attrs, gss_OID_set *mechs) { uint32_t ret_min; uint32_t ret_maj; uint32_t discard; int present; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } if (!mechs) { *minor_status = 0; return GSS_S_CALL_INACCESSIBLE_WRITE; } ret_min = gpmint_init_global_mechs(); if (ret_min) { *minor_status = ret_min; return GSS_S_FAILURE; } ret_maj = gss_create_empty_oid_set(&ret_min, mechs); if (ret_maj) { *minor_status = ret_min; return ret_maj; } for (unsigned i = 0; i < global_mechs.info_len; i++) { if (desired_mech_attrs != GSS_C_NO_OID_SET) { unsigned j; for (j = 0; j < desired_mech_attrs->count; j++) { ret_maj = gss_test_oid_set_member(&ret_min, &desired_mech_attrs->elements[j], global_mechs.info[i].mech_attrs, &present); if (ret_maj) { /* skip in case of errors */ break; } if (!present) { break; } } /* if not desired skip */ if (j != desired_mech_attrs->count) { continue; } } if (except_mech_attrs != GSS_C_NO_OID_SET) { unsigned j; for (j = 0; j < except_mech_attrs->count; j++) { ret_maj = gss_test_oid_set_member(&ret_min, &except_mech_attrs->elements[j], global_mechs.info[i].mech_attrs, &present); if (ret_maj) { /* continue in case of errors */ continue; } if (present) { break; } } /* if excepted skip */ if (j == except_mech_attrs->count) { continue; } } if (critical_mech_attrs != GSS_C_NO_OID_SET) { unsigned j; for (j = 0; j < critical_mech_attrs->count; j++) { ret_maj = gss_test_oid_set_member(&ret_min, &critical_mech_attrs->elements[j], global_mechs.info[i].known_mech_attrs, &present); if (ret_maj) { /* skip in case of errors */ break; } if (!present) { break; } } /* if not known skip */ if (j != critical_mech_attrs->count) { continue; } } /* passes all tests, add to list */ ret_maj = gss_add_oid_set_member(&ret_min, global_mechs.info[i].mech, mechs); if (ret_maj) { goto done; } } done: if (ret_maj) { gss_release_oid_set(&discard, mechs); *minor_status = ret_min; return ret_maj; } *minor_status = 0; return GSS_S_COMPLETE; } gssproxy-v0.8.2/src/client/gpm_init_sec_context.c0000644000174300017420000001300013456641744021625 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" #include "src/gp_conv.h" static void return_new_cred_handle(struct gssx_option *val, gssx_cred **out_cred_handle) { gssx_cred *creds; XDR xdrctx; bool xdrok; creds = calloc(1, sizeof(*creds)); if (creds) { xdrmem_create(&xdrctx, val->value.octet_string_val, val->value.octet_string_len, XDR_DECODE); xdrok = xdr_gssx_cred(&xdrctx, creds); if (xdrok) { *out_cred_handle = creds; } else { free(creds); } } } OM_uint32 gpm_init_sec_context(OM_uint32 *minor_status, gssx_cred *cred_handle, gssx_ctx **context_handle, gssx_name *target_name, gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, gss_channel_bindings_t input_cb, gss_buffer_t input_token, gss_OID *actual_mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec, gssx_cred **out_cred_handle) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_init_sec_context *arg = &uarg.init_sec_context; gssx_res_init_sec_context *res = &ures.init_sec_context; gssx_ctx *ctx = NULL; gss_OID_desc *mech = NULL; gss_buffer_t outbuf = NULL; uint32_t ret_maj = GSS_S_COMPLETE; uint32_t ret_min = 0; int ret; memset(&uarg, 0, sizeof(union gp_rpc_arg)); memset(&ures, 0, sizeof(union gp_rpc_res)); /* prepare proxy request */ if (cred_handle != NULL) { arg->cred_handle = cred_handle; } if (*context_handle) { arg->context_handle = *context_handle; } /* always try request cred sync, ignore errors, not critical */ (void)gp_add_option(&arg->options.options_val, &arg->options.options_len, CRED_SYNC_OPTION, sizeof(CRED_SYNC_OPTION), CRED_SYNC_DEFAULT, sizeof(CRED_SYNC_DEFAULT)); arg->target_name = target_name; ret = gp_conv_oid_to_gssx(mech_type, &arg->mech_type); if (ret) { goto done; } arg->req_flags = req_flags; arg->time_req = time_req; if (input_cb) { ret = gp_conv_cb_to_gssx_alloc(input_cb, &arg->input_cb); if (ret) { goto done; } } if (input_token != GSS_C_NO_BUFFER) { ret = gp_conv_buffer_to_gssx_alloc(input_token, &arg->input_token); if (ret) { goto done; } } /* execute proxy request */ ret = gpm_make_call(GSSX_INIT_SEC_CONTEXT, &uarg, &ures); if (ret) { gpm_save_internal_status(ret, gp_strerror(ret)); goto done; } /* return values */ if (actual_mech_type) { if (res->status.mech.octet_string_len) { ret = gp_conv_gssx_to_oid_alloc(&res->status.mech, &mech); if (ret) { goto done; } } } if (res->context_handle) { ctx = res->context_handle; /* we are stealing the delegated creds on success, so we do not want * it to be freed by xdr_free */ res->context_handle = NULL; } if (res->output_token) { ret = gp_conv_gssx_to_buffer_alloc(res->output_token, &outbuf); if (ret) { gpm_save_internal_status(ret, gp_strerror(ret)); goto done; } } /* check if a sync cred was returned to us, don't fail on errors */ if (out_cred_handle && res->options.options_len > 0) { struct gssx_option *val = NULL; gp_options_find(val, res->options, CRED_SYNC_PAYLOAD, sizeof(CRED_SYNC_PAYLOAD)); if (val) { return_new_cred_handle(val, out_cred_handle); } } ret_maj = res->status.major_status; ret_min = res->status.minor_status; gpm_save_status(&res->status); done: if (ret != 0) { ret_min = ret; ret_maj = GSS_S_FAILURE; } /* we are putting our copy of these structures in here, * and do not want it to be freed by xdr_free */ arg->context_handle = NULL; arg->cred_handle = NULL; arg->target_name = NULL; gpm_free_xdrs(GSSX_INIT_SEC_CONTEXT, &uarg, &ures); if (ret_maj == GSS_S_COMPLETE || ret_maj == GSS_S_CONTINUE_NEEDED) { if (actual_mech_type) { *actual_mech_type = mech; } if (outbuf) { *output_token = *outbuf; free(outbuf); } if (ret_flags) { *ret_flags = ctx->ctx_flags; } if (time_rec) { *time_rec = ctx->lifetime; } } else { if (ctx) { xdr_free((xdrproc_t)xdr_gssx_ctx, (char *)ctx); free(ctx); ctx = NULL; } if (mech) { free(mech->elements); free(mech); } if (outbuf) { free(outbuf->value); free(outbuf); } } /* always replace old ctx handle and set new */ if (*context_handle) { xdr_free((xdrproc_t)xdr_gssx_ctx, (char *)*context_handle); free(*context_handle); } *context_handle = ctx; *minor_status = ret_min; return ret_maj; } gssproxy-v0.8.2/src/client/gpm_inquire_context.c0000644000174300017420000000452113456641744021514 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" OM_uint32 gpm_inquire_context(OM_uint32 *minor_status, gssx_ctx *context_handle, gssx_name **src_name, gssx_name **targ_name, OM_uint32 *lifetime_rec, gss_OID *mech_type, OM_uint32 *ctx_flags, int *locally_initiated, int *open) { OM_uint32 ret_maj; OM_uint32 tmp_min; int ret; if (!minor_status) { return GSS_S_CALL_INACCESSIBLE_WRITE; } *minor_status = 0; if (!context_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } if (src_name) { ret_maj = gpm_duplicate_name(minor_status, &context_handle->src_name, src_name); if (ret_maj != GSS_S_COMPLETE) { return ret_maj; } } if (targ_name) { ret_maj = gpm_duplicate_name(minor_status, &context_handle->targ_name, targ_name); if (ret_maj != GSS_S_COMPLETE) { if (src_name) { (void)gpm_release_name(&tmp_min, src_name); } return ret_maj; } } if (lifetime_rec) { *lifetime_rec = (OM_uint32)context_handle->lifetime; } if (mech_type) { ret = gp_conv_gssx_to_oid_alloc(&context_handle->mech, mech_type); if (ret) { if (src_name) { (void)gpm_release_name(&tmp_min, src_name); } if (targ_name) { (void)gpm_release_name(&tmp_min, targ_name); } *minor_status = ret; return GSS_S_FAILURE; } } if (ctx_flags) { *ctx_flags = (OM_uint32)context_handle->ctx_flags; } if (locally_initiated) { if (context_handle->locally_initiated) { *locally_initiated = 1; } else { *locally_initiated = 0; } } if (open) { if (context_handle->open) { *open = 1; } else { *open = 0; } } return GSS_S_COMPLETE; } gssproxy-v0.8.2/src/client/gpm_release_handle.c0000644000174300017420000000572513456641744021236 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" OM_uint32 gpm_release_cred(OM_uint32 *minor_status, gssx_cred **cred_handle) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_release_handle *arg = &uarg.release_handle; gssx_res_release_handle *res = &ures.release_handle; gssx_cred *r; int ret; if (cred_handle == NULL || *cred_handle == NULL) { return 0; } r = (*cred_handle); if (!r->needs_release) { ret = GSS_S_COMPLETE; goto done; } memset(&uarg, 0, sizeof(union gp_rpc_arg)); memset(&ures, 0, sizeof(union gp_rpc_res)); /* ignore call_ctx for now */ arg->cred_handle.handle_type = GSSX_C_HANDLE_CRED; arg->cred_handle.gssx_handle_u.cred_info = *r; /* execute proxy request */ ret = gpm_make_call(GSSX_RELEASE_HANDLE, &uarg, &ures); if (ret) { *minor_status = ret; ret = GSS_S_FAILURE; goto rel_done; } if (res->status.major_status) { gpm_save_status(&res->status); *minor_status = res->status.minor_status; ret = res->status.major_status; } rel_done: /* we passed in our copy by value, so clean out to avoid double frees */ memset(&arg->cred_handle.gssx_handle_u.cred_info, 0, sizeof(gssx_cred)); gpm_free_xdrs(GSSX_RELEASE_HANDLE, &uarg, &ures); done: xdr_free((xdrproc_t)xdr_gssx_cred, (char *)r); free(r); *cred_handle = NULL; return ret; } OM_uint32 gpm_delete_sec_context(OM_uint32 *minor_status, gssx_ctx **context_handle, gss_buffer_t output_token UNUSED) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_release_handle *arg = &uarg.release_handle; gssx_res_release_handle *res = &ures.release_handle; gssx_ctx *r; int ret; if (context_handle == NULL || *context_handle == NULL) { return 0; } r = (*context_handle); if (!r->needs_release) { ret = GSS_S_COMPLETE; goto done; } memset(&uarg, 0, sizeof(union gp_rpc_arg)); memset(&ures, 0, sizeof(union gp_rpc_res)); /* ignore call_ctx for now */ arg->cred_handle.handle_type = GSSX_C_HANDLE_SEC_CTX; arg->cred_handle.gssx_handle_u.sec_ctx_info = *r; /* execute proxy request */ ret = gpm_make_call(GSSX_RELEASE_HANDLE, &uarg, &ures); if (ret) { *minor_status = ret; ret = GSS_S_FAILURE; goto rel_done; } if (res->status.major_status) { gpm_save_status(&res->status); *minor_status = res->status.minor_status; ret = res->status.major_status; } rel_done: /* we passed in our copy by value, so clean out to avoid double frees */ memset(&arg->cred_handle.gssx_handle_u.sec_ctx_info, 0, sizeof(gssx_cred)); gpm_free_xdrs(GSSX_RELEASE_HANDLE, &uarg, &ures); done: xdr_free((xdrproc_t)xdr_gssx_ctx, (char *)r); return ret; } gssproxy-v0.8.2/src/client/gpm_unwrap.c0000644000174300017420000000564113456641744017614 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" #include "src/gp_conv.h" OM_uint32 gpm_unwrap(OM_uint32 *minor_status, gssx_ctx *context_handle, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int *conf_state, gss_qop_t *qop_state) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_unwrap *arg = &uarg.unwrap; gssx_res_unwrap *res = &ures.unwrap; uint32_t ret_min = 0; uint32_t ret_maj = 0; int ret = 0; gssx_buffer message_buffer; memset(&uarg, 0, sizeof(union gp_rpc_arg)); memset(&ures, 0, sizeof(union gp_rpc_res)); if (!context_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* format request */ /* NOTE: the final free will also release the old context */ arg->context_handle = *context_handle; if (qop_state) { arg->qop_state = *qop_state; } ret = gp_conv_buffer_to_gssx(input_message_buffer, &message_buffer); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } arg->token_buffer.token_buffer_val = calloc(1, sizeof(gssx_buffer)); if (!arg->token_buffer.token_buffer_val) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } arg->token_buffer.token_buffer_val[0] = message_buffer; arg->token_buffer.token_buffer_len = 1; /* execute proxy request */ ret = gpm_make_call(GSSX_UNWRAP, &uarg, &ures); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } /* format reply */ if (res->status.major_status) { gpm_save_status(&res->status); ret_min = res->status.minor_status; ret_maj = res->status.major_status; goto done; } if (conf_state) { *conf_state = *res->conf_state; } if (qop_state) { *qop_state = *res->qop_state; } if (res->message_buffer.message_buffer_len > 0) { ret = gp_copy_gssx_to_buffer(&res->message_buffer.message_buffer_val[0], output_message_buffer); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } } done: /* Steal the new context if available. * NOTE: We do not want it to be freed by xdr_free, so copy the contents * and cear up the structure to be freed so contents are not freed. */ if (res->context_handle) { *context_handle = *res->context_handle; memset(res->context_handle, 0, sizeof(gssx_ctx)); } else { /* prevent the contexthandle from being destroyed in case of server * error. */ memset(&arg->context_handle, 0, sizeof(gssx_ctx)); } gpm_free_xdrs(GSSX_UNWRAP, &uarg, &ures); *minor_status = ret_min; return ret_maj; } gssproxy-v0.8.2/src/client/gpm_verify_mic.c0000644000174300017420000000444413456641744020434 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" #include "src/gp_conv.h" OM_uint32 gpm_verify_mic(OM_uint32 *minor_status, gssx_ctx *context_handle, gss_buffer_t message_buffer, gss_buffer_t message_token, gss_qop_t *qop_state) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_verify_mic *arg = &uarg.verify_mic; gssx_res_verify_mic *res = &ures.verify_mic; uint32_t ret_min = 0; uint32_t ret_maj = 0; int ret = 0; memset(&uarg, 0, sizeof(union gp_rpc_arg)); memset(&ures, 0, sizeof(union gp_rpc_res)); if (!context_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* format request */ /* NOTE: the final free will also release the old context */ arg->context_handle = *context_handle; ret = gp_conv_buffer_to_gssx(message_buffer, &arg->message_buffer); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret = gp_conv_buffer_to_gssx(message_token, &arg->token_buffer); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } /* execute proxy request */ ret = gpm_make_call(GSSX_VERIFY, &uarg, &ures); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } /* Check and save error status */ if (res->status.major_status) { gpm_save_status(&res->status); ret_min = res->status.minor_status; ret_maj = res->status.major_status; goto done; } if (qop_state) { *qop_state = *res->qop_state; } done: /* Steal the new context if available. * NOTE: We do not want it to be freed by xdr_free, so copy the contents * and cear up the structure to be freed so contents are not freed. */ if (res->context_handle) { *context_handle = *res->context_handle; memset(res->context_handle, 0, sizeof(gssx_ctx)); } else { /* prevent the contexthandle from being destroyed in case of server * error. */ memset(&arg->context_handle, 0, sizeof(gssx_ctx)); } gpm_free_xdrs(GSSX_VERIFY, &uarg, &ures); *minor_status = ret_min; return ret_maj; } gssproxy-v0.8.2/src/client/gpm_wrap.c0000644000174300017420000000556413456641744017255 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" #include "src/gp_conv.h" OM_uint32 gpm_wrap(OM_uint32 *minor_status, gssx_ctx *context_handle, int conf_req_flag, gss_qop_t qop_req, const gss_buffer_t input_message_buffer, int *conf_state, gss_buffer_t output_message_buffer) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_wrap *arg = &uarg.wrap; gssx_res_wrap *res = &ures.wrap; uint32_t ret_min = 0; uint32_t ret_maj = 0; int ret = 0; gssx_buffer message_buffer; memset(&uarg, 0, sizeof(union gp_rpc_arg)); memset(&ures, 0, sizeof(union gp_rpc_res)); if (!context_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* format request */ /* NOTE: the final free will also release the old context */ arg->context_handle = *context_handle; arg->conf_req = conf_req_flag; arg->qop_state = qop_req; ret = gp_conv_buffer_to_gssx(input_message_buffer, &message_buffer); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } arg->message_buffer.message_buffer_val = calloc(1, sizeof(gssx_buffer)); if (!arg->message_buffer.message_buffer_val) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } arg->message_buffer.message_buffer_val[0] = message_buffer; arg->message_buffer.message_buffer_len = 1; /* execute proxy request */ ret = gpm_make_call(GSSX_WRAP, &uarg, &ures); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } /* format reply */ if (res->status.major_status) { gpm_save_status(&res->status); ret_min = res->status.minor_status; ret_maj = res->status.major_status; goto done; } if (conf_state) { *conf_state = *res->conf_state; } if (res->token_buffer.token_buffer_len > 0) { ret = gp_copy_gssx_to_buffer(&res->token_buffer.token_buffer_val[0], output_message_buffer); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } } done: /* Steal the new context if available. * NOTE: We do not want it to be freed by xdr_free, so copy the contents * and cear up the structure to be freed so contents are not freed. */ if (res->context_handle) { *context_handle = *res->context_handle; memset(res->context_handle, 0, sizeof(gssx_ctx)); } else { /* prevent the contexthandle from being destroyed in case of server * error. */ memset(&arg->context_handle, 0, sizeof(gssx_ctx)); } gpm_free_xdrs(GSSX_WRAP, &uarg, &ures); *minor_status = ret_min; return ret_maj; } gssproxy-v0.8.2/src/client/gpm_wrap_size_limit.c0000644000174300017420000000331113456641744021471 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #include "gssapi_gpm.h" #include "src/gp_conv.h" OM_uint32 gpm_wrap_size_limit(OM_uint32 *minor_status, gssx_ctx *context_handle, int conf_req, gss_qop_t qop_req, OM_uint32 size_req, OM_uint32 *max_size) { union gp_rpc_arg uarg; union gp_rpc_res ures; gssx_arg_wrap_size_limit *arg = &uarg.wrap_size_limit; gssx_res_wrap_size_limit *res = &ures.wrap_size_limit; uint32_t ret_min = 0; uint32_t ret_maj = 0; int ret = 0; memset(&uarg, 0, sizeof(union gp_rpc_arg)); memset(&ures, 0, sizeof(union gp_rpc_res)); if (!context_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* format request */ arg->context_handle = *context_handle; arg->conf_req = conf_req; arg->qop_state = qop_req; arg->req_output_size = size_req; /* execute proxy request */ ret = gpm_make_call(GSSX_WRAP_SIZE_LIMIT, &uarg, &ures); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } /* format reply */ if (res->status.major_status) { gpm_save_status(&res->status); ret_min = res->status.minor_status; ret_maj = res->status.major_status; goto done; } if (max_size) { *max_size = res->max_input_size; } done: /* prevent the context handle from being destroyed in gpm_free_xdrs */ memset(&arg->context_handle, 0, sizeof(gssx_ctx)); gpm_free_xdrs(GSSX_WRAP_SIZE_LIMIT, &uarg, &ures); *minor_status = ret_min; return ret_maj; } gssproxy-v0.8.2/src/client/gssapi_gpm.h0000644000174300017420000002331413456641744017570 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GSSAPI_GPM_H_ #define _GSSAPI_GPM_H_ #include "config.h" #include #include #include #include #include #include #include "rpcgen/gp_rpc.h" #include "rpcgen/gss_proxy.h" #include "src/gp_common.h" #include "src/gp_conv.h" int gpm_make_call(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res); void gpm_free_xdrs(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res); OM_uint32 gpm_release_name(OM_uint32 *minor_status, gssx_name **input_name); OM_uint32 gpm_release_buffer(OM_uint32 *minor_status, gss_buffer_t buffer); void gpm_display_status_init_once(void); void gpm_save_status(gssx_status *status); void gpm_save_internal_status(uint32_t err, char *err_str); OM_uint32 gpm_display_status(OM_uint32 *minor_status, OM_uint32 status_value, int status_type, const gss_OID mech_type, OM_uint32 *message_context, gss_buffer_t status_string); OM_uint32 gpm_accept_sec_context(OM_uint32 *minor_status, gssx_ctx **context_handle, gssx_cred *acceptor_cred_handle, gss_buffer_t input_token_buffer, gss_channel_bindings_t input_chan_bindings, gssx_name **src_name, gss_OID *mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec, gssx_cred **delegated_cred_handle); OM_uint32 gpm_release_cred(OM_uint32 *minor_status, gssx_cred **cred_handle); OM_uint32 gpm_delete_sec_context(OM_uint32 *minor_status, gssx_ctx **context_handle, gss_buffer_t output_token); OM_uint32 gpm_acquire_cred(OM_uint32 *minor_status, gssx_cred *imp_cred_handle, gssx_name *desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, bool impersonate, gssx_cred **output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec); OM_uint32 gpm_add_cred(OM_uint32 *minor_status, gssx_cred *input_cred_handle, gssx_name *desired_name, const gss_OID desired_mech, gss_cred_usage_t cred_usage, OM_uint32 initiator_time_req, OM_uint32 acceptor_time_req, gssx_cred **output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *initiator_time_rec, OM_uint32 *acceptor_time_rec); OM_uint32 gpm_inquire_cred(OM_uint32 *minor_status, gssx_cred *cred, gssx_name **name, OM_uint32 *lifetime, gss_cred_usage_t *cred_usage, gss_OID_set *mechanisms); OM_uint32 gpm_inquire_cred_by_mech(OM_uint32 *minor_status, gssx_cred *cred, gss_OID mech_type, gssx_name **name, OM_uint32 *initiator_lifetime, OM_uint32 *acceptor_lifetime, gss_cred_usage_t *cred_usage); OM_uint32 gpm_indicate_mechs(OM_uint32 *minor_status, gss_OID_set *mech_set); OM_uint32 gpm_inquire_names_for_mech(OM_uint32 *minor_status, gss_OID mech_type, gss_OID_set *mech_names); OM_uint32 gpm_inquire_mechs_for_name(OM_uint32 *minor_status, gssx_name *input_name, gss_OID_set *mech_types); OM_uint32 gpm_inquire_attrs_for_mech(OM_uint32 *minor_status, gss_OID mech, gss_OID_set *mech_attrs, gss_OID_set *known_mech_attrs); OM_uint32 gpm_inquire_saslname_for_mech(OM_uint32 *minor_status, const gss_OID desired_mech, gss_buffer_t sasl_mech_name, gss_buffer_t mech_name, gss_buffer_t mech_description); OM_uint32 gpm_display_mech_attr(OM_uint32 *minor_status, gss_const_OID mech_attr, gss_buffer_t name, gss_buffer_t short_desc, gss_buffer_t long_desc); OM_uint32 gpm_indicate_mechs_by_attrs(OM_uint32 *minor_status, gss_const_OID_set desired_mech_attrs, gss_const_OID_set except_mech_attrs, gss_const_OID_set critical_mech_attrs, gss_OID_set *mechs); OM_uint32 gpm_display_name(OM_uint32 *minor_status, gssx_name *in_name, gss_buffer_t output_name_buffer, gss_OID *output_name_type); OM_uint32 gpm_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer, gss_OID input_name_type, gssx_name **output_name); OM_uint32 gpm_export_name(OM_uint32 *minor_status, gssx_name *input_name, gss_buffer_t exported_name); OM_uint32 gpm_export_name_composite(OM_uint32 *minor_status, gssx_name *input_name, gss_buffer_t exported_composite_name); OM_uint32 gpm_duplicate_name(OM_uint32 *minor_status, gssx_name *input_name, gssx_name **dest_name); OM_uint32 gpm_canonicalize_name(OM_uint32 *minor_status, gssx_name *input_name, const gss_OID mech_type, gssx_name **output_name); OM_uint32 gpm_inquire_name(OM_uint32 *minor_status, gssx_name *name, int *name_is_NM, gss_OID *NM_mech, gss_buffer_set_t *attrs); OM_uint32 gpm_compare_name(OM_uint32 *minor_status, gssx_name *name1, gssx_name *name2, int *name_equal); OM_uint32 gpm_init_sec_context(OM_uint32 *minor_status, gssx_cred *cred_handle, gssx_ctx **context_handle, gssx_name *target_name, gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, gss_channel_bindings_t input_cb, gss_buffer_t input_token, gss_OID *actual_mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec, gssx_cred **out_cred_handle); OM_uint32 gpm_inquire_context(OM_uint32 *minor_status, gssx_ctx *context_handle, gssx_name **src_name, gssx_name **targ_name, OM_uint32 *lifetime_rec, gss_OID *mech_type, OM_uint32 *ctx_flags, int *locally_initiated, int *open); OM_uint32 gpm_get_mic(OM_uint32 *minor_status, gssx_ctx *context_handle, gss_qop_t qop_req, gss_buffer_t message_buffer, gss_buffer_t message_token); OM_uint32 gpm_verify_mic(OM_uint32 *minor_status, gssx_ctx *context_handle, gss_buffer_t message_buffer, gss_buffer_t message_token, gss_qop_t *qop_state); OM_uint32 gpm_wrap(OM_uint32 *minor_status, gssx_ctx *context_handle, int conf_req_flag, gss_qop_t qop_req, const gss_buffer_t input_message_buffer, int *conf_state, gss_buffer_t output_message_buffer); OM_uint32 gpm_unwrap(OM_uint32 *minor_status, gssx_ctx *context_handle, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int *conf_state, gss_qop_t *qop_state); OM_uint32 gpm_wrap_size_limit(OM_uint32 *minor_status, gssx_ctx *context_handle, int conf_req, gss_qop_t qop_req, OM_uint32 size_req, OM_uint32 *max_size); #endif /* _GSSAPI_GPM_H_ */ gssproxy-v0.8.2/src/gp_common.h0000644000174300017420000000733013456641744016137 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GP_COMMON_H_ #define _GP_COMMON_H_ #include "config.h" #include "gp_debug.h" #include "gp_log.h" #define no_const(ptr) ((void *)((uintptr_t)(ptr))) #define UNUSED __attribute__((unused)) /* add element to list head */ #define LIST_ADD(list, elem) do { \ elem->prev = NULL; \ elem->next = list; \ if (list) { \ list->prev = elem; \ } \ list = elem; \ } while (0) /* remove element from list */ #define LIST_DEL(list, elem) do { \ if (elem->next) { \ elem->next->prev = elem->prev; \ } \ if (elem->prev) { \ elem->prev->next = elem->next; \ } \ if (list == elem) { \ list = elem->next; \ } \ elem->prev = NULL; \ elem->next = NULL; \ } while (0) #define safefree(ptr) do { \ free(no_const(ptr)); \ ptr = NULL; \ } while(0) /* max out at 1MB for now */ #define MAX_RPC_SIZE 1024*1024 bool gp_same(const char *a, const char *b); bool gp_boolean_is_true(const char *s); char *gp_getenv(const char *name); ssize_t gp_safe_read(int fd, void *buf, size_t count); ssize_t gp_safe_write(int fd, const void *buf, size_t count); /* NOTE: read the note in gp_util.c before using gp_strerror() */ char *gp_strerror(int errnum); #include "rpcgen/gss_proxy.h" union gp_rpc_arg { gssx_arg_release_handle release_handle; gssx_arg_indicate_mechs indicate_mechs; gssx_arg_import_and_canon_name import_and_canon_name; gssx_arg_get_call_context get_call_context; gssx_arg_acquire_cred acquire_cred; gssx_arg_export_cred export_cred; gssx_arg_import_cred import_cred; gssx_arg_store_cred store_cred; gssx_arg_init_sec_context init_sec_context; gssx_arg_accept_sec_context accept_sec_context; gssx_arg_get_mic get_mic; gssx_arg_verify_mic verify_mic; gssx_arg_wrap wrap; gssx_arg_unwrap unwrap; gssx_arg_wrap_size_limit wrap_size_limit; }; union gp_rpc_res { gssx_res_release_handle release_handle; gssx_res_indicate_mechs indicate_mechs; gssx_res_import_and_canon_name import_and_canon_name; gssx_res_get_call_context get_call_context; gssx_res_acquire_cred acquire_cred; gssx_res_export_cred export_cred; gssx_res_import_cred import_cred; gssx_res_store_cred store_cred; gssx_res_init_sec_context init_sec_context; gssx_res_accept_sec_context accept_sec_context; gssx_res_get_mic get_mic; gssx_res_verify_mic verify_mic; gssx_res_wrap wrap; gssx_res_unwrap unwrap; gssx_res_wrap_size_limit wrap_size_limit; }; #define gpopt_string_match(buf, val, len) \ (len == (buf)->octet_string_len && \ strncmp((val), (buf)->octet_string_val, \ (buf)->octet_string_len) == 0) #define gp_option_name_match(opt, val, len) \ gpopt_string_match(&((opt)->option), val, len) #define gp_option_value_match(opt, val, len) \ gpopt_string_match(&((opt)->value), val, len) #define gp_options_find(res, opts, name, len) \ do { \ struct gssx_option *_v; \ res = NULL; \ for (unsigned _o = 0; _o < opts.options_len; _o++) { \ _v = &opts.options_val[_o]; \ if (gp_option_name_match(_v, name, len)) { \ res = _v; \ break; \ } \ } \ } while(0) #define ACQUIRE_TYPE_OPTION "acquire_type" #define ACQUIRE_IMPERSONATE_NAME "impersonate_name" #define CRED_SYNC_OPTION "sync_modified_creds" #define CRED_SYNC_DEFAULT "default" #define CRED_SYNC_PAYLOAD "sync_creds" #define GPKRB_MAX_CRED_SIZE 1024 * 512 uint32_t gp_add_option(gssx_option **options_val, u_int *options_len, const void *option, size_t option_len, const void *value, size_t value_len); #endif /* _GP_COMMON_H_ */ gssproxy-v0.8.2/src/gp_config.c0000644000174300017420000007403113456641744016111 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include #include #include #include #include "gp_proxy.h" #include "gp_config.h" #include "gp_selinux.h" #include #include struct gp_flag_def { const char *name; uint32_t value; }; struct gp_flag_def flag_names[] = { { "DELEGATE", GSS_C_DELEG_FLAG }, { "MUTUAL_AUTH", GSS_C_MUTUAL_FLAG }, { "REPLAY_DETECT", GSS_C_REPLAY_FLAG }, { "SEQUENCE", GSS_C_SEQUENCE_FLAG }, { "CONFIDENTIALITY", GSS_C_CONF_FLAG }, { "INTEGRITIY", GSS_C_INTEG_FLAG }, { "ANONYMOUS", GSS_C_ANON_FLAG }, { NULL, 0 } }; #define DEFAULT_FILTERED_FLAGS GSS_C_DELEG_FLAG #define DEFAULT_ENFORCED_FLAGS 0 static void free_str_array(const char ***a, int *count) { const char **array; int i; if (!a) { return; } array = *a; if (count) { for (i = 0; i < *count; i++) { safefree(array[i]); } } else { for (i = 0; array[i]; i++) { safefree(array[i]); } } safefree(*a); } void free_cred_store_elements(gss_key_value_set_desc *cs) { if (!cs->elements) return; for (unsigned i = 0; i < cs->count; i++) { safefree(cs->elements[i].key); safefree(cs->elements[i].value); } safefree(cs->elements); cs->count = 0; } static void gp_service_free(struct gp_service *svc) { free(svc->name); if (svc->mechs & GP_CRED_KRB5) { free(svc->krb5.principal); free_cred_store_elements(&svc->krb5.store); gp_free_creds_handle(&svc->krb5.creds_handle); } free(svc->socket); free(svc->program); SELINUX_context_free(svc->selinux_ctx); memset(svc, 0, sizeof(struct gp_service)); } static int setup_krb5_creds_handle(struct gp_service *svc) { uint32_t ret_maj, ret_min; const char *keytab = NULL; for (unsigned i = 0; i < svc->krb5.store.count; i++) { if (strcmp(svc->krb5.store.elements[i].key, "keytab") == 0) { keytab = svc->krb5.store.elements[i].value; break; } } ret_maj = gp_init_creds_handle(&ret_min, svc->name, keytab, &svc->krb5.creds_handle); if (ret_maj) { return ret_min; } return 0; } static int get_krb5_mech_cfg(struct gp_service *svc, struct gp_ini_context *ctx, const char *secname) { struct { const char *a; const char *b; } deprecated_vals[] = { {"krb5_keytab", "keytab" }, {"krb5_ccache", "ccache" }, {"krb5_client_keytab", "client_keytab" } }; const char *value; const char **strings = NULL; int count = 0; int i; int ret; ret = gp_config_get_string(ctx, secname, "krb5_principal", &value); if (ret == 0) { svc->krb5.principal = strdup(value); if (!svc->krb5.principal) { return ENOMEM; } } else if (ret != ENOENT) { return ret; } /* check for deprecated options */ for (i = 0; i < 3; i++) { ret = gp_config_get_string(ctx, secname, deprecated_vals[i].a, &value); if (ret == 0) { GPERROR("\"%s = %s\" is deprecated, " "please use \"cred_store = %s:%s\"\n", deprecated_vals[i].a, value, deprecated_vals[i].b, value); return EINVAL; } else if (ret != ENOENT) { return ret; } } /* instead look for the cred_store parameter */ ret = gp_config_get_string_array(ctx, secname, "cred_store", &count, &strings); if (ret == 0) { const char *p; ssize_t len; char *key; svc->krb5.store.elements = calloc(count, sizeof(gss_key_value_element_desc)); if (!svc->krb5.store.elements) { ret = ENOMEM; goto done; } svc->krb5.store.count = count; for (int c = 0; c < count; c++) { p = strchr(strings[c], ':'); if (!p) { GPERROR("Invalid cred_store value, no ':' separator found in" " [%s].\n", strings[c]); ret = EINVAL; goto done; } len = asprintf(&key, "%.*s", (int)(p - strings[c]), strings[c]); if (len == -1) { ret = ENOMEM; goto done; } svc->krb5.store.elements[c].key = key; svc->krb5.store.elements[c].value = strdup(p + 1); if (!svc->krb5.store.elements[c].value) { ret = ENOMEM; goto done; } } } else if (ret == ENOENT) { /* when not there we ignore */ ret = 0; } if (ret == 0) { ret = setup_krb5_creds_handle(svc); } done: free_str_array(&strings, &count); return ret; } static int parse_flags(const char *value, uint32_t *storage) { char *handle; char *token; char *str; bool add; unsigned long int conv; uint32_t flagval; int i; str = strdup(value); if (!str) { return ENOMEM; } for (token = strtok_r(str, ", ", &handle); token != NULL; token = strtok_r(NULL, ", ", &handle)) { switch (token[0]) { case '+': add = true; break; case '-': add = false; break; default: GPERROR("Ignoring flag [%s], missing +/- qualifier.\n", token); continue; } token++; for (i = 0; flag_names[i].name != NULL; i++) { if (strcasecmp(token, flag_names[i].name) == 0) { flagval = flag_names[i].value; break; } } if (flag_names[i].name == NULL) { conv = strtoul(token, &handle, 0); if (conv == 0 || conv == ULONG_MAX || *handle != '\0') { GPERROR("Ignoring flag [%s], unrecognized value.\n", token); continue; } flagval = conv; } GPDEBUG("%s Flag %s (%u).\n", add?"Add":"Remove", token, flagval); if (add) *storage |= flagval; else *storage &= ~flagval; } safefree(str); return 0; } static int check_services(const struct gp_config *cfg) { int i, j; struct gp_service *isvc, *jsvc; const char *isock, *jsock; int ret = 0; /* [gssproxy] section does not get placed in svcs */ for (i = 0; i < cfg->num_svcs; i++) { isvc = cfg->svcs[i]; isock = isvc->socket; if (!isock) { isock = GP_SOCKET_NAME; } if (isvc->program) { if (isvc->program[0] != '/') { ret = 1; GPERROR("Program paths must be absolute!\n"); } else if (strchr(isvc->program, '|')) { ret = 1; GPERROR("The character '|' is invalid in program paths!\n"); } } for (j = 0; j < i; j++) { jsvc = cfg->svcs[j]; jsock = jsvc->socket; if (!jsock) { jsock = GP_SOCKET_NAME; } if (!gp_same(isock, jsock) || !gp_same(isvc->program, jsvc->program) || !gp_selinux_ctx_equal(isvc->selinux_ctx, jsvc->selinux_ctx)) { continue; } if (jsvc->any_uid) { ret = 1; GPERROR("%s sets allow_any_uid with the same socket, " "selinux_context, and program as %s!\n", jsvc->name, isvc->name); } else if (jsvc->euid == isvc->euid) { ret = 1; GPERROR("socket, selinux_context, euid, and program for " "%s and %s should not match!\n", isvc->name, jsvc->name); } } } return ret; } static int load_services(struct gp_config *cfg, struct gp_ini_context *ctx) { int num_sec; char *secname = NULL; const char *value; char *vcopy; char *token; char *handle; int valnum; int ret; int i, n; num_sec = gp_config_get_nsec(ctx); /* allocate enough space for num_sec services, * we won't waste too much space by overallocating */ cfg->svcs = calloc(num_sec, sizeof(struct gp_service *)); if (!cfg->svcs) { ret = ENOMEM; goto done; } for (i = 0; i < num_sec; i++) { secname = gp_config_get_secname(ctx, i); ret = strncmp(secname, "service/", 8); if (ret == 0) { n = cfg->num_svcs; cfg->svcs[n] = calloc(1, sizeof(struct gp_service)); if (!cfg->svcs[n]) { ret = ENOMEM; goto done; } cfg->num_svcs++; /* by default allow both */ cfg->svcs[n]->cred_usage = GSS_C_BOTH; cfg->svcs[n]->name = strdup(secname + 8); if (!cfg->svcs[n]->name) { ret = ENOMEM; goto done; } /* euid can be a string or an int */ ret = gp_config_get_int(ctx, secname, "euid", &valnum); if (ret != 0) { ret = gp_config_get_string(ctx, secname, "euid", &value); if (ret == 0) { struct passwd *eu_passwd; /* static; do not free */ errno = 0; /* needs to be 0; otherwise it won't be set */ eu_passwd = getpwnam(value); if (!eu_passwd) { ret = errno; if (ret == 0) { /* not that it gets set anyway... */ ret = ENOENT; } } else { valnum = eu_passwd->pw_uid; } } if (ret != 0) { /* if euid is missing or there is an error retrieving it * return an error and end. This is a fatal condition. */ if (ret == ENOENT) { GPERROR("Option 'euid' is missing from [%s].\n", secname); ret = EINVAL; } gp_service_free(cfg->svcs[n]); cfg->num_svcs--; safefree(secname); goto done; } } cfg->svcs[n]->euid = valnum; ret = gp_config_get_string(ctx, secname, "allow_any_uid", &value); if (ret == 0) { if (gp_boolean_is_true(value)) { cfg->svcs[n]->any_uid = true; } } ret = gp_config_get_string(ctx, secname, "allow_protocol_transition", &value); if (ret == 0) { if (gp_boolean_is_true(value)) { cfg->svcs[n]->allow_proto_trans = true; } } ret = gp_config_get_string(ctx, secname, "allow_constrained_delegation", &value); if (ret == 0) { if (gp_boolean_is_true(value)) { cfg->svcs[n]->allow_const_deleg = true; } } ret = gp_config_get_string(ctx, secname, "allow_client_ccache_sync", &value); if (ret == 0) { if (gp_boolean_is_true(value)) { cfg->svcs[n]->allow_cc_sync = true; } } ret = gp_config_get_string(ctx, secname, "trusted", &value); if (ret == 0) { if (gp_boolean_is_true(value)) { cfg->svcs[n]->trusted = true; } } ret = gp_config_get_string(ctx, secname, "kernel_nfsd", &value); if (ret == 0) { if (gp_boolean_is_true(value)) { cfg->svcs[n]->kernel_nfsd = true; } } ret = gp_config_get_string(ctx, secname, "impersonate", &value); if (ret == 0) { if (gp_boolean_is_true(value)) { cfg->svcs[n]->impersonate = true; } } ret = gp_config_get_string(ctx, secname, "socket", &value); if (ret == 0) { cfg->svcs[n]->socket = strdup(value); if (!cfg->svcs[n]->socket) { ret = ENOMEM; goto done; } } ret = gp_config_get_string(ctx, secname, "mechs", &value); if (ret != 0) { /* if mechs is missing or there is an error retrieving it * return an error and end. This is a fatal condition. */ if (ret == ENOENT) { GPERROR("Option 'mechs' is missing from [%s].\n", secname); ret = EINVAL; } gp_service_free(cfg->svcs[n]); cfg->num_svcs--; safefree(secname); goto done; } vcopy = strdup(value); if (!vcopy) { ret = ENOMEM; goto done; } token = strtok_r(vcopy, ", ", &handle); do { ret = strcmp(value, "krb5"); if (ret == 0) { ret = get_krb5_mech_cfg(cfg->svcs[n], ctx, secname); if (ret == 0) { cfg->svcs[n]->mechs |= GP_CRED_KRB5; } else { GPERROR("Failed to read krb5 config for %s.\n", secname); safefree(vcopy); return ret; } } else { GPERROR("Unknown mech: %s in [%s], ignoring.\n", token, secname); } token = strtok_r(NULL, ", ", &handle); } while (token != NULL); safefree(vcopy); if (cfg->svcs[n]->mechs == 0) { GPDEBUG("No mechs found for [%s], ignoring.\n", secname); gp_service_free(cfg->svcs[n]); cfg->num_svcs--; safefree(secname); continue; } ret = gp_config_get_string(ctx, secname, "selinux_context", &value); if (ret == 0) { GPDEBUG( "selinux_ctx is deprecated; use euid/socket instead.\n"); cfg->svcs[n]->selinux_ctx = SELINUX_context_new(value); if (!cfg->svcs[n]->selinux_ctx) { ret = EINVAL; goto done; } } ret = gp_config_get_string(ctx, secname, "cred_usage", &value); if (ret == 0) { if (strcasecmp(value, "initiate") == 0) { cfg->svcs[n]->cred_usage = GSS_C_INITIATE; } else if (strcasecmp(value, "accept") == 0) { cfg->svcs[n]->cred_usage = GSS_C_ACCEPT; } else if (strcasecmp(value, "both") == 0) { cfg->svcs[n]->cred_usage = GSS_C_BOTH; } else { GPDEBUG("Invalid value '%s' for cred_usage in [%s].\n", value, secname); ret = EINVAL; goto done; } } cfg->svcs[n]->filter_flags = DEFAULT_FILTERED_FLAGS; ret = gp_config_get_string(ctx, secname, "filter_flags", &value); if (ret == 0) { parse_flags(value, &cfg->svcs[n]->filter_flags); } cfg->svcs[n]->enforce_flags = DEFAULT_ENFORCED_FLAGS; ret = gp_config_get_string(ctx, secname, "enforce_flags", &value); if (ret == 0) { ret = parse_flags(value, &cfg->svcs[n]->enforce_flags); if (ret) goto done; } ret = gp_config_get_string(ctx, secname, "program", &value); if (ret == 0) { cfg->svcs[n]->program = strdup(value); if (!cfg->svcs[n]->program) { ret = ENOMEM; goto done; } } } safefree(secname); } if (cfg->num_svcs == 0) { GPERROR("No service sections configured!\n"); return ENOENT; } ret = check_services(cfg); done: safefree(secname); return ret; } static int gp_init_ini_context(const char *config_file, const char *config_dir, struct gp_ini_context **ctxp) { struct gp_ini_context *ctx; int ret; if (!ctxp) { return EINVAL; } ctx = calloc(1, sizeof(struct gp_ini_context)); if (!ctx) { return ENOENT; } ret = gp_config_init(config_file, config_dir, ctx); if (ret) { free(ctx); } else { *ctxp = ctx; } return ret; } int load_config(struct gp_config *cfg) { struct gp_ini_context *ctx; const char *tmpstr; int tmp_dbg_lvl = 0; int tmpint = 0; int ret; ret = gp_init_ini_context(cfg->config_file, cfg->config_dir, &ctx); if (ret) { return ret; } ret = gp_config_get_string(ctx, "gssproxy", "debug", &tmpstr); if (ret == 0) { if (gp_boolean_is_true(tmpstr)) { if (tmp_dbg_lvl == 0) { tmp_dbg_lvl = 1; } } } else if (ret != ENOENT) { goto done; } ret = gp_config_get_int(ctx, "gssproxy", "debug_level", &tmpint); if (ret == 0) { tmp_dbg_lvl = tmpint; } else if (ret != ENOENT) { goto done; } ret = gp_config_get_string(ctx, "gssproxy", "run_as_user", &tmpstr); if (ret == 0) { cfg->proxy_user = strdup(tmpstr); if (!cfg->proxy_user) { ret = ENOMEM; goto done; } } else if (ret != ENOENT) { goto done; } ret = gp_config_get_int(ctx, "gssproxy", "worker threads", &cfg->num_workers); if (ret != 0 && ret != ENOENT) { goto done; } ret = load_services(cfg, ctx); done: if (ret != 0) { GPERROR("Error reading configuration %d: %s", ret, gp_strerror(ret)); } gp_debug_toggle(tmp_dbg_lvl); gp_config_close(ctx); safefree(ctx); return ret; } struct gp_config *read_config(char *config_file, char *config_dir, char *socket_name, int opt_daemonize) { const char *socket = GP_SOCKET_NAME; const char *dir = NULL; struct gp_config *cfg; int ret; cfg = calloc(1, sizeof(struct gp_config)); if (!cfg) { return NULL; } if (config_file) { cfg->config_file = strdup(config_file); if (!cfg->config_file) { ret = ENOMEM; goto done; } } else { ret = asprintf(&cfg->config_file, "%s/gssproxy.conf", PUBCONF_PATH); if (ret == -1) { goto done; } } if (config_dir) { dir = config_dir; } else if (!config_file) { dir = PUBCONF_PATH; } if (dir) { cfg->config_dir = strdup(dir); if (!cfg->config_dir) { ret = ENOMEM; goto done; } } if (socket_name) socket = socket_name; cfg->socket_name = strdup(socket); if (cfg->socket_name == NULL) { ret = ENOMEM; goto done; } switch (opt_daemonize) { case 0: /* daemonize by default */ case 1: cfg->daemonize = true; break; case 2: cfg->daemonize = false; break; } ret = load_config(cfg); if (ret) { GPDEBUG("Config file(s) not found!\n"); } done: if (ret) { /* recursively frees cfg */ free_config(&cfg); return NULL; } return cfg; } struct gp_creds_handle *gp_service_get_creds_handle(struct gp_service *svc) { return svc->krb5.creds_handle; } void free_config(struct gp_config **cfg) { struct gp_config *config = *cfg; if (!config) { return; } free(config->config_file); free(config->config_dir); free(config->socket_name); free(config->proxy_user); for (int i = 0; i < config->num_svcs; i++) { gp_service_free(config->svcs[i]); safefree(config->svcs[i]); } free(config->svcs); free(config); *cfg = NULL; } static int gp_config_from_file(const char *config_file, struct ini_cfgobj *ini_config, const uint32_t collision_flags) { struct ini_cfgfile *file_ctx = NULL; int ret; ret = ini_config_file_open(config_file, 0, /* metadata_flags, FIXME */ &file_ctx); if (ret) { GPERROR("Failed to open config file: %d (%s)\n", ret, gp_strerror(ret)); ini_config_destroy(ini_config); return ret; } ret = ini_config_parse(file_ctx, INI_STOP_ON_ANY, /* error_level */ collision_flags, INI_PARSE_NOWRAP, /* parse_flags */ ini_config); if (ret) { char **errors = NULL; /* we had a parsing failure */ GPERROR("Failed to parse config file: %d (%s)\n", ret, gp_strerror(ret)); if (ini_config_error_count(ini_config)) { ini_config_get_errors(ini_config, &errors); if (errors) { ini_config_print_errors(stderr, errors); ini_config_free_errors(errors); } } ini_config_file_destroy(file_ctx); ini_config_destroy(ini_config); return ret; } ini_config_file_destroy(file_ctx); return 0; } static int gp_config_from_dir(const char *config_dir, struct ini_cfgobj **ini_config, const uint32_t collision_flags) { struct ini_cfgobj *result_cfg = NULL; struct ref_array *error_list = NULL; int ret; const char *patterns[] = { /* match only files starting with "##-" and ending in ".conf" */ "^[0-9]\\{2\\}-.\\{1,\\}\\.conf$", NULL, }; const char *sections[] = { /* match either "gssproxy" or sections that start with "service/" */ "^gssproxy$", "^service/.*$", NULL, }; /* Permission check failures silently skip the file, so they are not * useful to us. */ ret = ini_config_augment(*ini_config, config_dir, patterns, sections, NULL, /* check_perm */ INI_STOP_ON_ANY, /* error_level */ collision_flags, INI_PARSE_NOWRAP, /* allow sections with the same name in * different files, but log warnings */ INI_MS_DETECT | INI_MS_PRESERVE, &result_cfg, &error_list, NULL); if (error_list) { uint32_t len; len = ref_array_len(error_list); for (uint32_t i = 0; i < len; i++) { /* libini has an unfixable bug where error strings are (char **) */ GPAUDIT("Error when reading config directory: %s\n", *(char **)ref_array_get(error_list, i, NULL)); } ref_array_destroy(error_list); } if (ret && ret != EEXIST) { GPERROR("Error when reading config directory number: %d\n", ret); ref_array_destroy(error_list); return ret; } /* if we read no new files, result_cfg will be NULL */ if (result_cfg) { ini_config_destroy(*ini_config); *ini_config = result_cfg; } return 0; } int gp_config_init(const char *config_file, const char *config_dir, struct gp_ini_context *ctx) { struct ini_cfgobj *ini_config = NULL; int ret; /* Within a single file, merge all collisions */ const uint32_t collision_flags = INI_MS_MERGE | INI_MV1S_ALLOW | INI_MV2S_ALLOW; if (!ctx) { return EINVAL; } ret = ini_config_create(&ini_config); if (ret) { return ENOENT; } if (config_file) { ret = gp_config_from_file(config_file, ini_config, collision_flags); if (ret) { GPDEBUG("Error when trying to read config file %s.\n", config_file); return ret; } } if (config_dir) { ret = gp_config_from_dir(config_dir, &ini_config, collision_flags); if (ret) { GPDEBUG("Error when trying to read config directory %s.\n", config_dir); return ret; } } ctx->private_data = ini_config; return 0; } int gp_config_get_string(struct gp_ini_context *ctx, const char *secname, const char *keyname, const char **value) { struct ini_cfgobj *ini_config = (struct ini_cfgobj *)ctx->private_data; struct value_obj *vo = NULL; int ret; const char *val; if (!value) { return -1; } *value = NULL; ret = ini_get_config_valueobj(secname, keyname, ini_config, INI_GET_FIRST_VALUE, &vo); if (ret) { return ret; } if (!vo) { return ENOENT; } val = ini_get_const_string_config_value(vo, &ret); if (ret) { return ret; } *value = val; return 0; } int gp_config_get_string_array(struct gp_ini_context *ctx, const char *secname, const char *keyname, int *num_values, const char ***values) { struct ini_cfgobj *ini_config = (struct ini_cfgobj *)ctx->private_data; struct value_obj *vo = NULL; const char *value; int ret; int i, count = 0; const char **array = NULL; const char **t_array; if (!values || !num_values) { return EINVAL; } *num_values = 0; *values = NULL; ret = ini_get_config_valueobj(secname, keyname, ini_config, INI_GET_FIRST_VALUE, &vo); if (ret) { return ret; } if (!vo) { return ENOENT; } value = ini_get_const_string_config_value(vo, &ret); if (ret) { return ret; } array = calloc(1, sizeof(char *)); if (array == NULL) { ret = ENOMEM; goto done; } array[count] = strdup(value); if (array[count] == NULL) { ret = ENOMEM; goto done; } count++; do { ret = ini_get_config_valueobj(secname, keyname, ini_config, INI_GET_NEXT_VALUE, &vo); if (ret) { goto done; } if (!vo) { break; } value = ini_get_const_string_config_value(vo, &ret); if (ret) { goto done; } t_array = realloc(array, (count+1) * sizeof(char *)); if (t_array == NULL) { ret = ENOMEM; goto done; } array = t_array; array[count] = strdup(value); if (array[count] == NULL) { ret = ENOMEM; goto done; } count++; } while (1); *num_values = count; *values = array; ret = 0; done: if (ret && array) { for (i = 0; i < count; i++) { safefree(array[i]); } safefree(array); } return ret; } int gp_config_get_int(struct gp_ini_context *ctx, const char *secname, const char *keyname, int *value) { struct ini_cfgobj *ini_config = (struct ini_cfgobj *)ctx->private_data; struct value_obj *vo = NULL; int ret; int val; if (!value) { return EINVAL; } *value = -1; ret = ini_get_config_valueobj(secname, keyname, ini_config, INI_GET_FIRST_VALUE, &vo); if (ret) { return ret; } if (!vo) { return ENOENT; } val = ini_get_int_config_value(vo, 0, /* strict */ 0, /* default */ &ret); if (ret) { return ret; } *value = val; return 0; } int gp_config_get_nsec(struct gp_ini_context *ctx) { struct ini_cfgobj *ini_config = (struct ini_cfgobj *)ctx->private_data; char **list = NULL; int count; int error; list = ini_get_section_list(ini_config, &count, &error); if (error) { return 0; } ini_free_section_list(list); return count; } char *gp_config_get_secname(struct gp_ini_context *ctx, int i) { struct ini_cfgobj *ini_config = (struct ini_cfgobj *)ctx->private_data; char **list = NULL; int count; int error; char *secname; list = ini_get_section_list(ini_config, &count, &error); if (error) { return NULL; } if (i >= count) { return NULL; } secname = strdup(list[i]); ini_free_section_list(list); if (!secname) { return NULL; } return secname; } int gp_config_close(struct gp_ini_context *ctx) { struct ini_cfgobj *ini_config = NULL; if (!ctx) { return 0; } ini_config = (struct ini_cfgobj *)ctx->private_data; ini_config_destroy(ini_config); return 0; } gssproxy-v0.8.2/src/gp_config.h0000644000174300017420000000217313456641744016114 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GSS_CONFIG_H_ #define _GSS_CONFIG_H_ struct gp_ini_context { void *private_data; }; int gp_config_init(const char *config_file, const char *config_dir, struct gp_ini_context *ctx); int gp_config_get_string(struct gp_ini_context *ctx, const char *secname, const char *keyname, const char **value); int gp_config_get_string_array(struct gp_ini_context *ctx, const char *secname, const char *keyname, int *num_values, const char ***values); int gp_config_get_int(struct gp_ini_context *ctx, const char *secname, const char *keyname, int *value); int gp_config_get_nsec(struct gp_ini_context *ctx); char *gp_config_get_secname(struct gp_ini_context *ctx, int i); int gp_config_close(struct gp_ini_context *ctx); #endif /* _GSS_CONFIG_H_ */ gssproxy-v0.8.2/src/gp_conv.c0000644000174300017420000004264013456641744015612 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include #include #include #include "gp_conv.h" #include "src/gp_common.h" void *gp_memdup(void *in, size_t len) { void *out; out = malloc(len); if (!out) { return NULL; } memcpy(out, in, len); return out; } int gp_conv_octet_string(size_t length, void *value, octet_string *out) { if (length == 0) { out->octet_string_val = NULL; out->octet_string_len = 0; return 0; } out->octet_string_val = gp_memdup(value, length); if (!out->octet_string_val) { return ENOMEM; } out->octet_string_len = length; return 0; } int gp_conv_octet_string_alloc(size_t length, void *value, octet_string **out) { octet_string *o; int ret; o = calloc(1, sizeof(octet_string)); if (!o) { return ENOMEM; } ret = gp_conv_octet_string(length, value, o); if (ret) { free(o); return ret; } *out = o; return 0; } void gp_conv_gssx_to_oid(gssx_OID *in, gss_OID out) { if (in == NULL) { out->length = 0; out->elements = NULL; return; } out->length = in->octet_string_len; out->elements = (void *)in->octet_string_val; } int gp_conv_gssx_to_oid_alloc(gssx_OID *in, gss_OID *out) { gss_OID o; if (in == NULL || in->octet_string_len == 0) { *out = GSS_C_NO_OID; return 0; } o = calloc(1, sizeof(gss_OID_desc)); if (!o) { return ENOMEM; } o->elements = gp_memdup(in->octet_string_val, in->octet_string_len); if (!o->elements) { free(o); return ENOMEM; } o->length = in->octet_string_len; *out = o; return 0; } int gp_conv_oid_to_gssx(gss_OID in, gssx_OID *out) { if (in == GSS_C_NO_OID) { return gp_conv_octet_string(0, NULL, out); } return gp_conv_octet_string(in->length, in->elements, out); } int gp_conv_oid_to_gssx_alloc(gss_OID in, gssx_OID **out) { if (in == GSS_C_NO_OID) { *out = NULL; return 0; } return gp_conv_octet_string_alloc(in->length, in->elements, out); } void gp_conv_gssx_to_buffer(gssx_buffer *in, gss_buffer_t out) { out->length = in->octet_string_len; out->value = (void *)in->octet_string_val; } int gp_conv_gssx_to_buffer_alloc(gssx_buffer *in, gss_buffer_t *out) { gss_buffer_desc *o; if (in->octet_string_len == 0) { *out = GSS_C_NO_BUFFER; return 0; } o = malloc(sizeof(gss_buffer_desc)); if (!o) { return ENOMEM; } o->value = gp_memdup(in->octet_string_val, in->octet_string_len); if (!o->value) { free(o); return ENOMEM; } o->length = in->octet_string_len; *out = o; return 0; } int gp_copy_gssx_to_buffer(gssx_buffer *in, gss_buffer_t out) { gss_buffer_desc empty = GSS_C_EMPTY_BUFFER; if (in->octet_string_len == 0) { *out = empty; return 0; } out->value = gp_memdup(in->octet_string_val, in->octet_string_len); if (!out->value) { return ENOMEM; } out->length = in->octet_string_len; return 0; } int gp_copy_gssx_to_string_buffer(gssx_buffer *in, gss_buffer_t out) { gss_buffer_desc empty = GSS_C_EMPTY_BUFFER; char *str; if (in->octet_string_len == 0) { *out = empty; return 0; } str = malloc(in->octet_string_len + 1); if (!str) { return ENOMEM; } memcpy(str, in->octet_string_val, in->octet_string_len); str[in->octet_string_len] = '\0'; out->length = in->octet_string_len; out->value = str; return 0; } int gp_conv_buffer_to_gssx(gss_buffer_t in, gssx_buffer *out) { return gp_conv_octet_string(in->length, in->value, out); } int gp_conv_buffer_to_gssx_alloc(gss_buffer_t in, gssx_buffer **out) { return gp_conv_octet_string_alloc(in->length, in->value, out); } void gp_conv_gssx_to_cb(gssx_cb *in, gss_channel_bindings_t out) { out->initiator_addrtype = in->initiator_addrtype; gp_conv_gssx_to_buffer(&in->initiator_address, &out->initiator_address); out->acceptor_addrtype = in->acceptor_addrtype; gp_conv_gssx_to_buffer(&in->acceptor_address, &out->acceptor_address); gp_conv_gssx_to_buffer(&in->application_data, &out->application_data); } int gp_conv_cb_to_gssx(gss_channel_bindings_t in, gssx_cb *out) { int ret; out->initiator_addrtype = in->initiator_addrtype; ret = gp_conv_buffer_to_gssx(&in->initiator_address, &out->initiator_address); if (ret) { goto done; } out->acceptor_addrtype = in->acceptor_addrtype; ret = gp_conv_buffer_to_gssx(&in->acceptor_address, &out->acceptor_address); if (ret) { goto done; } ret = gp_conv_buffer_to_gssx(&in->application_data, &out->application_data); if (ret) { goto done; } ret = 0; done: if (ret) { xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out->initiator_address); xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out->acceptor_address); xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out->application_data); } return ret; } int gp_conv_cb_to_gssx_alloc(gss_channel_bindings_t in, gssx_cb **out) { gssx_cb *o; int ret; o = malloc(sizeof(gssx_cb)); if (!o) { return ENOMEM; } ret = gp_conv_cb_to_gssx(in, o); if (ret) { free(o); return ENOMEM; } *out = o; return 0; } gssx_cred_usage gp_conv_cred_usage_to_gssx(gss_cred_usage_t in) { switch (in) { case GSS_C_BOTH: return GSSX_C_BOTH; case GSS_C_INITIATE: return GSSX_C_INITIATE; case GSS_C_ACCEPT: return GSSX_C_ACCEPT; default: return 0; } } gss_cred_usage_t gp_conv_gssx_to_cred_usage(gssx_cred_usage in) { switch (in) { case GSSX_C_BOTH: return GSS_C_BOTH; case GSSX_C_INITIATE: return GSS_C_INITIATE; case GSSX_C_ACCEPT: return GSS_C_ACCEPT; default: return 0; } } int gp_conv_err_to_gssx_string(uint32_t status, int type, gss_OID oid, utf8string *ret_str) { uint32_t ret_maj; uint32_t ret_min; uint32_t msg_ctx; gss_buffer_desc gssbuf; char *str, *t; int ret = 0; msg_ctx = 0; str = NULL; do { ret_maj = gss_display_status(&ret_min, status, type, oid, &msg_ctx, &gssbuf); if (ret_maj == GSS_S_COMPLETE) { if (str) { ret = asprintf(&t, "%s, %s", str, (char *)gssbuf.value); if (ret == -1) { ret = ENOMEM; } else { free(str); str = t; } } else { str = strdup((char *)gssbuf.value); if (!str) { ret = ENOMEM; } } gss_release_buffer(&ret_min, &gssbuf); } else { ret = EINVAL; } if (ret) { goto done; } } while (msg_ctx); ret_str->utf8string_len = strlen(str) + 1; ret_str->utf8string_val = str; ret = 0; done: if (ret) { free(str); } return ret; } uint32_t gp_conv_name_to_gssx(uint32_t *min, gss_name_t in, gssx_name *_out) { uint32_t ret_maj; uint32_t ret_min; gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER; gss_OID name_type; gss_buffer_desc exported_name = GSS_C_EMPTY_BUFFER; gss_buffer_desc exported_composite_name = GSS_C_EMPTY_BUFFER; gssx_name out = { .display_name.octet_string_len = 0 }; int ret; ret_maj = gss_display_name(&ret_min, in, &name_buffer, &name_type); if (ret_maj) { goto done; } ret = gp_conv_buffer_to_gssx(&name_buffer, &out.display_name); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret = gp_conv_oid_to_gssx(name_type, &out.name_type); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret_maj = gss_export_name(&ret_min, in, &exported_name); if (ret_maj == 0) { ret = gp_conv_buffer_to_gssx(&exported_name, &out.exported_name); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } } else { /* In case the error is GSS_S_NAME_NOT_MN the name was not * canonicalized but that is ok we simply do not export the name * in this case */ if (ret_maj != GSS_S_NAME_NOT_MN) { goto done; } } ret_maj = gss_export_name_composite(&ret_min, in, &exported_composite_name); if (ret_maj == 0) { ret = gp_conv_buffer_to_gssx(&exported_composite_name, &out.exported_composite_name); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } } else { /* In case the error is GSS_S_NAME_NOT_MN the name was not * canonicalized but that is ok we simply do not export the name * in this case */ if (ret_maj != GSS_S_NAME_NOT_MN && ret_maj != GSS_S_UNAVAILABLE) { goto done; } } ret_maj = GSS_S_COMPLETE; /* out->name_attributes */ done: *min = ret_min; gss_release_buffer(&ret_min, &name_buffer); gss_release_buffer(&ret_min, &exported_name); gss_release_buffer(&ret_min, &exported_composite_name); if (ret_maj) { xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out.display_name); xdr_free((xdrproc_t)xdr_gssx_OID, (char *)&out.name_type); xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out.exported_name); xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out.exported_composite_name); } else { *_out = out; } return ret_maj; } uint32_t gp_conv_name_to_gssx_alloc(uint32_t *min, gss_name_t in, gssx_name **out) { gssx_name *o; uint32_t ret_maj; o = calloc(1, sizeof(gssx_name)); if (!o) { return ENOMEM; } ret_maj = gp_conv_name_to_gssx(min, in, o); if (ret_maj) { free(o); } else { *out = o; } return ret_maj; } uint32_t gp_conv_gssx_to_name(uint32_t *min, gssx_name *in, gss_name_t *out) { gss_buffer_t input_name = GSS_C_NO_BUFFER; gss_OID name_type = GSS_C_NO_OID; gss_buffer_desc name_buffer; uint32_t ret_maj; uint32_t ret_min; int ret; if (in->display_name.octet_string_len != 0) { /* ok we have a display name. * In this case always import and canonicalize it so we can * safely export the name using the original form, even if we * already have exported_name */ ret = gp_conv_gssx_to_buffer_alloc(&in->display_name, &input_name); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret = gp_conv_gssx_to_oid_alloc(&in->name_type, &name_type); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret_maj = gss_import_name(&ret_min, input_name, name_type, out); if (ret_maj) { goto done; } } else { gp_conv_gssx_to_buffer(&in->exported_name, &name_buffer); ret_maj = gss_import_name(&ret_min, &name_buffer, GSS_C_NT_EXPORT_NAME, out); if (ret_maj) { goto done; } } done: *min = ret_min; gss_release_buffer(&ret_min, input_name); free(input_name); gss_release_oid(&ret_min, &name_type); return ret_maj; } int gp_conv_status_to_gssx(uint32_t ret_maj, uint32_t ret_min, gss_OID mech, struct gssx_status *status) { int ret; status->major_status = ret_maj; if (mech) { ret = gp_conv_oid_to_gssx(mech, &status->mech); if (ret) { goto done; } } status->minor_status = ret_min; if (ret_maj) { ret = gp_conv_err_to_gssx_string(ret_maj, GSS_C_GSS_CODE, mech, &status->major_status_string); if (ret) { goto done; } } if (ret_min) { ret = gp_conv_err_to_gssx_string(ret_min, GSS_C_MECH_CODE, mech, &status->minor_status_string); if (ret) { goto done; } } ret = 0; done: return ret; } int gp_copy_utf8string(utf8string *in, utf8string *out) { out->utf8string_val = gp_memdup(in->utf8string_val, in->utf8string_len); if (!out->utf8string_val) { return ENOMEM; } out->utf8string_len = in->utf8string_len; return 0; } int gp_copy_gssx_status_alloc(gssx_status *in, gssx_status **out) { gssx_status *o; int ret; o = calloc(1, sizeof(gssx_status)); if (!o) { return ENOMEM; } o->major_status = in->major_status; o->minor_status = in->minor_status; if (in->mech.octet_string_len) { ret = gp_conv_octet_string(in->mech.octet_string_len, in->mech.octet_string_val, &o->mech); if (ret) { goto done; } } if (in->major_status_string.utf8string_len) { ret = gp_copy_utf8string(&in->major_status_string, &o->major_status_string); if (ret) { goto done; } } if (in->minor_status_string.utf8string_len) { ret = gp_copy_utf8string(&in->minor_status_string, &o->minor_status_string); if (ret) { goto done; } } if (in->server_ctx.octet_string_len) { ret = gp_conv_octet_string(in->server_ctx.octet_string_len, in->server_ctx.octet_string_val, &o->server_ctx); if (ret) { goto done; } } *out = o; ret = 0; done: if (ret) { xdr_free((xdrproc_t)xdr_gssx_status, (char *)o); free(o); } return ret; } int gp_conv_gssx_to_oid_set(gssx_OID_set *in, gss_OID_set *out) { gss_OID_set o; if (in->gssx_OID_set_len == 0) { *out = GSS_C_NO_OID_SET; return 0; } o = malloc(sizeof(gss_OID_set_desc)); if (!o) { return ENOMEM; } o->count = in->gssx_OID_set_len; o->elements = calloc(o->count, sizeof(gss_OID_desc)); if (!o->elements) { free(o); return ENOMEM; } for (size_t i = 0; i < o->count; i++) { o->elements[i].elements = gp_memdup(in->gssx_OID_set_val[i].octet_string_val, in->gssx_OID_set_val[i].octet_string_len); if (!o->elements[i].elements) { while (i > 0) { i--; free(o->elements[i].elements); } free(o->elements); free(o); return ENOMEM; } o->elements[i].length = in->gssx_OID_set_val[i].octet_string_len; } *out = o; return 0; } int gp_conv_oid_set_to_gssx(gss_OID_set in, gssx_OID_set *out) { int ret; if (in->count == 0) { return 0; } out->gssx_OID_set_len = in->count; out->gssx_OID_set_val = calloc(in->count, sizeof(gssx_OID)); if (!out->gssx_OID_set_val) { return ENOMEM; } for (size_t i = 0; i < in->count; i++) { ret = gp_conv_octet_string(in->elements[i].length, in->elements[i].elements, &out->gssx_OID_set_val[i]); if (ret) { while (i > 0) { i--; free(out->gssx_OID_set_val[i].octet_string_val); } free(out->gssx_OID_set_val); return ENOMEM; } } return 0; } int gp_copy_gssx_name(gssx_name *in, gssx_name *out) { int ret; ret = gp_conv_octet_string(in->display_name.octet_string_len, in->display_name.octet_string_val, &out->display_name); if (ret) { goto done; } ret = gp_conv_octet_string(in->name_type.octet_string_len, in->name_type.octet_string_val, &out->name_type); if (ret) { goto done; } ret = gp_conv_octet_string(in->exported_name.octet_string_len, in->exported_name.octet_string_val, &out->exported_name); if (ret) { goto done; } ret = gp_conv_octet_string(in->exported_composite_name.octet_string_len, in->exported_composite_name.octet_string_val, &out->exported_composite_name); if (ret) { goto done; } done: if (ret) { xdr_free((xdrproc_t)xdr_gssx_name, (char *)out); } return ret; } int gp_copy_gssx_name_alloc(gssx_name *in, gssx_name **out) { gssx_name *o; int ret; o = calloc(1, sizeof(gssx_name)); if (!o) { return ENOMEM; } ret = gp_copy_gssx_name(in, o); if (ret) { free(o); return ret; } *out = o; return 0; } gssproxy-v0.8.2/src/gp_conv.h0000644000174300017420000000442613456641744015617 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GSS_CONV_H_ #define _GSS_CONV_H_ #include #include #include "rpcgen/gss_proxy.h" void *gp_memdup(void *in, size_t len); int gp_conv_octet_string(size_t length, void *value, octet_string *out); int gp_conv_octet_string_alloc(size_t length, void *value, octet_string **out); void gp_conv_gssx_to_oid(gssx_OID *in, gss_OID out); int gp_conv_gssx_to_oid_alloc(gssx_OID *in, gss_OID *out); int gp_conv_oid_to_gssx(gss_OID in, gssx_OID *out); int gp_conv_oid_to_gssx_alloc(gss_OID in, gssx_OID **out); void gp_conv_gssx_to_buffer(gssx_buffer *in, gss_buffer_t out); int gp_conv_gssx_to_buffer_alloc(gssx_buffer *in, gss_buffer_t *out); int gp_copy_gssx_to_buffer(gssx_buffer *in, gss_buffer_t out); int gp_copy_gssx_to_string_buffer(gssx_buffer *in, gss_buffer_t out); int gp_conv_buffer_to_gssx(gss_buffer_t in, gssx_buffer *out); int gp_conv_buffer_to_gssx_alloc(gss_buffer_t in, gssx_buffer **out); void gp_conv_gssx_to_cb(gssx_cb *in, gss_channel_bindings_t out); int gp_conv_cb_to_gssx(gss_channel_bindings_t in, gssx_cb *out); int gp_conv_cb_to_gssx_alloc(gss_channel_bindings_t in, gssx_cb **out); gssx_cred_usage gp_conv_cred_usage_to_gssx(gss_cred_usage_t in); gss_cred_usage_t gp_conv_gssx_to_cred_usage(gssx_cred_usage in); int gp_conv_err_to_gssx_string(uint32_t status, int type, gss_OID oid, utf8string *ret_str); uint32_t gp_conv_name_to_gssx(uint32_t *min, gss_name_t in, gssx_name *out); uint32_t gp_conv_name_to_gssx_alloc(uint32_t *min, gss_name_t in, gssx_name **out); uint32_t gp_conv_gssx_to_name(uint32_t *min, gssx_name *in, gss_name_t *out); int gp_conv_status_to_gssx(uint32_t ret_maj, uint32_t ret_min, gss_OID mech, struct gssx_status *status); int gp_copy_utf8string(utf8string *in, utf8string *out); int gp_copy_gssx_status_alloc(gssx_status *in, gssx_status **out); int gp_conv_gssx_to_oid_set(gssx_OID_set *in, gss_OID_set *out); int gp_conv_oid_set_to_gssx(gss_OID_set in, gssx_OID_set *out); int gp_copy_gssx_name(gssx_name *in, gssx_name *out); int gp_copy_gssx_name_alloc(gssx_name *in, gssx_name **out); #endif /* _GSS_CONV_H_ */ gssproxy-v0.8.2/src/gp_creds.c0000644000174300017420000010262013456641744015740 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "gp_proxy.h" #include "gp_rpc_creds.h" #include "gp_creds.h" #include "gp_conv.h" #include "gp_export.h" #define GSS_MECH_KRB5_OID_LENGTH 9 #define GSS_MECH_KRB5_OID "\052\206\110\206\367\022\001\002\002" gss_OID_desc gp_mech_krb5 = { GSS_MECH_KRB5_OID_LENGTH, discard_const(GSS_MECH_KRB5_OID) }; struct supported_mechs_map { int internal_id; const gss_OID mech; } supported_mechs_map[] = { { GP_CRED_KRB5, &gp_mech_krb5 }, { 0, NULL } }; bool gp_creds_allowed_mech(struct gp_call_ctx *gpcall, gss_OID desired_mech) { int i; for (i = 0; supported_mechs_map[i].internal_id != 0; i++) { if (gpcall->service->mechs & supported_mechs_map[i].internal_id) { if (gss_oid_equal(desired_mech, supported_mechs_map[i].mech)) { return true; } } } return false; } uint32_t gp_get_supported_mechs(uint32_t *min, gss_OID_set *set) { uint32_t ret_maj; uint32_t ret_min; int i; ret_maj = gss_create_empty_oid_set(&ret_min, set); if (ret_maj) { *min = ret_min; return ret_maj; } for (i = 0; supported_mechs_map[i].internal_id != 0; i++) { ret_maj = gss_add_oid_set_member(&ret_min, supported_mechs_map[i].mech, set); if (ret_maj) { *min = ret_min; gss_release_oid_set(&ret_min, set); return ret_maj; } } *min = 0; return GSS_S_COMPLETE; } struct gp_service *gp_creds_match_conn(struct gssproxy_ctx *gpctx, struct gp_conn *conn) { struct gp_creds *gcs; const char *socket; const char *program; gcs = gp_conn_get_creds(conn); socket = gp_conn_get_socket(conn); program = gp_conn_get_program(conn); for (int i = 0; i < gpctx->config->num_svcs; i++) { struct gp_service *svc = gpctx->config->svcs[i]; if ((!svc->any_uid && svc->euid != gcs->ucred.uid) || !gp_conn_check_selinux(conn, svc->selinux_ctx) || (svc->program && !gp_same(program, svc->program)) || (svc->socket && !gp_same(socket, svc->socket)) || (!svc->socket && !gp_same(socket, gpctx->config->socket_name))) { continue; } GPDEBUGN(2, "Connection matched service %s\n", svc->name); return svc; } GPDEBUGN(2, "No matching service found\n"); return NULL; } #define PWBUFLEN 2048 static char *uid_to_name(uid_t uid) { struct passwd pwd, *res = NULL; char buffer[PWBUFLEN]; int ret; ret = getpwuid_r(uid, &pwd, buffer, PWBUFLEN, &res); if (ret || !res) { return NULL; } return strdup(pwd.pw_name); } static char *get_formatted_string(const char *orig, uid_t target_uid) { int len, left, right; char *user = NULL; char *str; char *tmp; char *p; str = strdup(orig); if (!str) { return NULL; } len = strlen(str); p = str; while ((p = strchr(p, '%')) != NULL) { p++; switch (*p) { case '%': left = p - str; memmove(p, p + 1, left - 1); len--; continue; case 'U': p++; left = p - str; right = len - left; len = asprintf(&tmp, "%.*s%d%s", left - 2, str, target_uid, p); safefree(str); if (len == -1) { goto done; } str = tmp; p = str + (len - right); break; case 'u': if (!user) { user = uid_to_name(target_uid); if (!user) { safefree(str); goto done; } } p++; left = p - str; right = len - left; len = asprintf(&tmp, "%.*s%s%s", left - 2, str, user, p); safefree(str); if (len == -1) { goto done; } str = tmp; p = str + (len - right); break; default: GPDEBUG("Invalid format code '%%%c'\n", *p); safefree(str); goto done; } } done: safefree(user); return str; } int gp_get_acquire_type(struct gssx_arg_acquire_cred *arg) { struct gssx_option *val = NULL; gp_options_find(val, arg->options, ACQUIRE_TYPE_OPTION, sizeof(ACQUIRE_TYPE_OPTION)); if (val) { if (gp_option_value_match(val, ACQUIRE_IMPERSONATE_NAME, sizeof(ACQUIRE_IMPERSONATE_NAME))) { return ACQ_IMPNAME; } else { return -1; } } return ACQ_NORMAL; } static bool try_impersonate(struct gp_service *svc, gss_cred_usage_t cred_usage, enum gp_aqcuire_cred_type acquire_type) { if (acquire_type == ACQ_IMPNAME && (svc->allow_proto_trans || svc->trusted)) { return true; } if (svc->impersonate && (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)) { return true; } return false; } static void safe_free_mem_ccache(void *data) { krb5_error_code e; krb5_context ctx = NULL; krb5_ccache cc = NULL; char *ccname = (char *) data; if (!ccname) { return; } e = krb5_init_context(&ctx); if (e != 0) { goto done; } e = krb5_cc_resolve(ctx, ccname, &cc); if (e != 0) { goto done; } /* also closes handle */ krb5_cc_destroy(ctx, cc); done: if (ctx) { krb5_free_context(ctx); } free(ccname); } static int ensure_segregated_ccache(struct gp_call_ctx *gpcall, int cc_num, gss_key_value_set_desc *cs) { int ret; char *buf; pid_t tid = -1; if (cc_num != -1) { return 0; } /* We always have space for at least 1 more entry in cs. */ cc_num = cs->count; cs->elements[cc_num].key = strdup("ccache"); if (!cs->elements[cc_num].key) { return ENOMEM; } do { errno = 0; tid = syscall(SYS_gettid); } while (tid == -1 && errno == EINTR); ret = asprintf(&buf, "MEMORY:internal_%d", tid); if (ret == -1) { return ENOMEM; } gpcall->destroy_callback = safe_free_mem_ccache; gpcall->destroy_callback_data = buf; cs->elements[cc_num].value = strdup(buf); if (!cs->elements[cc_num].value) { return ENOMEM; } cs->count = cc_num + 1; return 0; } static int gp_get_cred_environment(struct gp_call_ctx *gpcall, gssx_name *desired_name, gss_name_t *requested_name, gss_cred_usage_t *cred_usage, gss_key_value_set_desc *cs) { struct gp_service *svc; gss_name_t name = GSS_C_NO_NAME; gss_buffer_desc namebuf; gss_OID_desc name_type; uint32_t ret_maj = 0; uint32_t ret_min = 0; uid_t target_uid; bool user_requested = false; bool use_service_keytab = false; int ret = -1; int k_num = -1; int ck_num = -1; int cc_num = -1; memset(cs, 0, sizeof(gss_key_value_set_desc)); target_uid = gp_conn_get_uid(gpcall->connection); svc = gpcall->service; /* filter based on cred_usage */ if (svc->cred_usage != GSS_C_BOTH) { if (*cred_usage == GSS_C_BOTH) { *cred_usage = svc->cred_usage; } else if (svc->cred_usage != *cred_usage) { ret = EACCES; goto done; } } if (desired_name) { gp_conv_gssx_to_oid(&desired_name->name_type, &name_type); /* A service retains the trusted flag only if the current uid matches * the configured euid */ if (svc->trusted && (svc->euid == target_uid) && (gss_oid_equal(&name_type, GSS_C_NT_STRING_UID_NAME) || gss_oid_equal(&name_type, GSS_C_NT_MACHINE_UID_NAME))) { target_uid = atol(desired_name->display_name.octet_string_val); user_requested = true; } else { /* it's a user request if it comes from an arbitrary uid */ if (svc->euid != target_uid) { user_requested = true; } else { use_service_keytab = true; } ret_maj = gp_conv_gssx_to_name(&ret_min, desired_name, &name); if (ret_maj) { goto done; } *requested_name = name; } } else { /* No name provided */ if (svc->trusted && (svc->euid == target_uid)) { use_service_keytab = true; } else if (svc->euid != target_uid) { user_requested = true; } } /* impersonation case (only for initiation) */ if (user_requested) { if (try_impersonate(svc, *cred_usage, ACQ_NORMAL)) { char *str; /* When impersonating we want to use the service keytab to * acquire initial credential ... */ use_service_keytab = true; /* ... and after that make the s4u2self delegation dance with the * target name identifying the user */ str = uid_to_name(target_uid); if (str == NULL) { GPERROR("Failed to get username from uid %d\n", target_uid); return ENOENT; } namebuf.value = str; namebuf.length = strlen(str); ret_maj = gss_import_name(&ret_min, &namebuf, GSS_C_NT_USER_NAME, requested_name); if (ret_maj) { GPERROR("Failed to import username %s\n", str); safefree(str); return ENOMEM; } safefree(str); } } if (use_service_keytab && (*requested_name == GSS_C_NO_NAME) && (svc->krb5.principal)) { /* configuration dictates to use a specific name */ gss_buffer_desc const_buf; const_buf.value = svc->krb5.principal; const_buf.length = strlen(svc->krb5.principal) + 1; ret_maj = gss_import_name(&ret_min, &const_buf, discard_const(GSS_KRB5_NT_PRINCIPAL_NAME), requested_name); if (ret_maj) { GPERROR("Failed to import krb5_principal name %s\n", svc->krb5.principal); goto done; } } if (svc->krb5.store.count == 0) { return 0; } /* allocate 2 more than in source, just in case we need to add * an internal client_keytab element and ccache */ cs->elements = calloc(svc->krb5.store.count + 2, sizeof(gss_key_value_element_desc)); if (!cs->elements) { ret = ENOMEM; goto done; } for (unsigned d = 0; d < svc->krb5.store.count; d++) { if (strcmp(svc->krb5.store.elements[d].key, "client_keytab") == 0) { ck_num = cs->count; } else if (strcmp(svc->krb5.store.elements[d].key, "keytab") == 0) { k_num = cs->count; } else if (strcmp(svc->krb5.store.elements[d].key, "ccache") == 0) { cc_num = cs->count; } cs->elements[cs->count].key = strdup(svc->krb5.store.elements[d].key); if (!cs->elements[cs->count].key) { ret = ENOMEM; goto done; } cs->elements[cs->count].value = get_formatted_string(svc->krb5.store.elements[d].value, target_uid); if (!cs->elements[cs->count].value) { safefree(cs->elements[cs->count].key); GPDEBUG("Failed to build credential store formatted string.\n"); ret = ENOMEM; goto done; } cs->count++; } /* when a user is not explicitly requested then it means the calling * application wants to use the credentials in the standard keytab, * if any. */ if (use_service_keytab) { if (k_num == -1) { if (ck_num == -1) { ret = EINVAL; } else { /* allow a service to define only the client keytab */ ret = 0; } goto done; } if (ck_num == -1) { /* we always have space for 1 more */ ck_num = cs->count; cs->elements[ck_num].key = strdup("client_keytab"); if (!cs->elements[ck_num].key) { ret = ENOMEM; goto done; } cs->count = ck_num + 1; } else { safefree(cs->elements[ck_num].value); } cs->elements[ck_num].value = strdup(cs->elements[k_num].value); if (!cs->elements[ck_num].value) { ret = ENOMEM; goto done; } } ret = ensure_segregated_ccache(gpcall, cc_num, cs); if (ret != 0) { goto done; } ret = 0; done: if (ret) { free_cred_store_elements(cs); } return ret; } static uint32_t gp_check_cred(uint32_t *min, gss_cred_id_t in_cred, gssx_name *desired_name, gss_cred_usage_t cred_usage) { uint32_t ret_maj = 0; uint32_t ret_min = 0; uint32_t discard; uint32_t i; gss_name_t req_name = GSS_C_NO_NAME; gss_name_t check_name = GSS_C_NO_NAME; gss_OID_set mechanisms = GSS_C_NO_OID_SET; gss_cred_usage_t usage; uint32_t lifetime; int present = 0; ret_maj = gss_inquire_cred(&ret_min, in_cred, desired_name?&check_name:NULL, &lifetime, &usage, &mechanisms); if (ret_maj) { goto done; } for (i = 0; i < mechanisms->count; i++) { present = gss_oid_equal(&mechanisms->elements[i], gss_mech_krb5); if (present) break; } if (!present) { ret_maj = GSS_S_CRED_UNAVAIL; goto done; } if (desired_name) { int equal; ret_maj = gp_conv_gssx_to_name(&ret_min, desired_name, &req_name); if (ret_maj) { goto done; } ret_maj = gss_compare_name(&ret_min, req_name, check_name, &equal); if (ret_maj) { goto done; } if (!equal) { ret_maj = GSS_S_CRED_UNAVAIL; goto done; } } switch (cred_usage) { case GSS_C_ACCEPT: if (usage == GSS_C_INITIATE) { ret_maj = GSS_S_NO_CRED; goto done; } break; case GSS_C_INITIATE: if (usage == GSS_C_ACCEPT) { ret_maj = GSS_S_NO_CRED; goto done; } break; case GSS_C_BOTH: if (usage != GSS_C_BOTH) { ret_maj = GSS_S_NO_CRED; goto done; } break; } if (lifetime == 0) { ret_maj = GSS_S_CREDENTIALS_EXPIRED; } else { ret_maj = GSS_S_COMPLETE; } done: gss_release_oid_set(&discard, &mechanisms); gss_release_name(&discard, &check_name); gss_release_name(&discard, &req_name); *min = ret_min; return ret_maj; } uint32_t gp_add_krb5_creds(uint32_t *min, struct gp_call_ctx *gpcall, enum gp_aqcuire_cred_type acquire_type, gss_cred_id_t in_cred, gssx_name *desired_name, gss_cred_usage_t cred_usage, uint32_t initiator_time_req UNUSED, uint32_t acceptor_time_req UNUSED, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, uint32_t *initiator_time_rec, uint32_t *acceptor_time_rec) { uint32_t ret_maj = 0; uint32_t ret_min = 0; uint32_t discard; gss_name_t req_name = GSS_C_NO_NAME; gss_OID_set_desc desired_mechs = { 1, &gp_mech_krb5 }; gss_key_value_set_desc cred_store = { 0 }; gss_cred_id_t impersonator_cred = GSS_C_NO_CREDENTIAL; gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT; gss_ctx_id_t acceptor_context = GSS_C_NO_CONTEXT; gss_name_t target_name = GSS_C_NO_NAME; gss_buffer_desc init_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc accept_token = GSS_C_EMPTY_BUFFER; gss_cred_id_t input_cred; if (!min || !output_cred_handle) { return GSS_S_CALL_INACCESSIBLE_WRITE; } *min = 0; *output_cred_handle = GSS_C_NO_CREDENTIAL; if (actual_mechs) { *actual_mechs = GSS_C_NO_OID_SET; } if (in_cred != GSS_C_NO_CREDENTIAL && acquire_type != ACQ_IMPNAME) { /* NOTE: we can't yet handle adding to an existing credential due * to the way gss_krb5_import_cred works. This limitation should * be removed by adding a gssapi extension that superceedes this * function completely */ /* just check if it is a valid krb5 cred */ ret_maj = gp_check_cred(&ret_min, in_cred, desired_name, cred_usage); if (ret_maj == GSS_S_COMPLETE) { return GSS_S_COMPLETE; } else if (ret_maj == GSS_S_CREDENTIALS_EXPIRED || ret_maj == GSS_S_NO_CRED) { /* continue and try to obtain new creds */ ret_maj = 0; ret_min = 0; } else { *min = ret_min; return GSS_S_CRED_UNAVAIL; } } if (acquire_type == ACQ_NORMAL) { ret_min = gp_get_cred_environment(gpcall, desired_name, &req_name, &cred_usage, &cred_store); if (ret_min) { ret_maj = GSS_S_CRED_UNAVAIL; } } else if (desired_name) { ret_maj = gp_conv_gssx_to_name(&ret_min, desired_name, &req_name); } if (ret_maj) { goto done; } if (!try_impersonate(gpcall->service, cred_usage, acquire_type)) { ret_maj = gss_acquire_cred_from(&ret_min, req_name, GSS_C_INDEFINITE, &desired_mechs, cred_usage, &cred_store, output_cred_handle, actual_mechs, NULL); if (ret_maj) { goto done; } } else { /* impersonation */ switch (acquire_type) { case ACQ_NORMAL: ret_maj = gss_acquire_cred_from(&ret_min, GSS_C_NO_NAME, GSS_C_INDEFINITE, &desired_mechs, GSS_C_BOTH, &cred_store, &impersonator_cred, NULL, NULL); if (ret_maj) { goto done; } input_cred = impersonator_cred; break; case ACQ_IMPNAME: input_cred = in_cred; break; default: ret_maj = GSS_S_FAILURE; ret_min = EFAULT; goto done; } ret_maj = gss_inquire_cred(&ret_min, input_cred, &target_name, NULL, NULL, NULL); if (ret_maj) { goto done; } ret_maj = gss_acquire_cred_impersonate_name(&ret_min, input_cred, req_name, GSS_C_INDEFINITE, &desired_mechs, GSS_C_INITIATE, &user_cred, actual_mechs, NULL); if (ret_maj) { goto done; } if (acquire_type == ACQ_IMPNAME) { /* we are done here */ *output_cred_handle = user_cred; user_cred = GSS_C_NO_CREDENTIAL; goto done; } /* now acquire credentials for impersonated user to self */ ret_maj = gss_init_sec_context(&ret_min, user_cred, &initiator_context, target_name, &gp_mech_krb5, GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, NULL, &init_token, NULL, NULL); if (ret_maj) { goto done; } /* accept context to be able to store delegated credentials */ ret_maj = gss_accept_sec_context(&ret_min, &acceptor_context, input_cred, &init_token, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &accept_token, NULL, NULL, output_cred_handle); if (ret_maj) { goto done; } } if (initiator_time_rec || acceptor_time_rec) { ret_maj = gss_inquire_cred_by_mech(&ret_min, *output_cred_handle, &gp_mech_krb5, NULL, initiator_time_rec, acceptor_time_rec, NULL); if (ret_maj) { goto done; } } done: if (ret_maj) { gp_log_status(&gp_mech_krb5, ret_maj, ret_min); if (*output_cred_handle) { gss_release_cred(&discard, output_cred_handle); } if (actual_mechs && *actual_mechs) { gss_release_oid_set(&discard, actual_mechs); } } free_cred_store_elements(&cred_store); gss_release_cred(&discard, &impersonator_cred); gss_release_cred(&discard, &user_cred); gss_release_name(&discard, &target_name); gss_delete_sec_context(&discard, &initiator_context, NULL); gss_release_buffer(&discard, &init_token); gss_release_buffer(&discard, &accept_token); gss_release_name(&discard, &req_name); *min = ret_min; return ret_maj; } void gp_filter_flags(struct gp_call_ctx *gpcall, uint32_t *flags) { *flags |= gpcall->service->enforce_flags; *flags &= ~gpcall->service->filter_flags; } static uint32_t get_impersonator_fallback(uint32_t *min, gss_cred_id_t cred, char **impersonator) { uint32_t ret_maj = 0; uint32_t ret_min = 0; char *memcache = NULL; krb5_context context = NULL; krb5_ccache ccache = NULL; krb5_data config; int err; err = krb5_init_context(&context); if (err) { ret_min = err; ret_maj = GSS_S_FAILURE; goto done; } /* Create a memory ccache we can iterate with libkrb5 functions */ gss_key_value_element_desc ccelement = { "ccache", NULL }; gss_key_value_set_desc cred_store = { 1, &ccelement }; err = asprintf(&memcache, "MEMORY:cred_allowed_%p", &memcache); if (err == -1) { memcache = NULL; ret_min = ENOMEM; ret_maj = GSS_S_FAILURE; goto done; } cred_store.elements[0].value = memcache; ret_maj = gss_store_cred_into(&ret_min, cred, GSS_C_INITIATE, discard_const(gss_mech_krb5), 1, 0, &cred_store, NULL, NULL); if (ret_maj != GSS_S_COMPLETE) { goto done; } err = krb5_cc_resolve(context, memcache, &ccache); if (err) { ret_min = err; ret_maj = GSS_S_FAILURE; goto done; } err = krb5_cc_get_config(context, ccache, NULL, "proxy_impersonator", &config); if (err == 0) { *impersonator = strndup(config.data, config.length); if (!*impersonator) { ret_min = ENOMEM; ret_maj = GSS_S_FAILURE; } else { ret_min = 0; ret_maj = GSS_S_COMPLETE; } krb5_free_data_contents(context, &config); } else { ret_min = err; ret_maj = GSS_S_FAILURE; } done: if (context) { if (ccache) { krb5_cc_destroy(context, ccache); } krb5_free_context(context); } free(memcache); *min = ret_min; return ret_maj; } #if !HAVE_DECL_GSS_KRB5_GET_CRED_IMPERSONATOR gss_OID_desc impersonator_oid = { 11, discard_const("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0e") }; const gss_OID GSS_KRB5_GET_CRED_IMPERSONATOR = &impersonator_oid; #endif static uint32_t get_impersonator_name(uint32_t *min, gss_cred_id_t cred, char **impersonator) { gss_buffer_set_t bufset = GSS_C_NO_BUFFER_SET; uint32_t ret_maj = 0; uint32_t ret_min = 0; uint32_t discard; *impersonator = NULL; ret_maj = gss_inquire_cred_by_oid(&ret_min, cred, GSS_KRB5_GET_CRED_IMPERSONATOR, &bufset); if (ret_maj == GSS_S_COMPLETE) { if (bufset->count == 0) { ret_min = ENOENT; ret_maj = GSS_S_COMPLETE; goto done; } *impersonator = strndup(bufset->elements[0].value, bufset->elements[0].length); if (!*impersonator) { ret_min = ENOMEM; ret_maj = GSS_S_FAILURE; } } else if (ret_maj == GSS_S_UNAVAILABLE) { /* Not supported by krb5 library yet, fallback to raw krb5 calls */ /* TODO: Remove once we set a minimum required dependency on a * release that supports this call */ ret_maj = get_impersonator_fallback(&ret_min, cred, impersonator); if (ret_maj == GSS_S_FAILURE) { if (ret_min == (uint32_t)KRB5_CC_NOTFOUND) { ret_min = ENOENT; ret_maj = GSS_S_COMPLETE; } } } done: (void)gss_release_buffer_set(&discard, &bufset); *min = ret_min; return ret_maj; } static uint32_t check_impersonator_name(uint32_t *min, gss_name_t target_name, const char *impersonator) { gss_name_t canon_name = NULL; gss_buffer_desc buf; uint32_t ret_maj = 0; uint32_t ret_min = 0; uint32_t discard; bool match; ret_maj = gss_canonicalize_name(&discard, target_name, &gp_mech_krb5, &canon_name); if (ret_maj != GSS_S_COMPLETE) { *min = ret_min; return ret_maj; } ret_maj = gss_display_name(&discard, canon_name, &buf, NULL); gss_release_name(&discard, &canon_name); if (ret_maj != GSS_S_COMPLETE) { *min = ret_min; return ret_maj; } match = (strncmp(impersonator, buf.value, buf.length) == 0) && (strlen(impersonator) == buf.length); gss_release_buffer(&discard, &buf); *min = 0; if (match) { return GSS_S_COMPLETE; } else { return GSS_S_UNAUTHORIZED; } } uint32_t gp_cred_allowed(uint32_t *min, struct gp_call_ctx *gpcall, gss_cred_id_t cred, gss_name_t target_name) { char *impersonator = NULL; uint32_t ret_maj = 0; uint32_t ret_min = 0; if (cred == GSS_C_NO_CREDENTIAL) { return GSS_S_CRED_UNAVAIL; } if (gpcall->service->trusted || gpcall->service->impersonate || gpcall->service->allow_const_deleg) { GPDEBUGN(2, "Credentials allowed by configuration\n"); *min = 0; return GSS_S_COMPLETE; } ret_maj = get_impersonator_name(&ret_min, cred, &impersonator); if (ret_maj) goto done; /* if we find an impersonator entry we bail as that is not authorized, * *unless* the target is the impersonator itself! If the operation * were authorized then gpcall->service->allow_const_deleg would have * caused the ealier check to return GSS_S_COMPLETE already */ if (impersonator != NULL) { ret_maj = check_impersonator_name(&ret_min, target_name, impersonator); } done: switch (ret_maj) { case GSS_S_UNAUTHORIZED: GPDEBUGN(2, "Unauthorized impersonator credentials detected\n"); break; case GSS_S_COMPLETE: if (impersonator) { GPDEBUGN(2, "Credentials allowed for 'self'\n"); } else { GPDEBUGN(2, "No impersonator credentials detected\n"); } break; default: GPDEBUG("Failure while checking credentials\n"); break; } free(impersonator); *min = ret_min; return ret_maj; } uint32_t gp_count_tickets(uint32_t *min, gss_cred_id_t cred, uint32_t *ccsum) { uint32_t ret_maj = 0; uint32_t ret_min = 0; char *memcache = NULL; krb5_context context = NULL; krb5_ccache ccache = NULL; krb5_cc_cursor cursor = NULL; krb5_creds creds; int err; err = krb5_init_context(&context); if (err != 0) { ret_min = err; ret_maj = GSS_S_FAILURE; goto done; } /* Create a memory ccache we can iterate with libkrb5 functions */ gss_key_value_element_desc ccelement = { "ccache", NULL }; gss_key_value_set_desc cred_store = { 1, &ccelement }; err = asprintf(&memcache, "MEMORY:cred_allowed_%p", &memcache); if (err == -1) { memcache = NULL; ret_min = ENOMEM; ret_maj = GSS_S_FAILURE; goto done; } cred_store.elements[0].value = memcache; ret_maj = gss_store_cred_into(&ret_min, cred, GSS_C_INITIATE, discard_const(gss_mech_krb5), 1, 0, &cred_store, NULL, NULL); if (ret_maj != GSS_S_COMPLETE) { goto done; } err = krb5_cc_resolve(context, memcache, &ccache); if (err != 0) { ret_min = err; ret_maj = GSS_S_FAILURE; goto done; } err = krb5_cc_start_seq_get(context, ccache, &cursor); if (err != 0) { ret_min = err; ret_maj = GSS_S_FAILURE; goto done; } do { err = krb5_cc_next_cred(context, ccache, &cursor, &creds); if (err != 0 && err != KRB5_CC_END) { ret_min = err; ret_maj = GSS_S_FAILURE; goto done; } krb5_free_cred_contents(context, &creds); /* TODO: Should we do a real checksum over all creds->ticket data and * flags in future ? */ (*ccsum)++; } while (err == 0); err = krb5_cc_end_seq_get(context, ccache, &cursor); if (err != 0) { ret_min = err; ret_maj = GSS_S_FAILURE; goto done; } done: if (context) { /* NOTE: destroy only if we created a MEMORY ccache */ if (ccache) { if (memcache) { krb5_cc_destroy(context, ccache); } else { krb5_cc_close(context, ccache); } } krb5_free_context(context); } free(memcache); *min = ret_min; return ret_maj; } /* Check if cred refresh is being requested by the client. * if so, take a snapshot of the cred so that later we can check if anything * was added */ uint32_t gp_check_sync_creds(struct gp_cred_check_handle *h, gss_cred_id_t cred) { uint32_t ret_maj = 0; uint32_t ret_min = 0; struct gp_service *svc = h->ctx->service; struct gssx_option *opt = NULL; uint32_t ccsum = 0; if (!svc->allow_cc_sync) return 0; gp_options_find(opt, h->options, CRED_SYNC_OPTION, sizeof(CRED_SYNC_OPTION)); if (!opt) { return 0; } if (!gpopt_string_match(&opt->value, CRED_SYNC_DEFAULT, sizeof(CRED_SYNC_DEFAULT))) { return 0; } for (size_t i = 0; i < svc->krb5.store.count; i++) { if (strcmp(svc->krb5.store.elements[i].key, "ccache") == 0) { /* Saving in local ccache no need to sync up to client */ return 0; } } ret_maj = gp_count_tickets(&ret_min, cred, &ccsum); if (ret_maj) { return 0; } return ccsum; } uint32_t gp_export_sync_creds(uint32_t *min, struct gp_call_ctx *gpcall, gss_cred_id_t *cred, gssx_option **options_val, u_int *options_len) { uint32_t ret_maj = 0; uint32_t ret_min = 0; gssx_cred creds = { 0 }; char value[GPKRB_MAX_CRED_SIZE]; size_t len; XDR xdrctx; bool xdrok; ret_maj = gp_export_gssx_cred(&ret_min, gpcall, cred, &creds); if (ret_maj) { goto done; } xdrmem_create(&xdrctx, value, GPKRB_MAX_CRED_SIZE, XDR_ENCODE); xdrok = xdr_gssx_cred(&xdrctx, &creds); if (!xdrok) { ret_min = ENOSPC; ret_maj = GSS_S_FAILURE; goto done; } len = xdr_getpos(&xdrctx); ret_min = gp_add_option(options_val, options_len, CRED_SYNC_PAYLOAD, sizeof(CRED_SYNC_PAYLOAD), value, len); if (ret_min) { ret_maj = GSS_S_FAILURE; goto done; } ret_min = 0; ret_maj = GSS_S_COMPLETE; done: xdr_free((xdrproc_t)xdr_gssx_cred, (char *)&creds); *min = ret_min; return ret_maj; } gssproxy-v0.8.2/src/gp_creds.h0000644000174300017420000000062313456641744015745 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GP_CREDS_H_ #define _GP_CREDS_H_ #include "config.h" #include #include #include #include #define CRED_TYPE_NONE 0x00 #define CRED_TYPE_UNIX 0x01 #define CRED_TYPE_SELINUX 0x02 struct gp_creds { int type; struct ucred ucred; }; #endif /* _GP_CREDS_H_ */ gssproxy-v0.8.2/src/gp_debug.c0000644000174300017420000000355413456641744015734 0ustar gitgit00000000000000/* Copyright (C) 2011,2018 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include #include "gp_debug.h" #include "gp_log.h" /* global debug switch */ int gp_debug = 0; void gp_debug_toggle(int level) { if (level <= gp_debug) return; if (level >= 3 && !getenv("KRB5_TRACE")) setenv("KRB5_TRACE", "/dev/stderr", 1); gp_debug = level; GPDEBUG("Debug Enabled (level: %d)\n", level); } void gp_log_failure(gss_OID mech, uint32_t maj, uint32_t min) { char buf[MAX_LOG_LINE]; gp_fmt_status(mech, maj, min, buf, MAX_LOG_LINE); fprintf(stderr, "Failed with: %s\n", buf); } const char *gp_debug_timestamp(void) { static __thread char buffer[24]; static __thread time_t timestamp = 0; struct tm tm_info; time_t now; time(&now); if (now == timestamp) return buffer; gmtime_r(&now, &tm_info); strftime(buffer, 24, "[%Y/%m/%d %H:%M:%S]: ", &tm_info); timestamp = now; return buffer; } /* thread local connection/client id */ static __thread int cid; void gp_debug_set_conn_id(int id) { cid = id; } static const char*gp_debug_conn_id(void) { static __thread char buffer[18]; static __thread int last_cid = 0; if (cid == 0) { buffer[0] = '\0'; return buffer; } if (last_cid == cid) return buffer; (void)snprintf(buffer, 17, "[CID %d]", cid); buffer[17] = '\0'; last_cid = cid; return buffer; } void gp_debug_printf(const char *format, ...) { va_list varargs; va_start(varargs, format); vfprintf(stderr, format, varargs); va_end(varargs); } void gp_debug_time_printf(const char *format, ...) { va_list varargs; fprintf(stderr, "%s%s", gp_debug_conn_id(), gp_debug_timestamp()); va_start(varargs, format); vfprintf(stderr, format, varargs); va_end(varargs); } gssproxy-v0.8.2/src/gp_debug.h0000644000174300017420000000133013456641744015727 0ustar gitgit00000000000000/* Copyright (C) 2011,2018 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GP_DEBUG_H_ #define _GP_DEBUG_H_ #include #include #include #include extern int gp_debug; void gp_debug_toggle(int); void gp_debug_printf(const char *format, ...); void gp_debug_time_printf(const char *format, ...); void gp_debug_set_conn_id(int id); #define GPDEBUG(...) do { \ if (gp_debug) { \ gp_debug_time_printf(__VA_ARGS__); \ } \ } while(0) #define GPDEBUGN(lvl, ...) do { \ if (lvl <= gp_debug) { \ gp_debug_time_printf(__VA_ARGS__); \ } \ } while(0) void gp_log_failure(gss_OID mech, uint32_t maj, uint32_t min); #endif /* _GP_DEBUG_H_ */ gssproxy-v0.8.2/src/gp_export.c0000644000174300017420000007656713456641744016205 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include #include #include #include "gp_conv.h" #include "gp_export.h" #include "gp_debug.h" #include "gp_proxy.h" #include #include #include #include #define GP_CREDS_HANDLE_KEY_ENCTYPE ENCTYPE_AES256_CTS_HMAC_SHA1_96 struct gp_creds_handle { krb5_context context; krb5_keyblock *key; }; void gp_free_creds_handle(struct gp_creds_handle **in) { struct gp_creds_handle *handle = *in; if (!handle) { return; } if (handle->context) { krb5_free_keyblock(handle->context, handle->key); krb5_free_context(handle->context); } free(handle); *in = NULL; return; } uint32_t gp_init_creds_with_keytab(uint32_t *min, const char *svc_name, const char *keytab, struct gp_creds_handle *handle) { char ktname[MAX_KEYTAB_NAME_LEN + 1] = {0}; krb5_keytab ktid = NULL; krb5_kt_cursor cursor; krb5_keytab_entry entry; krb5_enctype *permitted = NULL; uint32_t ret_maj = 0; uint32_t ret_min = 0; int ret; if (keytab) { strncpy(ktname, keytab, MAX_KEYTAB_NAME_LEN); ret = krb5_kt_resolve(handle->context, keytab, &ktid); } /* if the keytab is not specified or fails to resolve try default */ if (!keytab || ret != 0) { ret = krb5_kt_default_name(handle->context, ktname, MAX_KEYTAB_NAME_LEN); if (ret) { strncpy(ktname, "[default]", MAX_KEYTAB_NAME_LEN); } ret = krb5_kt_default(handle->context, &ktid); } if (ret == 0) { ret = krb5_kt_have_content(handle->context, ktid); } if (ret) { GPDEBUG("Keytab %s has no content (%d)\n", ktname, ret); ret_min = ret; ret_maj = GSS_S_CRED_UNAVAIL; goto done; } ret = krb5_get_permitted_enctypes(handle->context, &permitted); if (ret) { GPDEBUG("Failed to source permitted enctypes (%d)\n", ret); ret_min = ret; ret_maj = GSS_S_FAILURE; goto done; } ret = krb5_kt_start_seq_get(handle->context, ktid, &cursor); if (ret) { GPDEBUG("krb5_kt_start_seq_get() failed (%d)\n", ret); ret_min = ret; ret_maj = GSS_S_FAILURE; goto done; } do { ret = krb5_kt_next_entry(handle->context, ktid, &entry, &cursor); if (ret == 0) { for (unsigned i = 0; permitted[i] != 0; i++) { if (permitted[i] == entry.key.enctype) { /* should we derive a key instead ? */ ret = krb5_copy_keyblock(handle->context, &entry.key, &handle->key); if (ret == 0) { GPDEBUG("Service: %s, Keytab: %s, Enctype: %d\n", svc_name, ktname, entry.key.enctype); ret = KRB5_KT_END; } else { GPDEBUG("krb5_copy_keyblock failed (%d)\n", ret); } break; } } (void)krb5_free_keytab_entry_contents(handle->context, &entry); } } while (ret == 0); (void)krb5_kt_end_seq_get(handle->context, ktid, &cursor); if ((ret == KRB5_KT_END) && (handle->key == NULL)) { ret = KRB5_WRONG_ETYPE; ret_maj = GSS_S_CRED_UNAVAIL; goto done; } if (ret != KRB5_KT_END) { ret_min = ret; ret_maj = GSS_S_CRED_UNAVAIL; goto done; } ret_min = 0; ret_maj = GSS_S_COMPLETE; done: krb5_free_enctypes(handle->context, permitted); if (ktid) { (void)krb5_kt_close(handle->context, ktid); } *min = ret_min; return ret_maj; } uint32_t gp_init_creds_handle(uint32_t *min, const char *svc_name, const char *keytab, struct gp_creds_handle **out) { struct gp_creds_handle *handle; uint32_t ret_maj = 0; uint32_t ret_min = 0; int ret; handle = calloc(1, sizeof(struct gp_creds_handle)); if (!handle) { ret_min = ENOMEM; ret_maj = GSS_S_FAILURE; goto done; } /* initialize key */ ret = krb5_init_context(&handle->context); if (ret) { ret_min = ret; ret_maj = GSS_S_FAILURE; goto done; } /* Try to use a keytab, and fall back to a random runtime secret if all * else fails */ ret_maj = gp_init_creds_with_keytab(&ret_min, svc_name, keytab, handle); if (ret_maj != GSS_S_COMPLETE) { /* fallback */ ret = krb5_init_keyblock(handle->context, GP_CREDS_HANDLE_KEY_ENCTYPE, 0, &handle->key); if (ret == 0) { ret = krb5_c_make_random_key(handle->context, handle->key->enctype, handle->key); GPDEBUG("Service: %s, Enckey: [ephemeral], Enctype: %d\n", svc_name, handle->key->enctype); } if (ret) { ret_min = ret; ret_maj = GSS_S_FAILURE; goto done; } } ret_maj = GSS_S_COMPLETE; ret_min = 0; done: *min = ret_min; if (ret_maj) { gp_free_creds_handle(&handle); } *out = handle; return ret_maj; } #define ENC_MIN_PAD_LEN 8 /* We need to pad our payloads because krb5_c_decrypt() may pad the * contents for some enctypes, and gss_import_cred() doesn't like * having extra bytes on tokens. * Explicit padding and depadding is used in order to maintain backwards * compatibility over upgrades (and downgrades), it would have been * better if we simply had a better formatting of the returned blob * so we could simply change a "blob version" number */ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key, size_t len, void *buf, octet_string *out) { int ret; krb5_data data_in; krb5_enc_data enc_handle; size_t cipherlen; size_t padcheck; uint8_t pad = 0; char *padded = NULL; if (len > (uint32_t)(-1)) { /* Needs to fit in 4 bytes of payload, so... */ ret = ENOMEM; goto done; } ret = krb5_c_encrypt_length(context, key->enctype, len, &cipherlen); if (ret) { goto done; } /* try again with len + 1 to see if padding is required */ ret = krb5_c_encrypt_length(context, key->enctype, len + 1, &padcheck); if (ret) { goto done; } if (padcheck == cipherlen) { int i; /* padding required */ pad = ENC_MIN_PAD_LEN; /* always add enough padding that it makes it extremely unlikley * legitimate plaintext will be incorrectly depadded in the * decrypt function */ ret = krb5_c_encrypt_length(context, key->enctype, len + pad, &cipherlen); if (ret) { goto done; } /* we support only block sizes up to 16 bytes as this is the largest * supported block size in krb ciphers for now */ for (i = 0; i < 15; i++) { /* find the point at which padcheck increases, that's when we * cross a blocksize boundary internally and we can calculate * the padding that will be used */ ret = krb5_c_encrypt_length(context, key->enctype, len + pad + i + 1, &padcheck); if (ret) { goto done; } if (padcheck > cipherlen) { pad += i; break; } } if (i > 15) { ret = EINVAL; goto done; } } if (pad != 0) { padded = malloc(len + pad); if (!padded) { ret = errno; goto done; } memcpy(padded, buf, len); memset(padded + len, pad, pad); data_in.length = len + pad; data_in.data = padded; } else { data_in.length = len; data_in.data = buf; } enc_handle.ciphertext.length = cipherlen; enc_handle.ciphertext.data = malloc(enc_handle.ciphertext.length); if (!enc_handle.ciphertext.data) { ret = ENOMEM; goto done; } ret = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_APP_DATA_ENCRYPT, NULL, &data_in, &enc_handle); if (ret) { ret = EINVAL; goto done; } ret = gp_conv_octet_string(enc_handle.ciphertext.length, enc_handle.ciphertext.data, out); if (ret) { goto done; } done: free(padded); free(enc_handle.ciphertext.data); return ret; } /* See comment above on gp_encrypt_buffer(). */ static int gp_decrypt_buffer(krb5_context context, krb5_keyblock *key, octet_string *in, size_t *len, char *buf) { int ret; krb5_data data_out; krb5_enc_data enc_handle; uint8_t pad; int i, j; memset(&enc_handle, '\0', sizeof(krb5_enc_data)); enc_handle.enctype = key->enctype; enc_handle.ciphertext.data = in->octet_string_val; enc_handle.ciphertext.length = in->octet_string_len; data_out.length = *len; data_out.data = buf; ret = krb5_c_decrypt(context, key, KRB5_KEYUSAGE_APP_DATA_ENCRYPT, NULL, &enc_handle, &data_out); if (ret) { return ret; } /* And handle the padding. */ i = data_out.length - 1; pad = data_out.data[i]; if (pad >= ENC_MIN_PAD_LEN && pad < i) { j = pad; while (j > 0) { j--; if (pad != data_out.data[i - j]) break; } if (j == 0) { data_out.length -= pad; } } *len = data_out.length; return 0; } uint32_t gp_export_gssx_cred(uint32_t *min, struct gp_call_ctx *gpcall, gss_cred_id_t *in, gssx_cred *out) { uint32_t ret_maj; uint32_t ret_min; gss_name_t name = NULL; uint32_t lifetime; gss_cred_usage_t cred_usage; gss_OID_set mechanisms = NULL; uint32_t initiator_lifetime = 0; uint32_t acceptor_lifetime = 0; struct gssx_cred_element *el; int ret; struct gp_creds_handle *handle = NULL; gss_buffer_desc token = GSS_C_EMPTY_BUFFER; ret_maj = gss_inquire_cred(&ret_min, *in, &name, &lifetime, &cred_usage, &mechanisms); if (ret_maj) { goto done; } ret_maj = gp_conv_name_to_gssx(&ret_min, name, &out->desired_name); if (ret_maj) { goto done; } gss_release_name(&ret_min, &name); name = NULL; out->elements.elements_val = calloc(mechanisms->count, sizeof(gssx_cred_element)); if (!out->elements.elements_val) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } out->elements.elements_len = mechanisms->count; for (unsigned i = 0, j = 0; i < mechanisms->count; i++, j++) { el = &out->elements.elements_val[j]; ret_maj = gss_inquire_cred_by_mech(&ret_min, *in, &mechanisms->elements[i], &name, &initiator_lifetime, &acceptor_lifetime, &cred_usage); if (ret_maj) { gp_log_failure(&mechanisms->elements[i], ret_maj, ret_min); /* skip any offender */ out->elements.elements_len--; j--; continue; } ret_maj = gp_conv_name_to_gssx(&ret_min, name, &el->MN); if (ret_maj) { goto done; } gss_release_name(&ret_min, &name); name = NULL; ret = gp_conv_oid_to_gssx(&mechanisms->elements[i], &el->mech); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } el->cred_usage = gp_conv_cred_usage_to_gssx(cred_usage); el->initiator_time_rec = initiator_lifetime; el->acceptor_time_rec = acceptor_lifetime; } handle = gp_service_get_creds_handle(gpcall->service); if (!handle) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } ret_maj = gss_export_cred(&ret_min, *in, &token); if (ret_maj) { goto done; } ret = gp_encrypt_buffer(handle->context, handle->key, token.length, token.value, &out->cred_handle_reference); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } out->needs_release = false; /* now we have serialized creds in the hands of the client. * we can safey free them here so that we can remain sateless and * not leak memory */ gss_release_cred(&ret_min, in); ret_maj = GSS_S_COMPLETE; ret_min = 0; done: *min = ret_min; gss_release_buffer(&ret_min, &token); gss_release_name(&ret_min, &name); gss_release_oid_set(&ret_min, &mechanisms); return ret_maj; } #define KRB5_SET_ALLOWED_ENCTYPE "krb5_set_allowed_enctype_values" #define KRB5_SET_NO_CI_FLAGS "krb5_set_no_ci_flags" static void gp_set_cred_options(gssx_cred *cred, gss_cred_id_t gss_cred) { struct gssx_cred_element *ce; struct gssx_option *op; uint32_t num_ktypes = 0; krb5_enctype *ktypes; bool no_ci_flags = false; uint32_t maj, min; for (unsigned i = 0; i < cred->elements.elements_len; i++) { ce = &cred->elements.elements_val[i]; for (unsigned j = 0; j < ce->options.options_len; j++) { op = &ce->options.options_val[j]; if ((op->option.octet_string_len == sizeof(KRB5_SET_ALLOWED_ENCTYPE)) && (strncmp(KRB5_SET_ALLOWED_ENCTYPE, op->option.octet_string_val, op->option.octet_string_len) == 0)) { num_ktypes = op->value.octet_string_len / sizeof(krb5_enctype); ktypes = (krb5_enctype *)op->value.octet_string_val; break; } else if ((op->option.octet_string_len == sizeof(KRB5_SET_NO_CI_FLAGS)) && (strncmp(KRB5_SET_NO_CI_FLAGS, op->option.octet_string_val, op->option.octet_string_len) == 0)) { no_ci_flags = true; } } } if (num_ktypes) { maj = gss_krb5_set_allowable_enctypes(&min, gss_cred, num_ktypes, ktypes); if (maj != GSS_S_COMPLETE) { GPDEBUG("Failed to set allowable enctypes\n"); } } if (no_ci_flags) { gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER; maj = gss_set_cred_option(&min, &gss_cred, discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X), &empty_buffer); if (maj != GSS_S_COMPLETE) { GPDEBUG("Failed to set NO CI Flags\n"); } } } uint32_t gp_import_gssx_cred(uint32_t *min, struct gp_call_ctx *gpcall, gssx_cred *cred, gss_cred_id_t *out) { gss_buffer_desc token = GSS_C_EMPTY_BUFFER; struct gp_creds_handle *handle = NULL; uint32_t ret_maj = GSS_S_COMPLETE; uint32_t ret_min = 0; int ret; *out = GSS_C_NO_CREDENTIAL; handle = gp_service_get_creds_handle(gpcall->service); if (!handle) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } token.length = cred->cred_handle_reference.octet_string_len; token.value = malloc(token.length); if (!token.value) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } ret = gp_decrypt_buffer(handle->context, handle->key, &cred->cred_handle_reference, &token.length, token.value); if (ret) { /* Allow for re-issuance of the keytab. */ GPDEBUG("Stored ccache failed to decrypt; treating as empty\n"); goto done; } ret_maj = gss_import_cred(&ret_min, &token, out); if (ret_maj) { GPDEBUG("gss_import_cred failed when importing gssx cred\n"); goto done; } /* check if there is any client option we need to set on credentials */ gp_set_cred_options(cred, *out); done: *min = ret_min; free(token.value); return ret_maj; } /* Exported Contexts */ #define EXP_CTX_TYPE_OPTION "exported_context_type" #define LINUX_LUCID_V1 "linux_lucid_v1" enum exp_ctx_types { EXP_CTX_PARTIAL = -1, /* cannot be specified by client */ EXP_CTX_DEFAULT = 0, EXP_CTX_LINUX_LUCID_V1 = 1, }; int gp_get_exported_context_type(struct gssx_call_ctx *ctx) { struct gssx_option *val = NULL; gp_options_find(val, ctx->options, EXP_CTX_TYPE_OPTION, sizeof(EXP_CTX_TYPE_OPTION)); if (val) { if (gp_option_value_match(val, LINUX_LUCID_V1, sizeof(LINUX_LUCID_V1))) { return EXP_CTX_LINUX_LUCID_V1; } else { return EXP_CTX_PARTIAL; } } return EXP_CTX_DEFAULT; } int gp_get_continue_needed_type(void) { return EXP_CTX_PARTIAL; } #define KRB5_CTX_FLAG_INITIATOR 0x00000001 #define KRB5_CTX_FLAG_CFX 0x00000002 #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 /* we use what svcgssd calls a "krb5_rfc4121_buffer" * Format: uint32_t flags * int32_t endtime * uint64_t seq_send * uint32_t enctype * u8[] raw key */ static uint32_t gp_format_linux_lucid_v1(uint32_t *min, gss_krb5_lucid_context_v1_t *lucid, gssx_buffer *out) { uint8_t *buffer; uint8_t *p; size_t length; uint32_t flags; uint32_t enctype; uint32_t keysize; void *keydata; uint32_t maj; if (lucid->version != 1 || (lucid->protocol != 0 && lucid->protocol != 1)) { *min = ENOTSUP; return GSS_S_FAILURE; } flags = 0; if (lucid->initiate) { flags |= KRB5_CTX_FLAG_INITIATOR; } if (lucid->protocol == 1) { flags |= KRB5_CTX_FLAG_CFX; } if (lucid->protocol == 1 && lucid->cfx_kd.have_acceptor_subkey == 1) { flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; } if (lucid->protocol == 0) { enctype = lucid->rfc1964_kd.ctx_key.type; keysize = lucid->rfc1964_kd.ctx_key.length; keydata = lucid->rfc1964_kd.ctx_key.data; } else { if (lucid->cfx_kd.have_acceptor_subkey == 1) { enctype = lucid->cfx_kd.acceptor_subkey.type; keysize = lucid->cfx_kd.acceptor_subkey.length; keydata = lucid->cfx_kd.acceptor_subkey.data; } else { enctype = lucid->cfx_kd.ctx_key.type; keysize = lucid->cfx_kd.ctx_key.length; keydata = lucid->cfx_kd.ctx_key.data; } } length = sizeof(flags) + sizeof(lucid->endtime) + sizeof(lucid->send_seq) + sizeof(enctype) + keysize; buffer = calloc(1, length); if (!buffer) { *min = ENOMEM; maj = GSS_S_FAILURE; goto done; } p = buffer; memcpy(p, &flags, sizeof(flags)); p += sizeof(flags); memcpy(p, &lucid->endtime, sizeof(lucid->endtime)); p += sizeof(lucid->endtime); memcpy(p, &lucid->send_seq, sizeof(lucid->send_seq)); p += sizeof(lucid->send_seq); memcpy(p, &enctype, sizeof(enctype)); p += sizeof(enctype); memcpy(p, keydata, keysize); out->octet_string_val = (void *)buffer; out->octet_string_len = length; maj = GSS_S_COMPLETE; *min = 0; done: if (maj) { free(buffer); } return maj; } uint32_t gp_export_ctx_id_to_gssx(uint32_t *min, int type, gss_OID mech, gss_ctx_id_t *in, gssx_ctx *out) { uint32_t ret_maj; uint32_t ret_min; gss_name_t src_name = GSS_C_NO_NAME; gss_name_t targ_name = GSS_C_NO_NAME; gss_buffer_desc export_buffer = GSS_C_EMPTY_BUFFER; gss_krb5_lucid_context_v1_t *lucid = NULL; uint32_t lifetime_rec; gss_OID mech_type; uint32_t ctx_flags; int is_locally_initiated; int is_open; int ret; /* we do not need the client to release anything until we handle state */ out->needs_release = false; ret_maj = gss_inquire_context(&ret_min, *in, &src_name, &targ_name, &lifetime_rec, &mech_type, &ctx_flags, &is_locally_initiated, &is_open); if (ret_maj) { if (type == EXP_CTX_PARTIAL) { /* This may happen on partially established context, * so just go on and put in what we can */ goto export; } goto done; } ret = gp_conv_oid_to_gssx(mech_type, &out->mech); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } if (src_name != GSS_C_NO_NAME) { ret_maj = gp_conv_name_to_gssx(&ret_min, src_name, &out->src_name); if (ret_maj) { goto done; } } if (targ_name != GSS_C_NO_NAME) { ret_maj = gp_conv_name_to_gssx(&ret_min, targ_name, &out->targ_name); if (ret_maj) { goto done; } } out->lifetime = lifetime_rec; out->ctx_flags = ctx_flags; if (is_locally_initiated) { out->locally_initiated = true; } if (is_open) { out->open = true; } export: /* note: once converted the original context token is not usable anymore, * so this must be the last call to use it */ switch (type) { case EXP_CTX_PARTIAL: /* this happens only when a init_sec_context call returns a partially * initialized context so we return only what we have, not much */ xdr_free((xdrproc_t)xdr_gssx_OID, (char *)&out->mech); ret = gp_conv_oid_to_gssx(mech, &out->mech); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } out->locally_initiated = true; out->open = false; /* out->state; */ /* fall through */ case EXP_CTX_DEFAULT: ret_maj = gss_export_sec_context(&ret_min, in, &export_buffer); if (ret_maj) { goto done; } ret = gp_conv_buffer_to_gssx(&export_buffer, &out->exported_context_token); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } break; case EXP_CTX_LINUX_LUCID_V1: ret_maj = gss_krb5_export_lucid_sec_context(&ret_min, in, 1, (void **)&lucid); if (ret_maj) { goto done; } ret_maj = gp_format_linux_lucid_v1(&ret_min, lucid, &out->exported_context_token); if (ret_maj) { goto done; } /* suppress names exported_composite_name, the kernel doesn't want * this information */ xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out->src_name.exported_composite_name); memset(&out->src_name.exported_composite_name, 0, sizeof(out->src_name.exported_composite_name)); xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out->targ_name.exported_composite_name); memset(&out->targ_name.exported_composite_name, 0, sizeof(out->targ_name.exported_composite_name)); break; default: ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } /* Leave this empty, used only on the way in for init_sec_context */ /* out->gssx_option */ done: *min = ret_min; gss_release_name(&ret_min, &src_name); gss_release_name(&ret_min, &targ_name); gss_release_buffer(&ret_min, &export_buffer); if (lucid) { gss_krb5_free_lucid_sec_context(&ret_min, lucid); } if (ret_maj) { xdr_free((xdrproc_t)xdr_gssx_OID, (char *)&out->mech); xdr_free((xdrproc_t)xdr_gssx_name, (char *)&out->src_name); xdr_free((xdrproc_t)xdr_gssx_name, (char *)&out->targ_name); } return ret_maj; } uint32_t gp_import_gssx_to_ctx_id(uint32_t *min, int type, gssx_ctx *in, gss_ctx_id_t *out) { gss_buffer_desc export_buffer = GSS_C_EMPTY_BUFFER; if (type != EXP_CTX_DEFAULT) { *min = EINVAL; return GSS_S_FAILURE; } gp_conv_gssx_to_buffer(&in->exported_context_token, &export_buffer); return gss_import_sec_context(min, &export_buffer, out); } /* Exported Creds */ #define EXP_CREDS_TYPE_OPTION "exported_creds_type" #define LINUX_CREDS_V1 "linux_creds_v1" enum exp_creds_types { EXP_CREDS_NO_CREDS = 0, EXP_CREDS_LINUX_V1 = 1, }; int gp_get_export_creds_type(struct gssx_call_ctx *ctx) { struct gssx_option *val = NULL; gp_options_find(val, ctx->options, EXP_CREDS_TYPE_OPTION, sizeof(EXP_CREDS_TYPE_OPTION)); if (val) { if (gp_option_value_match(val, LINUX_CREDS_V1, sizeof(LINUX_CREDS_V1))) { return EXP_CREDS_LINUX_V1; } return -1; } return EXP_CREDS_NO_CREDS; } #define CREDS_BUF_MAX (NGROUPS_MAX * sizeof(int32_t)) #define CREDS_HDR (3 * sizeof(uint32_t)) /* uid, gid, count */ static uint32_t gp_export_creds_enoent(uint32_t *min, gss_buffer_t buf) { uint32_t *p; p = malloc(CREDS_HDR); if (!p) { *min = ENOMEM; return GSS_S_FAILURE; } p[0] = -1; /* uid */ p[1] = -1; /* gid */ p[2] = 0; /* num groups */ buf->value = p; buf->length = CREDS_HDR; *min = 0; return GSS_S_COMPLETE; } static uint32_t gp_export_creds_linux(uint32_t *min, gss_name_t name, gss_const_OID mech, gss_buffer_t buf) { gss_buffer_desc localname = {}; uint32_t ret_maj; uint32_t ret_min; struct passwd pwd, *res; char *pwbuf = NULL; char *grbuf = NULL; uint32_t *p; size_t len; int count, num; int ret; /* We use gss_localname() to map the name. Then just use nsswitch to * look up the user. * * (TODO: If gss_localname() fails we may wanto agree with SSSD on a name * format to match principal names, es: gss:foo@REALM.COM, or just * foo@REALM.COM) until sssd can provide a libkrb5 interface to augment * gss_localname() resolution for trusted realms */ ret_maj = gss_localname(&ret_min, name, mech, &localname); if (ret_maj) { if (ret_min == ENOENT) { return gp_export_creds_enoent(min, buf); } *min = ret_min; return ret_maj; } len = 1024; pwbuf = malloc(len); if (!pwbuf) { ret_min = ENOMEM; ret_maj = GSS_S_FAILURE; goto done; } ret = 0; do { if (ret == ERANGE) { if (len == CREDS_BUF_MAX) { ret_min = ENOSPC; ret_maj = GSS_S_FAILURE; goto done; } len *= 2; if (len > CREDS_BUF_MAX) { len = CREDS_BUF_MAX; } p = realloc(pwbuf, len); if (!p) { ret_min = ENOMEM; ret_maj = GSS_S_FAILURE; goto done; } pwbuf = (char *)p; } ret = getpwnam_r((char *)localname.value, &pwd, pwbuf, len, &res); } while (ret == EINTR || ret == ERANGE); switch (ret) { case 0: if (res != NULL) { break; } /* ret == NULL is equivalent to ENOENT */ /* fall through */ case ENOENT: case ESRCH: free(pwbuf); gss_release_buffer(&ret_min, &localname); return gp_export_creds_enoent(min, buf); default: ret_min = ret; ret_maj = GSS_S_FAILURE; goto done; } /* start with a reasonably sized buffer */ count = 256; num = 0; do { if (count >= NGROUPS_MAX) { ret_min = ENOSPC; ret_maj = GSS_S_FAILURE; goto done; } count *= 2; if (count < num) { count = num; } if (count > NGROUPS_MAX) { count = NGROUPS_MAX; } len = count * sizeof(int32_t); p = realloc(grbuf, len + CREDS_HDR); if (!p) { ret_min = ENOMEM; ret_maj = GSS_S_FAILURE; goto done; } grbuf = (char *)p; num = count; ret = getgrouplist(pwd.pw_name, pwd.pw_gid, (gid_t *)&p[3], &num); } while (ret == -1); /* we got the buffer, now fill in [uid, gid, num] and we are done */ p[0] = pwd.pw_uid; p[1] = pwd.pw_gid; p[2] = num; buf->value = p; buf->length = (num + 3) * sizeof(int32_t); ret_min = 0; ret_maj = GSS_S_COMPLETE; done: if (ret_maj) { free(grbuf); } free(pwbuf); *min = ret_min; gss_release_buffer(&ret_min, &localname); return ret_maj; } uint32_t gp_export_creds_to_gssx_options(uint32_t *min, int type, gss_name_t src_name, gss_const_OID mech_type, unsigned int *opt_num, gssx_option **opt_array) { gss_buffer_desc export_buffer = GSS_C_EMPTY_BUFFER; unsigned int num; gssx_option *opta; uint32_t ret_min; uint32_t ret_maj; switch (type) { case EXP_CREDS_NO_CREDS: *min = 0; return GSS_S_COMPLETE; case EXP_CREDS_LINUX_V1: ret_maj = gp_export_creds_linux(&ret_min, src_name, mech_type, &export_buffer); if (ret_maj) { if (ret_min == ENOENT) { /* if not user, return w/o adding anything to the array */ ret_min = 0; ret_maj = GSS_S_COMPLETE; } *min = ret_min; return ret_maj; } break; default: *min = EINVAL; return GSS_S_FAILURE; } num = *opt_num; opta = realloc(*opt_array, sizeof(gssx_option) * (num + 1)); if (!opta) { ret_min = ENOMEM; ret_maj = GSS_S_FAILURE; goto done; } *opt_array = opta; opta[num].option.octet_string_val = strdup(LINUX_CREDS_V1); if (!opta[num].option.octet_string_val) { ret_min = ENOMEM; ret_maj = GSS_S_FAILURE; goto done; } opta[num].option.octet_string_len = sizeof(LINUX_CREDS_V1); opta[num].value.octet_string_val = export_buffer.value; opta[num].value.octet_string_len = export_buffer.length; num++; *opt_num = num; ret_min = 0; ret_maj = GSS_S_COMPLETE; done: *min = ret_min; if (ret_maj) { gss_release_buffer(&ret_min, &export_buffer); } return ret_maj; } gssproxy-v0.8.2/src/gp_export.h0000644000174300017420000000235513456641744016172 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GSS_EXPORT_H_ #define _GSS_EXPORT_H_ #include #include "rpcgen/gss_proxy.h" struct gp_call_ctx; uint32_t gp_export_gssx_cred(uint32_t *min, struct gp_call_ctx *gpcall, gss_cred_id_t *in, gssx_cred *out); uint32_t gp_import_gssx_cred(uint32_t *min, struct gp_call_ctx *gpcall, gssx_cred *cred, gss_cred_id_t *out); int gp_get_exported_context_type(struct gssx_call_ctx *ctx); int gp_get_continue_needed_type(void); uint32_t gp_export_ctx_id_to_gssx(uint32_t *min, int type, gss_OID mech, gss_ctx_id_t *in, gssx_ctx *out); uint32_t gp_import_gssx_to_ctx_id(uint32_t *min, int type, gssx_ctx *in, gss_ctx_id_t *out); int gp_get_export_creds_type(struct gssx_call_ctx *ctx); uint32_t gp_export_creds_to_gssx_options(uint32_t *min, int type, gss_name_t src_name, gss_const_OID mech_type, unsigned int *opt_num, gssx_option **opt_array); #endif /* _GSS_EXPORT_H_ */ gssproxy-v0.8.2/src/gp_init.c0000644000174300017420000002327713456641744015615 0ustar gitgit00000000000000/* Copyright (C) 2011,2015 the GSS-PROXY contributors, see COPYING for license */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CAP #include #include #include #endif #include "gp_proxy.h" void init_server(bool daemonize, int *wait_fd) { pid_t pid, sid; int ret; *wait_fd = -1; if (daemonize) { int pipefd[2]; char buf[1]; /* create parent-child pipe */ ret = pipe(pipefd); if (ret == -1) { exit(EXIT_FAILURE); } pid = fork(); if (pid == -1) { /* fork error ? abort */ exit(EXIT_FAILURE); } if (pid != 0) { /* wait for child to signal it is ready */ close(pipefd[1]); ret = gp_safe_read(pipefd[0], buf, 1); if (ret == 1) { /* child signaled all ok */ exit(EXIT_SUCCESS); } else { /* lost child, something went wrong */ exit(EXIT_FAILURE); } } /* child */ close(pipefd[0]); *wait_fd = pipefd[1]; sid = setsid(); if (sid == -1) { /* setsid error ? abort */ exit(EXIT_FAILURE); } } ret = chdir("/"); if (ret == -1) { exit(EXIT_FAILURE); } /* Set strict umask by default */ umask(0177); /* Set up neutral locale */ setlocale(LC_ALL, ""); /* Set env var to avoid looping to ourselves in GSSAPI */ setenv("GSS_USE_PROXY", "NO", 1); gp_logging_init(); } void init_done(int wait_fd) { char buf = 0; int ret; if (wait_fd != -1) { ret = gp_safe_write(wait_fd, &buf, 1); if (ret != 1) { exit(EXIT_FAILURE); } close(wait_fd); } } void fini_server(void) { closelog(); } static void break_loop(verto_ctx *vctx, verto_ev *ev UNUSED) { GPDEBUG("Exiting after receiving a signal\n"); verto_break(vctx); } verto_ctx *init_event_loop(void) { verto_ctx *vctx; verto_ev *ev; vctx = verto_default(NULL, VERTO_EV_TYPE_IO | VERTO_EV_TYPE_SIGNAL | VERTO_EV_TYPE_TIMEOUT); if (!vctx) { return NULL; } ev = verto_add_signal(vctx, VERTO_EV_FLAG_PERSIST, break_loop, SIGINT); if (!ev) { verto_free(vctx); return NULL; } ev = verto_add_signal(vctx, VERTO_EV_FLAG_PERSIST, break_loop, SIGTERM); if (!ev) { verto_free(vctx); return NULL; } ev = verto_add_signal(vctx, VERTO_EV_FLAG_PERSIST, break_loop, SIGQUIT); if (!ev) { verto_free(vctx); return NULL; } ev = verto_add_signal(vctx, VERTO_EV_FLAG_PERSIST, VERTO_SIG_IGN, SIGPIPE); if (!ev) { verto_free(vctx); return NULL; } /* SIGHUP handler added in main */ return vctx; } void init_proc_nfsd(struct gp_config *cfg) { char buf[] = "1"; bool enabled = false; int fd, ret; static int poked = 0; /* check first if any service enabled kernel support */ for (int i = 0; i < cfg->num_svcs; i++) { if (cfg->svcs[i]->kernel_nfsd) { enabled = true; break; } } if (!enabled || poked) { return; } fd = open(LINUX_PROC_USE_GSS_PROXY_FILE, O_RDWR); if (fd == -1) { ret = errno; GPDEBUG("Kernel doesn't support GSS-Proxy (can't open %s: %d (%s))\n", LINUX_PROC_USE_GSS_PROXY_FILE, ret, gp_strerror(ret)); goto fail; } ret = write(fd, buf, 1); if (ret != 1) { ret = errno; GPDEBUG("Failed to write to %s: %d (%s)\n", LINUX_PROC_USE_GSS_PROXY_FILE, ret, gp_strerror(ret)); close(fd); goto fail; } poked = 1; close(fd); return; fail: GPDEBUG("Problem with kernel communication! NFS server will not work\n"); } void write_pid(void) { pid_t pid; FILE *f; int ret; pid = getpid(); f = fopen(GP_PID_FILE, "w"); if (!f) { ret = errno; GPDEBUG("Failed to open %s: %d (%s)\n", GP_PID_FILE, ret, gp_strerror(ret)); return; } ret = fprintf(f, "%d\n", pid); if (ret <= 0) { GPDEBUG("Failed to write pid to %s\n", GP_PID_FILE); } ret = fclose(f); if (ret != 0) { ret = errno; GPDEBUG("Failed to close %s: %d (%s)\n", GP_PID_FILE, ret, gp_strerror(ret)); } } int drop_privs(struct gp_config *cfg) { char buf[2048]; struct passwd *pw, pws; int ret; #ifdef HAVE_CAP /* Retain capabilities when changing UID to non-zero. We drop the ones we * don't need after the switch. */ ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); if (ret) { ret = errno; GPDEBUG("Failed to set keep capabilities: [%d:%s]\n", ret, gp_strerror(ret)); return ret; } #endif ret = getpwnam_r(cfg->proxy_user, &pws, buf, 2048, &pw); if (ret) { GPDEBUG("Failed to look up proxy user: '%s'! [%d:%s]\n", cfg->proxy_user, ret, gp_strerror(ret)); return ret; } ret = initgroups(pw->pw_name, pw->pw_gid); if (ret) { GPDEBUG("Failed to set access credentials: [%d:%s]\n", ret, gp_strerror(ret)); return ret; } ret = setgid(pw->pw_gid); if (ret == -1) { ret = errno; GPDEBUG("Failed to set group id to %d: [%d:%s]\n", pw->pw_gid, ret, gp_strerror(ret)); return ret; } ret = setuid(pw->pw_uid); if (ret == -1) { ret = errno; GPDEBUG("Failed to set user id to %d: [%d:%s]\n", pw->pw_uid, ret, gp_strerror(ret)); return ret; } #ifdef HAVE_CAP /* Now drop the capabilities we don't need, and turn PR_SET_KEEPCAPS back * off. */ ret = drop_caps(); if (ret) { return ret; } if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0)) { ret = errno; GPDEBUG("Failed to reset keep capabilities: [%d:%s]\n", ret, gp_strerror(ret)); return ret; } #endif return 0; } #ifdef HAVE_CAP /* Remove all capabilties from the process. (In order to manipulate our * capability set, we need to have CAP_SETPCAP.) */ int clear_bound_caps() { cap_t caps = NULL; cap_value_t cap = 0; const cap_value_t setpcap_list[] = { CAP_SETPCAP }; int ret; caps = cap_get_proc(); if (caps == NULL) { ret = errno; GPDEBUG("Failed to get current capabilities: [%d:%s]\n", ret, gp_strerror(ret)); goto done; } if (cap_set_flag(caps, CAP_EFFECTIVE, 1, setpcap_list, CAP_SET) == -1) { ret = errno; GPDEBUG("Failed to set CAP_SETPCAP in effective set: [%d:%s]\n", ret, gp_strerror(ret)); goto done; } if (cap_set_proc(caps) == -1) { ret = errno; GPDEBUG("Failed to apply CAP_SETPCAP: [%d:%s]\n", ret, gp_strerror(ret)); goto done; } /* Now that we have CAP_SETPCAP in the effective set, remove all other * capabilities. */ while (CAP_IS_SUPPORTED(cap)) { if (cap_drop_bound(cap) != 0) { ret = errno; GPDEBUG("Failed to drop bounding set capability: [%d:%s]\n", ret, gp_strerror(ret)); goto done; } cap++; } ret = 0; done: if (caps && cap_free(caps) == -1) { ret = errno; GPDEBUG("Failed to free capability state: [%d:%s]\n", ret, gp_strerror(ret)); } return ret; } /* For program name matching, we need to have CAP_SYS_PTRACE in order to read * /proc/pid/exe. Because we've set PR_SET_KEEPCAPS, every thread inherits * the process set of its parent, so we drop everything but CAP_SYS_PTRACE. */ int drop_caps() { cap_t caps = NULL; int ret; const cap_value_t ptrace_list[] = { CAP_SYS_PTRACE }; /* Completely drop the bounding set. */ ret = clear_bound_caps(); if (ret) { goto done; } ret = CAP_IS_SUPPORTED(CAP_SYS_PTRACE); if (ret == -1) { ret = errno; GPDEBUG("Failed to check if CAP_SYS_PTRACE is supported: [%d:%s]\n", ret, gp_strerror(ret)); goto done; } else if (!ret) { GPDEBUG("Capability CAPS_SYS_PTRACE is not supported\n"); ret = EINVAL; goto done; } /* Now, make an empty capabilitiy set and put CAP_SYS_PTRACE in it. */ caps = cap_init(); if (caps == NULL) { ret = errno; GPDEBUG("Failed to init capabilities: [%d:%s]\n", ret, gp_strerror(ret)); goto done; } if (cap_set_flag(caps, CAP_PERMITTED, 1, ptrace_list, CAP_SET) == -1) { ret = errno; GPDEBUG("Failed to set permitted capabilities: [%d:%s]\n", ret, gp_strerror(ret)); goto done; } if (cap_set_flag(caps, CAP_EFFECTIVE, 1, ptrace_list, CAP_SET) == -1) { ret = errno; GPDEBUG("Failed to set effective capabilities: [%d:%s]\n", ret, gp_strerror(ret)); goto done; } if (cap_set_proc(caps) == -1) { ret = errno; GPDEBUG("Failed to apply capability set: [%d:%s]\n", ret, gp_strerror(ret)); goto done; } ret = 0; done: if (caps && cap_free(caps) == -1) { ret = errno; GPDEBUG("Failed to free capability state: [%d:%s]\n", ret, gp_strerror(ret)); } return ret; } #endif gssproxy-v0.8.2/src/gp_log.c0000644000174300017420000000305313456641744015421 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include "gp_log.h" #include #include void gp_logging_init(void) { openlog("gssproxy", LOG_CONS|LOG_NDELAY|LOG_NOWAIT|LOG_PERROR|LOG_PID, LOG_AUTHPRIV); } static size_t gp_append(char *buf, size_t max, const char *fmt, ...) { va_list ap; size_t res; if (max <= 0) return 0; va_start(ap, fmt); res = vsnprintf(buf, max, fmt, ap); va_end(ap); return res; } void gp_fmt_status(gss_OID mech, uint32_t maj, uint32_t min, char *buf, size_t buf_size) { uint32_t msgctx; uint32_t discard; gss_buffer_desc tmp; size_t used = 0; if (mech != GSS_C_NO_OID) { gss_oid_to_str(&discard, mech, &tmp); used += gp_append(buf + used, buf_size - used, "(OID: %s) ", (char *)tmp.value); gss_release_buffer(&discard, &tmp); } msgctx = 0; gss_display_status(&discard, maj, GSS_C_GSS_CODE, mech, &msgctx, &tmp); used += gp_append(buf + used, buf_size - used, "%s, ", (char *)tmp.value); gss_release_buffer(&discard, &tmp); msgctx = 0; gss_display_status(&discard, min, GSS_C_MECH_CODE, mech, &msgctx, &tmp); used += gp_append(buf + used, buf_size - used, "%s", (char *)tmp.value); gss_release_buffer(&discard, &tmp); } void gp_log_status(gss_OID mech, uint32_t maj, uint32_t min) { char buf[MAX_LOG_LINE]; gp_fmt_status(mech, maj, min, buf, MAX_LOG_LINE); GPERROR("%s\n", buf); } gssproxy-v0.8.2/src/gp_log.h0000644000174300017420000000101113456641744015416 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GP_LOG_H_ #define _GP_LOG_H_ #include #include #define MAX_LOG_LINE 1024 #define GPERROR(...) syslog(LOG_ERR, __VA_ARGS__); #define GPAUDIT(...) syslog(LOG_INFO, __VA_ARGS__); void gp_logging_init(void); void gp_fmt_status(gss_OID mech, uint32_t maj, uint32_t min, char *buf, size_t buf_size); void gp_log_status(gss_OID mech, uint32_t maj, uint32_t min); #endif /* _GP_LOG_H_ */ gssproxy-v0.8.2/src/gp_proxy.h0000644000174300017420000001034313456641744016026 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GP_PROXY_H_ #define _GP_PROXY_H_ #include #include #include #include #include "verto.h" #include "gp_common.h" #include "gp_selinux.h" #define _(STRING) gettext(STRING) #define discard_const(ptr) ((void *)((uintptr_t)(ptr))) #define LINUX_PROC_USE_GSS_PROXY_FILE "/proc/net/rpc/use-gss-proxy" #define GP_CRED_KRB5 0x01 struct gp_creds_handle; struct gp_cred_krb5 { char *principal; gss_key_value_set_desc store; struct gp_creds_handle *creds_handle; }; struct gp_service { char *name; uid_t euid; bool any_uid; bool allow_proto_trans; bool allow_const_deleg; bool allow_cc_sync; bool trusted; bool kernel_nfsd; bool impersonate; char *socket; SELINUX_CTX selinux_ctx; gss_cred_usage_t cred_usage; uint32_t filter_flags; uint32_t enforce_flags; char *program; uint32_t mechs; struct gp_cred_krb5 krb5; verto_ev *ev; }; struct gp_config { char *config_file; /* gssproxy configuration file */ char *config_dir; /* gssproxy configuration directory */ bool daemonize; /* let gssproxy daemonize */ char *socket_name; /* the socket name to use for */ int num_workers; /* number of worker threads */ struct gp_service **svcs; int num_svcs; char *proxy_user; /* user to drop privs to if not NULL */ }; struct gp_workers; struct gssproxy_ctx { struct gp_config *config; struct gp_workers *workers; verto_ctx *vctx; verto_ev *sock_ev; /* default socket event */ }; struct gp_sock_ctx { struct gssproxy_ctx *gpctx; const char *socket; int fd; }; struct gp_conn; struct gp_call_ctx { struct gssproxy_ctx *gpctx; struct gp_service *service; struct gp_conn *connection; void (*destroy_callback)(void *); void *destroy_callback_data; }; /* from gp_config.c */ struct gp_config *read_config(char *config_file, char *config_dir, char *socket_name, int opt_daemonize); struct gp_creds_handle *gp_service_get_creds_handle(struct gp_service *svc); void free_config(struct gp_config **config); void free_cred_store_elements(gss_key_value_set_desc *cs); /* from gp_init.c */ void init_server(bool daemonize, int *wait_fd); void init_done(int wait_fd); void fini_server(void); verto_ctx *init_event_loop(void); void init_proc_nfsd(struct gp_config *cfg); void write_pid(void); int drop_privs(struct gp_config *cfg); #ifdef HAVE_CAP int drop_caps(void); int clear_bound_caps(void); #endif /* from gp_socket.c */ void free_unix_socket(verto_ctx *ctx, verto_ev *ev); struct gp_sock_ctx *init_unix_socket(struct gssproxy_ctx *gpctx, const char *file_name); void accept_sock_conn(verto_ctx *vctx, verto_ev *ev); void gp_conn_free(struct gp_conn *conn); void gp_socket_send_data(verto_ctx *vctx, struct gp_conn *conn, uint8_t *buffer, size_t buflen); struct gp_creds *gp_conn_get_creds(struct gp_conn *conn); uid_t gp_conn_get_uid(struct gp_conn *conn); const char *gp_conn_get_socket(struct gp_conn *conn); int gp_conn_get_cid(struct gp_conn *conn); const char *gp_conn_get_program(struct gp_conn *conn); bool gp_selinux_ctx_equal(SELINUX_CTX ctx1, SELINUX_CTX ctx2); bool gp_conn_check_selinux(struct gp_conn *conn, SELINUX_CTX ctx); /* from gp_workers.c */ int gp_workers_init(struct gssproxy_ctx *gpctx); void gp_workers_free(struct gp_workers *w); int gp_query_new(struct gp_workers *w, struct gp_conn *conn, uint8_t *buffer, size_t buflen); /* from gp_rpc.c */ int gp_rpc_process_call(struct gp_call_ctx *gpcall, uint8_t *inbuf, size_t inlen, uint8_t **outbuf, size_t *outlen); /* from gp_creds.c */ struct gp_service *gp_creds_match_conn(struct gssproxy_ctx *gpctx, struct gp_conn *conn); /* from gp_export.c */ uint32_t gp_init_creds_handle(uint32_t *min, const char *svc_name, const char *keytab, struct gp_creds_handle **out); void gp_free_creds_handle(struct gp_creds_handle **in); #endif /* _GP_PROXY_H_ */ gssproxy-v0.8.2/src/gp_rpc_accept_sec_context.c0000644000174300017420000001356613456641744021353 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gp_rpc_process.h" int gp_accept_sec_context(struct gp_call_ctx *gpcall, union gp_rpc_arg *arg, union gp_rpc_res *res) { struct gssx_arg_accept_sec_context *asca; struct gssx_res_accept_sec_context *ascr; uint32_t ret_maj; uint32_t ret_min; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_cred_id_t ach = GSS_C_NO_CREDENTIAL; gss_buffer_desc ibuf; struct gss_channel_bindings_struct cbs; gss_channel_bindings_t pcbs; gss_name_t src_name = GSS_C_NO_NAME; gss_OID oid = GSS_C_NO_OID; gss_buffer_desc obuf = GSS_C_EMPTY_BUFFER; uint32_t ret_flags; gss_cred_id_t dch = GSS_C_NO_CREDENTIAL; gss_cred_id_t *pdch = NULL; int exp_ctx_type; int exp_creds_type; uint32_t acpt_maj; uint32_t acpt_min; struct gp_cred_check_handle gcch = { .ctx = gpcall, .options.options_len = arg->accept_sec_context.options.options_len, .options.options_val = arg->accept_sec_context.options.options_val, }; uint32_t gccn_before = 0; uint32_t gccn_after = 0; int ret; asca = &arg->accept_sec_context; ascr = &res->accept_sec_context; GPRPCDEBUG(gssx_arg_accept_sec_context, asca); exp_ctx_type = gp_get_exported_context_type(&asca->call_ctx); if (exp_ctx_type == -1) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } exp_creds_type = gp_get_export_creds_type(&asca->call_ctx); if (exp_creds_type == -1) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } if (asca->cred_handle) { ret_maj = gp_import_gssx_cred(&ret_min, gpcall, asca->cred_handle, &ach); if (ret_maj) { goto done; } gccn_before = gp_check_sync_creds(&gcch, ach); } if (ach == GSS_C_NO_CREDENTIAL) { ret_maj = gp_add_krb5_creds(&ret_min, gpcall, ACQ_NORMAL, NULL, NULL, GSS_C_ACCEPT, 0, 0, &ach, NULL, NULL, NULL); if (ret_maj) { goto done; } } gp_conv_gssx_to_buffer(&asca->input_token, &ibuf); if (asca->input_cb) { pcbs = &cbs; gp_conv_gssx_to_cb(asca->input_cb, pcbs); } else { pcbs = GSS_C_NO_CHANNEL_BINDINGS; } if (asca->ret_deleg_cred) { pdch = &dch; } ret_maj = gss_accept_sec_context(&ret_min, &ctx, ach, &ibuf, pcbs, &src_name, &oid, &obuf, &ret_flags, NULL, pdch); if (ret_maj != GSS_S_COMPLETE && ret_maj != GSS_S_CONTINUE_NEEDED) { goto done; } else { acpt_maj = ret_maj; acpt_min = ret_min; } if (acpt_maj == GSS_S_CONTINUE_NEEDED) { exp_ctx_type = gp_get_continue_needed_type(); } ascr->context_handle = calloc(1, sizeof(gssx_ctx)); if (!ascr->context_handle) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } ret_maj = gp_export_ctx_id_to_gssx(&ret_min, exp_ctx_type, oid, &ctx, ascr->context_handle); if (ret_maj) { goto done; } ascr->output_token = calloc(1, sizeof(gssx_buffer)); if (!ascr->output_token) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } ret = gp_conv_buffer_to_gssx(&obuf, ascr->output_token); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } if ((ret_flags & GSS_C_DELEG_FLAG) && asca->ret_deleg_cred && dch) { ascr->delegated_cred_handle = calloc(1, sizeof(gssx_cred)); if (!ascr->delegated_cred_handle) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } ret_maj = gp_export_gssx_cred(&ret_min, gpcall, &dch, ascr->delegated_cred_handle); if (ret_maj) { goto done; } } ret_maj = gp_export_creds_to_gssx_options(&ret_min, exp_creds_type, src_name, oid, &ascr->options.options_len, &ascr->options.options_val); if (ret_maj) { goto done; } gccn_after = gp_check_sync_creds(&gcch, ach); if (gccn_before != gccn_after) { /* export creds back to client for sync up */ ret_maj = gp_export_sync_creds(&ret_min, gpcall, &ach, &ascr->options.options_val, &ascr->options.options_len); if (ret_maj) { /* not fatal, log and continue */ GPDEBUG("Failed to export sync creds (%d: %d)", (int)ret_maj, (int)ret_min); } } ret_maj = GSS_S_COMPLETE; done: if (ret_maj == GSS_S_COMPLETE) { ret_maj = acpt_maj; ret_min = acpt_min; } ret = gp_conv_status_to_gssx(ret_maj, ret_min, oid, &ascr->status); GPRPCDEBUG(gssx_res_accept_sec_context, ascr); gss_release_name(&ret_min, &src_name); gss_release_buffer(&ret_min, &obuf); gss_release_cred(&ret_min, &dch); gss_release_cred(&ret_min, &ach); gss_delete_sec_context(&ret_min, &ctx, GSS_C_NO_BUFFER); return ret; } gssproxy-v0.8.2/src/gp_rpc_acquire_cred.c0000644000174300017420000001211213456641744020126 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gp_rpc_process.h" #include int gp_acquire_cred(struct gp_call_ctx *gpcall, union gp_rpc_arg *arg, union gp_rpc_res *res) { struct gssx_arg_acquire_cred *aca; struct gssx_res_acquire_cred *acr; uint32_t ret_maj; uint32_t ret_min; gss_cred_id_t in_cred = GSS_C_NO_CREDENTIAL; gss_OID_set desired_mechs = GSS_C_NO_OID_SET; gss_OID_set use_mechs = GSS_C_NO_OID_SET; gss_OID desired_mech = GSS_C_NO_OID; gss_cred_usage_t cred_usage; gss_cred_id_t out_cred = GSS_C_NO_CREDENTIAL; gss_cred_id_t *add_out_cred = NULL; int acquire_type = ACQ_NORMAL; int ret; aca = &arg->acquire_cred; acr = &res->acquire_cred; GPRPCDEBUG(gssx_arg_acquire_cred, aca); if (aca->input_cred_handle) { ret_maj = gp_import_gssx_cred(&ret_min, gpcall, aca->input_cred_handle, &in_cred); if (ret_maj) { goto done; } acquire_type = gp_get_acquire_type(aca); if (acquire_type == -1) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } } if (aca->add_cred_to_input_handle) { add_out_cred = &in_cred; } else { add_out_cred = &out_cred; } ret = gp_conv_gssx_to_oid_set(&aca->desired_mechs, &desired_mechs); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } /* if a mech list is specified check if it includes the mechs * allowed by this service configuration */ if (desired_mechs != GSS_C_NO_OID_SET) { ret_maj = gss_create_empty_oid_set(&ret_min, &use_mechs); if (ret_maj) { goto done; } for (unsigned i = 0; i < desired_mechs->count; i++) { desired_mech = &desired_mechs->elements[i]; if (!gp_creds_allowed_mech(gpcall, desired_mech)) { continue; } ret_maj = gss_add_oid_set_member(&ret_min, desired_mech, &use_mechs); if (ret_maj) { goto done; } } if (use_mechs->count == 0) { /* no allowed mech, return nothing */ desired_mech = GSS_C_NO_OID; ret_maj = GSS_S_NO_CRED; ret_min = 0; goto done; } } else { ret_maj = gp_get_supported_mechs(&ret_min, &use_mechs); if (ret_maj) { goto done; } } cred_usage = gp_conv_gssx_to_cred_usage(aca->cred_usage); for (unsigned i = 0; i < use_mechs->count; i++) { desired_mech = &use_mechs->elements[i]; /* this should really be folded into an extended * gss_add_cred in gssapi that can accept a set of URIs * that define keytabs and ccaches and principals */ if (gss_oid_equal(desired_mech, gss_mech_krb5)) { ret_maj = gp_add_krb5_creds(&ret_min, gpcall, acquire_type, in_cred, aca->desired_name, cred_usage, aca->initiator_time_req, aca->acceptor_time_req, add_out_cred, NULL, NULL, NULL); if (ret_maj) { goto done; } } else { /* we support only the krb5 mech for now */ ret_maj = GSS_S_BAD_MECH; goto done; } } if (out_cred == GSS_C_NO_CREDENTIAL) { if (in_cred != GSS_C_NO_CREDENTIAL) { out_cred = in_cred; } else { ret_maj = GSS_S_NO_CRED; ret_min = 0; goto done; } } if (out_cred == in_cred) { acr->output_cred_handle = aca->input_cred_handle; aca->input_cred_handle = NULL; } else { acr->output_cred_handle = calloc(1, sizeof(gssx_cred)); if (!acr->output_cred_handle) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } ret_maj = gp_export_gssx_cred(&ret_min, gpcall, &out_cred, acr->output_cred_handle); if (ret_maj) { goto done; } } done: ret = gp_conv_status_to_gssx(ret_maj, ret_min, desired_mech, &acr->status); GPRPCDEBUG(gssx_res_acquire_cred, acr); if (add_out_cred != &in_cred && add_out_cred != &out_cred) gss_release_cred(&ret_min, add_out_cred); if (in_cred != out_cred) gss_release_cred(&ret_min, &in_cred); gss_release_cred(&ret_min, &out_cred); gss_release_oid_set(&ret_min, &use_mechs); gss_release_oid_set(&ret_min, &desired_mechs); return ret; } gssproxy-v0.8.2/src/gp_rpc_creds.h0000644000174300017420000000362613456641744016617 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GP_RPC_CREDS_H_ #define _GP_RPC_CREDS_H_ #include "config.h" #include #include struct gp_call_ctx; bool gp_creds_allowed_mech(struct gp_call_ctx *gpcall, gss_OID desired_mech); uint32_t gp_get_supported_mechs(uint32_t *min, gss_OID_set *set); struct gssx_arg_acquire_cred; enum gp_aqcuire_cred_type { ACQ_NORMAL = 0, ACQ_IMPNAME = 1, }; int gp_get_acquire_type(struct gssx_arg_acquire_cred *arg); uint32_t gp_add_krb5_creds(uint32_t *min, struct gp_call_ctx *gpcall, enum gp_aqcuire_cred_type acquire_type, gss_cred_id_t in_cred, gssx_name *desired_name, gss_cred_usage_t cred_usage, uint32_t initiator_time_req, uint32_t acceptor_time_req, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, uint32_t *initiator_time_rec, uint32_t *acceptor_time_rec); uint32_t gp_cred_allowed(uint32_t *min, struct gp_call_ctx *gpcall, gss_cred_id_t cred, gss_name_t target_name); void gp_filter_flags(struct gp_call_ctx *gpcall, uint32_t *flags); struct gp_cred_check_handle { struct gp_call_ctx *ctx; struct { u_int options_len; gssx_option *options_val; } options; }; uint32_t gp_check_sync_creds(struct gp_cred_check_handle *h, gss_cred_id_t cred); uint32_t gp_export_sync_creds(uint32_t *min, struct gp_call_ctx *gpcall, gss_cred_id_t *cred, gssx_option **options_val, u_int *options_len); #endif /* _GP_RPC_CREDS_H_ */ gssproxy-v0.8.2/src/gp_rpc_debug.c0000644000174300017420000005127013456641744016576 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "rpcgen/gss_proxy.h" #include "gp_rpc_debug.h" #include void gpdbg_utf8string(utf8string *x) { gp_debug_printf("\"%.*s\" ", (int)x->utf8string_len, x->utf8string_val); } void gpdbg_octet_string(octet_string *x) { fprintf(stderr, "[ "); if ((GP_RPC_DEBUG_FULL > gp_debug) && (x->octet_string_len > 16)) { for (int i = 0; i < 16; i++) { char c = x->octet_string_val[i]; fprintf(stderr, "%c", isalnum(c) ? c : '.'); } fprintf(stderr, "... ] "); } else { for (unsigned i = 0; i < x->octet_string_len; i++) { fprintf(stderr, "%x", x->octet_string_val[i]); } fprintf(stderr, " ] "); } } void gpdbg_gssx_uint64(gssx_uint64 *x) { gp_debug_printf("%llu ", (long long unsigned)*x); } void gpdbg_gssx_OID(gssx_OID *x) { gss_OID_desc oid = { x->octet_string_len, x->octet_string_val }; gss_buffer_desc oidbuf; uint32_t maj, min; if (x->octet_string_len == 0) { gp_debug_printf(" "); return; } maj = gss_oid_to_str(&min, &oid, &oidbuf); if (GSS_ERROR(maj)) { gp_debug_printf(" "); } else { gp_debug_printf("%.*s ", oidbuf.length, (char *)oidbuf.value); } maj = gss_release_buffer(&min, &oidbuf); } void gpdbg_gssx_OID_set(gssx_OID_set *x) { gp_debug_printf("{ "); for (unsigned i = 0; i < x->gssx_OID_set_len; i++) { gpdbg_gssx_OID(&x->gssx_OID_set_val[i]); } gp_debug_printf("} "); } void gpdbg_gssx_cred_usage(gssx_cred_usage *x) { switch (*x) { case GSSX_C_INITIATE: gp_debug_printf("INITIATE "); break; case GSSX_C_ACCEPT: gp_debug_printf("ACCEPT "); break; case GSSX_C_BOTH: gp_debug_printf("BOTH "); break; default: gp_debug_printf(" ", (unsigned)*x); break; } } void gpdbg_gssx_option(gssx_option *x) { gp_debug_printf("{ "); gpdbg_gssx_buffer(&x->option); gpdbg_gssx_buffer(&x->value); gp_debug_printf("} "); } #define gpdbg_extensions(x) do { \ if ((x)->extensions.extensions_len > 0) { \ gp_debug_printf("[ "); \ for (unsigned i = 0; i < (x)->extensions.extensions_len; i++) { \ gpdbg_gssx_option(&(x)->extensions.extensions_val[i]); \ } \ gp_debug_printf("] "); \ } \ } while(0) #define gpdbg_options(x) do { \ if ((x)->options.options_len > 0) { \ gp_debug_printf("[ "); \ for (unsigned i = 0; i < (x)->options.options_len; i++) { \ gpdbg_gssx_option(&(x)->options.options_val[i]); \ } \ gp_debug_printf("] "); \ } \ } while(0) void gpdbg_gssx_mech_attr(gssx_mech_attr *x) { gp_debug_printf("{ "); gpdbg_gssx_OID(&x->attr); gpdbg_gssx_buffer(&x->name); gpdbg_gssx_buffer(&x->short_desc); gpdbg_gssx_buffer(&x->long_desc); gpdbg_extensions(x); gp_debug_printf("} "); } void gpdbg_gssx_mech_info(gssx_mech_info *x) { gp_debug_printf("{ "); gpdbg_gssx_OID(&x->mech); gpdbg_gssx_OID_set(&x->name_types); gpdbg_gssx_OID_set(&x->mech_attrs); gpdbg_gssx_OID_set(&x->known_mech_attrs); gpdbg_gssx_OID_set(&x->cred_options); gpdbg_gssx_OID_set(&x->sec_ctx_options); gpdbg_gssx_buffer(&x->saslname_sasl_mech_name); gpdbg_gssx_buffer(&x->saslname_mech_name); gpdbg_gssx_buffer(&x->saslname_mech_desc); gpdbg_extensions(x); gp_debug_printf("} "); } void gpdbg_gssx_name_attr(gssx_name_attr *x) { gp_debug_printf("{ "); gpdbg_gssx_buffer(&x->attr); gpdbg_gssx_buffer(&x->value); gpdbg_extensions(x); gp_debug_printf("} "); } void gpdbg_gssx_status(gssx_status *x) { gp_debug_printf("{ "); gpdbg_gssx_uint64(&x->major_status); gpdbg_gssx_OID(&x->mech); gpdbg_gssx_uint64(&x->minor_status); gpdbg_utf8string(&x->major_status_string); gpdbg_utf8string(&x->minor_status_string); gpdbg_octet_string(&x->server_ctx); gpdbg_options(x); gp_debug_printf("} "); } void gpdbg_gssx_call_ctx(gssx_call_ctx *x) { gp_debug_printf("{ "); gpdbg_utf8string(&x->locale); gpdbg_octet_string(&x->server_ctx); gpdbg_options(x); gp_debug_printf("} "); } #define gpdbg_name_attributes(X) do { \ gp_debug_printf("[ "); \ if (x->name_attributes.name_attributes_len > 0) { \ for (unsigned i = 0; i < x->name_attributes.name_attributes_len; i++) { \ gpdbg_gssx_name_attr( \ &x->name_attributes.name_attributes_val[i]); \ } \ } \ gp_debug_printf("] "); \ } while(0) void gpdbg_gssx_name(gssx_name *x) { if (GP_RPC_DEBUG_FULL <= gp_debug) { gp_debug_printf("{ "); } gpdbg_utf8string((utf8string *)&x->display_name); if (GP_RPC_DEBUG_FULL <= gp_debug) { gpdbg_gssx_OID(&x->name_type); gpdbg_gssx_buffer(&x->exported_name); gpdbg_gssx_buffer(&x->exported_composite_name); gpdbg_name_attributes(x); gpdbg_extensions(x); gp_debug_printf("} "); } } void gpdbg_gssx_cred_element(gssx_cred_element *x) { gp_debug_printf("{ "); gpdbg_gssx_name(&x->MN); gpdbg_gssx_OID(&x->mech); gpdbg_gssx_cred_usage(&x->cred_usage); gpdbg_gssx_time(&x->initiator_time_rec); gpdbg_gssx_time(&x->acceptor_time_rec); gpdbg_options(x); gp_debug_printf("} "); } void gpdbg_gssx_cred(gssx_cred *x) { gp_debug_printf("{ "); gpdbg_gssx_name(&x->desired_name); gp_debug_printf("[ "); for (unsigned i = 0; i < x->elements.elements_len; i++) { gpdbg_gssx_cred_element(&x->elements.elements_val[i]); } gp_debug_printf("] "); gpdbg_octet_string(&x->cred_handle_reference); gp_debug_printf("%d } ", (int)x->needs_release); } void gpdbg_gssx_ctx(gssx_ctx *x) { gp_debug_printf("{ "); gpdbg_octet_string((octet_string *)&x->exported_context_token); gpdbg_octet_string(&x->state); gp_debug_printf("%d ", (int)x->needs_release); gpdbg_gssx_OID(&x->mech); gpdbg_gssx_name(&x->src_name); gpdbg_gssx_name(&x->targ_name); gpdbg_gssx_time(&x->lifetime); gpdbg_gssx_uint64(&x->ctx_flags); gp_debug_printf("%d ", (int)x->locally_initiated); gp_debug_printf("%d ", (int)x->open); gpdbg_options(x); gp_debug_printf("} "); } void gpdbg_gssx_handle(gssx_handle *x) { switch (x->handle_type) { case GSSX_C_HANDLE_SEC_CTX: gpdbg_gssx_ctx(&x->gssx_handle_u.sec_ctx_info); break; case GSSX_C_HANDLE_CRED: gpdbg_gssx_cred(&x->gssx_handle_u.cred_info); break; default: gp_debug_printf(" "); break; } } void gpdbg_gssx_cb(gssx_cb *x) { gp_debug_printf("{ "); gpdbg_gssx_uint64(&x->initiator_addrtype); gpdbg_gssx_buffer(&x->initiator_address); gpdbg_gssx_uint64(&x->acceptor_addrtype); gpdbg_gssx_buffer(&x->acceptor_address); gpdbg_gssx_buffer(&x->application_data); gp_debug_printf("} "); } /* Actual RPCs Start Here */ void gpdbg_gssx_arg_release_handle(gssx_arg_release_handle *x) { gp_debug_printf(" GSSX_ARG_RELEASE_HANDLE( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("cred_handle: "); gpdbg_gssx_handle(&x->cred_handle); gp_debug_printf(")\n"); } void gpdbg_gssx_res_release_handle(gssx_res_release_handle *x) { gp_debug_printf(" GSSX_RES_RELEASE_HANDLE( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_indicate_mechs(gssx_arg_indicate_mechs *x) { gp_debug_printf(" GSSX_ARG_INDICATE_MECHS( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf(")\n"); } void gpdbg_gssx_res_indicate_mechs(gssx_res_indicate_mechs *x) { gp_debug_printf(" GSSX_RES_INDICATE_MECHS( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("mechs: [ "); for (unsigned i = 0; i < x->mechs.mechs_len; i++) { gpdbg_gssx_mech_info(&x->mechs.mechs_val[i]); } gp_debug_printf("] "); gp_debug_printf("mech_attr_descs: [ "); for (unsigned i = 0; i < x->mech_attr_descs.mech_attr_descs_len; i++) { gpdbg_gssx_mech_attr(&x->mech_attr_descs.mech_attr_descs_val[i]); } gp_debug_printf("] "); gp_debug_printf("supported_extensions: [ "); for (unsigned i = 0; i < x->supported_extensions.supported_extensions_len; i++) { gpdbg_gssx_buffer( &x->supported_extensions.supported_extensions_val[i]); } gp_debug_printf("] "); gpdbg_extensions(x); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_import_and_canon_name(gssx_arg_import_and_canon_name *x) { gp_debug_printf(" GSSX_ARG_IMPORT_AND_CANON_NAME( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("input_name: "); gpdbg_gssx_name(&x->input_name); gp_debug_printf("mech: "); gpdbg_gssx_OID(&x->mech); gp_debug_printf("name_attributes: "); gpdbg_name_attributes(x); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_res_import_and_canon_name(gssx_res_import_and_canon_name *x) { gp_debug_printf(" GSSX_RES_IMPORT_AND_CANON_NAME( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("output_name: "); GPRPCDEBUG(gssx_name, x->output_name); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_get_call_context(gssx_arg_get_call_context *x) { gp_debug_printf(" GSSX_ARG_GET_CALL_CONTEXT( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_res_get_call_context(gssx_res_get_call_context *x) { gp_debug_printf(" GSSX_RES_GET_CALL_CONTEXT( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("server_call_ctx: "); gpdbg_octet_string(&x->server_call_ctx); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_acquire_cred(gssx_arg_acquire_cred *x) { gp_debug_printf(" GSSX_ARG_ACQUIRE_CRED( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("input_cred_handle: "); GPRPCDEBUG(gssx_cred, x->input_cred_handle); gp_debug_printf("add_cred: "); gp_debug_printf("%d ", (int)x->add_cred_to_input_handle); gp_debug_printf("desired_name: "); GPRPCDEBUG(gssx_name, x->desired_name); gp_debug_printf("time_req: "); gpdbg_gssx_time(&x->time_req); gp_debug_printf("desired_mechs: "); gpdbg_gssx_OID_set(&x->desired_mechs); gp_debug_printf("cred_usage: "); gpdbg_gssx_cred_usage(&x->cred_usage); gp_debug_printf("initiator_time_req: "); gpdbg_gssx_time(&x->initiator_time_req); gp_debug_printf("acceptor_time_req: "); gpdbg_gssx_time(&x->acceptor_time_req); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_res_acquire_cred(gssx_res_acquire_cred *x) { gp_debug_printf(" GSSX_RES_ACQUIRE_CRED( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("output_cred_handle: "); GPRPCDEBUG(gssx_cred, x->output_cred_handle); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_export_cred(gssx_arg_export_cred *x) { gp_debug_printf(" GSSX_ARG_EXPORT_CRED( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("input_cred_handle: "); gpdbg_gssx_cred(&x->input_cred_handle); gp_debug_printf("cred_usage: "); gpdbg_gssx_cred_usage(&x->cred_usage); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_res_export_cred(gssx_res_export_cred *x) { gp_debug_printf(" GSSX_RES_EXPORT_CRED( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("usage_exported: "); gpdbg_gssx_cred_usage(&x->usage_exported); gp_debug_printf("exported_handle: "); if (x->exported_handle) { gpdbg_octet_string(x->exported_handle); } else { gp_debug_printf(" "); } gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_import_cred(gssx_arg_import_cred *x) { gp_debug_printf(" GSSX_ARG_IMPORT_CRED( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("exported_handle: "); gpdbg_octet_string(&x->exported_handle); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_res_import_cred(gssx_res_import_cred *x) { gp_debug_printf(" GSSX_RES_IMPORT_CRED( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("output_cred_handle: "); GPRPCDEBUG(gssx_cred, x->output_cred_handle); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_store_cred(gssx_arg_store_cred *x) { gp_debug_printf(" GSSX_ARG_STORE_CRED( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("input_cred_handle: "); gpdbg_gssx_cred(&x->input_cred_handle); gp_debug_printf("cred_usage: "); gpdbg_gssx_cred_usage(&x->cred_usage); gp_debug_printf("desired_mech: "); gpdbg_gssx_OID(&x->desired_mech); gp_debug_printf("overwrite_cred: "); gp_debug_printf("%d ", (int)x->overwrite_cred); gp_debug_printf("default_cred: "); gp_debug_printf("%d ", (int)x->default_cred); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_res_store_cred(gssx_res_store_cred *x) { gp_debug_printf(" GSSX_RES_STORE_CRED( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("elements_stored: "); gpdbg_gssx_OID_set(&x->elements_stored); gp_debug_printf("cred_usage_stored: "); gpdbg_gssx_cred_usage(&x->cred_usage_stored); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_init_sec_context(gssx_arg_init_sec_context *x) { gp_debug_printf(" GSSX_ARG_INIT_SEC_CONTEXT( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("context_handle: "); GPRPCDEBUG(gssx_ctx, x->context_handle); gp_debug_printf("cred_handle: "); GPRPCDEBUG(gssx_cred, x->cred_handle); gp_debug_printf("target_name: "); GPRPCDEBUG(gssx_name, x->target_name); gp_debug_printf("mech_type: "); gpdbg_gssx_OID(&x->mech_type); gp_debug_printf("req_flags: "); gpdbg_gssx_uint64(&x->req_flags); gp_debug_printf("time_req: "); gpdbg_gssx_time(&x->time_req); gp_debug_printf("input_cb: "); GPRPCDEBUG(gssx_cb, x->input_cb); gp_debug_printf("input_token: "); if (x->input_token) { gpdbg_octet_string(x->input_token); } else { gp_debug_printf(" "); } gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_res_init_sec_context(gssx_res_init_sec_context *x) { gp_debug_printf(" GSSX_RES_INIT_SEC_CONTEXT( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("context_handle: "); GPRPCDEBUG(gssx_ctx, x->context_handle); gp_debug_printf("output_token: "); if (x->output_token) { gpdbg_octet_string(x->output_token); } else { gp_debug_printf(" "); } gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_accept_sec_context(gssx_arg_accept_sec_context *x) { gp_debug_printf(" GSSX_ARG_ACCEPT_SEC_CONTEXT( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("context_handle: "); GPRPCDEBUG(gssx_ctx, x->context_handle); gp_debug_printf("cred_handle: "); GPRPCDEBUG(gssx_cred, x->cred_handle); gp_debug_printf("input_token: "); gpdbg_octet_string(&x->input_token); gp_debug_printf("input_cb: "); GPRPCDEBUG(gssx_cb, x->input_cb); gp_debug_printf("ret_deleg_cred: "); gp_debug_printf("%d ", (int)x->ret_deleg_cred); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_res_accept_sec_context(gssx_res_accept_sec_context *x) { gp_debug_printf(" GSSX_RES_ACCEPT_SEC_CONTEXT( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("context_handle: "); GPRPCDEBUG(gssx_ctx, x->context_handle); gp_debug_printf("output_token: "); if (x->output_token) { gpdbg_octet_string(x->output_token); } else { gp_debug_printf(" "); } gp_debug_printf("delegated_cred_handle: "); GPRPCDEBUG(gssx_cred, x->delegated_cred_handle); gpdbg_options(x); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_get_mic(gssx_arg_get_mic *x) { gp_debug_printf(" GSSX_ARG_GET_MIC( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("context_handle: "); gpdbg_gssx_ctx(&x->context_handle); gp_debug_printf("qop_req: "); gpdbg_gssx_qop(&x->qop_req); gp_debug_printf("message_buffer: "); gpdbg_octet_string(&x->message_buffer); gp_debug_printf(")\n"); } void gpdbg_gssx_res_get_mic(gssx_res_get_mic *x) { gp_debug_printf(" GSSX_RES_GET_MIC( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("context_handle: "); GPRPCDEBUG(gssx_ctx, x->context_handle); gp_debug_printf("token_buffer: "); gpdbg_octet_string(&x->token_buffer); gp_debug_printf("qop_state: "); GPRPCDEBUG(gssx_qop, x->qop_state); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_verify_mic(gssx_arg_verify_mic *x) { gp_debug_printf(" GSSX_ARG_VERIFY_MIC( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("context_handle: "); gpdbg_gssx_ctx(&x->context_handle); gp_debug_printf("message_buffer: "); gpdbg_octet_string(&x->message_buffer); gp_debug_printf("token_buffer: "); gpdbg_octet_string(&x->token_buffer); gp_debug_printf(")\n"); } void gpdbg_gssx_res_verify_mic(gssx_res_verify_mic *x) { gp_debug_printf(" GSSX_RES_VERIFY_MIC( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("context_handle: "); GPRPCDEBUG(gssx_ctx, x->context_handle); gp_debug_printf("qop_state: "); GPRPCDEBUG(gssx_qop, x->qop_state); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_wrap(gssx_arg_wrap *x) { gp_debug_printf(" GSSX_ARG_WRAP( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("context_handle: "); gpdbg_gssx_ctx(&x->context_handle); gp_debug_printf("conf_req: "); gp_debug_printf("%d ", (int)x->conf_req); gp_debug_printf("message_buffer: [ "); for (unsigned i = 0; i < x->message_buffer.message_buffer_len; i++) { gpdbg_octet_string(&x->message_buffer.message_buffer_val[i]); } gp_debug_printf("] "); gp_debug_printf("qop_state: "); gpdbg_gssx_qop(&x->qop_state); gp_debug_printf(")\n"); } void gpdbg_gssx_res_wrap(gssx_res_wrap *x) { gp_debug_printf(" GSSX_RES_WRAP( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("context_handle: "); GPRPCDEBUG(gssx_ctx, x->context_handle); gp_debug_printf("token_buffer: [ "); for (unsigned i = 0; i < x->token_buffer.token_buffer_len; i++) { gpdbg_octet_string(&x->token_buffer.token_buffer_val[i]); } gp_debug_printf("] "); gp_debug_printf("conf_state: "); if (x->conf_state) { gp_debug_printf("%d ", (int)*(x->conf_state)); } else { gp_debug_printf(" "); } gp_debug_printf("qop_state: "); GPRPCDEBUG(gssx_qop, x->qop_state); gp_debug_printf(")\n"); } void gpdbg_gssx_arg_unwrap(gssx_arg_unwrap *x) { gp_debug_printf(" GSSX_ARG_UNWRAP( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("context_handle: "); gpdbg_gssx_ctx(&x->context_handle); gp_debug_printf("token_buffer: [ "); for (unsigned i = 0; i < x->token_buffer.token_buffer_len; i++) { gpdbg_octet_string(&x->token_buffer.token_buffer_val[i]); } gp_debug_printf("] "); gp_debug_printf("qop_state: "); gpdbg_gssx_qop(&x->qop_state); gp_debug_printf(")\n"); } void gpdbg_gssx_res_unwrap(gssx_res_unwrap *x) { gp_debug_printf(" GSSX_RES_UNWRAP( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("context_handle: "); GPRPCDEBUG(gssx_ctx, x->context_handle); gp_debug_printf("message_buffer: [ "); for (unsigned i = 0; i < x->message_buffer.message_buffer_len; i++) { gpdbg_octet_string(&x->message_buffer.message_buffer_val[i]); } gp_debug_printf("] "); gp_debug_printf("conf_state: "); if (x->conf_state) { gp_debug_printf("%d ", (int)*(x->conf_state)); } else { gp_debug_printf(" "); } gp_debug_printf("qop_state: "); GPRPCDEBUG(gssx_qop, x->qop_state); } void gpdbg_gssx_arg_wrap_size_limit(gssx_arg_wrap_size_limit *x) { gp_debug_printf(" GSSX_ARG_WRAP_SIZE_LIMIT( call_ctx: "); gpdbg_gssx_call_ctx(&x->call_ctx); gp_debug_printf("context_handle: "); gpdbg_gssx_ctx(&x->context_handle); gp_debug_printf("conf_req: "); gp_debug_printf("%d ", (int)x->conf_req); gp_debug_printf("qop_state: "); gpdbg_gssx_qop(&x->qop_state); gp_debug_printf("req_output_size: "); gpdbg_gssx_uint64(&x->req_output_size); } void gpdbg_gssx_res_wrap_size_limit(gssx_res_wrap_size_limit *x) { gp_debug_printf(" GSSX_RES_WRAP_SIZE_LIMIT( status: "); gpdbg_gssx_status(&x->status); gp_debug_printf("max_input_size: "); gpdbg_gssx_uint64(&x->max_input_size); } gssproxy-v0.8.2/src/gp_rpc_debug.h0000644000174300017420000000612013456641744016575 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GP_RPC_DEBUG_H_ #define _GP_RPC_DEBUG_H_ #include "gp_debug.h" void gpdbg_utf8string(utf8string *x); void gpdbg_octet_string(octet_string *x); void gpdbg_gssx_uint64(gssx_uint64 *x); #define gpdbg_gssx_qop gpdbg_gssx_uint64 #define gpdbg_gssx_buffer gpdbg_octet_string void gpdbg_gssx_OID(gssx_OID *x); void gpdbg_gssx_OID_set(gssx_OID_set *x); void gpdbg_gssx_cred_usage(gssx_cred_usage *x); #define gpdbg_gssx_time gpdbg_gssx_uint64 void gpdbg_gssx_option(gssx_option *x); void gpdbg_gssx_mech_attr(gssx_mech_attr *x); void gpdbg_gssx_mech_info(gssx_mech_info *x); void gpdbg_gssx_name_attr(gssx_name_attr *x); void gpdbg_gssx_status(gssx_status *x); void gpdbg_gssx_call_ctx(gssx_call_ctx *x); void gpdbg_gssx_name(gssx_name *x); void gpdbg_gssx_cred_element(gssx_cred_element *x); void gpdbg_gssx_cred(gssx_cred *x); void gpdbg_gssx_ctx(gssx_ctx *x); void gpdbg_gssx_handle(gssx_handle *x); void gpdbg_gssx_cb(gssx_cb *x); void gpdbg_gssx_arg_release_handle(gssx_arg_release_handle *x); void gpdbg_gssx_res_release_handle(gssx_res_release_handle *x); void gpdbg_gssx_arg_indicate_mechs(gssx_arg_indicate_mechs *x); void gpdbg_gssx_res_indicate_mechs(gssx_res_indicate_mechs *x); void gpdbg_gssx_arg_import_and_canon_name(gssx_arg_import_and_canon_name *x); void gpdbg_gssx_res_import_and_canon_name(gssx_res_import_and_canon_name *x); void gpdbg_gssx_arg_get_call_context(gssx_arg_get_call_context *x); void gpdbg_gssx_res_get_call_context(gssx_res_get_call_context *x); void gpdbg_gssx_arg_acquire_cred(gssx_arg_acquire_cred *x); void gpdbg_gssx_res_acquire_cred(gssx_res_acquire_cred *x); void gpdbg_gssx_arg_export_cred(gssx_arg_export_cred *x); void gpdbg_gssx_res_export_cred(gssx_res_export_cred *x); void gpdbg_gssx_arg_import_cred(gssx_arg_import_cred *x); void gpdbg_gssx_res_import_cred(gssx_res_import_cred *x); void gpdbg_gssx_arg_store_cred(gssx_arg_store_cred *x); void gpdbg_gssx_res_store_cred(gssx_res_store_cred *x); void gpdbg_gssx_arg_init_sec_context(gssx_arg_init_sec_context *x); void gpdbg_gssx_res_init_sec_context(gssx_res_init_sec_context *x); void gpdbg_gssx_arg_accept_sec_context(gssx_arg_accept_sec_context *x); void gpdbg_gssx_res_accept_sec_context(gssx_res_accept_sec_context *x); void gpdbg_gssx_arg_get_mic(gssx_arg_get_mic *x); void gpdbg_gssx_res_get_mic(gssx_res_get_mic *x); void gpdbg_gssx_arg_verify_mic(gssx_arg_verify_mic *x); void gpdbg_gssx_res_verify_mic(gssx_res_verify_mic *x); void gpdbg_gssx_arg_wrap(gssx_arg_wrap *x); void gpdbg_gssx_res_wrap(gssx_res_wrap *x); void gpdbg_gssx_arg_unwrap(gssx_arg_unwrap *x); void gpdbg_gssx_res_unwrap(gssx_res_unwrap *x); void gpdbg_gssx_arg_wrap_size_limit(gssx_arg_wrap_size_limit *x); void gpdbg_gssx_res_wrap_size_limit(gssx_res_wrap_size_limit *x); #define GP_RPC_DEBUG_LVL 2 #define GP_RPC_DEBUG_FULL 3 #define GPRPCDEBUG(name, x) do { \ if (GP_RPC_DEBUG_LVL <= gp_debug) { \ if (x == NULL) { \ gp_debug_printf(" "); \ } else { \ gpdbg_##name(x); \ } \ } \ } while(0) #endif /* _GP_RPC_DEBUG_H_ */ gssproxy-v0.8.2/src/gp_rpc_get_mic.c0000644000174300017420000000456713456641744017126 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #include "gp_rpc_process.h" #include int gp_get_mic(struct gp_call_ctx *gpcall UNUSED, union gp_rpc_arg *arg, union gp_rpc_res *res) { gss_buffer_desc message_buffer = GSS_C_EMPTY_BUFFER; gss_buffer_desc message_token = GSS_C_EMPTY_BUFFER; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; struct gssx_arg_get_mic *gma; struct gssx_res_get_mic *gmr; uint32_t ret_maj; uint32_t ret_min; int ret; int exp_ctx_type; gma = &arg->get_mic; gmr = &res->get_mic; GPRPCDEBUG(gssx_arg_get_mic, gma); exp_ctx_type = gp_get_exported_context_type(&gma->call_ctx); if (exp_ctx_type == -1) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } ret_maj = gp_import_gssx_to_ctx_id(&ret_min, 0, &gma->context_handle, &context_handle); if (ret_maj) { goto done; } gp_conv_gssx_to_buffer(&gma->message_buffer, &message_buffer); ret_maj = gss_get_mic(&ret_min, context_handle, gma->qop_req, &message_buffer, &message_token); if (ret_maj) { goto done; } gmr->context_handle = calloc(1, sizeof(gssx_ctx)); if (!gmr->context_handle) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } ret_maj = gp_export_ctx_id_to_gssx(&ret_min, exp_ctx_type, GSS_C_NO_OID, &context_handle, gmr->context_handle); if (ret_maj) { goto done; } gmr->qop_state = calloc(1, sizeof(gssx_qop)); if (!gmr->qop_state) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } /* what is the point of returning an input parameter ? - gd */ *gmr->qop_state = gma->qop_req; ret = gp_conv_buffer_to_gssx(&message_token, &gmr->token_buffer); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret_maj = GSS_S_COMPLETE; ret_min = 0; done: ret = gp_conv_status_to_gssx(ret_maj, ret_min, GSS_C_NO_OID, &gmr->status); GPRPCDEBUG(gssx_res_get_mic, gmr); gss_release_buffer(&ret_min, &message_token); return ret; } gssproxy-v0.8.2/src/gp_rpc_import_and_canon_name.c0000644000174300017420000000450213456641744022016 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gp_rpc_process.h" /* NOTE: Very Important, before ever touching this function please read * carefully RFC 2744 section 3.10 "Names". * I am not kidding, if you hav not read it, go back and do it now, or do not * touch this function */ int gp_import_and_canon_name(struct gp_call_ctx *gpcall UNUSED, union gp_rpc_arg *arg, union gp_rpc_res *res) { struct gssx_arg_import_and_canon_name *icna; struct gssx_res_import_and_canon_name *icnr; gss_OID mech = GSS_C_NO_OID; gss_name_t import_name = GSS_C_NO_NAME; gss_name_t output_name = GSS_C_NO_NAME; uint32_t ret_maj = 0; uint32_t ret_min = 0; int ret; icna = &arg->import_and_canon_name; icnr = &res->import_and_canon_name; GPRPCDEBUG(gssx_arg_import_and_canon_name, icna); if (icna->input_name.display_name.octet_string_len == 0 && icna->input_name.exported_name.octet_string_len == 0) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } ret_maj = gp_conv_gssx_to_name(&ret_min, &icna->input_name, &import_name); if (ret_maj) { goto done; } if (icna->mech.octet_string_len != 0) { ret = gp_conv_gssx_to_oid_alloc(&icna->mech, &mech); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret_maj = gss_canonicalize_name(&ret_min, import_name, mech, &output_name); if (ret_maj) { goto done; } ret_maj = gp_conv_name_to_gssx_alloc(&ret_min, output_name, &icnr->output_name); } else { ret_maj = gp_conv_name_to_gssx_alloc(&ret_min, import_name, &icnr->output_name); } /* TODO: check also icna->input_name.exported_composite_name */ /* TODO: icna->name_attributes */ done: ret = gp_conv_status_to_gssx(ret_maj, ret_min, mech, &icnr->status); GPRPCDEBUG(gssx_res_import_and_canon_name, icnr); gss_release_oid(&ret_min, &mech); gss_release_name(&ret_min, &import_name); gss_release_name(&ret_min, &output_name); return ret; } gssproxy-v0.8.2/src/gp_rpc_indicate_mechs.c0000644000174300017420000002052513456641744020446 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gp_rpc_process.h" #include "gp_debug.h" int gp_indicate_mechs(struct gp_call_ctx *gpcall UNUSED, union gp_rpc_arg *arg, union gp_rpc_res *res) { struct gssx_arg_indicate_mechs *ima; struct gssx_res_indicate_mechs *imr; gss_OID_set mech_set = GSS_C_NO_OID_SET; gss_OID_set name_types = GSS_C_NO_OID_SET; gss_OID_set mech_attrs = GSS_C_NO_OID_SET; gss_OID_set known_mech_attrs = GSS_C_NO_OID_SET; gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER; gss_buffer_desc mech_name = GSS_C_EMPTY_BUFFER; gss_buffer_desc mech_desc = GSS_C_EMPTY_BUFFER; gss_OID_set attr_set = GSS_C_NO_OID_SET; gss_buffer_desc name = GSS_C_EMPTY_BUFFER; gss_buffer_desc short_desc = GSS_C_EMPTY_BUFFER; gss_buffer_desc long_desc = GSS_C_EMPTY_BUFFER; gssx_mech_info *mi; gssx_mech_attr *ma; uint32_t ret_maj; uint32_t ret_min; int present; int ret; ima = &arg->indicate_mechs; imr = &res->indicate_mechs; GPRPCDEBUG(gssx_arg_indicate_mechs, ima); /* get all mechs */ ret_maj = gss_indicate_mechs(&ret_min, &mech_set); if (ret_maj) { goto done; } ret_maj = gss_create_empty_oid_set(&ret_min, &attr_set); if (ret_maj) { goto done; } /* fill up gssx_mech_info */ imr->mechs.mechs_val = calloc(mech_set->count, sizeof(gssx_mech_info)); if (!imr->mechs.mechs_val) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } imr->mechs.mechs_len = mech_set->count; for (unsigned i = 0, h = 0; i < mech_set->count; i++, h++) { mi = &imr->mechs.mechs_val[h]; ret = gp_conv_oid_to_gssx(&mech_set->elements[i], &mi->mech); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret_maj = gss_inquire_names_for_mech(&ret_min, &mech_set->elements[i], &name_types); if (ret_maj) { gp_log_failure(&mech_set->elements[i], ret_maj, ret_min); /* temporarily skip any offender */ imr->mechs.mechs_len--; h--; xdr_free((xdrproc_t)xdr_gssx_OID, (char *)&mi->mech); continue; #if 0 ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; #endif } ret = gp_conv_oid_set_to_gssx(name_types, &mi->name_types); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } gss_release_oid_set(&ret_min, &name_types); ret_maj = gss_inquire_attrs_for_mech(&ret_min, &mech_set->elements[i], &mech_attrs, &known_mech_attrs); if (ret_maj) { goto done; } ret = gp_conv_oid_set_to_gssx(mech_attrs, &mi->mech_attrs); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } for (unsigned j = 0; j < mech_attrs->count; j++) { ret_maj = gss_test_oid_set_member(&ret_min, &mech_attrs->elements[j], attr_set, &present); if (ret_maj) { goto done; } if (present) { continue; } ret_maj = gss_add_oid_set_member(&ret_min, &mech_attrs->elements[j], &attr_set); if (ret_maj) { goto done; } } gss_release_oid_set(&ret_min, &mech_attrs); ret = gp_conv_oid_set_to_gssx(known_mech_attrs, &mi->known_mech_attrs); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } for (unsigned j = 0; j < known_mech_attrs->count; j++) { ret_maj = gss_test_oid_set_member(&ret_min, &known_mech_attrs->elements[j], attr_set, &present); if (ret_maj) { goto done; } if (present) { continue; } ret_maj = gss_add_oid_set_member(&ret_min, &known_mech_attrs->elements[j], &attr_set); if (ret_maj) { goto done; } } gss_release_oid_set(&ret_min, &known_mech_attrs); ret_maj = gss_inquire_saslname_for_mech(&ret_min, &mech_set->elements[i], &sasl_mech_name, &mech_name, &mech_desc); if (ret_maj) { goto done; } ret = gp_conv_buffer_to_gssx(&sasl_mech_name, &mi->saslname_sasl_mech_name); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } gss_release_buffer(&ret_min, &sasl_mech_name); ret = gp_conv_buffer_to_gssx(&mech_name, &mi->saslname_mech_name); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } gss_release_buffer(&ret_min, &mech_name); ret = gp_conv_buffer_to_gssx(&mech_desc, &mi->saslname_mech_desc); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } gss_release_buffer(&ret_min, &mech_desc); } /* fill up gssx_mech_attr */ imr->mech_attr_descs.mech_attr_descs_val = calloc(attr_set->count, sizeof(gssx_mech_attr)); if (!imr->mech_attr_descs.mech_attr_descs_val) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } imr->mech_attr_descs.mech_attr_descs_len = attr_set->count; for (unsigned i = 0; i < attr_set->count; i++) { ma = &imr->mech_attr_descs.mech_attr_descs_val[i]; ret = gp_conv_oid_to_gssx(&attr_set->elements[i], &ma->attr); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret_maj = gss_display_mech_attr(&ret_min, &attr_set->elements[i], &name, &short_desc, &long_desc); if (ret_maj) { goto done; } ret = gp_conv_buffer_to_gssx(&name, &ma->name); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } gss_release_buffer(&ret_min, &name); ret = gp_conv_buffer_to_gssx(&short_desc, &ma->short_desc); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } gss_release_buffer(&ret_min, &short_desc); ret = gp_conv_buffer_to_gssx(&long_desc, &ma->long_desc); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } gss_release_buffer(&ret_min, &long_desc); } done: ret = gp_conv_status_to_gssx(ret_maj, ret_min, GSS_C_NO_OID, &imr->status); GPRPCDEBUG(gssx_res_indicate_mechs, imr); gss_release_buffer(&ret_min, &long_desc); gss_release_buffer(&ret_min, &short_desc); gss_release_buffer(&ret_min, &name); gss_release_oid_set(&ret_min, &attr_set); gss_release_buffer(&ret_min, &mech_desc); gss_release_buffer(&ret_min, &mech_name); gss_release_buffer(&ret_min, &sasl_mech_name); gss_release_oid_set(&ret_min, &known_mech_attrs); gss_release_oid_set(&ret_min, &mech_attrs); gss_release_oid_set(&ret_min, &name_types); gss_release_oid_set(&ret_min, &mech_set); return ret; } gssproxy-v0.8.2/src/gp_rpc_init_sec_context.c0000644000174300017420000001350113456641744021044 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gp_rpc_process.h" #include int gp_init_sec_context(struct gp_call_ctx *gpcall, union gp_rpc_arg *arg, union gp_rpc_res *res) { struct gssx_arg_init_sec_context *isca; struct gssx_res_init_sec_context *iscr; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_cred_id_t ich = GSS_C_NO_CREDENTIAL; gss_name_t target_name = GSS_C_NO_NAME; gss_OID mech_type = GSS_C_NO_OID; uint32_t req_flags; uint32_t time_req; struct gss_channel_bindings_struct cbs; gss_channel_bindings_t pcbs; gss_buffer_desc ibuf = { 0, NULL }; gss_buffer_t pibuf = &ibuf; gss_OID actual_mech_type = GSS_C_NO_OID; gss_buffer_desc obuf = GSS_C_EMPTY_BUFFER; uint32_t ret_maj; uint32_t ret_min; uint32_t init_maj; uint32_t init_min; int exp_ctx_type; struct gp_cred_check_handle gcch = { .ctx = gpcall, .options.options_len = arg->init_sec_context.options.options_len, .options.options_val = arg->init_sec_context.options.options_val, }; uint32_t gccn_before = 0; uint32_t gccn_after = 0; int ret; isca = &arg->init_sec_context; iscr = &res->init_sec_context; GPRPCDEBUG(gssx_arg_init_sec_context, isca); exp_ctx_type = gp_get_exported_context_type(&isca->call_ctx); if (exp_ctx_type == -1) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } if (isca->context_handle) { ret_maj = gp_import_gssx_to_ctx_id(&ret_min, 0, isca->context_handle, &ctx); if (ret_maj) { goto done; } } if (isca->cred_handle) { ret_maj = gp_import_gssx_cred(&ret_min, gpcall, isca->cred_handle, &ich); if (ret_maj) { goto done; } gccn_before = gp_check_sync_creds(&gcch, ich); } ret_maj = gp_conv_gssx_to_name(&ret_min, isca->target_name, &target_name); if (ret_maj) { goto done; } ret = gp_conv_gssx_to_oid_alloc(&isca->mech_type, &mech_type); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } req_flags = isca->req_flags; time_req = isca->time_req; if (isca->input_cb) { pcbs = &cbs; gp_conv_gssx_to_cb(isca->input_cb, pcbs); } else { pcbs = GSS_C_NO_CHANNEL_BINDINGS; } if (isca->input_token) { gp_conv_gssx_to_buffer(isca->input_token, &ibuf); } if (!ich) { if (gss_oid_equal(mech_type, gss_mech_krb5)) { ret_maj = gp_add_krb5_creds(&ret_min, gpcall, ACQ_NORMAL, NULL, NULL, GSS_C_INITIATE, time_req, 0, &ich, NULL, NULL, NULL); } else { ret_maj = GSS_S_NO_CRED; ret_min = 0; } if (ret_maj) { goto done; } } ret_maj = gp_cred_allowed(&ret_min, gpcall, ich, target_name); if (ret_maj) { goto done; } gp_filter_flags(gpcall, &req_flags); ret_maj = gss_init_sec_context(&ret_min, ich, &ctx, target_name, mech_type, req_flags, time_req, pcbs, pibuf, &actual_mech_type, &obuf, NULL, NULL); if (ret_maj != GSS_S_COMPLETE && ret_maj != GSS_S_CONTINUE_NEEDED) { goto done; } else { init_maj = ret_maj; init_min = ret_min; } if (init_maj == GSS_S_CONTINUE_NEEDED) { exp_ctx_type = gp_get_continue_needed_type(); } iscr->context_handle = calloc(1, sizeof(gssx_ctx)); if (!iscr->context_handle) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } ret_maj = gp_export_ctx_id_to_gssx(&ret_min, exp_ctx_type, mech_type, &ctx, iscr->context_handle); if (ret_maj) { goto done; } if (obuf.length != 0) { iscr->output_token = calloc(1, sizeof(gssx_buffer)); if (!iscr->output_token) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } ret = gp_conv_buffer_to_gssx(&obuf, iscr->output_token); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } } gccn_after = gp_check_sync_creds(&gcch, ich); if (gccn_before != gccn_after) { /* export creds back to client for sync up */ ret_maj = gp_export_sync_creds(&ret_min, gpcall, &ich, &iscr->options.options_val, &iscr->options.options_len); if (ret_maj) { /* not fatal, log and continue */ GPDEBUG("Failed to export sync creds (%d: %d)", (int)ret_maj, (int)ret_min); } } ret_maj = GSS_S_COMPLETE; done: if (ret_maj == GSS_S_COMPLETE) { ret_maj = init_maj; ret_min = init_min; } ret = gp_conv_status_to_gssx(ret_maj, ret_min, mech_type, &iscr->status); GPRPCDEBUG(gssx_res_init_sec_context, iscr); gss_release_name(&ret_min, &target_name); gss_release_oid(&ret_min, &mech_type); gss_release_cred(&ret_min, &ich); gss_release_buffer(&ret_min, &obuf); return ret; } gssproxy-v0.8.2/src/gp_rpc_process.c0000644000174300017420000002612013456641744017162 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gp_proxy.h" #include "gp_rpc_process.h" typedef int (*gp_exec_fn)(gp_exec_std_args); struct gp_rpc_fn_set { uint32_t proc; const char *proc_name; xdrproc_t arg_fn; xdrproc_t res_fn; gp_exec_fn exec_fn; } gp_xdr_set[] = { { 0, "NULLPROC", (xdrproc_t)xdr_void, (xdrproc_t)xdr_void, NULL }, { GSSX_INDICATE_MECHS, "GSSX_INDICATE_MECHS", (xdrproc_t)xdr_gssx_arg_indicate_mechs, (xdrproc_t)xdr_gssx_res_indicate_mechs, gp_indicate_mechs }, { GSSX_GET_CALL_CONTEXT, "GSSX_GET_CALL_CONTEXT", (xdrproc_t)xdr_gssx_arg_get_call_context, (xdrproc_t)xdr_gssx_res_get_call_context, gp_get_call_context }, { GSSX_IMPORT_AND_CANON_NAME, "GSSX_IMPORT_AND_CANON_NAME", (xdrproc_t)xdr_gssx_arg_import_and_canon_name, (xdrproc_t)xdr_gssx_res_import_and_canon_name, gp_import_and_canon_name }, { GSSX_EXPORT_CRED, "GSSX_EXPORT_CRED", (xdrproc_t)xdr_gssx_arg_export_cred, (xdrproc_t)xdr_gssx_res_export_cred, gp_export_cred }, { GSSX_IMPORT_CRED, "GSSX_IMPORT_CRED", (xdrproc_t)xdr_gssx_arg_import_cred, (xdrproc_t)xdr_gssx_res_import_cred, gp_import_cred }, { GSSX_ACQUIRE_CRED, "GSSX_ACQUIRE_CRED", (xdrproc_t)xdr_gssx_arg_acquire_cred, (xdrproc_t)xdr_gssx_res_acquire_cred, gp_acquire_cred }, { GSSX_STORE_CRED, "GSSX_STORE_CRED", (xdrproc_t)xdr_gssx_arg_store_cred, (xdrproc_t)xdr_gssx_res_store_cred, gp_store_cred }, { GSSX_INIT_SEC_CONTEXT, "GSSX_INIT_SEC_CONTEXT", (xdrproc_t)xdr_gssx_arg_init_sec_context, (xdrproc_t)xdr_gssx_res_init_sec_context, gp_init_sec_context }, { GSSX_ACCEPT_SEC_CONTEXT, "GSSX_ACCEPT_SEC_CONTEXT", (xdrproc_t)xdr_gssx_arg_accept_sec_context, (xdrproc_t)xdr_gssx_res_accept_sec_context, gp_accept_sec_context }, { GSSX_RELEASE_HANDLE, "GSSX_RELEASE_HANDLE", (xdrproc_t)xdr_gssx_arg_release_handle, (xdrproc_t)xdr_gssx_res_release_handle, gp_release_handle }, { GSSX_GET_MIC, "GSSX_GET_MIC", (xdrproc_t)xdr_gssx_arg_get_mic, (xdrproc_t)xdr_gssx_res_get_mic, gp_get_mic }, { GSSX_VERIFY, "GSSX_VERIFY", (xdrproc_t)xdr_gssx_arg_verify_mic, (xdrproc_t)xdr_gssx_res_verify_mic, gp_verify_mic }, { GSSX_WRAP, "GSSX_WRAP", (xdrproc_t)xdr_gssx_arg_wrap, (xdrproc_t)xdr_gssx_res_wrap, gp_wrap }, { GSSX_UNWRAP, "GSSX_UNWRAP", (xdrproc_t)xdr_gssx_arg_unwrap, (xdrproc_t)xdr_gssx_res_unwrap, gp_unwrap }, { GSSX_WRAP_SIZE_LIMIT, "GSSX_WRAP_SIZE_LIMIT", (xdrproc_t)xdr_gssx_arg_wrap_size_limit, (xdrproc_t)xdr_gssx_res_wrap_size_limit, gp_wrap_size_limit } }; static int gp_rpc_decode_call_header(XDR *xdr_call_ctx, uint32_t *xid, uint32_t *proc, gp_rpc_accept_status *acc, gp_rpc_reject_status *rej) { struct gp_rpc_call_header *chdr; gp_rpc_msg msg; bool decoded; int ret; memset(&msg, 0, sizeof(gp_rpc_msg)); decoded = xdr_gp_rpc_msg(xdr_call_ctx, &msg); if (!decoded) { return EFAULT; } *xid = msg.xid; if (msg.header.type != GP_RPC_CALL) { *acc = GP_RPC_GARBAGE_ARGS; ret = EINVAL; goto done; } chdr = &msg.header.gp_rpc_msg_union_u.chdr; if (chdr->rpcvers != 2) { *rej = GP_RPC_RPC_MISMATCH; ret = EACCES; goto done; } if (chdr->prog != GSSPROXY) { *acc = GP_RPC_PROG_UNAVAIL; ret = EINVAL; goto done; } if (chdr->vers != GSSPROXYVERS) { *acc = GP_RPC_PROG_MISMATCH; ret = EINVAL; goto done; } if (chdr->proc < GSSX_PROC_MIN || chdr->proc > GSSX_PROC_MAX) { *acc = GP_RPC_PROC_UNAVAIL; ret = EINVAL; goto done; } if (chdr->cred.flavor != GP_RPC_AUTH_NONE) { *rej = GP_RPC_AUTH_ERROR; ret = EACCES; goto done; } *proc = chdr->proc; *acc = GP_RPC_SUCCESS; ret = 0; done: xdr_free((xdrproc_t)xdr_gp_rpc_msg, (char *)&msg); return ret; } static int gp_rpc_decode_call(XDR *xdr_call_ctx, uint32_t *xid, uint32_t *proc, union gp_rpc_arg *arg, gp_rpc_accept_status *acc, gp_rpc_reject_status *rej) { bool xdrok; int ret; ret = gp_rpc_decode_call_header(xdr_call_ctx, xid, proc, acc, rej); if (ret) { return ret; } xdrok = gp_xdr_set[*proc].arg_fn(xdr_call_ctx, (char *)arg); if (!xdrok) { *acc = GP_RPC_GARBAGE_ARGS; return EINVAL; } return 0; } static int gp_rpc_encode_reply_header(XDR *xdr_reply_ctx, uint32_t xid, int err, gp_rpc_accept_status acc, gp_rpc_reject_status rej) { gp_rpc_msg msg; gp_rpc_reply_header *rhdr; gp_rpc_accepted_reply *accepted; gp_rpc_rejected_reply *rejected; bool encoded; memset(&msg, 0, sizeof(gp_rpc_msg)); msg.xid = xid; msg.header.type = GP_RPC_REPLY; rhdr = &msg.header.gp_rpc_msg_union_u.rhdr; accepted = &rhdr->gp_rpc_reply_header_u.accepted; rejected = &rhdr->gp_rpc_reply_header_u.rejected; switch (err) { case EFAULT: return EFAULT; case EACCES: rhdr->status = GP_RPC_MSG_DENIED; rejected->status = rej; if (rej == GP_RPC_RPC_MISMATCH) { rejected->gp_rpc_rejected_reply_u.mismatch_info.high = 2; rejected->gp_rpc_rejected_reply_u.mismatch_info.low = 2; } else { rejected->gp_rpc_rejected_reply_u.status = GP_RPC_AUTH_FAILED; } break; case EINVAL: rhdr->status = GP_RPC_MSG_ACCEPTED; accepted->reply_data.status = acc; if (acc == GP_RPC_PROG_MISMATCH) { accepted->reply_data.gp_rpc_reply_union_u.mismatch_info.high = GSSPROXYVERS; accepted->reply_data.gp_rpc_reply_union_u.mismatch_info.low = GSSPROXYVERS; } break; case 0: rhdr->status = GP_RPC_MSG_ACCEPTED; accepted->reply_data.status = GP_RPC_SUCCESS; break; default: rhdr->status = GP_RPC_MSG_ACCEPTED; accepted->reply_data.status = GP_RPC_SYSTEM_ERR; break; } /* always reset xdr_ctx position, as this function may be called * multiple times in case errors occurred after the initial header * was created */ xdr_setpos(xdr_reply_ctx, 0); encoded = xdr_gp_rpc_msg(xdr_reply_ctx, &msg); if (!encoded) { return EFAULT; } return 0; } static int gp_rpc_encode_reply(XDR *xdr_reply_ctx, uint32_t xid, uint32_t proc, union gp_rpc_res *res, int err, gp_rpc_accept_status acc, gp_rpc_reject_status rej) { bool xdrok; int ret; ret = gp_rpc_encode_reply_header(xdr_reply_ctx, xid, err, acc, rej); if (ret != 0 || err != 0) { return ret; } xdrok = gp_xdr_set[proc].res_fn(xdr_reply_ctx, (char *)res); if (!xdrok) { return gp_rpc_encode_reply_header(xdr_reply_ctx, xid, EINVAL, GP_RPC_SYSTEM_ERR, GP_RPC_RPC_MISMATCH); } return 0; } static const char *gp_rpc_procname(uint32_t proc) { if (proc > GSSX_PROC_MAX) { return NULL; } return gp_xdr_set[proc].proc_name; } static int gp_rpc_execute(struct gp_call_ctx *gpcall, uint32_t proc, union gp_rpc_arg *arg, union gp_rpc_res *res) { GPDEBUG("gp_rpc_execute: executing %d (%s) for service \"%s\", euid: %d," "socket: %s\n", proc, gp_rpc_procname(proc), gpcall->service->name, gp_conn_get_uid(gpcall->connection), gpcall->service->socket); return gp_xdr_set[proc].exec_fn(gpcall, arg, res); } static int gp_rpc_return_buffer(XDR *xdr_reply_ctx, char *reply_buffer, uint8_t **outbuf, size_t *outlen) { unsigned int length; uint8_t *buffer; length = xdr_getpos(xdr_reply_ctx); buffer = malloc(length); if (!buffer) { return ENOMEM; } memcpy(buffer, reply_buffer, length); *outbuf = buffer; *outlen = length; return 0; } static void gp_rpc_free_xdrs(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res) { xdr_free(gp_xdr_set[proc].arg_fn, (char *)arg); xdr_free(gp_xdr_set[proc].res_fn, (char *)res); } int gp_rpc_process_call(struct gp_call_ctx *gpcall, uint8_t *inbuf, size_t inlen, uint8_t **outbuf, size_t *outlen) { XDR xdr_call_ctx; XDR xdr_reply_ctx; gp_rpc_accept_status acc = GP_RPC_SUCCESS; gp_rpc_reject_status rej = GP_RPC_RPC_MISMATCH; char reply_buffer[MAX_RPC_SIZE]; union gp_rpc_arg arg; union gp_rpc_res res; uint32_t xid = 0; uint32_t proc; int ret; memset(&arg, 0, sizeof(union gp_rpc_arg)); memset(&res, 0, sizeof(union gp_rpc_res)); proc = 0; xdrmem_create(&xdr_call_ctx, (caddr_t)inbuf, inlen, XDR_DECODE); xdrmem_create(&xdr_reply_ctx, reply_buffer, MAX_RPC_SIZE, XDR_ENCODE); /* decode request */ GPDEBUGN(3, "[status] Processing request [%p (%zu)]\n", inbuf, inlen); ret = gp_rpc_decode_call(&xdr_call_ctx, &xid, &proc, &arg, &acc, &rej); if (!ret) { /* execute request */ GPDEBUGN(3, "[status] Executing request %d (%s) from [%p (%zu)]\n", proc, gp_rpc_procname(proc), inbuf, inlen); ret = gp_rpc_execute(gpcall, proc, &arg, &res); if (ret) { acc = GP_RPC_SYSTEM_ERR; ret = EINVAL; } } /* encode reply */ ret = gp_rpc_encode_reply(&xdr_reply_ctx, xid, proc, &res, ret, acc, rej); if (ret == 0) { /* return encoded buffer */ ret = gp_rpc_return_buffer(&xdr_reply_ctx, reply_buffer, outbuf, outlen); GPDEBUGN(3, "[status] Returned buffer %d (%s) from [%p (%zu)]: " "[%p (%zu)]\n", proc, gp_rpc_procname(proc), inbuf, inlen, *outbuf, *outlen); } /* free resources */ gp_rpc_free_xdrs(proc, &arg, &res); xdr_destroy(&xdr_call_ctx); xdr_destroy(&xdr_reply_ctx); return ret; } GP_EXEC_UNUSED_FUNC(gp_get_call_context); GP_EXEC_UNUSED_FUNC(gp_export_cred); GP_EXEC_UNUSED_FUNC(gp_import_cred); GP_EXEC_UNUSED_FUNC(gp_store_cred); gssproxy-v0.8.2/src/gp_rpc_process.h0000644000174300017420000000270113456641744017166 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GP_RPC_PROCESS_H_ #define _GP_RPC_PROCESS_H_ #include "config.h" #include #include #include #include #include #include "gp_common.h" #include "gp_conv.h" #include "gp_export.h" #include "rpcgen/gss_proxy.h" #include "rpcgen/gp_rpc.h" #include "gp_rpc_creds.h" #include "gp_rpc_debug.h" struct gssproxy_ctx; struct gp_service; #define gp_exec_std_args struct gp_call_ctx *gpcall, \ union gp_rpc_arg *arg, \ union gp_rpc_res *res #define GP_EXEC_UNUSED_FUNC(name) \ int name(struct gp_call_ctx *gpcall UNUSED, \ union gp_rpc_arg *arg UNUSED, \ union gp_rpc_res *res UNUSED) \ { return 0; } int gp_indicate_mechs(gp_exec_std_args); int gp_get_call_context(gp_exec_std_args); int gp_import_and_canon_name(gp_exec_std_args); int gp_export_cred(gp_exec_std_args); int gp_import_cred(gp_exec_std_args); int gp_acquire_cred(gp_exec_std_args); int gp_store_cred(gp_exec_std_args); int gp_init_sec_context(gp_exec_std_args); int gp_accept_sec_context(gp_exec_std_args); int gp_release_handle(gp_exec_std_args); int gp_get_mic(gp_exec_std_args); int gp_verify_mic(gp_exec_std_args); int gp_wrap(gp_exec_std_args); int gp_unwrap(gp_exec_std_args); int gp_wrap_size_limit(gp_exec_std_args); #endif /* _GP_RPC_PROCESS_H_ */ gssproxy-v0.8.2/src/gp_rpc_release_handle.c0000644000174300017420000000225213456641744020437 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "gp_rpc_process.h" int gp_release_handle(struct gp_call_ctx *gpcall UNUSED, union gp_rpc_arg *arg, union gp_rpc_res *res) { struct gssx_arg_release_handle *rha; struct gssx_res_release_handle *rhr; uint32_t ret_maj = GSS_S_COMPLETE; uint32_t ret_min = 0; int ret; rha = &arg->release_handle; rhr = &res->release_handle; GPRPCDEBUG(gssx_arg_release_handle, rha); switch (rha->cred_handle.handle_type) { case GSSX_C_HANDLE_SEC_CTX: /* We do not need release for any security * context for now */ ret_maj = GSS_S_UNAVAILABLE; ret_min = 0; break; case GSSX_C_HANDLE_CRED: /* We do not need release for any creds now */ ret_maj = GSS_S_UNAVAILABLE; ret_min = 0; break; default: ret_maj = GSS_S_CALL_BAD_STRUCTURE; ret_min = 0; break; } ret = gp_conv_status_to_gssx(ret_maj, ret_min, GSS_C_NO_OID, &rhr->status); GPRPCDEBUG(gssx_res_release_handle, rhr); return ret; } gssproxy-v0.8.2/src/gp_rpc_unwrap.c0000644000174300017420000000650313456641744017023 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #include "gp_rpc_process.h" #include int gp_unwrap(struct gp_call_ctx *gpcall UNUSED, union gp_rpc_arg *arg, union gp_rpc_res *res) { gss_buffer_desc input_message_buffer = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_message_buffer = GSS_C_EMPTY_BUFFER; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; struct gssx_arg_unwrap *uwa; struct gssx_res_unwrap *uwr; uint32_t ret_maj; uint32_t ret_min; int ret; int exp_ctx_type; int conf_state = 0; gss_qop_t qop_state = 0; uwa = &arg->unwrap; uwr = &res->unwrap; GPRPCDEBUG(gssx_arg_unwrap, uwa); exp_ctx_type = gp_get_exported_context_type(&uwa->call_ctx); if (exp_ctx_type == -1) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } ret_maj = gp_import_gssx_to_ctx_id(&ret_min, 0, &uwa->context_handle, &context_handle); if (ret_maj) { goto done; } /* apparently it is ok to send an empty message, in that case we dont need * to bother to do any conversion - gd */ if ((uwa->token_buffer.token_buffer_len > 0) && (uwa->token_buffer.token_buffer_val != NULL)) { gp_conv_gssx_to_buffer(&uwa->token_buffer.token_buffer_val[0], &input_message_buffer); } ret_maj = gss_unwrap(&ret_min, context_handle, &input_message_buffer, &output_message_buffer, &conf_state, &qop_state); if (ret_maj) { goto done; } uwr->context_handle = calloc(1, sizeof(gssx_ctx)); if (!uwr->context_handle) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } ret_maj = gp_export_ctx_id_to_gssx(&ret_min, exp_ctx_type, GSS_C_NO_OID, &context_handle, uwr->context_handle); if (ret_maj) { goto done; } uwr->qop_state = malloc(sizeof(gssx_qop)); if (!uwr->qop_state) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } *uwr->qop_state = uwa->qop_state; uwr->conf_state = malloc(sizeof(bool_t)); if (!uwr->conf_state) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } *uwr->conf_state = conf_state; uwr->message_buffer.message_buffer_val = calloc(1, sizeof(gssx_buffer)); if (!uwr->message_buffer.message_buffer_val) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } uwr->message_buffer.message_buffer_len = 1; ret = gp_conv_buffer_to_gssx(&output_message_buffer, &uwr->message_buffer.message_buffer_val[0]); if (ret ) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret_maj = GSS_S_COMPLETE; ret_min = 0; done: ret = gp_conv_status_to_gssx(ret_maj, ret_min, GSS_C_NO_OID, &uwr->status); GPRPCDEBUG(gssx_res_unwrap, uwr); gss_release_buffer(&ret_min, &output_message_buffer); return ret; } gssproxy-v0.8.2/src/gp_rpc_verify_mic.c0000644000174300017420000000434113456641744017641 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #include "gp_rpc_process.h" #include int gp_verify_mic(struct gp_call_ctx *gpcall UNUSED, union gp_rpc_arg *arg, union gp_rpc_res *res) { gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; struct gssx_arg_verify_mic *vma; struct gssx_res_verify_mic *vmr; gss_buffer_desc message_buffer; gss_buffer_desc token_buffer; gss_qop_t qop_state; int exp_ctx_type; uint32_t ret_maj; uint32_t ret_min; int ret; vma = &arg->verify_mic; vmr = &res->verify_mic; GPRPCDEBUG(gssx_arg_verify_mic, vma); exp_ctx_type = gp_get_exported_context_type(&vma->call_ctx); if (exp_ctx_type == -1) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } ret_maj = gp_import_gssx_to_ctx_id(&ret_min, 0, &vma->context_handle, &context_handle); if (ret_maj) { goto done; } gp_conv_gssx_to_buffer(&vma->message_buffer, &message_buffer); gp_conv_gssx_to_buffer(&vma->token_buffer, &token_buffer); ret_maj = gss_verify_mic(&ret_min, context_handle, &message_buffer, &token_buffer, &qop_state); if (ret_maj) { goto done; } vmr->context_handle = calloc(1, sizeof(gssx_ctx)); if (!vmr->context_handle) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } ret_maj = gp_export_ctx_id_to_gssx(&ret_min, exp_ctx_type, GSS_C_NO_OID, &context_handle, vmr->context_handle); if (ret_maj) { goto done; } vmr->qop_state = calloc(1, sizeof(gssx_qop)); if (!vmr->qop_state) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } *vmr->qop_state = qop_state; ret_maj = GSS_S_COMPLETE; ret_min = 0; done: ret = gp_conv_status_to_gssx(ret_maj, ret_min, GSS_C_NO_OID, &vmr->status); GPRPCDEBUG(gssx_res_verify_mic, vmr); return ret; } gssproxy-v0.8.2/src/gp_rpc_wrap.c0000644000174300017420000000631213456641744016456 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #include "gp_rpc_process.h" #include int gp_wrap(struct gp_call_ctx *gpcall UNUSED, union gp_rpc_arg *arg, union gp_rpc_res *res) { gss_buffer_desc input_message_buffer = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_message_buffer = GSS_C_EMPTY_BUFFER; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; struct gssx_arg_wrap *wa; struct gssx_res_wrap *wr; uint32_t ret_maj; uint32_t ret_min; int ret; int exp_ctx_type; int conf_state = 0; wa = &arg->wrap; wr = &res->wrap; GPRPCDEBUG(gssx_arg_wrap, wa); exp_ctx_type = gp_get_exported_context_type(&wa->call_ctx); if (exp_ctx_type == -1) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } ret_maj = gp_import_gssx_to_ctx_id(&ret_min, 0, &wa->context_handle, &context_handle); if (ret_maj) { goto done; } /* apparently it is ok to send an empty message, in that case we dont need * to bother to do any conversion - gd */ if ((wa->message_buffer.message_buffer_len > 0) && (wa->message_buffer.message_buffer_val != NULL)) { gp_conv_gssx_to_buffer(&wa->message_buffer.message_buffer_val[0], &input_message_buffer); } ret_maj = gss_wrap(&ret_min, context_handle, wa->conf_req, wa->qop_state, &input_message_buffer, &conf_state, &output_message_buffer); if (ret_maj) { goto done; } wr->context_handle = calloc(1, sizeof(gssx_ctx)); if (!wr->context_handle) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } ret_maj = gp_export_ctx_id_to_gssx(&ret_min, exp_ctx_type, GSS_C_NO_OID, &context_handle, wr->context_handle); if (ret_maj) { goto done; } wr->qop_state = malloc(sizeof(gssx_qop)); if (!wr->qop_state) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } *wr->qop_state = wa->qop_state; wr->conf_state = malloc(sizeof(bool_t)); if (!wr->conf_state) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } *wr->conf_state = conf_state; wr->token_buffer.token_buffer_val = calloc(1, sizeof(gssx_buffer)); if (!wr->token_buffer.token_buffer_val) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } wr->token_buffer.token_buffer_len = 1; ret = gp_conv_buffer_to_gssx(&output_message_buffer, &wr->token_buffer.token_buffer_val[0]); if (ret ) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } ret_maj = GSS_S_COMPLETE; ret_min = 0; done: ret = gp_conv_status_to_gssx(ret_maj, ret_min, GSS_C_NO_OID, &wr->status); GPRPCDEBUG(gssx_res_wrap, wr); gss_release_buffer(&ret_min, &output_message_buffer); return ret; } gssproxy-v0.8.2/src/gp_rpc_wrap_size_limit.c0000644000174300017420000000324013456641744020703 0ustar gitgit00000000000000/* Copyright (C) 2011,2012 the GSS-PROXY contributors, see COPYING for license */ #include "gp_rpc_process.h" #include int gp_wrap_size_limit(struct gp_call_ctx *gpcall UNUSED, union gp_rpc_arg *arg, union gp_rpc_res *res) { gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; struct gssx_arg_wrap_size_limit *wsla; struct gssx_res_wrap_size_limit *wslr; uint32_t ret_maj; uint32_t ret_min; int ret; int exp_ctx_type; OM_uint32 max_size; wsla = &arg->wrap_size_limit; wslr = &res->wrap_size_limit; GPRPCDEBUG(gssx_arg_wrap_size_limit, wsla); exp_ctx_type = gp_get_exported_context_type(&wsla->call_ctx); if (exp_ctx_type == -1) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } ret_maj = gp_import_gssx_to_ctx_id(&ret_min, 0, &wsla->context_handle, &context_handle); if (ret_maj) { goto done; } ret_maj = gss_wrap_size_limit(&ret_min, context_handle, wsla->conf_req, wsla->qop_state, wsla->req_output_size, &max_size); if (ret_maj) { goto done; } wslr->max_input_size = max_size; ret_maj = GSS_S_COMPLETE; ret_min = 0; done: ret = gp_conv_status_to_gssx(ret_maj, ret_min, GSS_C_NO_OID, &wslr->status); GPRPCDEBUG(gssx_res_wrap_size_limit, wslr); return ret; } gssproxy-v0.8.2/src/gp_selinux.h0000644000174300017420000000256613456641744016344 0ustar gitgit00000000000000/* Copyright (C) 2013 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GP_SELINUX_H_ #define _GP_SELINUX_H_ #ifdef HAVE_SELINUX #include #define SELINUX_CTX context_t #include #define SEC_CTX security_context_t #define SELINUX_context_new context_new #define SELINUX_context_free context_free #define SELINUX_context_str context_str #define SELINUX_context_type_get context_type_get #define SELINUX_context_user_get context_user_get #define SELINUX_context_role_get context_role_get #define SELINUX_context_range_get context_range_get #define SELINUX_getpeercon getpeercon #define SELINUX_freecon freecon #else /* not HAVE_SELINUX */ #define SELINUX_CTX void * #define SEC_CTX void * #define SELINUX_context_new(x) NULL #define SELINUX_context_free(x) (x) = NULL #define SELINUX_context_dummy_get(x) "" #define SELINUX_context_str SELINUX_context_dummy_get #define SELINUX_context_type_get SELINUX_context_dummy_get #define SELINUX_context_user_get SELINUX_context_dummy_get #define SELINUX_context_role_get SELINUX_context_dummy_get #define SELINUX_context_range_get SELINUX_context_dummy_get #include #define SELINUX_getpeercon(x, y) -1; do { \ *(y) = NULL; \ errno = ENOTSUP; \ } while(0) #define SELINUX_freecon(x) (x) = NULL #endif /* done HAVE_SELINUX */ #endif /*_GP_SELINUX_H_ */ gssproxy-v0.8.2/src/gp_socket.c0000644000174300017420000003611313456641744016133 0ustar gitgit00000000000000/* Copyright (C) 2011,2015 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include "gp_proxy.h" #include "gp_creds.h" #include "gp_selinux.h" #include #include #include #include #include #include #include #include #include #include #define FRAGMENT_BIT (1 << 31) struct unix_sock_conn { int sd; struct sockaddr_un sock_addr; socklen_t sock_addr_len; }; struct gp_conn { struct gp_sock_ctx *sock_ctx; struct unix_sock_conn us; struct gp_creds creds; SELINUX_CTX selinux_ctx; char *program; }; struct gp_buffer { struct gp_conn *conn; uint8_t *data; size_t size; size_t pos; }; bool gp_selinux_ctx_equal(SELINUX_CTX ctx1, SELINUX_CTX ctx2) { const char *ra, *rb; if (ctx1 == ctx2) { return true; } if (ctx1 == NULL || ctx2 == NULL) { return false; } if (strcmp(SELINUX_context_user_get(ctx1), SELINUX_context_user_get(ctx2)) != 0) { return false; } if (strcmp(SELINUX_context_role_get(ctx1), SELINUX_context_role_get(ctx2)) != 0) { return false; } if (strcmp(SELINUX_context_type_get(ctx1), SELINUX_context_type_get(ctx2)) != 0) { return false; } ra = SELINUX_context_range_get(ctx1); rb = SELINUX_context_range_get(ctx2); if (ra && rb && (strcmp(ra, rb) != 0)) { return false; } return true; } bool gp_conn_check_selinux(struct gp_conn *conn, SELINUX_CTX ctx) { if (ctx == NULL) { return true; } if (!(conn->creds.type & CRED_TYPE_SELINUX) || (conn->selinux_ctx == NULL)) { return false; } return gp_selinux_ctx_equal(ctx, conn->selinux_ctx); } struct gp_creds *gp_conn_get_creds(struct gp_conn *conn) { return &conn->creds; } uid_t gp_conn_get_uid(struct gp_conn *conn) { return conn->creds.ucred.uid; } const char *gp_conn_get_socket(struct gp_conn *conn) { return conn->sock_ctx->socket; } int gp_conn_get_cid(struct gp_conn *conn) { return conn->us.sd; } const char *gp_conn_get_program(struct gp_conn *conn) { return conn->program; } void gp_conn_free(struct gp_conn *conn) { if (!conn) return; if (conn->us.sd != -1) { close(conn->us.sd); } free(conn->program); SELINUX_context_free(conn->selinux_ctx); free(conn); } static void gp_buffer_free(struct gp_buffer *wbuf) { free(wbuf->data); free(wbuf); } static int set_status_flags(int fd, int flags) { int cur; int ret; cur = fcntl(fd, F_GETFL, 0); cur |= flags; ret = fcntl(fd, F_SETFL, cur); if (ret == -1) { return errno; } return 0; } static int set_fd_flags(int fd, int flags) { int cur; int ret; cur = fcntl(fd, F_GETFD, 0); cur |= flags; ret = fcntl(fd, F_SETFD, cur); if (ret == -1) { return errno; } return 0; } void free_unix_socket(verto_ctx *ctx UNUSED, verto_ev *ev) { struct gp_sock_ctx *sock_ctx = NULL; sock_ctx = verto_get_private(ev); free(sock_ctx); } struct gp_sock_ctx *init_unix_socket(struct gssproxy_ctx *gpctx, const char *file_name) { struct sockaddr_un addr = {0}; struct gp_sock_ctx *sock_ctx; mode_t old_mode; int ret = 0; int fd = -1; sock_ctx = calloc(1, sizeof(struct gp_sock_ctx)); if (!sock_ctx) { return NULL; } /* can't bind if an old socket is around */ unlink(file_name); /* socket should be r/w by anyone */ old_mode = umask(0111); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, file_name, sizeof(addr.sun_path)-1); addr.sun_path[sizeof(addr.sun_path)-1] = '\0'; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { ret = errno; GPDEBUG("Failed to init socket! (%d: %s)\n", ret, gp_strerror(ret)); goto done; } ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); if (ret == -1) { ret = errno; GPDEBUG("Failed to bind socket %s! (%d: %s)\n", addr.sun_path, ret, gp_strerror(ret)); goto done; } ret = listen(fd, 10); if (ret == -1) { ret = errno; GPDEBUG("Failed to listen! (%d: %s)\n", ret, gp_strerror(ret)); goto done; } ret = set_status_flags(fd, O_NONBLOCK); if (ret != 0) { GPDEBUG("Failed to set O_NONBLOCK on %d!\n", fd); goto done; } ret = set_fd_flags(fd, FD_CLOEXEC); if (ret != 0) { GPDEBUG("Failed to set FD_CLOEXEC on %d!\n", fd); goto done; } done: if (ret) { GPERROR("Failed to create Unix Socket! (%d:%s)", ret, gp_strerror(ret)); if (fd != -1) { close(fd); fd = -1; } safefree(sock_ctx); } else { sock_ctx->gpctx = gpctx; sock_ctx->socket = file_name; sock_ctx->fd = fd; } umask(old_mode); return sock_ctx; } static int get_peercred(int fd, struct gp_conn *conn) { SEC_CTX secctx; socklen_t len; int ret; len = sizeof(struct ucred); ret = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &conn->creds.ucred, &len); if (ret == -1) { ret = errno; GPDEBUG("Failed to get SO_PEERCRED options! (%d:%s)\n", ret, gp_strerror(ret)); return ret; } if (len != sizeof(struct ucred)) { return EIO; } conn->creds.type |= CRED_TYPE_UNIX; ret = SELINUX_getpeercon(fd, &secctx); if (ret == 0) { conn->creds.type |= CRED_TYPE_SELINUX; conn->selinux_ctx = SELINUX_context_new(secctx); SELINUX_freecon(secctx); } else { ret = errno; GPDEBUG("Failed to get peer's SELinux context (%d:%s)\n", ret, gp_strerror(ret)); /* consider thisnot fatal, selinux may be disabled */ } return 0; } static char *get_program(pid_t pid) { char procfile[21]; char *program; int ret, e; struct stat sb; ret = snprintf(procfile, 20, "/proc/%u/exe", pid); if (ret < 0) { e = errno; GPERROR("Internal error in snprintf: %d (%s)", e, strerror(e)); return NULL; } procfile[ret] = '\0'; program = realpath(procfile, NULL); if (program) { return program; } e = errno; if (e != ENOENT) { GPERROR("Unexpected failure in realpath: %d (%s)", e, strerror(e)); return NULL; } /* check if /proc is even around */ procfile[ret - 4] = '\0'; ret = stat(procfile, &sb); /* complains if we give it NULL */ e = errno; if (ret == -1 && e == ENOENT) { /* kernel thread */ return NULL; } GPERROR("Problem with /proc; program name matching won't work: %d (%s)", e, strerror(e)); return NULL; } static void gp_socket_read(verto_ctx *vctx, verto_ev *ev); static void gp_socket_schedule_read(verto_ctx *vctx, struct gp_buffer *rbuf) { verto_ev *ev; ev = verto_add_io(vctx, VERTO_EV_FLAG_IO_READ, gp_socket_read, rbuf->conn->us.sd); if (!ev) { GPDEBUG("Failed to add io/read event!\n"); gp_conn_free(rbuf->conn); gp_buffer_free(rbuf); return; } verto_set_private(ev, rbuf, NULL); } static void gp_setup_reader(verto_ctx *vctx, struct gp_conn *conn) { struct gp_buffer *buf; /* create initial read buffer */ buf = calloc(1, sizeof(struct gp_buffer)); if (!buf) { gp_conn_free(conn); return; } buf->conn = conn; gp_socket_schedule_read(vctx, buf); } static void gp_socket_read(verto_ctx *vctx, verto_ev *ev) { struct gp_buffer *rbuf; uint32_t size; bool header = false; ssize_t rn; int ret; int fd; fd = verto_get_fd(ev); rbuf = verto_get_private(ev); if (rbuf->data == NULL) { header = true; /* new connection, need to read length first */ rn = read(fd, &size, sizeof(uint32_t)); if (rn == -1) { if (errno == EAGAIN || errno == EINTR) { /* spin again */ ret = EAGAIN; } else { ret = EIO; } goto done; } if (rn != sizeof(uint32_t)) { /* client closed, * or we didn't get even 4 bytes, * close conn, not worth trying 1 byte reads at this time */ ret = EIO; goto done; } /* allocate buffer for receiving data */ rbuf->size = ntohl(size); /* FIXME: need to support multiple fragments */ /* for now just make sure we have the last fragment bit * then remove it */ if (rbuf->size & FRAGMENT_BIT) { rbuf->size &= ~FRAGMENT_BIT; } else { ret = EIO; goto done; } if (rbuf->size > MAX_RPC_SIZE) { /* req too big close conn. */ ret = EIO; goto done; } rbuf->data = malloc(rbuf->size); if (!rbuf->data) { ret = ENOMEM; goto done; } } errno = 0; rn = read(fd, rbuf->data + rbuf->pos, rbuf->size - rbuf->pos); if (rn == -1) { if (errno == EAGAIN || errno == EINTR) { /* spin again */ ret = EAGAIN; } else { ret = EIO; } goto done; } if (rn == 0) { if (!header) { /* client closed before the buffer was fully read */ ret = EIO; } else { ret = EAGAIN; } goto done; } rbuf->pos += rn; if (rbuf->pos == rbuf->size) { /* got all data, hand over packet */ ret = gp_query_new(rbuf->conn->sock_ctx->gpctx->workers, rbuf->conn, rbuf->data, rbuf->size); if (ret != 0) { /* internal error, not much we can do */ goto done; } /* we successfully handed over the data */ rbuf->data = NULL; gp_buffer_free(rbuf); return; } ret = EAGAIN; done: switch (ret) { case EAGAIN: gp_socket_schedule_read(vctx, rbuf); return; default: gp_conn_free(rbuf->conn); gp_buffer_free(rbuf); } } static void gp_socket_write(verto_ctx *vctx, verto_ev *ev); static void gp_socket_schedule_write(verto_ctx *vctx, struct gp_buffer *wbuf) { verto_ev *ev; ev = verto_add_io(vctx, VERTO_EV_FLAG_IO_WRITE, gp_socket_write, wbuf->conn->us.sd); if (!ev) { GPDEBUG("Failed to add io/write event!\n"); gp_conn_free(wbuf->conn); gp_buffer_free(wbuf); return; } verto_set_private(ev, wbuf, NULL); } void gp_socket_send_data(verto_ctx *vctx, struct gp_conn *conn, uint8_t *buffer, size_t buflen) { struct gp_buffer *wbuf; wbuf = calloc(1, sizeof(struct gp_buffer)); if (!wbuf) { GPDEBUGN(3, "[status] OOM in gp_socket_send_data: %p (%zu)\n", buffer, buflen); /* too bad, must kill the client connection now */ gp_conn_free(conn); return; } wbuf->conn = conn; wbuf->data = buffer; wbuf->size = buflen; gp_socket_schedule_write(vctx, wbuf); } static void gp_socket_write(verto_ctx *vctx, verto_ev *ev) { struct gp_buffer *wbuf; struct iovec iov[2]; uint32_t size; ssize_t wn; int vecs; int fd; fd = verto_get_fd(ev); wbuf = verto_get_private(ev); vecs = 0; GPDEBUGN(3, "[status] Sending data: %p (%zu)\n", wbuf->data, wbuf->size); if (wbuf->pos == 0) { /* first write, send the buffer size as packet header */ size = wbuf->size | FRAGMENT_BIT; size = htonl(size); iov[0].iov_base = &size; iov[0].iov_len = sizeof(size); vecs = 1; } iov[vecs].iov_base = wbuf->data + wbuf->pos; iov[vecs].iov_len = wbuf->size - wbuf->pos; vecs++; errno = 0; wn = writev(fd, iov, vecs); if (wn == -1) { if (errno == EAGAIN || errno == EINTR) { /* try again later */ gp_socket_schedule_write(vctx, wbuf); } else { /* error on socket, close and release it */ GPDEBUGN(3, "[status] Error %d in gp_socket_write on writing for " "[%p (%zu:%zu)]\n", errno, wbuf->data, wbuf->pos, wbuf->size); gp_conn_free(wbuf->conn); gp_buffer_free(wbuf); } return; } if (vecs == 2) { if (wn < (ssize_t) sizeof(size)) { /* don't bother trying to handle sockets that can't * buffer even 4 bytes */ GPDEBUGN(3, "[status] Sending data [%p (%zu)]: failed with short " "write of %d\n", wbuf->data, wbuf->size, wn); gp_conn_free(wbuf->conn); gp_buffer_free(wbuf); return; } wn -= sizeof(size); } GPDEBUGN(3, "[status] Sending data [%p (%zu)]: successful write of %d\n", wbuf->data, wbuf->size, wn); wbuf->pos += wn; if (wbuf->size > wbuf->pos) { /* short write, reschedule */ gp_socket_schedule_write(vctx, wbuf); } else { /* now setup again the reader */ gp_setup_reader(vctx, wbuf->conn); /* all done, free write context */ gp_buffer_free(wbuf); } } void accept_sock_conn(verto_ctx *vctx, verto_ev *ev) { struct gp_conn *conn = NULL; int listen_fd; int fd = -1; int ret; conn = calloc(1, sizeof(struct gp_conn)); if (!conn) { ret = ENOMEM; goto done; } conn->sock_ctx = verto_get_private(ev); conn->us.sd = -1; listen_fd = verto_get_fd(ev); fd = accept(listen_fd, (struct sockaddr *)&conn->us.sock_addr, &conn->us.sock_addr_len); if (fd == -1) { ret = errno; if (ret == EINTR) { /* let the event loop retry later */ return; } goto done; } conn->us.sd = fd; ret = set_status_flags(fd, O_NONBLOCK); if (ret) { GPDEBUG("Failed to set O_NONBLOCK on %d!\n", fd); goto done; } ret = set_fd_flags(fd, FD_CLOEXEC); if (ret) { GPDEBUG("Failed to set FD_CLOEXEC on %d!\n", fd); goto done; } ret = get_peercred(fd, conn); if (ret) { goto done; } conn->program = get_program(conn->creds.ucred.pid); GPDEBUG("Client "); if (conn->program) { GPDEBUG("(%s) ", conn->program); } GPDEBUG(" connected (fd = %d)", fd); if (conn->creds.type & CRED_TYPE_UNIX) { GPDEBUG(" (pid = %d) (uid = %d) (gid = %d)", conn->creds.ucred.pid, conn->creds.ucred.uid, conn->creds.ucred.gid); } if (conn->creds.type & CRED_TYPE_SELINUX) { GPDEBUG(" (context = %s)", SELINUX_context_str(conn->selinux_ctx)); } GPDEBUG("\n"); gp_setup_reader(vctx, conn); ret = 0; done: if (ret) { GPERROR("Error connecting client: (%d:%s)", ret, gp_strerror(ret)); gp_conn_free(conn); } } gssproxy-v0.8.2/src/gp_util.c0000644000174300017420000001127213456641744015617 0ustar gitgit00000000000000/* Copyright (C) 2013 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include #include #include #include #include #include "gp_common.h" bool gp_same(const char *a, const char *b) { if (a == b || (a && b && strcmp(a, b) == 0)) { return true; } return false; } bool gp_boolean_is_true(const char *s) { if (strcasecmp(s, "1") == 0 || strcasecmp(s, "on") == 0 || strcasecmp(s, "true") == 0 || strcasecmp(s, "yes") == 0) { return true; } return false; } char *gp_getenv(const char *name) { #if HAVE_SECURE_GETENV return secure_getenv(name); #elif HAVE___SECURE_GETENV return __secure_getenv(name); #else #include #include #warning secure_getenv not available, falling back to poorman emulation if ((getuid() == geteuid()) && (getgid() == getegid())) { return getenv(name); } return NULL; #endif } /* NOTE: because strerror_r() is such a mess with glibc, we need to do some * magic checking to find out what function prototype is being used of the * two incompatible ones, and pray it doesn't change in the future. * On top of that to avoid impacting the current code too much we've got to use * thread-local storage to hold a buffer. * gp_strerror() is basically a thread-safe version of strerror() that can * never fail. */ const char gp_internal_err[] = "Internal strerror_r() error."; #define MAX_GP_STRERROR 1024 char *gp_strerror(int errnum) { static __thread char buf[MAX_GP_STRERROR]; int saved_errno = errno; #if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) /* XSI version */ int ret; ret = strerror_r(errnum, buf, MAX_GP_STRERROR); if (ret == -1) ret = errno; switch (ret) { case 0: break; case EINVAL: ret = snprintf(buf, MAX_GP_STRERROR, "Unknown error code: %d", errnum); if (ret > 0) break; /* fallthrough */ default: ret = snprintf(buf, MAX_GP_STRERROR, "Internal error describing error code: %d", errnum); if (ret > 0) break; memset(buf, 0, MAX_GP_STRERROR); strncpy(buf, gp_internal_err, MAX_GP_STRERROR); buf[MAX_GP_STRERROR -1] = '\0'; } #else /* GNU-specific version */ char *ret; ret = strerror_r(errnum, buf, MAX_GP_STRERROR); if (ret == NULL) { memset(buf, 0, MAX_GP_STRERROR); strncpy(buf, gp_internal_err, MAX_GP_STRERROR); buf[MAX_GP_STRERROR -1] = '\0'; } else if (ret != buf) { memset(buf, 0, MAX_GP_STRERROR); strncpy(buf, ret, MAX_GP_STRERROR); buf[MAX_GP_STRERROR -1] = '\0'; } #endif errno = saved_errno; return buf; } ssize_t gp_safe_read(int fd, void *buf, size_t count) { char *b = (char *)buf; size_t len = 0; ssize_t ret; do { ret = read(fd, &b[len], count - len); if (ret == -1) { if (errno == EINTR) continue; return ret; } if (ret == 0) break; /* EOF */ len += ret; } while (count > len); return len; } ssize_t gp_safe_write(int fd, const void *buf, size_t count) { const char *b = (const char *)buf; size_t len = 0; ssize_t ret; do { ret = write(fd, &b[len], count - len); if (ret == -1) { if (errno == EINTR) continue; return ret; } if (ret == 0) break; /* EOF */ len += ret; } while (count > len); return len; } uint32_t gp_add_option(gssx_option **options_val, u_int *options_len, const void *option, size_t option_len, const void *value, size_t value_len) { gssx_option opt = { 0 }; gssx_option *out; uint32_t ret; opt.option.octet_string_val = malloc(option_len); if (!opt.option.octet_string_val) { ret = ENOMEM; goto done; } memcpy(opt.option.octet_string_val, option, option_len); opt.option.octet_string_len = option_len; if (value_len != 0) { opt.value.octet_string_val = malloc(value_len); if (!opt.value.octet_string_val) { ret = ENOMEM; goto done; } memcpy(opt.value.octet_string_val, value, value_len); opt.value.octet_string_len = value_len; } out = realloc(*options_val, (*options_len + 1) * sizeof(gssx_option)); if (!out) { ret = ENOMEM; goto done; } out[*options_len] = opt; *options_val = out; (*options_len)++; ret = 0; done: if (ret) { xdr_free((xdrproc_t)xdr_gssx_option, (char *)&opt); } return ret; } gssproxy-v0.8.2/src/gp_workers.c0000644000174300017420000002544513456641744016345 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include #include #include #include #include #include #include "gp_proxy.h" #define DEFAULT_WORKER_THREADS_NUM 5 #define GP_QUERY_IN 0 #define GP_QUERY_OUT 1 #define GP_QUERY_ERR 2 struct gp_query { struct gp_query *next; struct gp_conn *conn; uint8_t *buffer; size_t buflen; int status; }; struct gp_thread { struct gp_thread *prev; struct gp_thread *next; struct gp_workers *pool; pthread_t tid; struct gp_query *query; pthread_mutex_t cond_mutex; pthread_cond_t cond_wakeup; }; struct gp_workers { pthread_mutex_t lock; struct gssproxy_ctx *gpctx; bool shutdown; struct gp_query *wait_list; struct gp_query *reply_list; struct gp_thread *free_list; struct gp_thread *busy_list; int num_threads; int sig_pipe[2]; }; static void *gp_worker_main(void *pvt); static void gp_handle_query(struct gp_workers *w, struct gp_query *q); static void gp_handle_reply(verto_ctx *vctx, verto_ev *ev); /** DISPATCHER FUNCTIONS **/ int gp_workers_init(struct gssproxy_ctx *gpctx) { struct gp_workers *w; struct gp_thread *t; pthread_attr_t attr; verto_ev *ev; int vflags; int ret; int i; w = calloc(1, sizeof(struct gp_workers)); if (!w) { return ENOMEM; } w->gpctx = gpctx; /* init global queue mutex */ ret = pthread_mutex_init(&w->lock, NULL); if (ret) { free(w); return ENOMEM; } if (gpctx->config->num_workers > 0) { w->num_threads = gpctx->config->num_workers; } else { w->num_threads = DEFAULT_WORKER_THREADS_NUM; } /* make thread joinable (portability) */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); /* init all workers */ for (i = 0; i < w->num_threads; i++) { t = calloc(1, sizeof(struct gp_thread)); if (!t) { ret = -1; goto done; } t->pool = w; ret = pthread_cond_init(&t->cond_wakeup, NULL); if (ret) { free(t); goto done; } ret = pthread_mutex_init(&t->cond_mutex, NULL); if (ret) { free(t); goto done; } ret = pthread_create(&t->tid, &attr, gp_worker_main, t); if (ret) { free(t); goto done; } LIST_ADD(w->free_list, t); } /* add wakeup pipe, so that threads can hand back replies to the * dispatcher */ ret = pipe2(w->sig_pipe, O_NONBLOCK | O_CLOEXEC); if (ret == -1) { goto done; } vflags = VERTO_EV_FLAG_PERSIST | VERTO_EV_FLAG_IO_READ; ev = verto_add_io(gpctx->vctx, vflags, gp_handle_reply, w->sig_pipe[0]); if (!ev) { ret = -1; goto done; } verto_set_private(ev, w, NULL); gpctx->workers = w; ret = 0; done: if (ret) { gp_workers_free(w); } return ret; } void gp_workers_free(struct gp_workers *w) { struct gp_thread *t; void *retval; /* ======> POOL LOCK */ pthread_mutex_lock(&w->lock); w->shutdown = true; /* <====== POOL LOCK */ pthread_mutex_unlock(&w->lock); /* we do not run the following operations within * the lock, or deadlocks may arise for threads * that are just finishing doing some work */ /* we guarantee nobody is touching these lists by * preventing workers from touching the free/busy * lists when a 'shutdown' is in progress */ while (w->free_list) { /* pick threads one by one */ t = w->free_list; LIST_DEL(w->free_list, t); /* wake up threads, then join them */ /* ======> COND_MUTEX */ pthread_mutex_lock(&t->cond_mutex); pthread_cond_signal(&t->cond_wakeup); /* <====== COND_MUTEX */ pthread_mutex_unlock(&t->cond_mutex); pthread_join(t->tid, &retval); pthread_mutex_destroy(&t->cond_mutex); pthread_cond_destroy(&t->cond_wakeup); free(t); } /* do the same with the busy list */ while (w->busy_list) { /* pick threads one by one */ t = w->busy_list; LIST_DEL(w->free_list, t); /* wake up threads, then join them */ /* ======> COND_MUTEX */ pthread_mutex_lock(&t->cond_mutex); pthread_cond_signal(&t->cond_wakeup); /* <====== COND_MUTEX */ pthread_mutex_unlock(&t->cond_mutex); pthread_join(t->tid, &retval); pthread_mutex_destroy(&t->cond_mutex); pthread_cond_destroy(&t->cond_wakeup); free(t); } close(w->sig_pipe[0]); close(w->sig_pipe[1]); pthread_mutex_destroy(&w->lock); free(w); } static void gp_query_assign(struct gp_workers *w, struct gp_query *q) { struct gp_thread *t = NULL; /* then either find a free thread or queue in the wait list */ /* ======> POOL LOCK */ pthread_mutex_lock(&w->lock); if (w->free_list) { t = w->free_list; LIST_DEL(w->free_list, t); LIST_ADD(w->busy_list, t); } /* <====== POOL LOCK */ pthread_mutex_unlock(&w->lock); if (t) { /* found free thread, assign work */ /* ======> COND_MUTEX */ pthread_mutex_lock(&t->cond_mutex); /* hand over the query */ t->query = q; pthread_cond_signal(&t->cond_wakeup); /* <====== COND_MUTEX */ pthread_mutex_unlock(&t->cond_mutex); } else { /* all threads are busy, store in wait list */ /* only the dispatcher handles wait_list * so we do not need to lock around it */ q->next = w->wait_list; w->wait_list = q; } } static void gp_query_free(struct gp_query *q, bool free_buffer) { if (!q) { return; } if (free_buffer) { free(q->buffer); } free(q); } int gp_query_new(struct gp_workers *w, struct gp_conn *conn, uint8_t *buffer, size_t buflen) { struct gp_query *q; /* create query struct */ q = calloc(1, sizeof(struct gp_query)); if (!q) { return ENOMEM; } q->conn = conn; q->buffer = buffer; q->buflen = buflen; gp_query_assign(w, q); return 0; } static void gp_handle_reply(verto_ctx *vctx, verto_ev *ev) { struct gp_workers *w; struct gp_query *q = NULL; char dummy; int ret; w = verto_get_private(ev); /* first read out the dummy so the pipe doesn't get clogged */ ret = read(w->sig_pipe[0], &dummy, 1); if (ret) { /* ignore errors */ } /* grab a query reply if any */ if (w->reply_list) { /* ======> POOL LOCK */ pthread_mutex_lock(&w->lock); if (w->reply_list != NULL) { q = w->reply_list; w->reply_list = q->next; } /* <====== POOL LOCK */ pthread_mutex_unlock(&w->lock); } if (q) { switch (q->status) { case GP_QUERY_IN: /* ?! fallback and kill client conn */ case GP_QUERY_ERR: GPDEBUGN(3, "[status] Handling query error, terminating CID %d.\n", gp_conn_get_cid(q->conn)); gp_conn_free(q->conn); gp_query_free(q, true); break; case GP_QUERY_OUT: GPDEBUGN(3, "[status] Handling query reply: %p (%zu)\n", q->buffer, q->buflen); gp_socket_send_data(vctx, q->conn, q->buffer, q->buflen); gp_query_free(q, false); break; } } /* while we are at it, check if there is anything in the wait list * we need to process, as one thread just got free :-) */ q = NULL; if (w->wait_list) { /* only the dispatcher handles wait_list * so we do not need to lock around it */ if (w->wait_list) { q = w->wait_list; w->wait_list = q->next; q->next = NULL; } } if (q) { gp_query_assign(w, q); } } /** WORKER THREADS **/ static void *gp_worker_main(void *pvt) { struct gp_thread *t = (struct gp_thread *)pvt; struct gp_query *q = NULL; char dummy = 0; int ret; while (!t->pool->shutdown) { /* initialize debug client id to 0 until work is scheduled */ gp_debug_set_conn_id(0); /* ======> COND_MUTEX */ pthread_mutex_lock(&t->cond_mutex); while (t->query == NULL) { /* wait for next query */ pthread_cond_wait(&t->cond_wakeup, &t->cond_mutex); if (t->pool->shutdown) { pthread_exit(NULL); } } /* grab the query off the shared pointer */ q = t->query; t->query = NULL; /* <====== COND_MUTEX */ pthread_mutex_unlock(&t->cond_mutex); /* set client id before hndling requests */ gp_debug_set_conn_id(gp_conn_get_cid(q->conn)); /* handle the client request */ GPDEBUGN(3, "[status] Handling query input: %p (%zu)\n", q->buffer, q->buflen); gp_handle_query(t->pool, q); GPDEBUGN(3 ,"[status] Handling query output: %p (%zu)\n", q->buffer, q->buflen); /* now get lock on main queue, to play with the reply list */ /* ======> POOL LOCK */ pthread_mutex_lock(&t->pool->lock); /* put back query so that dispatcher can send reply */ q->next = t->pool->reply_list; t->pool->reply_list = q; /* add us back to the free list but only if we are not * shutting down */ if (!t->pool->shutdown) { LIST_DEL(t->pool->busy_list, t); LIST_ADD(t->pool->free_list, t); } /* <====== POOL LOCK */ pthread_mutex_unlock(&t->pool->lock); /* and wake up dispatcher so it will handle it */ ret = write(t->pool->sig_pipe[1], &dummy, 1); if (ret == -1) { GPERROR("Failed to signal dispatcher!"); } } pthread_exit(NULL); } static void gp_handle_query(struct gp_workers *w, struct gp_query *q) { struct gp_call_ctx gpcall = { 0 }; uint8_t *buffer; size_t buflen; int ret; /* find service */ gpcall.gpctx = w->gpctx; gpcall.service = gp_creds_match_conn(w->gpctx, q->conn); if (!gpcall.service) { q->status = GP_QUERY_ERR; return; } gpcall.connection = q->conn; ret = gp_rpc_process_call(&gpcall, q->buffer, q->buflen, &buffer, &buflen); if (ret) { q->status = GP_QUERY_ERR; } else { q->status = GP_QUERY_OUT; free(q->buffer); q->buffer = buffer; q->buflen = buflen; } if (gpcall.destroy_callback) { gpcall.destroy_callback(gpcall.destroy_callback_data); } } gssproxy-v0.8.2/src/gssproxy.c0000644000174300017420000002065613456641744016060 0ustar gitgit00000000000000/* Copyright (C) 2011,2015 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include "popt.h" #include "gp_proxy.h" #include #include const int vflags = VERTO_EV_FLAG_PERSIST | VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_IO_CLOSE_FD; char *opt_config_file = NULL; char *opt_config_dir = NULL; char *opt_config_socket = NULL; int opt_daemon = 0; struct gssproxy_ctx *gpctx; static struct gp_service * find_service_by_name(struct gp_config *cfg, const char *name) { int i; struct gp_service *ret = NULL; for (i = 0; i < cfg->num_svcs; i++) { if (strcmp(cfg->svcs[i]->name, name) == 0) { ret = cfg->svcs[i]; break; } } return ret; } static verto_ev *setup_socket(char *sock_name, verto_ctx *vctx) { struct gp_sock_ctx *sock_ctx; verto_ev *ev; sock_ctx = init_unix_socket(gpctx, sock_name); if (!sock_ctx) { return NULL; } ev = verto_add_io(vctx, vflags, accept_sock_conn, sock_ctx->fd); if (!ev) { free(sock_ctx); return NULL; } verto_set_private(ev, sock_ctx, free_unix_socket); return ev; } static int init_sockets(verto_ctx *vctx, struct gp_config *old_config) { int i; struct gp_sock_ctx *sock_ctx; verto_ev *ev; struct gp_service *svc; /* init main socket */ if (!old_config) { ev = setup_socket(gpctx->config->socket_name, vctx); if (!ev) { return 1; } gpctx->sock_ev = ev; } else if (strcmp(old_config->socket_name, gpctx->config->socket_name) != 0) { ev = setup_socket(gpctx->config->socket_name, vctx); if (!ev) { return 1; } gpctx->sock_ev = ev; verto_del(gpctx->sock_ev); } else { /* free_config will erase the socket name; update it accordingly */ sock_ctx = verto_get_private(gpctx->sock_ev); sock_ctx->socket = gpctx->config->socket_name; } /* propagate any sockets that shouldn't change */ if (old_config) { for (i = 0; i < old_config->num_svcs; i++) { if (old_config->svcs[i]->ev) { svc = find_service_by_name(gpctx->config, old_config->svcs[i]->name); if (svc && ((svc->socket == old_config->svcs[i]->socket) || ((svc->socket != NULL) && (old_config->svcs[i]->socket != NULL) && strcmp(svc->socket, old_config->svcs[i]->socket) == 0))) { svc->ev = old_config->svcs[i]->ev; sock_ctx = verto_get_private(svc->ev); sock_ctx->socket = svc->socket; } else { verto_del(old_config->svcs[i]->ev); } } } } /* init all other sockets */ for (i = 0; i < gpctx->config->num_svcs; i++) { svc = gpctx->config->svcs[i]; if (svc->socket != NULL && svc->ev == NULL) { ev = setup_socket(svc->socket, vctx); if (!ev) { return 1; } svc->ev = ev; } } return 0; } static void hup_handler(verto_ctx *vctx, verto_ev *ev UNUSED) { int ret; struct gp_config *new_config, *old_config; GPDEBUG("Received SIGHUP; re-reading config.\n"); new_config = read_config(opt_config_file, opt_config_dir, opt_config_socket, opt_daemon); if (!new_config) { GPERROR("Error reading new configuration on SIGHUP; keeping old " "configuration instead!\n"); return; } old_config = gpctx->config; gpctx->config = new_config; ret = init_sockets(vctx, old_config); if (ret != 0) { exit(ret); } /* conditionally reload kernel interface */ init_proc_nfsd(gpctx->config); free_config(&old_config); GPDEBUG("New config loaded successfully.\n"); return; } int main(int argc, const char *argv[]) { int opt; poptContext pc; int opt_interactive = 0; int opt_version = 0; int opt_debug = 0; int opt_debug_level = 0; verto_ctx *vctx; verto_ev *ev; int wait_fd; int ret = -1; /* initialize debug client id to 0 in the main thread */ /* we do this early, before any code starts using debug statements */ gp_debug_set_conn_id(0); struct poptOption long_options[] = { POPT_AUTOHELP {"daemon", 'D', POPT_ARG_NONE, &opt_daemon, 0, \ _("Become a daemon (default)"), NULL }, \ {"interactive", 'i', POPT_ARG_NONE, &opt_interactive, 0, \ _("Run interactive (not a daemon)"), NULL}, \ {"config", 'c', POPT_ARG_STRING, &opt_config_file, 0, \ _("Specify a non-default config file"), NULL}, \ {"configdir", 'C', POPT_ARG_STRING, &opt_config_dir, 0, \ _("Specify a non-default config directory"), NULL}, \ {"socket", 's', POPT_ARG_STRING, &opt_config_socket, 0, \ _("Specify a custom default socket"), NULL}, \ {"debug", 'd', POPT_ARG_NONE, &opt_debug, 0, \ _("Enable debugging"), NULL}, \ {"debug-level", '\0', POPT_ARG_INT, &opt_debug_level, 0, \ _("Set debugging level"), NULL}, \ {"version", '\0', POPT_ARG_NONE, &opt_version, 0, \ _("Print version number and exit"), NULL }, \ POPT_TABLEEND }; pc = poptGetContext(argv[0], argc, argv, long_options, 0); while((opt = poptGetNextOpt(pc)) != -1) { switch(opt) { default: fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0), poptStrerror(opt)); poptPrintUsage(pc, stderr, 0); ret = 1; goto cleanup; } } if (opt_version) { puts(VERSION""DISTRO_VERSION""PRERELEASE_VERSION); ret = 0; goto cleanup; } if (opt_debug || opt_debug_level > 0) { if (opt_debug_level == 0) opt_debug_level = 1; gp_debug_toggle(opt_debug_level); } if (opt_daemon && opt_interactive) { fprintf(stderr, "Option -i|--interactive is not allowed together with -D|--daemon\n"); poptPrintUsage(pc, stderr, 0); ret = 0; goto cleanup; } if (opt_interactive) { opt_daemon = 2; } gpctx = calloc(1, sizeof(struct gssproxy_ctx)); gpctx->config = read_config(opt_config_file, opt_config_dir, opt_config_socket, opt_daemon); if (!gpctx->config) { ret = EXIT_FAILURE; goto cleanup; } init_server(gpctx->config->daemonize, &wait_fd); write_pid(); vctx = init_event_loop(); if (!vctx) { fprintf(stderr, "Failed to initialize event loop. " "Is there at least one libverto backend installed?\n"); ret = 1; goto cleanup; } gpctx->vctx = vctx; /* Add SIGHUP here so that gpctx is in scope for the handler */ ev = verto_add_signal(vctx, VERTO_EV_FLAG_PERSIST, hup_handler, SIGHUP); if (!ev) { fprintf(stderr, "Failed to register SIGHUP handler with verto!\n"); ret = 1; goto cleanup; } ret = init_sockets(vctx, NULL); if (ret != 0) { goto cleanup; } /* We need to tell nfsd that GSS-Proxy is available before it starts, * as nfsd needs to know GSS-Proxy is in use before the first time it * needs to call accept_sec_context. */ init_proc_nfsd(gpctx->config); /* Now it is safe to tell the init system that we're done starting up, * so it can continue with dependencies and start nfsd */ init_done(wait_fd); /* if config option "run_as_user" is missing, then it's no need to * drop privileges */ if (gpctx->config->proxy_user) { ret = drop_privs(gpctx->config); if (ret) { ret = EXIT_FAILURE; goto cleanup; } } ret = gp_workers_init(gpctx); if (ret) { ret = EXIT_FAILURE; goto cleanup; } verto_run(vctx); verto_free(vctx); gp_workers_free(gpctx->workers); fini_server(); free_config(&gpctx->config); free(gpctx); ret = 0; cleanup: poptFreeContext(pc); free(opt_config_file); free(opt_config_dir); free(opt_config_socket); #ifdef HAVE_VERTO_CLEANUP verto_cleanup(); #endif return ret; } gssproxy-v0.8.2/src/mechglue/0000755000174300017420000000000013456641744015576 5ustar gitgit00000000000000gssproxy-v0.8.2/src/mechglue/README0000644000174300017420000000016213456641744016455 0ustar gitgit00000000000000This directory now contains the actual mechglue plugin, client functions have been moved to the client directory. gssproxy-v0.8.2/src/mechglue/gpp_accept_sec_context.c0000644000174300017420000001151313456641744022446 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "gss_plugin.h" OM_uint32 gssi_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_cred_id_t acceptor_cred_handle, gss_buffer_t input_token_buffer, gss_channel_bindings_t input_chan_bindings, gss_name_t *src_name, gss_OID *mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec, gss_cred_id_t *delegated_cred_handle) { enum gpp_behavior behavior; struct gpp_context_handle *ctx_handle = NULL; struct gpp_cred_handle *cred_handle = NULL; struct gpp_cred_handle *deleg_cred = NULL; struct gpp_name_handle *name = NULL; OM_uint32 maj, min; GSSI_TRACE(); behavior = gpp_get_behavior(); if (*context_handle) { ctx_handle = (struct gpp_context_handle *)*context_handle; if (ctx_handle->local) { /* if we already have a local context it means this is * a continuation, force local only behavior, nothing else * makes sense */ behavior = GPP_LOCAL_ONLY; } else if (ctx_handle->remote) { behavior = GPP_REMOTE_ONLY; } } else { ctx_handle = calloc(1, sizeof(struct gpp_context_handle)); if (!ctx_handle) { maj = GSS_S_FAILURE; min = ENOMEM; goto done; } } if (acceptor_cred_handle != GSS_C_NO_CREDENTIAL) { cred_handle = (struct gpp_cred_handle *)acceptor_cred_handle; } else { maj = gppint_get_def_creds(&min, behavior, NULL, GSS_C_ACCEPT, &cred_handle); if (maj != GSS_S_COMPLETE) { goto done; } } if (cred_handle->local) { if (behavior == GPP_REMOTE_ONLY) { min = 0; maj = GSS_S_DEFECTIVE_CREDENTIAL; goto done; } behavior = GPP_LOCAL_ONLY; } else if (cred_handle->remote) { if (behavior == GPP_LOCAL_ONLY) { min = 0; maj = GSS_S_DEFECTIVE_CREDENTIAL; goto done; } behavior = GPP_REMOTE_ONLY; } if (src_name) { name = calloc(1, sizeof(struct gpp_name_handle)); if (!name) { maj = GSS_S_FAILURE; min = ENOMEM; goto done; } } if (delegated_cred_handle) { maj = gpp_cred_handle_init(&min, false, NULL, &deleg_cred); if (maj != GSS_S_COMPLETE) { goto done; } } /* behavior has been set to local only or remote only by context or * credential handler inspection, so we only have those 2 cases, * anything else is an error at this point. */ if (behavior == GPP_LOCAL_ONLY) { maj = gss_accept_sec_context(&min, &ctx_handle->local, cred_handle->local, input_token_buffer, input_chan_bindings, name ? &name->local : NULL, mech_type, output_token, ret_flags, time_rec, deleg_cred ? &deleg_cred->local : NULL); } else if (behavior == GPP_REMOTE_ONLY) { maj = gpm_accept_sec_context(&min, &ctx_handle->remote, cred_handle->remote, input_token_buffer, input_chan_bindings, name ? &name->remote : NULL, mech_type, output_token, ret_flags, time_rec, deleg_cred ? &deleg_cred->remote : NULL); } else { min = 0; maj = GSS_S_FAILURE; } done: *minor_status = gpp_map_error(min); if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) { if (ctx_handle && ctx_handle->local == GSS_C_NO_CONTEXT && ctx_handle->remote == NULL) { free(ctx_handle); ctx_handle = NULL; } free(deleg_cred); free(name); } else { if (src_name) { *src_name = (gss_name_t)name; } if (delegated_cred_handle) { *delegated_cred_handle = (gss_cred_id_t)deleg_cred; } } /* always replace the provided context handle to avoid * dangling pointers when a context has been passed in */ *context_handle = (gss_ctx_id_t)ctx_handle; if (acceptor_cred_handle == GSS_C_NO_CREDENTIAL) { (void)gssi_release_cred(&min, (gss_cred_id_t *)&cred_handle); } return maj; } gssproxy-v0.8.2/src/mechglue/gpp_acquire_cred.c0000644000174300017420000004257713456641744021255 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "gss_plugin.h" static OM_uint32 acquire_local(OM_uint32 *minor_status, struct gpp_cred_handle *imp_cred_handle, struct gpp_name_handle *name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_const_key_value_set_t cred_store, struct gpp_cred_handle *out_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec) { gss_OID_set special_mechs = GSS_C_NO_OID_SET; OM_uint32 maj, min; special_mechs = gpp_special_available_mechs(desired_mechs); if (special_mechs == GSS_C_NO_OID_SET) { maj = GSS_S_BAD_MECH; min = 0; goto done; } if (name && name->remote && !name->local) { maj = gpp_name_to_local(&min, name->remote, name->mech_type, &name->local); if (maj) { goto done; } } if (imp_cred_handle) { maj = gss_acquire_cred_impersonate_name(&min, imp_cred_handle->local, name ? name->local : NULL, time_req, special_mechs, cred_usage, &out_cred_handle->local, actual_mechs, time_rec); goto done; } maj = gss_acquire_cred_from(&min, name ? name->local : NULL, time_req, special_mechs, cred_usage, cred_store, &out_cred_handle->local, actual_mechs, time_rec); done: *minor_status = min; (void)gss_release_oid_set(&min, &special_mechs); return maj; } OM_uint32 gssi_acquire_cred(OM_uint32 *minor_status, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec) { return gssi_acquire_cred_from(minor_status, desired_name, time_req, desired_mechs, cred_usage, NULL, output_cred_handle, actual_mechs, time_rec); } OM_uint32 gssi_acquire_cred_from(OM_uint32 *minor_status, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_const_key_value_set_t cred_store, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec) { enum gpp_behavior behavior; struct gpp_name_handle *name; struct gpp_cred_handle *out_cred_handle = NULL; struct gssx_cred *in_cred_remote = NULL; const char *ccache_name = NULL; OM_uint32 maj, min; OM_uint32 tmaj, tmin; GSSI_TRACE(); if (!output_cred_handle) { *minor_status = gpp_map_error(EINVAL); return GSS_S_FAILURE; } tmaj = GSS_S_COMPLETE; tmin = 0; name = (struct gpp_name_handle *)desired_name; behavior = gpp_get_behavior(); /* Always check if we have remote creds stored in the local ccache */ for (unsigned i = 0; cred_store && i < cred_store->count; i++) { if (strcmp(cred_store->elements[i].key, "ccache") == 0) { ccache_name = cred_store->elements[i].value; break; } } maj = gpp_cred_handle_init(&min, !ccache_name, ccache_name, &out_cred_handle); if (maj != GSS_S_COMPLETE) { goto done; } if (behavior != GPP_LOCAL_ONLY) { in_cred_remote = calloc(1, sizeof(gssx_cred)); if (!in_cred_remote) { maj = GSS_S_FAILURE; min = ENOMEM; goto done; } maj = gppint_retrieve_remote_creds(&min, ccache_name, NULL, in_cred_remote); if (maj == GSS_S_COMPLETE) { behavior = GPP_REMOTE_FIRST; } else { safefree(in_cred_remote); if (ccache_name) { behavior = GPP_LOCAL_FIRST; } } } /* See if we should try local first */ if (behavior == GPP_LOCAL_ONLY || behavior == GPP_LOCAL_FIRST) { maj = acquire_local(&min, NULL, name, time_req, desired_mechs, cred_usage, cred_store, out_cred_handle, actual_mechs, time_rec); if (maj == GSS_S_COMPLETE || behavior == GPP_LOCAL_ONLY) { goto done; } /* not successful, save actual local error if remote fallback fails */ tmaj = maj; tmin = min; } /* Then try with remote */ if (name && name->local && !name->remote) { maj = gpp_local_to_name(&min, name->local, &name->remote); if (maj) { goto done; } } maj = gpm_acquire_cred(&min, in_cred_remote, name ? name->remote : NULL, time_req, desired_mechs, cred_usage, false, &out_cred_handle->remote, actual_mechs, time_rec); if (maj == GSS_S_COMPLETE) { /* store back creds if they changed */ if (out_cred_handle->remote && !gpp_creds_are_equal(in_cred_remote, out_cred_handle->remote)) { tmaj = gpp_store_remote_creds(&tmin, out_cred_handle->default_creds, &out_cred_handle->store, out_cred_handle->remote); if (tmaj != GSS_S_COMPLETE) { maj = tmaj; } } goto done; } if (behavior == GPP_REMOTE_FIRST) { if (maj != GSS_S_COMPLETE) { /* save errors */ tmaj = maj; tmin = min; } /* So remote failed, but we can fallback to local, try that */ maj = acquire_local(&min, NULL, name, time_req, desired_mechs, cred_usage, cred_store, out_cred_handle, actual_mechs, time_rec); } done: if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED && tmaj != GSS_S_COMPLETE) { maj = tmaj; min = tmin; } if (in_cred_remote) { xdr_free((xdrproc_t)xdr_gssx_cred, (char *)in_cred_remote); free(in_cred_remote); } if (maj == GSS_S_COMPLETE) { *output_cred_handle = (gss_cred_id_t)out_cred_handle; } else { (void)gpp_cred_handle_free(&tmin, out_cred_handle); } *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_add_cred(OM_uint32 *minor_status, const gss_cred_id_t input_cred_handle, const gss_name_t desired_name, const gss_OID desired_mech, gss_cred_usage_t cred_usage, OM_uint32 initiator_time_req, OM_uint32 acceptor_time_req, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *initiator_time_rec, OM_uint32 *acceptor_time_rec) { return gssi_add_cred_from(minor_status, input_cred_handle, desired_name, desired_mech, cred_usage, initiator_time_req, acceptor_time_req, NULL, output_cred_handle, actual_mechs, initiator_time_rec, acceptor_time_rec); } OM_uint32 gssi_add_cred_from(OM_uint32 *minor_status, const gss_cred_id_t input_cred_handle, const gss_name_t desired_name, const gss_OID desired_mech, gss_cred_usage_t cred_usage, OM_uint32 initiator_time_req, OM_uint32 acceptor_time_req, gss_const_key_value_set_t cred_store, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *initiator_time_rec, OM_uint32 *acceptor_time_rec) { gss_OID_set desired_mechs = GSS_C_NO_OID_SET; OM_uint32 time_req, time_rec; OM_uint32 maj, min; GSSI_TRACE(); if (!output_cred_handle) { return GSS_S_CALL_INACCESSIBLE_WRITE; } if (desired_mech) { maj = gss_create_empty_oid_set(&min, &desired_mechs); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } maj = gss_add_oid_set_member(&min, desired_mech, &desired_mechs); if (maj != GSS_S_COMPLETE) { (void)gss_release_oid_set(&min, &desired_mechs); *minor_status = gpp_map_error(min); return maj; } } switch (cred_usage) { case GSS_C_ACCEPT: time_req = acceptor_time_req; break; case GSS_C_INITIATE: time_req = initiator_time_req; break; case GSS_C_BOTH: if (acceptor_time_req > initiator_time_req) { time_req = acceptor_time_req; } else { time_req = initiator_time_req; } break; default: time_req = 0; } maj = gssi_acquire_cred_from(minor_status, desired_name, time_req, desired_mechs, cred_usage, NULL, output_cred_handle, actual_mechs, &time_rec); if (maj == GSS_S_COMPLETE) { if (acceptor_time_rec && (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)) { *acceptor_time_rec = time_rec; } if (initiator_time_rec && (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)) { *initiator_time_rec = time_rec; } } (void)gss_release_oid_set(&min, &desired_mechs); return maj; } OM_uint32 gssi_acquire_cred_with_password(OM_uint32 *minor_status, const gss_name_t desired_name, const gss_buffer_t password, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec) { enum gpp_behavior behavior; struct gpp_name_handle *name; struct gpp_cred_handle *out_cred_handle = NULL; gss_OID_set special_mechs; OM_uint32 maj, min; GSSI_TRACE(); if (desired_name == GSS_C_NO_NAME) { *minor_status = gpp_map_error(EINVAL); return GSS_S_BAD_NAME; } name = (struct gpp_name_handle *)desired_name; if (!output_cred_handle) { *minor_status = gpp_map_error(EINVAL); return GSS_S_FAILURE; } if (desired_mechs == GSS_C_NO_OID_SET) { return GSS_S_CALL_INACCESSIBLE_READ; } behavior = gpp_get_behavior(); maj = gpp_cred_handle_init(&min, false, NULL, &out_cred_handle); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } switch (behavior) { case GPP_LOCAL_ONLY: case GPP_LOCAL_FIRST: case GPP_REMOTE_FIRST: /* re-enter the mechglue, using the special OIDs for skipping * the use of the interposer */ special_mechs = gpp_special_available_mechs(desired_mechs); if (special_mechs == GSS_C_NO_OID_SET) { min = EINVAL; maj = GSS_S_FAILURE; goto done; } if (name->remote && !name->local) { maj = gpp_name_to_local(&min, name->remote, name->mech_type, &name->local); if (maj) { goto done; } } maj = gss_acquire_cred_with_password(&min, name->local, password, time_req, special_mechs, cred_usage, &out_cred_handle->local, actual_mechs, time_rec); break; /* fall through if we got no creds locally and we are in * automatic mode */ case GPP_REMOTE_ONLY: /* FIXME: not currently available */ /* fall through for now */ default: maj = GSS_S_FAILURE; min = EINVAL; } done: if (maj == GSS_S_COMPLETE) { *output_cred_handle = (gss_cred_id_t)out_cred_handle; } else { free(out_cred_handle); } *minor_status = gpp_map_error(min); (void)gss_release_oid_set(&min, &special_mechs); return maj; } OM_uint32 gssi_acquire_cred_impersonate_name(OM_uint32 *minor_status, gss_cred_id_t *imp_cred_handle, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec) { enum gpp_behavior behavior; struct gpp_name_handle *name; struct gpp_cred_handle *impersonator_cred_handle = NULL; struct gpp_cred_handle *out_cred_handle = NULL; OM_uint32 maj, min; OM_uint32 tmaj, tmin; GSSI_TRACE(); if (!imp_cred_handle) { *minor_status = gpp_map_error(EINVAL); return GSS_S_NO_CRED; } impersonator_cred_handle = (struct gpp_cred_handle *)imp_cred_handle; if (!output_cred_handle) { *minor_status = gpp_map_error(EINVAL); return GSS_S_FAILURE; } tmaj = GSS_S_COMPLETE; tmin = 0; maj = gpp_cred_handle_init(&min, false, NULL, &out_cred_handle); if (maj != GSS_S_COMPLETE) { goto done; } name = (struct gpp_name_handle *)desired_name; behavior = gpp_get_behavior(); /* See if we should try local first */ if (behavior == GPP_LOCAL_ONLY || behavior == GPP_LOCAL_FIRST) { maj = acquire_local(&min, impersonator_cred_handle, name, time_req, desired_mechs, cred_usage, NULL, out_cred_handle, actual_mechs, time_rec); if (maj == GSS_S_COMPLETE || behavior == GPP_LOCAL_ONLY) { goto done; } /* not successful, save actual local error if remote fallback fails */ tmaj = maj; tmin = min; } /* Then try with remote */ if (name && name->local && !name->remote) { maj = gpp_local_to_name(&min, name->local, &name->remote); if (maj) { goto done; } } maj = gpm_acquire_cred(&min, impersonator_cred_handle->remote, name ? name->remote : NULL, time_req, desired_mechs, cred_usage, true, &out_cred_handle->remote, actual_mechs, time_rec); if (maj == GSS_S_COMPLETE || behavior == GPP_REMOTE_ONLY) { goto done; } if (behavior == GPP_REMOTE_FIRST) { /* So remote failed, but we can fallback to local, try that */ maj = acquire_local(&min, impersonator_cred_handle, name, time_req, desired_mechs, cred_usage, NULL, out_cred_handle, actual_mechs, time_rec); } done: if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED && tmaj != GSS_S_COMPLETE) { maj = tmaj; min = tmin; } if (maj == GSS_S_COMPLETE) { *output_cred_handle = (gss_cred_id_t)out_cred_handle; } else { free(out_cred_handle); } *minor_status = gpp_map_error(min); return maj; } gssproxy-v0.8.2/src/mechglue/gpp_context.c0000644000174300017420000002744713456641744020312 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "gss_plugin.h" #include OM_uint32 gssi_export_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t interprocess_token) { struct gpp_context_handle *ctx; gss_buffer_desc output_token; OM_uint32 maj, min; GSSI_TRACE(); ctx = (struct gpp_context_handle *)context_handle; if (!ctx) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we have support only for some specific known * mechanisms for which we can export/import the context */ if (ctx->remote && !ctx->local) { maj = gpp_remote_to_local_ctx(&min, &ctx->remote, &ctx->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } maj = gss_export_sec_context(minor_status, &ctx->local, interprocess_token); if (maj == GSS_S_COMPLETE && ctx->remote) { (void)gpm_delete_sec_context(&min, &ctx->remote, &output_token); } return maj; } OM_uint32 gssi_import_sec_context(OM_uint32 *minor_status, gss_buffer_t interprocess_token, gss_ctx_id_t *context_handle) { GSSI_TRACE(); return GSS_S_UNAVAILABLE; } OM_uint32 gssi_import_sec_context_by_mech(OM_uint32 *minor_status, gss_OID mech_type, gss_buffer_t interprocess_token, gss_ctx_id_t *context_handle) { struct gpp_context_handle *ctx; gss_buffer_desc wrap_token = {0}; OM_uint32 maj, min = 0; GSSI_TRACE(); ctx = calloc(1, sizeof(struct gpp_context_handle)); if (!ctx) { *minor_status = 0; return GSS_S_FAILURE; } /* NOTE: it makes no sense to import a context remotely atm, * so we only handle the local case for now. */ maj = gpp_wrap_sec_ctx_token(&min, mech_type, interprocess_token, &wrap_token); if (maj != GSS_S_COMPLETE) { goto done; } maj = gss_import_sec_context(&min, &wrap_token, &ctx->local); done: *minor_status = gpp_map_error(min); if (maj == GSS_S_COMPLETE) { *context_handle = (gss_ctx_id_t)ctx; } else { free(ctx); } (void)gss_release_buffer(&min, &wrap_token); return maj; } OM_uint32 gssi_process_context_token(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_buffer_t token_buffer) { struct gpp_context_handle *ctx; OM_uint32 maj, min; GSSI_TRACE(); ctx = (struct gpp_context_handle *)context_handle; if (!ctx) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we have support only for some specific known * mechanisms for which we can export/import the context */ if (ctx->remote && !ctx->local) { maj = gpp_remote_to_local_ctx(&min, &ctx->remote, &ctx->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_process_context_token(minor_status, ctx->local, token_buffer); } OM_uint32 gssi_context_time(OM_uint32 *minor_status, gss_ctx_id_t context_handle, OM_uint32 *time_rec) { struct gpp_context_handle *ctx; OM_uint32 maj, min; GSSI_TRACE(); *minor_status = 0; ctx = (struct gpp_context_handle *)context_handle; if (!ctx) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we have support only for some specific known * mechanisms for which we can export/import the context */ if (ctx->remote) { OM_uint32 lifetime; maj = gpm_inquire_context(&min, ctx->remote, NULL, NULL, &lifetime, NULL, NULL, NULL, NULL); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } if (lifetime > 0) { *time_rec = lifetime; return GSS_S_COMPLETE; } else { *time_rec = 0; return GSS_S_CONTEXT_EXPIRED; } } else if (ctx->local) { return gss_context_time(minor_status, ctx->local, time_rec); } else { return GSS_S_NO_CONTEXT; } } OM_uint32 gssi_inquire_context(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_name_t *src_name, gss_name_t *targ_name, OM_uint32 *lifetime_rec, gss_OID *mech_type, OM_uint32 *ctx_flags, int *locally_initiated, int *open) { struct gpp_context_handle *ctx_handle; struct gpp_name_handle *s_name = NULL; struct gpp_name_handle *t_name = NULL; gss_OID mech_oid; OM_uint32 maj, min; GSSI_TRACE(); if (!context_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle->local && !ctx_handle->remote) { return GSS_S_CALL_INACCESSIBLE_READ; } if (src_name) { s_name = calloc(1, sizeof(struct gpp_name_handle)); if (!s_name) { min = ENOMEM; maj = GSS_S_FAILURE; goto done; } } if (targ_name) { t_name = calloc(1, sizeof(struct gpp_name_handle)); if (!t_name) { min = ENOMEM; maj = GSS_S_FAILURE; goto done; } } if (ctx_handle->local) { maj = gss_inquire_context(&min, ctx_handle->local, s_name ? &s_name->local : NULL, t_name ? &t_name->local : NULL, lifetime_rec, &mech_oid, ctx_flags, locally_initiated, open); } else { maj = gpm_inquire_context(&min, ctx_handle->remote, s_name ? &s_name->remote : NULL, t_name ? &t_name->remote : NULL, lifetime_rec, &mech_oid, ctx_flags, locally_initiated, open); } if (maj != GSS_S_COMPLETE) { goto done; } if (s_name) { maj = gpp_copy_oid(&min, mech_oid, &s_name->mech_type); if (maj != GSS_S_COMPLETE) { goto done; } } if (t_name) { maj = gpp_copy_oid(&min, mech_oid, &t_name->mech_type); if (maj != GSS_S_COMPLETE) { goto done; } } done: *minor_status = gpp_map_error(min); if (maj == GSS_S_COMPLETE) { if (mech_type) { *mech_type = mech_oid; } else { (void)gss_release_oid(&min, &mech_oid); } if (src_name) { *src_name = (gss_name_t)s_name; } if (targ_name) { *targ_name = (gss_name_t)t_name; } } else { (void)gss_release_oid(&min, &mech_oid); (void)gssi_release_name(&min, (gss_name_t *)&s_name); (void)gssi_release_name(&min, (gss_name_t *)&t_name); } return maj; } OM_uint32 gssi_inquire_sec_context_by_oid(OM_uint32 *minor_status, const gss_ctx_id_t context_handle, const gss_OID desired_object, gss_buffer_set_t *data_set) { struct gpp_context_handle *ctx; OM_uint32 maj, min; GSSI_TRACE(); ctx = (struct gpp_context_handle *)context_handle; if (!ctx) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we have support only for some specific known * mechanisms for which we can export/import the context */ if (ctx->remote && !ctx->local) { maj = gpp_remote_to_local_ctx(&min, &ctx->remote, &ctx->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_inquire_sec_context_by_oid(minor_status, ctx->local, desired_object, data_set); } OM_uint32 gssi_set_sec_context_option(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, const gss_OID desired_object, const gss_buffer_t value) { struct gpp_context_handle *ctx; OM_uint32 maj, min; GSSI_TRACE(); if (*context_handle) { ctx = (struct gpp_context_handle *)(*context_handle); } else { ctx = calloc(1, sizeof(struct gpp_context_handle)); if (!ctx) { *minor_status = 0; return GSS_S_FAILURE; } } /* for now we have support only for some specific known * mechanisms for which we can export/import the context */ if (ctx->remote && !ctx->local) { maj = gpp_remote_to_local_ctx(&min, &ctx->remote, &ctx->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); goto done; } } maj = gss_set_sec_context_option(minor_status, &ctx->local, desired_object, value); done: *context_handle = (gss_ctx_id_t)ctx; if (maj != GSS_S_COMPLETE) { (void)gssi_delete_sec_context(&min, context_handle, NULL); } return maj; } OM_uint32 gssi_delete_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t output_token) { struct gpp_context_handle *ctx; OM_uint32 maj, min; OM_uint32 rmaj = GSS_S_COMPLETE; GSSI_TRACE(); ctx = (struct gpp_context_handle *)*context_handle; *context_handle = GSS_C_NO_CONTEXT; if (ctx == NULL) { *minor_status = 0; return GSS_S_COMPLETE; } if (ctx->local) { maj = gss_delete_sec_context(&min, &ctx->local, output_token); if (maj != GSS_S_COMPLETE) { rmaj = maj; *minor_status = gpp_map_error(min); } } if (ctx->remote) { maj = gpm_delete_sec_context(&min, &ctx->remote, output_token); if (maj && rmaj == GSS_S_COMPLETE) { rmaj = maj; *minor_status = gpp_map_error(min); } } free(ctx); return rmaj; } OM_uint32 gssi_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int prf_key, const gss_buffer_t prf_in, ssize_t desired_output_len, gss_buffer_t prf_out) { struct gpp_context_handle *ctx; OM_uint32 maj, min; GSSI_TRACE(); ctx = (struct gpp_context_handle *)context_handle; if (!ctx) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we have support only for some specific known * mechanisms for which we can export/import the context */ if (ctx->remote && !ctx->local) { maj = gpp_remote_to_local_ctx(&min, &ctx->remote, &ctx->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_pseudo_random(minor_status, ctx->local, prf_key, prf_in, desired_output_len, prf_out); } gssproxy-v0.8.2/src/mechglue/gpp_creds.c0000644000174300017420000006514013456641744017716 0ustar gitgit00000000000000/* Copyright (C) 2015 the GSS-PROXY contributors, see COPYING for license */ #include "gss_plugin.h" #include #define GPKRB_SRV_NAME "Encrypted/Credentials/v1@X-GSSPROXY:" uint32_t gpp_cred_handle_init(uint32_t *min, bool defcred, const char *ccache, struct gpp_cred_handle **out_handle) { struct gpp_cred_handle *h = NULL; uint32_t maj = 0; h = calloc(1, sizeof(struct gpp_cred_handle)); if (!h) { *min = ENOMEM; return GSS_S_FAILURE; } h->default_creds = defcred; if (ccache) { h->store.elements = calloc(1, sizeof(gss_key_value_element_desc)); if (!h->store.elements) { *min = ENOMEM; maj = GSS_S_FAILURE; goto done; } h->store.count = 1; h->store.elements[0].key = strdup("ccache"); if (!h->store.elements[0].key) { *min = ENOMEM; maj = GSS_S_FAILURE; goto done; } h->store.elements[0].value = strdup(ccache); if (!h->store.elements[0].value) { *min = ENOMEM; maj = GSS_S_FAILURE; goto done; } } done: if (maj) { uint32_t tmp; (void)gpp_cred_handle_free(&tmp, h); } else { *out_handle = h; } return maj; } uint32_t gpp_cred_handle_free(uint32_t *min, struct gpp_cred_handle *handle) { uint32_t maj = GSS_S_COMPLETE; *min = 0; if (!handle) { return GSS_S_COMPLETE; } if (handle->local) { maj = gss_release_cred(min, &handle->local); } if (handle->remote) { xdr_free((xdrproc_t)xdr_gssx_cred, (char *)handle->remote); free(handle->remote); } if (handle->store.count > 0) { for (size_t i = 0; i < handle->store.count; i++) { free((void *)handle->store.elements[i].key); free((void *)handle->store.elements[i].value); } free(handle->store.elements); handle->store.count = 0; } free(handle); return maj; } /* NOTE: currently the only things we check for are the cred name and the * cred_handle_reference. We do NOT check each cred element beyond that they * match in number */ bool gpp_creds_are_equal(gssx_cred *a, gssx_cred *b) { gssx_buffer *ta; gssx_buffer *tb; if (!a && !b) { return true; } else if (!a || !b) { return false; } ta = &a->desired_name.display_name; tb = &b->desired_name.display_name; if (ta->octet_string_len != tb->octet_string_len) { return false; } else if (!ta->octet_string_val && tb->octet_string_val) { return false; } else if (ta->octet_string_val) { if (!tb->octet_string_val) { return false; } else if (memcmp(ta->octet_string_val, tb->octet_string_val, ta->octet_string_len) != 0) { return false; } } if (a->elements.elements_len != b->elements.elements_len) { return false; } ta = &a->cred_handle_reference; tb = &b->cred_handle_reference; if (ta->octet_string_len != tb->octet_string_len) { return false; } else if (!ta->octet_string_val && tb->octet_string_val) { return false; } else if (ta->octet_string_val) { if (!tb->octet_string_val) { return false; } else if (memcmp(ta->octet_string_val, tb->octet_string_val, ta->octet_string_len) != 0) { return false; } } return true; } static krb5_error_code gpp_construct_cred(gssx_cred *creds, krb5_context ctx, krb5_creds *cred, char *cred_name) { XDR xdrctx; bool xdrok; krb5_error_code ret = 0; memset(cred, 0, sizeof(*cred)); memcpy(cred_name, creds->desired_name.display_name.octet_string_val, creds->desired_name.display_name.octet_string_len); cred_name[creds->desired_name.display_name.octet_string_len] = '\0'; ret = krb5_parse_name(ctx, cred_name, &cred->client); if (ret) { return ret; } ret = krb5_parse_name(ctx, GPKRB_SRV_NAME, &cred->server); if (ret) { return ret; } cred->ticket.data = malloc(GPKRB_MAX_CRED_SIZE); xdrmem_create(&xdrctx, cred->ticket.data, GPKRB_MAX_CRED_SIZE, XDR_ENCODE); xdrok = xdr_gssx_cred(&xdrctx, creds); if (!xdrok) { return ENOSPC; } cred->ticket.length = xdr_getpos(&xdrctx); return 0; } /* Store creds from remote in a local ccache, updating where possible. * * If store_as_default_cred is true, the cred is made default for its * collection, if there is one. Note that if the ccache is not of a * collection type, the creds will overwrite the ccache. * * If no "ccache" entry is specified in cred_store, the default ccache for a * new context will be used. */ uint32_t gpp_store_remote_creds(uint32_t *min, bool store_as_default_cred, gss_const_key_value_set_t cred_store, gssx_cred *creds) { krb5_context ctx = NULL; krb5_ccache ccache = NULL; krb5_creds cred; krb5_error_code ret; char cred_name[creds->desired_name.display_name.octet_string_len + 1]; const char *cc_name; *min = 0; ret = krb5_init_context(&ctx); if (ret) goto done; ret = gpp_construct_cred(creds, ctx, &cred, cred_name); if (ret) { goto done; } for (unsigned i = 0; cred_store && i < cred_store->count; i++) { if (strcmp(cred_store->elements[i].key, "ccache") == 0) { /* krb5 creates new ccaches based off the default name. */ ret = krb5_cc_set_default_name(ctx, cred_store->elements[i].value); if (ret) goto done; break; } } cc_name = krb5_cc_default_name(ctx); if (strncmp(cc_name, "FILE:", 5) == 0 || !strchr(cc_name, ':')) { /* FILE ccaches don't handle updates properly: if they have the same * principal name, they are blackholed. We either have to change the * name (at which point the file grows forever) or flash the cache on * every update. */ ret = krb5_cc_default(ctx, &ccache); if (ret) goto done; ret = krb5_cc_initialize(ctx, ccache, cred.client); if (ret != 0) goto done; ret = krb5_cc_store_cred(ctx, ccache, &cred); goto done; } ret = krb5_cc_cache_match(ctx, cred.client, &ccache); if (ret == KRB5_CC_NOTFOUND) { /* A new ccache within the collection whose name is based off the * default_name for the context. krb5_cc_new_unique only accepts the * leading component of a name as a type. */ char *cc_type; const char *p; p = strchr(cc_name, ':'); /* can't be FILE here */ cc_type = strndup(cc_name, p - cc_name); if (!cc_type) { ret = ENOMEM; goto done; } ret = krb5_cc_new_unique(ctx, cc_type, NULL, &ccache); free(cc_type); if (ret) goto done; /* krb5_cc_new_unique() doesn't initialize, and we need to initialize * before storing into the ccache. Note that this will only clobber * the ccache handle, not the whole collection. */ ret = krb5_cc_initialize(ctx, ccache, cred.client); } if (ret) goto done; ret = krb5_cc_store_cred(ctx, ccache, &cred); if (ret) goto done; if (store_as_default_cred) { ret = krb5_cc_switch(ctx, ccache); } done: if (ctx) { krb5_free_cred_contents(ctx, &cred); if (ccache) krb5_cc_close(ctx, ccache); krb5_free_context(ctx); } *min = ret; return ret ? GSS_S_FAILURE : GSS_S_COMPLETE; } OM_uint32 gppint_retrieve_remote_creds(uint32_t *min, const char *ccache_name, gssx_name *name, gssx_cred *creds) { krb5_context ctx = NULL; krb5_ccache ccache = NULL; krb5_creds cred; krb5_creds icred; krb5_error_code ret; XDR xdrctx; bool xdrok; memset(&cred, 0, sizeof(krb5_creds)); memset(&icred, 0, sizeof(krb5_creds)); ret = krb5_init_context(&ctx); if (ret) goto done; if (ccache_name) { ret = krb5_cc_resolve(ctx, ccache_name, &ccache); } else { ret = krb5_cc_default(ctx, &ccache); } if (ret) goto done; if (name) { char client_name[name->display_name.octet_string_len + 1]; memcpy(client_name, name->display_name.octet_string_val, name->display_name.octet_string_len); client_name[name->display_name.octet_string_len] = '\0'; ret = krb5_parse_name(ctx, client_name, &icred.client); } else { ret = krb5_cc_get_principal(ctx, ccache, &icred.client); } if (ret) goto done; ret = krb5_parse_name(ctx, GPKRB_SRV_NAME, &icred.server); if (ret) goto done; ret = krb5_cc_retrieve_cred(ctx, ccache, 0, &icred, &cred); if (ret) goto done; xdrmem_create(&xdrctx, cred.ticket.data, cred.ticket.length, XDR_DECODE); xdrok = xdr_gssx_cred(&xdrctx, creds); if (xdrok) { ret = 0; } else { ret = EIO; } done: if (ctx) { krb5_free_cred_contents(ctx, &cred); krb5_free_cred_contents(ctx, &icred); if (ccache) krb5_cc_close(ctx, ccache); krb5_free_context(ctx); } *min = ret; return ret ? GSS_S_FAILURE : GSS_S_COMPLETE; } static OM_uint32 get_local_def_creds(OM_uint32 *minor_status, struct gpp_name_handle *name, gss_cred_usage_t cred_usage, gss_cred_id_t *cred_handle) { gss_OID_set interposed_mechs = GSS_C_NO_OID_SET; gss_OID_set special_mechs = GSS_C_NO_OID_SET; OM_uint32 maj, min; maj = GSS_S_FAILURE; min = 0; interposed_mechs = gss_mech_interposer((gss_OID)&gssproxy_mech_interposer); if (interposed_mechs == GSS_C_NO_OID_SET) { goto done; } special_mechs = gpp_special_available_mechs(interposed_mechs); if (special_mechs == GSS_C_NO_OID_SET) { goto done; } maj = gss_acquire_cred(&min, name ? name->local : NULL, 0, special_mechs, cred_usage, cred_handle, NULL, NULL); done: *minor_status = min; (void)gss_release_oid_set(&min, &special_mechs); (void)gss_release_oid_set(&min, &interposed_mechs); return maj; } OM_uint32 gppint_get_def_creds(OM_uint32 *minor_status, enum gpp_behavior behavior, struct gpp_name_handle *name, gss_cred_usage_t cred_usage, struct gpp_cred_handle **cred_handle) { struct gpp_cred_handle *cred; OM_uint32 tmaj = GSS_S_COMPLETE; OM_uint32 tmin = 0; OM_uint32 maj = GSS_S_FAILURE; OM_uint32 min = 0; if (*cred_handle) { cred = *cred_handle; } else { maj = gpp_cred_handle_init(&min, true, NULL, &cred); if (maj != GSS_S_COMPLETE) { *minor_status = min; return maj; } } /* See if we should try local first */ if (behavior == GPP_LOCAL_ONLY || behavior == GPP_LOCAL_FIRST) { maj = get_local_def_creds(&min, name, cred_usage, &cred->local); if (maj == GSS_S_COMPLETE || behavior == GPP_LOCAL_ONLY) { goto done; } /* not successful, save actual local error if remote fallback fails */ tmaj = maj; tmin = min; } /* Then try with remote */ if (behavior != GPP_LOCAL_ONLY) { gssx_cred remote; gssx_cred *premote = NULL; memset(&remote, 0, sizeof(gssx_cred)); /* We intentionally ignore failures as finding creds is optional */ maj = gppint_retrieve_remote_creds(&min, NULL, name ? name->remote : NULL, &remote); if (maj == GSS_S_COMPLETE) { premote = &remote; } maj = gpm_acquire_cred(&min, premote, NULL, 0, NULL, cred_usage, false, &cred->remote, NULL, NULL); if (maj == GSS_S_COMPLETE) { if (premote && !gpp_creds_are_equal(premote, cred->remote)) { maj = gpp_store_remote_creds(&min, cred->default_creds, &cred->store, cred->remote); } } xdr_free((xdrproc_t)xdr_gssx_cred, (char *)&remote); if (maj == GSS_S_COMPLETE) { goto done; } if (behavior == GPP_REMOTE_FIRST) { /* So remote failed, but we can fallback to local, try that */ maj = get_local_def_creds(&min, name, cred_usage, &cred->local); } } done: if (maj != GSS_S_COMPLETE && tmaj != GSS_S_COMPLETE) { maj = tmaj; min = tmin; } *minor_status = min; if (maj != GSS_S_COMPLETE) { if (cred != *cred_handle) { gssi_release_cred(&min, (gss_cred_id_t *)&cred); } } *cred_handle = cred; return maj; } OM_uint32 gssi_inquire_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_name_t *name, OM_uint32 *lifetime, gss_cred_usage_t *cred_usage, gss_OID_set *mechanisms) { struct gpp_cred_handle *cred = NULL; struct gpp_name_handle *gpname = NULL; OM_uint32 maj, min; GSSI_TRACE(); if (cred_handle == GSS_C_NO_CREDENTIAL) { maj = gppint_get_def_creds(&min, gpp_get_behavior(), NULL, GSS_C_INITIATE, &cred); if (maj != GSS_S_COMPLETE) { goto done; } } else { cred = (struct gpp_cred_handle *)cred_handle; } if (name) { gpname = calloc(1, sizeof(struct gpp_name_handle)); if (!gpname) { min = ENOMEM; maj = GSS_S_FAILURE; goto done; } } if (cred->local) { maj = gss_inquire_cred(&min, cred->local, gpname ? &gpname->local : NULL, lifetime, cred_usage, mechanisms); } else if (cred->remote) { maj = gpm_inquire_cred(&min, cred->remote, gpname ? &gpname->remote : NULL, lifetime, cred_usage, mechanisms); } else { min = 0; maj = GSS_S_FAILURE; } done: *minor_status = gpp_map_error(min); if (cred_handle == GSS_C_NO_CREDENTIAL) { gssi_release_cred(&min, (gss_cred_id_t*)&cred); } if (name && maj == GSS_S_COMPLETE) { *name = (gss_name_t)gpname; } else { free(gpname); } return maj; } OM_uint32 gssi_inquire_cred_by_mech(OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_OID mech_type, gss_name_t *name, OM_uint32 *initiator_lifetime, OM_uint32 *acceptor_lifetime, gss_cred_usage_t *cred_usage) { struct gpp_cred_handle *cred = NULL; struct gpp_name_handle *gpname = NULL; OM_uint32 maj, min; GSSI_TRACE(); if (cred_handle == GSS_C_NO_CREDENTIAL) { maj = gppint_get_def_creds(&min, gpp_get_behavior(), NULL, GSS_C_INITIATE, &cred); if (maj != GSS_S_COMPLETE) { goto done; } } else { cred = (struct gpp_cred_handle *)cred_handle; } if (name) { gpname = calloc(1, sizeof(struct gpp_name_handle)); if (!gpname) { min = ENOMEM; maj = GSS_S_FAILURE; goto done; } } if (cred->local) { maj = gss_inquire_cred_by_mech(&min, cred->local, gpp_special_mech(mech_type), gpname ? &gpname->local : NULL, initiator_lifetime, acceptor_lifetime, cred_usage); } else if (cred->remote) { maj = gpm_inquire_cred_by_mech(&min, cred->remote, gpp_unspecial_mech(mech_type), gpname ? &gpname->remote : NULL, initiator_lifetime, acceptor_lifetime, cred_usage); } else { min = 0; maj = GSS_S_FAILURE; } done: *minor_status = gpp_map_error(min); if (cred_handle == GSS_C_NO_CREDENTIAL) { gssi_release_cred(&min, (gss_cred_id_t*)&cred); } if (name && maj == GSS_S_COMPLETE) { *name = (gss_name_t)gpname; } else { free(gpname); } return maj; } OM_uint32 gssi_inquire_cred_by_oid(OM_uint32 *minor_status, const gss_cred_id_t cred_handle, const gss_OID desired_object, gss_buffer_set_t *data_set) { struct gpp_cred_handle *cred = NULL; OM_uint32 maj, min; GSSI_TRACE(); *minor_status = 0; if (cred_handle == GSS_C_NO_CREDENTIAL) { return GSS_S_CALL_INACCESSIBLE_READ; } cred = (struct gpp_cred_handle *)cred_handle; /* NOTE: For now we can do this only for local credentials, * but as far as I know there is no real oid defined, at least * for the krb5 mechs, so this may be a mooot point */ if (!cred->local) { return GSS_S_UNAVAILABLE; } maj = gss_inquire_cred_by_oid(&min, cred->local, desired_object, data_set); *minor_status = gpp_map_error(min); return maj; } #define GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID_LENGTH 11 #define GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x04" const gss_OID_desc gpp_allowed_enctypes_oid = { .length = GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID_LENGTH, .elements = GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID }; struct gpp_allowable_enctypes { uint32_t num_ktypes; krb5_enctype *ktypes; }; #define KRB5_SET_ALLOWED_ENCTYPE "krb5_set_allowed_enctype_values" static uint32_t gpp_set_opt_allowable_entypes(uint32_t *min, gssx_cred *cred, const gss_buffer_t value) { struct gpp_allowable_enctypes *ae; struct gssx_cred_element *ce = NULL; gss_OID_desc mech; /* Find the first element that matches one of the krb related OIDs */ for (unsigned i = 0; i < cred->elements.elements_len; i++) { gp_conv_gssx_to_oid(&cred->elements.elements_val[i].mech, &mech); if (gpp_is_krb5_oid(&mech)) { ce = &cred->elements.elements_val[i]; break; } } if (!ce) { *min = EINVAL; return GSS_S_FAILURE; } ae = (struct gpp_allowable_enctypes *)value->value; *min = gp_add_option(&ce->options.options_val, &ce->options.options_len, KRB5_SET_ALLOWED_ENCTYPE, sizeof(KRB5_SET_ALLOWED_ENCTYPE), ae->ktypes, sizeof(krb5_enctype) * ae->num_ktypes); if (*min != 0) { return GSS_S_FAILURE; } return GSS_S_COMPLETE; } #define KRB5_SET_NO_CI_FLAGS "krb5_set_no_ci_flags" static uint32_t gpp_set_no_ci_flags(uint32_t *min, gssx_cred *cred, const gss_buffer_t value) { struct gssx_cred_element *ce = NULL; gss_OID_desc mech; /* Find the first element that matches one of the krb related OIDs */ for (unsigned i = 0; i < cred->elements.elements_len; i++) { gp_conv_gssx_to_oid(&cred->elements.elements_val[i].mech, &mech); if (gpp_is_krb5_oid(&mech)) { ce = &cred->elements.elements_val[i]; break; } } if (!ce) { *min = EINVAL; return GSS_S_FAILURE; } *min = gp_add_option(&ce->options.options_val, &ce->options.options_len, KRB5_SET_NO_CI_FLAGS, sizeof(KRB5_SET_NO_CI_FLAGS), NULL, 0); if (*min != 0) { return GSS_S_FAILURE; } return GSS_S_COMPLETE; } static uint32_t gpp_remote_options(uint32_t *min, gssx_cred *cred, const gss_OID desired_object, const gss_buffer_t value) { uint32_t maj = GSS_S_UNAVAILABLE; if (gss_oid_equal(&gpp_allowed_enctypes_oid, desired_object)) { maj = gpp_set_opt_allowable_entypes(min, cred, value); } else if (gss_oid_equal(GSS_KRB5_CRED_NO_CI_FLAGS_X, desired_object)) { maj = gpp_set_no_ci_flags(min, cred, value); } return maj; } OM_uint32 gssi_set_cred_option(OM_uint32 *minor_status, gss_cred_id_t *cred_handle, const gss_OID desired_object, const gss_buffer_t value) { struct gpp_cred_handle *cred = NULL; OM_uint32 maj, min; GSSI_TRACE(); *minor_status = 0; if (*cred_handle == GSS_C_NO_CREDENTIAL) { return GSS_S_CALL_INACCESSIBLE_READ; } cred = (struct gpp_cred_handle *)*cred_handle; /* NOTE: For now we can do this only for known objects * or local credentials */ if (cred->remote) { return gpp_remote_options(minor_status, cred->remote, desired_object, value); } if (!cred->local) { return GSS_S_UNAVAILABLE; } maj = gss_set_cred_option(&min, &cred->local, desired_object, value); *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_store_cred(OM_uint32 *minor_status, const gss_cred_id_t input_cred_handle, gss_cred_usage_t input_usage, const gss_OID desired_mech, OM_uint32 overwrite_cred, OM_uint32 default_cred, gss_OID_set *elements_stored, gss_cred_usage_t *cred_usage_stored) { return gssi_store_cred_into(minor_status, input_cred_handle, input_usage, desired_mech, overwrite_cred, default_cred, NULL, elements_stored, cred_usage_stored); } OM_uint32 gssi_store_cred_into(OM_uint32 *minor_status, const gss_cred_id_t input_cred_handle, gss_cred_usage_t input_usage, const gss_OID desired_mech, OM_uint32 overwrite_cred, OM_uint32 default_cred, gss_const_key_value_set_t cred_store, gss_OID_set *elements_stored, gss_cred_usage_t *cred_usage_stored) { struct gpp_cred_handle *cred = NULL; OM_uint32 maj, min; GSSI_TRACE(); *minor_status = 0; if (input_cred_handle == GSS_C_NO_CREDENTIAL) { return GSS_S_CALL_INACCESSIBLE_READ; } cred = (struct gpp_cred_handle *)input_cred_handle; if (cred->remote) { maj = gpp_store_remote_creds(&min, default_cred != 0, cred_store, cred->remote); goto done; } maj = gss_store_cred_into(&min, cred->local, input_usage, gpp_special_mech(desired_mech), overwrite_cred, default_cred, cred_store, elements_stored, cred_usage_stored); done: *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle) { struct gpp_cred_handle *handle; uint32_t tmaj; uint32_t tmin; uint32_t maj; uint32_t min; GSSI_TRACE(); if (cred_handle == NULL) { return GSS_S_CALL_INACCESSIBLE_READ; } handle = (struct gpp_cred_handle *)*cred_handle; tmaj = gpm_release_cred(&tmin, &handle->remote); maj = gpp_cred_handle_free(&min, handle); if (tmaj && maj == GSS_S_COMPLETE) { maj = tmaj; min = tmin; } *cred_handle = GSS_C_NO_CREDENTIAL; *minor_status = min; return maj; } OM_uint32 gssi_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_buffer_t token) { struct gpp_cred_handle *cred; GSSI_TRACE(); cred = (struct gpp_cred_handle *)cred_handle; if (!cred) { return GSS_S_CALL_INACCESSIBLE_READ; } /* We do not support exporting creds via the proxy. * It's exclusively a local operation for now */ if (!cred->local) { return GSS_S_CRED_UNAVAIL; } return gss_export_cred(minor_status, cred->local, token); } OM_uint32 gssi_import_cred(OM_uint32 *minor_status, gss_buffer_t token, gss_cred_id_t *cred_handle) { GSSI_TRACE(); return GSS_S_UNAVAILABLE; } OM_uint32 gssi_import_cred_by_mech(OM_uint32 *minor_status, gss_OID mech_type, gss_buffer_t token, gss_cred_id_t *cred_handle) { struct gpp_cred_handle *cred; gss_buffer_desc wrap_token = {0}; gss_OID spmech; OM_uint32 maj, min = 0; uint32_t len; GSSI_TRACE(); maj = gpp_cred_handle_init(minor_status, false, NULL, &cred); if (maj) { return maj; } /* NOTE: it makes no sense to import a cred remotely atm, * so we only handle the local case for now. */ spmech = gpp_special_mech(mech_type); if (spmech == GSS_C_NO_OID) { maj = GSS_S_FAILURE; goto done; } wrap_token.length = sizeof(uint32_t) + spmech->length + token->length; wrap_token.value = malloc(wrap_token.length); if (!wrap_token.value) { wrap_token.length = 0; maj = GSS_S_FAILURE; goto done; } len = htobe32(wrap_token.length); memcpy(wrap_token.value, &len, sizeof(uint32_t)); memcpy(wrap_token.value + sizeof(uint32_t), spmech->elements, spmech->length); memcpy(wrap_token.value + sizeof(uint32_t) + spmech->length, token->value, token->length); maj = gss_import_cred(&min, &wrap_token, &cred->local); done: *minor_status = gpp_map_error(min); if (maj == GSS_S_COMPLETE) { *cred_handle = (gss_cred_id_t)cred; } else { free(cred); } (void)gss_release_buffer(&min, &wrap_token); return maj; } gssproxy-v0.8.2/src/mechglue/gpp_display_status.c0000644000174300017420000000277513456641744021673 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "gss_plugin.h" OM_uint32 gssi_display_status(OM_uint32 *minor_status, OM_uint32 status_value, int status_type, const gss_OID mech_type, OM_uint32 *message_context, gss_buffer_t status_string) { OM_uint32 maj, min, val; GSSI_TRACE(); /* This function is only ever called for minor status values */ if (status_type != GSS_C_MECH_CODE) { return GSS_S_BAD_STATUS; } val = gpp_unmap_error(status_value); maj = gpm_display_status(&min, val, GSS_C_MECH_CODE, GSS_C_NO_OID, message_context, status_string); /* if we do not have a matching saved error code * try to see if we can come up with one from the * mechglue by re-entering it. * We do not spcify the mech in this case it's not used by * the mechglue anyways */ if (maj == GSS_S_UNAVAILABLE) { return gss_display_status(minor_status, val, GSS_C_MECH_CODE, GSS_C_NO_OID, message_context, status_string); } *minor_status = min; return maj; } gssproxy-v0.8.2/src/mechglue/gpp_import_and_canon_name.c0000644000174300017420000002717513456641744023136 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "gss_plugin.h" OM_uint32 gssi_display_name(OM_uint32 *minor_status, gss_name_t input_name, gss_buffer_t output_name_buffer, gss_OID *output_name_type) { struct gpp_name_handle *name; OM_uint32 maj, min = 0; output_name_buffer->length = 0; output_name_buffer->value = NULL; if (output_name_type) *output_name_type = GSS_C_NO_OID; GSSI_TRACE(); name = (struct gpp_name_handle *)input_name; if (!name->local && !name->remote) { return GSS_S_BAD_NAME; } if (name->local) { maj = gss_display_name(&min, name->local, output_name_buffer, output_name_type); } else { maj = gpm_display_name(&min, name->remote, output_name_buffer, output_name_type); } *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_display_name_ext(OM_uint32 *minor_status, gss_name_t input_name, gss_OID display_as_name_type, gss_buffer_t display_name) { struct gpp_name_handle *name; OM_uint32 maj, min = 0; GSSI_TRACE(); name = (struct gpp_name_handle *)input_name; if (!name->local && !name->remote) { return GSS_S_BAD_NAME; } if (name->local) { maj = gss_display_name_ext(&min, name->local, display_as_name_type, display_name); } else { /* FIXME: Implement remote function ? * Or export/import via local mechanism ? */ maj = GSS_S_UNAVAILABLE; } *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name) { GSSI_TRACE(); return GSS_S_UNAVAILABLE; } OM_uint32 gssi_import_name_by_mech(OM_uint32 *minor_status, gss_OID mech_type, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name) { struct gpp_name_handle *name; OM_uint32 maj, min = 0; GSSI_TRACE(); if (mech_type == GSS_C_NO_OID) { return GSS_S_CALL_INACCESSIBLE_READ; } name = calloc(1, sizeof(struct gpp_name_handle)); if (!name) { *minor_status = gpp_map_error(ENOMEM); return GSS_S_FAILURE; } maj = gpp_copy_oid(&min, mech_type, &name->mech_type); if (maj != GSS_S_COMPLETE) { goto done; } /* Always use remote name by default, otherwise canonicalization * will loose information about the original name, for example * it will convert names of the special type GSS_C_NT_STRING_UID_NAME * or GSS_NT_MACHINE_UID_NAME in a non reversible way and the proxy * will not be able to use them as intended (for impersonation by * trusted services) */ maj = gpm_import_name(&min, input_name_buffer, input_name_type, &name->remote); if (maj != GSS_S_COMPLETE) { goto done; } done: *minor_status = gpp_map_error(min); if (maj != GSS_S_COMPLETE) { (void)gss_release_oid(&min, &name->mech_type); (void)gpm_release_name(&min, &name->remote); free(name); } else { *output_name = (gss_name_t)name; } return maj; } /* OM_uint32 gssi_export_name(OM_uint32 *minor_status, const gss_name_t input_name, gss_buffer_t exported_name) */ #if 0 /* disabled until better understood */ OM_uint32 gssi_export_name_composite(OM_uint32 *minor_status, const gss_name_t input_name, gss_buffer_t exported_composite_name) { struct gpp_name_handle *name; OM_uint32 maj, min = 0; GSSI_TRACE(); name = (struct gpp_name_handle *)input_name; if (!name->local && !name->remote) { return GSS_S_BAD_NAME; } if (name->local) { maj = gss_export_name_composite(&min, name->local, exported_composite_name); } else { maj = gpm_export_name_composite(&min, name->remote, exported_composite_name); } *minor_status = gpp_map_error(min); return maj; } #endif OM_uint32 gssi_duplicate_name(OM_uint32 *minor_status, const gss_name_t input_name, gss_name_t *dest_name) { struct gpp_name_handle *in_name; struct gpp_name_handle *out_name; OM_uint32 maj, min = 0; GSSI_TRACE(); in_name = (struct gpp_name_handle *)input_name; if (!in_name->local && !in_name->remote) { return GSS_S_BAD_NAME; } out_name = calloc(1, sizeof(struct gpp_name_handle)); if (!out_name) { *minor_status = gpp_map_error(ENOMEM); return GSS_S_FAILURE; } if (in_name->mech_type) { maj = gpp_copy_oid(&min, in_name->mech_type, &out_name->mech_type); if (maj != GSS_S_COMPLETE) { goto done; } } if (in_name->remote) { maj = gpm_duplicate_name(&min, in_name->remote, &out_name->remote); } else { maj = gss_duplicate_name(&min, in_name->local, &out_name->local); } done: *minor_status = gpp_map_error(min); if (maj != GSS_S_COMPLETE) { (void)gss_release_oid(&min, &out_name->mech_type); free(out_name); } else { *dest_name = (gss_name_t)out_name; } return maj; } OM_uint32 gssi_inquire_name(OM_uint32 *minor_status, gss_name_t input_name, int *name_is_NM, gss_OID *NM_mech, gss_buffer_set_t *attrs) { struct gpp_name_handle *name; OM_uint32 maj, min = 0; GSSI_TRACE(); name = (struct gpp_name_handle *)input_name; if (!name->local && !name->remote) { return GSS_S_BAD_NAME; } if (name->local) { maj = gss_inquire_name(&min, name->local, name_is_NM, NM_mech, attrs); } else { maj = gpm_inquire_name(&min, name->remote, name_is_NM, NM_mech, attrs); } *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_release_name(OM_uint32 *minor_status, gss_name_t *input_name) { struct gpp_name_handle *name; uint32_t rmaj, rmin = 0; OM_uint32 maj = 0, min = 0; GSSI_TRACE(); name = (struct gpp_name_handle *)*input_name; if (!name || (!name->local && !name->remote)) { return GSS_S_BAD_NAME; } rmaj = gpm_release_name(&rmin, &name->remote); if (name->local) { maj = gss_release_name(&min, &name->local); } free(name); *input_name = GSS_C_NO_NAME; if (rmaj && !maj) { maj = rmaj; min = rmin; } *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_compare_name(OM_uint32 *minor_status, gss_name_t name1, gss_name_t name2, int *name_equal) { struct gpp_name_handle *gpname1; struct gpp_name_handle *gpname2; OM_uint32 maj, min = 0; GSSI_TRACE(); gpname1 = (struct gpp_name_handle *)name1; gpname2 = (struct gpp_name_handle *)name2; if (gpname1->local || gpname2->local) { if (!gpname1->local) { if (!gpname1->remote){ return GSS_S_CALL_INACCESSIBLE_READ; } maj = gpp_name_to_local(&min, gpname1->remote, gpname1->mech_type, &gpname1->local); if (maj != GSS_S_COMPLETE) { goto done; } } if (!gpname2->local) { if (!gpname2->remote){ return GSS_S_CALL_INACCESSIBLE_READ; } maj = gpp_name_to_local(&min, gpname2->remote, gpname2->mech_type, &gpname2->local); if (maj != GSS_S_COMPLETE) { goto done; } } maj = gss_compare_name(&min, gpname1->local, gpname2->local, name_equal); goto done; } if (!gpname1->remote && !gpname2->remote) { return GSS_S_CALL_INACCESSIBLE_READ; } maj = gpm_compare_name(&min, gpname1->remote, gpname2->remote, name_equal); done: *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_get_name_attribute(OM_uint32 *minor_status, gss_name_t input_name, gss_buffer_t attr, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { struct gpp_name_handle *name; OM_uint32 maj, min = 0; GSSI_TRACE(); name = (struct gpp_name_handle *)input_name; if (!name->local && !name->remote) { return GSS_S_BAD_NAME; } if (name->local) { maj = gss_get_name_attribute(&min, name->local, attr, authenticated, complete, value, display_value, more); } else { /* FIXME: Implement retrieving remote attributes! */ maj = GSS_S_UNAVAILABLE; } *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_set_name_attribute(OM_uint32 *minor_status, gss_name_t input_name, int complete, gss_buffer_t attr, gss_buffer_t value) { struct gpp_name_handle *name; OM_uint32 maj, min = 0; GSSI_TRACE(); name = (struct gpp_name_handle *)input_name; if (!name->local && !name->remote) { return GSS_S_BAD_NAME; } if (name->local) { maj = gss_set_name_attribute(&min, name->local, complete, attr, value); } else { /* FIXME: Implement retrieving remote attributes! */ maj = GSS_S_UNAVAILABLE; } *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_delete_name_attribute(OM_uint32 *minor_status, gss_name_t input_name, gss_buffer_t attr) { struct gpp_name_handle *name; OM_uint32 maj, min = 0; GSSI_TRACE(); name = (struct gpp_name_handle *)input_name; if (!name->local && !name->remote) { return GSS_S_BAD_NAME; } if (name->local) { maj = gss_delete_name_attribute(&min, name->local, attr); } else { /* FIXME: Implement retrieving remote attributes! */ maj = GSS_S_UNAVAILABLE; } *minor_status = gpp_map_error(min); return maj; } gssproxy-v0.8.2/src/mechglue/gpp_indicate_mechs.c0000644000174300017420000001311313456641744021546 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "gss_plugin.h" /* This will never be called, added only for completeness */ OM_uint32 gssi_indicate_mechs(OM_uint32 *minor_status, gss_OID_set *mech_set) { GSSI_TRACE(); *minor_status = 0; return GSS_S_FAILURE; } OM_uint32 gssi_inquire_names_for_mech(OM_uint32 *minor_status, gss_OID mech_type, gss_OID_set *mech_names) { enum gpp_behavior behavior; OM_uint32 tmaj, tmin; OM_uint32 maj, min; GSSI_TRACE(); behavior = gpp_get_behavior(); tmaj = GSS_S_COMPLETE; tmin = 0; /* See if we should try local first */ if (behavior == GPP_LOCAL_ONLY || behavior == GPP_LOCAL_FIRST) { maj = gss_inquire_names_for_mech(&min, gpp_special_mech(mech_type), mech_names); if (maj == GSS_S_COMPLETE || behavior == GPP_LOCAL_ONLY) { goto done; } /* not successful, save actual local error if remote fallback fails */ tmaj = maj; tmin = min; } /* Then try with remote */ if (behavior != GPP_LOCAL_ONLY) { maj = gpm_inquire_names_for_mech(&min, mech_type, mech_names); if (maj == GSS_S_COMPLETE || behavior == GPP_REMOTE_ONLY) { goto done; } /* So remote failed, but we can fallback to local, try that */ maj = gss_inquire_names_for_mech(&min, gpp_special_mech(mech_type), mech_names); } done: if (maj != GSS_S_COMPLETE && tmaj != GSS_S_COMPLETE) { maj = tmaj; min = tmin; } *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_inquire_attrs_for_mech(OM_uint32 *minor_status, gss_OID mech, gss_OID_set *mech_attrs, gss_OID_set *known_mech_attrs) { enum gpp_behavior behavior; OM_uint32 tmaj, tmin; OM_uint32 maj, min; GSSI_TRACE(); behavior = gpp_get_behavior(); tmaj = GSS_S_COMPLETE; tmin = 0; /* See if we should try local first */ if (behavior == GPP_LOCAL_ONLY || behavior == GPP_LOCAL_FIRST) { maj = gss_inquire_attrs_for_mech(&min, gpp_special_mech(mech), mech_attrs, known_mech_attrs); if (maj == GSS_S_COMPLETE || behavior == GPP_LOCAL_ONLY) { goto done; } /* not successful, save actual local error if remote fallback fails */ tmaj = maj; tmin = min; } /* Then try with remote */ if (behavior != GPP_LOCAL_ONLY) { maj = gpm_inquire_attrs_for_mech(&min, mech, mech_attrs, known_mech_attrs); if (maj == GSS_S_COMPLETE || behavior == GPP_REMOTE_ONLY) { goto done; } /* So remote failed, but we can fallback to local, try that */ maj = gss_inquire_attrs_for_mech(&min, gpp_special_mech(mech), mech_attrs, known_mech_attrs); } done: if (maj != GSS_S_COMPLETE && tmaj != GSS_S_COMPLETE) { maj = tmaj; min = tmin; } *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_inquire_saslname_for_mech(OM_uint32 *minor_status, const gss_OID desired_mech, gss_buffer_t sasl_mech_name, gss_buffer_t mech_name, gss_buffer_t mech_description) { enum gpp_behavior behavior; OM_uint32 tmaj, tmin; OM_uint32 maj, min; GSSI_TRACE(); behavior = gpp_get_behavior(); tmaj = GSS_S_COMPLETE; tmin = 0; /* See if we should try local first */ if (behavior == GPP_LOCAL_ONLY || behavior == GPP_LOCAL_FIRST) { maj = gss_inquire_saslname_for_mech(&min, gpp_special_mech(desired_mech), sasl_mech_name, mech_name, mech_description); if (maj == GSS_S_COMPLETE || behavior == GPP_LOCAL_ONLY) { goto done; } /* not successful, save actual local error if remote fallback fails */ tmaj = maj; tmin = min; } /* Then try with remote */ if (behavior != GPP_LOCAL_ONLY) { maj = gpm_inquire_saslname_for_mech(&min, desired_mech, sasl_mech_name, mech_name, mech_description); if (maj == GSS_S_COMPLETE || behavior == GPP_REMOTE_ONLY) { goto done; } /* So remote failed, but we can fallback to local, try that */ maj = gss_inquire_saslname_for_mech(&min, gpp_special_mech(desired_mech), sasl_mech_name, mech_name, mech_description); } done: if (maj != GSS_S_COMPLETE && tmaj != GSS_S_COMPLETE) { maj = tmaj; min = tmin; } *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_inquire_mech_for_saslname(OM_uint32 *minor_status, const gss_buffer_t sasl_mech_name, gss_OID *mech_type) { GSSI_TRACE(); /* FIXME: How to call into mechglue ? */ return GSS_S_UNAVAILABLE; } gssproxy-v0.8.2/src/mechglue/gpp_init_sec_context.c0000644000174300017420000001755313456641744022164 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "gss_plugin.h" static OM_uint32 init_ctx_local(OM_uint32 *minor_status, struct gpp_cred_handle *cred_handle, struct gpp_context_handle *ctx_handle, struct gpp_name_handle *name, gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, gss_channel_bindings_t input_cb, gss_buffer_t input_token, gss_OID *actual_mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec) { OM_uint32 maj, min; if (name->remote && !name->local) { maj = gpp_name_to_local(&min, name->remote, mech_type, &name->local); if (maj) { goto done; } } maj = gss_init_sec_context(&min, cred_handle->local, &ctx_handle->local, name->local, gpp_special_mech(mech_type), req_flags, time_req, input_cb, input_token, actual_mech_type, output_token, ret_flags, time_rec); done: *minor_status = min; return maj; } OM_uint32 gssi_init_sec_context(OM_uint32 *minor_status, gss_cred_id_t claimant_cred_handle, gss_ctx_id_t *context_handle, gss_name_t target_name, gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, gss_channel_bindings_t input_cb, gss_buffer_t input_token, gss_OID *actual_mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec) { enum gpp_behavior behavior = GPP_UNINITIALIZED; struct gpp_context_handle *ctx_handle = NULL; struct gpp_cred_handle *cred_handle = NULL; gssx_cred *out_cred = NULL; struct gpp_name_handle *name; OM_uint32 tmaj, tmin; OM_uint32 maj, min; GSSI_TRACE(); *minor_status = 0; if (target_name == GSS_C_NO_NAME) { return GSS_S_CALL_INACCESSIBLE_READ; } if (mech_type == GSS_C_NO_OID || gpp_is_special_oid(mech_type)) { return GSS_S_BAD_MECH; } tmaj = GSS_S_COMPLETE; tmin = 0; if (*context_handle) { ctx_handle = (struct gpp_context_handle *)*context_handle; if (ctx_handle->local) { /* ok this means a previous call decided to use the local mech, * so let's just re-enter the mechglue here and keep at it */ behavior = GPP_LOCAL_ONLY; } } else { ctx_handle = calloc(1, sizeof(struct gpp_context_handle)); if (!ctx_handle) { maj = GSS_S_FAILURE; min = ENOMEM; goto done; } } if (claimant_cred_handle != GSS_C_NO_CREDENTIAL) { cred_handle = (struct gpp_cred_handle *)claimant_cred_handle; if (cred_handle->local) { /* ok this means a previous call decided to short circuit to the * local mech, so let's just re-enter the mechglue here, as we * have no way to export creds yet. */ behavior = GPP_LOCAL_ONLY; } else if (behavior == GPP_LOCAL_ONLY) { maj = GSS_S_DEFECTIVE_CREDENTIAL; min = 0; goto done; } } else { maj = gpp_cred_handle_init(&min, true, NULL, &cred_handle); if (maj) { goto done; } } name = (struct gpp_name_handle *)target_name; if (behavior == GPP_UNINITIALIZED) { behavior = gpp_get_behavior(); } /* See if we should try local first */ if (behavior == GPP_LOCAL_ONLY || behavior == GPP_LOCAL_FIRST) { maj = init_ctx_local(&min, cred_handle, ctx_handle, name, mech_type, req_flags, time_req, input_cb, input_token, actual_mech_type, output_token, ret_flags, time_rec); if (maj == GSS_S_COMPLETE || maj == GSS_S_CONTINUE_NEEDED || behavior == GPP_LOCAL_ONLY) { goto done; } /* not successful, save actual local error if remote fallback fails */ tmaj = maj; tmin = min; } /* Then try with remote */ if (behavior != GPP_LOCAL_ONLY) { if (name->local && !name->remote) { maj = gpp_local_to_name(&min, name->local, &name->remote); if (maj) { goto done; } } if (!cred_handle->remote) { /* we ignore failures here */ (void)gppint_get_def_creds(&min, GPP_REMOTE_ONLY, NULL, GSS_C_INITIATE, &cred_handle); } maj = gpm_init_sec_context(&min, cred_handle->remote, &ctx_handle->remote, name->remote, mech_type, req_flags, time_req, input_cb, input_token, actual_mech_type, output_token, ret_flags, time_rec, &out_cred); if (maj == GSS_S_COMPLETE || maj == GSS_S_CONTINUE_NEEDED) { if (out_cred) { xdr_free((xdrproc_t)xdr_gssx_cred, (char *)cred_handle->remote); free(cred_handle->remote); cred_handle->remote = out_cred; out_cred = NULL; /* failuire is not fatal */ (void)gpp_store_remote_creds(&tmin, cred_handle->default_creds, &cred_handle->store, cred_handle->remote); } goto done; } if (behavior == GPP_REMOTE_FIRST) { /* So remote failed, but we can fallback to local, try that */ maj = init_ctx_local(&min, cred_handle, ctx_handle, name, mech_type, req_flags, time_req, input_cb, input_token, actual_mech_type, output_token, ret_flags, time_rec); } } done: if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED && tmaj != GSS_S_COMPLETE) { maj = tmaj; min = tmin; } if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) { if (ctx_handle && ctx_handle->local == GSS_C_NO_CONTEXT && ctx_handle->remote == NULL) { free(ctx_handle); ctx_handle = NULL; } *minor_status = gpp_map_error(min); } /* always replace the provided context handle to avoid * dangling pointers when a context has been passed in */ *context_handle = (gss_ctx_id_t)ctx_handle; if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { free(cred_handle); } return maj; } gssproxy-v0.8.2/src/mechglue/gpp_misc.c0000644000174300017420000001160713456641744017550 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "gss_plugin.h" OM_uint32 gssi_mech_invoke(OM_uint32 *minor_status, const gss_OID desired_mech, const gss_OID desired_object, gss_buffer_t value) { enum gpp_behavior behavior; OM_uint32 maj, min; GSSI_TRACE(); /* FIXME: implement remote invoke mech, only local for now */ behavior = gpp_get_behavior(); if (behavior == GPP_REMOTE_ONLY) { return GSS_S_UNAVAILABLE; } maj = gssspi_mech_invoke(&min, gpp_special_mech(desired_mech), desired_object, value); *minor_status = gpp_map_error(min); return maj; } /* NOTE: This call is currently useful only for the Spnego mech which we * never interpose */ #if 0 OM_uint32 gssi_set_neg_mechs(OM_uint32 *minor_status, gss_cred_id_t cred_handle, const gss_OID_set mech_set); #endif /* NOTE: I know of no mechanism that uses this yet, although NTLM might */ #if 0 OM_uint32 gssi_complete_auth_token(OM_uint32 *minor_status, const gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer); #endif OM_uint32 gssi_localname(OM_uint32 *minor_status, const gss_name_t name, gss_OID mech_type, gss_buffer_t localname) { struct gpp_name_handle *gpname; OM_uint32 maj, min; GSSI_TRACE(); *minor_status = 0; if (name == GSS_C_NO_NAME) { return GSS_S_CALL_INACCESSIBLE_READ; } /* FIXME: implement remote localname lookup ? Only local for now */ gpname = (struct gpp_name_handle *)name; if (gpname->remote && !gpname->local) { maj = gpp_name_to_local(&min, gpname->remote, mech_type, &gpname->local); if (maj) { goto done; } } maj = gss_localname(&min, gpname->local, gpp_special_mech(mech_type), localname); done: *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_authorize_localname(OM_uint32 *minor_status, const gss_name_t name, gss_buffer_t local_user, gss_OID local_nametype) { struct gpp_name_handle *gpname; gss_name_t username = GSS_C_NO_NAME; OM_uint32 maj, min; GSSI_TRACE(); *minor_status = 0; if (name == GSS_C_NO_NAME) { return GSS_S_CALL_INACCESSIBLE_READ; } /* FIXME: implement remote localname lookup ? Only local for now */ gpname = (struct gpp_name_handle *)name; if (gpname->remote && !gpname->local) { maj = gpp_name_to_local(&min, gpname->remote, gpname->mech_type, &gpname->local); if (maj != GSS_S_COMPLETE) { goto done; } } maj = gss_import_name(&min, local_user, local_nametype, &username); if (maj != GSS_S_COMPLETE) { goto done; } maj = gss_authorize_localname(&min, gpname->local, username); done: *minor_status = gpp_map_error(min); (void)gss_release_name(&min, &username); return maj; } OM_uint32 gssi_map_name_to_any(OM_uint32 *minor_status, gss_name_t name, int authenticated, gss_buffer_t type_id, gss_any_t *output) { struct gpp_name_handle *gpname; OM_uint32 maj, min; GSSI_TRACE(); *minor_status = 0; if (name == GSS_C_NO_NAME) { return GSS_S_CALL_INACCESSIBLE_READ; } /* FIXME: implement remote localname lookup ? Only local for now */ gpname = (struct gpp_name_handle *)name; if (gpname->remote && !gpname->local) { maj = gpp_name_to_local(&min, gpname->remote, gpname->mech_type, &gpname->local); if (maj != GSS_S_COMPLETE) { goto done; } } maj = gss_map_name_to_any(&min, gpname->local, authenticated, type_id, output); done: *minor_status = gpp_map_error(min); return maj; } OM_uint32 gssi_release_any_name_mapping(OM_uint32 *minor_status, gss_name_t name, gss_buffer_t type_id, gss_any_t *input) { struct gpp_name_handle *gpname; OM_uint32 maj, min; GSSI_TRACE(); *minor_status = 0; if (name == GSS_C_NO_NAME) { return GSS_S_CALL_INACCESSIBLE_READ; } /* FIXME: implement remote localname lookup ? Only local for now */ gpname = (struct gpp_name_handle *)name; if (!gpname->local) { return GSS_S_UNAVAILABLE; } maj = gss_release_any_name_mapping(&min, gpname->local, type_id, input); *minor_status = gpp_map_error(min); return maj; } gssproxy-v0.8.2/src/mechglue/gpp_priv_integ.c0000644000174300017420000002716413456641744020770 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "gss_plugin.h" OM_uint32 gssi_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, gss_buffer_t input_message_buffer, int *conf_state, gss_buffer_t output_message_buffer) { struct gpp_context_handle *ctx_handle; OM_uint32 maj, min; GSSI_TRACE(); ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we do encryption only locally and only for specific known * mechanisms for which we can export/import the context */ if (ctx_handle->remote && !ctx_handle->local) { maj = gpp_remote_to_local_ctx(&min, &ctx_handle->remote, &ctx_handle->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_wrap(minor_status, ctx_handle->local, conf_req_flag, qop_req, input_message_buffer, conf_state, output_message_buffer); } OM_uint32 gssi_wrap_size_limit(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, OM_uint32 req_output_size, OM_uint32 *max_input_size) { struct gpp_context_handle *ctx_handle; OM_uint32 maj, min; GSSI_TRACE(); ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we do encryption only locally and only for specific known * mechanisms for which we can export/import the context */ if (ctx_handle->remote && !ctx_handle->local) { maj = gpp_remote_to_local_ctx(&min, &ctx_handle->remote, &ctx_handle->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_wrap_size_limit(minor_status, ctx_handle->local, conf_req_flag, qop_req, req_output_size, max_input_size); } OM_uint32 gssi_wrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, int *conf_state, gss_iov_buffer_desc *iov, int iov_count) { struct gpp_context_handle *ctx_handle; OM_uint32 maj, min; GSSI_TRACE(); ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we do encryption only locally and only for specific known * mechanisms for which we can export/import the context */ if (ctx_handle->remote && !ctx_handle->local) { maj = gpp_remote_to_local_ctx(&min, &ctx_handle->remote, &ctx_handle->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_wrap_iov(minor_status, ctx_handle->local, conf_req_flag, qop_req, conf_state, iov, iov_count); } OM_uint32 gssi_wrap_iov_length(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, int *conf_state, gss_iov_buffer_desc *iov, int iov_count) { struct gpp_context_handle *ctx_handle; OM_uint32 maj, min; GSSI_TRACE(); ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we do encryption only locally and only for specific known * mechanisms for which we can export/import the context */ if (ctx_handle->remote && !ctx_handle->local) { maj = gpp_remote_to_local_ctx(&min, &ctx_handle->remote, &ctx_handle->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_wrap_iov_length(minor_status, ctx_handle->local, conf_req_flag, qop_req, conf_state, iov, iov_count); } OM_uint32 gssi_wrap_aead(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, gss_buffer_t input_assoc_buffer, gss_buffer_t input_payload_buffer, int *conf_state, gss_buffer_t output_message_buffer) { struct gpp_context_handle *ctx_handle; OM_uint32 maj, min; GSSI_TRACE(); ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we do encryption only locally and only for specific known * mechanisms for which we can export/import the context */ if (ctx_handle->remote && !ctx_handle->local) { maj = gpp_remote_to_local_ctx(&min, &ctx_handle->remote, &ctx_handle->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_wrap_aead(minor_status, ctx_handle->local, conf_req_flag, qop_req, input_assoc_buffer, input_payload_buffer, conf_state, output_message_buffer); } OM_uint32 gssi_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int *conf_state, gss_qop_t *qop_state) { struct gpp_context_handle *ctx_handle; OM_uint32 maj, min; GSSI_TRACE(); ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we do encryption only locally and only for specific known * mechanisms for which we can export/import the context */ if (ctx_handle->remote && !ctx_handle->local) { maj = gpp_remote_to_local_ctx(&min, &ctx_handle->remote, &ctx_handle->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_unwrap(minor_status, ctx_handle->local, input_message_buffer, output_message_buffer, conf_state, qop_state); } OM_uint32 gssi_unwrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int *conf_state, gss_qop_t *qop_state, gss_iov_buffer_desc *iov, int iov_count) { struct gpp_context_handle *ctx_handle; OM_uint32 maj, min; GSSI_TRACE(); ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we do encryption only locally and only for specific known * mechanisms for which we can export/import the context */ if (ctx_handle->remote && !ctx_handle->local) { maj = gpp_remote_to_local_ctx(&min, &ctx_handle->remote, &ctx_handle->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_unwrap_iov(minor_status, ctx_handle->local, conf_state, qop_state, iov, iov_count); } OM_uint32 gssi_unwrap_aead(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer, gss_buffer_t input_assoc_buffer, gss_buffer_t output_payload_buffer, int *conf_state, gss_qop_t *qop_state) { struct gpp_context_handle *ctx_handle; OM_uint32 maj, min; GSSI_TRACE(); ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we do encryption only locally and only for specific known * mechanisms for which we can export/import the context */ if (ctx_handle->remote && !ctx_handle->local) { maj = gpp_remote_to_local_ctx(&min, &ctx_handle->remote, &ctx_handle->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_unwrap_aead(minor_status, ctx_handle->local, input_message_buffer, input_assoc_buffer, output_payload_buffer, conf_state, qop_state); } OM_uint32 gssi_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_qop_t qop_req, gss_buffer_t message_buffer, gss_buffer_t message_token) { struct gpp_context_handle *ctx_handle; OM_uint32 maj, min; GSSI_TRACE(); ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we do encryption only locally and only for specific known * mechanisms for which we can export/import the context */ if (ctx_handle->remote && !ctx_handle->local) { maj = gpp_remote_to_local_ctx(&min, &ctx_handle->remote, &ctx_handle->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_get_mic(minor_status, ctx_handle->local, qop_req, message_buffer, message_token); } OM_uint32 gssi_verify_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_buffer_t message_buffer, gss_buffer_t message_token, gss_qop_t *qop_state) { struct gpp_context_handle *ctx_handle; OM_uint32 maj, min; GSSI_TRACE(); ctx_handle = (struct gpp_context_handle *)context_handle; if (!ctx_handle) { return GSS_S_CALL_INACCESSIBLE_READ; } /* for now we do encryption only locally and only for specific known * mechanisms for which we can export/import the context */ if (ctx_handle->remote && !ctx_handle->local) { maj = gpp_remote_to_local_ctx(&min, &ctx_handle->remote, &ctx_handle->local); if (maj != GSS_S_COMPLETE) { *minor_status = gpp_map_error(min); return maj; } } return gss_verify_mic(minor_status, ctx_handle->local, message_buffer, message_token, qop_state); } gssproxy-v0.8.2/src/mechglue/gss_plugin.c0000644000174300017420000003727713456641744020134 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #include "gss_plugin.h" #include #include #include #define KRB5_OID_LEN 9 #define KRB5_OID "\052\206\110\206\367\022\001\002\002" #define KRB5_OLD_OID_LEN 5 #define KRB5_OLD_OID "\053\005\001\005\002" /* Incorrect krb5 mech OID emitted by MS. */ #define KRB5_WRONG_OID_LEN 9 #define KRB5_WRONG_OID "\052\206\110\202\367\022\001\002\002" #define IAKERB_OID_LEN 6 #define IAKERB_OID "\053\006\001\005\002\005" const gss_OID_desc gpoid_krb5 = { .length = KRB5_OID_LEN, .elements = KRB5_OID }; const gss_OID_desc gpoid_krb5_old = { .length = KRB5_OLD_OID_LEN, .elements = KRB5_OLD_OID }; const gss_OID_desc gpoid_krb5_wrong = { .length = KRB5_WRONG_OID_LEN, .elements = KRB5_WRONG_OID }; const gss_OID_desc gpoid_iakerb = { .length = IAKERB_OID_LEN, .elements = IAKERB_OID }; enum gpp_behavior gpp_get_behavior(void) { static enum gpp_behavior behavior = GPP_UNINITIALIZED; char *envval; if (behavior == GPP_UNINITIALIZED) { envval = gp_getenv("GSSPROXY_BEHAVIOR"); if (envval) { if (strcmp(envval, "LOCAL_ONLY") == 0) { behavior = GPP_LOCAL_ONLY; } else if (strcmp(envval, "LOCAL_FIRST") == 0) { behavior = GPP_LOCAL_FIRST; } else if (strcmp(envval, "REMOTE_FIRST") == 0) { behavior = GPP_REMOTE_FIRST; } else if (strcmp(envval, "REMOTE_ONLY") == 0) { behavior = GPP_REMOTE_ONLY; } else { /* unknown setting, default to what has been configured * (by default local first) */ behavior = GPP_DEFAULT_BEHAVIOR; } } else { /* default to what has been configured (by default local only) */ behavior = GPP_DEFAULT_BEHAVIOR; } } return behavior; } /* 2.16.840.1.113730.3.8.15.1 */ const gss_OID_desc gssproxy_mech_interposer = { .length = 11, .elements = "\140\206\110\001\206\370\102\003\010\017\001" }; gss_OID_set gss_mech_interposer(gss_OID mech_type) { gss_OID_set interposed_mechs; OM_uint32 maj, min; char *envval; /* avoid looping in the gssproxy daemon by avoiding to interpose * any mechanism */ envval = gp_getenv("GSS_USE_PROXY"); if (!envval) { return NULL; } if (!gp_boolean_is_true(envval)) { return NULL; } interposed_mechs = NULL; maj = 0; if (gss_oid_equal(&gssproxy_mech_interposer, mech_type)) { maj = gss_create_empty_oid_set(&min, &interposed_mechs); if (maj != 0) { return NULL; } maj = gss_add_oid_set_member(&min, no_const(&gpoid_krb5), &interposed_mechs); if (maj != 0) { goto done; } maj = gss_add_oid_set_member(&min, no_const(&gpoid_krb5_old), &interposed_mechs); if (maj != 0) { goto done; } maj = gss_add_oid_set_member(&min, no_const(&gpoid_krb5_wrong), &interposed_mechs); if (maj != 0) { goto done; } maj = gss_add_oid_set_member(&min, no_const(&gpoid_iakerb), &interposed_mechs); if (maj != 0) { goto done; } } /* while there also initiaize special_mechs */ (void)gpp_special_available_mechs(interposed_mechs); done: if (maj != 0) { (void)gss_release_oid_set(&min, &interposed_mechs); interposed_mechs = NULL; } return interposed_mechs; } bool gpp_is_special_oid(const gss_OID mech_type) { if (mech_type != GSS_C_NO_OID && mech_type->length >= gssproxy_mech_interposer.length && memcmp(gssproxy_mech_interposer.elements, mech_type->elements, gssproxy_mech_interposer.length) == 0) { return true; } return false; } static bool gpp_special_equal(const gss_OID s, const gss_OID n) { int base_len = gssproxy_mech_interposer.length; if (s->length - base_len == n->length && memcmp(s->elements + base_len, n->elements, n->length) == 0) { return true; } return false; } struct gpp_special_oid_list { gss_OID_desc regular_oid; gss_OID_desc special_oid; struct gpp_special_oid_list *next; sig_atomic_t next_is_set; }; /* This is an ADD-ONLY list, and the pointer to next is updated * atomically so that we can avoid using mutexes for mere access * to the list. */ static struct gpp_special_oid_list *gpp_s_mechs; static sig_atomic_t gpp_s_mechs_is_set; static inline struct gpp_special_oid_list *gpp_get_special_oids(void) { int is_set; is_set = gpp_s_mechs_is_set; __sync_synchronize(); if (is_set != 0) { return gpp_s_mechs; } return NULL; } static inline struct gpp_special_oid_list *gpp_next_special_oids( struct gpp_special_oid_list *item) { int is_set; is_set = item->next_is_set; __sync_synchronize(); if (is_set != 0) { return item->next; } return NULL; } static inline struct gpp_special_oid_list *gpp_last_special_oids( struct gpp_special_oid_list *list) { struct gpp_special_oid_list *item; item = list; while (item && item->next_is_set) { item = item->next; } return item; } static inline void gpp_add_special_oids(struct gpp_special_oid_list *item) { struct gpp_special_oid_list *list, *last; list = gpp_get_special_oids(); if (list == NULL) { gpp_s_mechs = item; __sync_synchronize(); gpp_s_mechs_is_set = 1; } else { last = gpp_last_special_oids(list); last->next = item; __sync_synchronize(); last->next_is_set = 1; } } static const gss_OID gpp_new_special_mech(const gss_OID n) { gss_const_OID base = &gssproxy_mech_interposer; struct gpp_special_oid_list *item; item = calloc(1, sizeof(struct gpp_special_oid_list)); if (!item) { return GSS_C_NO_OID; } item->regular_oid.length = n->length; item->regular_oid.elements = malloc(n->length); item->special_oid.length = base->length + n->length; item->special_oid.elements = malloc(item->special_oid.length); if (!item->regular_oid.elements || !item->special_oid.elements) { free(item->regular_oid.elements); free(item->special_oid.elements); free(item); return GSS_C_NO_OID; } memcpy(item->regular_oid.elements, n->elements, n->length); memcpy(item->special_oid.elements, base->elements, base->length); memcpy(item->special_oid.elements + base->length, n->elements, n->length); gpp_add_special_oids(item); return (const gss_OID)&item->special_oid; } const gss_OID gpp_special_mech(const gss_OID mech_type) { struct gpp_special_oid_list *item = NULL; if (gpp_is_special_oid(mech_type)) { return mech_type; } item = gpp_get_special_oids(); if (mech_type == GSS_C_NO_OID) { /* return the first special one if none specified */ if (item) { return (const gss_OID)&item->special_oid; } return GSS_C_NO_OID; } while (item) { if (gpp_special_equal(&item->special_oid, mech_type)) { return (const gss_OID)&item->special_oid; } item = gpp_next_special_oids(item); } /* none matched, add new special oid to the set */ return gpp_new_special_mech(mech_type); } const gss_OID gpp_unspecial_mech(const gss_OID mech_type) { struct gpp_special_oid_list *item = NULL; if (!gpp_is_special_oid(mech_type)) { return mech_type; } item = gpp_get_special_oids(); while (item) { if (gss_oid_equal(&item->special_oid, mech_type)) { return (const gss_OID)&item->regular_oid; } item = gpp_next_special_oids(item); } /* none matched */ return mech_type; } gss_OID_set gpp_special_available_mechs(const gss_OID_set mechs) { gss_OID_set amechs = GSS_C_NO_OID_SET; struct gpp_special_oid_list *item; gss_OID n; uint32_t maj, min; int i; item = gpp_get_special_oids(); maj = gss_create_empty_oid_set(&min, &amechs); if (maj) { return GSS_C_NO_OID_SET; } for (i = 0; i < mechs->count; i++) { while (item) { if (gpp_is_special_oid(&mechs->elements[i])) { maj = gss_add_oid_set_member(&min, &mechs->elements[i], &amechs); if (maj != GSS_S_COMPLETE) { goto done; } break; } if (gpp_special_equal(&item->special_oid, &mechs->elements[i])) { maj = gss_add_oid_set_member(&min, &item->special_oid, &amechs); if (maj != GSS_S_COMPLETE) { goto done; } break; } item = gpp_next_special_oids(item); } if (item == NULL) { /* not found, add to static list */ n = gpp_new_special_mech(&mechs->elements[i]); if (n == GSS_C_NO_OID) { maj = GSS_S_FAILURE; } else { maj = gss_add_oid_set_member(&min, n, &amechs); } if (maj != GSS_S_COMPLETE) { goto done; } } } done: if (maj != GSS_S_COMPLETE || amechs->count == 0) { (void)gss_release_oid_set(&min, &amechs); } return amechs; } OM_uint32 gssi_internal_release_oid(OM_uint32 *minor_status, gss_OID *oid) { struct gpp_special_oid_list *item = NULL; *minor_status = 0; if (&gssproxy_mech_interposer == *oid) { *oid = GSS_C_NO_OID; return GSS_S_COMPLETE; } item = gpp_get_special_oids(); while (item) { if ((&item->regular_oid == *oid) || (&item->special_oid == *oid)) { *oid = GSS_C_NO_OID; return GSS_S_COMPLETE; } item = gpp_next_special_oids(item); } /* none matched, it's not ours */ return GSS_S_CONTINUE_NEEDED; } #define MAP_ERROR_BASE 0x04200000 uint32_t gpp_map_error(uint32_t err) { /* placeholder, * we will need an actual map but to speed up testing just make a sum with * a special base and hope no conflicts will happen in the mechglue */ if (err) { err += MAP_ERROR_BASE; } return err; } uint32_t gpp_unmap_error(uint32_t err) { /* placeholder, * we will need an actual map but to speed up testing just make a sum with * a special base and hope no conflicts will happen in the mechglue */ if (err) { err -= MAP_ERROR_BASE; } return err; } uint32_t gpp_wrap_sec_ctx_token(uint32_t *minor, gss_OID mech_type, gss_buffer_t token, gss_buffer_t wrap_token) { gss_OID spmech; uint32_t len; spmech = gpp_special_mech(mech_type); if (spmech == GSS_C_NO_OID) { return GSS_S_FAILURE; } wrap_token->length = sizeof(uint32_t) + spmech->length + token->length; wrap_token->value = malloc(wrap_token->length); if (!wrap_token->value) { wrap_token->length = 0; return GSS_S_FAILURE; } len = htobe32(spmech->length); memcpy(wrap_token->value, &len, sizeof(uint32_t)); memcpy(wrap_token->value + sizeof(uint32_t), spmech->elements, spmech->length); memcpy(wrap_token->value + sizeof(uint32_t) + spmech->length, token->value, token->length); return GSS_S_COMPLETE; } uint32_t gpp_remote_to_local_ctx(uint32_t *minor, gssx_ctx **remote_ctx, gss_ctx_id_t *local_ctx) { gss_buffer_desc wrap_token = {0}; gss_buffer_desc token; gss_OID_desc mech; uint32_t hlen, len; uint32_t maj, min; gp_conv_gssx_to_buffer(&(*remote_ctx)->exported_context_token, &token); /* To get a local context we need to call import_sec_context with a token * wrapping that uses the special mech oid. Otherwise the mechglue will * give us back an interposed context. */ if (token.length <= sizeof(uint32_t)) { return GSS_S_FAILURE; } memcpy(&len, token.value, sizeof(uint32_t)); mech.length = be32toh(len); hlen = sizeof(uint32_t) + mech.length; if (token.length <= hlen) { return GSS_S_FAILURE; } mech.elements = malloc(mech.length); if (!mech.elements) { return GSS_S_FAILURE; } memcpy(mech.elements, token.value + sizeof(uint32_t), mech.length); token.length -= hlen; token.value += hlen; maj = gpp_wrap_sec_ctx_token(&min, &mech, &token, &wrap_token); if (maj != GSS_S_COMPLETE) { free(mech.elements); return maj; } maj = gss_import_sec_context(minor, &wrap_token, local_ctx); free(mech.elements); (void)gss_release_buffer(&min, &wrap_token); xdr_free((xdrproc_t)xdr_gssx_ctx, (char *)(*remote_ctx)); *remote_ctx = NULL; return maj; } uint32_t gpp_name_to_local(uint32_t *minor, gssx_name *name, gss_OID mech_type, gss_name_t *mech_name) { uint32_t maj, min; gss_buffer_desc display_name_buffer = GSS_C_EMPTY_BUFFER; gss_OID display_name_type = GSS_C_NO_OID; gss_name_t tmpname = NULL; maj = gpm_display_name(minor, name, &display_name_buffer, &display_name_type); if (maj) { return maj; } maj = gss_import_name(minor, &display_name_buffer, display_name_type, &tmpname); (void)gss_release_buffer(&min, &display_name_buffer); (void)gss_release_oid(&min, &display_name_type); if (maj) { return maj; } if (mech_type != GSS_C_NO_OID) { /* name for specific mech requested */ maj = gss_canonicalize_name(minor, tmpname, gpp_special_mech(mech_type), NULL); } *mech_name = tmpname; return maj; } uint32_t gpp_local_to_name(uint32_t *minor, gss_name_t local_name, gssx_name **name) { uint32_t maj, min; gss_buffer_desc display_name_buffer = GSS_C_EMPTY_BUFFER; gss_OID display_name_type = GSS_C_NO_OID; maj = gss_display_name(minor, local_name, &display_name_buffer, &display_name_type); if (maj) { return maj; } maj = gpm_import_name(minor, &display_name_buffer, display_name_type, name); (void)gss_release_buffer(&min, &display_name_buffer); (void)gss_release_oid(&min, &display_name_type); return maj; } uint32_t gpp_copy_oid(uint32_t *minor, gss_OID in, gss_OID *out) { gss_OID c; c = calloc(1, sizeof(gss_OID_desc)); if (!c) { *minor = ENOMEM; return GSS_S_FAILURE; } c->length = in->length; c->elements = malloc(in->length); if (!c->elements) { free(c); *minor = ENOMEM; return GSS_S_FAILURE; } memcpy(c->elements, in->elements, in->length); *out = c; *minor = 0; return GSS_S_COMPLETE; } bool gpp_is_krb5_oid(const gss_OID mech) { if (gss_oid_equal(&gpoid_krb5, mech)) { return true; } else if (gss_oid_equal(&gpoid_krb5_old, mech)) { return true; } else if (gss_oid_equal(&gpoid_krb5_wrong, mech)) { return true; } else if (gss_oid_equal(&gpoid_iakerb, mech)) { return true; } return false; } gssproxy-v0.8.2/src/mechglue/gss_plugin.h0000644000174300017420000005306313456641744020130 0ustar gitgit00000000000000/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */ #ifndef _GSS_PLUGIN_H_ #define _GSS_PLUGIN_H_ #include "src/client/gssapi_gpm.h" struct gpp_cred_handle { gssx_cred *remote; gss_key_value_set_desc store; bool default_creds; gss_cred_id_t local; }; struct gpp_context_handle { gssx_ctx *remote; gss_ctx_id_t local; }; struct gpp_name_handle { gss_OID mech_type; gssx_name *remote; gss_name_t local; }; extern const gss_OID_desc gssproxy_mech_interposer; enum gpp_behavior { GPP_UNINITIALIZED = 0, GPP_LOCAL_ONLY, GPP_LOCAL_FIRST, GPP_REMOTE_FIRST, GPP_REMOTE_ONLY, }; #ifdef GSSI_DEBUGGING #define GSSI_DEBUG(...) \ do { \ fprintf(stderr, "GSSI %s:%d ", __FUNCTION__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ fflush(stderr); \ } while(0); #define GSSI_TRACE(...) \ do { \ fprintf(stderr, "GSSI %s:%d called\n", __FUNCTION__, __LINE__); \ fflush(stderr); \ } while(0); #else #define GSSI_DEBUG(...) #define GSSI_TRACE(...) #endif /* GSSI_DEBUGGING */ gss_OID_set gss_mech_interposer(gss_OID mech_type); enum gpp_behavior gpp_get_behavior(void); bool gpp_is_special_oid(const gss_OID mech_type); const gss_OID gpp_special_mech(const gss_OID mech_type); const gss_OID gpp_unspecial_mech(const gss_OID mech_type); gss_OID_set gpp_special_available_mechs(const gss_OID_set mechs); uint32_t gpp_map_error(uint32_t err); uint32_t gpp_unmap_error(uint32_t err); uint32_t gpp_wrap_sec_ctx_token(uint32_t *minor, gss_OID mech_type, gss_buffer_t token, gss_buffer_t wrap_token); uint32_t gpp_remote_to_local_ctx(uint32_t *minor, gssx_ctx **remote_ctx, gss_ctx_id_t *local_ctx); uint32_t gpp_copy_oid(uint32_t *minor, gss_OID in, gss_OID *out); bool gpp_is_krb5_oid(const gss_OID mech); uint32_t gpp_name_to_local(uint32_t *minor, gssx_name *name, gss_OID mech_type, gss_name_t *mech_name); uint32_t gpp_local_to_name(uint32_t *minor, gss_name_t local_name, gssx_name **name); uint32_t gpp_cred_handle_init(uint32_t *min, bool defcred, const char *ccache, struct gpp_cred_handle **out_handle); uint32_t gpp_cred_handle_free(uint32_t *min, struct gpp_cred_handle *handle); bool gpp_creds_are_equal(gssx_cred *a, gssx_cred *b); uint32_t gpp_store_remote_creds(uint32_t *min, bool store_as_default_cred, gss_const_key_value_set_t cred_store, gssx_cred *creds); OM_uint32 gssi_internal_release_oid(OM_uint32 *minor_status, gss_OID *oid); OM_uint32 gssi_acquire_cred(OM_uint32 *minor_status, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec); OM_uint32 gssi_acquire_cred_from(OM_uint32 *minor_status, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_const_key_value_set_t cred_store, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec); OM_uint32 gssi_add_cred(OM_uint32 *minor_status, const gss_cred_id_t input_cred_handle, const gss_name_t desired_name, const gss_OID desired_mech, gss_cred_usage_t cred_usage, OM_uint32 initiator_time_req, OM_uint32 acceptor_time_req, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *initiator_time_rec, OM_uint32 *acceptor_time_rec); OM_uint32 gssi_add_cred_from(OM_uint32 *minor_status, const gss_cred_id_t input_cred_handle, const gss_name_t desired_name, const gss_OID desired_mech, gss_cred_usage_t cred_usage, OM_uint32 initiator_time_req, OM_uint32 acceptor_time_req, gss_const_key_value_set_t cred_store, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *initiator_time_rec, OM_uint32 *acceptor_time_rec); OM_uint32 gssi_acquire_cred_with_password(OM_uint32 *minor_status, const gss_name_t desired_name, const gss_buffer_t password, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec); OM_uint32 gssi_acquire_cred_impersonate_name(OM_uint32 *minor_status, gss_cred_id_t *imp_cred_handle, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec); OM_uint32 gppint_retrieve_remote_creds(uint32_t *min, const char *ccache_name, gssx_name *name, gssx_cred *creds); OM_uint32 gppint_get_def_creds(OM_uint32 *minor_status, enum gpp_behavior behavior, struct gpp_name_handle *name, gss_cred_usage_t cred_usage, struct gpp_cred_handle **cred_handle); OM_uint32 gssi_inquire_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_name_t *name, OM_uint32 *lifetime, gss_cred_usage_t *cred_usage, gss_OID_set *mechanisms); OM_uint32 gssi_inquire_cred_by_mech(OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_OID mech_type, gss_name_t *name, OM_uint32 *initiator_lifetime, OM_uint32 *acceptor_lifetime, gss_cred_usage_t *cred_usage); OM_uint32 gssi_inquire_cred_by_oid(OM_uint32 *minor_status, const gss_cred_id_t cred_handle, const gss_OID desired_object, gss_buffer_set_t *data_set); OM_uint32 gssi_set_cred_option(OM_uint32 *minor_status, gss_cred_id_t *cred_handle, const gss_OID desired_object, const gss_buffer_t value); OM_uint32 gssi_store_cred(OM_uint32 *minor_status, const gss_cred_id_t input_cred_handle, gss_cred_usage_t input_usage, const gss_OID desired_mech, OM_uint32 overwrite_cred, OM_uint32 default_cred, gss_OID_set *elements_stored, gss_cred_usage_t *cred_usage_stored); OM_uint32 gssi_store_cred_into(OM_uint32 *minor_status, const gss_cred_id_t input_cred_handle, gss_cred_usage_t input_usage, const gss_OID desired_mech, OM_uint32 overwrite_cred, OM_uint32 default_cred, gss_const_key_value_set_t cred_store, gss_OID_set *elements_stored, gss_cred_usage_t *cred_usage_stored); OM_uint32 gssi_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle); OM_uint32 gssi_export_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t interprocess_token); OM_uint32 gssi_import_sec_context(OM_uint32 *minor_status, gss_buffer_t interprocess_token, gss_ctx_id_t *context_handle); OM_uint32 gssi_import_sec_context_by_mech(OM_uint32 *minor_status, gss_OID mech_type, gss_buffer_t interprocess_token, gss_ctx_id_t *context_handle); OM_uint32 gssi_process_context_token(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_buffer_t token_buffer); OM_uint32 gssi_context_time(OM_uint32 *minor_status, gss_ctx_id_t context_handle, OM_uint32 *time_rec); OM_uint32 gssi_inquire_context(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_name_t *src_name, gss_name_t *targ_name, OM_uint32 *lifetime_rec, gss_OID *mech_type, OM_uint32 *ctx_flags, int *locally_initiated, int *open); OM_uint32 gssi_inquire_sec_context_by_oid(OM_uint32 *minor_status, const gss_ctx_id_t context_handle, const gss_OID desired_object, gss_buffer_set_t *data_set); OM_uint32 gssi_set_sec_context_option(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, const gss_OID desired_object, const gss_buffer_t value); OM_uint32 gssi_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int prf_key, const gss_buffer_t prf_in, ssize_t desired_output_len, gss_buffer_t prf_out); OM_uint32 gssi_delete_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t output_token); OM_uint32 gssi_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_cred_id_t acceptor_cred_handle, gss_buffer_t input_token_buffer, gss_channel_bindings_t input_chan_bindings, gss_name_t *src_name, gss_OID *mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec, gss_cred_id_t *delegated_cred_handle); OM_uint32 gssi_init_sec_context(OM_uint32 *minor_status, gss_cred_id_t claimant_cred_handle, gss_ctx_id_t *context_handle, gss_name_t target_name, gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, gss_channel_bindings_t input_cb, gss_buffer_t input_token, gss_OID *actual_mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec); OM_uint32 gssi_display_status(OM_uint32 *minor_status, OM_uint32 status_value, int status_type, const gss_OID mech_type, OM_uint32 *message_context, gss_buffer_t status_string); OM_uint32 gssi_display_name(OM_uint32 *minor_status, gss_name_t input_name, gss_buffer_t output_name_buffer, gss_OID *output_name_type); OM_uint32 gssi_display_name_ext(OM_uint32 *minor_status, gss_name_t name, gss_OID display_as_name_type, gss_buffer_t display_name); OM_uint32 gssi_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name); OM_uint32 gssi_import_name_by_mech(OM_uint32 *minor_status, gss_OID mech_type, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name); OM_uint32 gssi_release_name(OM_uint32 *minor_status, gss_name_t *input_name); OM_uint32 gssi_export_name(OM_uint32 *minor_status, const gss_name_t input_name, gss_buffer_t exported_name); OM_uint32 gssi_export_name_composite(OM_uint32 *minor_status, const gss_name_t input_name, gss_buffer_t exported_composite_name); OM_uint32 gssi_duplicate_name(OM_uint32 *minor_status, const gss_name_t input_name, gss_name_t *dest_name); OM_uint32 gssi_compare_name(OM_uint32 *minor_status, gss_name_t name1, gss_name_t name2, int *name_equal); OM_uint32 gssi_inquire_name(OM_uint32 *minor_status, gss_name_t name, int *name_is_NM, gss_OID *NM_mech, gss_buffer_set_t *attrs); OM_uint32 gssi_get_name_attribute(OM_uint32 *minor_status, gss_name_t input_name, gss_buffer_t attr, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more); OM_uint32 gssi_set_name_attribute(OM_uint32 *minor_status, gss_name_t input_name, int complete, gss_buffer_t attr, gss_buffer_t value); OM_uint32 gssi_delete_name_attribute(OM_uint32 *minor_status, gss_name_t input_name, gss_buffer_t attr); OM_uint32 gssi_indicate_mechs(OM_uint32 *minor_status, gss_OID_set *mech_set); OM_uint32 gssi_inquire_names_for_mech(OM_uint32 *minor_status, gss_OID mech_type, gss_OID_set *mech_names); OM_uint32 gssi_inquire_attrs_for_mech(OM_uint32 *minor_status, gss_OID mech, gss_OID_set *mech_attrs, gss_OID_set *known_mech_attrs); OM_uint32 gssi_inquire_saslname_for_mech(OM_uint32 *minor_status, const gss_OID desired_mech, gss_buffer_t sasl_mech_name, gss_buffer_t mech_name, gss_buffer_t mech_description); OM_uint32 gssi_inquire_mech_for_saslname(OM_uint32 *minor_status, const gss_buffer_t sasl_mech_name, gss_OID *mech_type); OM_uint32 gssi_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, gss_buffer_t input_message_buffer, int *conf_state, gss_buffer_t output_message_buffer); OM_uint32 gssi_wrap_size_limit(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, OM_uint32 req_output_size, OM_uint32 *max_input_size); OM_uint32 gssi_wrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, int *conf_state, gss_iov_buffer_desc *iov, int iov_count); OM_uint32 gssi_wrap_iov_length(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, int *conf_state, gss_iov_buffer_desc *iov, int iov_count); OM_uint32 gssi_wrap_aead(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, gss_buffer_t input_assoc_buffer, gss_buffer_t input_payload_buffer, int *conf_state, gss_buffer_t output_message_buffer); OM_uint32 gssi_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int *conf_state, gss_qop_t *qop_state); OM_uint32 gssi_unwrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int *conf_state, gss_qop_t *qop_state, gss_iov_buffer_desc *iov, int iov_count); OM_uint32 gssi_unwrap_aead(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer, gss_buffer_t input_assoc_buffer, gss_buffer_t output_payload_buffer, int *conf_state, gss_qop_t *qop_state); OM_uint32 gssi_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_qop_t qop_req, gss_buffer_t message_buffer, gss_buffer_t message_token); OM_uint32 gssi_verify_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_buffer_t message_buffer, gss_buffer_t message_token, gss_qop_t *qop_state); OM_uint32 gssi_mech_invoke(OM_uint32 *minor_status, const gss_OID desired_mech, const gss_OID desired_object, gss_buffer_t value); #if 0 OM_uint32 gssi_set_neg_mechs(OM_uint32 *minor_status, gss_cred_id_t cred_handle, const gss_OID_set mech_set); #endif #if 0 OM_uint32 gssi_complete_auth_token(OM_uint32 *minor_status, const gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer); #endif OM_uint32 gssi_localname(OM_uint32 *minor_status, const gss_name_t name, gss_OID mech_type, gss_buffer_t localname); OM_uint32 gssi_authorize_localname(OM_uint32 *minor_status, const gss_name_t name, gss_buffer_t local_user, gss_OID local_nametype); OM_uint32 gssi_map_name_to_any(OM_uint32 *minor_status, gss_name_t name, int authenticated, gss_buffer_t type_id, gss_any_t *output); OM_uint32 gssi_release_any_name_mapping(OM_uint32 *minor_status, gss_name_t name, gss_buffer_t type_id, gss_any_t *input); #endif /* _GSS_PLUGIN_H_ */ gssproxy-v0.8.2/systemd/0000755000174300017420000000000013456641744014706 5ustar gitgit00000000000000gssproxy-v0.8.2/systemd/gssproxy.service.in0000644000174300017420000000075213456641744020577 0ustar gitgit00000000000000[Unit] Description=GSSAPI Proxy Daemon # GSSPROXY will not be started until syslog is After=syslog.target Before=nfs-secure.service nfs-secure-server.service [Service] Environment=KRB5RCACHEDIR=/var/lib/gssproxy/rcache ExecStart=@sbindir@/gssproxy -D # These two should be used with traditional UNIX forking daemons # consult systemd.service(5) for more details Type=forking PIDFile=@localstatedir@/run/gssproxy.pid ExecReload=/bin/kill -HUP $MAINPID [Install] WantedBy=multi-user.target gssproxy-v0.8.2/tests/0000755000174300017420000000000013456641744014360 5ustar gitgit00000000000000gssproxy-v0.8.2/tests/Makefile.am0000644000174300017420000000177713456641744016430 0ustar gitgit00000000000000t_acquire_SOURCES = \ t_utils.c \ t_acquire.c t_acquire_LDADD = \ $(GSSAPI_LIBS) t_accept_SOURCES = \ t_utils.c \ t_accept.c t_accept_LDADD = \ $(GSSAPI_LIBS) t_cred_store_SOURCES = \ t_utils.c \ t_cred_store.c t_cred_store_LDADD = \ $(GSSAPI_LIBS) t_impersonate_SOURCES = \ t_utils.c \ t_impersonate.c t_impersonate_LDADD = \ $(GSSAPI_LIBS) t_init_SOURCES = \ t_utils.c \ t_init.c t_init_LDADD = \ $(GSSAPI_LIBS) t_setcredopt_SOURCES = \ t_utils.c \ t_setcredopt.c t_setcredopt_LDADD = \ $(GSSAPI_LIBS) check_PROGRAMS = \ t_acquire \ t_cred_store \ t_impersonate \ t_accept \ t_init \ t_setcredopt \ $(NULL) noinst_PROGRAMS = $(check_PROGRAMS) noinst_HEADERS = \ t_utils.h EXTRA_DIST = \ runtests.py \ t_acquire.py \ t_basic.py \ t_cred_store.py \ testlib.py \ t_impersonate.py \ t_interpose.py \ t_multi_key.py \ t_reloading.py \ $(NULL) all: $(check_PROGRAMS) gssproxy-v0.8.2/tests/cli_srv_comm.c0000644000174300017420000004574313456641744017215 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "src/gp_proxy.h" #include "src/gp_rpc_process.h" #include "src/gp_conv.h" #include "src/gp_debug.h" #include "src/client/gssapi_gpm.h" #include "popt.h" #include "t_utils.h" int gp_send_accept_sec_context(int fd, gssx_arg_accept_sec_context *arg, gssx_res_accept_sec_context *res) { XDR xdr_call_ctx; XDR xdr_reply_ctx; gp_rpc_msg msg; char buffer[MAX_RPC_SIZE]; uint32_t length; bool xdrok; int ret; memset(&msg, 0, sizeof(gp_rpc_msg)); xdrmem_create(&xdr_call_ctx, buffer, MAX_RPC_SIZE, XDR_ENCODE); xdrmem_create(&xdr_reply_ctx, buffer, MAX_RPC_SIZE, XDR_DECODE); msg.xid = 1; msg.header.type = GP_RPC_CALL; msg.header.gp_rpc_msg_union_u.chdr.rpcvers = 2; msg.header.gp_rpc_msg_union_u.chdr.prog = GSSPROXY; msg.header.gp_rpc_msg_union_u.chdr.vers = GSSPROXYVERS; msg.header.gp_rpc_msg_union_u.chdr.proc = GSSX_ACCEPT_SEC_CONTEXT; msg.header.gp_rpc_msg_union_u.chdr.cred.flavor = GP_RPC_AUTH_NONE; msg.header.gp_rpc_msg_union_u.chdr.cred.body.body_len = 0; msg.header.gp_rpc_msg_union_u.chdr.cred.body.body_val = NULL; msg.header.gp_rpc_msg_union_u.chdr.verf.flavor = GP_RPC_AUTH_NONE; msg.header.gp_rpc_msg_union_u.chdr.verf.body.body_len = 0; msg.header.gp_rpc_msg_union_u.chdr.verf.body.body_val = NULL; /* encode header */ xdrok = xdr_gp_rpc_msg(&xdr_call_ctx, &msg); if (!xdrok) { return EFAULT; } /* encode data */ xdrok = xdr_gssx_arg_accept_sec_context(&xdr_call_ctx, arg); if (!xdrok) { return EFAULT; } /* send to proxy */ ret = t_send_buffer(fd, buffer, xdr_getpos(&xdr_call_ctx)); if (ret) { return EIO; } /* receive answer */ ret = t_recv_buffer(fd, buffer, &length); if (ret) { return EIO; } /* decode header */ xdrok = xdr_gp_rpc_msg(&xdr_reply_ctx, &msg); if (!xdrok) { return EFAULT; } if (msg.xid != 1 || msg.header.type != GP_RPC_REPLY || msg.header.gp_rpc_msg_union_u.rhdr.status != GP_RPC_MSG_ACCEPTED || msg.header.gp_rpc_msg_union_u.rhdr.gp_rpc_reply_header_u.accepted.reply_data.status != GP_RPC_SUCCESS) { return EINVAL; } /* decode answer */ xdrok = xdr_gssx_res_accept_sec_context(&xdr_reply_ctx, res); if (!xdrok) { return EFAULT; } xdr_free((xdrproc_t)xdr_gp_rpc_msg, (char *)&msg); xdr_destroy(&xdr_call_ctx); xdr_destroy(&xdr_reply_ctx); return 0; } struct athread { const char **argv; pthread_t tid; int *cli_pipe; int *srv_pipe; char *target; }; #define CLI_MSG "I am the buffer" void *client_thread(void *pvt) { const char **argv; struct athread *data; uint32_t ret_maj; uint32_t ret_min; char buffer[MAX_RPC_SIZE]; uint32_t buflen; gss_buffer_desc target_buf; gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER; struct gssx_name *name = NULL; struct gssx_ctx *ctx = NULL; struct gssx_cred *cred_handle = NULL; int ret = 0; gss_buffer_desc msg_buf = GSS_C_EMPTY_BUFFER; int conf_state; uint32_t max_size; data = (struct athread *)pvt; argv = data->argv; target_buf.value = (void *)data->target; target_buf.length = strlen(data->target) + 1; ret_maj = gpm_import_name(&ret_min, &target_buf, GSS_C_NT_HOSTBASED_SERVICE, &name); if (ret_maj) { goto done; } do { ret_maj = gpm_init_sec_context(&ret_min, cred_handle, &ctx, name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_token, NULL, &out_token, NULL, NULL, NULL); if (ret_maj != GSS_S_COMPLETE && ret_maj != GSS_S_CONTINUE_NEEDED) { DEBUG("gss_init_sec_context() failed with: %d\n", ret_maj); goto done; } if (out_token.length != 0) { /* send to server */ ret = t_send_buffer(data->srv_pipe[1], out_token.value, out_token.length); if (ret) { goto done; } gss_release_buffer(&ret_min, &out_token); } if (!ctx) { goto done; } if (ret_maj == GSS_S_CONTINUE_NEEDED) { /* and wait for reply */ ret = t_recv_buffer(data->cli_pipe[0], buffer, &buflen); if (ret) { goto done; } in_token.value = buffer; in_token.length = buflen; } } while (ret_maj == GSS_S_CONTINUE_NEEDED); memcpy(buffer, CLI_MSG, sizeof(CLI_MSG)); msg_buf.value = (void *)buffer; msg_buf.length = sizeof(CLI_MSG); ret_maj = gpm_get_mic(&ret_min, ctx, GSS_C_QOP_DEFAULT, &msg_buf, &out_token); if (ret_maj) { DEBUG("gpm_get_mic failed: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } /* send msg to server */ ret = t_send_buffer(data->srv_pipe[1], msg_buf.value, msg_buf.length); if (ret) { goto done; } /* send signature to server */ ret = t_send_buffer(data->srv_pipe[1], out_token.value, out_token.length); if (ret) { goto done; } gss_release_buffer(&ret_min, &out_token); in_token.value = discard_const(CLI_MSG); in_token.length = strlen(in_token.value) + 1; ret_maj = gpm_wrap(&ret_min, ctx, 1, /* conf_req_flag */ GSS_C_QOP_DEFAULT, /* qop_req */ &in_token, &conf_state, &out_token); if (ret_maj) { DEBUG("gpm_wrap failed: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } /* send to server */ ret = t_send_buffer(data->srv_pipe[1], out_token.value, out_token.length); if (ret) { goto done; } ret_maj = gpm_wrap_size_limit(&ret_min, ctx, 1, /* conf_req */ GSS_C_QOP_DEFAULT, /* qop_req */ 4096, /* size_req */ &max_size); if (ret_maj) { DEBUG("gpm_wrap_size_limit failed: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } DEBUG("client: Success!\n"); done: gpm_release_name(&ret_min, &name); gss_release_buffer(&ret_min, &out_token); close(data->cli_pipe[0]); close(data->srv_pipe[1]); pthread_exit(NULL); } void *server_thread(void *pvt) { const char **argv; struct athread *data; char buffer[MAX_RPC_SIZE]; uint32_t buflen; gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER; uint32_t ret_maj; uint32_t ret_min; struct gssx_ctx *context_handle = NULL; struct gssx_cred *cred_handle = NULL; struct gssx_name *src_name = NULL; gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER; struct gssx_cred *deleg_cred = NULL; gss_OID_set mech_set = GSS_C_NO_OID_SET; gss_OID_set mech_names = GSS_C_NO_OID_SET; gss_OID_set mech_types = GSS_C_NO_OID_SET; gss_OID_set mech_attrs = GSS_C_NO_OID_SET; gss_OID_set known_mech_attrs = GSS_C_NO_OID_SET; gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER; gss_buffer_desc mech_name = GSS_C_EMPTY_BUFFER; gss_buffer_desc mech_description = GSS_C_EMPTY_BUFFER; gss_buffer_desc name = GSS_C_EMPTY_BUFFER; gss_buffer_desc short_desc = GSS_C_EMPTY_BUFFER; gss_buffer_desc long_desc = GSS_C_EMPTY_BUFFER; gss_OID_set mechs = GSS_C_NO_OID_SET; gss_buffer_desc target_buf; struct gssx_name *target_name = NULL; struct gssx_name *canon_name = NULL; gss_buffer_desc out_name_buf = GSS_C_EMPTY_BUFFER; gss_OID out_name_type = GSS_C_NO_OID; gss_buffer_desc msg_token = GSS_C_EMPTY_BUFFER; int ret; gss_buffer_desc input_message_buffer = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_message_buffer = GSS_C_EMPTY_BUFFER; gss_qop_t qop_state; int conf_state; data = (struct athread *)pvt; argv = data->argv; target_buf.value = (void *)data->target; target_buf.length = strlen(data->target) + 1; /* import name family functions tests */ ret_maj = gpm_import_name(&ret_min, &target_buf, GSS_C_NT_HOSTBASED_SERVICE, &target_name); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gpm_canonicalize_name(&ret_min, target_name, discard_const(gss_mech_krb5), &canon_name); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gpm_display_name(&ret_min, canon_name, &out_name_buf, &out_name_type); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } DEBUG("Acquiring for: %s\n", (char *)out_name_buf.value); /* indicate mechs family functions tests */ ret_maj = gpm_indicate_mechs(&ret_min, &mech_set); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gpm_inquire_names_for_mech(&ret_min, &mech_set->elements[0], &mech_names); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(&mech_set->elements[0], ret_maj, ret_min); goto done; } ret_maj = gpm_inquire_attrs_for_mech(&ret_min, &mech_set->elements[0], &mech_attrs, &known_mech_attrs); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(&mech_set->elements[0], ret_maj, ret_min); goto done; } ret_maj = gpm_inquire_saslname_for_mech(&ret_min, &mech_set->elements[0], &sasl_mech_name, &mech_name, &mech_description); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(&mech_set->elements[0], ret_maj, ret_min); goto done; } ret_maj = gpm_display_mech_attr(&ret_min, &mech_attrs->elements[0], &name, &short_desc, &long_desc); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gpm_indicate_mechs_by_attrs(&ret_min, GSS_C_NO_OID_SET, GSS_C_NO_OID_SET, GSS_C_NO_OID_SET, &mechs); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gpm_inquire_mechs_for_name(&ret_min, target_name, &mech_types); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gpm_acquire_cred(&ret_min, NULL, NULL, GSS_C_INDEFINITE, mech_set, GSS_C_ACCEPT, false, &cred_handle, NULL, NULL); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_recv_buffer(data->srv_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } in_token.value = buffer; in_token.length = buflen; ret_maj = gpm_accept_sec_context(&ret_min, &context_handle, cred_handle, &in_token, GSS_C_NO_CHANNEL_BINDINGS, &src_name, NULL, &out_token, NULL, NULL, &deleg_cred); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } if (out_token.length) { ret = t_send_buffer(data->cli_pipe[1], out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to client!\n"); goto done; } } /* receive message from client */ ret = t_recv_buffer(data->srv_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } in_token.value = buffer; in_token.length = buflen; /* receive signature from client */ ret = t_recv_buffer(data->srv_pipe[0], &buffer[in_token.length], &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } msg_token.value = &buffer[in_token.length]; msg_token.length = buflen; ret_maj = gpm_verify_mic(&ret_min, context_handle, &in_token, &msg_token, NULL); if (ret_maj) { DEBUG("gpm_verify_mic failed: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } DEBUG("Received valid msg from client: [%s]\n", buffer); ret = t_recv_buffer(data->srv_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } input_message_buffer.value = buffer; input_message_buffer.length = buflen; ret_maj = gpm_unwrap(&ret_min, context_handle, &input_message_buffer, &output_message_buffer, &conf_state, &qop_state); if (ret_maj) { DEBUG("gpm_unwrap failed: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } DEBUG("Received valid msg from client: [%s]\n", (char *)output_message_buffer.value); done: gpm_release_name(&ret_min, &src_name); gpm_release_buffer(&ret_min, &out_token); gpm_release_buffer(&ret_min, &output_message_buffer); gpm_release_cred(&ret_min, &deleg_cred); gpm_delete_sec_context(&ret_min, &context_handle, GSS_C_NO_BUFFER); gss_release_oid_set(&ret_min, &mech_set); gss_release_oid_set(&ret_min, &mech_names); gss_release_oid_set(&ret_min, &mech_types); gss_release_oid_set(&ret_min, &mech_attrs); gss_release_oid_set(&ret_min, &known_mech_attrs); gss_release_buffer(&ret_min, &sasl_mech_name); gss_release_buffer(&ret_min, &mech_name); gss_release_buffer(&ret_min, &mech_description); gss_release_buffer(&ret_min, &name); gss_release_buffer(&ret_min, &short_desc); gss_release_buffer(&ret_min, &long_desc); gss_release_oid_set(&ret_min, &mechs); gpm_release_name(&ret_min, &target_name); gpm_release_name(&ret_min, &canon_name); gss_release_buffer(&ret_min, &out_name_buf); gss_release_oid(&ret_min, &out_name_type); close(data->srv_pipe[0]); close(data->cli_pipe[1]); pthread_exit(NULL); } int main(int argc, const char *argv[]) { int opt; poptContext pc; int opt_version = 0; char *opt_target = NULL; int srv_pipe[2]; int cli_pipe[2]; pthread_attr_t attr; struct athread server; struct athread client; void *retval; int ret; struct poptOption long_options[] = { POPT_AUTOHELP {"target", 't', POPT_ARG_STRING, &opt_target, 0, \ _("Specify the target name used for the tests"), NULL}, \ {"version", '\0', POPT_ARG_NONE, &opt_version, 0, \ _("Print version number and exit"), NULL }, \ POPT_TABLEEND }; pc = poptGetContext(argv[0], argc, argv, long_options, 0); while((opt = poptGetNextOpt(pc)) != -1) { switch(opt) { default: fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0), poptStrerror(opt)); poptPrintUsage(pc, stderr, 0); return 1; } } if (opt_version) { puts(VERSION""DISTRO_VERSION""PRERELEASE_VERSION); return 0; } if (opt_target == NULL) { fprintf(stderr, "Missing target!\n"); poptPrintUsage(pc, stderr, 0); return 1; } ret = pipe(srv_pipe); if (ret) { return -1; } ret = pipe(cli_pipe); if (ret) { return -1; } /* make thread joinable (portability) */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); server.argv = argv; server.srv_pipe = srv_pipe; server.cli_pipe = cli_pipe; server.target = opt_target; ret = pthread_create(&server.tid, &attr, server_thread, &server); if (ret) { return -1; } client.argv = argv; client.srv_pipe = srv_pipe; client.cli_pipe = cli_pipe; client.target = opt_target; ret = pthread_create(&client.tid, &attr, client_thread, &client); if (ret) { return -1; } pthread_join(server.tid, &retval); pthread_join(client.tid, &retval); return 0; } gssproxy-v0.8.2/tests/interposetest.c0000644000174300017420000006371113456641744017444 0ustar gitgit00000000000000/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "src/gp_debug.h" #include "src/gp_log.h" #include "popt.h" #include #include "t_utils.h" #define _(STRING) gettext(STRING) static const char *actor = ""; const char **argv; static int gptest_inq_context(gss_ctx_id_t ctx) { gss_name_t targ_name = GSS_C_NO_NAME; gss_name_t src_name = GSS_C_NO_NAME; OM_uint32 lifetime_rec = -1; OM_uint32 ctx_flags = -1; gss_OID mech_type = GSS_C_NO_OID; int locally_initiated = -1; int open = -1; gss_buffer_desc sname = {0}; gss_buffer_desc tname = {0}; gss_buffer_desc mechstr = {0}; OM_uint32 time_rec = -1; OM_uint32 maj, min; maj = gss_inquire_context(&min, ctx, &src_name, &targ_name, &lifetime_rec, &mech_type, &ctx_flags, &locally_initiated, &open); if (maj == GSS_S_COMPLETE) { gss_OID type = GSS_C_NO_OID; maj = gss_display_name(&min, src_name, &sname, &type); if (maj != GSS_S_COMPLETE) { goto done; } maj = gss_display_name(&min, targ_name, &tname, &type); if (maj != GSS_S_COMPLETE) { goto done; } maj = gss_oid_to_str(&min, mech_type, &mechstr); if (maj != GSS_S_COMPLETE) { goto done; } DEBUG("Context properties: " "[ s:%s, t:%s, m:%s, l:%d, f:%d, i:%d, o:%d ]\n", (char *)sname.value, (char *)tname.value, (char *)mechstr.value, (int)lifetime_rec, (int)ctx_flags, locally_initiated, open); } maj = gss_context_time(&min, ctx, &time_rec); if (maj) { DEBUG("gss_context_time failed\n"); gp_log_failure(GSS_C_NO_OID, maj, min); goto done; } DEBUG("Context validity: %d sec.\n", time_rec); done: (void)gss_release_buffer(&min, &sname); (void)gss_release_buffer(&min, &tname); (void)gss_release_buffer(&min, &mechstr); return maj; } #define PROXY_LOCAL_ONLY 0 #define PROXY_LOCAL_FIRST 1 #define PROXY_REMOTE_FIRST 2 #define PROXY_REMOTE_ONLY 3 #define GSSPROXY_BEHAVIOR_ENV "GSSPROXY_BEHAVIOR" static const char *lookup_gssproxy_behavior(int proxy_mode) { switch(proxy_mode) { case PROXY_LOCAL_ONLY: return "LOCAL_ONLY"; case PROXY_LOCAL_FIRST: return "LOCAL_FIRST"; case PROXY_REMOTE_FIRST: return "REMOTE_FIRST"; case PROXY_REMOTE_ONLY: return "REMOTE_ONLY"; default: break; } return NULL; } static int setup_gssproxy_behavior(int proxy_mode) { const char *env; if (getenv(GSSPROXY_BEHAVIOR_ENV)) { return 0; } env = lookup_gssproxy_behavior(proxy_mode); if (env == NULL) { return -1; } return setenv(GSSPROXY_BEHAVIOR_ENV, env, 0); } struct aproc { int proxy_type; int *cli_pipe; int *srv_pipe; char *target; }; void run_client(struct aproc *data) { uint32_t ret_maj; uint32_t ret_min; char buffer[MAX_RPC_SIZE]; uint32_t buflen; gss_buffer_desc target_buf; gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER; gss_name_t name = GSS_C_NO_NAME; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL; gss_buffer_desc msg_buf = GSS_C_EMPTY_BUFFER; const char *message = "SECRET"; int ret = -1; gss_iov_buffer_desc iov[2] = { { 0, { 0, NULL } }, { 0, { 0, NULL } } }; int sealed; uint32_t max_size = 0; ret = setup_gssproxy_behavior(data->proxy_type); if (ret) { goto done; } DEBUG("%s behavior: %s\n", actor, getenv(GSSPROXY_BEHAVIOR_ENV)); target_buf.value = (void *)data->target; target_buf.length = strlen(data->target) + 1; ret_maj = gss_import_name(&ret_min, &target_buf, GSS_C_NT_HOSTBASED_SERVICE, &name); if (ret_maj) { DEBUG("gss_import_name failed\n"); goto done; } do { ret_maj = gss_init_sec_context(&ret_min, cred_handle, &ctx, name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_token, NULL, &out_token, NULL, NULL); if (ret_maj != GSS_S_COMPLETE && ret_maj != GSS_S_CONTINUE_NEEDED) { DEBUG("gss_init_sec_context() failed with: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } if (out_token.length != 0) { /* send to server */ ret = t_send_buffer(data->srv_pipe[1], out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to server!\n"); goto done; } gss_release_buffer(&ret_min, &out_token); } if (!ctx) { DEBUG("No context!\n"); goto done; } if (ret_maj == GSS_S_CONTINUE_NEEDED) { /* and wait for reply */ ret = t_recv_buffer(data->cli_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to receive data from server!\n"); goto done; } in_token.value = buffer; in_token.length = buflen; } } while (ret_maj == GSS_S_CONTINUE_NEEDED); ret = gptest_inq_context(ctx); if (ret) { DEBUG("Failed to inquire context!\n"); goto done; } /* test gss_wrap_size_limit */ ret_maj = gss_wrap_size_limit(&ret_min, ctx, 1, /* conf_req */ GSS_C_QOP_DEFAULT, /* qop_req */ 4096, /* size_req */ &max_size); if (ret_maj) { DEBUG("gss_wrap_size_limit failed.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } /* test encryption */ msg_buf.length = strlen(message) + 1; msg_buf.value = discard_const(message); ret_maj = gss_wrap(&ret_min, ctx, 1, 0, &msg_buf, NULL, &out_token); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to wrap message.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_send_buffer(data->srv_pipe[1], out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to server!\n"); goto done; } gss_release_buffer(&ret_min, &out_token); ret = t_recv_buffer(data->cli_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to receive data from server!\n"); goto done; } msg_buf.value = (void *)buffer; msg_buf.length = buflen; buffer[buflen] = '\0'; in_token.value = (void *)&buffer[buflen + 1]; ret = t_recv_buffer(data->cli_pipe[0], in_token.value, &buflen); if (ret) { DEBUG("Failed to receive data from server!\n"); goto done; } in_token.length = buflen; ret_maj = gss_verify_mic(&ret_min, ctx, &msg_buf, &in_token, NULL); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to verify message (%s).\n", buffer); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } fprintf(stdout, "Client, RECV: [%s]\n", buffer); /* test gss_wrap_iov_length */ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; iov[0].buffer.value = NULL; iov[0].buffer.length = 0; iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; iov[1].buffer.value = NULL; iov[1].buffer.length = msg_buf.length; ret_maj = gss_wrap_iov_length(&ret_min, ctx, 1, /* seal */ GSS_C_QOP_DEFAULT, &sealed, iov, 2); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_wrap_iov_length failed\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } buflen = iov[0].buffer.length; /* test gss_wrap_iov */ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; iov[0].buffer.length = buflen; iov[0].buffer.value = malloc(iov[0].buffer.length); if (!iov[0].buffer.value) { DEBUG("Out of memory\n"); goto done; } iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; iov[1].buffer.value = msg_buf.value; iov[1].buffer.length = msg_buf.length; ret_maj = gss_wrap_iov(&ret_min, ctx, 1, /* seal */ GSS_C_QOP_DEFAULT, &sealed, iov, 2); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_wrap_iov failed.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_send_buffer(data->srv_pipe[1], iov[0].buffer.value, iov[0].buffer.length); if (ret) { DEBUG("Failed to send data to server!\n"); goto done; } ret = t_send_buffer(data->srv_pipe[1], iov[1].buffer.value, iov[1].buffer.length); if (ret) { DEBUG("Failed to send data to server!\n"); goto done; } ret_maj = gss_delete_sec_context(&ret_min, &ctx, &out_token); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to delete context!\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_send_buffer(data->srv_pipe[1], out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to server!\n"); goto done; } gss_release_buffer(&ret_min, &out_token); DEBUG("Success!\n"); done: free(iov[0].buffer.value); gss_release_name(&ret_min, &name); gss_release_buffer(&ret_min, &out_token); close(data->cli_pipe[0]); close(data->srv_pipe[1]); exit(ret); } void run_server(struct aproc *data) { char buffer[MAX_RPC_SIZE]; uint32_t buflen; gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER; uint32_t ret_maj; uint32_t ret_min; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL; gss_name_t src_name; gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER; gss_cred_id_t deleg_cred = GSS_C_NO_CREDENTIAL; gss_OID_set mech_set = GSS_C_NO_OID_SET; gss_OID_set mech_names = GSS_C_NO_OID_SET; gss_OID_set mech_types = GSS_C_NO_OID_SET; gss_OID_set mech_attrs = GSS_C_NO_OID_SET; gss_OID_set known_mech_attrs = GSS_C_NO_OID_SET; gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER; gss_buffer_desc mech_name = GSS_C_EMPTY_BUFFER; gss_buffer_desc mech_description = GSS_C_EMPTY_BUFFER; gss_buffer_desc name = GSS_C_EMPTY_BUFFER; gss_buffer_desc short_desc = GSS_C_EMPTY_BUFFER; gss_buffer_desc long_desc = GSS_C_EMPTY_BUFFER; gss_OID_set mechs = GSS_C_NO_OID_SET; gss_buffer_desc const_buf; gss_name_t target_name = GSS_C_NO_NAME; gss_name_t canon_name = GSS_C_NO_NAME; gss_buffer_desc out_name_buf = GSS_C_EMPTY_BUFFER; gss_OID out_name_type = GSS_C_NO_OID; gss_buffer_desc exported_name = GSS_C_EMPTY_BUFFER; const char *message = "This message is authentic!"; int ret = -1; gss_iov_buffer_desc iov[2]; int sealed; ret = setup_gssproxy_behavior(data->proxy_type); if (ret) { goto done; } DEBUG("%s behavior: %s\n", actor, getenv(GSSPROXY_BEHAVIOR_ENV)); const_buf.value = (void *)data->target; const_buf.length = strlen(data->target) + 1; /* import name family functions tests */ ret_maj = gss_import_name(&ret_min, &const_buf, GSS_C_NT_HOSTBASED_SERVICE, &target_name); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_canonicalize_name(&ret_min, target_name, discard_const(gss_mech_krb5), &canon_name); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_export_name(&ret_min, canon_name, &exported_name); if (ret_maj) { DEBUG("gss_export_name() failed with: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } #if 0 /* disabled until gss_export_name_composite server-side is fixed */ gss_release_buffer(&ret_min, &exported_name); ret_maj = gss_export_name_composite(&ret_min, canon_name, &exported_name); if (ret_maj) { DEBUG("gss_export_name_composite() failed with: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } #endif ret_maj = gss_display_name(&ret_min, canon_name, &out_name_buf, &out_name_type); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } fprintf(stdout, "Acquiring for: %s\n", (char *)out_name_buf.value); /* indicate mechs family functions tests */ ret_maj = gss_indicate_mechs(&ret_min, &mech_set); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_inquire_names_for_mech(&ret_min, &mech_set->elements[0], &mech_names); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(&mech_set->elements[0], ret_maj, ret_min); goto done; } ret_maj = gss_inquire_attrs_for_mech(&ret_min, &mech_set->elements[0], &mech_attrs, &known_mech_attrs); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(&mech_set->elements[0], ret_maj, ret_min); goto done; } ret_maj = gss_inquire_saslname_for_mech(&ret_min, &mech_set->elements[0], &sasl_mech_name, &mech_name, &mech_description); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(&mech_set->elements[0], ret_maj, ret_min); goto done; } ret_maj = gss_display_mech_attr(&ret_min, &mech_attrs->elements[0], &name, &short_desc, &long_desc); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_indicate_mechs_by_attrs(&ret_min, GSS_C_NO_OID_SET, GSS_C_NO_OID_SET, GSS_C_NO_OID_SET, &mechs); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_inquire_mechs_for_name(&ret_min, target_name, &mech_types); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_acquire_cred(&ret_min, GSS_C_NO_NAME, GSS_C_INDEFINITE, mech_set, GSS_C_ACCEPT, &cred_handle, NULL, NULL); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_recv_buffer(data->srv_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } in_token.value = buffer; in_token.length = buflen; ret_maj = gss_accept_sec_context(&ret_min, &context_handle, cred_handle, &in_token, GSS_C_NO_CHANNEL_BINDINGS, &src_name, NULL, &out_token, NULL, NULL, &deleg_cred); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } if (out_token.length) { ret = t_send_buffer(data->cli_pipe[1], out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to client!\n"); goto done; } } gss_release_buffer(&ret_min, &out_token); ret = gptest_inq_context(context_handle); if (ret) { DEBUG("Failed to inquire context!\n"); goto done; } ret = t_recv_buffer(data->srv_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } in_token.value = buffer; in_token.length = buflen; ret_maj = gss_unwrap(&ret_min, context_handle, &in_token, &out_token, NULL, NULL); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to unwrap message.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } fprintf(stdout, "Server, RECV: %s\n", (char *)out_token.value); gss_release_buffer(&ret_min, &out_token); in_token.value = discard_const(message); in_token.length = strlen(message); ret_maj = gss_get_mic(&ret_min, context_handle, 0, &in_token, &out_token); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to protect message.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_send_buffer(data->cli_pipe[1], in_token.value, in_token.length); if (ret) { DEBUG("Failed to send data to client!\n"); goto done; } ret = t_send_buffer(data->cli_pipe[1], out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to client!\n"); goto done; } gss_release_buffer(&ret_min, &out_token); /* test gss_unwrap_iov */ ret = t_recv_buffer(data->srv_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; iov[0].buffer.value = buffer; iov[0].buffer.length = buflen; ret = t_recv_buffer(data->srv_pipe[0], buffer+buflen, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; iov[1].buffer.value = buffer+iov[0].buffer.length; iov[1].buffer.length = buflen; ret_maj = gss_unwrap_iov(&ret_min, context_handle, &sealed, NULL, /* qop_state */ iov, 2); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_unwrap_iov failed\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_recv_buffer(data->srv_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } const_buf.value = buffer; const_buf.length = buflen; ret_maj = gss_process_context_token(&ret_min, context_handle, &const_buf); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to process context token.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } /* The follwing will cause the context to leak, but it is unavoidable until * gss_process_context_token() is fixed to at least NULL the internal * context in the union context. */ context_handle = GSS_C_NO_CONTEXT; DEBUG("Success!\n"); done: gss_release_name(&ret_min, &src_name); gss_release_buffer(&ret_min, &out_token); gss_release_cred(&ret_min, &deleg_cred); gss_delete_sec_context(&ret_min, &context_handle, GSS_C_NO_BUFFER); gss_release_oid_set(&ret_min, &mech_set); gss_release_oid_set(&ret_min, &mech_names); gss_release_oid_set(&ret_min, &mech_types); gss_release_oid_set(&ret_min, &mech_attrs); gss_release_oid_set(&ret_min, &known_mech_attrs); gss_release_buffer(&ret_min, &sasl_mech_name); gss_release_buffer(&ret_min, &mech_name); gss_release_buffer(&ret_min, &mech_description); gss_release_buffer(&ret_min, &name); gss_release_buffer(&ret_min, &short_desc); gss_release_buffer(&ret_min, &long_desc); gss_release_oid_set(&ret_min, &mechs); gss_release_name(&ret_min, &target_name); gss_release_name(&ret_min, &canon_name); gss_release_buffer(&ret_min, &out_name_buf); gss_release_oid(&ret_min, &out_name_type); gss_release_buffer(&ret_min, &exported_name); close(data->srv_pipe[0]); close(data->cli_pipe[1]); exit(ret); } static int run_cli_srv_test(int server_proxy_type, int client_proxy_type, char *target) { int srv_pipe[2]; int cli_pipe[2]; struct aproc client, server; pid_t cli, srv, w; int closewait; int options; int status; int ret; ret = pipe(srv_pipe); if (ret) { return -1; } ret = pipe(cli_pipe); if (ret) { return -1; } srv = -1; cli = -1; server.proxy_type = server_proxy_type; server.srv_pipe = srv_pipe; server.cli_pipe = cli_pipe; server.target = target; srv = fork(); switch (srv) { case -1: ret = -1; goto done; case 0: actor = "SERVER"; run_server(&server); exit(0); default: close(srv_pipe[0]); close(cli_pipe[1]); break; } client.proxy_type = client_proxy_type; client.srv_pipe = srv_pipe; client.cli_pipe = cli_pipe; client.target = target; cli = fork(); switch (cli) { case -1: ret = -1; goto done; case 0: actor = "CLIENT"; run_client(&client); exit(0); default: close(srv_pipe[1]); close(cli_pipe[0]); break; } closewait = -1; while (cli != -1 || srv != -1) { if (closewait < 0) { options = 0; } else { options = WNOHANG; } w = waitpid(-1, &status, options); if (w == cli) { cli = -1; } else if (w == srv) { srv = -1; } else { /* ? */ ret = -1; goto done; } /* wait 0.1s for max ten times for the other process to terminate, * then just exit */ if (closewait > 10) { ret = -1; goto done; } usleep(100000); closewait++; } ret = 0; done: if (cli > 0) { kill(cli, SIGKILL); } if (srv > 0) { kill(srv, SIGKILL); } return ret; } int main(int argc, const char *main_argv[]) { int opt; poptContext pc; int opt_version = 0; int opt_all = 0; char *opt_target = NULL; int ret, i, k; struct poptOption long_options[] = { POPT_AUTOHELP {"target", 't', POPT_ARG_STRING, &opt_target, 0, \ _("Specify the target name used for the tests"), NULL}, \ {"version", '\0', POPT_ARG_NONE, &opt_version, 0, \ _("Print version number and exit"), NULL }, \ {"all", '\0', POPT_ARG_NONE, &opt_all, 0, \ _("Test all gssproxy modes"), NULL }, \ POPT_TABLEEND }; argv = main_argv; pc = poptGetContext(argv[0], argc, argv, long_options, 0); while((opt = poptGetNextOpt(pc)) != -1) { switch(opt) { default: fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0), poptStrerror(opt)); poptPrintUsage(pc, stderr, 0); return 1; } } if (opt_version) { puts(VERSION""DISTRO_VERSION""PRERELEASE_VERSION); return 0; } if (opt_target == NULL) { fprintf(stderr, "Missing target!\n"); poptPrintUsage(pc, stderr, 0); return 1; } if (!opt_all) { return run_cli_srv_test(PROXY_LOCAL_ONLY, PROXY_LOCAL_ONLY, opt_target); } for (i=0; i<4; i++) { for (k=0; k<4; k++) { ret = run_cli_srv_test(i, k, opt_target); fprintf(stderr, "Running test with server proxy mode %s " "and client proxy mode %s %s\n", lookup_gssproxy_behavior(i), lookup_gssproxy_behavior(k), ret ? "failed" : "succeeded"); if (ret) { return ret; } } } return ret; } gssproxy-v0.8.2/tests/runtests.py0000755000174300017420000001117113456641744016625 0ustar gitgit00000000000000#!/usr/bin/python3 # Copyright (C) 2014,2015,2016 - GSS-Proxy contributors; see COPYING for the license. import argparse import importlib import signal import subprocess import sys import traceback import testlib from testlib import * def check_exec(name): env = {'PATH': '/sbin:/bin:/usr/sbin:/usr/bin'} ret = subprocess.call(["which", name], stdout=subprocess.DEVNULL, env=env) if ret != 0: print(f"Executable '{name}' not found in {env['PATH']}", file=sys.stderr) exit(1) def parse_args(): parser = argparse.ArgumentParser(description='GSS-Proxy Tests Environment') parser.add_argument('--path', default='%s/testdir' % os.getcwd(), help="Directory in which tests are run") parser.add_argument('--debug-all', default=False, action="store_true", help="Enable debugging for all test cases") parser.add_argument('--debug-gssproxy', default=False, action="store_true", help="Enable debugging for gssproxy command") parser.add_argument('--debug-cmd', default="gdb --args", help="Set the debugging command. Defaults to gdb " + "--args") parser.add_argument('--debug-num', default=-1, type=int, help="Specify the testcase number to debug") parser.add_argument('--timeout', default=15, type=int, help="Specify test case timeout limit") parser.add_argument('--valgrind-cmd', default="valgrind " + "--track-origins=yes", help="Set the valgrind command. Defaults to " + "valgrind --track-origins=yes") parser.add_argument('--force-valgrind', default=False, action="store_true", help="Force valgrind to be run on all test cases") args = vars(parser.parse_args()) testlib_process_args(args) return args def runtests_main(testfiles): args = parse_args() for e in ["bash", "pkg-config", "zcat", "kinit", "krb5kdc", "kdb5_util", "kadmin.local", "kdb5_ldap_util", "slapd", "slapadd", "ldapmodify", "valgrind"]: if e == "valgrind" and not args['valgrind_cmd'].startswith('valgrind'): continue check_exec(e) testdir = args['path'] if os.path.exists(testdir): shutil.rmtree(testdir) os.makedirs(testdir) processes = dict() errored = False try: wrapenv = setup_wrappers(testdir) write_ldap_krb5_config(testdir) ldapproc, ldapenv = setup_ldap(testdir, wrapenv) processes["LDAP(%d)" % ldapproc.pid] = ldapproc kdcproc, kdcenv = setup_kdc(testdir, wrapenv) processes['KDC(%d)' % kdcproc.pid] = kdcproc keysenv = setup_keys(testdir, kdcenv) gssapienv = setup_gssapi_env(testdir, kdcenv) if 'TERM' in os.environ: gssapienv['TERM'] = os.environ['TERM'] gssproxylog = os.path.join(testdir, 'gssproxy.log') logfile = open(gssproxylog, "a") gssproxyenv = keysenv gssproxyenv['KRB5_TRACE'] = os.path.join(testdir, 'gssproxy.trace') gproc, gpsocket = setup_gssproxy(testdir, logfile, gssproxyenv) time.sleep(5) #Give time to gssproxy to fully start up processes['GSS-Proxy(%d)' % gproc.pid] = gproc gssapienv['GSSPROXY_SOCKET'] = gpsocket basicconf = {'svc_name': "host@%s" % WRAP_HOSTNAME, 'keytab': os.path.join(testdir, SVC_KTNAME)} basicconf["gpid"] = gproc.pid basicconf["keysenv"] = keysenv print("Tests to be run: " + ", ".join(testfiles)) for f in testfiles: fmod = f[:-len(".py")] t = importlib.__import__(fmod) basicconf['prefix'] = str(testlib.cmd_index) basicconf['logpath'] = testdir r = t.run(testdir, gssapienv, basicconf) if r != 0: errored = True except Exception: traceback.print_exc() errored = True finally: for name in processes: print("Killing %s" % name) os.killpg(processes[name].pid, signal.SIGTERM) if errored: sys.exit(1) sys.exit(0) if __name__ == "__main__": print("\n") print("To pass arguments to the test suite, use CHECKARGS:") print(" make check CHECKARGS='--debug-num='") print("A full set of available options can be seen with --help") print("\n") testfiles = [f for f in os.listdir(os.path.dirname(sys.argv[0])) \ if f.endswith(".py") and f.startswith("t_")] testfiles.sort() runtests_main(testfiles) gssproxy-v0.8.2/tests/scripts/0000755000174300017420000000000013456641744016047 5ustar gitgit00000000000000gssproxy-v0.8.2/tests/scripts/dlopen.sh0000755000174300017420000000243013456641744017666 0ustar gitgit00000000000000#!/bin/sh test -n "$TMPDIR" || exit 1 tempdir="$TMPDIR/dlopentest" mkdir -p $tempdir cat >> $tempdir/dlopen.c << _EOF #include #include #include /* Simple program to see if dlopen() would succeed. */ int main(int argc, char **argv) { int i; char buf[PATH_MAX]; for (i = 1; i < argc; i++) { if (dlopen(argv[i], RTLD_NOW)) { fprintf(stdout, "dlopen() of \"%s\" succeeded.\n", argv[i]); } else { snprintf(buf, sizeof(buf), "./%s", argv[i]); if (dlopen(buf, RTLD_NOW)) { fprintf(stdout, "dlopen() of \"./%s\" " "succeeded.\n", argv[i]); } else { fprintf(stdout, "dlopen() of \"%s\" failed: " "%s\n", argv[i], dlerror()); return 1; } } } return 0; } _EOF for arg in $@ ; do case "$arg" in "") ;; -I*|-D*|-f*|-m*|-g*|-O*|-W*) cflags="$cflags $arg" ;; -l*|-L*) ldflags="$ldflags $arg" ;; /*) modules="$modules $arg" ;; *) modules="$modules $arg" ;; esac done ${CC:-gcc} $RPM_OPT_FLAGS $CFLAGS -o $tempdir/dlopen $cflags $tempdir/dlopen.c $ldflags -ldl retval=0 for module in $modules ; do case "$module" in "") ;; /*) $tempdir/dlopen "$module" retval=$? ;; *) $tempdir/dlopen ./"$module" retval=$? ;; esac done rm -f $tempdir/dlopen $tempdir/dlopen.c rmdir $tempdir exit $retval gssproxy-v0.8.2/tests/t_accept.c0000644000174300017420000000356113456641744016313 0ustar gitgit00000000000000/* Copyright (C) 2014 the GSS-PROXY contributors, see COPYING for license */ #include "t_utils.h" int main(int argc, const char *argv[]) { char buffer[MAX_RPC_SIZE]; uint32_t buflen; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER; gss_name_t src_name; uint32_t ret_maj; uint32_t ret_min; int ret = -1; /* We get stuff from stdin and spit it out on stderr */ ret = t_recv_buffer(STDIN_FD, buffer, &buflen); if (ret != 0) { DEBUG("Failed to read token from STDIN\n"); ret = -1; goto done; } in_token.value = buffer; in_token.length = buflen; ret_maj = gss_accept_sec_context(&ret_min, &context_handle, GSS_C_NO_CREDENTIAL, &in_token, GSS_C_NO_CHANNEL_BINDINGS, &src_name, NULL, &out_token, NULL, NULL, NULL); if (ret_maj) { DEBUG("Error accepting context\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } if (!out_token.length) { DEBUG("No output token ?"); ret = -1; goto done; } ret = t_send_buffer(STDOUT_FD, out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to client!\n"); ret = -1; goto done; } ret = 0; done: gss_delete_sec_context(&ret_min, &context_handle, NULL); gss_release_buffer(&ret_min, &out_token); gss_release_name(&ret_min, &src_name); return ret; } gssproxy-v0.8.2/tests/t_acquire.c0000644000174300017420000001115613456641744016504 0ustar gitgit00000000000000/* Copyright (C) 2014 the GSS-PROXY contributors, see COPYING for license */ #include "t_utils.h" #include int main(int argc, const char *argv[]) { gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL; gss_ctx_id_t init_ctx = GSS_C_NO_CONTEXT; gss_ctx_id_t accept_ctx = GSS_C_NO_CONTEXT; gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER; gss_name_t target_name; gss_OID_set_desc oid_set = { 1, discard_const(gss_mech_krb5) }; uint32_t ret_maj; uint32_t ret_min; int ret = -1; if (argc != 2) return -1; ret = t_string_to_name(argv[1], &target_name, GSS_C_NT_HOSTBASED_SERVICE); if (ret) { DEBUG("Failed to import server name from argv[1]\n"); ret = -1; goto done; } ret_maj = gss_acquire_cred(&ret_min, GSS_C_NO_NAME, GSS_C_INDEFINITE, &oid_set, GSS_C_INITIATE, &cred_handle, NULL, NULL); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_acquire_cred() failed\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } ret_maj = gss_store_cred(&ret_min, cred_handle, GSS_C_INITIATE, GSS_C_NULL_OID, 1, 1, NULL, NULL); if (ret_maj) { DEBUG("Error saving credentials\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } gss_release_cred(&ret_min, &cred_handle); ret_maj = gss_init_sec_context(&ret_min, GSS_C_NO_CREDENTIAL, &init_ctx, target_name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_token, NULL, &out_token, NULL, NULL); if (ret_maj != GSS_S_CONTINUE_NEEDED) { DEBUG("gss_init_sec_context() failed\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } /* We get stuff from stdin and spit it out on stderr */ if (!out_token.length) { DEBUG("No output token ?"); ret = -1; goto done; } /* in/out token inverted here intentionally */ ret_maj = gss_accept_sec_context(&ret_min, &accept_ctx, GSS_C_NO_CREDENTIAL, &out_token, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &in_token, NULL, NULL, NULL); if (ret_maj) { DEBUG("Error accepting context\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } if (!in_token.length) { DEBUG("No output token ?"); ret = -1; goto done; } gss_release_buffer(&ret_min, &out_token); ret_maj = gss_init_sec_context(&ret_min, GSS_C_NO_CREDENTIAL, &init_ctx, target_name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_token, NULL, &out_token, NULL, NULL); if (ret_maj) { DEBUG("Error initializing context\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } ret = 0; done: gss_release_buffer(&ret_min, &in_token); gss_release_buffer(&ret_min, &out_token); gss_release_cred(&ret_min, &cred_handle); gss_release_name(&ret_min, &target_name); gss_delete_sec_context(&ret_min, &init_ctx, GSS_C_NO_BUFFER); gss_delete_sec_context(&ret_min, &accept_ctx, GSS_C_NO_BUFFER); return ret; } gssproxy-v0.8.2/tests/t_acquire.py0000755000174300017420000000176013456641744016715 0ustar gitgit00000000000000#!/usr/bin/python3 # Copyright (C) 2015,2016 - GSS-Proxy contributors; see COPYING for the license. from testlib import * def run(testdir, env, conf, expected_failure=False): print("Testing basic acquire creds...", file=sys.stderr) conf['prefix'] = str(cmd_index) svc_keytab = os.path.join(testdir, SVC_KTNAME) testenv = {'KRB5CCNAME': os.path.join(testdir, 't' + conf['prefix'] + '_acquire.ccache'), 'KRB5_KTNAME': conf['keytab'], 'KRB5_TRACE': os.path.join(testdir, 't' + conf['prefix'] + '_acquire.trace'), 'GSS_USE_PROXY': 'yes', 'GSSPROXY_BEHAVIOR': 'REMOTE_FIRST'} testenv.update(env) cmd = "./tests/t_acquire " + conf['svc_name'] return run_testcase_cmd(testenv, conf, cmd, "Acquire", expected_failure) if __name__ == "__main__": from runtests import runtests_main runtests_main(["t_acquire.py"]) gssproxy-v0.8.2/tests/t_basic.py0000755000174300017420000001320313456641744016340 0ustar gitgit00000000000000#!/usr/bin/python3 # Copyright (C) 2014,2015,2016 - GSS-Proxy contributors; see COPYING for the license import testlib import os import signal import subprocess import sys def run(testdir, env, conf, expected_failure=False): print("Testing basic init/accept context", file=sys.stderr) conf['prefix'] = str(testlib.cmd_index) init_logfile = os.path.join(conf['logpath'], "test_%d.log" % testlib.cmd_index) init_logfile = open(init_logfile, 'a') accept_logfile = os.path.join(conf['logpath'], "test_%d.log" % (testlib.cmd_index + 1)) accept_logfile = open(accept_logfile, 'a') svcenv = {'KRB5_KTNAME': conf['keytab'], 'KRB5CCNAME': os.path.join(testdir, 't' + conf['prefix'] + '_accept.ccache'), 'KRB5_TRACE': os.path.join(testdir, 't' + conf['prefix'] + '_accept.trace')} svcenv.update(env) client_name = conf.get('client_name', None) if client_name is not None: init_cmd = ["./tests/t_init", conf['svc_name'], client_name] else: init_cmd = ["./tests/t_init", conf['svc_name']] init_cmd = " ".join(init_cmd) accept_cmd = " ".join(["./tests/t_accept"]) clienv = {'KRB5CCNAME': os.path.join(testdir, 't' + conf['prefix'] + '_init.ccache'), 'KRB5_TRACE': os.path.join(testdir, 't' + conf['prefix'] + '_init.trace'), 'GSS_USE_PROXY': 'yes', 'GSSPROXY_BEHAVIOR': 'REMOTE_FIRST'} clienv.update(env) print("[CLIENV]\n%s\nCLI NAME: %s\n" % ( clienv, client_name), file=init_logfile) print("[SRVENV]\n%s\n" % (svcenv), file=accept_logfile) init_logfile.flush() accept_logfile.flush() pipe0 = os.pipe() pipe1 = os.pipe() if testlib.debug_cmd_index == testlib.cmd_index: p1 = subprocess.Popen(init_cmd, stdin=pipe0[0], stdout=pipe1[1], stderr=init_logfile, env=clienv, preexec_fn=os.setsid, shell=True, executable="/bin/bash") print("PID: %d\n" % p1.pid) print("Attach and start debugging, then press enter to start t_init.") input() p2 = subprocess.Popen(["./tests/t_accept"], stdin=pipe1[0], stdout=pipe0[1], stderr=accept_logfile, env=svcenv, preexec_fn=os.setsid, shell=True, executable="/bin/bash") print("To resume tests if hung, kill pid %d\n" % p2.pid) p2.wait() init_logfile.close() accept_logfile.close() testlib.cmd_index += 2 return int(expected_failure) elif testlib.debug_cmd_index == testlib.cmd_index+1: p2 = subprocess.Popen(["./tests/t_accept"], stdin=pipe1[0], stdout=pipe0[1], stderr=accept_logfile, env=svcenv, preexec_fn=os.setsid, shell=True, executable="/bin/bash") print("PID: %d\n" % p2.pid) print("Attach and start debugging, then press enter to start t_init.") input() p1 = subprocess.Popen(init_cmd, stdin=pipe0[0], stdout=pipe1[1], stderr=init_logfile, env=clienv, preexec_fn=os.setsid, shell=True, executable="/bin/bash") print("To resume tests if hung, kill pid %d\n" % p1.pid) p1.wait() init_logfile.close() accept_logfile.close() testlib.cmd_index += 2 return int(expected_failure) if testlib.valgrind_everywhere: accept_cmd = testlib.valgrind_cmd + accept_cmd init_cmd = testlib.valgrind_cmd + init_cmd p2 = subprocess.Popen(accept_cmd, stdin=pipe1[0], stdout=pipe0[1], stderr=accept_logfile, env=svcenv, preexec_fn=os.setsid, shell=True, executable="/bin/bash") p1 = subprocess.Popen(init_cmd, stdin=pipe0[0], stdout=pipe1[1], stderr=init_logfile, env=clienv, preexec_fn=os.setsid, shell=True, executable="/bin/bash") try: p1.wait(testlib.testcase_wait) p2.wait(testlib.testcase_wait) except subprocess.TimeoutExpired: # {p1,p2}.returncode are set to None here if not expected_failure: testlib.print_warning("warning", "timeout") init_logfile.close() accept_logfile.close() testlib.print_return(p1.returncode, testlib.cmd_index, "(%d) Init" % testlib.cmd_index, expected_failure) testlib.print_return(p2.returncode, testlib.cmd_index + 1, "(%d) Accept" % (testlib.cmd_index + 1), expected_failure) testlib.cmd_index += 2 try: os.killpg(p1.pid, signal.SIGTERM) os.killpg(p2.pid, signal.SIGTERM) except OSError: pass if p1.returncode != 0: return p1.returncode if not expected_failure else int(not p1.returncode) elif p2.returncode != 0: return p2.returncode if not expected_failure else int(not p2.returncode) return int(expected_failure) if __name__ == "__main__": from runtests import runtests_main runtests_main(["t_basic.py"]) gssproxy-v0.8.2/tests/t_cred_store.c0000644000174300017420000000542413456641744017205 0ustar gitgit00000000000000/* Copyright (C) 2016 the GSS-PROXY contributors; see COPYING for license */ #include "t_utils.h" #include int main(int argc, const char *argv[]) { uint32_t major, minor; gss_key_value_set_desc store = {}; int ret = -1; gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL; gss_OID_set_desc oid_set = { 1, discard_const(gss_mech_krb5) }; if (argc != 3) { DEBUG("Usage: %s source_ccache dest_ccache\n", argv[0]); goto done; } store.elements = calloc(1, sizeof(struct gss_key_value_element_struct)); if (!store.elements) { DEBUG("calloc failed\n"); goto done; } store.count = 1; store.elements[0].key = "ccache"; /* Acquire initial cred handle from store */ store.elements[0].value = argv[1]; major = gss_acquire_cred_from(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE, &oid_set, GSS_C_INITIATE, &store, &cred_handle, NULL, NULL); if (major != GSS_S_COMPLETE) { DEBUG("gss_acquire_cred_from() failed\n"); t_log_failure(GSS_C_NO_OID, major, minor); goto done; } /* Test storing credentials */ store.elements[0].value = argv[2]; major = gss_store_cred_into(&minor, cred_handle, GSS_C_INITIATE, GSS_C_NO_OID, 1, 1, &store, NULL, NULL); if (major != GSS_S_COMPLETE) { DEBUG("gss_store_cred_into() failed\n"); t_log_failure(GSS_C_NO_OID, major, minor); goto done; } /* Test that we can actually manipulate the stored credentials */ gss_release_cred(&minor, &cred_handle); cred_handle = GSS_C_NO_CREDENTIAL; major = gss_acquire_cred_from(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE, &oid_set, GSS_C_INITIATE, &store, &cred_handle, NULL, NULL); if (major != GSS_S_COMPLETE) { DEBUG("second gss_acquire_cred_from() failed\n"); t_log_failure(GSS_C_NO_OID, major, minor); goto done; } ret = 0; done: if (store.elements) { free(store.elements); } gss_release_cred(&minor, &cred_handle); return ret; } gssproxy-v0.8.2/tests/t_cred_store.py0000755000174300017420000000273413456641744017417 0ustar gitgit00000000000000#!/usr/bin/python3 # Copyright (C) 2016 - GSS-Proxy contributors; see COPYING for the license. from testlib import * def run(testdir, env, conf): print("Testing cred store extensions...", file=sys.stderr) conf['prefix'] = str(cmd_index) logfile = os.path.join(conf["logpath"], "test_%d.log" % cmd_index) logfile = open(logfile, 'a') ccache = "FILE:" + os.path.join(testdir, "t" + conf["prefix"] + "_cred_store.ccache") testenv = {"KRB5CCNAME": ccache} testenv.update(env) usr_keytab = os.path.join(testdir, USR_KTNAME) ksetup = subprocess.Popen(["kinit", "-kt", usr_keytab, USR_NAME], stdout=logfile, stderr=logfile, env=testenv, preexec_fn=os.setsid) ksetup.wait() if ksetup.returncode != 0: raise ValueError("Kinit %s failed" % USR_NAME) testenv = {"KRB5_TRACE": os.path.join(testdir, "t" + conf["prefix"] + ".trace"), "GSS_USE_PROXY": "yes", "GSSPROXY_BEHAVIOR": "REMOTE_FIRST"} testenv.update(env) temp_ccache = "FILE:" + os.path.join(testdir, "t" + conf["prefix"] + "_temp.ccache") cmd = " ".join(["./tests/t_cred_store", ccache, temp_ccache]) return run_testcase_cmd(testenv, conf, cmd, "Cred store") if __name__ == "__main__": from runtests import runtests_main runtests_main(["t_cred_store.py"]) gssproxy-v0.8.2/tests/t_impersonate.c0000644000174300017420000001664613456641744017412 0ustar gitgit00000000000000/* Copyright (C) 2014 the GSS-PROXY contributors, see COPYING for license */ #include "t_utils.h" #include #include int main(int argc, const char *argv[]) { gss_cred_id_t impersonator_cred_handle = GSS_C_NO_CREDENTIAL; gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL; gss_ctx_id_t init_ctx = GSS_C_NO_CONTEXT; gss_ctx_id_t accept_ctx = GSS_C_NO_CONTEXT; gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER; gss_name_t user_name; gss_name_t proxy_name; gss_name_t target_name; gss_OID_set_desc oid_set = { 1, discard_const(gss_mech_krb5) }; uint32_t ret_maj; uint32_t ret_min; uint32_t flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG; int ret = -1; bool selfhalf = false; bool proxyhalf = false; gss_key_value_element_desc ccelement = { "ccache", NULL }; gss_key_value_set_desc cred_store = { 1, &ccelement }; if (argc < 5) return -1; ret = t_string_to_name(argv[1], &user_name, GSS_C_NT_USER_NAME); if (ret) { DEBUG("Failed to import user name from argv[1]\n"); ret = -1; goto done; } ret = t_string_to_name(argv[2], &proxy_name, GSS_C_NT_HOSTBASED_SERVICE); if (ret) { DEBUG("Failed to import server name from argv[2]\n"); ret = -1; goto done; } ret = t_string_to_name(argv[3], &target_name, GSS_C_NT_HOSTBASED_SERVICE); if (ret) { DEBUG("Failed to import server name from argv[2]\n"); ret = -1; goto done; } ccelement.value = argv[4]; if (argc > 5) { if (strcmp(argv[5], "s4u2self") == 0) { selfhalf = true; } else if (strcmp(argv[5], "s4u2proxy") == 0) { proxyhalf = true; } else { DEBUG("Invalid argument 5: %s\n", argv[5]); ret = -1; goto done; } DEBUG("S4U2%s half\n", selfhalf ? "Self" : "Proxy"); } if (proxyhalf) { ret_maj = gss_acquire_cred_from(&ret_min, user_name, GSS_C_INDEFINITE, &oid_set, GSS_C_INITIATE, &cred_store, &cred_handle, NULL, NULL); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_acquire_cred_from() [s4u2proxy] failed\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } flags = GSS_C_MUTUAL_FLAG; } else { ret_maj = gss_acquire_cred_from(&ret_min, proxy_name, GSS_C_INDEFINITE, &oid_set, GSS_C_BOTH, &cred_store, &impersonator_cred_handle, NULL, NULL); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_acquire_cred_from() [%s] failed\n", selfhalf ? "s4u2self" : "impersonate"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } ret_maj = gss_acquire_cred_impersonate_name(&ret_min, impersonator_cred_handle, user_name, GSS_C_INDEFINITE, &oid_set, GSS_C_INITIATE, &cred_handle, NULL, NULL); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_acquire_cred_impersonate_name() failed\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } } if (selfhalf) { ret_maj = gss_store_cred_into(&ret_min, cred_handle, GSS_C_INITIATE, discard_const(gss_mech_krb5), 1, 0, &cred_store, NULL, NULL); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_store_cred_into() failed\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; } goto done; } ret_maj = gss_init_sec_context(&ret_min, cred_handle, &init_ctx, target_name, GSS_C_NO_OID, flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_token, NULL, &out_token, NULL, NULL); if (ret_maj != GSS_S_CONTINUE_NEEDED) { DEBUG("gss_init_sec_context() failed\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } /* We get stuff from stdin and spit it out on stderr */ if (!out_token.length) { DEBUG("No output token ?"); ret = -1; goto done; } /* in/out token inverted here intentionally */ ret_maj = gss_accept_sec_context(&ret_min, &accept_ctx, GSS_C_NO_CREDENTIAL, &out_token, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &in_token, NULL, NULL, NULL); if (ret_maj) { DEBUG("Error accepting context\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } if (!in_token.length) { DEBUG("No output token ?"); ret = -1; goto done; } gss_release_buffer(&ret_min, &out_token); ret_maj = gss_init_sec_context(&ret_min, cred_handle, &init_ctx, target_name, GSS_C_NO_OID, flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_token, NULL, &out_token, NULL, NULL); if (ret_maj) { DEBUG("Error initializing context\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } ret = 0; done: gss_release_buffer(&ret_min, &in_token); gss_release_buffer(&ret_min, &out_token); gss_release_cred(&ret_min, &impersonator_cred_handle); gss_release_cred(&ret_min, &cred_handle); return ret; } gssproxy-v0.8.2/tests/t_impersonate.py0000755000174300017420000001162513456641744017613 0ustar gitgit00000000000000#!/usr/bin/python3 # Copyright (C) 2015,2016 - GSS-Proxy contributors; see COPYING for the license from testlib import * IMPERSONATE_CONF_TEMPLATE = ''' [gssproxy] debug_level = 2 [service/impersonate] socket = ${TESTDIR}/impersonate.socket mechs = krb5 cred_store = keytab:${GSSPROXY_KEYTAB} cred_store = client_keytab:${GSSPROXY_CLIENT_KEYTAB} allow_protocol_transition = yes allow_constrained_delegation = yes euid = ${UIDNUMBER} [service/selfonly] socket = ${TESTDIR}/impersonate-selfonly.socket mechs = krb5 cred_store = keytab:${GSSPROXY_KEYTAB} cred_store = client_keytab:${GSSPROXY_CLIENT_KEYTAB} allow_protocol_transition = yes euid = ${UIDNUMBER} [service/proxyonly] socket = ${TESTDIR}/impersonate-proxyonly.socket mechs = krb5 cred_store = keytab:${GSSPROXY_KEYTAB} cred_store = client_keytab:${GSSPROXY_CLIENT_KEYTAB} allow_constrained_delegation = yes euid = ${UIDNUMBER} ''' def run_cmd(testdir, env, conf, name, socket, cmd, keytab, expected_failure): conf['prefix'] = str(cmd_index) testenv = env.copy() testenv.update({'KRB5CCNAME': os.path.join(testdir, 't' + conf['prefix'] + '_impersonate.ccache'), 'KRB5_KTNAME': os.path.join(testdir, keytab), 'KRB5_TRACE': os.path.join(testdir, 't' + conf['prefix'] + '_impersonate.trace'), 'GSS_USE_PROXY': 'yes', 'GSSPROXY_SOCKET': socket, 'GSSPROXY_BEHAVIOR': 'REMOTE_FIRST'}) return run_testcase_cmd(testenv, conf, cmd, name, expected_failure) def run(testdir, env, conf): print("Testing impersonate creds...", file=sys.stderr) path_prefix = os.path.join(testdir, 't' + conf['prefix'] + '_') # Change gssproxy conf for our test keysenv = conf["keysenv"].copy() keysenv['KRB5_KTNAME'] = os.path.join(testdir, PROXY_KTNAME) update_gssproxy_conf(testdir, keysenv, IMPERSONATE_CONF_TEMPLATE) os.kill(conf["gpid"], signal.SIGHUP) time.sleep(1) #Let gssproxy reload everything rets = [] # Test all permitted msg = "Impersonate" socket = os.path.join(testdir, 'impersonate.socket') cmd = " ".join(["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, path_prefix + 'impersonate.cache']) r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, False) rets.append(r) #Test self fail msg = "Impersonate fail self" socket = os.path.join(testdir, 'impersonate-proxyonly.socket') cmd = " ".join(["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, path_prefix + 'impersonate.cache']) r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, True) rets.append(r) #Test proxy fail msg = "Impersonate fail proxy" socket = os.path.join(testdir, 'impersonate-selfonly.socket') cmd = " ".join(["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, path_prefix + 'impersonate.cache']) r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, True) rets.append(r) #Test s4u2self half succeed msg = "s4u2self delegation" socket = os.path.join(testdir, 'impersonate-selfonly.socket') cmd = " ".join(["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, path_prefix + 'impersonate.cache', 's4u2self']) r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, False) rets.append(r) #Test proxy to self succeed msg = "Impersonate to self" socket = os.path.join(testdir, 'impersonate-selfonly.socket') cmd = " ".join(["./tests/t_impersonate", USR_NAME, HOST_GSS, HOST_GSS, path_prefix + 'impersonate.cache', 's4u2proxy']) r = run_cmd(testdir, env, conf, msg, socket, cmd, SVC_KTNAME, False) rets.append(r) #Test s4u2proxy half fail msg = "s4u2proxy fail" socket = os.path.join(testdir, 'impersonate-selfonly.socket') cmd = " ".join(["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, path_prefix + 'impersonate.cache', 's4u2proxy']) r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, True) rets.append(r) #Test s4u2proxy half succeed msg = "s4u2proxy" socket = os.path.join(testdir, 'impersonate-proxyonly.socket') cmd = " ".join(["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, path_prefix + 'impersonate.cache', 's4u2proxy']) r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, False) rets.append(r) # Reset back gssproxy conf update_gssproxy_conf(testdir, keysenv, GSSPROXY_CONF_TEMPLATE) os.kill(conf["gpid"], signal.SIGHUP) time.sleep(1) #Let gssproxy reload everything e = [r for r in rets if r != 0] if len(e) > 0: return e[0] return 0 if __name__ == "__main__": from runtests import runtests_main runtests_main(["t_impersonate.py"]) gssproxy-v0.8.2/tests/t_init.c0000644000174300017420000000766213456641744016025 0ustar gitgit00000000000000/* Copyright (C) 2014 the GSS-PROXY contributors, see COPYING for license */ #include "t_utils.h" int main(int argc, const char *argv[]) { char buffer[MAX_RPC_SIZE]; uint32_t buflen; gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER; gss_name_t name; gss_name_t i_name; gss_OID_set_desc oid_set = { 1, discard_const(gss_mech_krb5) }; uint32_t ret_maj; uint32_t ret_min; int ret = -1; ret = t_string_to_name(argv[1], &name, GSS_C_NT_HOSTBASED_SERVICE); if (ret) { DEBUG("Failed to import server name from argv[1]\n"); ret = -1; goto done; } if (argc > 2) { ret = t_string_to_name(argv[2], &i_name, discard_const(GSS_KRB5_NT_PRINCIPAL_NAME)); if (ret) { DEBUG("Failed to import client name from argv[2]\n"); ret = -1; goto done; } ret_maj = gss_acquire_cred(&ret_min, i_name, GSS_C_INDEFINITE, &oid_set, GSS_C_INITIATE, &cred_handle, NULL, NULL); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_acquire_cred() failed\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } } ret_maj = gss_init_sec_context(&ret_min, cred_handle, &context_handle, name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_token, NULL, &out_token, NULL, NULL); if (ret_maj != GSS_S_CONTINUE_NEEDED) { DEBUG("gss_init_sec_context() failed\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } /* We get stuff from stdin and spit it out on stderr */ if (!out_token.length) { DEBUG("No output token ?"); ret = -1; goto done; } ret = t_send_buffer(STDOUT_FD, out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to server!\n"); ret = -1; goto done; } ret = t_recv_buffer(STDIN_FD, buffer, &buflen); if (ret != 0) { DEBUG("Failed to read token from STDIN\n"); ret = -1; goto done; } in_token.value = buffer; in_token.length = buflen; ret_maj = gss_init_sec_context(&ret_min, cred_handle, &context_handle, name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_token, NULL, &out_token, NULL, NULL); if (ret_maj) { DEBUG("Error initializing context\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } ret = 0; done: gss_delete_sec_context(&ret_min, &context_handle, NULL); gss_release_cred(&ret_min, &cred_handle); gss_release_buffer(&ret_min, &out_token); gss_release_name(&ret_min, &name); return ret; } gssproxy-v0.8.2/tests/t_interpose.py0000755000174300017420000000210113456641744017262 0ustar gitgit00000000000000#!/usr/bin/python3 # Copyright (C) 2014,2015,2016 - GSS-Proxy contributors; see COPYING for the license from testlib import * def run(testdir, env, conf): print("Testing interposer...", file=sys.stderr) conf['prefix'] = str(cmd_index) logfile = os.path.join(conf['logpath'], "test_%d.log" % cmd_index) logfile = open(logfile, 'a') ienv = {"KRB5CCNAME": os.path.join(testdir, 'interpose_ccache'), "KRB5_KTNAME": os.path.join(testdir, SVC_KTNAME)} ienv.update(env) usr_keytab = os.path.join(testdir, USR_KTNAME) ksetup = subprocess.Popen(["kinit", "-kt", usr_keytab, USR_NAME], stdout=logfile, stderr=logfile, env=ienv, preexec_fn=os.setsid) ksetup.wait() if ksetup.returncode != 0: raise ValueError('Kinit %s failed' % USR_NAME) cmd = " ".join(["./interposetest", "-t", "host@%s" % WRAP_HOSTNAME]) return run_testcase_cmd(ienv, conf, cmd, "Interpose") if __name__ == "__main__": from runtests import runtests_main runtests_main(["t_interpose.py"]) gssproxy-v0.8.2/tests/t_multi_key.py0000755000174300017420000000436213456641744017267 0ustar gitgit00000000000000#!/usr/bin/python3 # Copyright (C) 2014,2015,2016 - GSS-Proxy contributors; see COPYING for the license from t_basic import run as run_basic_test from testlib import * # Q: What are we testing here ? # # A: A client calling gss_init_sec_context() w/o explicitly acquiring # credentials before hand. [Note: in this case gssproxy uses the 'keytab' # specified in the store and ignores the 'client_keytab' one]. # # A gssproxy configruation where the keytab containes multiple keys, and a # krb5_principal option that sepcify what name we want to use. # # We try both names to make sure we target a specific key and not just pick up # the first in the keytab (which is the normal behavior). def run(testdir, env, conf): setup_multi_keys(testdir, env) conf['prefix'] = str(cmd_index) prefix = conf["prefix"] print("Testing multiple keys Keytab with first principal", file=sys.stderr) sys.stderr.write(" ") conf["prefix"] = prefix + "_1" if os.path.exists(os.path.join(testdir, 'gssproxy', 'gpccache')): os.unlink(os.path.join(testdir, 'gssproxy', 'gpccache')) p1env = {} p1env.update(conf["keysenv"]) p1env['client_name'] = MULTI_UPN p1env['KRB5_KTNAME'] = os.path.join(testdir, MULTI_KTNAME) update_gssproxy_conf(testdir, p1env, GSSPROXY_MULTI_TEMPLATE) os.kill(conf["gpid"], signal.SIGHUP) time.sleep(1) #Let gssproxy reload everything r1 = run_basic_test(testdir, env, conf) print("Testing multiple keys Keytab with second principal", file=sys.stderr) sys.stderr.write(" ") if os.path.exists(os.path.join(testdir, 'gssproxy', 'gpccache')): os.unlink(os.path.join(testdir, 'gssproxy', 'gpccache')) conf['prefix'] = prefix + "_2" p2env = {} p2env.update(conf["keysenv"]) p2env['client_name'] = MULTI_SVC p2env['KRB5_KTNAME'] = os.path.join(testdir, MULTI_KTNAME) update_gssproxy_conf(testdir, p2env, GSSPROXY_MULTI_TEMPLATE) os.kill(conf["gpid"], signal.SIGHUP) time.sleep(1) #Let gssproxy reload everything r2 = run_basic_test(testdir, env, conf) if r1 != 0: return r1 elif r2 != 0: return r2 return 0 if __name__ == "__main__": from runtests import runtests_main runtests_main(["t_multi_key.py"]) gssproxy-v0.8.2/tests/t_program.py0000644000174300017420000000316413456641744016730 0ustar gitgit00000000000000#!/usr/bin/python3 # Copyright (C) 2017 - GSS-Proxy contributors; see COPYING for the license. from testlib import * from t_acquire import run as run_acquire_test import os GSSPROXY_PROGRAM = ''' [gssproxy] debug_level = 3 [service/t_acquire] mechs = krb5 cred_store = keytab:${GSSPROXY_KEYTAB} cred_store = client_keytab:${GSSPROXY_CLIENT_KEYTAB} trusted = yes euid = ${UIDNUMBER} allow_client_ccache_sync = yes program = ${PROGDIR}/t_acquire ''' def run(testdir, env, conf): conf['prefix'] = str(cmd_index) prefix = conf["prefix"] retval = 0 print("Testing positive program name matching...", file=sys.stderr) sys.stderr.write(" ") conf["prefix"] = prefix + "_1" update_gssproxy_conf(testdir, conf["keysenv"], GSSPROXY_PROGRAM) os.kill(conf["gpid"], signal.SIGHUP) time.sleep(1) retval |= run_acquire_test(testdir, env, conf) print("Testing negative program name matching...", file=sys.stderr) sys.stderr.write(" ") conf["prefix"] = prefix + "_2" bad_progdir = GSSPROXY_PROGRAM.replace("${PROGDIR}", "//bad/path") update_gssproxy_conf(testdir, conf["keysenv"], bad_progdir) os.kill(conf["gpid"], signal.SIGHUP) time.sleep(1) retval |= run_acquire_test(testdir, env, conf, expected_failure=True) # be a good citizen and clean up after ourselves update_gssproxy_conf(testdir, conf["keysenv"], GSSPROXY_CONF_TEMPLATE) os.kill(conf["gpid"], signal.SIGHUP) time.sleep(1) print_return(retval, -1, "Program", False) return retval if __name__ == "__main__": from runtests import runtests_main runtests_main(["t_program.py"]) gssproxy-v0.8.2/tests/t_reloading.py0000755000174300017420000000425113456641744017226 0ustar gitgit00000000000000#!/usr/bin/python3 # Copyright (C) 2014,2015,2016 - GSS-Proxy contributors; see COPYING for the license from testlib import * from t_basic import run as run_basic_test def run(testdir, env, basicconf): basicconf['prefix'] = str(cmd_index) prefix = basicconf['prefix'] keysenv = basicconf["keysenv"] rets = [] print("Testing basic SIGHUP with no change", file=sys.stderr) sys.stderr.write(" ") basicconf['prefix'] += prefix + "_1" os.kill(basicconf["gpid"], signal.SIGHUP) time.sleep(1) #Let gssproxy reload everything r = run_basic_test(testdir, env, basicconf) rets.append(r) print("Testing SIGHUP with dropped service", file=sys.stderr) sys.stderr.write(" ") basicconf['prefix'] = prefix + "_2" update_gssproxy_conf(testdir, keysenv, GSSPROXY_CONF_MINIMAL_TEMPLATE) os.kill(basicconf["gpid"], signal.SIGHUP) time.sleep(1) #Let gssproxy reload everything r = run_basic_test(testdir, env, basicconf, True) rets.append(r) print("Testing SIGHUP with new service", file=sys.stderr) sys.stderr.write(" ") basicconf['prefix'] = prefix + "_3" update_gssproxy_conf(testdir, keysenv, GSSPROXY_CONF_TEMPLATE) os.kill(basicconf["gpid"], signal.SIGHUP) time.sleep(1) #Let gssproxy reload everything r = run_basic_test(testdir, env, basicconf) rets.append(r) print("Testing SIGHUP with change of socket", file=sys.stderr) sys.stderr.write(" ") basicconf['prefix'] = prefix + "_4" update_gssproxy_conf(testdir, keysenv, GSSPROXY_CONF_SOCKET_TEMPLATE) env['GSSPROXY_SOCKET'] += "2" os.kill(basicconf["gpid"], signal.SIGHUP) time.sleep(1) #Let gssproxy reload everything r = run_basic_test(testdir, env, basicconf) rets.append(r) # restore old configuration env['GSSPROXY_SOCKET'] = env['GSSPROXY_SOCKET'][:-1] update_gssproxy_conf(testdir, keysenv, GSSPROXY_CONF_TEMPLATE) os.kill(basicconf["gpid"], signal.SIGHUP) time.sleep(1) #Let gssproxy reload everything e = [r for r in rets if r != 0] if len(e) > 0: return e[0] return 0 if __name__ == "__main__": from runtests import runtests_main runtests_main(["t_reloading.py"]) gssproxy-v0.8.2/tests/t_setcredopt.c0000644000174300017420000001311713456641744017226 0ustar gitgit00000000000000/* Copyright (C) 2017 the GSS-PROXY contributors, see COPYING for license */ #include "t_utils.h" #include #include int main(int argc, const char *argv[]) { gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL; gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER; gss_ctx_id_t init_ctx = GSS_C_NO_CONTEXT; gss_ctx_id_t accept_ctx = GSS_C_NO_CONTEXT; gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER; gss_name_t user_name; gss_name_t target_name; gss_OID_set_desc oid_set = { 1, discard_const(gss_mech_krb5) }; uint32_t ret_maj; uint32_t ret_min; uint32_t flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG; uint32_t ret_flags = 0; int ret = -1; gss_key_value_element_desc ccelement = { "ccache", NULL }; gss_key_value_set_desc cred_store = { 1, &ccelement }; krb5_enctype enc = ENCTYPE_AES256_CTS_HMAC_SHA1_96; if (argc < 3) return -1; ret = t_string_to_name(argv[1], &user_name, GSS_C_NT_USER_NAME); if (ret) { DEBUG("Failed to import user name from argv[1]\n"); ret = -1; goto done; } ret = t_string_to_name(argv[2], &target_name, GSS_C_NT_HOSTBASED_SERVICE); if (ret) { DEBUG("Failed to import server name from argv[2]\n"); ret = -1; goto done; } ccelement.value = argv[3]; ret_maj = gss_acquire_cred_from(&ret_min, user_name, GSS_C_INDEFINITE, &oid_set, GSS_C_INITIATE, &cred_store, &cred_handle, NULL, NULL); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_acquire_cred_from() [%s,%s] failed\n", argv[1], argv[3]); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } ret_maj = gss_set_cred_option(&ret_min, &cred_handle, (gss_OID)GSS_KRB5_CRED_NO_CI_FLAGS_X, &empty_buffer); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_set_cred_option(GSS_KRB5_CRED_NO_CI_FLAGS_X) failed\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } ret_maj = gss_krb5_set_allowable_enctypes(&ret_min, cred_handle, 1, &enc); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_krb5_set_allowable_enctypes() failed\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } ret_maj = gss_init_sec_context(&ret_min, cred_handle, &init_ctx, target_name, GSS_C_NO_OID, flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_token, NULL, &out_token, NULL, NULL); if (ret_maj != GSS_S_CONTINUE_NEEDED) { DEBUG("gss_init_sec_context() failed\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } /* We get stuff from stdin and spit it out on stderr */ if (!out_token.length) { DEBUG("No output token ?"); ret = -1; goto done; } /* in/out token inverted here intentionally */ ret_maj = gss_accept_sec_context(&ret_min, &accept_ctx, GSS_C_NO_CREDENTIAL, &out_token, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &in_token, &ret_flags, NULL, NULL); if (ret_maj) { DEBUG("Error accepting context\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } /* now test that flags are as expected */ if (ret_flags & (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG)) { DEBUG("Set NO CI Flags but ret_flags matches (%x)!\n", ret_flags); ret = -1; goto done; } if (!in_token.length) { DEBUG("No output token ?"); ret = -1; goto done; } gss_release_buffer(&ret_min, &out_token); ret_maj = gss_init_sec_context(&ret_min, cred_handle, &init_ctx, target_name, GSS_C_NO_OID, flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_token, NULL, &out_token, NULL, NULL); if (ret_maj) { DEBUG("Error initializing context\n"); t_log_failure(GSS_C_NO_OID, ret_maj, ret_min); ret = -1; goto done; } ret = 0; done: gss_release_buffer(&ret_min, &in_token); gss_release_buffer(&ret_min, &out_token); gss_release_cred(&ret_min, &cred_handle); return ret; } gssproxy-v0.8.2/tests/t_setcredopt.py0000755000174300017420000000260213456641744017434 0ustar gitgit00000000000000#!/usr/bin/python3 # Copyright (C) 2017 - GSS-Proxy contributors; see COPYING for the license from testlib import * def run(testdir, env, conf): print("Testing setting credential options...", file=sys.stderr) conf['prefix'] = str(cmd_index) path_prefix = os.path.join(testdir, 't' + conf['prefix'] + '_') init_ccache = path_prefix + 'sco_init.ccache' logfile = os.path.join(conf['logpath'], "test_%d.log" % cmd_index) logfile = open(logfile, 'a') testenv = env.copy() testenv.update({'KRB5CCNAME': init_ccache}) usr_keytab = os.path.join(testdir, USR_KTNAME) ksetup = subprocess.Popen(["kinit", "-kt", usr_keytab, USR_NAME], stdout=logfile, stderr=logfile, env=testenv, preexec_fn=os.setsid) ksetup.wait() if ksetup.returncode != 0: raise ValueError("Kinit %s failed" % USR_NAME) cmd = " ".join(["./tests/t_setcredopt", USR_NAME, HOST_GSS, init_ccache]) testenv.update({'KRB5CCNAME': path_prefix + 'sco.ccache', 'KRB5_KTNAME': os.path.join(testdir, PROXY_KTNAME), 'KRB5_TRACE': path_prefix + 'sco.trace', 'GSSPROXY_BEHAVIOR': 'REMOTE_FIRST'}) return run_testcase_cmd(testenv, conf, cmd, "Set cred options") if __name__ == "__main__": from runtests import runtests_main runtests_main(["t_setcredopt.py"]) gssproxy-v0.8.2/tests/t_utils.c0000644000174300017420000000441513456641744016213 0ustar gitgit00000000000000/* Copyright (C) 2014 the GSS-PROXY contributors, see COPYING for license */ #include "t_utils.h" #include #include #include int t_send_buffer(int fd, char *buf, uint32_t len) { uint32_t size; ssize_t wn; size_t pos; size = htonl(len); wn = write(fd, &size, sizeof(uint32_t)); if (wn != 4) { return EIO; } pos = 0; while (len > pos) { wn = write(fd, buf + pos, len - pos); if (wn == -1) { if (errno == EINTR) { continue; } return errno; } pos += wn; } return 0; } int t_recv_buffer(int fd, char *buf, uint32_t *len) { uint32_t size; ssize_t rn; size_t pos; rn = read(fd, &size, sizeof(uint32_t)); if (rn != 4) { return EIO; } *len = ntohl(size); if (*len > MAX_RPC_SIZE) { return EINVAL; } pos = 0; while (*len > pos) { rn = read(fd, buf + pos, *len - pos); if (rn == -1) { if (errno == EINTR) { continue; } return errno; } if (rn == 0) { return EIO; } pos += rn; } return 0; } void t_log_failure(gss_OID mech, uint32_t maj, uint32_t min) { uint32_t msgctx; uint32_t discard; gss_buffer_desc tmp; fprintf(stderr, "Failed with:"); if (mech != GSS_C_NO_OID) { gss_oid_to_str(&discard, mech, &tmp); fprintf(stderr, " (OID: %s)", (char *)tmp.value); gss_release_buffer(&discard, &tmp); } msgctx = 0; gss_display_status(&discard, maj, GSS_C_GSS_CODE, mech, &msgctx, &tmp); fprintf(stderr, " %s,", (char *)tmp.value); gss_release_buffer(&discard, &tmp); msgctx = 0; gss_display_status(&discard, min, GSS_C_MECH_CODE, mech, &msgctx, &tmp); fprintf(stderr, " %s\n", (char *)tmp.value); gss_release_buffer(&discard, &tmp); } int t_string_to_name(const char *string, gss_name_t *name, gss_OID type) { gss_buffer_desc target_buf; uint32_t ret_maj; uint32_t ret_min; target_buf.value = strdup(string); target_buf.length = strlen(string) + 1; ret_maj = gss_import_name(&ret_min, &target_buf, type, name); free(target_buf.value); return ret_maj; } gssproxy-v0.8.2/tests/t_utils.h0000644000174300017420000000146413456641744016221 0ustar gitgit00000000000000/* Copyright (C) 2014 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include #include #include #include #include #define STDIN_FD 0 #define STDOUT_FD 1 #define MAX_RPC_SIZE 1024*1024 #define discard_const(ptr) ((void *)((uintptr_t)(ptr))) #define DEBUG(...) do { \ char msg[4096]; \ snprintf(msg, 4096, __VA_ARGS__); \ fprintf(stderr, "%s[%s:%d]: %s", argv[0], __FUNCTION__, __LINE__, msg); \ fflush(stderr); \ } while(0); int t_send_buffer(int fd, char *buf, uint32_t len); int t_recv_buffer(int fd, char *buf, uint32_t *len); void t_log_failure(gss_OID mech, uint32_t maj, uint32_t min); int t_string_to_name(const char *string, gss_name_t *name, gss_OID type); gssproxy-v0.8.2/tests/testlib.py0000755000174300017420000005645413456641744016421 0ustar gitgit00000000000000#!/usr/bin/python3 # Copyright (C) 2014,2015,2016 - GSS-Proxy contributors; see COPYING for the license. import binascii import glob import os import shutil import signal from string import Template import subprocess import sys import time testcase_wait = 15 cmd_index = 0 debug_all = False debug_gssproxy = False debug_cmd = "gdb --args" debug_cmd_index = -1 valgrind_cmd = "valgrind", "--track-origins=yes" valgrind_everywhere = False try: from colorama import Fore, Style def format_key(status, key): if status == "success": color = Fore.GREEN elif status == "failure": color = Fore.RED else: color = Style.DIM + Fore.YELLOW return "[" + color + key + Style.RESET_ALL + "]" except ImportError: def format_key(status, key): return "[" + key + "]" def testlib_process_args(args): global debug_all, debug_cmd, debug_cmd_index, debug_gssproxy global testcase_wait, valgrind_cmd, valgrind_everywhere testcase_wait = args['timeout'] debug_cmd_index = args['debug_num'] debug_all = args['debug_all'] debug_cmd = args['debug_cmd'] + " " debug_gssproxy = args['debug_gssproxy'] valgrind_cmd = args['valgrind_cmd'] + " " valgrind_everywhere = args['force_valgrind'] def print_keyed(status, key, text, io): print("%s %s" % (format_key(status, key), text), file=io) def print_success(key, text, io=sys.stderr): print_keyed("success", key, text, io) def print_failure(key, text, io=sys.stderr): print_keyed("failure", key, text, io) def print_warning(key, text, io=sys.stderr): print_keyed("other", key, text, io) def print_return(ret, num, name, expected_failure): key = "PASS" expected = "zero" if not expected_failure else "nonzero" if (ret == 0 and expected_failure) or \ (ret != 0 and not expected_failure): key = "FAIL" if (ret == 0 and not expected_failure) or \ (ret != 0 and expected_failure): print_success(key, "%s test returned %s" % (name, str(ret))) else: print_failure(key, "%s test returned %s (expected %s)" % (name, str(ret), expected)) if num != -1: print_warning("INFO", "To debug this test case, run:\n" + (" make check CHECKARGS='--debug-num=%d'" % num)) WRAP_HOSTNAME = "kdc.gssproxy.dev" def run_testcase_cmd(env, conf, cmd, name, expected_failure=False, wait=True): global testcase_wait, debug_cmd_index, cmd_index global valgrind_everywhere, valgrind_cmd, debug_all logfile = os.path.join(conf['logpath'], "test_%d.log" % cmd_index) logfile = open(logfile, 'w') print("[NAME]\n%s\n[COMMAND %d]\n%s\n[ENVIRONMENT]\n%s\n\n" % (name, cmd_index, cmd, env), file=logfile) logfile.flush() testenv = env.copy() if debug_all or debug_cmd_index == cmd_index: return rundebug_cmd(testenv, conf, cmd, name, expected_failure) run_cmd = cmd if valgrind_everywhere: run_cmd = valgrind_cmd + cmd p1 = subprocess.Popen(run_cmd, stderr=subprocess.STDOUT, stdout=logfile, env=testenv, preexec_fn=os.setsid, shell=True, executable="/bin/bash") if not wait: cmd_index += 1 conf['prefix'] = str(cmd_index) return p1 try: p1.wait(testcase_wait) except subprocess.TimeoutExpired: # p1.returncode is set to None here if not expected_failure: print_warning("warning", "timeout") logfile.close() print_return(p1.returncode, cmd_index, "(%d) %s" % (cmd_index, name), expected_failure) cmd_index += 1 conf['prefix'] = str(cmd_index) return p1.returncode if not expected_failure else int(not p1.returncode) def rundebug_cmd(env, conf, cmd, name, expected_failure=False): global debug_cmd, cmd_index run_cmd = debug_cmd + cmd returncode = subprocess.call(run_cmd, env=env, shell=True, executable="/bin/bash") print_return(returncode, cmd_index, "(%d) %s" % (cmd_index, name), expected_failure) cmd_index += 1 return returncode if not expected_failure else int(not returncode) def setup_wrappers(base): pkgcfg = subprocess.Popen(['pkg-config', '--exists', 'socket_wrapper']) pkgcfg.wait() if pkgcfg.returncode != 0: raise ValueError('Socket Wrappers not available') pkgcfg = subprocess.Popen(['pkg-config', '--exists', 'nss_wrapper']) pkgcfg.wait() if pkgcfg.returncode != 0: raise ValueError('Socket Wrappers not available') wrapdir = os.path.join(base, 'wrapdir') if not os.path.exists(wrapdir): os.makedirs(wrapdir) hosts_file = os.path.join(base, 'hosts') with open(hosts_file, 'w+') as f: f.write('127.0.0.9 %s' % WRAP_HOSTNAME) wenv = {'LD_PRELOAD': 'libsocket_wrapper.so libnss_wrapper.so', 'SOCKET_WRAPPER_DIR': wrapdir, 'SOCKET_WRAPPER_DEFAULT_IFACE': '9', 'NSS_WRAPPER_HOSTNAME': WRAP_HOSTNAME, 'NSS_WRAPPER_HOSTS': hosts_file} return wenv KRB5_CN = "Kerberos" KRB5_USER = "cn=root" LDAP_DC = "gssproxy" LDAP_REALM = "dc=" + LDAP_DC + ",dc=dev" LDAP_PW = "root" SLAPD_CONF_TEMPLATE = """ include ${LDAP_KRB_SCHEMA} include ${SCHEMADIR}/core.schema include ${SCHEMADIR}/cosine.schema include ${SCHEMADIR}/inetorgperson.schema include ${SCHEMADIR}/nis.schema allow bind_v2 pidfile ${LDAPDIR}/slapd.pid database config rootdn ${KRB5_USER},cn=config rootpw ${LDAP_PW} moduleload back_mdb database mdb suffix "${LDAP_REALM}" rootdn "${KRB5_USER},${LDAP_REALM}" rootpw ${LDAP_PW} directory ${LDAPDIR} logfile ${LDAP_LOG} """ KERBEROS_LDIF_TEMPLATE=""" dn: ${LDAP_REALM} objectClass: domain dc: ${LDAP_DC} dn: cn=${KRB5_CN},${LDAP_REALM} objectClass: krbContainer cn: ${KRB5_CN} """ TESTREALM = "GSSPROXY.DEV" KDC_DBNAME = 'db.file' KDC_STASH = 'stash.file' KDC_PASSWORD = 'gssproxy' KRB5_CONF_TEMPLATE = ''' [libdefaults] default_realm = ${TESTREALM} dns_lookup_realm = false dns_lookup_kdc = false rdns = false ticket_lifetime = 24h forwardable = yes default_ccache_name = FILE://${TESTDIR}/ccaches/krb5_ccache_XXXXXX [realms] ${TESTREALM} = { kdc = ${WRAP_HOSTNAME} admin_server = ${WRAP_HOSTNAME} } [domain_realm] .gssproxy.dev = GSSPROXY.DEV gssproxy.dev = GSSPROXY.DEV [dbmodules] ${TESTREALM} = { db_library = kldap ldap_kerberos_container_dn = cn=${KRB5_CN},${LDAP_REALM} ldap_kdc_dn = ${KRB5_USER},${LDAP_REALM} ldap_kadmind_dn = ${KRB5_USER},${LDAP_REALM} ldap_service_password_file = ${TESTDIR}/ldap_passwd ldap_servers = ldap://${WRAP_HOSTNAME} } ''' KDC_CONF_TEMPLATE = ''' [kdcdefaults] kdc_ports = 88 kdc_tcp_ports = 88 restrict_anonymous_to_tgt = true [realms] ${TESTREALM} = { master_key_type = aes256-cts max_life = 7d max_renewable_life = 14d acl_file = ${KDCDIR}/kadm5.acl dict_file = /usr/share/dict/words default_principal_flags = +preauth key_stash_file = ${KDCDIR}/${KDC_STASH} } [logging] kdc = FILE:${KDCLOG} ''' def write_ldap_krb5_config(testdir): # LDAP environment config files ldapdir = os.path.join(testdir, "ldap") ldapconf = os.path.join(ldapdir, "slapd.conf") ldif = os.path.join(ldapdir, "k5.ldif") testlog = os.path.join(testdir, "ldap.log") stashfile = os.path.join(testdir, "ldap_passwd") # Kerberos environment config files testlog = os.path.join(testdir, 'kkrb5kdc.log') krb5conf = os.path.join(testdir, 'krb5.conf') kdcconf = os.path.join(testdir, 'kdc.conf') kdcdir = os.path.join(testdir, 'kdc') kdcstash = os.path.join(kdcdir, KDC_STASH) kdcdb = os.path.join(kdcdir, KDC_DBNAME) # Create directories for config files if os.path.exists(ldapdir): shutil.rmtree(ldapdir) os.makedirs(ldapdir) if os.path.exists(kdcdir): shutil.rmtree(kdcdir) os.makedirs(kdcdir) # Template LDAP config files # Different distros do LDAP naming differently schemadir = None for path in ["/etc/openldap/schema", "/etc/ldap/schema"]: if os.path.exists(path): schemadir = path break if schemadir == None: raise ValueError("Did not find LDAP schemas; is openldap installed?") k5schema = None for path in ["/usr/share/doc/krb5-server-ldap*/kerberos.schema", "/usr/share/doc/krb5-kdc-ldap/kerberos.schema.gz"]: pathlist = glob.glob(path) if len(pathlist) > 0: k5schema = pathlist[0] break if k5schema == None: print("Please be sure krb5 ldap packages are installed") raise ValueError("No LDAP kerberos.schema found") elif k5schema.endswith(".gz"): sdata = subprocess.check_output(["zcat", k5schema]) k5schema = os.path.join(ldapdir, "kerberos.schema") with open(k5schema, "w") as f: f.write(sdata.decode("UTF-8")) t = Template(SLAPD_CONF_TEMPLATE) text = t.substitute({"LDAPDIR": ldapdir, "LDAP_REALM": LDAP_REALM, "LDAP_PW": LDAP_PW, "LDAP_LOG": testlog, "LDAP_KRB_SCHEMA": k5schema, "SCHEMADIR": schemadir, "KRB5_USER": KRB5_USER}) with open(ldapconf, "w+") as f: f.write(text) t = Template(KERBEROS_LDIF_TEMPLATE) text = t.substitute({"LDAP_REALM": LDAP_REALM, "LDAP_DC": LDAP_DC, "KRB5_CN": KRB5_CN}) with open(ldif, "w+") as f: f.write(text) # Template Kerberos config files t = Template(KRB5_CONF_TEMPLATE) text = t.substitute({'TESTREALM': TESTREALM, 'TESTDIR': testdir, 'KDCDIR': kdcdir, 'KRB5_CN': KRB5_CN, 'KRB5_USER': KRB5_USER, 'KDC_DBNAME': KDC_DBNAME, 'LDAP_REALM': LDAP_REALM, 'WRAP_HOSTNAME': WRAP_HOSTNAME}) with open(krb5conf, 'w+') as f: f.write(text) t = Template(KDC_CONF_TEMPLATE) text = t.substitute({'TESTREALM': TESTREALM, 'KDCDIR': kdcdir, 'KDCLOG': testlog, 'KDC_STASH': KDC_STASH}) with open(kdcconf, 'w+') as f: f.write(text) def setup_ldap(testdir, wrapenv): write_ldap_krb5_config(testdir) # Set LDAP environment paths ldapdir = os.path.join(testdir, "ldap") ldapconf = os.path.join(ldapdir, "slapd.conf") ldif = os.path.join(ldapdir, "k5.ldif") testlog = os.path.join(testdir, "ldap.log") stashfile = os.path.join(testdir, "ldap_passwd") krb5conf = os.path.join(testdir, 'krb5.conf') ldapenv = {'PATH': '/sbin:/bin:/usr/sbin:/usr/bin', 'KRB5_CONFIG': krb5conf} ldapenv.update(wrapenv) with open(testlog, "a") as logfile: lsetup = subprocess.Popen(["slapadd", "-f", ldapconf, "-l", ldif], stdout=logfile, stderr=logfile, env=ldapenv, preexec_fn=os.setsid) lsetup.wait() if lsetup.returncode != 0: raise ValueError("LDAP Setup failed") with open(testlog, "a") as logfile: ldapproc = subprocess.Popen(["slapd", "-d", "0", "-f", ldapconf, "-h", "ldap://%s" % WRAP_HOSTNAME], env=ldapenv, preexec_fn=os.setsid) print("Waiting for LDAP server to start...") time.sleep(5) with open(testlog, "a") as logfile: ssetup = subprocess.Popen(["kdb5_ldap_util", "stashsrvpw", "-w", LDAP_PW, "-H", "ldap://%s" % WRAP_HOSTNAME, "-f", stashfile, "%s,%s" % (KRB5_USER, LDAP_REALM)], stdin=subprocess.PIPE, stdout=logfile, stderr=logfile, env=ldapenv, preexec_fn=os.setsid) ssetup.communicate((LDAP_PW + '\n' + LDAP_PW + '\n').encode("UTF-8")) if ssetup.returncode != 0: os.killpg(ldapproc.pid, signal.SIGTERM) raise ValueError("stashsrvpw failed") return ldapproc, ldapenv def setup_kdc(testdir, wrapenv): # Set Kerberos environtment paths testlog = os.path.join(testdir, 'kkrb5kdc.log') krb5conf = os.path.join(testdir, 'krb5.conf') kdcconf = os.path.join(testdir, 'kdc.conf') kdcdir = os.path.join(testdir, 'kdc') kdcstash = os.path.join(kdcdir, KDC_STASH) kdcdb = os.path.join(kdcdir, KDC_DBNAME) kdcenv = {'PATH': '/sbin:/bin:/usr/sbin:/usr/bin', 'KRB5_CONFIG': krb5conf, 'KRB5_KDC_PROFILE': kdcconf} kdcenv.update(wrapenv) with (open(testlog, 'a')) as logfile: ksetup = subprocess.Popen(["kdb5_ldap_util", "-H", "ldap://%s" % WRAP_HOSTNAME, "-D", "%s,%s" % (KRB5_USER, LDAP_REALM), "create", "-w", LDAP_PW, "-P", KDC_PASSWORD, "-s", "-r", TESTREALM], stdout=logfile, stderr=logfile, env=kdcenv, preexec_fn=os.setsid) ksetup.wait() if ksetup.returncode != 0: raise ValueError('KDC Setup failed') kdcproc = subprocess.Popen(['krb5kdc', '-n'], env=kdcenv, preexec_fn=os.setsid) time.sleep(5) return kdcproc, kdcenv def kadmin_local(cmd, env, logfile): ksetup = subprocess.Popen(["kadmin.local", "-q", cmd], stdout=logfile, stderr=logfile, env=env, preexec_fn=os.setsid) ksetup.wait() if ksetup.returncode != 0: raise ValueError('Kadmin local [%s] failed' % cmd) USR_NAME = "user" USR_KTNAME = "user.gssproxy.keytab" USR_CCACHE = "krb5ccache_usr" SVC_KTNAME = "kdc.gssproxy.keytab" KEY_TYPE = "aes256-cts-hmac-sha1-96:normal" USR2_NAME = "user2" USR2_PWD = "usrpwd" MULTI_KTNAME = "multi.gssproxy.keytab" MULTI_UPN = "multi$" MULTI_SVC = "multi/%s" % WRAP_HOSTNAME HOST_SVC = "host/%s" % WRAP_HOSTNAME HOST_GSS = "host@%s" % WRAP_HOSTNAME PROXY_SVC = "proxy/%s" % WRAP_HOSTNAME PROXY_GSS = "proxy@%s" % WRAP_HOSTNAME PROXY_KTNAME = "proxy.keytab" PROXY_LDIF_TEMPLATE = """ dn: krbPrincipalName=${HOST_SVC}@${TESTREALM},cn=${TESTREALM},cn=${KRB5_CN},${LDAP_REALM} changetype: modify add: krbAllowedToDelegateTo krbAllowedToDelegateTo: ${PROXY_SVC}@${TESTREALM} - """ def authorize_to_proxy(testdir, env): testlog = os.path.join(testdir, 'kerbsetup.log') t = Template(PROXY_LDIF_TEMPLATE) text = t.substitute({"HOST_SVC": HOST_SVC, "PROXY_SVC": PROXY_SVC, "TESTREALM": TESTREALM, "LDAP_REALM": LDAP_REALM, "KRB5_CN": KRB5_CN}) ldif = os.path.join(testdir, "ldap", "k5proxy.ldif") with open(ldif, "w+") as f: f.write(text) with open(testlog, "a") as logfile: lmod = subprocess.Popen(["ldapmodify", "-w", LDAP_PW, "-H", "ldap://%s" % WRAP_HOSTNAME, "-D", "%s,%s" % (KRB5_USER, LDAP_REALM), "-f", ldif], stdout=logfile, stderr=logfile, env=env, preexec_fn=os.setsid) lmod.wait() if lmod.returncode != 0: raise ValueError("Proxy princ setup failed") def setup_keys(testdir, env): testlog = os.path.join(testdir, 'kerbsetup.log') svc_name = "host/%s" % WRAP_HOSTNAME svc_keytab = os.path.join(testdir, SVC_KTNAME) cmd = "addprinc -randkey -e %s +ok_to_auth_as_delegate %s" % (KEY_TYPE, svc_name) with (open(testlog, 'a')) as logfile: kadmin_local(cmd, env, logfile) cmd = "ktadd -k %s -e %s %s" % (svc_keytab, KEY_TYPE, svc_name) with (open(testlog, 'a')) as logfile: kadmin_local(cmd, env, logfile) usr_keytab = os.path.join(testdir, USR_KTNAME) cmd = "addprinc -randkey -e %s %s" % (KEY_TYPE, USR_NAME) with (open(testlog, 'a')) as logfile: kadmin_local(cmd, env, logfile) cmd = "ktadd -k %s -e %s %s" % (usr_keytab, KEY_TYPE, USR_NAME) with (open(testlog, 'a')) as logfile: kadmin_local(cmd, env, logfile) cmd = "addprinc -pw %s %s" % (USR2_PWD, USR2_NAME) with (open(testlog, 'a')) as logfile: kadmin_local(cmd, env, logfile) proxy_keytab = os.path.join(testdir, PROXY_KTNAME) cmd = "addprinc -randkey -e %s -requires_preauth %s" % (KEY_TYPE, PROXY_SVC) with (open(testlog, 'a')) as logfile: kadmin_local(cmd, env, logfile) shutil.copy(svc_keytab, proxy_keytab) cmd = "ktadd -k %s -e %s %s" % (proxy_keytab, KEY_TYPE, PROXY_SVC) with (open(testlog, 'a')) as logfile: kadmin_local(cmd, env, logfile) authorize_to_proxy(testdir, env) keys_env = {"client_keytab": usr_keytab, "KRB5_KTNAME": svc_keytab} keys_env.update(env) return keys_env def setup_multi_keys(testdir, env): testlog = os.path.join(testdir, 'kerbsetup.log') keytab = os.path.join(testdir, MULTI_KTNAME) cmd = "addprinc -randkey -e %s %s" % (KEY_TYPE, MULTI_SVC) with (open(testlog, 'a')) as logfile: kadmin_local(cmd, env, logfile) cmd = "ktadd -k %s -e %s %s" % (keytab, KEY_TYPE, MULTI_SVC) with (open(testlog, 'a')) as logfile: kadmin_local(cmd, env, logfile) # add a second key using the UPN cmd = "addprinc -randkey -e %s %s" % (KEY_TYPE, MULTI_UPN) with (open(testlog, 'a')) as logfile: kadmin_local(cmd, env, logfile) cmd = "ktadd -k %s -e %s %s" % (keytab, KEY_TYPE, MULTI_UPN) with (open(testlog, 'a')) as logfile: kadmin_local(cmd, env, logfile) # This is relative to the path where the test binary is being run GSSAPI_SYMLINK_DIR = ".test655" MECH_CONF_TEMPLATE = ''' gssproxy_v1 2.16.840.1.113730.3.8.15.1 ${PROXYMECH} ''' def setup_gssapi_env(testdir, wrapenv): libgssapi_dir = os.path.join(testdir, 'libgssapi') libgssapi_mechd_dir = os.path.join(GSSAPI_SYMLINK_DIR, 'mech.d') if os.path.exists(libgssapi_dir): shutil.rmtree(libgssapi_dir) os.makedirs(libgssapi_dir) if os.path.lexists(GSSAPI_SYMLINK_DIR): os.unlink(GSSAPI_SYMLINK_DIR) os.symlink(libgssapi_dir, GSSAPI_SYMLINK_DIR) os.makedirs(libgssapi_mechd_dir) lib = None try: libs = subprocess.check_output( ['pkg-config', '--libs-only-L', 'krb5-gssapi']).decode("utf-8") except: raise ValueError('libgssapi not available') # find them all and get the longest name in the hopes # we hit /usr/lib64/libgssapi_krb5.so.2.2 in preference if libs is not None and libs.startswith("-L"): libs = glob.glob(libs[2:].strip() + "/libgssapi*.so*") else: libs = glob.glob("/usr/lib*/libgssapi*.so*") lib_len = 0 for l in libs: if len(l) > lib_len: lib_len = len(l) lib = l if not lib: raise KeyError('Gssapi library not found') libgssapi_lib = os.path.join(libgssapi_dir, os.path.basename(lib)) libgssapi_conf = os.path.join(libgssapi_mechd_dir, 'gssproxy-mech.conf') # horrible, horrible hack to load our own configuration later with open(lib, 'rb') as f: data = binascii.hexlify(f.read()) with open(libgssapi_lib, 'wb') as f: data = data.replace(binascii.hexlify(b'/etc/gss/mech.d'), binascii.hexlify( libgssapi_mechd_dir.encode("utf-8"))) f.write(binascii.unhexlify(data)) shutil.copy('.libs/proxymech.so', libgssapi_dir) proxymech = os.path.join(libgssapi_dir, 'proxymech.so') t = Template(MECH_CONF_TEMPLATE) text = t.substitute({'PROXYMECH': proxymech}) with open(libgssapi_conf, 'w+') as f: f.write(text) # first swallow in wrapenv vars if any gssapi_env = dict() gssapi_env.update(wrapenv) # then augment preload if any ld_pre = '' if 'LD_PRELOAD' in wrapenv: ld_pre = wrapenv['LD_PRELOAD'] + ' ' ld_pre = ld_pre + os.path.join(GSSAPI_SYMLINK_DIR, os.path.basename(libgssapi_lib)) gssapi_env['LD_PRELOAD'] = ld_pre return gssapi_env GSSPROXY_CONF_TEMPLATE = ''' [gssproxy] debug_level = 3 [service/test] mechs = krb5 cred_store = keytab:${GSSPROXY_KEYTAB} cred_store = client_keytab:${GSSPROXY_CLIENT_KEYTAB} trusted = yes euid = ${UIDNUMBER} allow_client_ccache_sync = yes [service/badkeytab] mechs = krb5 cred_store = keytab:/intentionally/missing/keytab euid = 123 ''' # Contains a garbage service entry GSSPROXY_CONF_MINIMAL_TEMPLATE = ''' [gssproxy] debug_level = 3 [service/dontuse] mechs = krb5 cred_store = keytab:${GSSPROXY_KEYTAB} cred_store = client_keytab:${GSSPROXY_CLIENT_KEYTAB} trusted = yes euid = nobody allow_client_ccache_sync = yes ''' GSSPROXY_CONF_SOCKET_TEMPLATE = ''' [gssproxy] debug_level = 3 [service/test] mechs = krb5 cred_store = keytab:${GSSPROXY_KEYTAB} cred_store = client_keytab:${GSSPROXY_CLIENT_KEYTAB} trusted = yes euid = ${UIDNUMBER} socket = ${SECOND_SOCKET} allow_client_ccache_sync = yes ''' GSSPROXY_MULTI_TEMPLATE = ''' [gssproxy] debug_level = 2 [service/test] mechs = krb5 cred_store = keytab:${GSSPROXY_KEYTAB} krb5_principal = ${GSSPROXY_CLIENT_PRINCIPAL} trusted = yes euid = ${UIDNUMBER} allow_client_ccache_sync = yes ''' def update_gssproxy_conf(testdir, env, template): gssproxy = os.path.join(testdir, 'gssproxy') ccache = os.path.join(gssproxy, 'gpccache') ckeytab = env['client_keytab'] conf = os.path.join(gssproxy, 'gp.conf') socket2 = os.path.join(gssproxy, 'gp.sock2') t = Template(template) subs = {'GSSPROXY_KEYTAB': env['KRB5_KTNAME'], 'GSSPROXY_CLIENT_CCACHE': ccache, 'GSSPROXY_CLIENT_KEYTAB': ckeytab, 'UIDNUMBER': os.getuid(), 'SECOND_SOCKET': socket2, 'PROGDIR': os.path.join(os.getcwd(), "tests"), 'TESTDIR': testdir} if 'client_name' in env: subs['GSSPROXY_CLIENT_PRINCIPAL'] = env['client_name'] text = t.substitute(subs) with open(conf, 'w+') as f: f.write(text) def setup_gssproxy(testdir, logfile, env): global debug_gssproxy, valgrind_cmd gssproxy = os.path.join(testdir, 'gssproxy') if os.path.exists(gssproxy): shutil.rmtree(gssproxy) os.makedirs(gssproxy) update_gssproxy_conf(testdir, env, GSSPROXY_CONF_TEMPLATE) gpenv = env.copy() gpenv['KRB5_TRACE'] = os.path.join(testdir, 'gp_krb5_trace.log') socket = os.path.join(gssproxy, 'gp.sock') conf = os.path.join(gssproxy, 'gp.conf') cmd = "./gssproxy -i -s " + socket + " -c " + conf full_command = valgrind_cmd + cmd if debug_gssproxy: full_command = cmd gproc = subprocess.Popen(full_command, stdout=logfile, stderr=logfile, env=gpenv, preexec_fn=os.setsid, shell=True, executable="/bin/bash") if debug_gssproxy: print("PID: %d" % (gproc.pid)) print("Attach and start debugging, then press enter to continue.") input() return gproc, socket gssproxy-v0.8.2/version.m40000644000174300017420000000033013456641744015141 0ustar gitgit00000000000000# Primary version number m4_define([VERSION_NUMBER], [0.8.2]) # If the PRERELEASE_VERSION_NUMBER is set, we'll append # it to the release tag when creating an RPM or SRPM m4_define([PRERELEASE_VERSION_NUMBER], []) gssproxy-v0.8.2/x-files/0000755000174300017420000000000013456641744014565 5ustar gitgit00000000000000gssproxy-v0.8.2/x-files/gp_rpc.x0000644000174300017420000001001213456641744016222 0ustar gitgit00000000000000/* * ONC RPC request/reply header XDR. * * Note that this XDR is extracted from RFC1831 and massaged so that * rpcgen(1) accepts it, but it is intended to be wire-compatible with * RFC1831. * * Also, in order to avoid symbol naming conflicts, we prefix "gp_" and * "GP_" to all symbols from RFC1831. "GP" stands for "GSS proxy". */ enum gp_rpc_auth_flavor { GP_RPC_AUTH_NONE = 0, GP_RPC_AUTH_SYS = 1, GP_RPC_AUTH_SHORT = 2, GP_RPC_AUTH_DH = 3, GP_RPC_RPCSEC_GSS = 6 /* and more to be defined */ }; struct gp_rpc_opaque_auth { gp_rpc_auth_flavor flavor; opaque body<400>; }; enum gp_rpc_msg_type { GP_RPC_CALL = 0, GP_RPC_REPLY = 1 }; enum gp_rpc_reply_status { GP_RPC_MSG_ACCEPTED = 0, GP_RPC_MSG_DENIED = 1 }; enum gp_rpc_accept_status { GP_RPC_SUCCESS = 0, /* RPC executed successfully */ GP_RPC_PROG_UNAVAIL = 1, /* remote hasn't exported program */ GP_RPC_PROG_MISMATCH = 2, /* remote can't support version # */ GP_RPC_PROC_UNAVAIL = 3, /* program can't support procedure */ GP_RPC_GARBAGE_ARGS = 4, /* procedure can't decode params */ GP_RPC_SYSTEM_ERR = 5 /* e.g. memory allocation failure */ }; enum gp_rpc_reject_status { GP_RPC_RPC_MISMATCH = 0, /* RPC version number != 2 */ GP_RPC_AUTH_ERROR = 1 /* remote can't authenticate caller */ }; enum gp_rpc_auth_status { GP_RPC_AUTH_OK = 0, /* success */ /* * failed at remote end */ GP_RPC_AUTH_BADCRED = 1, /* bad credential (seal broken) */ GP_RPC_AUTH_REJECTEDCRED = 2, /* client must begin new session */ GP_RPC_AUTH_BADVERF = 3, /* bad verifier (seal broken) */ GP_RPC_AUTH_REJECTEDVERF = 4, /* verifier expired or replayed */ GP_RPC_AUTH_TOOWEAK = 5, /* rejected for security reasons */ /* * failed locally */ GP_RPC_AUTH_INVALIDRESP = 6, /* bogus response verifier */ GP_RPC_AUTH_FAILED = 7, /* reason unknown */ /* * AUTH_KERB errors; deprecated. See [RFC2695] */ GP_RPC_AUTH_KERB_GENERIC = 8, /* kerberos generic error */ GP_RPC_AUTH_TIMEEXPIRE = 9, /* time of credential expired */ GP_RPC_AUTH_TKT_FILE = 10, /* problem with ticket file */ GP_RPC_AUTH_DECODE = 11, /* can't decode authenticator */ GP_RPC_AUTH_NET_ADDR = 12, /* wrong net address in ticket */ /* * RPCSEC_GSS GSS related errors */ GP_RPC_RPCSEC_GSS_CREDPROBLEM = 13, /* no credentials for user */ GP_RPC_RPCSEC_GSS_CTXPROBLEM = 14 /* problem with context */ }; struct gp_rpc_mismatch_info { unsigned int low; unsigned int high; }; union gp_rpc_reply_union switch (gp_rpc_accept_status status) { case GP_RPC_SUCCESS: opaque results[0]; /* * procedure-specific results start here */ case GP_RPC_PROG_MISMATCH: gp_rpc_mismatch_info mismatch_info; default: /* * Void. Cases include PROG_UNAVAIL, PROC_UNAVAIL, * GARBAGE_ARGS, and SYSTEM_ERR. */ void; }; struct gp_rpc_accepted_reply { gp_rpc_opaque_auth verf; gp_rpc_reply_union reply_data; }; union gp_rpc_rejected_reply switch (gp_rpc_reject_status status) { case GP_RPC_RPC_MISMATCH: gp_rpc_mismatch_info mismatch_info; case GP_RPC_AUTH_ERROR: gp_rpc_auth_status status; }; struct gp_rpc_call_header { unsigned int rpcvers; /* must be equal to two (2) */ unsigned int prog; unsigned int vers; unsigned int proc; gp_rpc_opaque_auth cred; gp_rpc_opaque_auth verf; /* procedure-specific parameters start here */ }; union gp_rpc_reply_header switch (gp_rpc_reply_status status) { case GP_RPC_MSG_ACCEPTED: gp_rpc_accepted_reply accepted; case GP_RPC_MSG_DENIED: gp_rpc_rejected_reply rejected; }; union gp_rpc_msg_union switch (gp_rpc_msg_type type) { case GP_RPC_CALL: gp_rpc_call_header chdr; case GP_RPC_REPLY: gp_rpc_reply_header rhdr; }; struct gp_rpc_msg { unsigned int xid; gp_rpc_msg_union header; }; gssproxy-v0.8.2/x-files/gss_proxy.x0000644000174300017420000005664113456641744017027 0ustar gitgit00000000000000/* * Copyright (c) 2011, 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. * */ /* * README First! * * This is an initial attempt at creating an XDR representation of the * GSS-API for the implementation of a GSS proxy client/server protocol, * both over local IPC (for NFS and various other applications) and * remote (for ssh-agent-like functionality). * * This is a work-in-progress. However, rpcgen(1) on Ubuntu does * compile this file. * * Because the GSS-API is based on "functions" and XDR is the basis for * ONC RPC (which is based on "procedures") we use "_arg_" and "_res_" * affixes to name structures, and we use those structures to encode * function arguments and results, respectively. * * We unify functions as much as possible into as few RPCs as possible. * For example, we unify GSS_Import/Canonicalize/Display_name(). We * also unify GSS_Acquire/Add_cred() and the credentials handle inquiry * functions. This way we reduce the number of round-trips needed to * use the GSS proxy protocol effectively. * * Similarly, GSS_Init/Accept_sec_context() return all the information * about a context that the app could want, including an exported * security context token (so the app can import it). * * All general meta-data functions, such as GSS_Indicate_mechs() and * GSS_Inquire_attrs_for_mech(), are unified as well. * * We support stateful and stateless proxy server implementations both. * Stateless servers will need to store various internal state on the * client side, in the form of serialized credential handle references * (e.g., ccache names) and exported security context tokens even for * partially established security contexts. Stateless servers will * generally want to MAC state stored on the client side. * * We use gssx_ as a prefix to avoid colliding with the C bindings. * * We use the XDR '*' operator to denote "optional" fields in structs. * But for optional gss_OID and gss_OID_set arguments, and only those * types of arguments, we use empty OID/OID set to denote "not present" * (presently no GSS functions have any special semantics for empty * OIDs/OID sets; we can use '*' in the future if any new functions are * added with such semantics). * * Most/all RPC arguments/results have typed holes for extensibility. * We call these "options" where existing GSS extension APIs have such a * concept or where we think we might have such extension APIs soon, * else we call them "extensions" and think of them more as ASN.1 * extensibility markers. * * For functions like GSS_Set_name_attribute(), GSS_Set_cred_option(), * and GSS_Set_sec_ctx_option(), the way these are intended to be * implemented with this GSS proxy protocol is as follows: * * - For name attributes the client must call the IMPORT_AND_CANON_NAME * RPC once again for each additional name attribute. The input_name * argument be the same as returned by the previous call to the same * RPC. * * This means that an RPC (round trip) is needed for each name * attribute to be set. This is a result of the semantics of * GSS_Set_name_attribute() and cannot be avoided. * * - For credential handle options the client can call ACQUIRE_CRED * with cred_options. If called with an existing credential handle * and no new elements are needed then no elements will be added to * the output credential, but the desired cred_options should be set * in the new credential (and will be visible). This allows a * default credential handle to be acquired with cred_options in just * one round-trip for the first option. Each additional cred_option * requires an additional round-trip with today's * GSS_Set_cred_option(). * * Note that supported cred_options are indicated on a per-mechanism * basis by the INDICATE_MECHS RPC. * * - For security context options the options must be passed in on the * initial call to INIT/ACCEPT_SEC_CONTEXT (and may be repeated on * the remaining calls for a security context, but only the first * will matter). * * Note that supported context_options are indicated on a * per-mechanism basis by the INDICATE_MECHS RPC. */ /* Generic base types */ typedef opaque utf8string<>; typedef opaque octet_string<>; /* GSS base types */ typedef unsigned hyper gssx_uint64; /* 64-bit for future proofing */ typedef unsigned hyper gssx_qop; typedef octet_string gssx_buffer; /* empty -> empty, !missing */ typedef octet_string gssx_OID; /* empty -> GSS_C_NO_OID */ typedef gssx_OID gssx_OID_set<>; /* empty -> GSS_C_NO_OID_SET */ enum gssx_cred_usage {GSSX_C_INITIATE = 1, GSSX_C_ACCEPT = 2, GSSX_C_BOTH = 3}; typedef unsigned hyper gssx_time; /* seconds since Unix epoch */ /* * Major status codes will be per-RFC2744, cast to gssx_uint64. * * XXX Should #include the RFC2744 headers here? */ /* Extensions */ struct gssx_option { gssx_buffer option; /* a URN, possibly a stringified OID */ gssx_buffer value; /* a string with format give by option */ }; /* Mechanism attributes */ struct gssx_mech_attr { gssx_OID attr; gssx_buffer name; gssx_buffer short_desc; gssx_buffer long_desc; gssx_option extensions<>; }; /* Mechanism meta-data */ struct gssx_mech_info { gssx_OID mech; gssx_OID_set name_types; gssx_OID_set mech_attrs; gssx_OID_set known_mech_attrs; gssx_OID_set cred_options; gssx_OID_set sec_ctx_options; gssx_buffer saslname_sasl_mech_name; gssx_buffer saslname_mech_name; gssx_buffer saslname_mech_desc; gssx_option extensions<>; }; /* Name attributes are {attribute name, attribute value} */ struct gssx_name_attr { gssx_buffer attr; gssx_buffer value; gssx_option extensions<>; }; /* * We avoid round-trips for GSS_Display_status() by always sending * displayed status messages. These are intended to be localized to the * locale specified by the client (see below). * * Note that the minor_status is not really meaningful unless the * mechanism specifies specific minor_status numeric values, which no * mechanism does! The server repeats the mechanism OID here for * convenience, so the client can have a single structure that contains * the mechanism OID and minor_status value for whatever purpose the * client might put them to. * * The server_ctx value is opaque and intended for the client to replace * its' caller context's server_ctx value with. */ struct gssx_status { gssx_uint64 major_status; gssx_OID mech; gssx_uint64 minor_status; utf8string major_status_string; utf8string minor_status_string; octet_string server_ctx; gssx_option options<>; }; /* * Caller context. * * Caller contexts are objects that are created by the caller. But the * server may return some octet string (in gssx_status; see above) that * the client must use in its call context in the future. * * This is useful to help the proxy server find user credentials, for * example. And for conveying locale information for status display * string localization. It could be used in the future for other * extensions. It could be used for gss_set_context_option() for some * context options, for example. * * A credential store is always implied in the GSS-API, but for a proxy * GSS protocol we may need an *option* to make the credential store * explicit. If we do need that option we'll use the extensions field * for it. */ struct gssx_call_ctx { utf8string locale; /* for status display string L10N */ octet_string server_ctx; /* server-assigned (see above) */ gssx_option options<>; }; /* * For NAMEs we don't use a plain opaque handle representation. * * Our aim is to be able to implement GSS_Import_name() and * GSS_Display_name() without talking to the proxy server (e.g., when * the name type is not an exported name type), and to unify those and * GSS_Canonicalize_name() and GSS_Get/Set_name_attribute() into one * RPC. * * We support multi-MNs by having arrays of exported name tokens, rather * than just one, just in case we end up with multi-MN extensions. */ struct gssx_name { /* Non-MNs MUST have these; MNs MAY have these */ gssx_buffer display_name; gssx_OID name_type; /* MNs MUST have at least one exported name form */ gssx_buffer exported_name; gssx_buffer exported_composite_name; /* Name attributes */ gssx_name_attr name_attributes<>; /* Future extensions */ gssx_option extensions<>; }; /* * CREDENTIAL HANDLEs are really just a description plus whatever state * reference or encoded (and protected) state the server needs. * * Of course, the way CREDENTIAL HANDLEs work in the GSS-API they are * actually sets of elements, all of which are supposed to be for the * same desired_name but different mechanism OIDs. In practice the * desired_names for each element will be MNs, thus all different, but * we might have the original non-MN desired_name, and that is useful to * keep for GSS_Inquire_cred(). * * First we have credential elements: */ struct gssx_cred_element { /* GSS_Inquire_cred_by_mech() outputs */ gssx_name MN; gssx_OID mech; gssx_cred_usage cred_usage; gssx_time initiator_time_rec; gssx_time acceptor_time_rec; gssx_option options<>; }; /* * Then we have the actual credential handle: */ struct gssx_cred { gssx_name desired_name; /* possibly not an MN */ gssx_cred_element elements<>; /* * Server-side state reference or encoded state; may or may not * require releasing. This may be just a ccache name, or an encoded * list of URI-like strings, for example, or it might be an exported * credential, possibly encrypted and/or MACed with a server secret * key. * * Stateful servers MUST be able to clean up unreferenced state * automatically, using an LRU/LFU type cache. However, stateful * servers SHOULD (or at least MAY) indicate statefulness so that * the client can release server-side state sooner than the server * might otherwise do it. */ octet_string cred_handle_reference; bool needs_release; }; /* * Security CONTEXT HANDLEs consist of a description of the security * context and an exported security context token or (if the server * can't export partially established security contexts) a server-side * state reference. */ struct gssx_ctx { /* The exported context token, if available */ gssx_buffer exported_context_token; octet_string state; /* * Stateful servers MUST be able to clean up unreferenced state * automatically, using an LRU/LFU type cache. However, stateful * servers SHOULD (or at least MAY) indicate statefulness so that * the client can release server-side state sooner than the server * might otherwise do it. */ bool needs_release; /* GSS_Inquire_context() outputs */ gssx_OID mech; gssx_name src_name; gssx_name targ_name; gssx_time lifetime; gssx_uint64 ctx_flags; bool locally_initiated; bool open; gssx_option options<>; }; /* * We have a union type for CREDENTIAL and security CONTEXT HANDLEs so * that we can have a unified handle release RPC (which is needed only * when the server is stateful). */ enum gssx_handle_type { GSSX_C_HANDLE_SEC_CTX = 0, GSSX_C_HANDLE_CRED = 1 }; union gssx_handle switch (gssx_handle_type handle_type) { case GSSX_C_HANDLE_CRED: gssx_cred cred_info; case GSSX_C_HANDLE_SEC_CTX: gssx_ctx sec_ctx_info; default: octet_string extensions; /* Future handle types */ }; /* * We should probably come up with a standard RFC4121 context export * token structure here. We only need, basically, the session keys and * initial token sequence numbers (plus, for clients that want to proxy * per-msg token functions to stateless servers, we'd need a sequence * number window structure). Things like authz-data can be placed in * the gssx_name's exported_composite_name or extensions fields, in the * handle_info. */ /* Channel bindings */ struct gssx_cb { /* * Address type CB is deprecated; use only application_data. * See RFCs 5056 and 5554. */ gssx_uint64 initiator_addrtype; /* deprecated */ gssx_buffer initiator_address; /* deprecated */ gssx_uint64 acceptor_addrtype; /* deprecated */ gssx_buffer acceptor_address; /* deprecated */ gssx_buffer application_data; /* * There's no extensibility here, and there must not be. All CB * extensibility in the GSS-API now is a matter of * application_data formatting conventions. */ }; typedef struct gssx_cb gssx_cb; /* One RPC for all handle release functions */ struct gssx_arg_release_handle { gssx_call_ctx call_ctx; gssx_handle cred_handle; }; struct gssx_res_release_handle { gssx_status status; }; /* Various mechanism inquiry functions, all unified into one RPC */ struct gssx_arg_indicate_mechs { gssx_call_ctx call_ctx; }; struct gssx_res_indicate_mechs { gssx_status status; gssx_mech_info mechs<>; gssx_mech_attr mech_attr_descs<>; gssx_buffer supported_extensions<>; /* and options */ gssx_option extensions<>; }; /* We unify GSS_Import/Canonicalize_name() and GSS_Get/Set_name_attribute() */ struct gssx_arg_import_and_canon_name { gssx_call_ctx call_ctx; gssx_name input_name; gssx_OID mech; gssx_name_attr name_attributes<>; gssx_option options<>; }; struct gssx_res_import_and_canon_name { gssx_status status; gssx_name *output_name; gssx_option options<>; }; /* We probably don't need this RPC */ struct gssx_arg_get_call_context { gssx_call_ctx call_ctx; gssx_option options<>; }; struct gssx_res_get_call_context { gssx_status status; octet_string server_call_ctx; /* server-assigned (see above) */ gssx_option options<>; }; /* We unify GSS_Acquire/Add_cred() here */ struct gssx_arg_acquire_cred { gssx_call_ctx call_ctx; gssx_cred *input_cred_handle; bool add_cred_to_input_handle; gssx_name *desired_name; /* absent -> GSS_C_NO_NAME */ gssx_time time_req; gssx_OID_set desired_mechs; /* no need to dist. empty vs. absent */ gssx_cred_usage cred_usage; gssx_time initiator_time_req; gssx_time acceptor_time_req; gssx_option options<>; }; struct gssx_res_acquire_cred { gssx_status status; gssx_cred *output_cred_handle; /* includes info */ gssx_option options<>; }; /* GSS_Export/Import_cred() are not unified */ struct gssx_arg_export_cred { gssx_call_ctx call_ctx; gssx_cred input_cred_handle; gssx_cred_usage cred_usage; gssx_option options<>; }; struct gssx_res_export_cred { gssx_status status; gssx_cred_usage usage_exported; octet_string *exported_handle; /* exported credential token */ gssx_option options<>; }; struct gssx_arg_import_cred { gssx_call_ctx call_ctx; octet_string exported_handle; /* exported credential token */ gssx_option options<>; }; struct gssx_res_import_cred { gssx_status status; gssx_cred *output_cred_handle; /* includes info */ gssx_option options<>; }; /* GSS_Store_cred() */ struct gssx_arg_store_cred { gssx_call_ctx call_ctx; gssx_cred input_cred_handle; gssx_cred_usage cred_usage; gssx_OID desired_mech; bool overwrite_cred; bool default_cred; gssx_option options<>; }; struct gssx_res_store_cred { gssx_status status; gssx_OID_set elements_stored; gssx_cred_usage cred_usage_stored; gssx_option options<>; }; /* * Security context functions * * We don't need GSS_Inquire_context(), nor GSS_Import/ * Export_sec_context(). These are all subsumed into * GSS_Init/Accept_sec_context() in this protocol. */ struct gssx_arg_init_sec_context { gssx_call_ctx call_ctx; gssx_ctx *context_handle; gssx_cred *cred_handle; /* absent -> GSS_C_NO_CREDENTIAL */ gssx_name *target_name; /* absent -> GSS_C_NO_NAME */ gssx_OID mech_type; gssx_uint64 req_flags; gssx_time time_req; gssx_cb *input_cb; /* input channel bindings */ gssx_buffer *input_token; gssx_option options<>; }; struct gssx_res_init_sec_context { gssx_status status; gssx_ctx *context_handle; /* includes info outputs */ gssx_buffer *output_token; gssx_option options<>; }; struct gssx_arg_accept_sec_context { gssx_call_ctx call_ctx; gssx_ctx *context_handle; gssx_cred *cred_handle; /* absent -> GSS_C_NO_CREDENTIAL */ gssx_buffer input_token; gssx_cb *input_cb; /* input channel bindings */ bool ret_deleg_cred; /* return delegated credentials */ gssx_option options<>; }; struct gssx_res_accept_sec_context { gssx_status status; gssx_ctx *context_handle; /* includes info outputs */ gssx_buffer *output_token; gssx_cred *delegated_cred_handle; gssx_option options<>; }; /* * We provide per-message token functions for testing and bootstrap * purposes: a client might not have a provider for a given mechanism, * in which case the proxy can provide per-message token functions to * the client. This is primarily useful for testing that the * client-side provider and the server-side provider have interoperable * per-message token functions, which can be especially important for * kernel-mode client use cases. (I.e., setup an NFS client without a * kernel-mode GSS mechanism provider and test it against an NFS server * that does have a kernel-mode GSS mechanism provider, and vice-versa.) * * The results of these functions have an optional context_handle output * so that stateless servers can store sequence number windows in the * returned handle. * * Server support for this is optional. Clients should really not need * this for any purpose other than testing. */ struct gssx_arg_get_mic { gssx_call_ctx call_ctx; gssx_ctx context_handle; gssx_qop qop_req; gssx_buffer message_buffer; }; struct gssx_res_get_mic { gssx_status status; gssx_ctx *context_handle; gssx_buffer token_buffer; /* empty on error */ gssx_qop *qop_state; }; struct gssx_arg_verify_mic { gssx_call_ctx call_ctx; gssx_ctx context_handle; gssx_buffer message_buffer; gssx_buffer token_buffer; }; struct gssx_res_verify_mic { gssx_status status; gssx_ctx *context_handle; gssx_qop *qop_state; }; /* * We use gssx_buffer<> to make implementation of iov variants slightly * easier. */ struct gssx_arg_wrap { gssx_call_ctx call_ctx; gssx_ctx context_handle; bool conf_req; gssx_buffer message_buffer<>; gssx_qop qop_state; }; struct gssx_res_wrap { gssx_status status; gssx_ctx *context_handle; gssx_buffer token_buffer<>; bool *conf_state; gssx_qop *qop_state; }; struct gssx_arg_unwrap { gssx_call_ctx call_ctx; gssx_ctx context_handle; gssx_buffer token_buffer<>; gssx_qop qop_state; }; struct gssx_res_unwrap { gssx_status status; gssx_ctx *context_handle; gssx_buffer message_buffer<>; bool *conf_state; gssx_qop *qop_state; }; struct gssx_arg_wrap_size_limit { gssx_call_ctx call_ctx; gssx_ctx context_handle; bool conf_req; gssx_qop qop_state; gssx_uint64 req_output_size; }; struct gssx_res_wrap_size_limit { gssx_status status; gssx_uint64 max_input_size; }; program GSSPROXY { version GSSPROXYVERS { /* rpcgen knows to automatically generate a NULLPROC */ gssx_res_indicate_mechs GSSX_INDICATE_MECHS(gssx_arg_indicate_mechs) = 1; gssx_res_get_call_context GSSX_GET_CALL_CONTEXT(gssx_arg_get_call_context) = 2; gssx_res_import_and_canon_name GSSX_IMPORT_AND_CANON_NAME(gssx_arg_import_and_canon_name) = 3; gssx_res_export_cred GSSX_EXPORT_CRED(gssx_arg_export_cred) = 4; gssx_res_import_cred GSSX_IMPORT_CRED(gssx_arg_import_cred) = 5; gssx_res_acquire_cred GSSX_ACQUIRE_CRED(gssx_arg_acquire_cred) = 6; gssx_res_store_cred GSSX_STORE_CRED(gssx_arg_store_cred) = 7; gssx_res_init_sec_context GSSX_INIT_SEC_CONTEXT(gssx_arg_init_sec_context) = 8; gssx_res_accept_sec_context GSSX_ACCEPT_SEC_CONTEXT(gssx_arg_accept_sec_context) = 9; gssx_res_release_handle GSSX_RELEASE_HANDLE(gssx_arg_release_handle) = 10; gssx_res_get_mic GSSX_GET_MIC(gssx_arg_get_mic) = 11; gssx_res_verify_mic GSSX_VERIFY(gssx_arg_verify_mic) = 12; gssx_res_wrap GSSX_WRAP(gssx_arg_wrap) = 13; gssx_res_unwrap GSSX_UNWRAP(gssx_arg_unwrap) = 14; gssx_res_wrap_size_limit GSSX_WRAP_SIZE_LIMIT(gssx_arg_wrap_size_limit) = 15; } = 1; } = 400112; /* gss-proxy: http://www.iana.org/assignments/rpc-program-numbers */