diff options
author | Russ Allbery <rra@stanford.edu> | 2013-05-16 18:48:11 -0700 |
---|---|---|
committer | Russ Allbery <rra@stanford.edu> | 2013-05-16 19:45:12 -0700 |
commit | d896a4d1fc26b495ee9ecba5d9dfa0ea26829b3c (patch) | |
tree | 1826a5ecb546660ebbdb469ce992a8a92d8a612f /client | |
parent | 8d862b7381e505ee196d550e8a5605020f8ed9d7 (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.c | 41 | ||||
-rw-r--r-- | client/error.c | 20 | ||||
-rw-r--r-- | client/internal.h | 14 | ||||
-rw-r--r-- | client/open.c | 55 |
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); |