mirror of
https://github.com/tmux/tmux.git
synced 2024-12-25 10:58:48 +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.
This commit is contained in:
parent
6e9a9d265e
commit
d6306b634e
@ -123,6 +123,7 @@ dist_tmux_SOURCES = \
|
|||||||
cmd-select-pane.c \
|
cmd-select-pane.c \
|
||||||
cmd-select-window.c \
|
cmd-select-window.c \
|
||||||
cmd-send-keys.c \
|
cmd-send-keys.c \
|
||||||
|
cmd-server-access.c \
|
||||||
cmd-set-buffer.c \
|
cmd-set-buffer.c \
|
||||||
cmd-set-environment.c \
|
cmd-set-environment.c \
|
||||||
cmd-set-option.c \
|
cmd-set-option.c \
|
||||||
@ -172,6 +173,7 @@ dist_tmux_SOURCES = \
|
|||||||
screen-redraw.c \
|
screen-redraw.c \
|
||||||
screen-write.c \
|
screen-write.c \
|
||||||
screen.c \
|
screen.c \
|
||||||
|
server-acl.c \
|
||||||
server-client.c \
|
server-client.c \
|
||||||
server-fn.c \
|
server-fn.c \
|
||||||
server.c \
|
server.c \
|
||||||
|
1
client.c
1
client.c
@ -361,6 +361,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
|||||||
/* Send identify messages. */
|
/* Send identify messages. */
|
||||||
client_send_identify(ttynam, termname, caps, ncaps, cwd, feat);
|
client_send_identify(ttynam, termname, caps, ncaps, cwd, feat);
|
||||||
tty_term_free_list(caps, ncaps);
|
tty_term_free_list(caps, ncaps);
|
||||||
|
proc_flush_peer(client_peer);
|
||||||
|
|
||||||
/* Send first command. */
|
/* Send first command. */
|
||||||
if (msg == MSG_COMMAND) {
|
if (msg == MSG_COMMAND) {
|
||||||
|
@ -43,7 +43,7 @@ const struct cmd_entry cmd_attach_session_entry = {
|
|||||||
|
|
||||||
/* -t is special */
|
/* -t is special */
|
||||||
|
|
||||||
.flags = CMD_STARTSERVER,
|
.flags = CMD_STARTSERVER|CMD_READONLY,
|
||||||
.exec = cmd_attach_session_exec
|
.exec = cmd_attach_session_exec
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,6 +69,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
|
|||||||
|
|
||||||
if (c == NULL)
|
if (c == NULL)
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|
||||||
if (server_client_check_nested(c)) {
|
if (server_client_check_nested(c)) {
|
||||||
cmdq_error(item, "sessions should be nested with care, "
|
cmdq_error(item, "sessions should be nested with care, "
|
||||||
"unset $TMUX to force");
|
"unset $TMUX to force");
|
||||||
|
26
cmd-queue.c
26
cmd-queue.c
@ -19,9 +19,11 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <pwd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
|
||||||
@ -558,17 +560,31 @@ cmdq_add_message(struct cmdq_item *item)
|
|||||||
{
|
{
|
||||||
struct client *c = item->client;
|
struct client *c = item->client;
|
||||||
struct cmdq_state *state = item->state;
|
struct cmdq_state *state = item->state;
|
||||||
const char *name, *key;
|
const char *key;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
|
uid_t uid;
|
||||||
|
struct passwd *pw;
|
||||||
|
char *user = NULL;
|
||||||
|
|
||||||
tmp = cmd_print(item->cmd);
|
tmp = cmd_print(item->cmd);
|
||||||
if (c != NULL) {
|
if (c != NULL) {
|
||||||
name = c->name;
|
uid = proc_get_peer_uid(c->peer);
|
||||||
|
if (uid != getuid()) {
|
||||||
|
if ((pw = getpwuid(uid)) != NULL)
|
||||||
|
xasprintf(&user, "[%s]", pw->pw_name);
|
||||||
|
else
|
||||||
|
user = xstrdup("[unknown]");
|
||||||
|
} else
|
||||||
|
user = xstrdup("");
|
||||||
if (c->session != NULL && state->event.key != KEYC_NONE) {
|
if (c->session != NULL && state->event.key != KEYC_NONE) {
|
||||||
key = key_string_lookup_key(state->event.key, 0);
|
key = key_string_lookup_key(state->event.key, 0);
|
||||||
server_add_message("%s key %s: %s", name, key, tmp);
|
server_add_message("%s%s key %s: %s", c->name, user,
|
||||||
} else
|
key, tmp);
|
||||||
server_add_message("%s command: %s", name, tmp);
|
} else {
|
||||||
|
server_add_message("%s%s command: %s", c->name, user,
|
||||||
|
tmp);
|
||||||
|
}
|
||||||
|
free(user);
|
||||||
} else
|
} else
|
||||||
server_add_message("command: %s", tmp);
|
server_add_message("command: %s", tmp);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
|
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
@ -95,6 +95,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_select_window_entry;
|
||||||
extern const struct cmd_entry cmd_send_keys_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_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_buffer_entry;
|
||||||
extern const struct cmd_entry cmd_set_environment_entry;
|
extern const struct cmd_entry cmd_set_environment_entry;
|
||||||
extern const struct cmd_entry cmd_set_hook_entry;
|
extern const struct cmd_entry cmd_set_hook_entry;
|
||||||
@ -187,6 +188,7 @@ const struct cmd_entry *cmd_table[] = {
|
|||||||
&cmd_select_window_entry,
|
&cmd_select_window_entry,
|
||||||
&cmd_send_keys_entry,
|
&cmd_send_keys_entry,
|
||||||
&cmd_send_prefix_entry,
|
&cmd_send_prefix_entry,
|
||||||
|
&cmd_server_access_entry,
|
||||||
&cmd_set_buffer_entry,
|
&cmd_set_buffer_entry,
|
||||||
&cmd_set_environment_entry,
|
&cmd_set_environment_entry,
|
||||||
&cmd_set_hook_entry,
|
&cmd_set_hook_entry,
|
||||||
|
6
proc.c
6
proc.c
@ -349,6 +349,12 @@ proc_kill_peer(struct tmuxpeer *peer)
|
|||||||
peer->flags |= PEER_BAD;
|
peer->flags |= PEER_BAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
proc_flush_peer(struct tmuxpeer *peer)
|
||||||
|
{
|
||||||
|
imsg_flush(&peer->ibuf);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
proc_toggle_log(struct tmuxproc *tp)
|
proc_toggle_log(struct tmuxproc *tp)
|
||||||
{
|
{
|
||||||
|
182
server-acl.c
Normal file
182
server-acl.c
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/* $OpenBSD$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Holland Schutte, Jayson Morberg
|
||||||
|
* 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/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "tmux.h"
|
||||||
|
|
||||||
|
struct server_acl_user {
|
||||||
|
uid_t uid;
|
||||||
|
|
||||||
|
int flags;
|
||||||
|
#define SERVER_ACL_READONLY 0x1
|
||||||
|
|
||||||
|
RB_ENTRY(server_acl_user) entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
server_acl_cmp(struct server_acl_user *user1, struct server_acl_user *user2)
|
||||||
|
{
|
||||||
|
if (user1->uid < user2->uid)
|
||||||
|
return -1;
|
||||||
|
return user1->uid > user2->uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_HEAD(server_acl_entries, server_acl_user) server_acl_entries;
|
||||||
|
RB_GENERATE_STATIC(server_acl_entries, server_acl_user, entry, server_acl_cmp);
|
||||||
|
|
||||||
|
/* Initialize server_acl tree. */
|
||||||
|
void
|
||||||
|
server_acl_init(void)
|
||||||
|
{
|
||||||
|
RB_INIT(&server_acl_entries);
|
||||||
|
|
||||||
|
if (getuid() != 0)
|
||||||
|
server_acl_user_allow(0);
|
||||||
|
server_acl_user_allow(getuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find user entry. */
|
||||||
|
struct server_acl_user*
|
||||||
|
server_acl_user_find(uid_t uid)
|
||||||
|
{
|
||||||
|
struct server_acl_user find = { .uid = uid };
|
||||||
|
|
||||||
|
return RB_FIND(server_acl_entries, &server_acl_entries, &find);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Display the tree. */
|
||||||
|
void
|
||||||
|
server_acl_display(struct cmdq_item *item)
|
||||||
|
{
|
||||||
|
struct server_acl_user *loop;
|
||||||
|
struct passwd *pw;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
RB_FOREACH(loop, server_acl_entries, &server_acl_entries) {
|
||||||
|
if (loop->uid == 0)
|
||||||
|
continue;
|
||||||
|
if ((pw = getpwuid(loop->uid)) != NULL)
|
||||||
|
name = pw->pw_name;
|
||||||
|
else
|
||||||
|
name = "unknown";
|
||||||
|
if (loop->flags == SERVER_ACL_READONLY)
|
||||||
|
cmdq_print(item, "%s (R)", name);
|
||||||
|
else
|
||||||
|
cmdq_print(item, "%s (W)", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allow a user. */
|
||||||
|
void
|
||||||
|
server_acl_user_allow(uid_t uid)
|
||||||
|
{
|
||||||
|
struct server_acl_user *user;
|
||||||
|
|
||||||
|
user = server_acl_user_find(uid);
|
||||||
|
if (user == NULL) {
|
||||||
|
user = xcalloc(1, sizeof *user);
|
||||||
|
user->uid = uid;
|
||||||
|
RB_INSERT(server_acl_entries, &server_acl_entries, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deny a user (remove from the tree). */
|
||||||
|
void
|
||||||
|
server_acl_user_deny(uid_t uid)
|
||||||
|
{
|
||||||
|
struct server_acl_user *user;
|
||||||
|
|
||||||
|
user = server_acl_user_find(uid);
|
||||||
|
if (user != NULL) {
|
||||||
|
RB_REMOVE(server_acl_entries, &server_acl_entries, user);
|
||||||
|
free(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allow this user write access. */
|
||||||
|
void
|
||||||
|
server_acl_user_allow_write(uid_t uid)
|
||||||
|
{
|
||||||
|
struct server_acl_user *user;
|
||||||
|
struct client *c;
|
||||||
|
|
||||||
|
user = server_acl_user_find(uid);
|
||||||
|
if (user == NULL)
|
||||||
|
return;
|
||||||
|
user->flags &= ~SERVER_ACL_READONLY;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
|
uid = proc_get_peer_uid(c->peer);
|
||||||
|
if (uid != (uid_t)-1 && uid == user->uid)
|
||||||
|
c->flags &= ~CLIENT_READONLY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deny this user write access. */
|
||||||
|
void
|
||||||
|
server_acl_user_deny_write(uid_t uid)
|
||||||
|
{
|
||||||
|
struct server_acl_user *user;
|
||||||
|
struct client *c;
|
||||||
|
|
||||||
|
user = server_acl_user_find(uid);
|
||||||
|
if (user == NULL)
|
||||||
|
return;
|
||||||
|
user->flags |= SERVER_ACL_READONLY;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
|
uid = proc_get_peer_uid(c->peer);
|
||||||
|
if (uid == user->uid && uid == user->uid)
|
||||||
|
c->flags |= CLIENT_READONLY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the client's UID exists in the ACL list and if so, set as read only
|
||||||
|
* if needed. Return false if the user does not exist.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
server_acl_join(struct client *c)
|
||||||
|
{
|
||||||
|
struct server_acl_user *user;
|
||||||
|
uid_t uid = proc_get_peer_uid(c->peer);
|
||||||
|
|
||||||
|
user = server_acl_user_find(uid);
|
||||||
|
if (user == NULL)
|
||||||
|
return (0);
|
||||||
|
if (user->flags & SERVER_ACL_READONLY)
|
||||||
|
c->flags |= CLIENT_READONLY;
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get UID for user entry. */
|
||||||
|
uid_t
|
||||||
|
server_acl_get_uid(struct server_acl_user *user)
|
||||||
|
{
|
||||||
|
return (user->uid);
|
||||||
|
}
|
@ -2772,6 +2772,14 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback when command is not allowed. */
|
||||||
|
static enum cmd_retval
|
||||||
|
server_client_read_only(struct cmdq_item *item, __unused void *data)
|
||||||
|
{
|
||||||
|
cmdq_error(item, "client is read-only");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback when command is done. */
|
/* Callback when command is done. */
|
||||||
static enum cmd_retval
|
static enum cmd_retval
|
||||||
server_client_command_done(struct cmdq_item *item, __unused void *data)
|
server_client_command_done(struct cmdq_item *item, __unused void *data)
|
||||||
@ -2796,6 +2804,7 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
|
|||||||
char **argv, *cause;
|
char **argv, *cause;
|
||||||
struct cmd_parse_result *pr;
|
struct cmd_parse_result *pr;
|
||||||
struct args_value *values;
|
struct args_value *values;
|
||||||
|
struct cmdq_item *new_item;
|
||||||
|
|
||||||
if (c->flags & CLIENT_EXIT)
|
if (c->flags & CLIENT_EXIT)
|
||||||
return;
|
return;
|
||||||
@ -2834,7 +2843,12 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
|
|||||||
free(values);
|
free(values);
|
||||||
cmd_free_argv(argc, argv);
|
cmd_free_argv(argc, argv);
|
||||||
|
|
||||||
cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL));
|
if ((c->flags & CLIENT_READONLY) &&
|
||||||
|
!cmd_list_all_have(pr->cmdlist, CMD_READONLY))
|
||||||
|
new_item = cmdq_get_callback(server_client_read_only, NULL);
|
||||||
|
else
|
||||||
|
new_item = cmdq_get_command(pr->cmdlist, NULL);
|
||||||
|
cmdq_append(c, new_item);
|
||||||
cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL));
|
cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL));
|
||||||
|
|
||||||
cmd_list_free(pr->cmdlist);
|
cmd_list_free(pr->cmdlist);
|
||||||
@ -3072,9 +3086,11 @@ server_client_set_flags(struct client *c, const char *flags)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
log_debug("client %s set flag %s", c->name, next);
|
log_debug("client %s set flag %s", c->name, next);
|
||||||
if (not)
|
if (not) {
|
||||||
|
if (c->flags & CLIENT_READONLY)
|
||||||
|
flag &= ~CLIENT_READONLY;
|
||||||
c->flags &= ~flag;
|
c->flags &= ~flag;
|
||||||
else
|
} else
|
||||||
c->flags |= flag;
|
c->flags |= flag;
|
||||||
if (flag == CLIENT_CONTROL_NOOUTPUT)
|
if (flag == CLIENT_CONTROL_NOOUTPUT)
|
||||||
control_reset_offsets(c);
|
control_reset_offsets(c);
|
||||||
|
10
server.c
10
server.c
@ -245,6 +245,8 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
|
|||||||
evtimer_set(&server_ev_tidy, server_tidy_event, NULL);
|
evtimer_set(&server_ev_tidy, server_tidy_event, NULL);
|
||||||
evtimer_add(&server_ev_tidy, &tv);
|
evtimer_add(&server_ev_tidy, &tv);
|
||||||
|
|
||||||
|
server_acl_init();
|
||||||
|
|
||||||
server_add_accept(0);
|
server_add_accept(0);
|
||||||
proc_loop(server_proc, server_loop);
|
proc_loop(server_proc, server_loop);
|
||||||
|
|
||||||
@ -364,6 +366,7 @@ server_accept(int fd, short events, __unused void *data)
|
|||||||
struct sockaddr_storage sa;
|
struct sockaddr_storage sa;
|
||||||
socklen_t slen = sizeof sa;
|
socklen_t slen = sizeof sa;
|
||||||
int newfd;
|
int newfd;
|
||||||
|
struct client *c;
|
||||||
|
|
||||||
server_add_accept(0);
|
server_add_accept(0);
|
||||||
if (!(events & EV_READ))
|
if (!(events & EV_READ))
|
||||||
@ -380,11 +383,16 @@ server_accept(int fd, short events, __unused void *data)
|
|||||||
}
|
}
|
||||||
fatal("accept failed");
|
fatal("accept failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server_exit) {
|
if (server_exit) {
|
||||||
close(newfd);
|
close(newfd);
|
||||||
return;
|
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
@ -1488,6 +1488,44 @@ option.
|
|||||||
.D1 Pq alias: Ic rename
|
.D1 Pq alias: Ic rename
|
||||||
Rename the session to
|
Rename the session to
|
||||||
.Ar new-name .
|
.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
|
.Tg showmsgs
|
||||||
.It Xo Ic show-messages
|
.It Xo Ic show-messages
|
||||||
.Op Fl JT
|
.Op Fl JT
|
||||||
@ -5072,7 +5110,7 @@ The following variables are available, where appropriate:
|
|||||||
.It Li "client_name" Ta "" Ta "Name of client"
|
.It Li "client_name" Ta "" Ta "Name of client"
|
||||||
.It Li "client_pid" Ta "" Ta "PID of client process"
|
.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_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_session" Ta "" Ta "Name of the client's session"
|
||||||
.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any"
|
.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any"
|
||||||
.It Li "client_termname" Ta "" Ta "Terminal name of client"
|
.It Li "client_termname" Ta "" Ta "Terminal name of client"
|
||||||
|
12
tmux.h
12
tmux.h
@ -2025,6 +2025,7 @@ struct tmuxpeer *proc_add_peer(struct tmuxproc *, int,
|
|||||||
void (*)(struct imsg *, void *), void *);
|
void (*)(struct imsg *, void *), void *);
|
||||||
void proc_remove_peer(struct tmuxpeer *);
|
void proc_remove_peer(struct tmuxpeer *);
|
||||||
void proc_kill_peer(struct tmuxpeer *);
|
void proc_kill_peer(struct tmuxpeer *);
|
||||||
|
void proc_flush_peer(struct tmuxpeer *);
|
||||||
void proc_toggle_log(struct tmuxproc *);
|
void proc_toggle_log(struct tmuxproc *);
|
||||||
pid_t proc_fork_and_daemon(int *);
|
pid_t proc_fork_and_daemon(int *);
|
||||||
uid_t proc_get_peer_uid(struct tmuxpeer *);
|
uid_t proc_get_peer_uid(struct tmuxpeer *);
|
||||||
@ -3269,4 +3270,15 @@ struct window_pane *spawn_pane(struct spawn_context *, char **);
|
|||||||
/* regsub.c */
|
/* regsub.c */
|
||||||
char *regsub(const char *, const char *, const char *, int);
|
char *regsub(const char *, const char *, const char *, int);
|
||||||
|
|
||||||
|
/* server-acl.c */
|
||||||
|
void server_acl_init(void);
|
||||||
|
struct server_acl_user *server_acl_user_find(uid_t);
|
||||||
|
void server_acl_display(struct cmdq_item *);
|
||||||
|
void server_acl_user_allow(uid_t);
|
||||||
|
void server_acl_user_deny(uid_t);
|
||||||
|
void server_acl_user_allow_write(uid_t);
|
||||||
|
void server_acl_user_deny_write(uid_t);
|
||||||
|
int server_acl_join(struct client *);
|
||||||
|
uid_t server_acl_get_uid(struct server_acl_user *);
|
||||||
|
|
||||||
#endif /* TMUX_H */
|
#endif /* TMUX_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user