summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorRuss Allbery <rra@stanford.edu>2013-05-16 18:48:11 -0700
committerRuss Allbery <rra@stanford.edu>2013-05-16 19:45:12 -0700
commitd896a4d1fc26b495ee9ecba5d9dfa0ea26829b3c (patch)
tree1826a5ecb546660ebbdb469ce992a8a92d8a612f /client
parent8d862b7381e505ee196d550e8a5605020f8ed9d7 (diff)
Use gss_krb5_import_cred for remctl_set_ccache
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. This also requires importing the Kerberos portability layer, so make sure that it's usable for the TAP Kerberos add-on. Change-Id: I561812d0e36df6adf52d974dd5390953940865c5 Reviewed-on: https://gerrit.stanford.edu/1198 Reviewed-by: Russ Allbery <rra@stanford.edu> Tested-by: Russ Allbery <rra@stanford.edu>
Diffstat (limited to 'client')
-rw-r--r--client/api.c41
-rw-r--r--client/error.c20
-rw-r--r--client/internal.h14
-rw-r--r--client/open.c55
4 files changed, 117 insertions, 13 deletions
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);