| |
@@ -4,9 +4,122 @@
|
| |
#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,
|
| |
+ uint32_t gpp_cred_handle_init(uint32_t *min, bool defcred, const char *ccache,
|
| |
+ struct gpp_cred_handle **out_handle)
|
| |
+ {
|
| |
+ struct gpp_cred_handle *h = NULL;
|
| |
+ uint32_t maj = 0;
|
| |
+
|
| |
+ h = calloc(1, sizeof(struct gpp_cred_handle));
|
| |
+ if (!h) {
|
| |
+ *min = ENOMEM;
|
| |
+ return GSS_S_FAILURE;
|
| |
+ }
|
| |
+
|
| |
+ h->default_creds = defcred;
|
| |
+
|
| |
+ if (ccache) {
|
| |
+ h->store.elements = calloc(1, sizeof(gss_key_value_element_desc));
|
| |
+ if (!h->store.elements) {
|
| |
+ *min = ENOMEM;
|
| |
+ maj = GSS_S_FAILURE;
|
| |
+ goto done;
|
| |
+ }
|
| |
+ h->store.count = 1;
|
| |
+
|
| |
+ h->store.elements[0].key = strdup("ccache");
|
| |
+ if (!h->store.elements[0].key) {
|
| |
+ *min = ENOMEM;
|
| |
+ maj = GSS_S_FAILURE;
|
| |
+ goto done;
|
| |
+ }
|
| |
+
|
| |
+ h->store.elements[0].value = strdup(ccache);
|
| |
+ if (!h->store.elements[0].value) {
|
| |
+ *min = ENOMEM;
|
| |
+ maj = GSS_S_FAILURE;
|
| |
+ goto done;
|
| |
+ }
|
| |
+ }
|
| |
+
|
| |
+ done:
|
| |
+ if (maj) {
|
| |
+ uint32_t tmp;
|
| |
+ (void)gpp_cred_handle_free(&tmp, h);
|
| |
+ } else {
|
| |
+ *out_handle = h;
|
| |
+ }
|
| |
+ return maj;
|
| |
+ }
|
| |
+
|
| |
+ uint32_t gpp_cred_handle_free(uint32_t *min, struct gpp_cred_handle *handle)
|
| |
+ {
|
| |
+ uint32_t maj = GSS_S_COMPLETE;
|
| |
+
|
| |
+ *min = 0;
|
| |
+
|
| |
+ if (!handle) return GSS_S_COMPLETE;
|
| |
+
|
| |
+ if (handle->local) {
|
| |
+ maj = gss_release_cred(min, &handle->local);
|
| |
+ }
|
| |
+
|
| |
+ if (handle->remote) {
|
| |
+ xdr_free((xdrproc_t)xdr_gssx_cred, (char *)handle->remote);
|
| |
+ free(handle->remote);
|
| |
+ }
|
| |
+
|
| |
+ if (handle->store.count > 0) {
|
| |
+ for (size_t i = 0; i < handle->store.count; i++) {
|
| |
+ free((void *)handle->store.elements[i].key);
|
| |
+ free((void *)handle->store.elements[i].value);
|
| |
+ }
|
| |
+ free(handle->store.elements);
|
| |
+ handle->store.count = 0;
|
| |
+ }
|
| |
+
|
| |
+ free(handle);
|
| |
+ return maj;
|
| |
+ }
|
| |
+
|
| |
+ /* NOTE: currently the only things we check for are the cred name and the
|
| |
+ * cred_handle_reference, we do NOT check each cred element except for them to
|
| |
+ * match in number */
|
| |
+ bool gpp_creds_are_equal(gssx_cred *a, gssx_cred *b)
|
| |
+ {
|
| |
+ gssx_buffer *ta;
|
| |
+ gssx_buffer *tb;
|
| |
+
|
| |
+ if (!a && !b) return true;
|
| |
+ if (!a || !b) return false;
|
| |
+
|
| |
+ ta = &a->desired_name.display_name;
|
| |
+ tb = &b->desired_name.display_name;
|
| |
+ if (ta->octet_string_len != tb->octet_string_len) return false;
|
| |
+ if (!ta->octet_string_val && tb->octet_string_val) return false;
|
| |
+ if (ta->octet_string_val) {
|
| |
+ if (!tb->octet_string_val) return false;
|
| |
+ if (memcmp(ta->octet_string_val, tb->octet_string_val,
|
| |
+ ta->octet_string_len) != 0) return false;
|
| |
+ }
|
| |
+
|
| |
+ if (a->elements.elements_len != b->elements.elements_len) return false;
|
| |
+
|
| |
+ ta = &a->cred_handle_reference;
|
| |
+ tb = &b->cred_handle_reference;
|
| |
+ if (ta->octet_string_len != tb->octet_string_len) return false;
|
| |
+ if (!ta->octet_string_val && tb->octet_string_val) return false;
|
| |
+ if (ta->octet_string_val) {
|
| |
+ if (!tb->octet_string_val) return false;
|
| |
+ if (memcmp(ta->octet_string_val, tb->octet_string_val,
|
| |
+ ta->octet_string_len) != 0) return false;
|
| |
+ }
|
| |
+
|
| |
+ return true;
|
| |
+ }
|
| |
+
|
| |
+ uint32_t gpp_store_remote_creds(uint32_t *min, bool default_creds,
|
| |
gss_const_key_value_set_t cred_store,
|
| |
gssx_cred *creds)
|
| |
{
|
| |
@@ -36,6 +149,10 @@
|
| |
}
|
| |
}
|
| |
if (!ccache) {
|
| |
+ if (!default_creds) {
|
| |
+ ret = ENOMEDIUM;
|
| |
+ goto done;
|
| |
+ }
|
| |
ret = krb5_cc_default(ctx, &ccache);
|
| |
if (ret) goto done;
|
| |
}
|
| |
@@ -141,7 +258,7 @@
|
| |
static OM_uint32 get_local_def_creds(OM_uint32 *minor_status,
|
| |
struct gpp_name_handle *name,
|
| |
gss_cred_usage_t cred_usage,
|
| |
- struct gpp_cred_handle *cred_handle)
|
| |
+ gss_cred_id_t *cred_handle)
|
| |
{
|
| |
gss_OID_set interposed_mechs = GSS_C_NO_OID_SET;
|
| |
gss_OID_set special_mechs = GSS_C_NO_OID_SET;
|
| |
@@ -160,7 +277,7 @@
|
| |
}
|
| |
|
| |
maj = gss_acquire_cred(&min, name ? name->local : NULL, 0, special_mechs,
|
| |
- cred_usage, &cred_handle->local, NULL, NULL);
|
| |
+ cred_usage, cred_handle, NULL, NULL);
|
| |
done:
|
| |
*minor_status = min;
|
| |
(void)gss_release_oid_set(&min, &special_mechs);
|
| |
@@ -180,17 +297,21 @@
|
| |
OM_uint32 maj = GSS_S_FAILURE;
|
| |
OM_uint32 min = 0;
|
| |
|
| |
- cred = calloc(1, sizeof(struct gpp_cred_handle));
|
| |
- if (!cred) {
|
| |
- min = ENOMEM;
|
| |
- goto done;
|
| |
+ if (*cred_handle) {
|
| |
+ cred = *cred_handle;
|
| |
+ } else {
|
| |
+ maj = gpp_cred_handle_init(&min, true, NULL, &cred);
|
| |
+ if (maj) {
|
| |
+ *minor_status = min;
|
| |
+ return maj;
|
| |
+ }
|
| |
}
|
| |
|
| |
/* See if we should try local first */
|
| |
if (behavior == GPP_LOCAL_ONLY || behavior == GPP_LOCAL_FIRST) {
|
| |
|
| |
- maj = get_local_def_creds(&min, name, cred_usage, cred);
|
| |
- if (maj != GSS_S_NO_CRED || behavior != GPP_LOCAL_FIRST) {
|
| |
+ maj = get_local_def_creds(&min, name, cred_usage, &cred->local);
|
| |
+ if (maj == GSS_S_COMPLETE || behavior == GPP_LOCAL_ONLY) {
|
| |
goto done;
|
| |
}
|
| |
|
| |
@@ -200,7 +321,7 @@
|
| |
}
|
| |
|
| |
/* Then try with remote */
|
| |
- if (behavior == GPP_REMOTE_ONLY || behavior == GPP_REMOTE_FIRST) {
|
| |
+ if (behavior != GPP_LOCAL_ONLY) {
|
| |
gssx_cred remote;
|
| |
gssx_cred *premote = NULL;
|
| |
|
| |
@@ -217,15 +338,24 @@
|
| |
maj = gpm_acquire_cred(&min, premote,
|
| |
NULL, 0, NULL, cred_usage, false,
|
| |
&cred->remote, NULL, NULL);
|
| |
+ if (maj == GSS_S_COMPLETE) {
|
| |
+ if (premote &&
|
| |
+ !gpp_creds_are_equal(premote, cred->remote)) {
|
| |
+ maj = gpp_store_remote_creds(&min, cred->default_creds,
|
| |
+ &cred->store, cred->remote);
|
| |
+ }
|
| |
+ }
|
| |
|
| |
xdr_free((xdrproc_t)xdr_gssx_cred, (char *)&remote);
|
| |
|
| |
- if (maj == GSS_S_COMPLETE || behavior == GPP_REMOTE_ONLY) {
|
| |
+ if (maj == GSS_S_COMPLETE) {
|
| |
goto done;
|
| |
}
|
| |
|
| |
- /* So remote failed, but we can fallback to local, try that */
|
| |
- maj = get_local_def_creds(&min, name, cred_usage, cred);
|
| |
+ if (behavior == GPP_REMOTE_FIRST) {
|
| |
+ /* So remote failed, but we can fallback to local, try that */
|
| |
+ maj = get_local_def_creds(&min, name, cred_usage, &cred->local);
|
| |
+ }
|
| |
}
|
| |
|
| |
done:
|
| |
@@ -235,7 +365,9 @@
|
| |
}
|
| |
*minor_status = min;
|
| |
if (maj != GSS_S_COMPLETE) {
|
| |
- gssi_release_cred(&min, (gss_cred_id_t *)&cred);
|
| |
+ if (cred != *cred_handle) {
|
| |
+ gssi_release_cred(&min, (gss_cred_id_t *)&cred);
|
| |
+ }
|
| |
}
|
| |
*cred_handle = cred;
|
| |
return maj;
|
| |
@@ -544,7 +676,8 @@
|
| |
cred = (struct gpp_cred_handle *)input_cred_handle;
|
| |
|
| |
if (cred->remote) {
|
| |
- maj = gpp_store_remote_creds(&min, cred_store, cred->remote);
|
| |
+ maj = gpp_store_remote_creds(&min, default_cred != 0, cred_store,
|
| |
+ cred->remote);
|
| |
goto done;
|
| |
}
|
| |
|
| |
@@ -560,40 +693,29 @@
|
| |
OM_uint32 gssi_release_cred(OM_uint32 *minor_status,
|
| |
gss_cred_id_t *cred_handle)
|
| |
{
|
| |
- struct gpp_cred_handle *cred;
|
| |
- OM_uint32 maj, min;
|
| |
- OM_uint32 rmaj = GSS_S_COMPLETE;
|
| |
+ struct gpp_cred_handle *handle;
|
| |
+ uint32_t tmaj;
|
| |
+ uint32_t tmin;
|
| |
+ uint32_t maj;
|
| |
+ uint32_t min;
|
| |
|
| |
GSSI_TRACE();
|
| |
|
| |
- if (cred_handle == NULL) {
|
| |
- return GSS_S_NO_CRED | GSS_S_CALL_INACCESSIBLE_READ;
|
| |
- } else if (*cred_handle == GSS_C_NO_CREDENTIAL) {
|
| |
- *minor_status = 0;
|
| |
- return GSS_S_COMPLETE;
|
| |
- }
|
| |
+ if (cred_handle == NULL) return GSS_S_CALL_INACCESSIBLE_READ;
|
| |
|
| |
- cred = (struct gpp_cred_handle *)*cred_handle;
|
| |
+ handle = (struct gpp_cred_handle *)*cred_handle;
|
| |
|
| |
- if (cred->local) {
|
| |
- maj = gss_release_cred(&min, &cred->local);
|
| |
- if (maj != GSS_S_COMPLETE) {
|
| |
- rmaj = maj;
|
| |
- *minor_status = gpp_map_error(min);
|
| |
- }
|
| |
- }
|
| |
+ tmaj = gpm_release_cred(&tmin, &handle->remote);
|
| |
|
| |
- if (cred->remote) {
|
| |
- maj = gpm_release_cred(&min, &cred->remote);
|
| |
- if (maj && rmaj == GSS_S_COMPLETE) {
|
| |
- rmaj = maj;
|
| |
- *minor_status = gpp_map_error(min);
|
| |
- }
|
| |
+ maj = gpp_cred_handle_free(&min, handle);
|
| |
+ if (tmaj && maj == GSS_S_COMPLETE) {
|
| |
+ maj = tmaj;
|
| |
+ min = tmin;
|
| |
}
|
| |
|
| |
- free(cred);
|
| |
*cred_handle = GSS_C_NO_CREDENTIAL;
|
| |
- return rmaj;
|
| |
+ *minor_status = min;
|
| |
+ return maj;
|
| |
}
|
| |
|
| |
OM_uint32 gssi_export_cred(OM_uint32 *minor_status,
|
| |
@@ -639,11 +761,8 @@
|
| |
|
| |
GSSI_TRACE();
|
| |
|
| |
- cred = calloc(1, sizeof(struct gpp_cred_handle));
|
| |
- if (!cred) {
|
| |
- *minor_status = 0;
|
| |
- return GSS_S_FAILURE;
|
| |
- }
|
| |
+ maj = gpp_cred_handle_init(minor_status, false, NULL, &cred);
|
| |
+ if (maj) return maj;
|
| |
|
| |
/* NOTE: it makes no sense to import a cred remotely atm,
|
| |
* so we only handle the local case for now. */
|
| |
@@ -680,4 +799,3 @@
|
| |
(void)gss_release_buffer(&min, &wrap_token);
|
| |
return maj;
|
| |
}
|
| |
-
|
| |
With the introduction of the in memory ccache for gssproxy we gain the fact that we do not litter the filesystem with ccaches and we have a better security proposition in that we do not risk ccaches mixupo, however we lost the ability to cache anything but the initial ticket/TGT because we never returned modified credential caches back to the client application.
This patcheset implements the ability to synchronize back to the client app in 2 cases. On credential acquisistion, in case credentials are renewed for example, and on context init, to account for new tickets and cache them back on the client side.
Some preparatory and general fixing patches for issues discovered during this work are included as the first commits, and some tests that were incorrectly depending on non-updated ccaches have been fixed as the last commit.