summaryrefslogtreecommitdiffstats
path: root/proxy/src
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2015-11-09 17:10:56 -0500
committerRobbie Harwood <rharwood@redhat.com>2015-12-01 17:45:56 -0500
commit84fcb276d2253f34e5d50a87e42e34c1fd918a55 (patch)
treeb30a1b41811ff66c4471da2a0dd4bd1697e9f22b /proxy/src
parent7e71db8edc9694ed75110ddd9efa373250cc0545 (diff)
Add helpers to store and retrieve encrypted creds
This will allow to (ab)use the krb5 ccache to store encrypted credentials in the user's ccache for later reuse. Signed-off-by: Simo Sorce <simo@redhat.com> Reviewed-by: Robbie Harwoood <rharwood@redhat.com>
Diffstat (limited to 'proxy/src')
-rw-r--r--proxy/src/gp_creds.c100
-rw-r--r--proxy/src/mechglue/gpp_creds.c139
-rw-r--r--proxy/src/mechglue/gpp_init_sec_context.c15
-rw-r--r--proxy/src/mechglue/gss_plugin.h1
4 files changed, 246 insertions, 9 deletions
diff --git a/proxy/src/gp_creds.c b/proxy/src/gp_creds.c
index c28c10a..e00f735 100644
--- a/proxy/src/gp_creds.c
+++ b/proxy/src/gp_creds.c
@@ -414,6 +414,91 @@ done:
return ret;
}
+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,
@@ -453,11 +538,20 @@ uint32_t gp_add_krb5_creds(uint32_t *min,
}
if (in_cred != GSS_C_NO_CREDENTIAL && acquire_type != ACQ_IMPNAME) {
- /* we can't yet handle adding to an existing credential due to
- * the way gss_krb5_import_cred works. This limitation should
+ /* 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 */
- return GSS_S_CRED_UNAVAIL;
+
+ /* 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) {
+ *min = ret_min;
+ return GSS_S_CRED_UNAVAIL;
+ }
}
if (acquire_type == ACQ_NORMAL) {
diff --git a/proxy/src/mechglue/gpp_creds.c b/proxy/src/mechglue/gpp_creds.c
index dcfc896..ff1dfda 100644
--- a/proxy/src/mechglue/gpp_creds.c
+++ b/proxy/src/mechglue/gpp_creds.c
@@ -1,8 +1,125 @@
-/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */
+/* Copyright (C) 2015 the GSS-PROXY contributors, see COPYING for license */
#include "gss_plugin.h"
#include <gssapi/gssapi_krb5.h>
+#define GPKRB_SRV_NAME "Encrypted/Credentials/v1@X-GSSPROXY:"
+#define GPKRB_MAX_CRED_SIZE 1024 * 512
+
+uint32_t gpp_store_remote_creds(uint32_t *min, gssx_cred *creds)
+{
+ krb5_context ctx = NULL;
+ krb5_ccache ccache = NULL;
+ krb5_creds cred;
+ krb5_error_code ret;
+ XDR xdrctx;
+ bool xdrok;
+
+ *min = 0;
+
+ if (creds == NULL) return GSS_S_CALL_INACCESSIBLE_READ;
+
+ memset(&cred, 0, sizeof(cred));
+
+ ret = krb5_init_context(&ctx);
+ if (ret) return ret;
+
+ ret = krb5_cc_default(ctx, &ccache);
+ if (ret) goto done;
+
+ ret = krb5_parse_name(ctx,
+ creds->desired_name.display_name.octet_string_val,
+ &cred.client);
+ if (ret) goto done;
+
+ ret = krb5_parse_name(ctx, GPKRB_SRV_NAME, &cred.server);
+ if (ret) goto done;
+
+ 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) {
+ ret = ENOSPC;
+ goto done;
+ }
+ cred.ticket.length = xdr_getpos(&xdrctx);
+
+ ret = krb5_cc_store_cred(ctx, ccache, &cred);
+
+ if (ret == KRB5_FCC_NOFILE) {
+ /* If a ccache does not exit, try to create one */
+ ret = krb5_cc_initialize(ctx, ccache, cred.client);
+ if (ret) goto done;
+
+ /* and try again to store the cred */
+ ret = krb5_cc_store_cred(ctx, ccache, &cred);
+ }
+
+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;
+}
+
+static uint32_t retrieve_remote_creds(uint32_t *min, 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;
+
+ ret = krb5_cc_default(ctx, &ccache);
+ if (ret) goto done;
+
+ if (name) {
+ ret = krb5_parse_name(ctx,
+ name->display_name.octet_string_val,
+ &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,
@@ -66,11 +183,21 @@ OM_uint32 gppint_get_def_creds(OM_uint32 *minor_status,
/* Then try with remote */
if (behavior == GPP_REMOTE_ONLY || behavior == GPP_REMOTE_FIRST) {
+ gssx_cred remote = {0};
+ gssx_cred *premote = NULL;
- maj = gpm_acquire_cred(&min, NULL,
+ /* We intentionally ignore failures as finding creds is optional */
+ maj = retrieve_remote_creds(&min, 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);
+ xdr_free((xdrproc_t)xdr_gssx_cred, (char *)&remote);
+
if (maj == GSS_S_COMPLETE || behavior == GPP_REMOTE_ONLY) {
goto done;
}
@@ -379,16 +506,16 @@ OM_uint32 gssi_store_cred(OM_uint32 *minor_status,
}
cred = (struct gpp_cred_handle *)input_cred_handle;
- /* NOTE: For now we can do this only for local credentials */
- if (!cred->local) {
- return GSS_S_UNAVAILABLE;
+ if (cred->remote) {
+ maj = gpp_store_remote_creds(&min, cred->remote);
+ goto done;
}
maj = gss_store_cred(&min, cred->local, input_usage,
gpp_special_mech(desired_mech),
overwrite_cred, default_cred,
elements_stored, cred_usage_stored);
-
+done:
*minor_status = gpp_map_error(min);
return maj;
}
diff --git a/proxy/src/mechglue/gpp_init_sec_context.c b/proxy/src/mechglue/gpp_init_sec_context.c
index 6cfe0e1..2327b58 100644
--- a/proxy/src/mechglue/gpp_init_sec_context.c
+++ b/proxy/src/mechglue/gpp_init_sec_context.c
@@ -151,6 +151,21 @@ OM_uint32 gssi_init_sec_context(OM_uint32 *minor_status,
}
}
+ if (!cred_handle->remote) {
+ struct gpp_cred_handle *r_creds;
+
+ maj = gppint_get_def_creds(&min,
+ GPP_REMOTE_ONLY,
+ GSS_C_NO_NAME,
+ GSS_C_INITIATE,
+ &r_creds);
+ if (maj == GSS_S_COMPLETE) {
+ /* steal result */
+ cred_handle->remote = r_creds->remote;
+ free(r_creds);
+ }
+ }
+
maj = gpm_init_sec_context(&min,
cred_handle->remote,
&ctx_handle->remote,
diff --git a/proxy/src/mechglue/gss_plugin.h b/proxy/src/mechglue/gss_plugin.h
index 6df4c6a..d7ab0b4 100644
--- a/proxy/src/mechglue/gss_plugin.h
+++ b/proxy/src/mechglue/gss_plugin.h
@@ -69,6 +69,7 @@ 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);
+
OM_uint32 gssi_internal_release_oid(OM_uint32 *minor_status, gss_OID *oid);
OM_uint32 gssi_acquire_cred(OM_uint32 *minor_status,