diff --git a/tmux.1 b/tmux.1 index be1582933..88eb9e7f3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.1173 2026/09/23 12:37:50 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.1174 2026/09/25 13:44:49 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: September 23 2026 $ +.Dd $Mdocdate: September 25 2026 $ .Dt TMUX 1 .Os .Sh NAME @@ -9192,6 +9192,9 @@ See the option for details. .It Ev TMUX_TMPDIR The parent directory of the directory containing the server sockets. +It must be an absolute path to an existing directory and must not contain +.Ql .. ; +if it is empty it is ignored. See the .Fl L option for details. diff --git a/tmux.c b/tmux.c index 51b0c29eb..1ea10b767 100644 --- a/tmux.c +++ b/tmux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.c,v 1.224 2026/09/25 09:11:18 nicm Exp $ */ +/* $OpenBSD: tmux.c,v 1.225 2026/09/25 13:44:49 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -201,10 +201,33 @@ expand_paths(const char *s, char ***paths, u_int *n, int no_realpath) free(copy); } +static int +check_socket_path(const char *path, char **cause) +{ + char *copy, *next, *tmp; + + if (*path != '/') { + xasprintf(cause, "socket directory %s is not an absolute path", + path); + return (0); + } + copy = tmp = xstrdup(path); + while ((next = strsep(&tmp, "/")) != NULL) { + if (strcmp(next, "..") == 0) { + xasprintf(cause, "socket directory %s contains ..", + path); + free(copy); + return (0); + } + } + free(copy); + return (1); +} + static char * make_label(const char *label, char **cause) { - char **paths, *path, *base; + char **paths, *path = NULL, *base, resolved[PATH_MAX]; u_int i, n; struct stat sb; uid_t uid; @@ -214,15 +237,34 @@ make_label(const char *label, char **cause) label = "default"; uid = getuid(); - expand_paths(TMUX_SOCK, &paths, &n, 0); - if (n == 0) { - xasprintf(cause, "no suitable socket path"); - return (NULL); + /* + * An unset variable has already been dropped and an empty one is + * skipped, but anything else must be an existing absolute path with no + * ".." or it is an error. + */ + expand_paths(TMUX_SOCK, &paths, &n, 1); + for (i = 0; i < n; i++) { + if (*paths[i] == '\0') + continue; + if (!check_socket_path(paths[i], cause)) + break; + if (realpath(paths[i], resolved) == NULL) { + xasprintf(cause, + "couldn't resolve socket directory %s (%s)", + paths[i], strerror(errno)); + break; + } + path = xstrdup(resolved); + break; } - path = paths[0]; /* can only have one socket! */ - for (i = 1; i < n; i++) + for (i = 0; i < n; i++) free(paths[i]); free(paths); + if (path == NULL) { + if (*cause == NULL) + xasprintf(cause, "no suitable socket path"); + return (NULL); + } xasprintf(&base, "%s/tmux-%ld", path, (long)uid); free(path);