openssh/openssh-7.7p1-gssapi-new-unique.patch
DistroBaker d029bb77ce Merged update from upstream sources
This is an automated DistroBaker update from upstream sources.
If you do not know what this is about or would like to opt out,
contact the OSCI team.

Source: https://src.fedoraproject.org/rpms/openssh.git#44aae310bd4e0f19369ea1c91ada03334f29c843
2021-03-22 10:10:25 +00:00

648 lines
19 KiB
Diff

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 <unistd.h>
#include <string.h>
#include <krb5.h>
+#include <profile.h>
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,11 +223,54 @@ 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;
+
+ krb5_cc_destroy(ctx, authctxt->krb5_fwd_ccache);
authctxt->krb5_fwd_ccache = NULL;
+
+ ret = krb5_cccol_cursor_new(ctx, &cursor);
+ if (ret)
+ goto out;
+
+ 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 if (authctxt->krb5_ccname != NULL) {
+ /* 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));
+ }
+ }
+ }
+ krb5_cccol_cursor_free(ctx, &cursor);
}
+out:
if (authctxt->krb5_user) {
krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
authctxt->krb5_user = NULL;
@@ -237,36 +281,188 @@ krb5_cleanup_proc(Authctxt *authctxt)
}
}
-#ifndef HEIMDAL
+
+#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_f("called, template = %s", 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_f("unsupported token %s in %s", 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_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
- int tmpfd, ret, oerrno;
- char ccname[40];
+ssh_krb5_get_cctemplate(krb5_context ctx, char **ccname) {
+ profile_t p;
+ int ret = 0;
+ char *value = NULL;
+
+ debug3_f("called");
+ 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_f("returning with ccname = %s", *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;
- 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;
- }
+ debug3_f("called");
+ if (need_environment)
+ *need_environment = 0;
+ ret = ssh_krb5_get_cctemplate(ctx, &ccname);
+ if (ret || !ccname || options.kerberos_unique_ccache) {
+ /* 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;
- if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
+ 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_f("setting default ccname to %s", 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_f("calling cc_new_unique(%s)", ccname);
+ ret = krb5_cc_new_unique(ctx, type, NULL, ccache);
+ free(type);
+ if (ret)
+ return ret;
+
+ debug3_f("calling cc_switch()");
+ 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
+ */
+ free(type);
+ debug3_f("calling cc_resolve(%s)", 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;
@@ -238,7 +239,7 @@ int sys_auth_passwd(struct ssh *, const char *);
int sys_auth_passwd(struct ssh *, const char *);
#if defined(KRB5) && !defined(HEIMDAL)
-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 /* AUTH_H */
diff -up openssh-7.9p1/gss-serv-krb5.c.ccache_name openssh-7.9p1/gss-serv-krb5.c
--- openssh-7.9p1/gss-serv-krb5.c.ccache_name 2019-03-01 15:17:42.708611802 +0100
+++ openssh-7.9p1/gss-serv-krb5.c 2019-03-01 15:17:42.713611844 +0100
@@ -267,7 +267,7 @@ ssh_gssapi_krb5_cmdok(krb5_principal pri
/* 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;
@@ -276,14 +276,15 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
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
@@ -297,14 +298,14 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
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 */
@@ -313,7 +314,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
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))) {
@@ -322,7 +323,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
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);
@@ -331,32 +332,21 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
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);
- client->store.filename = NULL;
-#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';
+
+ if (set_env) {
+ client->store.envvar = "KRB5CCNAME";
}
if ((strcmp(new_cctype, "FILE") == 0) || (strcmp(new_cctype, "DIR") == 0))
client->store.filename = xstrdup(new_ccname);
-#endif
#ifdef USE_PAM
- if (options.use_pam)
+ if (options.use_pam && set_env)
do_pam_putenv(client->store.envvar, client->store.envval);
#endif
@@ -361,7 +355,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
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 child's environment based
@@ -498,9 +500,7 @@ ssh_gssapi_rekey_creds() {
char *envstr;
#endif
- if (gssapi_client.store.filename == NULL &&
- gssapi_client.store.envval == NULL &&
- gssapi_client.store.envvar == NULL)
+ if (gssapi_client.store.envval == NULL)
return;
ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
diff -up openssh-7.9p1/servconf.c.ccache_name openssh-7.9p1/servconf.c
--- openssh-7.9p1/servconf.c.ccache_name 2019-03-01 15:17:42.704611768 +0100
+++ openssh-7.9p1/servconf.c 2019-03-01 15:17:42.713611844 +0100
@@ -123,6 +123,7 @@ initialize_server_options(ServerOptions
options->kerberos_or_local_passwd = -1;
options->kerberos_ticket_cleanup = -1;
options->kerberos_get_afs_token = -1;
+ options->kerberos_unique_ccache = -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_ccache == -1)
+ options->kerberos_unique_ccache = 0;
if (options->gss_authentication == -1)
options->gss_authentication = 0;
if (options->gss_keyex == -1)
@@ -447,7 +450,8 @@ typedef enum {
sPermitRootLogin, sLogFacility, sLogLevel, sLogVerbose,
sRhostsRSAAuthentication, sRSAAuthentication,
sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup,
- sKerberosGetAFSToken, sChallengeResponseAuthentication,
+ sKerberosGetAFSToken, sKerberosUniqueCCache,
+ sChallengeResponseAuthentication,
sPasswordAuthentication, sKbdInteractiveAuthentication,
sListenAddress, sAddressFamily,
sPrintMotd, sPrintLastLog, sIgnoreRhosts,
@@ -526,11 +530,13 @@ static struct {
#else
{ "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL },
#endif
+ { "kerberosuniqueccache", sKerberosUniqueCCache, SSHCFG_GLOBAL },
#else
{ "kerberosauthentication", sUnsupported, SSHCFG_ALL },
{ "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL },
{ "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL },
{ "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL },
+ { "kerberosuniqueccache", 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 sKerberosUniqueCCache:
+ intptr = &options->kerberos_unique_ccache;
+ 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(sKerberosUniqueCCache, o->kerberos_unique_ccache);
#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_ccache; /* If true, the acquired 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 child's 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
@@ -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,14 @@ Specifies whether to automatically destroy the user's ticket cache
file on logout.
The default is
.Cm yes .
+.It Cm KerberosUniqueCCache
+Specifies whether to store the acquired tickets in the per-session credential
+cache under /tmp/ or whether to use per-user credential cache as configured in
+.Pa /etc/krb5.conf .
+The default value
+.Cm no
+can lead to overwriting previous tickets by subseqent connections to the same
+user account.
.It Cm KexAlgorithms
Specifies the available KEX (Key Exchange) algorithms.
Multiple algorithms must be comma-separated.