Give the server its own session keyring on Linux

The server otherwise keeps the session keyring of the login session that
started it, which pam_keyinit(8) may revoke when that session ends,
leaving the server and its panes unable to use the kernel keyring. Join
a new session keyring with the user keyring linked in once the
configuration file has been loaded, and revoke it when the server exits.

This is controlled by a new session-keyring server option, on by
default.
This commit is contained in:
Ilya Priven
2026-10-12 11:26:28 +00:00
parent 82abcd175c
commit bba451c959
8 changed files with 83 additions and 0 deletions

View File

@@ -239,6 +239,11 @@ if NEED_FORKPTY
nodist_tmux_SOURCES += compat/forkpty-@PLATFORM@.c
endif
# Add compat file for Linux session keyrings.
if HAVE_KEYCTL
nodist_tmux_SOURCES += compat/keyring-linux.c
endif
# Add compat file for systemd.
if HAVE_SYSTEMD
nodist_tmux_SOURCES += compat/systemd.c

4
cfg.c
View File

@@ -62,6 +62,10 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
}
prompt_load_history();
#ifdef HAVE_KEYCTL
if (options_get_number(global_options, "session-keyring"))
keyring_join();
#endif
return (CMD_RETURN_NORMAL);
}

View File

@@ -441,6 +441,12 @@ void *reallocarray(void *, size_t, size_t);
void *recallocarray(void *, size_t, size_t, size_t);
#endif
#ifdef HAVE_KEYCTL
/* keyring-linux.c */
void keyring_join(void);
void keyring_revoke(void);
#endif
#ifdef HAVE_SYSTEMD
/* systemd.c */
int systemd_activated(void);

42
compat/keyring-linux.c Normal file
View File

@@ -0,0 +1,42 @@
#include <sys/types.h>
#include <sys/syscall.h>
#include <linux/keyctl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
/* Serial of the session keyring we joined, or 0 if none. */
static long keyring_serial;
/*
* Create a new session keyring since the current one belongs to the login
* session which we're meant to outlive (and pam_keyinit(8) is likely to revoke
* the login session's keyring when it ends).
*/
void
keyring_join(void)
{
long serial;
serial = syscall(SYS_keyctl, KEYCTL_JOIN_SESSION_KEYRING, NULL);
if (serial == -1) {
log_debug("%s: join failed: %s", __func__, strerror(errno));
return;
}
if (syscall(SYS_keyctl, KEYCTL_LINK, KEY_SPEC_USER_KEYRING,
KEY_SPEC_SESSION_KEYRING) == -1)
log_debug("%s: link failed: %s", __func__, strerror(errno));
keyring_serial = serial;
}
void
keyring_revoke(void)
{
if (keyring_serial == 0)
return;
if (syscall(SYS_keyctl, KEYCTL_REVOKE, keyring_serial) == -1)
log_debug("%s: revoke failed: %s", __func__, strerror(errno));
}

View File

@@ -795,6 +795,13 @@ else
AC_LIBOBJ(daemon)
fi
# Look for linux/keyctl.h, compat/keyring-linux.c used if found.
AC_CHECK_HEADER(linux/keyctl.h, found_keyctl=yes, found_keyctl=no)
if test "x$found_keyctl" = xyes; then
AC_DEFINE(HAVE_KEYCTL)
fi
AM_CONDITIONAL(HAVE_KEYCTL, [test "x$found_keyctl" = xyes])
# Look for stravis, compat/{vis,unvis}.c used if missing.
AC_CHECK_FUNC(stravis, found_stravis=yes, found_stravis=no)
if test "x$found_stravis" = xyes; then

View File

@@ -532,6 +532,14 @@ const struct options_table_entry options_table[] = {
.text = "Maximum number of commands to keep in history."
},
{ .name = "session-keyring",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SERVER,
.default_num = 1,
.text = "Whether the server should join a new session keyring "
"(Linux only)."
},
{ .name = "set-clipboard",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SERVER,

View File

@@ -259,6 +259,9 @@ server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base,
job_kill_all();
prompt_save_history();
#ifdef HAVE_KEYCTL
keyring_revoke();
#endif
exit(0);
}

8
tmux.1
View File

@@ -5096,6 +5096,14 @@ each client.
.It Ic prompt\-history\-limit Ar number
Set the number of history items to save in the history file for each type of
command prompt.
.It Xo Ic session\-keyring
.Op Cm on | off
.Xc
On Linux, if enabled (the default), the server joins a new session keyring with
the user keyring linked into it, rather than keeping the session keyring of the
login session it was started from, which may be revoked when that session ends.
The new keyring is revoked when the server exits.
This option is checked once the configuration file has been loaded.
.It Xo Ic set\-clipboard
.Op Cm on | external | off
.Xc