mirror of
https://github.com/tmux/tmux.git
synced 2024-11-17 18:08:51 +00:00
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.
This commit is contained in:
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");
|
||||
|
147
cmd-server-access.c
Normal file
147
cmd-server-access.c
Normal file
@ -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
Block a user