Coverage Report

Created: 2025-07-18 06:48

/src/tmux/server-acl.c
Line
Count
Source (jump to first uncovered line)
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 <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
0
#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
0
{
44
0
  if (user1->uid < user2->uid)
45
0
    return (-1);
46
0
  return (user1->uid > user2->uid);
47
0
}
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
0
{
56
0
  RB_INIT(&server_acl_entries);
57
58
0
  if (getuid() != 0)
59
0
    server_acl_user_allow(0);
60
0
  server_acl_user_allow(getuid());
61
0
}
62
63
/* Find user entry. */
64
struct server_acl_user*
65
server_acl_user_find(uid_t uid)
66
0
{
67
0
  struct server_acl_user  find = { .uid = uid };
68
69
0
  return (RB_FIND(server_acl_entries, &server_acl_entries, &find));
70
0
}
71
72
/* Display the tree. */
73
void
74
server_acl_display(struct cmdq_item *item)
75
0
{
76
0
  struct server_acl_user  *loop;
77
0
  struct passwd   *pw;
78
0
  const char    *name;
79
80
0
  RB_FOREACH(loop, server_acl_entries, &server_acl_entries) {
81
0
    if (loop->uid == 0)
82
0
      continue;
83
0
    if ((pw = getpwuid(loop->uid)) != NULL)
84
0
      name = pw->pw_name;
85
0
    else
86
0
      name = "unknown";
87
0
    if (loop->flags == SERVER_ACL_READONLY)
88
0
      cmdq_print(item, "%s (R)", name);
89
0
    else
90
0
      cmdq_print(item, "%s (W)", name);
91
0
  }
92
0
}
93
94
/* Allow a user. */
95
void
96
server_acl_user_allow(uid_t uid)
97
0
{
98
0
  struct server_acl_user  *user;
99
100
0
  user = server_acl_user_find(uid);
101
0
  if (user == NULL) {
102
0
    user = xcalloc(1, sizeof *user);
103
0
    user->uid = uid;
104
0
    RB_INSERT(server_acl_entries, &server_acl_entries, user);
105
0
  }
106
0
}
107
108
/* Deny a user (remove from the tree). */
109
void
110
server_acl_user_deny(uid_t uid)
111
0
{
112
0
  struct server_acl_user  *user;
113
114
0
  user = server_acl_user_find(uid);
115
0
  if (user != NULL) {
116
0
    RB_REMOVE(server_acl_entries, &server_acl_entries, user);
117
0
    free(user);
118
0
  }
119
0
}
120
121
/* Allow this user write access. */
122
void
123
server_acl_user_allow_write(uid_t uid)
124
0
{
125
0
  struct server_acl_user  *user;
126
0
  struct client   *c;
127
128
0
  user = server_acl_user_find(uid);
129
0
  if (user == NULL)
130
0
    return;
131
0
  user->flags &= ~SERVER_ACL_READONLY;
132
133
0
  TAILQ_FOREACH(c, &clients, entry) {
134
0
    uid = proc_get_peer_uid(c->peer);
135
0
    if (uid != (uid_t)-1 && uid == user->uid)
136
0
      c->flags &= ~CLIENT_READONLY;
137
0
  }
138
0
}
139
140
/* Deny this user write access. */
141
void
142
server_acl_user_deny_write(uid_t uid)
143
0
{
144
0
  struct server_acl_user  *user;
145
0
  struct client   *c;
146
147
0
  user = server_acl_user_find(uid);
148
0
  if (user == NULL)
149
0
    return;
150
0
  user->flags |= SERVER_ACL_READONLY;
151
152
0
  TAILQ_FOREACH(c, &clients, entry) {
153
0
    uid = proc_get_peer_uid(c->peer);
154
0
    if (uid != (uid_t)-1 && uid == user->uid)
155
0
      c->flags |= CLIENT_READONLY;
156
0
  }
157
0
}
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
0
{
166
0
  struct server_acl_user  *user;
167
0
  uid_t      uid;
168
169
0
  uid = proc_get_peer_uid(c->peer);
170
0
  if (uid == (uid_t)-1)
171
0
    return (0);
172
173
0
  user = server_acl_user_find(uid);
174
0
  if (user == NULL)
175
0
    return (0);
176
0
  if (user->flags & SERVER_ACL_READONLY)
177
0
    c->flags |= CLIENT_READONLY;
178
0
  return (1);
179
0
}
180
181
/* Get UID for user entry. */
182
uid_t
183
server_acl_get_uid(struct server_acl_user *user)
184
0
{
185
0
  return (user->uid);
186
0
}