Line | Count | Source |
1 | | /* $OpenBSD$ */ |
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 <grp.h> |
26 | | #include <pwd.h> |
27 | | #include <stdlib.h> |
28 | | #include <string.h> |
29 | | #include <unistd.h> |
30 | | |
31 | | #include "tmux.h" |
32 | | |
33 | | struct server_acl_entry { |
34 | | id_t id; |
35 | | |
36 | | int flags; |
37 | | |
38 | | RB_ENTRY(server_acl_entry) entry; |
39 | | }; |
40 | | |
41 | | static int |
42 | | server_acl_cmp(struct server_acl_entry *entry1, |
43 | | struct server_acl_entry *entry2) |
44 | 0 | { |
45 | 0 | if ((entry1->flags ^ entry2->flags) & SERVER_ACL_IS_GROUP) { |
46 | 0 | if (entry1->flags & SERVER_ACL_IS_GROUP) |
47 | 0 | return (1); |
48 | 0 | return (-1); |
49 | 0 | } |
50 | | |
51 | 0 | if (entry1->id < entry2->id) |
52 | 0 | return (-1); |
53 | 0 | return (entry1->id > entry2->id); |
54 | 0 | } |
55 | | |
56 | | RB_HEAD(server_acl_entries, server_acl_entry) server_acl_entries; |
57 | 0 | RB_GENERATE_STATIC(server_acl_entries, server_acl_entry, entry, server_acl_cmp); Unexecuted instantiation: server-acl.c:server_acl_entries_RB_FIND Unexecuted instantiation: server-acl.c:server_acl_entries_RB_MINMAX Unexecuted instantiation: server-acl.c:server_acl_entries_RB_INSERT Unexecuted instantiation: server-acl.c:server_acl_entries_RB_REMOVE Unexecuted instantiation: server-acl.c:server_acl_entries_RB_REMOVE_COLOR |
58 | 0 |
|
59 | 0 | static struct server_acl_entry * |
60 | 0 | server_acl_entry_find(id_t id, int flags) |
61 | 0 | { |
62 | 0 | struct server_acl_entry find = { |
63 | 0 | .id = id, |
64 | 0 | .flags = flags & SERVER_ACL_IS_GROUP |
65 | 0 | }; |
66 | |
|
67 | 0 | return (RB_FIND(server_acl_entries, &server_acl_entries, &find)); |
68 | 0 | } |
69 | | |
70 | | static struct server_acl_entry * |
71 | | server_acl_check(struct client *c) |
72 | 0 | { |
73 | 0 | struct server_acl_entry *entry; |
74 | 0 | uid_t uid; |
75 | 0 | gid_t gid; |
76 | |
|
77 | 0 | uid = proc_get_peer_uid(c->peer); |
78 | 0 | if (uid == (uid_t)-1) |
79 | 0 | return (NULL); |
80 | | |
81 | 0 | entry = server_acl_entry_find(uid, 0); |
82 | 0 | if (entry != NULL) |
83 | 0 | return (entry); |
84 | | |
85 | 0 | gid = proc_get_peer_gid(c->peer); |
86 | 0 | if (gid == (gid_t)-1) |
87 | 0 | return (NULL); |
88 | | |
89 | 0 | return (server_acl_entry_find(gid, SERVER_ACL_IS_GROUP)); |
90 | 0 | } |
91 | | |
92 | | static void |
93 | | server_acl_update(void) |
94 | 0 | { |
95 | 0 | struct server_acl_entry *entry; |
96 | 0 | struct client *c; |
97 | |
|
98 | 0 | TAILQ_FOREACH(c, &clients, entry) { |
99 | 0 | entry = server_acl_check(c); |
100 | 0 | if (entry == NULL) { |
101 | 0 | c->exit_message = xstrdup("access not allowed"); |
102 | 0 | c->flags |= CLIENT_EXIT; |
103 | 0 | } else if (entry->flags & SERVER_ACL_READONLY) |
104 | 0 | c->flags |= CLIENT_READONLY; |
105 | 0 | else |
106 | 0 | c->flags &= ~CLIENT_READONLY; |
107 | 0 | } |
108 | 0 | } |
109 | | |
110 | | /* Initialize ACL tree. */ |
111 | | void |
112 | | server_acl_init(void) |
113 | 0 | { |
114 | 0 | RB_INIT(&server_acl_entries); |
115 | |
|
116 | 0 | if (getuid() != 0) |
117 | 0 | server_acl_allow(0, 0); |
118 | 0 | server_acl_allow(getuid(), 0); |
119 | 0 | } |
120 | | |
121 | | /* Check if an ACL entry exists. */ |
122 | | int |
123 | | server_acl_find(id_t id, int flags) |
124 | 0 | { |
125 | 0 | return (server_acl_entry_find(id, flags) != NULL); |
126 | 0 | } |
127 | | |
128 | | /* Display the tree. */ |
129 | | void |
130 | | server_acl_display(struct cmdq_item *item) |
131 | 0 | { |
132 | 0 | struct server_acl_entry *loop; |
133 | 0 | struct passwd *pw; |
134 | 0 | struct group *gr; |
135 | 0 | const char *name; |
136 | 0 | char type; |
137 | |
|
138 | 0 | RB_FOREACH(loop, server_acl_entries, &server_acl_entries) { |
139 | 0 | if (~loop->flags & SERVER_ACL_IS_GROUP) { |
140 | 0 | if (loop->id == 0) |
141 | 0 | continue; |
142 | 0 | if ((pw = getpwuid(loop->id)) != NULL) |
143 | 0 | name = pw->pw_name; |
144 | 0 | else |
145 | 0 | name = "unknown"; |
146 | 0 | type = 'U'; |
147 | 0 | } else { |
148 | 0 | if ((gr = getgrgid(loop->id)) != NULL) |
149 | 0 | name = gr->gr_name; |
150 | 0 | else |
151 | 0 | name = "unknown"; |
152 | 0 | type = 'G'; |
153 | 0 | } |
154 | 0 | if (loop->flags & SERVER_ACL_READONLY) |
155 | 0 | cmdq_print(item, "%s (%c,R)", name, type); |
156 | 0 | else |
157 | 0 | cmdq_print(item, "%s (%c,W)", name, type); |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | | /* Allow an ACL entry. */ |
162 | | void |
163 | | server_acl_allow(id_t id, int flags) |
164 | 0 | { |
165 | 0 | struct server_acl_entry *entry; |
166 | |
|
167 | 0 | entry = server_acl_entry_find(id, flags); |
168 | 0 | if (entry == NULL) { |
169 | 0 | entry = xcalloc(1, sizeof *entry); |
170 | 0 | entry->id = id; |
171 | 0 | entry->flags = flags & SERVER_ACL_IS_GROUP; |
172 | 0 | RB_INSERT(server_acl_entries, &server_acl_entries, entry); |
173 | 0 | } |
174 | 0 | } |
175 | | |
176 | | /* Deny an ACL entry (remove it from the tree). */ |
177 | | void |
178 | | server_acl_deny(id_t id, int flags) |
179 | 0 | { |
180 | 0 | struct server_acl_entry *entry; |
181 | |
|
182 | 0 | entry = server_acl_entry_find(id, flags); |
183 | 0 | if (entry != NULL) { |
184 | 0 | RB_REMOVE(server_acl_entries, &server_acl_entries, entry); |
185 | 0 | free(entry); |
186 | 0 | server_acl_update(); |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | | /* Allow this ACL entry write access. */ |
191 | | void |
192 | | server_acl_allow_write(id_t id, int flags) |
193 | 0 | { |
194 | 0 | struct server_acl_entry *entry; |
195 | |
|
196 | 0 | entry = server_acl_entry_find(id, flags); |
197 | 0 | if (entry == NULL) |
198 | 0 | return; |
199 | 0 | entry->flags &= ~SERVER_ACL_READONLY; |
200 | 0 | server_acl_update(); |
201 | 0 | } |
202 | | |
203 | | /* Deny this ACL entry write access. */ |
204 | | void |
205 | | server_acl_deny_write(id_t id, int flags) |
206 | 0 | { |
207 | 0 | struct server_acl_entry *entry; |
208 | |
|
209 | 0 | entry = server_acl_entry_find(id, flags); |
210 | 0 | if (entry == NULL) |
211 | 0 | return; |
212 | 0 | entry->flags |= SERVER_ACL_READONLY; |
213 | 0 | server_acl_update(); |
214 | 0 | } |
215 | | |
216 | | /* |
217 | | * Check if the client's UID or GID exists in the ACL list and if so, set as |
218 | | * read only if needed. UID entries take precedence over GID entries. Return |
219 | | * false if no entry exists. |
220 | | */ |
221 | | int |
222 | | server_acl_join(struct client *c) |
223 | 0 | { |
224 | 0 | struct server_acl_entry *entry; |
225 | |
|
226 | 0 | entry = server_acl_check(c); |
227 | 0 | if (entry == NULL) |
228 | 0 | return (0); |
229 | 0 | if (entry->flags & SERVER_ACL_READONLY) |
230 | 0 | c->flags |= CLIENT_READONLY; |
231 | 0 | return (1); |
232 | 0 | } |