xref: /openbsd-src/usr.bin/tmux/server-acl.c (revision 3e8355bd15d76a0b1ae5fe25085a5e74afed9b73)
1*3e8355bdSnicm /* $OpenBSD: server-acl.c,v 1.2 2022/05/30 12:55:25 nicm Exp $ */
28ab000fcSnicm 
38ab000fcSnicm /*
48ab000fcSnicm  * Copyright (c) 2021 Holland Schutte, Jayson Morberg
58ab000fcSnicm  * Copyright (c) 2021 Dallas Lyons <dallasdlyons@gmail.com>
68ab000fcSnicm  *
78ab000fcSnicm  * Permission to use, copy, modify, and distribute this software for any
88ab000fcSnicm  * purpose with or without fee is hereby granted, provided that the above
98ab000fcSnicm  * copyright notice and this permission notice appear in all copies.
108ab000fcSnicm  *
118ab000fcSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
128ab000fcSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
138ab000fcSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
148ab000fcSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
158ab000fcSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
168ab000fcSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
178ab000fcSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
188ab000fcSnicm  */
198ab000fcSnicm 
208ab000fcSnicm #include <sys/types.h>
218ab000fcSnicm #include <sys/stat.h>
228ab000fcSnicm #include <sys/socket.h>
238ab000fcSnicm 
248ab000fcSnicm #include <ctype.h>
258ab000fcSnicm #include <pwd.h>
268ab000fcSnicm #include <stdlib.h>
278ab000fcSnicm #include <string.h>
288ab000fcSnicm #include <unistd.h>
298ab000fcSnicm 
308ab000fcSnicm #include "tmux.h"
318ab000fcSnicm 
328ab000fcSnicm struct server_acl_user {
338ab000fcSnicm 	uid_t				uid;
348ab000fcSnicm 
358ab000fcSnicm 	int				flags;
368ab000fcSnicm #define SERVER_ACL_READONLY 0x1
378ab000fcSnicm 
388ab000fcSnicm 	RB_ENTRY(server_acl_user)	entry;
398ab000fcSnicm };
408ab000fcSnicm 
418ab000fcSnicm static int
server_acl_cmp(struct server_acl_user * user1,struct server_acl_user * user2)428ab000fcSnicm server_acl_cmp(struct server_acl_user *user1, struct server_acl_user *user2)
438ab000fcSnicm {
448ab000fcSnicm 	if (user1->uid < user2->uid)
45*3e8355bdSnicm 		return (-1);
46*3e8355bdSnicm 	return (user1->uid > user2->uid);
478ab000fcSnicm }
488ab000fcSnicm 
498ab000fcSnicm RB_HEAD(server_acl_entries, server_acl_user) server_acl_entries;
508ab000fcSnicm RB_GENERATE_STATIC(server_acl_entries, server_acl_user, entry, server_acl_cmp);
518ab000fcSnicm 
528ab000fcSnicm /* Initialize server_acl tree. */
538ab000fcSnicm void
server_acl_init(void)548ab000fcSnicm server_acl_init(void)
558ab000fcSnicm {
568ab000fcSnicm 	RB_INIT(&server_acl_entries);
578ab000fcSnicm 
588ab000fcSnicm 	if (getuid() != 0)
598ab000fcSnicm 		server_acl_user_allow(0);
608ab000fcSnicm 	server_acl_user_allow(getuid());
618ab000fcSnicm }
628ab000fcSnicm 
638ab000fcSnicm /* Find user entry. */
648ab000fcSnicm struct server_acl_user*
server_acl_user_find(uid_t uid)658ab000fcSnicm server_acl_user_find(uid_t uid)
668ab000fcSnicm {
678ab000fcSnicm 	struct server_acl_user	find = { .uid = uid };
688ab000fcSnicm 
69*3e8355bdSnicm 	return (RB_FIND(server_acl_entries, &server_acl_entries, &find));
708ab000fcSnicm }
718ab000fcSnicm 
728ab000fcSnicm /* Display the tree. */
738ab000fcSnicm void
server_acl_display(struct cmdq_item * item)748ab000fcSnicm server_acl_display(struct cmdq_item *item)
758ab000fcSnicm {
768ab000fcSnicm 	struct server_acl_user	*loop;
778ab000fcSnicm 	struct passwd		*pw;
788ab000fcSnicm 	const char		*name;
798ab000fcSnicm 
808ab000fcSnicm 	RB_FOREACH(loop, server_acl_entries, &server_acl_entries) {
818ab000fcSnicm 		if (loop->uid == 0)
828ab000fcSnicm 			continue;
838ab000fcSnicm 		if ((pw = getpwuid(loop->uid)) != NULL)
848ab000fcSnicm 			name = pw->pw_name;
858ab000fcSnicm 		else
868ab000fcSnicm 			name = "unknown";
878ab000fcSnicm 		if (loop->flags == SERVER_ACL_READONLY)
888ab000fcSnicm 			cmdq_print(item, "%s (R)", name);
898ab000fcSnicm 		else
908ab000fcSnicm 			cmdq_print(item, "%s (W)", name);
918ab000fcSnicm 	}
928ab000fcSnicm }
938ab000fcSnicm 
948ab000fcSnicm /* Allow a user. */
958ab000fcSnicm void
server_acl_user_allow(uid_t uid)968ab000fcSnicm server_acl_user_allow(uid_t uid)
978ab000fcSnicm {
988ab000fcSnicm 	struct server_acl_user	*user;
998ab000fcSnicm 
1008ab000fcSnicm 	user = server_acl_user_find(uid);
1018ab000fcSnicm 	if (user == NULL) {
1028ab000fcSnicm 		user = xcalloc(1, sizeof *user);
1038ab000fcSnicm 		user->uid = uid;
1048ab000fcSnicm 		RB_INSERT(server_acl_entries, &server_acl_entries, user);
1058ab000fcSnicm 	}
1068ab000fcSnicm }
1078ab000fcSnicm 
1088ab000fcSnicm /* Deny a user (remove from the tree). */
1098ab000fcSnicm void
server_acl_user_deny(uid_t uid)1108ab000fcSnicm server_acl_user_deny(uid_t uid)
1118ab000fcSnicm {
1128ab000fcSnicm 	struct server_acl_user	*user;
1138ab000fcSnicm 
1148ab000fcSnicm 	user = server_acl_user_find(uid);
1158ab000fcSnicm 	if (user != NULL) {
1168ab000fcSnicm 		RB_REMOVE(server_acl_entries, &server_acl_entries, user);
1178ab000fcSnicm 		free(user);
1188ab000fcSnicm 	}
1198ab000fcSnicm }
1208ab000fcSnicm 
1218ab000fcSnicm /* Allow this user write access. */
1228ab000fcSnicm void
server_acl_user_allow_write(uid_t uid)1238ab000fcSnicm server_acl_user_allow_write(uid_t uid)
1248ab000fcSnicm {
1258ab000fcSnicm 	struct server_acl_user	*user;
1268ab000fcSnicm 	struct client		*c;
1278ab000fcSnicm 
1288ab000fcSnicm 	user = server_acl_user_find(uid);
1298ab000fcSnicm 	if (user == NULL)
1308ab000fcSnicm 		return;
1318ab000fcSnicm 	user->flags &= ~SERVER_ACL_READONLY;
1328ab000fcSnicm 
1338ab000fcSnicm 	TAILQ_FOREACH(c, &clients, entry) {
1348ab000fcSnicm 		uid = proc_get_peer_uid(c->peer);
1358ab000fcSnicm 		if (uid != (uid_t)-1 && uid == user->uid)
1368ab000fcSnicm 			c->flags &= ~CLIENT_READONLY;
1378ab000fcSnicm 	}
1388ab000fcSnicm }
1398ab000fcSnicm 
1408ab000fcSnicm /* Deny this user write access. */
1418ab000fcSnicm void
server_acl_user_deny_write(uid_t uid)1428ab000fcSnicm server_acl_user_deny_write(uid_t uid)
1438ab000fcSnicm {
1448ab000fcSnicm 	struct server_acl_user	*user;
1458ab000fcSnicm 	struct client		*c;
1468ab000fcSnicm 
1478ab000fcSnicm 	user = server_acl_user_find(uid);
1488ab000fcSnicm 	if (user == NULL)
1498ab000fcSnicm 		return;
1508ab000fcSnicm 	user->flags |= SERVER_ACL_READONLY;
1518ab000fcSnicm 
1528ab000fcSnicm 	TAILQ_FOREACH(c, &clients, entry) {
1538ab000fcSnicm 		uid = proc_get_peer_uid(c->peer);
1548ab000fcSnicm 		if (uid != (uid_t)-1 && uid == user->uid)
1558ab000fcSnicm 			c->flags |= CLIENT_READONLY;
1568ab000fcSnicm 	}
1578ab000fcSnicm }
1588ab000fcSnicm 
1598ab000fcSnicm /*
1608ab000fcSnicm  * Check if the client's UID exists in the ACL list and if so, set as read only
1618ab000fcSnicm  * if needed. Return false if the user does not exist.
1628ab000fcSnicm  */
1638ab000fcSnicm int
server_acl_join(struct client * c)1648ab000fcSnicm server_acl_join(struct client *c)
1658ab000fcSnicm {
1668ab000fcSnicm 	struct server_acl_user	*user;
1678ab000fcSnicm 	uid_t			 uid;
1688ab000fcSnicm 
1698ab000fcSnicm 	uid = proc_get_peer_uid(c->peer);
1708ab000fcSnicm 	if (uid == (uid_t)-1)
1718ab000fcSnicm 		return (0);
1728ab000fcSnicm 
1738ab000fcSnicm 	user = server_acl_user_find(uid);
1748ab000fcSnicm 	if (user == NULL)
1758ab000fcSnicm 		return (0);
1768ab000fcSnicm 	if (user->flags & SERVER_ACL_READONLY)
1778ab000fcSnicm 		c->flags |= CLIENT_READONLY;
1788ab000fcSnicm 	return (1);
1798ab000fcSnicm }
1808ab000fcSnicm 
1818ab000fcSnicm /* Get UID for user entry. */
1828ab000fcSnicm uid_t
server_acl_get_uid(struct server_acl_user * user)1838ab000fcSnicm server_acl_get_uid(struct server_acl_user *user)
1848ab000fcSnicm {
1858ab000fcSnicm 	return (user->uid);
1868ab000fcSnicm }
187