Home | History | Annotate | Line # | Download | only in dist
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2021 Holland Schutte, Jayson Morberg
      5  * Copyright (c) 2021 Dallas Lyons <dallasdlyons (at) gmail.com>
      6  *
      7  * Permission to use, copy, modify, and distribute this software for any
      8  * purpose with or without fee is hereby granted, provided that the above
      9  * copyright notice and this permission notice appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     16  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     17  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 
     20 #include <sys/types.h>
     21 #include <sys/stat.h>
     22 #include <sys/socket.h>
     23 
     24 #include <ctype.h>
     25 #include <pwd.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 #include <unistd.h>
     29 
     30 #include "tmux.h"
     31 
     32 struct server_acl_user {
     33 	uid_t				uid;
     34 
     35 	int				flags;
     36 #define SERVER_ACL_READONLY 0x1
     37 
     38 	RB_ENTRY(server_acl_user)	entry;
     39 };
     40 
     41 static int
     42 server_acl_cmp(struct server_acl_user *user1, struct server_acl_user *user2)
     43 {
     44 	if (user1->uid < user2->uid)
     45 		return (-1);
     46 	return (user1->uid > user2->uid);
     47 }
     48 
     49 RB_HEAD(server_acl_entries, server_acl_user) server_acl_entries;
     50 RB_GENERATE_STATIC(server_acl_entries, server_acl_user, entry, server_acl_cmp);
     51 
     52 /* Initialize server_acl tree. */
     53 void
     54 server_acl_init(void)
     55 {
     56 	RB_INIT(&server_acl_entries);
     57 
     58 	if (getuid() != 0)
     59 		server_acl_user_allow(0);
     60 	server_acl_user_allow(getuid());
     61 }
     62 
     63 /* Find user entry. */
     64 struct server_acl_user*
     65 server_acl_user_find(uid_t uid)
     66 {
     67 	struct server_acl_user	find = { .uid = uid };
     68 
     69 	return (RB_FIND(server_acl_entries, &server_acl_entries, &find));
     70 }
     71 
     72 /* Display the tree. */
     73 void
     74 server_acl_display(struct cmdq_item *item)
     75 {
     76 	struct server_acl_user	*loop;
     77 	struct passwd		*pw;
     78 	const char		*name;
     79 
     80 	RB_FOREACH(loop, server_acl_entries, &server_acl_entries) {
     81 		if (loop->uid == 0)
     82 			continue;
     83 		if ((pw = getpwuid(loop->uid)) != NULL)
     84 			name = pw->pw_name;
     85 		else
     86 			name = "unknown";
     87 		if (loop->flags == SERVER_ACL_READONLY)
     88 			cmdq_print(item, "%s (R)", name);
     89 		else
     90 			cmdq_print(item, "%s (W)", name);
     91 	}
     92 }
     93 
     94 /* Allow a user. */
     95 void
     96 server_acl_user_allow(uid_t uid)
     97 {
     98 	struct server_acl_user	*user;
     99 
    100 	user = server_acl_user_find(uid);
    101 	if (user == NULL) {
    102 		user = xcalloc(1, sizeof *user);
    103 		user->uid = uid;
    104 		RB_INSERT(server_acl_entries, &server_acl_entries, user);
    105 	}
    106 }
    107 
    108 /* Deny a user (remove from the tree). */
    109 void
    110 server_acl_user_deny(uid_t uid)
    111 {
    112 	struct server_acl_user	*user;
    113 
    114 	user = server_acl_user_find(uid);
    115 	if (user != NULL) {
    116 		RB_REMOVE(server_acl_entries, &server_acl_entries, user);
    117 		free(user);
    118 	}
    119 }
    120 
    121 /* Allow this user write access. */
    122 void
    123 server_acl_user_allow_write(uid_t uid)
    124 {
    125 	struct server_acl_user	*user;
    126 	struct client		*c;
    127 
    128 	user = server_acl_user_find(uid);
    129 	if (user == NULL)
    130 		return;
    131 	user->flags &= ~SERVER_ACL_READONLY;
    132 
    133 	TAILQ_FOREACH(c, &clients, entry) {
    134 		uid = proc_get_peer_uid(c->peer);
    135 		if (uid != (uid_t)-1 && uid == user->uid)
    136 			c->flags &= ~CLIENT_READONLY;
    137 	}
    138 }
    139 
    140 /* Deny this user write access. */
    141 void
    142 server_acl_user_deny_write(uid_t uid)
    143 {
    144 	struct server_acl_user	*user;
    145 	struct client		*c;
    146 
    147 	user = server_acl_user_find(uid);
    148 	if (user == NULL)
    149 		return;
    150 	user->flags |= SERVER_ACL_READONLY;
    151 
    152 	TAILQ_FOREACH(c, &clients, entry) {
    153 		uid = proc_get_peer_uid(c->peer);
    154 		if (uid != (uid_t)-1 && uid == user->uid)
    155 			c->flags |= CLIENT_READONLY;
    156 	}
    157 }
    158 
    159 /*
    160  * Check if the client's UID exists in the ACL list and if so, set as read only
    161  * if needed. Return false if the user does not exist.
    162  */
    163 int
    164 server_acl_join(struct client *c)
    165 {
    166 	struct server_acl_user	*user;
    167 	uid_t			 uid;
    168 
    169 	uid = proc_get_peer_uid(c->peer);
    170 	if (uid == (uid_t)-1)
    171 		return (0);
    172 
    173 	user = server_acl_user_find(uid);
    174 	if (user == NULL)
    175 		return (0);
    176 	if (user->flags & SERVER_ACL_READONLY)
    177 		c->flags |= CLIENT_READONLY;
    178 	return (1);
    179 }
    180 
    181 /* Get UID for user entry. */
    182 uid_t
    183 server_acl_get_uid(struct server_acl_user *user)
    184 {
    185 	return (user->uid);
    186 }
    187