Coverage Report

Created: 2026-06-12 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/server-acl.c
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
}