diff options
author | Simo Sorce <simo@redhat.com> | 2016-11-30 09:06:33 -0500 |
---|---|---|
committer | Simo Sorce <simo@redhat.com> | 2017-01-13 15:50:06 -0500 |
commit | 2d49ba029e5b0fdaa4bafc3d5bca0cb1169c9877 (patch) | |
tree | 0eda5a7654de058300d3cbf642b60c3819c4e141 | |
parent | 56d2a3119c4713fbfabf98b0afc0882d64324166 (diff) | |
download | gss-proxy-2d49ba029e5b0fdaa4bafc3d5bca0cb1169c9877.tar.gz gss-proxy-2d49ba029e5b0fdaa4bafc3d5bca0cb1169c9877.tar.xz gss-proxy-2d49ba029e5b0fdaa4bafc3d5bca0cb1169c9877.zip |
Use a local keytab for creds encryption
If available use a keytab for creds encryption.
Since now we can store encrypted credentials, on the cient side, for later
reuse, it is better to be able to decrypt them even after a gssproxy daemon
restart (maintenance, crashes, etc..)
If a keytab is rotated this can cause a restarted gssproxy to fail to decrypt
stored credentials, but in that case those credentials are also probably
useless and need to be refreshed, so this is not a huge deal, and definitely
better than the status quo.
Signed-off-by: Simo Sorce <simo@redhat.com>
-rw-r--r-- | proxy/src/gp_config.c | 47 | ||||
-rw-r--r-- | proxy/src/gp_export.c | 120 | ||||
-rw-r--r-- | proxy/src/gp_proxy.h | 11 |
3 files changed, 142 insertions, 36 deletions
diff --git a/proxy/src/gp_config.c b/proxy/src/gp_config.c index 6398f28..dfc7800 100644 --- a/proxy/src/gp_config.c +++ b/proxy/src/gp_config.c @@ -62,12 +62,33 @@ static void gp_service_free(struct gp_service *svc) free(svc->krb5.principal); free_str_array(&(svc->krb5.cred_store), &svc->krb5.cred_count); + gp_free_creds_handle(&svc->krb5.creds_handle); } - gp_free_creds_handle(&svc->creds_handle); SELINUX_context_free(svc->selinux_ctx); memset(svc, 0, sizeof(struct gp_service)); } +static int setup_krb5_creds_handle(struct gp_service *svc) +{ + uint32_t ret_maj, ret_min; + const char *keytab = NULL; + + for (unsigned i = 0; i < svc->krb5.cred_count; i++) { + if (strncmp(svc->krb5.cred_store[i], "keytab:", 7) == 0) { + keytab = svc->krb5.cred_store[i] + 7; + break; + } + } + + ret_maj = gp_init_creds_handle(&ret_min, svc->name, keytab, + &svc->krb5.creds_handle); + if (ret_maj) { + return ret_min; + } + + return 0; +} + static int get_krb5_mech_cfg(struct gp_service *svc, struct gp_ini_context *ctx, const char *secname) @@ -115,6 +136,10 @@ static int get_krb5_mech_cfg(struct gp_service *svc, ret = 0; } + if (ret == 0) { + ret = setup_krb5_creds_handle(svc); + } + return ret; } @@ -171,18 +196,6 @@ static int parse_flags(const char *value, uint32_t *storage) return 0; } -static int setup_service_creds_handle(struct gp_service *svc) -{ - uint32_t ret_maj, ret_min; - - ret_maj = gp_init_creds_handle(&ret_min, &svc->creds_handle); - if (ret_maj) { - return ret_min; - } - - return 0; -} - static int check_services(const struct gp_config *cfg) { int i, j; @@ -339,11 +352,6 @@ static int load_services(struct gp_config *cfg, struct gp_ini_context *ctx) } } - ret = setup_service_creds_handle(cfg->svcs[n]); - if (ret) { - goto done; - } - ret = gp_config_get_string(ctx, secname, "mechs", &value); if (ret != 0) { /* if mechs is missing or there is an error retrieving it @@ -377,6 +385,7 @@ static int load_services(struct gp_config *cfg, struct gp_ini_context *ctx) safefree(vcopy); return ret; } + } else { GPERROR("Unknown mech: %s in [%s], ignoring.\n", token, secname); @@ -600,7 +609,7 @@ done: struct gp_creds_handle *gp_service_get_creds_handle(struct gp_service *svc) { - return svc->creds_handle; + return svc->krb5.creds_handle; } void free_config(struct gp_config **cfg) diff --git a/proxy/src/gp_export.c b/proxy/src/gp_export.c index 41af67b..c83303a 100644 --- a/proxy/src/gp_export.c +++ b/proxy/src/gp_export.c @@ -17,8 +17,8 @@ #define GP_CREDS_HANDLE_KEY_ENCTYPE ENCTYPE_AES256_CTS_HMAC_SHA1_96 struct gp_creds_handle { - krb5_keyblock key; krb5_context context; + krb5_keyblock *key; }; void gp_free_creds_handle(struct gp_creds_handle **in) @@ -30,7 +30,7 @@ void gp_free_creds_handle(struct gp_creds_handle **in) } if (handle->context) { - krb5_free_keyblock_contents(handle->context, &handle->key); + krb5_free_keyblock(handle->context, handle->key); krb5_free_context(handle->context); } @@ -39,11 +39,15 @@ void gp_free_creds_handle(struct gp_creds_handle **in) return; } -uint32_t gp_init_creds_handle(uint32_t *min, struct gp_creds_handle **out) +uint32_t gp_init_creds_handle(uint32_t *min, const char *svc_name, + const char *keytab, + struct gp_creds_handle **out) { struct gp_creds_handle *handle; uint32_t ret_maj = 0; uint32_t ret_min = 0; + krb5_keytab ktid = NULL; + char ktname[MAX_KEYTAB_NAME_LEN + 1] = {0}; int ret; handle = calloc(1, sizeof(struct gp_creds_handle)); @@ -61,19 +65,111 @@ uint32_t gp_init_creds_handle(uint32_t *min, struct gp_creds_handle **out) goto done; } - ret = krb5_c_make_random_key(handle->context, - GP_CREDS_HANDLE_KEY_ENCTYPE, + /* Try to use a keytab, and fall back to a random runtime secret if all + * else fails */ + if (keytab) { + ret = krb5_kt_resolve(handle->context, keytab, &ktid); + if (ret == 0) { + ret = krb5_kt_have_content(handle->context, ktid); + } + /* if a keytab is specified then it must be usable */ + if (ret) { + ret_min = ret; + ret_maj = GSS_S_CRED_UNAVAIL; + goto done; + } + strncpy(ktname, keytab, MAX_KEYTAB_NAME_LEN); + } else { + ret = krb5_kt_default(handle->context, &ktid); + /* if the default keyab does not exist or is empty it is not fatal */ + if (ret) { + ktid = NULL; + } else { + ret = krb5_kt_have_content(handle->context, ktid); + if (ret) { + (void)krb5_kt_close(handle->context, ktid); + ktid = NULL; + } else { + ret = krb5_kt_default_name(handle->context, ktname, + MAX_KEYTAB_NAME_LEN); + if (ret) strncpy(ktname, "[default]", MAX_KEYTAB_NAME_LEN); + } + } + } + + if (ktid) { + krb5_kt_cursor cursor; + krb5_keytab_entry entry; + krb5_enctype *permitted; + + ret = krb5_get_permitted_enctypes(handle->context, &permitted); + if (ret) { + ret_min = ret; + ret_maj = GSS_S_FAILURE; + goto done; + } + + ret = krb5_kt_start_seq_get(handle->context, ktid, &cursor); + if (ret) { + ret_min = ret; + ret_maj = GSS_S_FAILURE; + goto done; + } + do { + ret = krb5_kt_next_entry(handle->context, ktid, &entry, &cursor); + if (ret == 0) { + for (unsigned i = 0; permitted[i] != 0; i++) { + if (permitted[i] == entry.key.enctype) { + /* should we derive a key instead ? */ + ret = krb5_copy_keyblock(handle->context, &entry.key, + &handle->key); + if (ret == 0) { + GPDEBUG("Service: %s, Enckey: %s, Enctype: %d\n", + svc_name, ktname, entry.key.enctype); + ret = KRB5_KT_END; + } + break; + } + } + (void)krb5_free_keytab_entry_contents(handle->context, &entry); + } + } while (ret == 0); + (void)krb5_kt_end_seq_get(handle->context, ktid, &cursor); + if ((ret == KRB5_KT_END) && (handle->key == NULL)) { + ret = KRB5_WRONG_ETYPE; + ret_maj = GSS_S_CRED_UNAVAIL; + goto done; + } + if (ret != KRB5_KT_END) { + ret_min = ret; + ret_maj = GSS_S_CRED_UNAVAIL; + goto done; + } + } else { + ret = krb5_init_keyblock(handle->context, + GP_CREDS_HANDLE_KEY_ENCTYPE, 0, &handle->key); - if (ret) { - ret_min = ret; - ret_maj = GSS_S_FAILURE; - goto done; + if (ret == 0) { + ret = krb5_c_make_random_key(handle->context, + GP_CREDS_HANDLE_KEY_ENCTYPE, + handle->key); + GPDEBUG("Service: %s, Enckey: [ephemeral], Enctype: %d\n", + svc_name, GP_CREDS_HANDLE_KEY_ENCTYPE); + } + if (ret) { + ret_min = ret; + ret_maj = GSS_S_FAILURE; + goto done; + } } ret_maj = GSS_S_COMPLETE; ret_min = 0; done: + if (handle->context && ktid) { + (void)krb5_kt_close(handle->context, ktid); + } *min = ret_min; if (ret_maj) { gp_free_creds_handle(&handle); @@ -97,7 +193,7 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key, memset(&enc_handle, '\0', sizeof(krb5_enc_data)); ret = krb5_c_encrypt_length(context, - GP_CREDS_HANDLE_KEY_ENCTYPE, + key->enctype, data_in.length, &cipherlen); if (ret) { @@ -254,7 +350,7 @@ uint32_t gp_export_gssx_cred(uint32_t *min, struct gp_call_ctx *gpcall, goto done; } - ret = gp_encrypt_buffer(handle->context, &handle->key, + ret = gp_encrypt_buffer(handle->context, handle->key, token.length, token.value, &out->cred_handle_reference); if (ret) { @@ -338,7 +434,7 @@ uint32_t gp_import_gssx_cred(uint32_t *min, struct gp_call_ctx *gpcall, goto done; } - ret = gp_decrypt_buffer(handle->context, &handle->key, + ret = gp_decrypt_buffer(handle->context, handle->key, &cred->cred_handle_reference, &token.length, token.value); if (ret) { diff --git a/proxy/src/gp_proxy.h b/proxy/src/gp_proxy.h index be71d38..c7f4bb2 100644 --- a/proxy/src/gp_proxy.h +++ b/proxy/src/gp_proxy.h @@ -17,14 +17,15 @@ #define GP_CRED_KRB5 0x01 +struct gp_creds_handle; + struct gp_cred_krb5 { char *principal; const char **cred_store; int cred_count; + struct gp_creds_handle *creds_handle; }; -struct gp_creds_handle; - struct gp_service { char *name; uid_t euid; @@ -41,8 +42,6 @@ struct gp_service { uint32_t mechs; struct gp_cred_krb5 krb5; - struct gp_creds_handle *creds_handle; - verto_ev *ev; }; @@ -127,7 +126,9 @@ struct gp_service *gp_creds_match_conn(struct gssproxy_ctx *gpctx, struct gp_conn *conn); /* from gp_export.c */ -uint32_t gp_init_creds_handle(uint32_t *min, struct gp_creds_handle **out); +uint32_t gp_init_creds_handle(uint32_t *min, const char *svc_name, + const char *keytab, + struct gp_creds_handle **out); void gp_free_creds_handle(struct gp_creds_handle **in); #endif /* _GP_PROXY_H_ */ |