1 /* $OpenBSD: server-acl.c,v 1.2 2022/05/30 12:55:25 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2021 Holland Schutte, Jayson Morberg 5 * Copyright (c) 2021 Dallas Lyons <dallasdlyons@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