#162 Implement credentials synchronization back to the client
Merged 6 years ago by simo. Opened 7 years ago by simo.
simo/gssproxy refresh_creds  into  master

@@ -126,6 +126,20 @@ 

                  </varlistentry>

  

                  <varlistentry>

+                     <term>allow_client_ccache_sync (boolean)</term>

+                     <listitem>

+                         <para>Allow clients to request credentials to be sent back for better

+                               caching.</para>

+                         <para>This option allows the proxy, in certain circumstances, to send back

+                               an additional option in the response structure of certain calls when

+                               it determines that a new ticket may have been added to the internal

+                               ccache. Clients can then replace their (encrypted) copy with the

+                               updated ccache.</para>

+                         <para>Default: false</para>

+                     </listitem>

+                 </varlistentry>

+ 

+                 <varlistentry>

                      <term>cred_usage (string)</term>

                      <listitem>

                          <para>Allow to restrict the kind of operations permitted for this service.</para>

@@ -90,29 +90,16 @@ 

  

      /* impersonate calls use input cred and a special option */

      if (impersonate) {

-         gssx_option *opt;

-         arg->options.options_val = calloc(1, sizeof(gssx_option));

-         if (!arg->options.options_val) {

+         ret_min = gp_add_option(&arg->options.options_val,

+                                 &arg->options.options_len,

+                                 ACQUIRE_TYPE_OPTION,

+                                 sizeof(ACQUIRE_TYPE_OPTION),

+                                 ACQUIRE_IMPERSONATE_NAME,

+                                 sizeof(ACQUIRE_IMPERSONATE_NAME));

+         if (ret_min) {

              ret_maj = GSS_S_FAILURE;

-             ret_min = ENOMEM;

              goto done;

          }

-         arg->options.options_len = 1;

-         opt = &arg->options.options_val[0];

-         opt->option.octet_string_val = strdup(ACQUIRE_TYPE_OPTION);

-         if (!opt->option.octet_string_val) {

-             ret_maj = GSS_S_FAILURE;

-             ret_min = ENOMEM;

-             goto done;

-         }

-         opt->option.octet_string_len = sizeof(ACQUIRE_TYPE_OPTION);

-         opt->value.octet_string_val = strdup(ACQUIRE_IMPERSONATE_NAME);

-         if (!opt->value.octet_string_val) {

-             ret_maj = GSS_S_FAILURE;

-             ret_min = ENOMEM;

-             goto done;

-         }

-         opt->value.octet_string_len = sizeof(ACQUIRE_IMPERSONATE_NAME);

      }

  

      /* execute proxy request */

@@ -3,6 +3,26 @@ 

  #include "gssapi_gpm.h"

  #include "src/gp_conv.h"

  

+ static void return_new_cred_handle(struct gssx_option *val,

+                                    gssx_cred **out_cred_handle)

+ {

+     gssx_cred *creds;

+     XDR xdrctx;

+     bool xdrok;

+ 

+     creds = calloc(1, sizeof(*creds));

+     if (creds) {

+         xdrmem_create(&xdrctx, val->value.octet_string_val,

+                       val->value.octet_string_len, XDR_DECODE);

+         xdrok = xdr_gssx_cred(&xdrctx, creds);

+         if (xdrok) {

+             *out_cred_handle = creds;

+         } else {

+             free(creds);

+         }

+     }

+ }

+ 

  OM_uint32 gpm_init_sec_context(OM_uint32 *minor_status,

                                 gssx_cred *cred_handle,

                                 gssx_ctx **context_handle,
@@ -15,7 +35,8 @@ 

                                 gss_OID *actual_mech_type,

                                 gss_buffer_t output_token,

                                 OM_uint32 *ret_flags,

-                                OM_uint32 *time_rec)

+                                OM_uint32 *time_rec,

+                                gssx_cred **out_cred_handle)

  {

      union gp_rpc_arg uarg;

      union gp_rpc_res ures;
@@ -40,6 +61,12 @@ 

          arg->context_handle = *context_handle;

      }

  

+     /* always try request cred sync, ignore errors, not critical */

+     (void)gp_add_option(&arg->options.options_val,

+                         &arg->options.options_len,

+                         CRED_SYNC_OPTION, sizeof(CRED_SYNC_OPTION),

+                         CRED_SYNC_DEFAULT, sizeof(CRED_SYNC_DEFAULT));

+ 

      arg->target_name = target_name;

  

      ret = gp_conv_oid_to_gssx(mech_type, &arg->mech_type);
@@ -96,6 +123,16 @@ 

          }

      }

  

+     /* check if a sync cred was returned to us, don't fail on errors */

+     if (out_cred_handle && res->options.options_len > 0) {

+         struct gssx_option *val = NULL;

+         gp_options_find(val, res->options, CRED_SYNC_PAYLOAD,

+                         sizeof(CRED_SYNC_PAYLOAD));

+         if (val) {

+             return_new_cred_handle(val, out_cred_handle);

+         }

+     }

+ 

      ret_maj = res->status.major_status;

      ret_min = res->status.minor_status;

      gpm_save_status(&res->status);

@@ -51,6 +51,8 @@ 

      gpm_free_xdrs(GSSX_RELEASE_HANDLE, &uarg, &ures);

  done:

      xdr_free((xdrproc_t)xdr_gssx_cred, (char *)r);

+     free(r);

+     *cred_handle = NULL;

      return ret;

  }

  

@@ -158,7 +158,8 @@ 

                                 gss_OID *actual_mech_type,

                                 gss_buffer_t output_token,

                                 OM_uint32 *ret_flags,

-                                OM_uint32 *time_rec);

+                                OM_uint32 *time_rec,

+                                gssx_cred **out_cred_handle);

  OM_uint32 gpm_inquire_context(OM_uint32 *minor_status,

                                gssx_ctx *context_handle,

                                gssx_name **src_name,

file modified
+9
@@ -116,5 +116,14 @@ 

  

  #define ACQUIRE_TYPE_OPTION         "acquire_type"

  #define ACQUIRE_IMPERSONATE_NAME    "impersonate_name"

+ #define CRED_SYNC_OPTION "sync_modified_creds"

+ #define CRED_SYNC_DEFAULT "default"

+ #define CRED_SYNC_PAYLOAD "sync_creds"

+ 

+ #define GPKRB_MAX_CRED_SIZE 1024 * 512

+ 

+ uint32_t gp_add_option(gssx_option **options_val, u_int *options_len,

+                        const void *option, size_t option_len,

+                        const void *value, size_t value_len);

  

  #endif /* _GP_COMMON_H_ */

file modified
+8
@@ -387,6 +387,14 @@ 

                  }

              }

  

+             ret = gp_config_get_string(ctx, secname,

+                                        "allow_client_ccache_sync", &value);

+             if (ret == 0) {

+                 if (gp_boolean_is_true(value)) {

+                     cfg->svcs[n]->allow_cc_sync = true;

+                 }

+             }

+ 

              ret = gp_config_get_string(ctx, secname, "trusted", &value);

              if (ret == 0) {

                  if (gp_boolean_is_true(value)) {

file modified
+162
@@ -15,6 +15,7 @@ 

  #include "gp_rpc_creds.h"

  #include "gp_creds.h"

  #include "gp_conv.h"

+ #include "gp_export.h"

  

  #define GSS_MECH_KRB5_OID_LENGTH 9

  #define GSS_MECH_KRB5_OID "\052\206\110\206\367\022\001\002\002"
@@ -871,3 +872,164 @@ 

      *min = ret_min;

      return ret_maj;

  }

+ 

+ uint32_t gp_count_tickets(uint32_t *min, gss_cred_id_t cred, uint32_t *ccsum)

+ {

+     uint32_t ret_maj = 0;

+     uint32_t ret_min = 0;

+     char *memcache = NULL;

+     krb5_context context = NULL;

+     krb5_ccache ccache = NULL;

+     krb5_cc_cursor cursor = NULL;

+     krb5_creds creds;

+     int err;

+ 

+     err = krb5_init_context(&context);

+     if (err) {

+         ret_min = err;

+         ret_maj =  GSS_S_FAILURE;

+         goto done;

+     }

+ 

+     /* Create a memory ccache we can iterate with libkrb5 functions */

+     gss_key_value_element_desc ccelement = { "ccache", NULL };

+     gss_key_value_set_desc cred_store = { 1, &ccelement };

+ 

+     err = asprintf(&memcache, "MEMORY:cred_allowed_%p", &memcache);

+     if (err == -1) {

+         memcache = NULL;

+         ret_min = ENOMEM;

+         ret_maj = GSS_S_FAILURE;

+         goto done;

+     }

+     cred_store.elements[0].value = memcache;

+ 

+     ret_maj = gss_store_cred_into(&ret_min, cred, GSS_C_INITIATE,

+                                   discard_const(gss_mech_krb5), 1, 0,

+                                   &cred_store, NULL, NULL);

+     if (ret_maj != GSS_S_COMPLETE) {

+         goto done;

+     }

+ 

+     err = krb5_cc_resolve(context, memcache, &ccache);

+     if (err) {

+         ret_min = err;

+         ret_maj = GSS_S_FAILURE;

+         goto done;

+     }

+ 

+     err = krb5_cc_start_seq_get(context, ccache, &cursor);

+     if (err) {

+         ret_min = err;

+         ret_maj = GSS_S_FAILURE;

+         goto done;

+     }

+ 

+     do {

+         err = krb5_cc_next_cred(context, ccache, &cursor, &creds);

+         if (err && err != KRB5_CC_END) {

+             ret_min = err;

+             ret_maj = GSS_S_FAILURE;

+             goto done;

+         }

+ 

+         /* TODO: Should we ch real ecksum over all creds->ticket data and

+          * flags in future ? */

+         (*ccsum)++;

+ 

+     } while(err == 0);

+ 

+     err = krb5_cc_end_seq_get(context, ccache, &cursor);

+     if (err) {

+         ret_min = err;

+         ret_maj = GSS_S_FAILURE;

+         goto done;

+     }

+ 

+ done:

+     if (context) {

+         /* NOTE: destroy only if we created a MEMORY ccache */

+         if (ccache) {

+             if (memcache) krb5_cc_destroy(context, ccache);

+             else krb5_cc_close(context, ccache);

+         }

+         krb5_free_context(context);

+     }

+     free(memcache);

+     *min = ret_min;

+     return ret_maj;

+ }

+ 

+ /* Check if cred refresh is being requested by the client.

+  * if so, take a snapshot of the cred so that later we can check if anything

+  * was added */

+ 

+ uint32_t gp_check_sync_creds(struct gp_cred_check_handle *h,

+                              gss_cred_id_t cred)

+ {

+     uint32_t ret_maj = 0;

+     uint32_t ret_min = 0;

+     struct gp_service *svc = h->ctx->service;

+     struct gssx_option *opt = NULL;

+     uint32_t ccsum = 0;

+ 

+     if (!svc->allow_cc_sync) return 0;

+ 

+     gp_options_find(opt, h->options, CRED_SYNC_OPTION,

+                     sizeof(CRED_SYNC_OPTION));

+     if (!opt) return 0;

+     if (!gpopt_string_match(&opt->value, CRED_SYNC_DEFAULT,

+                             sizeof(CRED_SYNC_DEFAULT))) return 0;

+ 

+     for (size_t i = 0; i < svc->krb5.store.count; i++) {

+         if (strcmp(svc->krb5.store.elements[i].key, "ccache") == 0) {

+             /* Saving in local ccache no need to sync up to client */

+             return 0;

+         }

+     }

+ 

+     ret_maj = gp_count_tickets(&ret_min, cred, &ccsum);

+     if (ret_maj) return 0;

+ 

+     return ccsum;

+ }

+ 

+ uint32_t gp_export_sync_creds(uint32_t *min, struct gp_call_ctx *gpcall,

+                               gss_cred_id_t *cred,

+                               gssx_option **options_val, u_int *options_len)

+ {

+     uint32_t ret_maj = 0;

+     uint32_t ret_min = 0;

+     gssx_cred creds = { 0 };

+     char value[GPKRB_MAX_CRED_SIZE];

+     size_t len;

+     XDR xdrctx;

+     bool xdrok;

+ 

+     ret_maj = gp_export_gssx_cred(&ret_min, gpcall, cred, &creds);

+     if (ret_maj) goto done;

+ 

+     xdrmem_create(&xdrctx, value, GPKRB_MAX_CRED_SIZE, XDR_ENCODE);

+     xdrok = xdr_gssx_cred(&xdrctx, &creds);

+     if (!xdrok) {

+         ret_min = ENOSPC;

+         ret_maj = GSS_S_FAILURE;

+         goto done;

+     }

+     len = xdr_getpos(&xdrctx);

+ 

+     ret_min = gp_add_option(options_val, options_len, CRED_SYNC_PAYLOAD,

+                             sizeof(CRED_SYNC_PAYLOAD), value, len);

+     if (ret_min) {

+         ret_maj = GSS_S_FAILURE;

+         goto done;

+     }

+ 

+     ret_min = 0;

+     ret_maj = GSS_S_COMPLETE;

+ 

+ done:

+     xdr_free((xdrproc_t)xdr_gssx_cred, (char *)&creds);

+     *min = ret_min;

+     return ret_maj;

+ }

file modified
+1
@@ -32,6 +32,7 @@ 

      bool any_uid;

      bool allow_proto_trans;

      bool allow_const_deleg;

+     bool allow_cc_sync;

      bool trusted;

      bool kernel_nfsd;

      bool impersonate;

@@ -137,10 +137,16 @@ 

          ret_min = ENOMEM;

          goto done;

      }

-     ret_maj = gp_export_gssx_cred(&ret_min, gpcall,

-                                   &out_cred, acr->output_cred_handle);

-     if (ret_maj) {

-         goto done;

+ 

+     if (out_cred == in_cred) {

+         acr->output_cred_handle = aca->input_cred_handle;

+         aca->input_cred_handle = NULL;

+     } else {

+         ret_maj = gp_export_gssx_cred(&ret_min, gpcall,

+                                       &out_cred, acr->output_cred_handle);

+         if (ret_maj) {

+             goto done;

+         }

      }

  

  done:

file modified
+13
@@ -38,4 +38,17 @@ 

  

  void gp_filter_flags(struct gp_call_ctx *gpcall, uint32_t *flags);

  

+ struct gp_cred_check_handle {

+     struct gp_call_ctx *ctx;

+     struct {

+         u_int options_len;

+         gssx_option *options_val;

+     } options;

+ };

+ uint32_t gp_check_sync_creds(struct gp_cred_check_handle *h,

+                              gss_cred_id_t cred);

+ uint32_t gp_export_sync_creds(uint32_t *min, struct gp_call_ctx *gpcall,

+                               gss_cred_id_t *cred,

+                               gssx_option **options_val, u_int *options_len);

+ 

  #endif /* _GP_RPC_CREDS_H_ */

@@ -26,6 +26,13 @@ 

      uint32_t init_maj;

      uint32_t init_min;

      int exp_ctx_type;

+     struct gp_cred_check_handle gcch = {

+         .ctx = gpcall,

+         .options.options_len = arg->init_sec_context.options.options_len,

+         .options.options_val = arg->init_sec_context.options.options_val,

+     };

+     uint32_t gccn_before = 0;

+     uint32_t gccn_after = 0;

      int ret;

  

      isca = &arg->init_sec_context;
@@ -54,6 +61,8 @@ 

          if (ret_maj) {

              goto done;

          }

+ 

+         gccn_before = gp_check_sync_creds(&gcch, ich);

      }

  

      ret_maj = gp_conv_gssx_to_name(&ret_min, isca->target_name, &target_name);
@@ -157,6 +166,20 @@ 

          }

      }

  

+     gccn_after = gp_check_sync_creds(&gcch, ich);

+ 

+     if (gccn_before != gccn_after) {

+         /* export creds back to client for sync up */

+         ret_maj = gp_export_sync_creds(&ret_min, gpcall, &ich,

+                                        &iscr->options.options_val,

+                                        &iscr->options.options_len);

+         if (ret_maj) {

+             /* not fatal, log and continue */

+             GPDEBUG("Failed to export sync creds (%d: %d)",

+                     (int)ret_maj, (int)ret_min);

+         }

+     }

+ 

      ret_maj = GSS_S_COMPLETE;

  

  done:

file modified
+43
@@ -143,3 +143,46 @@ 

  

      return len;

  }

+ 

+ uint32_t gp_add_option(gssx_option **options_val, u_int *options_len,

+                        const void *option, size_t option_len,

+                        const void *value, size_t value_len)

+ {

+     gssx_option opt = { 0 };

+     gssx_option *out;

+     uint32_t ret;

+ 

+     opt.option.octet_string_val = malloc(option_len);

+     if (!opt.option.octet_string_val) {

+         ret = ENOMEM;

+         goto done;

+     }

+     memcpy(opt.option.octet_string_val, option, option_len);

+     opt.option.octet_string_len = option_len;

+ 

+     opt.value.octet_string_val = malloc(value_len);

+     if (!opt.value.octet_string_val) {

+         ret = ENOMEM;

+         goto done;

+     }

+     memcpy(opt.value.octet_string_val, value, value_len);

+     opt.value.octet_string_len = value_len;

+ 

+     out = realloc(*options_val, (*options_len + 1) * sizeof(gssx_option));

+     if (!out) {

+         ret = ENOMEM;

+         goto done;

+     }

+ 

+     out[*options_len] = opt;

+     *options_val = out;

+     (*options_len)++;

+ 

+     ret = 0;

+ 

+ done:

+     if (ret) {

+         xdr_free((xdrproc_t)xdr_gssx_option, (char *)&opt);

+     }

+     return ret;

+ }

@@ -79,12 +79,8 @@ 

      }

  

      if (delegated_cred_handle) {

-         deleg_cred = calloc(1, sizeof(struct gpp_cred_handle));

-         if (!deleg_cred) {

-             maj = GSS_S_FAILURE;

-             min = ENOMEM;

-             goto done;

-         }

+         maj = gpp_cred_handle_init(&min, false, NULL, &deleg_cred);

+         if (!maj) goto done;

      }

  

      /* behavior has been set to local only or remote only by context or

@@ -102,13 +102,6 @@ 

      tmaj = GSS_S_COMPLETE;

      tmin = 0;

  

-     out_cred_handle = calloc(1, sizeof(struct gpp_cred_handle));

-     if (!out_cred_handle) {

-         maj = GSS_S_FAILURE;

-         min = ENOMEM;

-         goto done;

-     }

- 

      name = (struct gpp_name_handle *)desired_name;

      behavior = gpp_get_behavior();

  
@@ -120,20 +113,26 @@ 

          }

      }

  

-     in_cred_remote = calloc(1, sizeof(gssx_cred));

-     if (!in_cred_remote) {

-         maj = GSS_S_FAILURE;

-         min = ENOMEM;

-         goto done;

-     }

-     maj = gppint_retrieve_remote_creds(&min, ccache_name, NULL,

-                                        in_cred_remote);

-     if (maj == GSS_S_COMPLETE) {

-         behavior = GPP_REMOTE_ONLY;

-     } else {

-         safefree(in_cred_remote);

-         if (ccache_name) {

-             behavior = GPP_LOCAL_ONLY;

+     maj = gpp_cred_handle_init(&min, !ccache_name, ccache_name,

+                                &out_cred_handle);

+     if (maj) goto done;

+ 

+     if (behavior != GPP_LOCAL_ONLY) {

+         in_cred_remote = calloc(1, sizeof(gssx_cred));

+         if (!in_cred_remote) {

+             maj = GSS_S_FAILURE;

+             min = ENOMEM;

+             goto done;

+         }

+         maj = gppint_retrieve_remote_creds(&min, ccache_name, NULL,

+                                            in_cred_remote);

+         if (maj == GSS_S_COMPLETE) {

+             behavior = GPP_REMOTE_FIRST;

+         } else {

+             safefree(in_cred_remote);

+             if (ccache_name) {

+                 behavior = GPP_LOCAL_FIRST;

+             }

          }

      }

  
@@ -169,7 +168,18 @@ 

                             &out_cred_handle->remote,

                             actual_mechs,

                             time_rec);

-     if (maj == GSS_S_COMPLETE || behavior == GPP_REMOTE_ONLY) {

+     if (maj == GSS_S_COMPLETE) {

+         /* store back creds if they changed */

+         if (out_cred_handle->remote &&

+             !gpp_creds_are_equal(in_cred_remote, out_cred_handle->remote)) {

+             tmaj = gpp_store_remote_creds(&tmin,

+                                           out_cred_handle->default_creds,

+                                           &out_cred_handle->store,

+                                           out_cred_handle->remote);

+             if (tmaj != GSS_S_COMPLETE) {

+                 maj = tmaj;

+             }

+         }

          goto done;

      }

  
@@ -194,7 +204,7 @@ 

      if (maj == GSS_S_COMPLETE) {

          *output_cred_handle = (gss_cred_id_t)out_cred_handle;

      } else {

-         free(out_cred_handle);

+         (void)gpp_cred_handle_free(&tmin, out_cred_handle);

      }

      *minor_status = gpp_map_error(min);

      return maj;
@@ -327,10 +337,10 @@ 

  

      behavior = gpp_get_behavior();

  

-     out_cred_handle = calloc(1, sizeof(struct gpp_cred_handle));

-     if (!out_cred_handle) {

-         *minor_status = gpp_map_error(ENOMEM);

-         return GSS_S_FAILURE;

+     maj = gpp_cred_handle_init(&min, false, NULL, &out_cred_handle);

+     if (maj) {

+         *minor_status = gpp_map_error(min);

+         return maj;

      }

  

      switch (behavior) {
@@ -424,12 +434,8 @@ 

      tmaj = GSS_S_COMPLETE;

      tmin = 0;

  

-     out_cred_handle = calloc(1, sizeof(struct gpp_cred_handle));

-     if (!out_cred_handle) {

-         maj = GSS_S_FAILURE;

-         min = ENOMEM;

-         goto done;

-     }

+     maj = gpp_cred_handle_init(&min, false, NULL, &out_cred_handle);

+     if (maj) goto done;

  

      name = (struct gpp_name_handle *)desired_name;

      behavior = gpp_get_behavior();

file modified
+165 -47
@@ -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;

  }

- 

@@ -110,12 +110,8 @@ 

              goto done;

          }

      } else {

-         cred_handle =  calloc(1, sizeof(struct gpp_cred_handle));

-         if (!cred_handle) {

-             maj = GSS_S_FAILURE;

-             min = ENOMEM;

-             goto done;

-         }

+         maj = gpp_cred_handle_init(&min, true, NULL, &cred_handle);

+         if (maj) goto done;

      }

  

      name = (struct gpp_name_handle *)target_name;
@@ -143,6 +139,7 @@ 

  

      /* Then try with remote */

      if (behavior != GPP_LOCAL_ONLY) {

+         gssx_cred *out_cred = NULL;

  

          if (name->local && !name->remote) {

              maj = gpp_local_to_name(&min, name->local, &name->remote);
@@ -152,18 +149,9 @@ 

          }

  

          if (!cred_handle->remote) {

-             struct gpp_cred_handle *r_creds;

- 

-             maj = gppint_get_def_creds(&min,

-                                        GPP_REMOTE_ONLY,

-                                        NULL,

-                                        GSS_C_INITIATE,

-                                        &r_creds);

-             if (maj == GSS_S_COMPLETE) {

-                 /* steal result */

-                 cred_handle->remote = r_creds->remote;

-                 free(r_creds);

-             }

+             /* we ignore failures here */

+             (void)gppint_get_def_creds(&min, GPP_REMOTE_ONLY, NULL,

+                                        GSS_C_INITIATE, &cred_handle);

          }

  

          maj = gpm_init_sec_context(&min,
@@ -178,17 +166,31 @@ 

                                     actual_mech_type,

                                     output_token,

                                     ret_flags,

-                                    time_rec);

-         if (maj == GSS_S_COMPLETE || maj == GSS_S_CONTINUE_NEEDED ||

-             behavior == GPP_REMOTE_ONLY) {

+                                    time_rec,

+                                    &out_cred);

+         if (maj == GSS_S_COMPLETE || maj == GSS_S_CONTINUE_NEEDED) {

+             if (out_cred) {

+                 xdr_free((xdrproc_t)xdr_gssx_cred,

+                          (char *)cred_handle->remote);

+                 free(cred_handle->remote);

+                 cred_handle->remote = out_cred;

+                 out_cred = NULL;

+                 /* failuire is not fatal */

+                 (void)gpp_store_remote_creds(&tmin,

+                                              cred_handle->default_creds,

+                                              &cred_handle->store,

+                                              cred_handle->remote);

+             }

              goto done;

          }

  

-         /* So remote failed, but we can fallback to local, try that */

-         maj = init_ctx_local(&min, cred_handle, ctx_handle, name,

-                              mech_type, req_flags, time_req, input_cb,

-                              input_token, actual_mech_type, output_token,

-                              ret_flags, time_rec);

+         if (behavior == GPP_REMOTE_FIRST) {

+             /* So remote failed, but we can fallback to local, try that */

+             maj = init_ctx_local(&min, cred_handle, ctx_handle, name,

+                                  mech_type, req_flags, time_req, input_cb,

+                                  input_token, actual_mech_type, output_token,

+                                  ret_flags, time_rec);

+         }

      }

  

  done:

@@ -7,9 +7,12 @@ 

  

  struct gpp_cred_handle {

      gssx_cred *remote;

+     gss_key_value_set_desc store;

+     bool default_creds;

      gss_cred_id_t local;

  };

  

+ 

  struct gpp_context_handle {

      gssx_ctx *remote;

      gss_ctx_id_t local;
@@ -69,6 +72,13 @@ 

                             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);

+ uint32_t gpp_cred_handle_init(uint32_t *min, bool defcred, const char *ccache,

+                               struct gpp_cred_handle **out_handle);

+ uint32_t gpp_cred_handle_free(uint32_t *min, struct gpp_cred_handle *handle);

+ bool gpp_creds_are_equal(gssx_cred *a, gssx_cred *b);

+ uint32_t gpp_store_remote_creds(uint32_t *min, bool default_creds,

+                                 gss_const_key_value_set_t cred_store,

+                                 gssx_cred *creds);

  

  OM_uint32 gssi_internal_release_oid(OM_uint32 *minor_status, gss_OID *oid);

  

@@ -154,6 +154,7 @@ 

                                         NULL,

                                         &out_token,

                                         NULL,

+                                        NULL,

                                         NULL);

          if (ret_maj != GSS_S_COMPLETE &&

              ret_maj != GSS_S_CONTINUE_NEEDED) {

file modified
+20 -27
@@ -22,9 +22,10 @@ 

      int ret = -1;

      bool selfhalf = false;

      bool proxyhalf = false;

-     const char *deleg_ccache = NULL;

+     gss_key_value_element_desc ccelement = { "ccache", NULL };

+     gss_key_value_set_desc cred_store = { 1, &ccelement };

  

-     if (argc < 4) return -1;

+     if (argc < 5) return -1;

  

      ret = t_string_to_name(argv[1], &user_name, GSS_C_NT_USER_NAME);

      if (ret) {
@@ -49,29 +50,22 @@ 

          goto done;

      }

  

-     if (argc > 4) {

-         if (strcmp(argv[4], "s4u2self") == 0) {

+     ccelement.value = argv[4];

+ 

+     if (argc > 5) {

+         if (strcmp(argv[5], "s4u2self") == 0) {

              selfhalf = true;

-         } else if (strcmp(argv[4], "s4u2proxy") == 0) {

+         } else if (strcmp(argv[5], "s4u2proxy") == 0) {

              proxyhalf = true;

          } else {

-             DEBUG("Invalid argument 4: %s\n", argv[4]);

-             ret = -1;

-             goto done;

-         }

-         if (argc < 6) {

-             DEBUG("Option %s requires additional arguments\n", argv[4]);

+             DEBUG("Invalid argument 5: %s\n", argv[5]);

              ret = -1;

              goto done;

          }

-         deleg_ccache = argv[5];

-         DEBUG("S4U2%s half [ccache %s]\n", selfhalf?"Self":"Proxy", argv[5]);

+         DEBUG("S4U2%s half\n", selfhalf?"Self":"Proxy", argv[5]);

      }

  

      if (proxyhalf) {

-         gss_key_value_element_desc ccelement = { "ccache", deleg_ccache };

-         gss_key_value_set_desc cred_store = { 1, &ccelement };

- 

          ret_maj = gss_acquire_cred_from(&ret_min,

                                          user_name,

                                          GSS_C_INDEFINITE,
@@ -90,15 +84,17 @@ 

          flags = GSS_C_MUTUAL_FLAG;

      } else {

  

-         ret_maj = gss_acquire_cred(&ret_min,

-                                    proxy_name,

-                                    GSS_C_INDEFINITE,

-                                    &oid_set,

-                                    GSS_C_BOTH,

-                                    &impersonator_cred_handle,

-                                    NULL, NULL);

+         ret_maj = gss_acquire_cred_from(&ret_min,

+                                         proxy_name,

+                                         GSS_C_INDEFINITE,

+                                         &oid_set,

+                                         GSS_C_BOTH,

+                                         &cred_store,

+                                         &impersonator_cred_handle,

+                                         NULL, NULL);

          if (ret_maj != GSS_S_COMPLETE) {

-             DEBUG("gss_acquire_cred() failed\n");

+             DEBUG("gss_acquire_cred_from() [%s] failed\n",

+                   selfhalf ? "s4u2self" : "impersonate");

              t_log_failure(GSS_C_NO_OID, ret_maj, ret_min);

              ret = -1;

              goto done;
@@ -121,9 +117,6 @@ 

      }

  

      if (selfhalf) {

-         gss_key_value_element_desc ccelement = { "ccache", deleg_ccache };

-         gss_key_value_set_desc cred_store = { 1, &ccelement };

- 

          ret_maj = gss_store_cred_into(&ret_min,

                                        cred_handle,

                                        GSS_C_INITIATE,

file modified
+12 -9
@@ -75,40 +75,43 @@ 

  

      # Test all permitted

      socket = os.path.join(testdir, 'impersonate.socket')

-     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS]

+     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS,

+            path_prefix + 'impersonate.cache']

      r = run_cmd(testdir, env, conf, "Impersonate", socket, cmd, False)

      rets.append(r)

  

      #Test fail

      socket = os.path.join(testdir, 'impersonate-proxyonly.socket')

-     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS]

+     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS,

+            path_prefix + 'impersonate.cache']

      r = run_cmd(testdir, env, conf, "Impersonate fail self", socket, cmd, True)

      rets.append(r)

  

      #Test fail

      socket = os.path.join(testdir, 'impersonate-selfonly.socket')

-     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS]

+     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS,

+            path_prefix + 'impersonate.cache']

      r = run_cmd(testdir, env, conf, "Impersonate fail proxy", socket, cmd, True)

      rets.append(r)

  

      #Test s4u2self half succeed

      socket = os.path.join(testdir, 'impersonate-selfonly.socket')

-     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, 's4u2self',

-            path_prefix + 'impersonate-proxy.ccache']

+     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS,

+            path_prefix + 'impersonate.cache', 's4u2self']

      r = run_cmd(testdir, env, conf, "s4u2self delegation", socket, cmd, False)

      rets.append(r)

  

      #Test s4u2proxy half fail

      socket = os.path.join(testdir, 'impersonate-selfonly.socket')

-     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, 's4u2proxy',

-            path_prefix + 'impersonate-proxy.ccache']

+     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS,

+            path_prefix + 'impersonate.cache', 's4u2proxy']

      r = run_cmd(testdir, env, conf, "s4u2proxy fail", socket, cmd, True)

      rets.append(r)

  

      #Test s4u2proxy half succeed

      socket = os.path.join(testdir, 'impersonate-proxyonly.socket')

-     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, 's4u2proxy',

-            path_prefix + 'impersonate-proxy.ccache']

+     cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS,

+            path_prefix + 'impersonate.cache', 's4u2proxy']

      r = run_cmd(testdir, env, conf, "s4u2proxy", socket, cmd, False)

      rets.append(r)

  

file modified
+4 -4
@@ -535,10 +535,10 @@ 

  [service/test]

    mechs = krb5

    cred_store = keytab:${GSSPROXY_KEYTAB}

-   cred_store = ccache:FILE:${GSSPROXY_CLIENT_CCACHE}

    cred_store = client_keytab:${GSSPROXY_CLIENT_KEYTAB}

    trusted = yes

    euid = ${UIDNUMBER}

+   allow_client_ccache_sync = yes

  

  [service/badkeytab]

    mechs = krb5
@@ -553,10 +553,10 @@ 

  [service/dontuse]

    mechs = krb5

    cred_store = keytab:${GSSPROXY_KEYTAB}

-   cred_store = ccache:FILE:${GSSPROXY_CLIENT_CCACHE}

    cred_store = client_keytab:${GSSPROXY_CLIENT_KEYTAB}

    trusted = yes

    euid = nobody

+   allow_client_ccache_sync = yes

  '''

  

  GSSPROXY_CONF_SOCKET_TEMPLATE = '''
@@ -566,11 +566,11 @@ 

  [service/test]

    mechs = krb5

    cred_store = keytab:${GSSPROXY_KEYTAB}

-   cred_store = ccache:FILE:${GSSPROXY_CLIENT_CCACHE}

    cred_store = client_keytab:${GSSPROXY_CLIENT_KEYTAB}

    trusted = yes

    euid = ${UIDNUMBER}

    socket = ${SECOND_SOCKET}

+   allow_client_ccache_sync = yes

  '''

  

  GSSPROXY_MULTI_TEMPLATE = '''
@@ -580,10 +580,10 @@ 

  [service/test]

    mechs = krb5

    cred_store = keytab:${GSSPROXY_KEYTAB}

-   cred_store = client_keytab:${GSSPROXY_CLIENT_KEYTAB}

    krb5_principal = ${GSSPROXY_CLIENT_PRINCIPAL}

    trusted = yes

    euid = ${UIDNUMBER}

+   allow_client_ccache_sync = yes

  '''

  

  def update_gssproxy_conf(testdir, env, template):

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.

1 new commit added

  • Add support for the NO_CI_FLAG credentials option
7 years ago

This whole blob should probably be a helper function.

12 new commits added

  • Fix impersonate tests to cope with credential sync
  • Change tests to always exercise ccache sycns
  • If credentials changed try to store them
  • Always request cred sync on init_sec_context
  • Add utility function to compare gssx_creds
  • Rework gpp_cred_handle management
  • Fix potential memleak from gpm_release_cred
  • Do not re-export unchanged creds
  • Add ability to sync creds back on modification
  • Add a helper function to pack options
  • Still fall back to remote with cred_store
  • Do not check for remote cred in LOCAL_ONLY case
7 years ago

Please expand on this comment.

Mostly looks good. Things I would like to see:

  • "Do not check for remote cred in LOCAL_ONLY case" squashed with "Still fall back to remote with cred_store".
  • "Fix potential memleak from gpm_release_cred" should be the first commit in the series. I'd like to tag a new .z version with that commit and not the rest of this series.
  • "If credentials changed try to store them" breaks the test suite. For backporting purposes, it needs to not do that.
  • Should we be running at least some tests without ccache syncs?
  • Two more comments inline
  • Misc style/typos (I'll fix these up when merging, unless you'd prefer to do it.)

I'll probably have a second round of stuff; apologies in advance for that.

I think I addressed all your points, please confirm.
For typos I can either rebase myself or you can push with those fixes.

rebased

7 years ago

Pull-Request has been closed by rharwood

7 years ago

Commit c640f17 fixes this pull-request

Pull-Request has been merged by simo@redhat.com

6 years ago

Commit 94478d4 fixes this pull-request

Pull-Request has been merged by simo@redhat.com

6 years ago