mirror of https://github.com/tmux/tmux.git
Add an ACL list for users connecting to the tmux socket. Users may be
forbidden from attaching, forced to attach read-only, or allowed to attach read-write. A new command, server-access, configures the list. tmux gets the user using getpeereid(3) of the client socket. Users must still configure file system permissions manually. From Dallas Lyons and others.pull/3201/head
parent
d4423dca19
commit
cd692b5a68
2
Makefile
2
Makefile
|
@ -55,6 +55,7 @@ SRCS= alerts.c \
|
|||
cmd-select-pane.c \
|
||||
cmd-select-window.c \
|
||||
cmd-send-keys.c \
|
||||
cmd-server-access.c \
|
||||
cmd-set-buffer.c \
|
||||
cmd-set-environment.c \
|
||||
cmd-set-option.c \
|
||||
|
@ -104,6 +105,7 @@ SRCS= alerts.c \
|
|||
screen-redraw.c \
|
||||
screen-write.c \
|
||||
screen.c \
|
||||
server-acl.c \
|
||||
server-client.c \
|
||||
server-fn.c \
|
||||
server.c \
|
||||
|
|
1
client.c
1
client.c
|
@ -360,6 +360,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
|||
/* Send identify messages. */
|
||||
client_send_identify(ttynam, termname, caps, ncaps, cwd, feat);
|
||||
tty_term_free_list(caps, ncaps);
|
||||
proc_flush_peer(client_peer);
|
||||
|
||||
/* Send first command. */
|
||||
if (msg == MSG_COMMAND) {
|
||||
|
|
|
@ -43,7 +43,7 @@ const struct cmd_entry cmd_attach_session_entry = {
|
|||
|
||||
/* -t is special */
|
||||
|
||||
.flags = CMD_STARTSERVER,
|
||||
.flags = CMD_STARTSERVER|CMD_READONLY,
|
||||
.exec = cmd_attach_session_exec
|
||||
};
|
||||
|
||||
|
@ -69,6 +69,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
|
|||
|
||||
if (c == NULL)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
if (server_client_check_nested(c)) {
|
||||
cmdq_error(item, "sessions should be nested with care, "
|
||||
"unset $TMUX to force");
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2021 Dallas Lyons <dallasdlyons@gmail.com>
|
||||
*
|
||||
* 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 <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/*
|
||||
* Controls access to session.
|
||||
*/
|
||||
|
||||
static enum cmd_retval cmd_server_access_exec(struct cmd *, struct cmdq_item *);
|
||||
|
||||
const struct cmd_entry cmd_server_access_entry = {
|
||||
.name = "server-access",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "adlrw", 0, 1, NULL },
|
||||
.usage = "[-adlrw]" CMD_TARGET_PANE_USAGE " [user]",
|
||||
|
||||
.flags = CMD_CLIENT_CANFAIL,
|
||||
.exec = cmd_server_access_exec
|
||||
};
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_server_access_deny(struct cmdq_item *item, struct passwd *pw)
|
||||
{
|
||||
struct client *loop;
|
||||
struct server_acl_user *user;
|
||||
uid_t uid;
|
||||
|
||||
if ((user = server_acl_user_find(pw->pw_uid)) == NULL) {
|
||||
cmdq_error(item, "user %s not found", pw->pw_name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
TAILQ_FOREACH(loop, &clients, entry) {
|
||||
uid = proc_get_peer_uid(loop->peer);
|
||||
if (uid == server_acl_get_uid(user)) {
|
||||
loop->exit_message = xstrdup("access not allowed");
|
||||
loop->flags |= CLIENT_EXIT;
|
||||
}
|
||||
}
|
||||
server_acl_user_deny(pw->pw_uid);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct client *c = cmdq_get_target_client(item);
|
||||
char *name;
|
||||
struct passwd *pw = NULL;
|
||||
|
||||
if (args_has(args, 'l')) {
|
||||
server_acl_display(item);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
if (args_count(args) == 0) {
|
||||
cmdq_error(item, "missing user arguement");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
name = format_single(item, args_string(args, 0), c, NULL, NULL, NULL);
|
||||
if (*name != '\0')
|
||||
pw = getpwnam(name);
|
||||
if (pw == NULL) {
|
||||
cmdq_error(item, "unknown user: %s", name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
free(name);
|
||||
|
||||
if (pw->pw_uid == 0 || pw->pw_uid == getuid()) {
|
||||
cmdq_error(item, "%s owns the server, can't change access",
|
||||
pw->pw_name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (args_has(args, 'a') && args_has(args, 'd')) {
|
||||
cmdq_error(item, "-a and -d cannot be used together");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (args_has(args, 'w') && args_has(args, 'r')) {
|
||||
cmdq_error(item, "-r and -w cannot be used together");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (args_has(args, 'd'))
|
||||
return (cmd_server_access_deny(item, pw));
|
||||
if (args_has(args, 'a')) {
|
||||
if (server_acl_user_find(pw->pw_uid) != NULL) {
|
||||
cmdq_error(item, "user %s is already added",
|
||||
pw->pw_name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_acl_user_allow(pw->pw_uid);
|
||||
/* Do not return - allow -r or -w with -a. */
|
||||
} else if (args_has(args, 'r') || args_has(args, 'w')) {
|
||||
/* -r or -w implies -a if user does not exist. */
|
||||
if (server_acl_user_find(pw->pw_uid) == NULL)
|
||||
server_acl_user_allow(pw->pw_uid);
|
||||
}
|
||||
|
||||
if (args_has(args, 'w')) {
|
||||
if (server_acl_user_find(pw->pw_uid) == NULL) {
|
||||
cmdq_error(item, "user %s not found", pw->pw_name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_acl_user_allow_write(pw->pw_uid);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
if (args_has(args, 'r')) {
|
||||
if (server_acl_user_find(pw->pw_uid) == NULL) {
|
||||
cmdq_error(item, "user %s not found", pw->pw_name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_acl_user_deny_write(pw->pw_uid);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
2
cmd.c
2
cmd.c
|
@ -96,6 +96,7 @@ extern const struct cmd_entry cmd_select_pane_entry;
|
|||
extern const struct cmd_entry cmd_select_window_entry;
|
||||
extern const struct cmd_entry cmd_send_keys_entry;
|
||||
extern const struct cmd_entry cmd_send_prefix_entry;
|
||||
extern const struct cmd_entry cmd_server_access_entry;
|
||||
extern const struct cmd_entry cmd_set_buffer_entry;
|
||||
extern const struct cmd_entry cmd_set_environment_entry;
|
||||
extern const struct cmd_entry cmd_set_hook_entry;
|
||||
|
@ -188,6 +189,7 @@ const struct cmd_entry *cmd_table[] = {
|
|||
&cmd_select_window_entry,
|
||||
&cmd_send_keys_entry,
|
||||
&cmd_send_prefix_entry,
|
||||
&cmd_server_access_entry,
|
||||
&cmd_set_buffer_entry,
|
||||
&cmd_set_environment_entry,
|
||||
&cmd_set_hook_entry,
|
||||
|
|
6
proc.c
6
proc.c
|
@ -337,6 +337,12 @@ proc_kill_peer(struct tmuxpeer *peer)
|
|||
peer->flags |= PEER_BAD;
|
||||
}
|
||||
|
||||
void
|
||||
proc_flush_peer(struct tmuxpeer *peer)
|
||||
{
|
||||
imsg_flush(&peer->ibuf);
|
||||
}
|
||||
|
||||
void
|
||||
proc_toggle_log(struct tmuxproc *tp)
|
||||
{
|
||||
|
|
16
server.c
16
server.c
|
@ -239,6 +239,8 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
|
|||
evtimer_set(&server_ev_tidy, server_tidy_event, NULL);
|
||||
evtimer_add(&server_ev_tidy, &tv);
|
||||
|
||||
server_acl_init();
|
||||
|
||||
server_add_accept(0);
|
||||
proc_loop(server_proc, server_loop);
|
||||
|
||||
|
@ -355,9 +357,10 @@ server_update_socket(void)
|
|||
static void
|
||||
server_accept(int fd, short events, __unused void *data)
|
||||
{
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t slen = sizeof sa;
|
||||
int newfd;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t slen = sizeof sa;
|
||||
int newfd;
|
||||
struct client *c;
|
||||
|
||||
server_add_accept(0);
|
||||
if (!(events & EV_READ))
|
||||
|
@ -374,11 +377,16 @@ server_accept(int fd, short events, __unused void *data)
|
|||
}
|
||||
fatal("accept failed");
|
||||
}
|
||||
|
||||
if (server_exit) {
|
||||
close(newfd);
|
||||
return;
|
||||
}
|
||||
server_client_create(newfd);
|
||||
c = server_client_create(newfd);
|
||||
if (!server_acl_join(c)) {
|
||||
c->exit_message = xstrdup("access not allowed");
|
||||
c->flags |= CLIENT_EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
40
tmux.1
40
tmux.1
|
@ -1485,6 +1485,44 @@ option.
|
|||
.D1 Pq alias: Ic rename
|
||||
Rename the session to
|
||||
.Ar new-name .
|
||||
.It Xo Ic server-access
|
||||
.Op Fl adlrw
|
||||
.Op Ar user
|
||||
.Xc
|
||||
Change the access or read/write permission of
|
||||
.Ar user .
|
||||
The user running the
|
||||
.Nm
|
||||
server (its owner) and the root user cannot be changed and are always
|
||||
permitted access.
|
||||
.Pp
|
||||
.Fl a
|
||||
and
|
||||
.Fl d
|
||||
are used to give or revoke access for the specified user.
|
||||
If the user is already attached, the
|
||||
.Fl d
|
||||
flag causes their clients to be detached.
|
||||
.Pp
|
||||
.Fl r
|
||||
and
|
||||
.Fl w
|
||||
change the permissions for
|
||||
.Ar user :
|
||||
.Fl r
|
||||
makes their clients read-only and
|
||||
.Fl w
|
||||
writable.
|
||||
.Fl l
|
||||
lists current access permissions.
|
||||
.Pp
|
||||
By default, the access list is empty and
|
||||
.Nm
|
||||
creates sockets with file system permissions preventing access by any user
|
||||
other than the owner (and root).
|
||||
These permissions must be changed manually.
|
||||
Great care should be taken not to allow access to untrusted users even
|
||||
read-only.
|
||||
.Tg showmsgs
|
||||
.It Xo Ic show-messages
|
||||
.Op Fl JT
|
||||
|
@ -5069,7 +5107,7 @@ The following variables are available, where appropriate:
|
|||
.It Li "client_name" Ta "" Ta "Name of client"
|
||||
.It Li "client_pid" Ta "" Ta "PID of client process"
|
||||
.It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed"
|
||||
.It Li "client_readonly" Ta "" Ta "1 if client is readonly"
|
||||
.It Li "client_readonly" Ta "" Ta "1 if client is read-only"
|
||||
.It Li "client_session" Ta "" Ta "Name of the client's session"
|
||||
.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any"
|
||||
.It Li "client_termname" Ta "" Ta "Terminal name of client"
|
||||
|
|
Loading…
Reference in New Issue