diff --git a/Makefile.am b/Makefile.am index 8b06c3ac8..81add05ef 100644 --- a/Makefile.am +++ b/Makefile.am @@ -240,6 +240,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 diff --git a/compat.h b/compat.h index 66a22640b..2a81845fb 100644 --- a/compat.h +++ b/compat.h @@ -441,6 +441,11 @@ void *reallocarray(void *, size_t, size_t); void *recallocarray(void *, size_t, size_t, size_t); #endif +#ifdef HAVE_KEYCTL +/* keyring-linux.c */ +void fork_session_keyring(void); +#endif + #ifdef HAVE_SYSTEMD /* systemd.c */ int systemd_activated(void); diff --git a/compat/keyring-linux.c b/compat/keyring-linux.c new file mode 100644 index 000000000..ab7e5d1db --- /dev/null +++ b/compat/keyring-linux.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2026 Ilya Priven + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* + * 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). Link the keys from the current + * session keyring into it so they remain available after that. + */ +void +fork_session_keyring(void) +{ + int32_t *keys = NULL; + long size = 0, n, i; + + /* KEYCTL_READ returns the full size; retry if keys were added. */ + for (;;) { + n = syscall(SYS_keyctl, KEYCTL_READ, KEY_SPEC_SESSION_KEYRING, + keys, size); + if (n <= size) + break; + size = n; + keys = xrealloc(keys, size); + } + n = n > 0 ? n / sizeof *keys : 0; + + /* + * Link the current session keyring into the thread keyring so we still + * possess its keys (and so are allowed to link them) after leaving it. + */ + if (syscall(SYS_keyctl, KEYCTL_LINK, KEY_SPEC_SESSION_KEYRING, + KEY_SPEC_THREAD_KEYRING) == -1) + log_debug("%s: link failed: %s", __func__, strerror(errno)); + + if (syscall(SYS_keyctl, KEYCTL_JOIN_SESSION_KEYRING, NULL) == -1) { + log_debug("%s: join failed: %s", __func__, strerror(errno)); + goto out; + } + for (i = 0; i < n; i++) { + if (syscall(SYS_keyctl, KEYCTL_LINK, keys[i], + KEY_SPEC_SESSION_KEYRING) == -1) { + log_debug("%s: link %d failed: %s", __func__, keys[i], + strerror(errno)); + } + } + if (syscall(SYS_keyctl, KEYCTL_LINK, KEY_SPEC_USER_KEYRING, + KEY_SPEC_SESSION_KEYRING) == -1) + log_debug("%s: link failed: %s", __func__, strerror(errno)); + +out: + syscall(SYS_keyctl, KEYCTL_CLEAR, KEY_SPEC_THREAD_KEYRING); + free(keys); +} diff --git a/configure.ac b/configure.ac index 703327bcf..0a7d81d1d 100644 --- a/configure.ac +++ b/configure.ac @@ -541,6 +541,21 @@ if test "x$enable_cgroups" = xyes; then fi fi +# Look for linux/keyctl.h, compat/keyring-linux.c used if found and enabled. +AC_ARG_ENABLE( + keyring, + AS_HELP_STRING(--disable-keyring, disable giving the server its own Linux session keyring) +) +if test "x$enable_keyring" != xno; then + AC_CHECK_HEADER(linux/keyctl.h, found_keyctl=yes, found_keyctl=no) + if test "x$found_keyctl" = xyes; then + AC_DEFINE(HAVE_KEYCTL) + elif test "x$enable_keyring" = xyes; then + AC_MSG_ERROR("linux/keyctl.h not found") + fi +fi +AM_CONDITIONAL(HAVE_KEYCTL, [test "x$found_keyctl" = xyes]) + # Enable image support. AC_ARG_ENABLE( images, diff --git a/server.c b/server.c index be4427954..540bfada0 100644 --- a/server.c +++ b/server.c @@ -220,6 +220,10 @@ server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base, TAILQ_INIT(&message_log); gettimeofday(&start_time, NULL); +#ifdef HAVE_KEYCTL + fork_session_keyring(); +#endif + #ifdef HAVE_SYSTEMD server_fd = systemd_create_socket(flags, &cause); #else