]> jfr.im git - irc/freenode/solanum.git/blob - ircd/privilege.c
Make privilegeset_privs more const
[irc/freenode/solanum.git] / ircd / privilege.c
1 /*
2 * Solanum: a slightly advanced ircd
3 * privilege.c: Dynamic privileges API.
4 *
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 *
22 * Copyright (c) 2008 William Pitcock <nenolod@dereferenced.org>
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>
42 #include "s_conf.h"
43 #include "privilege.h"
44 #include "numeric.h"
45 #include "s_assert.h"
46 #include "logger.h"
47 #include "send.h"
48
49 static rb_dlink_list privilegeset_list = {NULL, NULL, 0};
50
51 static struct PrivilegeSet *
52 privilegeset_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
62 if (!rb_strcasecmp(set->name, name))
63 return set;
64 }
65
66 return NULL;
67 }
68
69 static int
70 privilegeset_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
76 static void
77 privilegeset_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
94 static void
95 privilegeset_add_privs(struct PrivilegeSet *dst, const char *privs)
96 {
97 size_t alloc_size;
98 size_t n;
99
100 if (dst->priv_storage == NULL)
101 {
102 dst->stored_size = dst->allocated_size = 0;
103 alloc_size = 256;
104 }
105 else
106 {
107 alloc_size = dst->allocated_size;
108 }
109
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;
122 for (s = privs, d = dst->priv_storage; s < privs + strlen(privs); s += n , d += n)
123 {
124 const char *e = strchr(s, ' ');
125 /* up to space if there is one, else up to end of string */
126 n = 1 + (e != NULL ? e - s : strlen(s));
127 rb_strlcpy(d, s, n);
128
129 dst->size += 1;
130 }
131
132 privilegeset_index(dst);
133 }
134
135 static void
136 privilegeset_add_privilegeset(struct PrivilegeSet *dst, const struct PrivilegeSet *src)
137 {
138 size_t cur_size, alloc_size;
139
140 if (dst->priv_storage == NULL)
141 {
142 dst->stored_size = dst->allocated_size = 0;
143 cur_size = 0;
144 alloc_size = 256;
145 }
146 else
147 {
148 cur_size = dst->stored_size;
149 alloc_size = dst->allocated_size;
150 }
151
152 dst->stored_size = cur_size + src->stored_size;
153
154 while (alloc_size < dst->stored_size)
155 alloc_size *= 2;
156
157 if (alloc_size > dst->allocated_size)
158 dst->priv_storage = rb_realloc(dst->priv_storage, alloc_size);
159
160 dst->allocated_size = alloc_size;
161
162 memcpy(dst->priv_storage + cur_size, src->priv_storage, src->stored_size);
163 dst->size += src->size;
164
165 privilegeset_index(dst);
166 }
167
168 static struct PrivilegeSet *
169 privilegeset_new_orphan(const char *name)
170 {
171 struct PrivilegeSet *set;
172 set = rb_malloc(sizeof *set);
173 *set = (struct PrivilegeSet) {
174 .size = 0,
175 .privs = NULL,
176 .priv_storage = NULL,
177 .shadow = NULL,
178 .status = 0,
179 .refs = 0,
180 .name = rb_strdup(name),
181 };
182 return set;
183 }
184
185 static void
186 privilegeset_free(struct PrivilegeSet *set)
187 {
188 if (set == NULL)
189 return;
190
191 privilegeset_free(set->shadow);
192 rb_free(set->name);
193 rb_free(set->privs);
194 rb_free(set->priv_storage);
195 rb_free(set);
196 }
197
198 static void
199 privilegeset_shade(struct PrivilegeSet *set)
200 {
201 privilegeset_free(set->shadow);
202
203 set->shadow = privilegeset_new_orphan(set->name);
204 set->shadow->privs = set->privs;
205 set->shadow->size = set->size;
206 set->shadow->priv_storage = set->priv_storage;
207 set->shadow->stored_size = set->stored_size;
208 set->shadow->allocated_size = set->allocated_size;
209
210 set->privs = NULL;
211 set->size = 0;
212 set->priv_storage = NULL;
213 set->stored_size = 0;
214 set->allocated_size = 0;
215 }
216
217 static void
218 privilegeset_clear(struct PrivilegeSet *set)
219 {
220 rb_free(set->privs);
221 set->privs = NULL;
222 set->size = 0;
223 set->stored_size = 0;
224 }
225
226 bool
227 privilegeset_in_set(const struct PrivilegeSet *set, const char *priv)
228 {
229 s_assert(set != NULL);
230 s_assert(priv != NULL);
231
232 const char **found = bsearch(&priv, set->privs, set->size, sizeof *set->privs, privilegeset_cmp_priv);
233 return found != NULL;
234 }
235
236 const char *const *
237 privilegeset_privs(const struct PrivilegeSet *set)
238 {
239 static const char *no_privs[] = { NULL };
240 return set->privs != NULL ? set->privs : no_privs;
241 }
242
243 struct PrivilegeSet *
244 privilegeset_set_new(const char *name, const char *privs, PrivilegeFlags flags)
245 {
246 struct PrivilegeSet *set;
247
248 set = privilegeset_get_any(name);
249 if (set != NULL)
250 {
251 if (!(set->status & CONF_ILLEGAL))
252 ilog(L_MAIN, "Duplicate privset %s", name);
253 set->status &= ~CONF_ILLEGAL;
254 privilegeset_clear(set);
255 }
256 else
257 {
258 set = privilegeset_new_orphan(name);
259 rb_dlinkAdd(set, &set->node, &privilegeset_list);
260 }
261 privilegeset_add_privs(set, privs);
262 set->flags = flags;
263
264 return set;
265 }
266
267 struct PrivilegeSet *
268 privilegeset_extend(const struct PrivilegeSet *parent, const char *name, const char *privs, PrivilegeFlags flags)
269 {
270 struct PrivilegeSet *set;
271
272 s_assert(parent != NULL);
273 s_assert(name != NULL);
274 s_assert(privs != NULL);
275
276 set = privilegeset_set_new(name, privs, flags);
277 privilegeset_add_privilegeset(set, parent);
278 set->flags = flags;
279
280 return set;
281 }
282
283 struct PrivilegeSet *
284 privilegeset_get(const char *name)
285 {
286 struct PrivilegeSet *set;
287
288 set = privilegeset_get_any(name);
289 if (set != NULL && set->status & CONF_ILLEGAL)
290 set = NULL;
291 return set;
292 }
293
294 struct PrivilegeSet *
295 privilegeset_ref(struct PrivilegeSet *set)
296 {
297 s_assert(set != NULL);
298
299 set->refs++;
300
301 return set;
302 }
303
304 void
305 privilegeset_unref(struct PrivilegeSet *set)
306 {
307 s_assert(set != NULL);
308
309 if (set->refs > 0)
310 set->refs--;
311 else
312 ilog(L_MAIN, "refs on privset %s is already 0",
313 set->name);
314 if (set->refs == 0 && set->status & CONF_ILLEGAL)
315 {
316 rb_dlinkDelete(&set->node, &privilegeset_list);
317
318 privilegeset_free(set);
319 }
320 }
321
322 struct privset_diff
323 privilegeset_diff(const struct PrivilegeSet *old, const struct PrivilegeSet *new)
324 {
325 static const char *no_privs[] = { NULL };
326 static const struct PrivilegeSet empty = { .size = 0, .privs = no_privs };
327 static struct PrivilegeSet *set_unchanged = NULL,
328 *set_added = NULL,
329 *set_removed = NULL;
330 static size_t n_privs = 0;
331 size_t new_size = n_privs ? n_privs : 32;
332 size_t i = 0, j = 0;
333
334 if (set_unchanged == NULL)
335 {
336 set_unchanged = privilegeset_new_orphan("<unchanged>");
337 set_added = privilegeset_new_orphan("<added>");
338 set_removed = privilegeset_new_orphan("<removed>");
339 }
340
341 if (old == NULL)
342 old = &empty;
343 if (new == NULL)
344 new = &empty;
345
346 while (new_size < MAX(old->size, new->size) + 1)
347 new_size *= 2;
348
349 if (new_size > n_privs)
350 {
351 set_unchanged->privs = rb_realloc(set_unchanged->privs, sizeof *set_unchanged->privs * new_size);
352 set_added->privs = rb_realloc(set_added->privs, sizeof *set_added->privs * new_size);
353 set_removed->privs = rb_realloc(set_removed->privs, sizeof *set_removed->privs * new_size);
354 }
355
356 const char **res_unchanged = set_unchanged->privs;
357 const char **res_added = set_added->privs;
358 const char **res_removed = set_removed->privs;
359
360 while (i < old->size || j < new->size)
361 {
362 const char *oldpriv = NULL, *newpriv = NULL;
363 int ord = 0;
364 oldpriv = privilegeset_privs(old)[i];
365 newpriv = privilegeset_privs(new)[j];
366
367 if (oldpriv && newpriv)
368 ord = strcmp(oldpriv, newpriv);
369
370 if (newpriv == NULL || ord < 0)
371 {
372 *res_removed++ = oldpriv;
373 i++;
374 }
375 else if (oldpriv == NULL || ord > 0)
376 {
377 *res_added++ = newpriv;
378 j++;
379 }
380 else
381 {
382 *res_unchanged++ = oldpriv;
383 i++; j++;
384 }
385 }
386
387 *res_removed = *res_added = *res_unchanged = NULL;
388 set_unchanged->size = res_unchanged - set_unchanged->privs;
389 set_added->size = res_added - set_added->privs;
390 set_removed->size = res_removed - set_removed->privs;
391
392 return (struct privset_diff){
393 .unchanged = set_unchanged,
394 .added = set_added,
395 .removed = set_removed,
396 };
397 }
398
399 void
400 privilegeset_prepare_rehash()
401 {
402 rb_dlink_node *iter;
403
404 RB_DLINK_FOREACH(iter, privilegeset_list.head)
405 {
406 struct PrivilegeSet *set = iter->data;
407
408 /* the "default" privset is special and must remain available */
409 if (!strcmp(set->name, "default"))
410 continue;
411
412 set->status |= CONF_ILLEGAL;
413 privilegeset_shade(set);
414 }
415 }
416
417 void
418 privilegeset_cleanup_rehash()
419 {
420 rb_dlink_node *iter, *next;
421
422 RB_DLINK_FOREACH_SAFE(iter, next, privilegeset_list.head)
423 {
424 struct PrivilegeSet *set = iter->data;
425
426 if (set->shadow)
427 {
428 privilegeset_free(set->shadow);
429 set->shadow = NULL;
430 }
431
432 privilegeset_ref(set);
433 privilegeset_unref(set);
434 }
435 }
436
437 void
438 privilegeset_report(struct Client *source_p)
439 {
440 rb_dlink_node *ptr;
441
442 RB_DLINK_FOREACH(ptr, privilegeset_list.head)
443 {
444 struct PrivilegeSet *set = ptr->data;
445
446 /* use RPL_STATSDEBUG for now -- jilles */
447 send_multiline_init(source_p, " ", ":%s %03d %s O :%s ",
448 get_id(&me, source_p),
449 RPL_STATSDEBUG,
450 get_id(source_p, source_p),
451 set->name);
452 send_multiline_remote_pad(source_p, &me);
453 send_multiline_remote_pad(source_p, source_p);
454 for (const char *const *s = privilegeset_privs(set); *s != NULL; s++)
455 send_multiline_item(source_p, "%s", *s);
456 send_multiline_fini(source_p, NULL);
457 }
458 }