summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am8
-rw-r--r--NEWS8
-rw-r--r--README7
-rw-r--r--client/api.c41
-rw-r--r--client/error.c20
-rw-r--r--client/internal.h14
-rw-r--r--client/open.c55
-rw-r--r--configure.ac11
-rw-r--r--docs/api/remctl_set_ccache.pod28
-rw-r--r--m4/krb5.m4336
-rw-r--r--perl/lib/Net/Remctl.pm.in10
-rw-r--r--php/README13
-rw-r--r--portable/krb5-extra.c118
-rw-r--r--portable/krb5.h90
-rw-r--r--python/README13
-rw-r--r--ruby/README15
-rw-r--r--ruby/remctl.c4
-rw-r--r--tests/client/ccache-t.c25
18 files changed, 756 insertions, 60 deletions
diff --git a/Makefile.am b/Makefile.am
index 641dcb7..1753d36 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -108,15 +108,15 @@ lib_LTLIBRARIES = client/libremctl.la
client_libremctl_la_SOURCES = client/api.c client/client-v1.c \
client/client-v2.c client/error.c client/internal.h client/open.c
client_libremctl_la_LDFLAGS = -version-info 2:0:1 $(VERSION_LDFLAGS) \
- $(GSSAPI_LDFLAGS)
-client_libremctl_la_LIBADD = util/libutil.la $(GSSAPI_LIBS)
+ $(GSSAPI_LDFLAGS) $(KRB5_LDFLAGS)
+client_libremctl_la_LIBADD = util/libutil.la $(GSSAPI_LIBS) $(KRB5_LIBS)
include_HEADERS = client/remctl.h
noinst_LTLIBRARIES = portable/libportable.la util/libutil.la
portable_libportable_la_SOURCES = portable/dummy.c portable/getaddrinfo.h \
portable/getnameinfo.h portable/getopt.h portable/gssapi.h \
- portable/macros.h portable/socket.h portable/stdbool.h \
- portable/system.h portable/uio.h
+ portable/krb5.h portable/macros.h portable/socket.h \
+ portable/stdbool.h portable/system.h portable/uio.h
portable_libportable_la_LIBADD = $(LTLIBOBJS)
util_libutil_la_SOURCES = util/fdflag.c util/fdflag.h util/gss-errors.c \
util/gss-errors.h util/gss-tokens.c util/gss-tokens.h util/macros.h \
diff --git a/NEWS b/NEWS
index 9845c2f..1175c05 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,13 @@
User-Visible remctl Changes
+remctl 3.5 (unreleased)
+
+ If a Kerberos library and gss_krb5_import_cred are available at build
+ time, libremctl now uses them to implement remctl_set_ccache to avoid
+ affecting global program GSS-API state. If those requirements are
+ met, remctl_set_ccache will only affect the remctl context on which
+ it's called.
+
remctl 3.4 (2013-03-26)
Add new C APIs for establishing a remctl connection given a sockaddr,
diff --git a/README b/README
index 782cc2f..9ed18a7 100644
--- a/README
+++ b/README
@@ -70,7 +70,9 @@ REQUIREMENTS
a C compiler to build. Both will build against either MIT Kerberos or
Heimdal (tested with Heimdal 0.6 and later). remctl will also build
against the Kerberos GSS-API implementation shipped with AIX 5.2 and the
- Solaris 10 generic GSS-API library.
+ Solaris 10 generic GSS-API library. The remctl_set_ccache
+ implementation is improved by building against Kerberos libraries and a
+ GSS-API library that supports gss_krb5_import_cred.
The remctl server will support regex ACLs if the system supports the
POSIX regex API. The remctl server also optionally supports PCRE
@@ -234,6 +236,9 @@ INSTALLATION (UNIX)
krb5-config is not used and library probing is always done if either
--with-gssapi-include or --with-gssapi-lib are given.
+ You can similarly override krb5-config for Kerberos libraries (if you
+ have them) with --with-krb5, --with-krb5-include, and --with-krb5-lib.
+
remctl will automatically build with PCRE support if pcre-config or the
PCRE library are found. You can pass --with-pcre to configure to
specify the root directory where PCRE is installed, or set the include
diff --git a/client/api.c b/client/api.c
index 5eb2b78..388397c 100644
--- a/client/api.c
+++ b/client/api.c
@@ -18,9 +18,10 @@
*/
#include <config.h>
-#include <portable/system.h>
#include <portable/gssapi.h>
+#include <portable/krb5.h>
#include <portable/socket.h>
+#include <portable/system.h>
#include <portable/uio.h>
#include <errno.h>
@@ -219,9 +220,14 @@ remctl_new(void)
r->fd = INVALID_SOCKET;
r->host = NULL;
r->principal = NULL;
+ r->ccache = NULL;
r->context = GSS_C_NO_CONTEXT;
r->error = NULL;
r->output = NULL;
+#ifdef HAVE_KERBEROS
+ r->krb_ctx = NULL;
+ r->krb_ccache = NULL;
+#endif
return r;
}
@@ -238,11 +244,27 @@ remctl_new(void)
* not support setting the Kerberos ticket cache. A reasonable fallback is to
* set the KRB5CCNAME environment variable.
*
- * Be aware that this function sets the Kerberos credential cache globally for
- * all uses of GSS-API by that process. The GSS-API does not provide a way of
- * setting it only for one particular GSS-API context.
+ * If Kerberos libraries and the gss_krb5_import_cred function are available,
+ * this will be per-context. Otherwise, be aware that this function sets the
+ * Kerberos credential cache globally for all uses of GSS-API by that process.
*/
-#ifdef HAVE_GSS_KRB5_CCACHE_NAME
+#if defined(HAVE_KERBEROS) && defined(HAVE_GSS_KRB5_IMPORT_CRED)
+int
+remctl_set_ccache(struct remctl *r, const char *ccache)
+{
+ char *copy;
+
+ copy = strdup(ccache);
+ if (copy == NULL) {
+ internal_set_error(r, "cannot allocate memory: %s", strerror(errno));
+ return 0;
+ }
+ if (r->ccache != NULL)
+ free(r->ccache);
+ r->ccache = copy;
+ return 1;
+}
+#elif defined(HAVE_GSS_KRB5_CCACHE_NAME)
int
remctl_set_ccache(struct remctl *r, const char *ccache)
{
@@ -443,6 +465,8 @@ remctl_close(struct remctl *r)
internal_v2_quit(r);
if (r->source != NULL)
free(r->source);
+ if (r->ccache != NULL)
+ free(r->ccache);
if (r->fd != -1)
socket_close(r->fd);
if (r->error != NULL)
@@ -454,6 +478,13 @@ remctl_close(struct remctl *r)
}
if (r->context != GSS_C_NO_CONTEXT)
gss_delete_sec_context(&minor, &r->context, GSS_C_NO_BUFFER);
+#ifdef HAVE_KERBEROS
+ if (r->krb_ctx != NULL) {
+ if (r->krb_ccache != NULL)
+ krb5_cc_close(r->krb_ctx, r->krb_ccache);
+ krb5_free_context(r->krb_ctx);
+ }
+#endif
free(r);
}
socket_shutdown();
diff --git a/client/error.c b/client/error.c
index f7a5a38..1becd9a 100644
--- a/client/error.c
+++ b/client/error.c
@@ -7,7 +7,7 @@
* return the appropriate details.
*
* Written by Russ Allbery <rra@stanford.edu>
- * Copyright 2006, 2007, 2008, 2010
+ * Copyright 2006, 2007, 2008, 2010, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* See LICENSE for licensing terms.
@@ -64,6 +64,24 @@ internal_gssapi_error(struct remctl *r, const char *error, OM_uint32 major,
/*
+ * Internal function to set the remctl error message from a Kerberos error
+ * message.
+ */
+void
+internal_krb5_error(struct remctl *r, const char *error, krb5_error_code code)
+{
+ const char *message;
+
+ if (r->krb_ctx == NULL)
+ internal_set_error(r, "error %s: cannot create Kerberos context",
+ error);
+ message = krb5_get_error_message(r->krb_ctx, code);
+ internal_set_error(r, "error %s: %s", error, message);
+ krb5_free_error_message(r->krb_ctx, message);
+}
+
+
+/*
* Internal function to set the remctl error message from a token error.
* Handles the various token failure codes from the token_send and token_recv
* functions and their *_priv counterparts.
diff --git a/client/internal.h b/client/internal.h
index b01223d..0086091 100644
--- a/client/internal.h
+++ b/client/internal.h
@@ -3,7 +3,7 @@
*
* Written by Russ Allbery <rra@stanford.edu>
* Based on prior work by Anton Ushakov
- * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012
+ * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* See LICENSE for licensing terms.
@@ -14,6 +14,7 @@
#include <config.h>
#include <portable/gssapi.h>
+#include <portable/krb5.h>
#include <portable/macros.h>
#include <portable/socket.h>
#include <portable/stdbool.h>
@@ -30,12 +31,19 @@ struct remctl {
int protocol; /* Protocol version. */
char *source; /* Source address for connection. */
time_t timeout;
+ char *ccache; /* Path to client ticket cache. */
socket_type fd;
gss_ctx_id_t context;
char *error;
struct remctl_output *output;
int status;
bool ready; /* If true, we are expecting server output. */
+
+ /* Used to hold state for remctl_set_ccache. */
+#ifdef HAVE_KERBEROS
+ krb5_context krb_ctx;
+ krb5_ccache krb_ccache;
+#endif
};
BEGIN_DECLS
@@ -47,6 +55,10 @@ BEGIN_DECLS
void internal_set_error(struct remctl *, const char *, ...);
void internal_gssapi_error(struct remctl *, const char *error,
OM_uint32 major, OM_uint32 minor);
+#ifdef HAVE_KERBEROS
+void internal_krb5_error(struct remctl *, const char *error,
+ krb5_error_code code);
+#endif
void internal_token_error(struct remctl *, const char *error, int status,
OM_uint32 major, OM_uint32 minor);
diff --git a/client/open.c b/client/open.c
index 65029ff..6c2ce21 100644
--- a/client/open.c
+++ b/client/open.c
@@ -15,9 +15,10 @@
*/
#include <config.h>
-#include <portable/system.h>
+#include <portable/krb5.h>
#include <portable/gssapi.h>
#include <portable/socket.h>
+#include <portable/system.h>
#include <errno.h>
@@ -122,6 +123,42 @@ internal_import_name(struct remctl *r, const char *host,
/*
+ * Import the client credentials from a designated Kerberos ticket cache.
+ *
+ * This code is used if we have Kerberos libraries available and the GSS-API
+ * implementation supports gss_krb5_import_cred. In that case, we can tell
+ * GSS-API which ticket cache to use. Otherwise, we have to either set a
+ * global GSS-API variable with gss_krb5_ccache_name or just use whatever the
+ * default is. The other cases are handled in remctl_set_ccache.
+ */
+static bool
+internal_set_cred(struct remctl *r, gss_cred_id_t *gss_cred)
+{
+ krb5_error_code code;
+ OM_uint32 major, minor;
+
+ if (r->krb_ctx == NULL) {
+ code = krb5_init_context(&r->krb_ctx);
+ if (code != 0) {
+ internal_krb5_error(r, "opening ticket cache", code);
+ return false;
+ }
+ }
+ code = krb5_cc_resolve(r->krb_ctx, r->ccache, &r->krb_ccache);
+ if (code != 0) {
+ internal_krb5_error(r, "opening ticket cache", code);
+ return false;
+ }
+ major = gss_krb5_import_cred(&minor, r->krb_ccache, NULL, NULL, gss_cred);
+ if (major != GSS_S_COMPLETE) {
+ internal_gssapi_error(r, "importing ticket cache", major, minor);
+ return false;
+ }
+ return true;
+}
+
+
+/*
* Open a new connection to a server. Returns true on success, false on
* failure. On failure, sets the error message appropriately.
*/
@@ -132,6 +169,7 @@ internal_open(struct remctl *r, const char *host, const char *principal)
gss_buffer_desc send_tok, recv_tok, *token_ptr;
gss_buffer_desc empty_token = { 0, (void *) "" };
gss_name_t name = GSS_C_NO_NAME;
+ gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
OM_uint32 major, minor, init_minor, gss_flags;
static const OM_uint32 wanted_gss_flags
@@ -144,6 +182,11 @@ internal_open(struct remctl *r, const char *host, const char *principal)
if (!internal_import_name(r, host, principal, &name))
goto fail;
+ /* If the user has specified a Kerberos ticket cache, import it. */
+ if (r->ccache != NULL)
+ if (!internal_set_cred(r, &gss_cred))
+ goto fail;
+
/*
* Default to protocol version two, but if some other protocol is already
* set in the remctl struct, don't override. This facility is used only
@@ -160,7 +203,8 @@ internal_open(struct remctl *r, const char *host, const char *principal)
goto fail;
}
- /* Perform the context-establishment loop.
+ /*
+ * Perform the context-establishment loop.
*
* On each pass through the loop, token_ptr points to the token to send to
* the server (or GSS_C_NO_BUFFER on the first pass). Every generated
@@ -179,10 +223,9 @@ internal_open(struct remctl *r, const char *host, const char *principal)
*/
token_ptr = GSS_C_NO_BUFFER;
do {
- major = gss_init_sec_context(&init_minor, GSS_C_NO_CREDENTIAL,
- &gss_context, name, (const gss_OID) GSS_KRB5_MECHANISM,
- wanted_gss_flags, 0, NULL, token_ptr, NULL, &send_tok,
- &gss_flags, NULL);
+ major = gss_init_sec_context(&init_minor, gss_cred, &gss_context,
+ name, (const gss_OID) GSS_KRB5_MECHANISM, wanted_gss_flags,
+ 0, NULL, token_ptr, NULL, &send_tok, &gss_flags, NULL);
if (token_ptr != GSS_C_NO_BUFFER)
free(recv_tok.value);
diff --git a/configure.ac b/configure.ac
index 4cf4c83..86d91c2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,6 +29,15 @@ dnl Only used for the test suite.
AC_ARG_VAR([PATH_FAKEROOT], [Path to fakeroot for the test suite])
AC_PATH_PROG([PATH_FAKEROOT], [fakeroot])
+dnl Kerberos portability checks.
+RRA_LIB_KRB5_OPTIONAL
+AS_IF([test x"$rra_use_kerberos" != xfalse],
+ [RRA_LIB_KRB5_SWITCH
+ AC_CHECK_FUNCS([krb5_get_init_creds_opt_set_default_flags \
+ krb5_principal_get_realm])
+ AC_LIBOBJ([krb5-extra])
+ RRA_LIB_KRB5_RESTORE])
+
dnl GSS-API portability checks.
RRA_LIB_GSSAPI
RRA_LIB_GSSAPI_SWITCH
@@ -36,7 +45,7 @@ AC_CHECK_DECLS([GSS_KRB5_MECHANISM], [],
[AC_CHECK_DECLS([gss_mech_krb5], [],
[AC_LIBOBJ([gssapi-mech])], [RRA_INCLUDES_GSSAPI])],
[RRA_INCLUDES_GSSAPI])
-AC_CHECK_FUNCS([gss_krb5_ccache_name])
+AC_CHECK_FUNCS([gss_krb5_ccache_name gss_krb5_import_cred])
RRA_LIB_GSSAPI_RESTORE
AC_SEARCH_LIBS([gethostbyname], [nsl])
diff --git a/docs/api/remctl_set_ccache.pod b/docs/api/remctl_set_ccache.pod
index c7067c5..2edbb36 100644
--- a/docs/api/remctl_set_ccache.pod
+++ b/docs/api/remctl_set_ccache.pod
@@ -13,13 +13,13 @@ int B<remctl_set_ccache>(struct remctl *I<r>, const char *I<ccache>);
=head1 DESCRIPTION
-remctl_set_ccache() tells the underlying GSS-API library used by the
-remctl client library to use I<ccache> as the credential cache for
-authentication to a remctl server. It will affect any subsequent
-remctl_open() calls on the same struct remctl object (and may have broader
-effects; see below). I<ccache> is normally a path to a file in the file
-system that holds Kerberos credentials, but may take other values
-depending on the underlying Kerberos implementation used by GSS-API.
+remctl_set_ccache() tells the the remctl client library to use I<ccache>
+as the credential cache for authentication to a remctl server. It will
+affect any subsequent remctl_open() calls on the same struct remctl object
+(and may have broader effects; see below). I<ccache> is normally a path
+to a file in the file system that holds Kerberos credentials, but may take
+other values depending on the underlying Kerberos implementation used by
+GSS-API.
Calling this function will normally override any KRB5CCNAME environment
setting. If the caller wishes to honor that setting, it should either not
@@ -56,12 +56,16 @@ underlying library rather than some other cause.
=head1 WARNINGS
-Calling this function normally sets the credential cache used by the
+The effect of this function is only localized to this specific remctl
+client context if the remctl client library was built against a Kerberos
+as well as GSS-API library and the GSS-API library supported
+gss_krb5_import_cred(). Otherwise, it falls back to calling
+gss_krb5_ccache_name(), which sets the credential cache used by the
underlying GSS-API library for every GSS-API operation in the current
execution context (process or thread), not just for this remctl object or
-even just for remctl operations. This function therefore changes global
-execution state and may affect other GSS-API operations done elsewhere in
-the same process. This is an inherent limitation in the current GSS-API.
+even just for remctl operations. This function may therefore change
+global execution state and may affect other GSS-API operations done
+elsewhere in the same process.
=head1 SEE ALSO
@@ -77,7 +81,7 @@ Russ Allbery <rra@stanford.edu>
=head1 COPYRIGHT AND LICENSE
-Copyright 2011 The Board of Trustees of the Leland Stanford Junior
+Copyright 2011, 2013 The Board of Trustees of the Leland Stanford Junior
University
Copying and distribution of this file, with or without modification, are
diff --git a/m4/krb5.m4 b/m4/krb5.m4
new file mode 100644
index 0000000..964023a
--- /dev/null
+++ b/m4/krb5.m4
@@ -0,0 +1,336 @@
+dnl Find the compiler and linker flags for Kerberos.
+dnl
+dnl Finds the compiler and linker flags for linking with Kerberos libraries.
+dnl Provides the --with-krb5, --with-krb5-include, and --with-krb5-lib
+dnl configure options to specify non-standard paths to the Kerberos libraries.
+dnl Uses krb5-config where available unless reduced dependencies is requested
+dnl or --with-krb5-include or --with-krb5-lib are given.
+dnl
+dnl Provides the macro RRA_LIB_KRB5 and sets the substitution variables
+dnl KRB5_CPPFLAGS, KRB5_LDFLAGS, and KRB5_LIBS. Also provides
+dnl RRA_LIB_KRB5_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the
+dnl Kerberos libraries, saving the current values first, and
+dnl RRA_LIB_KRB5_RESTORE to restore those settings to before the last
+dnl RRA_LIB_KRB5_SWITCH. HAVE_KERBEROS will always be defined if RRA_LIB_KRB5
+dnl is used.
+dnl
+dnl If KRB5_CPPFLAGS, KRB5_LDFLAGS, or KRB5_LIBS are set before calling these
+dnl macros, their values will be added to whatever the macros discover.
+dnl
+dnl Provides the RRA_LIB_KRB5_OPTIONAL macro, which should be used if Kerberos
+dnl support is optional. This macro will still always set the substitution
+dnl variables, but they'll be empty unless --with-krb5 is given. Also,
+dnl HAVE_KERBEROS will be defined if --with-krb5 is given and
+dnl $rra_use_kerberos will be set to "true".
+dnl
+dnl Sets the Automake conditional KRB5_USES_COM_ERR saying whether we use
+dnl com_err, since if we're also linking with AFS libraries, we may have to
+dnl change library ordering in that case.
+dnl
+dnl Depends on RRA_ENABLE_REDUCED_DEPENDS and RRA_SET_LDFLAGS.
+dnl
+dnl Also provides RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS, which checks
+dnl whether krb5_get_init_creds_opt_free takes one argument or two. Defines
+dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments.
+dnl
+dnl Also provides RRA_INCLUDES_KRB5, which are the headers to include when
+dnl probing the Kerberos library properties.
+dnl
+dnl The canonical version of this file is maintained in the rra-c-util
+dnl package, available at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+dnl
+dnl Written by Russ Allbery <rra@stanford.edu>
+dnl Copyright 2005, 2006, 2007, 2008, 2009, 2010, 2011
+dnl The Board of Trustees of the Leland Stanford Junior University
+dnl
+dnl This file is free software; the authors give unlimited permission to copy
+dnl and/or distribute it, with or without modifications, as long as this
+dnl notice is preserved.
+
+dnl Headers to include when probing for Kerberos library properties.
+AC_DEFUN([RRA_INCLUDES_KRB5], [[
+#if HAVE_KRB5_H
+# include <krb5.h>
+#else
+# include <krb5/krb5.h>
+#endif
+]])
+
+dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to
+dnl versions that include the Kerberos flags. Used as a wrapper, with
+dnl RRA_LIB_KRB5_RESTORE, around tests.
+AC_DEFUN([RRA_LIB_KRB5_SWITCH],
+[rra_krb5_save_CPPFLAGS="$CPPFLAGS"
+ rra_krb5_save_LDFLAGS="$LDFLAGS"
+ rra_krb5_save_LIBS="$LIBS"
+ CPPFLAGS="$KRB5_CPPFLAGS $CPPFLAGS"
+ LDFLAGS="$KRB5_LDFLAGS $LDFLAGS"
+ LIBS="$KRB5_LIBS $LIBS"])
+
+dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before
+dnl RRA_LIB_KRB5_SWITCH was called).
+AC_DEFUN([RRA_LIB_KRB5_RESTORE],
+[CPPFLAGS="$rra_krb5_save_CPPFLAGS"
+ LDFLAGS="$rra_krb5_save_LDFLAGS"
+ LIBS="$rra_krb5_save_LIBS"])
+
+dnl Set KRB5_CPPFLAGS and KRB5_LDFLAGS based on rra_krb5_root,
+dnl rra_krb5_libdir, and rra_krb5_includedir.
+AC_DEFUN([_RRA_LIB_KRB5_PATHS],
+[AS_IF([test x"$rra_krb5_libdir" != x],
+ [KRB5_LDFLAGS="-L$rra_krb5_libdir"],
+ [AS_IF([test x"$rra_krb5_root" != x],
+ [RRA_SET_LDFLAGS([KRB5_LDFLAGS], [$rra_krb5_root])])])
+ AS_IF([test x"$rra_krb5_includedir" != x],
+ [KRB5_CPPFLAGS="-I$rra_krb5_includedir"],
+ [AS_IF([test x"$rra_krb5_root" != x],
+ [AS_IF([test x"$rra_krb5_root" != x/usr],
+ [KRB5_CPPFLAGS="-I${rra_krb5_root}/include"])])])])
+
+dnl Check for a header using a file existence check rather than using
+dnl AC_CHECK_HEADERS. This is used if there were arguments to configure
+dnl specifying the Kerberos header path, since we may have one header in the
+dnl default include path and another under our explicitly-configured Kerberos
+dnl location.
+AC_DEFUN([_RRA_LIB_KRB5_CHECK_HEADER],
+[AC_MSG_CHECKING([for $1])
+ AS_IF([test -f "${rra_krb5_incroot}/$1"],
+ [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), [1],
+ [Define to 1 if you have the <$1> header file.])
+ AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])])])
+
+dnl Does the appropriate library checks for reduced-dependency Kerberos
+dnl linkage. The single argument, if true, says to fail if Kerberos could not
+dnl be found.
+AC_DEFUN([_RRA_LIB_KRB5_REDUCED],
+[RRA_LIB_KRB5_SWITCH
+ AC_CHECK_LIB([krb5], [krb5_init_context], [KRB5_LIBS="-lkrb5"],
+ [AS_IF([test x"$1" = xtrue],
+ [AC_MSG_ERROR([cannot find usable Kerberos library])])])
+ LIBS="$KRB5_LIBS $LIBS"
+ AS_IF([test x"$rra_krb5_incroot" = x],
+ [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])],
+ [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h])
+ _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])])
+ AC_CHECK_FUNCS([krb5_get_error_message],
+ [AC_CHECK_FUNCS([krb5_free_error_message])],
+ [AC_CHECK_FUNCS([krb5_get_error_string], [],
+ [AC_CHECK_FUNCS([krb5_get_err_txt], [],
+ [AC_CHECK_LIB([ksvc], [krb5_svc_get_msg],
+ [KRB5_LIBS="$KRB5_LIBS -lksvc"
+ AC_DEFINE([HAVE_KRB5_SVC_GET_MSG], [1])
+ AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [],
+ [RRA_INCLUDES_KRB5])],
+ [AC_CHECK_LIB([com_err], [com_err],
+ [KRB5_LIBS="$KRB5_LIBS -lcom_err"],
+ [AC_MSG_ERROR([cannot find usable com_err library])])
+ AC_CHECK_HEADERS([et/com_err.h])])])])])
+ RRA_LIB_KRB5_RESTORE])
+
+dnl Does the appropriate library checks for Kerberos linkage when we don't
+dnl have krb5-config or reduced dependencies. The single argument, if true,
+dnl says to fail if Kerberos could not be found.
+AC_DEFUN([_RRA_LIB_KRB5_MANUAL],
+[RRA_LIB_KRB5_SWITCH
+ rra_krb5_extra=
+ LIBS=
+ AC_SEARCH_LIBS([res_search], [resolv], [],
+ [AC_SEARCH_LIBS([__res_search], [resolv])])
+ AC_SEARCH_LIBS([gethostbyname], [nsl])
+ AC_SEARCH_LIBS([socket], [socket], [],
+ [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], [],
+ [-lsocket])])
+ AC_SEARCH_LIBS([crypt], [crypt])
+ AC_SEARCH_LIBS([roken_concat], [roken])
+ rra_krb5_extra="$LIBS"
+ LIBS="$rra_krb5_save_LIBS"
+ AC_CHECK_LIB([krb5], [krb5_init_context],
+ [KRB5_LIBS="-lkrb5 -lasn1 -lcom_err -lcrypto $rra_krb5_extra"],
+ [AC_CHECK_LIB([krb5support], [krb5int_getspecific],
+ [rra_krb5_extra="-lkrb5support $rra_krb5_extra"],
+ [AC_CHECK_LIB([pthreads], [pthread_setspecific],
+ [rra_krb5_pthread="-lpthreads"],
+ [AC_CHECK_LIB([pthread], [pthread_setspecific],
+ [rra_krb5_pthread="-lpthread"])])
+ AC_CHECK_LIB([krb5support], [krb5int_setspecific],
+ [rra_krb5_extra="-lkrb5support $rra_krb5_extra $rra_krb5_pthread"],
+ [], [$rra_krb5_pthread $rra_krb5_extra])],
+ [$rra_krb5_extra])
+ AC_CHECK_LIB([com_err], [error_message],
+ [rra_krb5_extra="-lcom_err $rra_krb5_extra"], [], [$rra_krb5_extra])
+ AC_CHECK_LIB([ksvc], [krb5_svc_get_msg],
+ [rra_krb5_extra="-lksvc $rra_krb5_extra"], [], [$rra_krb5_extra])
+ AC_CHECK_LIB([k5crypto], [krb5int_hash_md5],
+ [rra_krb5_extra="-lk5crypto $rra_krb5_extra"], [], [$rra_krb5_extra])
+ AC_CHECK_LIB([k5profile], [profile_get_values],
+ [rra_krb5_extra="-lk5profile $rra_krb5_extra"], [], [$rra_krb5_extra])
+ AC_CHECK_LIB([krb5], [krb5_cc_default],
+ [KRB5_LIBS="-lkrb5 $rra_krb5_extra"],
+ [AS_IF([test x"$1" = xtrue],
+ [AC_MSG_ERROR([cannot find usable Kerberos library])])],
+ [$rra_krb5_extra])],
+ [-lasn1 -lcom_err -lcrypto $rra_krb5_extra])
+ LIBS="$KRB5_LIBS $LIBS"
+ AS_IF([test x"$rra_krb5_incroot" = x],
+ [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])],
+ [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h])
+ _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])])
+ AC_CHECK_FUNCS([krb5_get_error_message],
+ [AC_CHECK_FUNCS([krb5_free_error_message])],
+ [AC_CHECK_FUNCS([krb5_get_error_string], [],
+ [AC_CHECK_FUNCS([krb5_get_err_txt], [],
+ [AC_CHECK_FUNCS([krb5_svc_get_msg],
+ [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [],
+ [RRA_INCLUDES_KRB5])],
+ [AC_CHECK_HEADERS([et/com_err.h])])])])])
+ RRA_LIB_KRB5_RESTORE])
+
+dnl Sanity-check the results of krb5-config and be sure we can really link a
+dnl Kerberos program. If that fails, clear KRB5_CPPFLAGS and KRB5_LIBS so
+dnl that we know we don't have usable flags and fall back on the manual
+dnl check.
+AC_DEFUN([_RRA_LIB_KRB5_CHECK],
+[RRA_LIB_KRB5_SWITCH
+ AC_CHECK_FUNC([krb5_init_context],
+ [RRA_LIB_KRB5_RESTORE],
+ [RRA_LIB_KRB5_RESTORE
+ KRB5_CPPFLAGS=
+ KRB5_LIBS=
+ _RRA_LIB_KRB5_PATHS
+ _RRA_LIB_KRB5_MANUAL([$1])])])
+
+dnl Determine Kerberos compiler and linker flags from krb5-config. Does the
+dnl additional probing we need to do to uncover error handling features, and
+dnl falls back on the manual checks.
+AC_DEFUN([_RRA_LIB_KRB5_CONFIG],
+[RRA_KRB5_CONFIG([${rra_krb5_root}], [krb5], [KRB5],
+ [_RRA_LIB_KRB5_CHECK([$1])
+ RRA_LIB_KRB5_SWITCH
+ AS_IF([test x"$rra_krb5_incroot" = x],
+ [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])],
+ [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h])
+ _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])])
+ AC_CHECK_FUNCS([krb5_get_error_message],
+ [AC_CHECK_FUNCS([krb5_free_error_message])],
+ [AC_CHECK_FUNCS([krb5_get_error_string], [],
+ [AC_CHECK_FUNCS([krb5_get_err_txt], [],
+ [AC_CHECK_FUNCS([krb5_svc_get_msg],
+ [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [],
+ [RRA_INCLUDES_KRB5])],
+ [AC_CHECK_HEADERS([et/com_err.h])])])])])
+ RRA_LIB_KRB5_RESTORE],
+ [_RRA_LIB_KRB5_PATHS
+ _RRA_LIB_KRB5_MANUAL([$1])])])
+
+dnl The core of the library checking, shared between RRA_LIB_KRB5 and
+dnl RRA_LIB_KRB5_OPTIONAL. The single argument, if "true", says to fail if
+dnl Kerberos could not be found. Set up rra_krb5_incroot for later header
+dnl checking.
+AC_DEFUN([_RRA_LIB_KRB5_INTERNAL],
+[AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS])
+ rra_krb5_incroot=
+ AS_IF([test x"$rra_krb5_includedir" != x],
+ [rra_krb5_incroot="$rra_krb5_includedir"],
+ [AS_IF([test x"$rra_krb5_root" != x],
+ [rra_krb5_incroot="${rra_krb5_root}/include"])])
+ AS_IF([test x"$rra_reduced_depends" = xtrue],
+ [_RRA_LIB_KRB5_PATHS
+ _RRA_LIB_KRB5_REDUCED([$1])],
+ [AS_IF([test x"$rra_krb5_includedir" = x && test x"$rra_krb5_libdir" = x],
+ [_RRA_LIB_KRB5_CONFIG([$1])],
+ [_RRA_LIB_KRB5_PATHS
+ _RRA_LIB_KRB5_MANUAL([$1])])])
+ rra_krb5_uses_com_err=false
+ AS_CASE([$LIBS], [*-lcom_err*], [rra_krb5_uses_com_err=true])
+ AM_CONDITIONAL([KRB5_USES_COM_ERR], [test x"$rra_krb5_uses_com_err" = xtrue])])
+
+dnl The main macro for packages with mandatory Kerberos support.
+AC_DEFUN([RRA_LIB_KRB5],
+[rra_krb5_root=
+ rra_krb5_libdir=
+ rra_krb5_includedir=
+ rra_use_kerberos=true
+ AC_SUBST([KRB5_CPPFLAGS])
+ AC_SUBST([KRB5_LDFLAGS])
+ AC_SUBST([KRB5_LIBS])
+
+ AC_ARG_WITH([krb5],
+ [AS_HELP_STRING([--with-krb5=DIR],
+ [Location of Kerberos headers and libraries])],
+ [AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
+ [rra_krb5_root="$withval"])])
+ AC_ARG_WITH([krb5-include],
+ [AS_HELP_STRING([--with-krb5-include=DIR],
+ [Location of Kerberos headers])],
+ [AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
+ [rra_krb5_includedir="$withval"])])
+ AC_ARG_WITH([krb5-lib],
+ [AS_HELP_STRING([--with-krb5-lib=DIR],
+ [Location of Kerberos libraries])],
+ [AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
+ [rra_krb5_libdir="$withval"])])
+ _RRA_LIB_KRB5_INTERNAL([true])
+ AC_DEFINE([HAVE_KERBEROS], 1, [Define to enable Kerberos features.])])
+
+dnl The main macro for packages with optional Kerberos support.
+AC_DEFUN([RRA_LIB_KRB5_OPTIONAL],
+[rra_krb5_root=
+ rra_krb5_libdir=
+ rra_krb5_includedir=
+ rra_use_kerberos=
+ AC_SUBST([KRB5_CPPFLAGS])
+ AC_SUBST([KRB5_LDFLAGS])
+ AC_SUBST([KRB5_LIBS])
+
+ AC_ARG_WITH([krb5],
+ [AS_HELP_STRING([--with-krb5@<:@=DIR@:>@],
+ [Location of Kerberos headers and libraries])],
+ [AS_IF([test x"$withval" = xno],
+ [rra_use_kerberos=false],
+ [AS_IF([test x"$withval" != xyes], [rra_krb5_root="$withval"])
+ rra_use_kerberos=true])])
+ AC_ARG_WITH([krb5-include],
+ [AS_HELP_STRING([--with-krb5-include=DIR],
+ [Location of Kerberos headers])],
+ [AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
+ [rra_krb5_includedir="$withval"])])
+ AC_ARG_WITH([krb5-lib],
+ [AS_HELP_STRING([--with-krb5-lib=DIR],
+ [Location of Kerberos libraries])],
+ [AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
+ [rra_krb5_libdir="$withval"])])
+
+ AS_IF([test x"$rra_use_kerberos" != xfalse],
+ [AS_IF([test x"$rra_use_kerberos" = xtrue],
+ [_RRA_LIB_KRB5_INTERNAL([true])],
+ [_RRA_LIB_KRB5_INTERNAL([false])])],
+ [AM_CONDITIONAL([KRB5_USES_COM_ERR], [false])])
+ AS_IF([test x"$KRB5_LIBS" != x],
+ [AC_DEFINE([HAVE_KERBEROS], 1, [Define to enable Kerberos features.])])])
+
+dnl Source used by RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS.
+AC_DEFUN([_RRA_FUNC_KRB5_OPT_FREE_ARGS_SOURCE], [RRA_INCLUDES_KRB5] [[
+int
+main(void)
+{
+ krb5_get_init_creds_opt *opts;
+ krb5_context c;
+ krb5_get_init_creds_opt_free(c, opts);
+}
+]])
+
+dnl Check whether krb5_get_init_creds_opt_free takes one argument or two.
+dnl Early Heimdal used to take a single argument. Defines
+dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments.
+dnl
+dnl Should be called with RRA_LIB_KRB5_SWITCH active.
+AC_DEFUN([RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS],
+[AC_CACHE_CHECK([if krb5_get_init_creds_opt_free takes two arguments],
+ [rra_cv_func_krb5_get_init_creds_opt_free_args],
+ [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_RRA_FUNC_KRB5_OPT_FREE_ARGS_SOURCE])],
+ [rra_cv_func_krb5_get_init_creds_opt_free_args=yes],
+ [rra_cv_func_krb5_get_init_creds_opt_free_args=no])])
+ AS_IF([test $rra_cv_func_krb5_get_init_creds_opt_free_args = yes],
+ [AC_DEFINE([HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS], 1,
+ [Define if krb5_get_init_creds_opt_free takes two arguments.])])])
diff --git a/perl/lib/Net/Remctl.pm.in b/perl/lib/Net/Remctl.pm.in
index 1a22726..4aea510 100644
--- a/perl/lib/Net/Remctl.pm.in
+++ b/perl/lib/Net/Remctl.pm.in
@@ -5,7 +5,7 @@
# file contains the bootstrap and export code and the documentation.
#
# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2007, 2008, 2011, 2012
+# Copyright 2007, 2008, 2011, 2012, 2013
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
@@ -189,12 +189,12 @@ GSS-API. This method will affect all subsequent open() calls on at least
the same object, but will have no effect on connections that are already
open. Returns true on success and false on failure.
-For current GSS-API implementations, this will affect not only all
+If the remctl client library was built against a Kerberos library and the
+GSS-API library supported gss_krb5_import_cred, this call affects only
+this Net::Remctl object. Otherwise, this will affect not only all
subsequent open() calls for the same object, but all subsequent remctl
connections of any kind from the same process, and even other GSS-API
-connections from the same process unrelated to remctl. This is due to a
-limitation in the GSS-API that makes this setting a global setting for the
-process or thread.
+connections from the same process unrelated to remctl.
Not all GSS-API implementations support setting the credential cache. If
this is not supported, false will be returned.
diff --git a/php/README b/php/README
index d8e42eb..55099e2 100644
--- a/php/README
+++ b/php/README
@@ -118,12 +118,13 @@ FULL INTERFACE
effect on connections that are already open. Returns true on
success, false on failure.
- For current GSS-API implementations, this will affect not only all
- subsequent open() calls for the same object, but all subsequent
- remctl connections of any kind from the same process, and even other
- GSS-API connections from the same process unrelated to remctl. This
- is due to a limitation in the GSS-API that makes this setting a
- global setting for the process or thread.
+ If the remctl client library was built against a Kerberos library
+ and the GSS-API library supported gss_krb5_import_cred, this call
+ affects only this connection object. Otherwise, this will affect
+ not only all subsequent open() calls for the same object, but all
+ subsequent remctl connections of any kind from the same process, and
+ even other GSS-API connections from the same process unrelated to
+ remctl.
Not all GSS-API implementations support setting the credential
cache. If this is not supported, false (for failure) will be
diff --git a/portable/krb5-extra.c b/portable/krb5-extra.c
new file mode 100644
index 0000000..df98c61
--- /dev/null
+++ b/portable/krb5-extra.c
@@ -0,0 +1,118 @@
+/*
+ * Portability glue functions for Kerberos.
+ *
+ * This file provides definitions of the interfaces that portable/krb5.h
+ * ensures exist if the function wasn't available in the Kerberos libraries.
+ * Everything in this file will be protected by #ifndef. If the native
+ * Kerberos libraries are fully capable, this file will be skipped.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ *
+ * The authors hereby relinquish any claim to any copyright that they may have
+ * in this work, whether granted under contract or by operation of law or
+ * international treaty, and hereby commit to the public, at large, that they
+ * shall not, at any time in the future, seek to enforce any copyright in this
+ * work against any person or entity, or prevent any person or entity from
+ * copying, publishing, distributing or creating derivative works of this
+ * work.
+ */
+
+#include <config.h>
+#include <portable/krb5.h>
+#include <portable/system.h>
+
+#include <errno.h>
+
+/* Figure out what header files to include for error reporting. */
+#if !defined(HAVE_KRB5_GET_ERROR_MESSAGE) && !defined(HAVE_KRB5_GET_ERR_TEXT)
+# if !defined(HAVE_KRB5_GET_ERROR_STRING)
+# if defined(HAVE_IBM_SVC_KRB5_SVC_H)
+# include <ibm_svc/krb5_svc.h>
+# elif defined(HAVE_ET_COM_ERR_H)
+# include <et/com_err.h>
+# else
+# include <com_err.h>
+# endif
+# endif
+#endif
+
+/* Used for unused parameters to silence gcc warnings. */
+#define UNUSED __attribute__((__unused__))
+
+/*
+ * This string is returned for unknown error messages. We use a static
+ * variable so that we can be sure not to free it.
+ */
+static const char error_unknown[] = "unknown error";
+
+
+#ifndef HAVE_KRB5_GET_ERROR_MESSAGE
+/*
+ * Given a Kerberos error code, return the corresponding error. Prefer the
+ * Kerberos interface if available since it will provide context-specific
+ * error information, whereas the error_message() call will only provide a
+ * fixed message.
+ */
+const char *
+krb5_get_error_message(krb5_context ctx UNUSED, krb5_error_code code UNUSED)
+{
+ const char *msg = NULL;
+
+# if defined(HAVE_KRB5_GET_ERROR_STRING)
+ msg = krb5_get_error_string(ctx);
+# elif defined(HAVE_KRB5_GET_ERR_TEXT)
+ msg = krb5_get_err_text(ctx, code);
+# elif defined(HAVE_KRB5_SVC_GET_MSG)
+ krb5_svc_get_msg(code, (char **) &msg);
+# else
+ msg = error_message(code);
+# endif
+ if (msg == NULL)
+ return error_unknown;
+ else
+ return msg;
+}
+#endif /* !HAVE_KRB5_GET_ERROR_MESSAGE */
+
+
+#ifndef HAVE_KRB5_FREE_ERROR_MESSAGE
+/*
+ * Free an error string if necessary. If we returned a static string, make
+ * sure we don't free it.
+ *
+ * This code assumes that the set of implementations that have
+ * krb5_free_error_message is a subset of those with krb5_get_error_message.
+ * If this assumption ever breaks, we may call the wrong free function.
+ */
+void
+krb5_free_error_message(krb5_context ctx UNUSED, const char *msg)
+{
+ if (msg == error_unknown)
+ return;
+# if defined(HAVE_KRB5_GET_ERROR_STRING)
+ krb5_free_error_string(ctx, (char *) msg);
+# elif defined(HAVE_KRB5_SVC_GET_MSG)
+ krb5_free_string(ctx, (char *) msg);
+# endif
+}
+#endif /* !HAVE_KRB5_FREE_ERROR_MESSAGE */
+
+
+#ifndef HAVE_KRB5_PRINCIPAL_GET_REALM
+/*
+ * Return the realm of a principal as a const char *.
+ */
+const char *
+krb5_principal_get_realm(krb5_context ctx UNUSED, krb5_const_principal princ)
+{
+ const krb5_data *data;
+
+ data = krb5_princ_realm(ctx, princ);
+ if (data == NULL || data->data == NULL)
+ return NULL;
+ return data->data;
+}
+#endif /* !HAVE_KRB5_PRINCIPAL_GET_REALM */
diff --git a/portable/krb5.h b/portable/krb5.h
new file mode 100644
index 0000000..157b95d
--- /dev/null
+++ b/portable/krb5.h
@@ -0,0 +1,90 @@
+/*
+ * Portability wrapper around krb5.h.
+ *
+ * This header includes krb5.h and then adjusts for various portability
+ * issues, primarily between MIT Kerberos and Heimdal, so that code can be
+ * written to a consistent API.
+ *
+ * Unfortunately, due to the nature of the differences between MIT Kerberos
+ * and Heimdal, it's not possible to write code to either one of the APIs and
+ * adjust for the other one. In general, this header tries to make available
+ * the Heimdal API and fix it for MIT Kerberos, but there are places where MIT
+ * Kerberos requires a more specific call. For those cases, it provides the
+ * most specific interface.
+ *
+ * For example, MIT Kerberos has krb5_free_unparsed_name() whereas Heimdal
+ * prefers the generic krb5_xfree(). In this case, this header provides
+ * krb5_free_unparsed_name() for both APIs since it's the most specific call.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ *
+ * The authors hereby relinquish any claim to any copyright that they may have
+ * in this work, whether granted under contract or by operation of law or
+ * international treaty, and hereby commit to the public, at large, that they
+ * shall not, at any time in the future, seek to enforce any copyright in this
+ * work against any person or entity, or prevent any person or entity from
+ * copying, publishing, distributing or creating derivative works of this
+ * work.
+ */
+
+#ifndef PORTABLE_KRB5_H
+#define PORTABLE_KRB5_H 1
+
+/*
+ * Allow inclusion of config.h to be skipped, since sometimes we have to use a
+ * stripped-down version of config.h with a different name.
+ */
+#ifndef CONFIG_H_INCLUDED
+# include <config.h>
+#endif
+#include <portable/macros.h>
+
+#ifdef HAVE_KRB5_H
+# include <krb5.h>
+#else
+# include <krb5/krb5.h>
+#endif
+#include <stdlib.h>
+
+BEGIN_DECLS
+
+/* Default to a hidden visibility for all portability functions. */
+#pragma GCC visibility push(hidden)
+
+/*
+ * krb5_{get,free}_error_message are the preferred APIs for both current MIT
+ * and current Heimdal, but there are tons of older APIs we may have to fall
+ * back on for earlier versions.
+ *
+ * This function should be called immediately after the corresponding error
+ * without any intervening Kerberos calls. Otherwise, the correct error
+ * message and supporting information may not be returned.
+ */
+#ifndef HAVE_KRB5_GET_ERROR_MESSAGE
+const char *krb5_get_error_message(krb5_context, krb5_error_code);
+#endif
+#ifndef HAVE_KRB5_FREE_ERROR_MESSAGE
+void krb5_free_error_message(krb5_context, const char *);
+#endif
+
+/* Heimdal-specific. */
+#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_DEFAULT_FLAGS
+# define krb5_get_init_creds_opt_set_default_flags(c, p, r, o) /* empty */
+#endif
+
+/*
+ * Heimdal provides a nice function that just returns a const char *. On MIT,
+ * there's an accessor macro that returns the krb5_data pointer, which
+ * requires more work to get at the underlying char *.
+ */
+#ifndef HAVE_KRB5_PRINCIPAL_GET_REALM
+const char *krb5_principal_get_realm(krb5_context, krb5_const_principal);
+#endif
+
+/* Undo default visibility change. */
+#pragma GCC visibility pop
+
+#endif /* !PORTABLE_KRB5_H */
diff --git a/python/README b/python/README
index 261e155..040977e 100644
--- a/python/README
+++ b/python/README
@@ -110,12 +110,13 @@ FULL INTERFACE
subsequent open() calls on the same object, but will have no effect
on connections that are already open.
- For current GSS-API implementations, this will affect not only all
- subsequent open() calls for the same object, but all subsequent
- remctl connections of any kind from the same process, and even other
- GSS-API connections from the same process unrelated to remctl. This
- is due to a limitation in the GSS-API that makes this setting a
- global setting for the process or thread.
+ If the remctl client library was built against a Kerberos library
+ and the GSS-API library supported gss_krb5_import_cred, this call
+ affects only this Remctl object. Otherwise, this will affect not
+ only all subsequent open() calls for the same object, but all
+ subsequent remctl connections of any kind from the same process, and
+ even other GSS-API connections from the same process unrelated to
+ remctl.
Not all GSS-API implementations support setting the credential
cache. If this operation is not supported, a RemctlError exception
diff --git a/ruby/README b/ruby/README
index 2942097..0360601 100644
--- a/ruby/README
+++ b/ruby/README
@@ -101,12 +101,15 @@ FULL INTERFACE
To override the credential cache used by GSS-API, assign the
credential cache (as a string) to Remctl.ccache before calling
- Remctl.new. Be aware that this will usually change the default
- credential cache for all uses of GSS-API in the same process,
- including other remctl objects, rather than just this remctl object
- due to a limitation in the GSS-API. Some GSS-API implementations do
- not support setting the credential cache, in which case Remctl.new
- will throw a Remctl::Error exception if this variable is set.
+ Remctl.new. If the remctl client library was built against a
+ Kerberos library and the GSS-API library supported
+ gss_krb5_import_cred, this will only affect subsequently-created
+ Remctl objects. Otherwise, this will change the default credential
+ cache for all uses of GSS-API in the same process, including other
+ remctl objects, rather than just this remctl object due to a
+ limitation in the GSS-API. Some GSS-API implementations do not
+ support setting the credential cache, in which case Remctl.new will
+ throw a Remctl::Error exception if this variable is set.
Normally, the source IP used to connect to the server is assigned by
the kernel. To use a specific source IP, assign that IP address (as
diff --git a/ruby/remctl.c b/ruby/remctl.c
index 97ff544..5587b46 100644
--- a/ruby/remctl.c
+++ b/ruby/remctl.c
@@ -6,7 +6,7 @@
*
* Original implementation by Anthony M. Martinez <twopir@nmt.edu>
* Copyright 2010 Anthony M. Martinez <twopir@nmt.edu>
- * Copyright 2010, 2011, 2012
+ * Copyright 2010, 2011, 2012, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* Permission to use, copy, modify, and distribute this software and its
@@ -251,7 +251,7 @@ rb_remctl_ccache_get(VALUE self UNUSED)
* Remctl.ccahe = '/path/to/some/file' -> 0
*
* Change the credential cache used for new remctl connections. This will
- * also, with most GSS-API implementations, affect all other GSS-API
+ * also, with older GSS-API implementations, affect all other GSS-API
* connections in the same process, including other remctl objects, once the
* value is used during open.
*/
diff --git a/tests/client/ccache-t.c b/tests/client/ccache-t.c
index 3ccccc1..1c83322 100644
--- a/tests/client/ccache-t.c
+++ b/tests/client/ccache-t.c
@@ -2,7 +2,7 @@
* Test suite for setting a specific Kerberos credential cache.
*
* Written by Russ Allbery <rra@stanford.edu>
- * Copyright 2011, 2012
+ * Copyright 2011, 2012, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* See LICENSE for licensing terms.
@@ -31,7 +31,24 @@ main(void)
config = kerberos_setup(TAP_KRB_NEEDS_KEYTAB);
remctld_start(config, "data/conf-simple", (char *) 0);
- plan(12);
+ plan(14);
+
+ /*
+ * Set the ticket cache to something nonexistent and ensure remctl_open
+ * fails even though KRB5CCNAME points to a valid cache.
+ */
+ r = remctl_new();
+ ok(r != NULL, "remctl_new");
+ status = remctl_set_ccache(r, "./nonexistent-file");
+ if (!status)
+ is_string("setting credential cache not supported", remctl_error(r),
+ "unsupported remctl_set_ccache failed with correct error");
+ else {
+ ok(!remctl_open(r, "127.0.0.1", 14373, config->principal),
+ "remctl_open to 127.0.0.1");
+ diag("%s", remctl_error(r));
+ }
+ remctl_close(r);
/* Get the current ticket cache and then change KRB5CCNAME. */
cache = getenv("KRB5CCNAME");
@@ -49,10 +66,10 @@ main(void)
status = remctl_set_ccache(r, cache);
if (!status) {
is_string("setting credential cache not supported", remctl_error(r),
- "remctl_set_ccache failed with correct error");
+ "unsupported remctl_set_ccache failed with correct error");
skip_block(9, "credential cache setting not supported");
} else {
- ok(remctl_set_ccache(r, cache), "remctl_set_ccache");
+ ok(status, "remctl_set_ccache");
ok(remctl_open(r, "127.0.0.1", 14373, config->principal),
"remctl_open to 127.0.0.1");
ok(remctl_command(r, command), "remctl_command");