From 84fcb276d2253f34e5d50a87e42e34c1fd918a55 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Dec 01 2015 22:45:56 +0000 Subject: 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 Reviewed-by: Robbie Harwoood --- 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 +#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,