]> jfr.im git - solanum.git/blame - ircd/privilege.c
doc/reference.conf: document the auth::umodes configuration option
[solanum.git] / ircd / privilege.c
CommitLineData
9c3f080b 1/*
a6f63a82 2 * Solanum: a slightly advanced ircd
9c3f080b
AC
3 * privilege.c: Dynamic privileges API.
4 *
8aadf0ce
EK
5 * Copyright (c) 2021 Ed Kellett <e@kellett.im>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
3fc0499e 22 * Copyright (c) 2008 Ariadne Conill <ariadne@dereferenced.org>
9c3f080b
AC
23 *
24 * Permission to use, copy, modify, and/or distribute this software for any
25 * purpose with or without fee is hereby granted, provided that the above
26 * copyright notice and this permission notice is present in all copies.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
29 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
30 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
31 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
32 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
33 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
34 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
37 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 */
40
41#include <stdinc.h>
422bb0b5 42#include "s_conf.h"
9c3f080b 43#include "privilege.h"
3a177354 44#include "numeric.h"
77d3d2db
KB
45#include "s_assert.h"
46#include "logger.h"
47#include "send.h"
9c3f080b 48
29c92cf9 49static rb_dlink_list privilegeset_list = {NULL, NULL, 0};
9c3f080b 50
422bb0b5
JT
51static struct PrivilegeSet *
52privilegeset_get_any(const char *name)
53{
54 rb_dlink_node *iter;
55
56 s_assert(name != NULL);
57
58 RB_DLINK_FOREACH(iter, privilegeset_list.head)
59 {
60 struct PrivilegeSet *set = (struct PrivilegeSet *) iter->data;
61
f956cb0f 62 if (!rb_strcasecmp(set->name, name))
422bb0b5
JT
63 return set;
64 }
65
66 return NULL;
67}
68
8aadf0ce
EK
69static int
70privilegeset_cmp_priv(const void *a_, const void *b_)
71{
72 const char *const *a = a_, *const *b = b_;
73 return strcmp(*a, *b);
74}
75
76static void
77privilegeset_index(struct PrivilegeSet *set)
78{
79 size_t n;
80 const char *s;
81 const char **p;
82
83 rb_free(set->privs);
84
85 set->privs = rb_malloc(sizeof *set->privs * (set->size + 1));
86 p = set->privs;
87
88 for (n = 0, s = set->priv_storage; n < set->size; n++, s += strlen(s) + 1)
89 *p++ = s;
90 qsort(set->privs, set->size, sizeof *set->privs, privilegeset_cmp_priv);
91 set->privs[set->size] = NULL;
92}
93
df4fead0 94void
8aadf0ce
EK
95privilegeset_add_privs(struct PrivilegeSet *dst, const char *privs)
96{
df4fead0 97 size_t alloc_size, old_stored_size;
8aadf0ce
EK
98
99 if (dst->priv_storage == NULL)
100 {
101 dst->stored_size = dst->allocated_size = 0;
102 alloc_size = 256;
103 }
104 else
105 {
106 alloc_size = dst->allocated_size;
107 }
108
df4fead0 109 old_stored_size = dst->stored_size;
8aadf0ce
EK
110 dst->stored_size += strlen(privs) + 1;
111
112 while (alloc_size < dst->stored_size)
113 alloc_size *= 2;
114
115 if (alloc_size > dst->allocated_size)
116 dst->priv_storage = rb_realloc(dst->priv_storage, alloc_size);
117
118 dst->allocated_size = alloc_size;
119
120 const char *s;
121 char *d;
8aadf0ce 122
df4fead0
EK
123 for (s = privs, d = dst->priv_storage + old_stored_size;
124 s <= privs + strlen(privs);
125 s++, d++)
126 {
127 *d = *s;
128 if (*d == ' ' || *d == '\0')
129 {
130 *d = '\0';
131 if (s > privs) dst->size += 1;
132 }
8aadf0ce
EK
133 }
134
135 privilegeset_index(dst);
136}
137
138static void
139privilegeset_add_privilegeset(struct PrivilegeSet *dst, const struct PrivilegeSet *src)
140{
141 size_t cur_size, alloc_size;
142
143 if (dst->priv_storage == NULL)
144 {
145 dst->stored_size = dst->allocated_size = 0;
146 cur_size = 0;
147 alloc_size = 256;
148 }
149 else
150 {
151 cur_size = dst->stored_size;
152 alloc_size = dst->allocated_size;
153 }
154
155 dst->stored_size = cur_size + src->stored_size;
156
157 while (alloc_size < dst->stored_size)
158 alloc_size *= 2;
159
160 if (alloc_size > dst->allocated_size)
161 dst->priv_storage = rb_realloc(dst->priv_storage, alloc_size);
162
163 dst->allocated_size = alloc_size;
164
165 memcpy(dst->priv_storage + cur_size, src->priv_storage, src->stored_size);
166 dst->size += src->size;
167
168 privilegeset_index(dst);
169}
170
171static struct PrivilegeSet *
172privilegeset_new_orphan(const char *name)
173{
174 struct PrivilegeSet *set;
175 set = rb_malloc(sizeof *set);
176 *set = (struct PrivilegeSet) {
177 .size = 0,
178 .privs = NULL,
179 .priv_storage = NULL,
180 .shadow = NULL,
181 .status = 0,
182 .refs = 0,
183 .name = rb_strdup(name),
184 };
185 return set;
186}
187
188static void
189privilegeset_free(struct PrivilegeSet *set)
190{
191 if (set == NULL)
192 return;
193
194 privilegeset_free(set->shadow);
195 rb_free(set->name);
196 rb_free(set->privs);
197 rb_free(set->priv_storage);
198 rb_free(set);
199}
200
201static void
202privilegeset_shade(struct PrivilegeSet *set)
203{
204 privilegeset_free(set->shadow);
205
206 set->shadow = privilegeset_new_orphan(set->name);
207 set->shadow->privs = set->privs;
208 set->shadow->size = set->size;
209 set->shadow->priv_storage = set->priv_storage;
210 set->shadow->stored_size = set->stored_size;
211 set->shadow->allocated_size = set->allocated_size;
212
213 set->privs = NULL;
214 set->size = 0;
215 set->priv_storage = NULL;
216 set->stored_size = 0;
217 set->allocated_size = 0;
218}
219
220static void
221privilegeset_clear(struct PrivilegeSet *set)
222{
223 rb_free(set->privs);
224 set->privs = NULL;
225 set->size = 0;
226 set->stored_size = 0;
227}
228
02690ad5
EK
229bool
230privilegeset_in_set(const struct PrivilegeSet *set, const char *priv)
231{
232 s_assert(set != NULL);
233 s_assert(priv != NULL);
234
235 const char **found = bsearch(&priv, set->privs, set->size, sizeof *set->privs, privilegeset_cmp_priv);
236 return found != NULL;
237}
238
181410f2 239const char *const *
8b832059
EK
240privilegeset_privs(const struct PrivilegeSet *set)
241{
242 static const char *no_privs[] = { NULL };
243 return set->privs != NULL ? set->privs : no_privs;
244}
245
9c3f080b
AC
246struct PrivilegeSet *
247privilegeset_set_new(const char *name, const char *privs, PrivilegeFlags flags)
248{
249 struct PrivilegeSet *set;
250
422bb0b5
JT
251 set = privilegeset_get_any(name);
252 if (set != NULL)
253 {
254 if (!(set->status & CONF_ILLEGAL))
255 ilog(L_MAIN, "Duplicate privset %s", name);
256 set->status &= ~CONF_ILLEGAL;
8aadf0ce 257 privilegeset_clear(set);
422bb0b5
JT
258 }
259 else
260 {
8aadf0ce 261 set = privilegeset_new_orphan(name);
422bb0b5
JT
262 rb_dlinkAdd(set, &set->node, &privilegeset_list);
263 }
8aadf0ce 264 privilegeset_add_privs(set, privs);
9c3f080b
AC
265 set->flags = flags;
266
9c3f080b
AC
267 return set;
268}
269
353f8625 270struct PrivilegeSet *
8aadf0ce 271privilegeset_extend(const struct PrivilegeSet *parent, const char *name, const char *privs, PrivilegeFlags flags)
353f8625
AC
272{
273 struct PrivilegeSet *set;
274
275 s_assert(parent != NULL);
276 s_assert(name != NULL);
277 s_assert(privs != NULL);
353f8625 278
8aadf0ce
EK
279 set = privilegeset_set_new(name, privs, flags);
280 privilegeset_add_privilegeset(set, parent);
353f8625 281 set->flags = flags;
353f8625
AC
282
283 return set;
284}
285
9c3f080b
AC
286struct PrivilegeSet *
287privilegeset_get(const char *name)
288{
422bb0b5 289 struct PrivilegeSet *set;
9c3f080b 290
422bb0b5
JT
291 set = privilegeset_get_any(name);
292 if (set != NULL && set->status & CONF_ILLEGAL)
293 set = NULL;
294 return set;
9c3f080b
AC
295}
296
598b4cf1 297struct PrivilegeSet *
9c3f080b
AC
298privilegeset_ref(struct PrivilegeSet *set)
299{
300 s_assert(set != NULL);
301
302 set->refs++;
598b4cf1
AC
303
304 return set;
9c3f080b
AC
305}
306
307void
308privilegeset_unref(struct PrivilegeSet *set)
309{
310 s_assert(set != NULL);
311
422bb0b5
JT
312 if (set->refs > 0)
313 set->refs--;
314 else
315 ilog(L_MAIN, "refs on privset %s is already 0",
316 set->name);
317 if (set->refs == 0 && set->status & CONF_ILLEGAL)
9c3f080b
AC
318 {
319 rb_dlinkDelete(&set->node, &privilegeset_list);
320
8aadf0ce
EK
321 privilegeset_free(set);
322 }
323}
324
9962f625 325struct privset_diff
8aadf0ce
EK
326privilegeset_diff(const struct PrivilegeSet *old, const struct PrivilegeSet *new)
327{
328 static const char *no_privs[] = { NULL };
329 static const struct PrivilegeSet empty = { .size = 0, .privs = no_privs };
330 static struct PrivilegeSet *set_unchanged = NULL,
331 *set_added = NULL,
332 *set_removed = NULL;
8aadf0ce
EK
333 static size_t n_privs = 0;
334 size_t new_size = n_privs ? n_privs : 32;
335 size_t i = 0, j = 0;
336
9962f625 337 if (set_unchanged == NULL)
8aadf0ce 338 {
9962f625
EK
339 set_unchanged = privilegeset_new_orphan("<unchanged>");
340 set_added = privilegeset_new_orphan("<added>");
341 set_removed = privilegeset_new_orphan("<removed>");
8aadf0ce
EK
342 }
343
344 if (old == NULL)
345 old = &empty;
346 if (new == NULL)
347 new = &empty;
348
349 while (new_size < MAX(old->size, new->size) + 1)
350 new_size *= 2;
351
352 if (new_size > n_privs)
353 {
354 set_unchanged->privs = rb_realloc(set_unchanged->privs, sizeof *set_unchanged->privs * new_size);
355 set_added->privs = rb_realloc(set_added->privs, sizeof *set_added->privs * new_size);
356 set_removed->privs = rb_realloc(set_removed->privs, sizeof *set_removed->privs * new_size);
357 }
358
359 const char **res_unchanged = set_unchanged->privs;
360 const char **res_added = set_added->privs;
361 const char **res_removed = set_removed->privs;
362
363 while (i < old->size || j < new->size)
364 {
365 const char *oldpriv = NULL, *newpriv = NULL;
366 int ord = 0;
8b832059
EK
367 oldpriv = privilegeset_privs(old)[i];
368 newpriv = privilegeset_privs(new)[j];
8aadf0ce
EK
369
370 if (oldpriv && newpriv)
371 ord = strcmp(oldpriv, newpriv);
372
373 if (newpriv == NULL || ord < 0)
374 {
375 *res_removed++ = oldpriv;
376 i++;
377 }
378 else if (oldpriv == NULL || ord > 0)
379 {
380 *res_added++ = newpriv;
381 j++;
382 }
383 else
384 {
385 *res_unchanged++ = oldpriv;
386 i++; j++;
387 }
9c3f080b 388 }
8aadf0ce
EK
389
390 *res_removed = *res_added = *res_unchanged = NULL;
391 set_unchanged->size = res_unchanged - set_unchanged->privs;
392 set_added->size = res_added - set_added->privs;
393 set_removed->size = res_removed - set_removed->privs;
394
9962f625
EK
395 return (struct privset_diff){
396 .unchanged = set_unchanged,
397 .added = set_added,
398 .removed = set_removed,
399 };
9c3f080b 400}
422bb0b5
JT
401
402void
8aadf0ce 403privilegeset_prepare_rehash()
422bb0b5
JT
404{
405 rb_dlink_node *iter;
406
407 RB_DLINK_FOREACH(iter, privilegeset_list.head)
408 {
8aadf0ce 409 struct PrivilegeSet *set = iter->data;
422bb0b5 410
a1d2fafd
JT
411 /* the "default" privset is special and must remain available */
412 if (!strcmp(set->name, "default"))
413 continue;
414
422bb0b5 415 set->status |= CONF_ILLEGAL;
8aadf0ce 416 privilegeset_shade(set);
422bb0b5
JT
417 }
418}
419
420void
8aadf0ce 421privilegeset_cleanup_rehash()
422bb0b5
JT
422{
423 rb_dlink_node *iter, *next;
424
425 RB_DLINK_FOREACH_SAFE(iter, next, privilegeset_list.head)
426 {
8aadf0ce
EK
427 struct PrivilegeSet *set = iter->data;
428
429 if (set->shadow)
430 {
431 privilegeset_free(set->shadow);
432 set->shadow = NULL;
433 }
422bb0b5
JT
434
435 privilegeset_ref(set);
436 privilegeset_unref(set);
437 }
438}
3a177354
JT
439
440void
441privilegeset_report(struct Client *source_p)
442{
443 rb_dlink_node *ptr;
444
445 RB_DLINK_FOREACH(ptr, privilegeset_list.head)
446 {
447 struct PrivilegeSet *set = ptr->data;
448
449 /* use RPL_STATSDEBUG for now -- jilles */
8aadf0ce
EK
450 send_multiline_init(source_p, " ", ":%s %03d %s O :%s ",
451 get_id(&me, source_p),
452 RPL_STATSDEBUG,
453 get_id(source_p, source_p),
454 set->name);
455 send_multiline_remote_pad(source_p, &me);
456 send_multiline_remote_pad(source_p, source_p);
181410f2 457 for (const char *const *s = privilegeset_privs(set); *s != NULL; s++)
8aadf0ce
EK
458 send_multiline_item(source_p, "%s", *s);
459 send_multiline_fini(source_p, NULL);
3a177354
JT
460 }
461}