From 117678877802956aefab8e9aa61c1f8de0738d52 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Tue, 19 Jun 2018 13:35:38 +0200 Subject: [PATCH] Improve kerberos credential cache handling (#1566494) --- ...h-6.3p1-krb5-use-default_ccache_name.patch | 247 ------- openssh-6.6p1-GSSAPIEnablek5users.patch | 6 +- openssh-6.6p1-kuserok.patch | 24 +- openssh-7.0p1-gssKexAlgorithms.patch | 2 +- openssh-7.2p1-gsskex.patch | 19 +- openssh-7.2p2-k5login_directory.patch | 2 +- openssh-7.5p1-gss-environment.patch | 259 ------- openssh-7.7p1-gssapi-new-unique.patch | 651 ++++++++++++++++++ openssh.spec | 11 +- 9 files changed, 678 insertions(+), 543 deletions(-) delete mode 100644 openssh-6.3p1-krb5-use-default_ccache_name.patch delete mode 100644 openssh-7.5p1-gss-environment.patch create mode 100644 openssh-7.7p1-gssapi-new-unique.patch diff --git a/openssh-6.3p1-krb5-use-default_ccache_name.patch b/openssh-6.3p1-krb5-use-default_ccache_name.patch deleted file mode 100644 index dd201a4..0000000 --- a/openssh-6.3p1-krb5-use-default_ccache_name.patch +++ /dev/null @@ -1,247 +0,0 @@ -diff -up openssh-6.3p1/auth-krb5.c.ccache_name openssh-6.3p1/auth-krb5.c ---- openssh-6.3p1/auth-krb5.c.ccache_name 2013-10-23 22:03:52.322950759 +0200 -+++ openssh-6.3p1/auth-krb5.c 2013-10-23 22:04:24.295799873 +0200 -@@ -50,7 +50,9 @@ - #include - #include - #include -+#include - #include -+#include - - extern ServerOptions options; - -@@ -91,6 +93,7 @@ auth_krb5_password(Authctxt *authctxt, c - #endif - krb5_error_code problem; - krb5_ccache ccache = NULL; -+ const char *ccache_type; - int len; - char *client, *platform_client; - const char *errmsg; -@@ -191,12 +194,30 @@ auth_krb5_password(Authctxt *authctxt, c - goto out; - #endif - -+ ccache_type = krb5_cc_get_type(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); - authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); - -- len = strlen(authctxt->krb5_ticket_file) + 6; -+ if (authctxt->krb5_ticket_file[0] == ':') -+ authctxt->krb5_ticket_file++; -+ -+ len = strlen(authctxt->krb5_ticket_file) + strlen(ccache_type) + 2; - authctxt->krb5_ccname = xmalloc(len); -- snprintf(authctxt->krb5_ccname, len, "FILE:%s", -+ -+#ifdef USE_CCAPI -+ snprintf(authctxt->krb5_ccname, len, "API:%s", - authctxt->krb5_ticket_file); -+#else -+ snprintf(authctxt->krb5_ccname, len, "%s:%s", -+ ccache_type, authctxt->krb5_ticket_file); -+#endif -+ -+ if (strcmp(ccache_type, "DIR") == 0) { -+ char *p; -+ p = strrchr(authctxt->krb5_ccname, '/'); -+ if (p) -+ *p = '\0'; -+ } -+ - - #ifdef USE_PAM - if (options.use_pam) -@@ -235,10 +256,34 @@ auth_krb5_password(Authctxt *authctxt, c - void - krb5_cleanup_proc(Authctxt *authctxt) - { -+ struct stat krb5_ccname_stat; -+ char krb5_ccname[128], *krb5_ccname_dir_start, *krb5_ccname_dir_end; -+ - debug("krb5_cleanup_proc called"); - if (authctxt->krb5_fwd_ccache) { - krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); - authctxt->krb5_fwd_ccache = NULL; -+ -+ strncpy(krb5_ccname, authctxt->krb5_ccname, sizeof(krb5_ccname) - 10); -+ krb5_ccname_dir_start = strchr(krb5_ccname, ':') + 1; -+ *krb5_ccname_dir_start++ = '\0'; -+ if (strcmp(krb5_ccname, "DIR") == 0) { -+ -+ strcat(krb5_ccname_dir_start, "/primary"); -+ -+ if (stat(krb5_ccname_dir_start, &krb5_ccname_stat) == 0) { -+ if (unlink(krb5_ccname_dir_start) == 0) { -+ krb5_ccname_dir_end = strrchr(krb5_ccname_dir_start, '/'); -+ *krb5_ccname_dir_end = '\0'; -+ if (rmdir(krb5_ccname_dir_start) == -1) -+ debug("cache dir '%s' remove failed: %s", krb5_ccname_dir_start, strerror(errno)); -+ } -+ else -+ debug("cache primary file '%s', remove failed: %s", -+ krb5_ccname_dir_start, strerror(errno) -+ ); -+ } -+ } - } - if (authctxt->krb5_user) { - krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user); -@@ -250,34 +295,139 @@ krb5_cleanup_proc(Authctxt *authctxt) - } - } - -+int -+ssh_asprintf_append(char **dsc, const char *fmt, ...) { -+ char *src, *old; -+ va_list ap; -+ int i; -+ -+ va_start(ap, fmt); -+ i = vasprintf(&src, fmt, ap); -+ va_end(ap); -+ -+ if (i == -1 || src == NULL) -+ return -1; -+ -+ old = *dsc; -+ -+ i = asprintf(dsc, "%s%s", *dsc, src); -+ if (i == -1 || src == NULL) { -+ free(src); -+ return -1; -+ } -+ -+ free(old); -+ free(src); -+ -+ return i; -+} -+ -+int -+ssh_krb5_expand_template(char **result, const char *template) { -+ char *p_n, *p_o, *r, *tmp_template; -+ -+ if (template == NULL) -+ return -1; -+ -+ tmp_template = p_n = p_o = xstrdup(template); -+ r = xstrdup(""); -+ -+ while ((p_n = strstr(p_o, "%{")) != NULL) { -+ -+ *p_n++ = '\0'; -+ if (ssh_asprintf_append(&r, "%s", p_o) == -1) -+ goto cleanup; -+ -+ if (strncmp(p_n, "{uid}", 5) == 0 || strncmp(p_n, "{euid}", 6) == 0 || -+ strncmp(p_n, "{USERID}", 8) == 0) { -+ p_o = strchr(p_n, '}') + 1; -+ if (ssh_asprintf_append(&r, "%d", geteuid()) == -1) -+ goto cleanup; -+ continue; -+ } -+ else if (strncmp(p_n, "{TEMP}", 6) == 0) { -+ p_o = strchr(p_n, '}') + 1; -+ if (ssh_asprintf_append(&r, "/tmp") == -1) -+ goto cleanup; -+ continue; -+ } else { -+ p_o = strchr(p_n, '}') + 1; -+ p_o = '\0'; -+ debug("%s: unsupported token %s in %s", __func__, p_n, template); -+ /* unknown token, fallback to the default */ -+ goto cleanup; -+ } -+ } -+ -+ if (ssh_asprintf_append(&r, "%s", p_o) == -1) -+ goto cleanup; -+ -+ *result = r; -+ free(tmp_template); -+ return 0; -+ -+cleanup: -+ free(r); -+ free(tmp_template); -+ return -1; -+} -+ -+krb5_error_code -+ssh_krb5_get_cctemplate(krb5_context ctx, char **ccname) { -+ profile_t p; -+ int ret = 0; -+ char *value = NULL; -+ -+ ret = krb5_get_profile(ctx, &p); -+ if (ret) -+ return ret; -+ -+ ret = profile_get_string(p, "libdefaults", "default_ccache_name", NULL, NULL, &value); -+ if (ret) -+ return ret; -+ -+ ret = ssh_krb5_expand_template(ccname, value); -+ -+ return ret; -+} -+ - #ifndef HEIMDAL - krb5_error_code - ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { - int tmpfd, ret, oerrno; -- char ccname[40]; -+ char *ccname; -+#ifdef USE_CCAPI -+ char cctemplate[] = "API:krb5cc_%d"; -+#else - mode_t old_umask; -+ char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX"; - -- ret = snprintf(ccname, sizeof(ccname), -- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); -- if (ret < 0 || (size_t)ret >= sizeof(ccname)) -- return ENOMEM; -- -- old_umask = umask(0177); -- tmpfd = mkstemp(ccname + strlen("FILE:")); -- oerrno = errno; -- umask(old_umask); -- if (tmpfd == -1) { -- logit("mkstemp(): %.100s", strerror(oerrno)); -- return oerrno; -- } -+#endif -+ -+ ret = ssh_krb5_get_cctemplate(ctx, &ccname); - -- if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { -+ if (ret) { -+ ret = asprintf(&ccname, cctemplate, geteuid()); -+ if (ret == -1) -+ return ENOMEM; -+ old_umask = umask(0177); -+ tmpfd = mkstemp(ccname + strlen("FILE:")); - oerrno = errno; -- logit("fchmod(): %.100s", strerror(oerrno)); -+ umask(old_umask); -+ if (tmpfd == -1) { -+ logit("mkstemp(): %.100s", strerror(oerrno)); -+ return oerrno; -+ } -+ -+ if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { -+ oerrno = errno; -+ logit("fchmod(): %.100s", strerror(oerrno)); -+ close(tmpfd); -+ return oerrno; -+ } - close(tmpfd); -- return oerrno; - } -- close(tmpfd); -+ debug("%s: Setting ccname to %s", __func__, ccname); - - return (krb5_cc_resolve(ctx, ccname, ccache)); - } diff --git a/openssh-6.6p1-GSSAPIEnablek5users.patch b/openssh-6.6p1-GSSAPIEnablek5users.patch index 9f91f14..5dec15b 100644 --- a/openssh-6.6p1-GSSAPIEnablek5users.patch +++ b/openssh-6.6p1-GSSAPIEnablek5users.patch @@ -82,8 +82,8 @@ diff -up openssh-7.4p1/servconf.c.GSSAPIEnablek5users openssh-7.4p1/servconf.c M_CP_INTOPT(rekey_interval); M_CP_INTOPT(log_level); @@ -2320,6 +2330,7 @@ dump_config(ServerOptions *o) - dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); # endif + dump_cfg_fmtint(sKerberosUniqueTicket, o->kerberos_unique_ticket); dump_cfg_fmtint(sKerberosUseKuserok, o->use_kuserok); + dump_cfg_fmtint(sGssEnablek5users, o->enable_k5users); #endif @@ -93,8 +93,8 @@ diff -up openssh-7.4p1/servconf.h.GSSAPIEnablek5users openssh-7.4p1/servconf.h --- openssh-7.4p1/servconf.h.GSSAPIEnablek5users 2016-12-23 15:18:40.616216100 +0100 +++ openssh-7.4p1/servconf.h 2016-12-23 15:18:40.629216102 +0100 @@ -174,6 +174,7 @@ typedef struct { - int kerberos_get_afs_token; /* If true, try to get AFS token if - * authenticated with Kerberos. */ + int kerberos_unique_ticket; /* If true, the aquired ticket will + * be stored in per-session ccache */ int use_kuserok; + int enable_k5users; int gss_authentication; /* If true, permit GSSAPI authentication */ diff --git a/openssh-6.6p1-kuserok.patch b/openssh-6.6p1-kuserok.patch index 2c65890..23f47ae 100644 --- a/openssh-6.6p1-kuserok.patch +++ b/openssh-6.6p1-kuserok.patch @@ -192,26 +192,26 @@ diff -up openssh-7.4p1/servconf.c.kuserok openssh-7.4p1/servconf.c if (options->password_authentication == -1) options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) -@@ -399,7 +402,8 @@ typedef enum { +@@ -399,7 +402,7 @@ typedef enum { sPermitRootLogin, sLogFacility, sLogLevel, sRhostsRSAAuthentication, sRSAAuthentication, sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, -- sKerberosGetAFSToken, sChallengeResponseAuthentication, -+ sKerberosGetAFSToken, sKerberosUseKuserok, -+ sChallengeResponseAuthentication, +- sKerberosGetAFSToken, sKerberosUniqueTicket, ++ sKerberosGetAFSToken, sKerberosUniqueTicket, sKerberosUseKuserok, + sChallengeResponseAuthentication, sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress, sAddressFamily, - sPrintMotd, sPrintLastLog, sIgnoreRhosts, -@@ -478,11 +481,13 @@ static struct { - #else +@@ -478,12 +481,14 @@ static struct { { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, #endif + { "kerberosuniqueticket", sKerberosUniqueTicket, SSHCFG_GLOBAL }, + { "kerberosusekuserok", sKerberosUseKuserok, SSHCFG_ALL }, #else { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosuniqueticket", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosusekuserok", sUnsupported, SSHCFG_ALL }, #endif { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, @@ -236,9 +236,9 @@ diff -up openssh-7.4p1/servconf.c.kuserok openssh-7.4p1/servconf.c M_CP_INTOPT(rekey_interval); M_CP_INTOPT(log_level); @@ -2309,6 +2319,7 @@ dump_config(ServerOptions *o) - # ifdef USE_AFS dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); # endif + dump_cfg_fmtint(sKerberosUniqueTicket, o->kerberos_unique_ticket); + dump_cfg_fmtint(sKerberosUseKuserok, o->use_kuserok); #endif #ifdef GSSAPI @@ -247,9 +247,9 @@ diff -up openssh-7.4p1/servconf.h.kuserok openssh-7.4p1/servconf.h --- openssh-7.4p1/servconf.h.kuserok 2016-12-23 14:36:07.630465944 +0100 +++ openssh-7.4p1/servconf.h 2016-12-23 14:36:07.645465936 +0100 @@ -118,6 +118,7 @@ typedef struct { - * file on logout. */ - int kerberos_get_afs_token; /* If true, try to get AFS token if * authenticated with Kerberos. */ + int kerberos_unique_ticket; /* If true, the aquired ticket will + * be stored in per-session ccache */ + int use_kuserok; int gss_authentication; /* If true, permit GSSAPI authentication */ int gss_keyex; /* If true, permit GSSAPI key exchange */ @@ -258,9 +258,9 @@ diff -up openssh-7.4p1/sshd_config.5.kuserok openssh-7.4p1/sshd_config.5 --- openssh-7.4p1/sshd_config.5.kuserok 2016-12-23 14:36:07.637465940 +0100 +++ openssh-7.4p1/sshd_config.5 2016-12-23 15:14:03.117162222 +0100 @@ -850,6 +850,10 @@ Specifies whether to automatically destr - file on logout. + tickets aquired in different sessions of the same user. The default is - .Cm yes . + .Cm no . +.It Cm KerberosUseKuserok +Specifies whether to look at .k5login file for user's aliases. +The default is diff --git a/openssh-7.0p1-gssKexAlgorithms.patch b/openssh-7.0p1-gssKexAlgorithms.patch index 57b5eaf..d2453d5 100644 --- a/openssh-7.0p1-gssKexAlgorithms.patch +++ b/openssh-7.0p1-gssKexAlgorithms.patch @@ -401,8 +401,8 @@ diff -up openssh-7.0p1/ssh-gss.h.gsskexalg openssh-7.0p1/ssh-gss.h + KEX_GSS_GRP14_SHA1_ID + typedef struct { - char *filename; char *envvar; + char *envval; @@ -147,9 +151,9 @@ int ssh_gssapi_credentials_updated(Gssct /* In the server */ typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, diff --git a/openssh-7.2p1-gsskex.patch b/openssh-7.2p1-gsskex.patch index 02436bd..c9034e8 100644 --- a/openssh-7.2p1-gsskex.patch +++ b/openssh-7.2p1-gsskex.patch @@ -540,7 +540,7 @@ diff -up openssh/gss-serv.c.gsskex openssh/gss-serv.c - { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, - GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; + { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL, -+ GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL}, 0, 0}; ++ GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL}, 0, 0}; ssh_gssapi_mech gssapi_null_mech = - { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; @@ -710,7 +710,7 @@ diff -up openssh/gss-serv.c.gsskex openssh/gss-serv.c /* Destroy delegated credentials if userok fails */ gss_release_buffer(&lmin, &gssapi_client.displayname); gss_release_buffer(&lmin, &gssapi_client.exportedname); -@@ -383,14 +469,90 @@ ssh_gssapi_userok(char *user) +@@ -383,14 +469,89 @@ ssh_gssapi_userok(char *user) return (0); } @@ -748,8 +748,7 @@ diff -up openssh/gss-serv.c.gsskex openssh/gss-serv.c + char *envstr; +#endif + -+ if (gssapi_client.store.filename == NULL && -+ gssapi_client.store.envval == NULL && ++ if (gssapi_client.store.envval == NULL && + gssapi_client.store.envvar == NULL) + return; + @@ -819,7 +818,7 @@ diff -up openssh/gss-serv-krb5.c.gsskex openssh/gss-serv-krb5.c const char *errmsg; if (client->creds == NULL) { -@@ -181,11 +181,26 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl +@@ -181,11 +181,23 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl return; } @@ -833,7 +832,6 @@ diff -up openssh/gss-serv-krb5.c.gsskex openssh/gss-serv-krb5.c - snprintf(client->store.envval, len, "FILE:%s", client->store.filename); +#ifdef USE_CCAPI + xasprintf(&client->store.envval, "API:%s", new_ccname); -+ client->store.filename = NULL; +#else + if (new_ccname[0] == ':') + new_ccname++; @@ -844,8 +842,6 @@ diff -up openssh/gss-serv-krb5.c.gsskex openssh/gss-serv-krb5.c + if (p) + *p = '\0'; + } -+ if ((strcmp(new_cctype, "FILE") == 0) || (strcmp(new_cctype, "DIR") == 0)) -+ client->store.filename = xstrdup(new_ccname); +#endif #ifdef USE_PAM @@ -1794,7 +1790,7 @@ diff -up openssh/monitor.c.gsskex openssh/monitor.c buffer_clear(m); buffer_put_int(m, authenticated); -@@ -1854,5 +1880,73 @@ mm_answer_gss_userok(int sock, Buffer *m +@@ -1854,5 +1880,71 @@ mm_answer_gss_userok(int sock, Buffer *m /* Monitor loop will terminate if authenticated */ return (authenticated); } @@ -1848,13 +1844,11 @@ diff -up openssh/monitor.c.gsskex openssh/monitor.c + ssh_gssapi_ccache store; + int ok; + -+ store.filename = buffer_get_string(m, NULL); + store.envvar = buffer_get_string(m, NULL); + store.envval = buffer_get_string(m, NULL); + + ok = ssh_gssapi_update_creds(&store); + -+ free(store.filename); + free(store.envvar); + free(store.envval); + @@ -1892,7 +1886,7 @@ diff -up openssh/monitor_wrap.c.gsskex openssh/monitor_wrap.c { Buffer m; int authenticated = 0; -@@ -967,5 +967,50 @@ mm_ssh_gssapi_userok(char *user) +@@ -967,5 +967,49 @@ mm_ssh_gssapi_userok(char *user) debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); return (authenticated); } @@ -1927,7 +1921,6 @@ diff -up openssh/monitor_wrap.c.gsskex openssh/monitor_wrap.c + + buffer_init(&m); + -+ buffer_put_cstring(&m, store->filename ? store->filename : ""); + buffer_put_cstring(&m, store->envvar ? store->envvar : ""); + buffer_put_cstring(&m, store->envval ? store->envval : ""); + diff --git a/openssh-7.2p2-k5login_directory.patch b/openssh-7.2p2-k5login_directory.patch index 308c452..09369aa 100644 --- a/openssh-7.2p2-k5login_directory.patch +++ b/openssh-7.2p2-k5login_directory.patch @@ -32,7 +32,7 @@ index f9d191c..c432d2f 100644 @@ -222,5 +222,7 @@ int sys_auth_passwd(Authctxt *, const char *); #if defined(KRB5) && !defined(HEIMDAL) #include - krb5_error_code ssh_krb5_cc_gen(krb5_context, krb5_ccache *); + krb5_error_code ssh_krb5_cc_new_unique(krb5_context, krb5_ccache *, int *); +krb5_error_code ssh_krb5_get_k5login_directory(krb5_context ctx, + char **k5login_directory); #endif diff --git a/openssh-7.5p1-gss-environment.patch b/openssh-7.5p1-gss-environment.patch deleted file mode 100644 index 5b5d661..0000000 --- a/openssh-7.5p1-gss-environment.patch +++ /dev/null @@ -1,259 +0,0 @@ -diff --git a/auth-krb5.c b/auth-krb5.c -index 09ed151..282fcca 100644 ---- a/auth-krb5.c -+++ b/auth-krb5.c -@@ -182,7 +182,8 @@ auth_krb5_password(Authctxt *authctxt, const char *password) - goto out; - } - -- problem = ssh_krb5_cc_gen(authctxt->krb5_ctx, &authctxt->krb5_fwd_ccache); -+ problem = ssh_krb5_cc_gen(authctxt->krb5_ctx, -+ &authctxt->krb5_fwd_ccache, &authctxt->krb5_set_env); - if (problem) - goto out; - -@@ -192,7 +192,7 @@ auth_krb5_password(Authctxt *authctxt, const char *password) - - - #ifdef USE_PAM -- if (options.use_pam) -+ if (options.use_pam && authctxt->krb5_set_env) - do_pam_putenv("KRB5CCNAME", authctxt->krb5_ccname); - #endif - -@@ -412,7 +413,7 @@ ssh_krb5_get_cctemplate(krb5_context ctx, char **ccname) { - - #ifndef HEIMDAL - krb5_error_code --ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { -+ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache, int *need_environment) { - int tmpfd, ret, oerrno; - char *ccname; - #ifdef USE_CCAPI -@@ -423,8 +424,10 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { - - #endif - -+ if (need_environment) -+ *need_environment = 0; - ret = ssh_krb5_get_cctemplate(ctx, &ccname); -- -+ /* fallback to the ccache in /tmp */ - if (ret) { - ret = asprintf(&ccname, cctemplate, geteuid()); - if (ret == -1) -@@ -444,6 +447,9 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { - close(tmpfd); - return oerrno; - } -+ /* make sure the KRBCCNAME is set for non-standard location */ -+ if (need_environment) -+ *need_environment = 1; - close(tmpfd); - } - debug("%s: Setting ccname to %s", __func__, ccname); -diff --git a/auth.h b/auth.h -index 954a0dd..0819483 100644 ---- a/auth.h -+++ b/auth.h -@@ -78,6 +78,7 @@ struct Authctxt { - krb5_principal krb5_user; - char *krb5_ticket_file; - char *krb5_ccname; -+ int krb5_set_env; - #endif - struct sshbuf *loginmsg; - -@@ -220,7 +221,7 @@ int sys_auth_passwd(Authctxt *, const char *); - - #if defined(KRB5) && !defined(HEIMDAL) - #include --krb5_error_code ssh_krb5_cc_gen(krb5_context, krb5_ccache *); -+krb5_error_code ssh_krb5_cc_gen(krb5_context, krb5_ccache *, int *); - krb5_error_code ssh_krb5_get_k5login_directory(krb5_context ctx, - char **k5login_directory); - #endif -diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c -index 0fa3838..4127245 100644 ---- a/gss-serv-krb5.c -+++ b/gss-serv-krb5.c -@@ -382,7 +382,7 @@ ssh_gssapi_krb5_cmdok(krb5_principal principal, const char *name, - /* This writes out any forwarded credentials from the structure populated - * during userauth. Called after we have setuid to the user */ - --static void -+static int - ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) - { - krb5_ccache ccache; -@@ -391,14 +391,15 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) - OM_uint32 maj_status, min_status; - const char *new_ccname, *new_cctype; - const char *errmsg; -+ int set_env = 0; - - if (client->creds == NULL) { - debug("No credentials stored"); -- return; -+ return 0; - } - - if (ssh_gssapi_krb5_init() == 0) -- return; -+ return 0; - - #ifdef HEIMDAL - # ifdef HAVE_KRB5_CC_NEW_UNIQUE -@@ -412,14 +413,14 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) - krb5_get_err_text(krb_context, problem)); - # endif - krb5_free_error_message(krb_context, errmsg); -- return; -+ return 0; - } - #else -- if ((problem = ssh_krb5_cc_gen(krb_context, &ccache))) { -+ if ((problem = ssh_krb5_cc_gen(krb_context, &ccache, &set_env))) { - errmsg = krb5_get_error_message(krb_context, problem); - logit("ssh_krb5_cc_gen(): %.100s", errmsg); - krb5_free_error_message(krb_context, errmsg); -- return; -+ return 0; - } - #endif /* #ifdef HEIMDAL */ - -@@ -428,7 +429,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) - errmsg = krb5_get_error_message(krb_context, problem); - logit("krb5_parse_name(): %.100s", errmsg); - krb5_free_error_message(krb_context, errmsg); -- return; -+ return 0; - } - - if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) { -@@ -437,7 +438,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) - krb5_free_error_message(krb_context, errmsg); - krb5_free_principal(krb_context, princ); - krb5_cc_destroy(krb_context, ccache); -- return; -+ return 0; - } - - krb5_free_principal(krb_context, princ); -@@ -446,7 +447,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) - client->creds, ccache))) { - logit("gss_krb5_copy_ccache() failed"); - krb5_cc_destroy(krb_context, ccache); -- return; -+ return 0; - } - - new_cctype = krb5_cc_get_type(krb_context, ccache); -@@ -471,7 +478,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) - #endif - - #ifdef USE_PAM -- if (options.use_pam) -+ if (options.use_pam && set_env) - do_pam_putenv(client->store.envvar, client->store.envval); - #endif - -@@ -479,7 +486,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) - - client->store.data = krb_context; - -- return; -+ return set_env; - } - - int -diff --git a/gss-serv.c b/gss-serv.c -index 681847a..2a02dae 100644 ---- a/gss-serv.c -+++ b/gss-serv.c -@@ -404,7 +404,7 @@ ssh_gssapi_cleanup_creds(void) - debug("%s: krb5_cc_resolve(): %.100s", __func__, - krb5_get_err_text(gssapi_client.store.data, problem)); - } else if ((problem = krb5_cc_destroy(gssapi_client.store.data, ccache))) { -- debug("%s: krb5_cc_resolve(): %.100s", __func__, -+ debug("%s: krb5_cc_destroy(): %.100s", __func__, - krb5_get_err_text(gssapi_client.store.data, problem)); - } else { - krb5_free_context(gssapi_client.store.data); -@@ -414,13 +414,15 @@ ssh_gssapi_cleanup_creds(void) - } - - /* As user */ --void -+int - ssh_gssapi_storecreds(void) - { - if (gssapi_client.mech && gssapi_client.mech->storecreds) { -- (*gssapi_client.mech->storecreds)(&gssapi_client); -+ return (*gssapi_client.mech->storecreds)(&gssapi_client); - } else - debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism"); -+ -+ return 0; - } - - /* This allows GSSAPI methods to do things to the childs environment based -diff --git a/session.c b/session.c -index df4985a..b7a6a57 100644 ---- a/session.c -+++ b/session.c -@@ -1084,7 +1084,8 @@ do_setup_env(Session *s, const char *shell) - /* Allow any GSSAPI methods that we've used to alter - * the childs environment as they see fit - */ -- ssh_gssapi_do_child(&env, &envsize); -+ if (s->authctxt->krb5_set_env) -+ ssh_gssapi_do_child(&env, &envsize); - #endif - - /* Set basic environment. */ -@@ -1196,7 +1197,7 @@ do_setup_env(Session *s, const char *shell) - } - #endif - #ifdef KRB5 -- if (s->authctxt->krb5_ccname) -+ if (s->authctxt->krb5_ccname && s->authctxt->krb5_set_env) - child_set_env(&env, &envsize, "KRB5CCNAME", - s->authctxt->krb5_ccname); - #endif -diff --git a/ssh-gss.h b/ssh-gss.h -index 6f2b0ac..73ef2c2 100644 ---- a/ssh-gss.h -+++ b/ssh-gss.h -@@ -106,7 +106,7 @@ typedef struct ssh_gssapi_mech_struct { - int (*dochild) (ssh_gssapi_client *); - int (*userok) (ssh_gssapi_client *, char *); - int (*localname) (ssh_gssapi_client *, char **); -- void (*storecreds) (ssh_gssapi_client *); -+ int (*storecreds) (ssh_gssapi_client *); - int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *); - } ssh_gssapi_mech; - -@@ -163,7 +163,7 @@ char* ssh_gssapi_get_displayname(void); - OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); - void ssh_gssapi_do_child(char ***, u_int *); - void ssh_gssapi_cleanup_creds(void); --void ssh_gssapi_storecreds(void); -+int ssh_gssapi_storecreds(void); - const char *ssh_gssapi_displayname(void); - - char *ssh_gssapi_server_mechanisms(void); -diff --git a/sshd.c b/sshd.c -index ce2e374..3c4e13e 100644 ---- a/sshd.c -+++ b/sshd.c -@@ -2221,7 +2221,7 @@ main(int ac, char **av) - #ifdef GSSAPI - if (options.gss_authentication) { - temporarily_use_uid(authctxt->pw); -- ssh_gssapi_storecreds(); -+ authctxt->krb5_set_env = ssh_gssapi_storecreds(); - restore_uid(); - } - #endif - diff --git a/openssh-7.7p1-gssapi-new-unique.patch b/openssh-7.7p1-gssapi-new-unique.patch new file mode 100644 index 0000000..3da318e --- /dev/null +++ b/openssh-7.7p1-gssapi-new-unique.patch @@ -0,0 +1,651 @@ +diff --git a/auth-krb5.c b/auth-krb5.c +index a5a81ed2..63f877f2 100644 +--- a/auth-krb5.c ++++ b/auth-krb5.c +@@ -51,6 +51,7 @@ + #include + #include + #include ++#include + + extern ServerOptions options; + +@@ -77,7 +78,7 @@ auth_krb5_password(Authctxt *authctxt, const char *password) + #endif + krb5_error_code problem; + krb5_ccache ccache = NULL; +- int len; ++ char *ticket_name = NULL; + char *client, *platform_client; + const char *errmsg; + +@@ -163,7 +164,8 @@ auth_krb5_password(Authctxt *authctxt, const char *password) + goto out; + } + +- problem = ssh_krb5_cc_gen(authctxt->krb5_ctx, &authctxt->krb5_fwd_ccache); ++ problem = ssh_krb5_cc_new_unique(authctxt->krb5_ctx, ++ &authctxt->krb5_fwd_ccache, &authctxt->krb5_set_env); + if (problem) + goto out; + +@@ -172,21 +174,20 @@ auth_krb5_password(Authctxt *authctxt, const char *password) + if (problem) + goto out; + +- problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, ++ problem = krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, + &creds); + if (problem) + goto out; + #endif + +- authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); ++ problem = krb5_cc_get_full_name(authctxt->krb5_ctx, ++ authctxt->krb5_fwd_ccache, &ticket_name); + +- len = strlen(authctxt->krb5_ticket_file) + 6; +- authctxt->krb5_ccname = xmalloc(len); +- snprintf(authctxt->krb5_ccname, len, "FILE:%s", +- authctxt->krb5_ticket_file); ++ authctxt->krb5_ccname = xstrdup(ticket_name); ++ krb5_free_string(authctxt->krb5_ctx, ticket_name); + + #ifdef USE_PAM +- if (options.use_pam) ++ if (options.use_pam && authctxt->krb5_set_env) + do_pam_putenv("KRB5CCNAME", authctxt->krb5_ccname); + #endif + +@@ -222,10 +223,59 @@ auth_krb5_password(Authctxt *authctxt, const char *password) + void + krb5_cleanup_proc(Authctxt *authctxt) + { ++ struct stat krb5_ccname_stat; ++ char krb5_ccname[128], *krb5_ccname_dir_start, *krb5_ccname_dir_end; ++ + debug("krb5_cleanup_proc called"); + if (authctxt->krb5_fwd_ccache) { +- krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); ++ krb5_context ctx = authctxt->krb5_ctx; ++ krb5_cccol_cursor cursor; ++ krb5_ccache ccache; ++ int ret; ++ ++ /* Avoid race conditions with other processes touching the same collection */ ++ //krb5_cccol_lock(ctx); ++ ++ krb5_cc_destroy(ctx, authctxt->krb5_fwd_ccache); + authctxt->krb5_fwd_ccache = NULL; ++ ++ ret = krb5_cccol_cursor_new(ctx, &cursor); ++ if (ret) ++ goto unlock; ++ ++ ret = krb5_cccol_cursor_next(ctx, cursor, &ccache); ++ if (ret == 0 && ccache != NULL) { ++ /* There is at least one other ccache in collection ++ * we can switch to */ ++ krb5_cc_switch(ctx, ccache); ++ } else { ++ /* Clean up the collection too */ ++ strncpy(krb5_ccname, authctxt->krb5_ccname, sizeof(krb5_ccname) - 10); ++ krb5_ccname_dir_start = strchr(krb5_ccname, ':') + 1; ++ *krb5_ccname_dir_start++ = '\0'; ++ if (strcmp(krb5_ccname, "DIR") == 0) { ++ ++ strcat(krb5_ccname_dir_start, "/primary"); ++ ++ if (stat(krb5_ccname_dir_start, &krb5_ccname_stat) == 0) { ++ if (unlink(krb5_ccname_dir_start) == 0) { ++ krb5_ccname_dir_end = strrchr(krb5_ccname_dir_start, '/'); ++ *krb5_ccname_dir_end = '\0'; ++ if (rmdir(krb5_ccname_dir_start) == -1) ++ debug("cache dir '%s' remove failed: %s", ++ krb5_ccname_dir_start, strerror(errno)); ++ } ++ else ++ debug("cache primary file '%s', remove failed: %s", ++ krb5_ccname_dir_start, strerror(errno)); ++ } ++ } ++ } ++unlock: ++ krb5_cccol_cursor_free(ctx, &cursor); ++ ++ /* Release lock */ ++ //krb5_cccol_unlock(ctx); + } + if (authctxt->krb5_user) { + krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user); +@@ -237,36 +287,186 @@ krb5_cleanup_proc(Authctxt *authctxt) + } + } + +-#ifndef HEIMDAL +-krb5_error_code +-ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { +- int tmpfd, ret, oerrno; +- char ccname[40]; +- mode_t old_umask; + +- ret = snprintf(ccname, sizeof(ccname), +- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); +- if (ret < 0 || (size_t)ret >= sizeof(ccname)) +- return ENOMEM; +- +- old_umask = umask(0177); +- tmpfd = mkstemp(ccname + strlen("FILE:")); +- oerrno = errno; +- umask(old_umask); +- if (tmpfd == -1) { +- logit("mkstemp(): %.100s", strerror(oerrno)); +- return oerrno; ++#if !defined(HEIMDAL) ++int ++ssh_asprintf_append(char **dsc, const char *fmt, ...) { ++ char *src, *old; ++ va_list ap; ++ int i; ++ ++ va_start(ap, fmt); ++ i = vasprintf(&src, fmt, ap); ++ va_end(ap); ++ ++ if (i == -1 || src == NULL) ++ return -1; ++ ++ old = *dsc; ++ ++ i = asprintf(dsc, "%s%s", *dsc, src); ++ if (i == -1 || src == NULL) { ++ free(src); ++ return -1; ++ } ++ ++ free(old); ++ free(src); ++ ++ return i; ++} ++ ++int ++ssh_krb5_expand_template(char **result, const char *template) { ++ char *p_n, *p_o, *r, *tmp_template; ++ ++ debug3("%s: called, template = %s", __func__, template); ++ if (template == NULL) ++ return -1; ++ ++ tmp_template = p_n = p_o = xstrdup(template); ++ r = xstrdup(""); ++ ++ while ((p_n = strstr(p_o, "%{")) != NULL) { ++ ++ *p_n++ = '\0'; ++ if (ssh_asprintf_append(&r, "%s", p_o) == -1) ++ goto cleanup; ++ ++ if (strncmp(p_n, "{uid}", 5) == 0 || strncmp(p_n, "{euid}", 6) == 0 || ++ strncmp(p_n, "{USERID}", 8) == 0) { ++ p_o = strchr(p_n, '}') + 1; ++ if (ssh_asprintf_append(&r, "%d", geteuid()) == -1) ++ goto cleanup; ++ continue; ++ } ++ else if (strncmp(p_n, "{TEMP}", 6) == 0) { ++ p_o = strchr(p_n, '}') + 1; ++ if (ssh_asprintf_append(&r, "/tmp") == -1) ++ goto cleanup; ++ continue; ++ } else { ++ p_o = strchr(p_n, '}') + 1; ++ p_o = '\0'; ++ debug("%s: unsupported token %s in %s", __func__, p_n, template); ++ /* unknown token, fallback to the default */ ++ goto cleanup; ++ } + } + +- if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { ++ if (ssh_asprintf_append(&r, "%s", p_o) == -1) ++ goto cleanup; ++ ++ *result = r; ++ free(tmp_template); ++ return 0; ++ ++cleanup: ++ free(r); ++ free(tmp_template); ++ return -1; ++} ++ ++krb5_error_code ++ssh_krb5_get_cctemplate(krb5_context ctx, char **ccname) { ++ profile_t p; ++ int ret = 0; ++ char *value = NULL; ++ ++ debug3("%s: called", __func__); ++ ret = krb5_get_profile(ctx, &p); ++ if (ret) ++ return ret; ++ ++ ret = profile_get_string(p, "libdefaults", "default_ccache_name", NULL, NULL, &value); ++ if (ret || !value) ++ return ret; ++ ++ ret = ssh_krb5_expand_template(ccname, value); ++ ++ debug3("%s: returning with ccname = %s", __func__, *ccname); ++ return ret; ++} ++ ++krb5_error_code ++ssh_krb5_cc_new_unique(krb5_context ctx, krb5_ccache *ccache, int *need_environment) { ++ int tmpfd, ret, oerrno, type_len; ++ char *ccname = NULL; ++ mode_t old_umask; ++ char *type = NULL, *colon = NULL; ++ ++ debug3("%s: called", __func__); ++ if (need_environment) ++ *need_environment = 0; ++ ret = ssh_krb5_get_cctemplate(ctx, &ccname); ++ if (ret || !ccname || options.kerberos_unique_ticket) { ++ /* Otherwise, go with the old method */ ++ if (ccname) ++ free(ccname); ++ ccname = NULL; ++ ++ ret = asprintf(&ccname, ++ "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); ++ if (ret < 0) ++ return ENOMEM; ++ ++ old_umask = umask(0177); ++ tmpfd = mkstemp(ccname + strlen("FILE:")); + oerrno = errno; +- logit("fchmod(): %.100s", strerror(oerrno)); ++ umask(old_umask); ++ if (tmpfd == -1) { ++ logit("mkstemp(): %.100s", strerror(oerrno)); ++ return oerrno; ++ } ++ ++ if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { ++ oerrno = errno; ++ logit("fchmod(): %.100s", strerror(oerrno)); ++ close(tmpfd); ++ return oerrno; ++ } ++ /* make sure the KRB5CCNAME is set for non-standard location */ ++ if (need_environment) ++ *need_environment = 1; + close(tmpfd); +- return oerrno; + } +- close(tmpfd); + +- return (krb5_cc_resolve(ctx, ccname, ccache)); ++ debug3("%s: setting default ccname to %s", __func__, ccname); ++ /* set the default with already expanded user IDs */ ++ ret = krb5_cc_set_default_name(ctx, ccname); ++ if (ret) ++ return ret; ++ ++ if ((colon = strstr(ccname, ":")) != NULL) { ++ type_len = colon - ccname; ++ type = malloc((type_len + 1) * sizeof(char)); ++ if (type == NULL) ++ return ENOMEM; ++ strncpy(type, ccname, type_len); ++ type[type_len] = 0; ++ } else { ++ type = strdup(ccname); ++ } ++ ++ /* If we have a credential cache from krb5.conf, we need to switch ++ * a primary cache for this collection, if it supports that (non-FILE) ++ */ ++ if (krb5_cc_support_switch(ctx, type)) { ++ debug3("%s: calling cc_new_unique(%s)", __func__, ccname); ++ ret = krb5_cc_new_unique(ctx, type, NULL, ccache); ++ if (ret) ++ return ret; ++ ++ debug3("%s: calling cc_switch()", __func__); ++ return krb5_cc_switch(ctx, *ccache); ++ } else { ++ /* Otherwise, we can not create a unique ccname here (either ++ * it is already unique from above or the type does not support ++ * collections ++ */ ++ debug3("%s: calling cc_resolve(%s)", __func__, ccname); ++ return (krb5_cc_resolve(ctx, ccname, ccache)); ++ } + } + #endif /* !HEIMDAL */ + #endif /* KRB5 */ +diff --git a/auth.h b/auth.h +index 29491df9..fdab5040 100644 +--- a/auth.h ++++ b/auth.h +@@ -82,6 +82,7 @@ struct Authctxt { + krb5_principal krb5_user; + char *krb5_ticket_file; + char *krb5_ccname; ++ int krb5_set_env; + #endif + struct sshbuf *loginmsg; + +@@ -243,6 +244,6 @@ int sys_auth_passwd(struct ssh *, const char *); + + #if defined(KRB5) && !defined(HEIMDAL) + #include +-krb5_error_code ssh_krb5_cc_gen(krb5_context, krb5_ccache *); ++krb5_error_code ssh_krb5_cc_new_unique(krb5_context, krb5_ccache *, int *); + #endif + #endif +diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c +index 795992d9..0623a107 100644 +--- a/gss-serv-krb5.c ++++ b/gss-serv-krb5.c +@@ -114,7 +114,7 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) + /* This writes out any forwarded credentials from the structure populated + * during userauth. Called after we have setuid to the user */ + +-static void ++static int + ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + { + krb5_ccache ccache; +@@ -121,16 +121,17 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + krb5_error_code problem; + krb5_principal princ; + OM_uint32 maj_status, min_status; +- const char *new_ccname, *new_cctype; ++ int len; + const char *errmsg; ++ int set_env = 0; + + if (client->creds == NULL) { + debug("No credentials stored"); +- return; ++ return 0; + } + + if (ssh_gssapi_krb5_init() == 0) +- return; ++ return 0; + + #ifdef HEIMDAL + # ifdef HAVE_KRB5_CC_NEW_UNIQUE +@@ -144,14 +145,14 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + krb5_get_err_text(krb_context, problem)); + # endif + krb5_free_error_message(krb_context, errmsg); +- return; ++ return 0; + } + #else +- if ((problem = ssh_krb5_cc_gen(krb_context, &ccache))) { ++ if ((problem = ssh_krb5_cc_new_unique(krb_context, &ccache, &set_env)) != 0) { + errmsg = krb5_get_error_message(krb_context, problem); +- logit("ssh_krb5_cc_gen(): %.100s", errmsg); ++ logit("ssh_krb5_cc_new_unique(): %.100s", errmsg); + krb5_free_error_message(krb_context, errmsg); +- return; ++ return 0; + } + #endif /* #ifdef HEIMDAL */ + +@@ -160,7 +161,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + errmsg = krb5_get_error_message(krb_context, problem); + logit("krb5_parse_name(): %.100s", errmsg); + krb5_free_error_message(krb_context, errmsg); +- return; ++ return 0; + } + + if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) { +@@ -169,7 +170,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + krb5_free_error_message(krb_context, errmsg); + krb5_free_principal(krb_context, princ); + krb5_cc_destroy(krb_context, ccache); +- return; ++ return 0; + } + + krb5_free_principal(krb_context, princ); +@@ -178,37 +179,27 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + client->creds, ccache))) { + logit("gss_krb5_copy_ccache() failed"); + krb5_cc_destroy(krb_context, ccache); +- return; ++ return 0; + } + +- new_cctype = krb5_cc_get_type(krb_context, ccache); +- new_ccname = krb5_cc_get_name(krb_context, ccache); +- +- client->store.envvar = "KRB5CCNAME"; +-#ifdef USE_CCAPI +- xasprintf(&client->store.envval, "API:%s", new_ccname); +-#else +- if (new_ccname[0] == ':') +- new_ccname++; +- xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname); +- if (strcmp(new_cctype, "DIR") == 0) { +- char *p; +- p = strrchr(client->store.envval, '/'); +- if (p) +- *p = '\0'; +- } +-#endif ++ if (set_env) { ++ const char *filename = krb5_cc_get_name(krb_context, ccache); ++ client->store.envvar = "KRB5CCNAME"; ++ len = strlen(filename) + 6; ++ client->store.envval = xmalloc(len); ++ snprintf(client->store.envval, len, "FILE:%s", filename); ++ } + + #ifdef USE_PAM +- if (options.use_pam) ++ if (options.use_pam && set_env) + do_pam_putenv(client->store.envvar, client->store.envval); + #endif + + krb5_cc_close(krb_context, ccache); + + client->store.data = krb_context; + +- return; ++ return set_env; + } + + int +diff --git a/gss-serv.c b/gss-serv.c +index 6cae720e..16e55cbc 100644 +--- a/gss-serv.c ++++ b/gss-serv.c +@@ -320,13 +320,15 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) + } + + /* As user */ +-void ++int + ssh_gssapi_storecreds(void) + { + if (gssapi_client.mech && gssapi_client.mech->storecreds) { +- (*gssapi_client.mech->storecreds)(&gssapi_client); ++ return (*gssapi_client.mech->storecreds)(&gssapi_client); + } else + debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism"); ++ ++ return 0; + } + + /* This allows GSSAPI methods to do things to the childs environment based +diff --git a/servconf.c b/servconf.c +index cb578658..a6e01df2 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -122,6 +122,7 @@ initialize_server_options(ServerOptions *options) + options->kerberos_or_local_passwd = -1; + options->kerberos_ticket_cleanup = -1; + options->kerberos_get_afs_token = -1; ++ options->kerberos_unique_ticket = -1; + options->gss_authentication=-1; + options->gss_keyex = -1; + options->gss_cleanup_creds = -1; +@@ -315,6 +316,8 @@ fill_default_server_options(ServerOptions *options) + options->kerberos_ticket_cleanup = 1; + if (options->kerberos_get_afs_token == -1) + options->kerberos_get_afs_token = 0; ++ if (options->kerberos_unique_ticket == -1) ++ options->kerberos_unique_ticket = 0; + if (options->gss_authentication == -1) + options->gss_authentication = 0; + if (options->gss_keyex == -1) +@@ -447,7 +450,8 @@ typedef enum { + sPermitRootLogin, sLogFacility, sLogLevel, + sRhostsRSAAuthentication, sRSAAuthentication, + sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, +- sKerberosGetAFSToken, sChallengeResponseAuthentication, ++ sKerberosGetAFSToken, sKerberosUniqueTicket, ++ sChallengeResponseAuthentication, + sPasswordAuthentication, sKbdInteractiveAuthentication, + sListenAddress, sAddressFamily, + sPrintMotd, sPrintLastLog, sIgnoreRhosts, +@@ -526,11 +530,13 @@ static struct { + #else + { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, + #endif ++ { "kerberosuniqueticket", sKerberosUniqueTicket, SSHCFG_GLOBAL }, + #else + { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, + { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, ++ { "kerberosuniqueticket", sUnsupported, SSHCFG_GLOBAL }, + #endif + { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, + { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, +@@ -1437,6 +1443,10 @@ process_server_config_line(ServerOptions *options, char *line, + intptr = &options->kerberos_get_afs_token; + goto parse_flag; + ++ case sKerberosUniqueTicket: ++ intptr = &options->kerberos_unique_ticket; ++ goto parse_flag; ++ + case sGssAuthentication: + intptr = &options->gss_authentication; + goto parse_flag; +@@ -2507,6 +2517,7 @@ dump_config(ServerOptions *o) + # ifdef USE_AFS + dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); + # endif ++ dump_cfg_fmtint(sKerberosUniqueTicket, o->kerberos_unique_ticket); + #endif + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); +diff --git a/servconf.h b/servconf.h +index db8362c6..4fa42d64 100644 +--- a/servconf.h ++++ b/servconf.h +@@ -123,6 +123,8 @@ typedef struct { + * file on logout. */ + int kerberos_get_afs_token; /* If true, try to get AFS token if + * authenticated with Kerberos. */ ++ int kerberos_unique_ticket; /* If true, the aquired ticket will ++ * be stored in per-session ccache */ + int gss_authentication; /* If true, permit GSSAPI authentication */ + int gss_keyex; /* If true, permit GSSAPI key exchange */ + int gss_cleanup_creds; /* If true, destroy cred cache on logout */ +diff --git a/session.c b/session.c +index 85df6a27..480a5ead 100644 +--- a/session.c ++++ b/session.c +@@ -1033,7 +1033,8 @@ do_setup_env(struct ssh *ssh, Session *s, const char *shell) + /* Allow any GSSAPI methods that we've used to alter + * the childs environment as they see fit + */ +- ssh_gssapi_do_child(&env, &envsize); ++ if (s->authctxt->krb5_set_env) ++ ssh_gssapi_do_child(&env, &envsize); + #endif + + /* Set basic environment. */ +@@ -1105,7 +1106,7 @@ do_setup_env(struct ssh *ssh, Session *s, const char *shell) + } + #endif + #ifdef KRB5 +- if (s->authctxt->krb5_ccname) ++ if (s->authctxt->krb5_ccname && s->authctxt->krb5_set_env) + child_set_env(&env, &envsize, "KRB5CCNAME", + s->authctxt->krb5_ccname); + #endif +diff --git a/ssh-gss.h b/ssh-gss.h +index 6593e422..245178af 100644 +--- a/ssh-gss.h ++++ b/ssh-gss.h +@@ -62,7 +62,6 @@ + #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" + + typedef struct { +- char *filename; + char *envvar; + char *envval; + struct passwd *owner; +@@ -83,7 +82,7 @@ typedef struct ssh_gssapi_mech_struct { + int (*dochild) (ssh_gssapi_client *); + int (*userok) (ssh_gssapi_client *, char *); + int (*localname) (ssh_gssapi_client *, char **); +- void (*storecreds) (ssh_gssapi_client *); ++ int (*storecreds) (ssh_gssapi_client *); + int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *); + } ssh_gssapi_mech; + +@@ -127,7 +126,7 @@ int ssh_gssapi_userok(char *name); + OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_do_child(char ***, u_int *); + void ssh_gssapi_cleanup_creds(void); +-void ssh_gssapi_storecreds(void); ++int ssh_gssapi_storecreds(void); + const char *ssh_gssapi_displayname(void); + + char *ssh_gssapi_server_mechanisms(void); +diff --git a/sshd.c b/sshd.c +index edbe815c..89514e8a 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -2162,7 +2162,7 @@ main(int ac, char **av) + #ifdef GSSAPI + if (options.gss_authentication) { + temporarily_use_uid(authctxt->pw); +- ssh_gssapi_storecreds(); ++ authctxt->krb5_set_env = ssh_gssapi_storecreds(); + restore_uid(); + } + #endif +diff --git a/sshd_config.5 b/sshd_config.5 +index c0683d4a..2349f477 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -860,6 +860,12 @@ Specifies whether to automatically destroy the user's ticket cache + file on logout. + The default is + .Cm yes . ++.It Cm KerberosUniqueTicket ++Specifies whether to store the aquired tickets in the per-session credential ++cache or whether to use per-user credential cache, which might overwrite ++tickets aquired in different sessions of the same user. ++The default is ++.Cm no . + .It Cm KexAlgorithms + Specifies the available KEX (Key Exchange) algorithms. + Multiple algorithms must be comma-separated. diff --git a/openssh.spec b/openssh.spec index 1012bc4..391e436 100644 --- a/openssh.spec +++ b/openssh.spec @@ -182,12 +182,11 @@ Patch802: openssh-6.6p1-GSSAPIEnablek5users.patch # Documentation about GSSAPI # from https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=765655 Patch803: openssh-7.1p1-gssapi-documentation.patch -# use default_ccache_name from /etc/krb5.conf (#991186) -Patch804: openssh-6.3p1-krb5-use-default_ccache_name.patch +# Improve ccache handling in openssh (#991186, #1199363, #1566494) +# https://bugzilla.mindrot.org/show_bug.cgi?id=2775 +Patch804: openssh-7.7p1-gssapi-new-unique.patch # Respect k5login_directory option in krk5.conf (#1328243) Patch805: openssh-7.2p2-k5login_directory.patch -# Do not export KRBCCNAME if the default path is used (#1199363) -Patch806: openssh-7.5p1-gss-environment.patch # Support SHA2 in GSS key exchanges from draft-ssorce-gss-keyex-sha2-02 Patch807: openssh-7.5p1-gssapi-kex-with-ec.patch @@ -233,7 +232,6 @@ Patch952: openssh-7.6p1-pkcs11-ecdsa.patch # Opening tun devices fails + other regressions in OpenSSH v7.7 (#2855, #1567775) Patch953: openssh-7.7p1-tun-devices.patch - License: BSD Group: Applications/Internet Requires: /sbin/nologin @@ -435,7 +433,6 @@ popd %patch803 -p1 -b .gss-docs %patch804 -p1 -b .ccache_name %patch805 -p1 -b .k5login -%patch806 -p1 -b .gss-env # %patch900 -p1 -b .canohost %patch901 -p1 -b .kuserok @@ -813,7 +810,7 @@ getent passwd sshd >/dev/null || \ - Drop support for ExposeAuthenticationMethods option * Mon Sep 11 2017 Jakub Jelen - 7.5p1-6 + 0.10.3-2 -- Do not export KRBCCNAME if the default path is used (#1199363) +- Do not export KRB5CCNAME if the default path is used (#1199363) - Add enablement for openssl-ibmca and openssl-ibmpkcs11 (#1477636) - Add new GSSAPI kex algorithms with SHA-2, but leave them disabled for now - Enforce pam_sepermit for all logins in SSH (#1492313)