2 * Solanum: a slightly advanced ircd
3 * privilege.c: Dynamic privileges API.
5 * Copyright (c) 2021 Ed Kellett <e@kellett.im>
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.
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.
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
22 * Copyright (c) 2008 Ariadne Conill <ariadne@dereferenced.org>
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.
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.
43 #include "privilege.h"
49 static rb_dlink_list privilegeset_list
= {NULL
, NULL
, 0};
51 static struct PrivilegeSet
*
52 privilegeset_get_any(const char *name
)
56 s_assert(name
!= NULL
);
58 RB_DLINK_FOREACH(iter
, privilegeset_list
.head
)
60 struct PrivilegeSet
*set
= (struct PrivilegeSet
*) iter
->data
;
62 if (!rb_strcasecmp(set
->name
, name
))
70 privilegeset_cmp_priv(const void *a_
, const void *b_
)
72 const char *const *a
= a_
, *const *b
= b_
;
73 return strcmp(*a
, *b
);
77 privilegeset_index(struct PrivilegeSet
*set
)
85 set
->privs
= rb_malloc(sizeof *set
->privs
* (set
->size
+ 1));
88 for (n
= 0, s
= set
->priv_storage
; n
< set
->size
; n
++, s
+= strlen(s
) + 1)
90 qsort(set
->privs
, set
->size
, sizeof *set
->privs
, privilegeset_cmp_priv
);
91 set
->privs
[set
->size
] = NULL
;
95 privilegeset_add_privs(struct PrivilegeSet
*dst
, const char *privs
)
97 size_t alloc_size
, old_stored_size
;
99 if (dst
->priv_storage
== NULL
)
101 dst
->stored_size
= dst
->allocated_size
= 0;
106 alloc_size
= dst
->allocated_size
;
109 old_stored_size
= dst
->stored_size
;
110 dst
->stored_size
+= strlen(privs
) + 1;
112 while (alloc_size
< dst
->stored_size
)
115 if (alloc_size
> dst
->allocated_size
)
116 dst
->priv_storage
= rb_realloc(dst
->priv_storage
, alloc_size
);
118 dst
->allocated_size
= alloc_size
;
123 for (s
= privs
, d
= dst
->priv_storage
+ old_stored_size
;
124 s
<= privs
+ strlen(privs
);
128 if (*d
== ' ' || *d
== '\0')
131 if (s
> privs
) dst
->size
+= 1;
135 privilegeset_index(dst
);
139 privilegeset_add_privilegeset(struct PrivilegeSet
*dst
, const struct PrivilegeSet
*src
)
141 size_t cur_size
, alloc_size
;
143 if (dst
->priv_storage
== NULL
)
145 dst
->stored_size
= dst
->allocated_size
= 0;
151 cur_size
= dst
->stored_size
;
152 alloc_size
= dst
->allocated_size
;
155 dst
->stored_size
= cur_size
+ src
->stored_size
;
157 while (alloc_size
< dst
->stored_size
)
160 if (alloc_size
> dst
->allocated_size
)
161 dst
->priv_storage
= rb_realloc(dst
->priv_storage
, alloc_size
);
163 dst
->allocated_size
= alloc_size
;
165 memcpy(dst
->priv_storage
+ cur_size
, src
->priv_storage
, src
->stored_size
);
166 dst
->size
+= src
->size
;
168 privilegeset_index(dst
);
171 static struct PrivilegeSet
*
172 privilegeset_new_orphan(const char *name
)
174 struct PrivilegeSet
*set
;
175 set
= rb_malloc(sizeof *set
);
176 *set
= (struct PrivilegeSet
) {
179 .priv_storage
= NULL
,
183 .name
= rb_strdup(name
),
189 privilegeset_free(struct PrivilegeSet
*set
)
194 privilegeset_free(set
->shadow
);
197 rb_free(set
->priv_storage
);
202 privilegeset_shade(struct PrivilegeSet
*set
)
204 privilegeset_free(set
->shadow
);
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
;
215 set
->priv_storage
= NULL
;
216 set
->stored_size
= 0;
217 set
->allocated_size
= 0;
221 privilegeset_clear(struct PrivilegeSet
*set
)
226 set
->stored_size
= 0;
230 privilegeset_in_set(const struct PrivilegeSet
*set
, const char *priv
)
232 s_assert(set
!= NULL
);
233 s_assert(priv
!= NULL
);
235 const char **found
= bsearch(&priv
, set
->privs
, set
->size
, sizeof *set
->privs
, privilegeset_cmp_priv
);
236 return found
!= NULL
;
240 privilegeset_privs(const struct PrivilegeSet
*set
)
242 static const char *no_privs
[] = { NULL
};
243 return set
->privs
!= NULL
? set
->privs
: no_privs
;
246 struct PrivilegeSet
*
247 privilegeset_set_new(const char *name
, const char *privs
, PrivilegeFlags flags
)
249 struct PrivilegeSet
*set
;
251 set
= privilegeset_get_any(name
);
254 if (!(set
->status
& CONF_ILLEGAL
))
255 ilog(L_MAIN
, "Duplicate privset %s", name
);
256 set
->status
&= ~CONF_ILLEGAL
;
257 privilegeset_clear(set
);
261 set
= privilegeset_new_orphan(name
);
262 rb_dlinkAdd(set
, &set
->node
, &privilegeset_list
);
264 privilegeset_add_privs(set
, privs
);
270 struct PrivilegeSet
*
271 privilegeset_extend(const struct PrivilegeSet
*parent
, const char *name
, const char *privs
, PrivilegeFlags flags
)
273 struct PrivilegeSet
*set
;
275 s_assert(parent
!= NULL
);
276 s_assert(name
!= NULL
);
277 s_assert(privs
!= NULL
);
279 set
= privilegeset_set_new(name
, privs
, flags
);
280 privilegeset_add_privilegeset(set
, parent
);
286 struct PrivilegeSet
*
287 privilegeset_get(const char *name
)
289 struct PrivilegeSet
*set
;
291 set
= privilegeset_get_any(name
);
292 if (set
!= NULL
&& set
->status
& CONF_ILLEGAL
)
297 struct PrivilegeSet
*
298 privilegeset_ref(struct PrivilegeSet
*set
)
300 s_assert(set
!= NULL
);
308 privilegeset_unref(struct PrivilegeSet
*set
)
310 s_assert(set
!= NULL
);
315 ilog(L_MAIN
, "refs on privset %s is already 0",
317 if (set
->refs
== 0 && set
->status
& CONF_ILLEGAL
)
319 rb_dlinkDelete(&set
->node
, &privilegeset_list
);
321 privilegeset_free(set
);
326 privilegeset_diff(const struct PrivilegeSet
*old
, const struct PrivilegeSet
*new)
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
,
333 static size_t n_privs
= 0;
334 size_t new_size
= n_privs
? n_privs
: 32;
337 if (set_unchanged
== NULL
)
339 set_unchanged
= privilegeset_new_orphan("<unchanged>");
340 set_added
= privilegeset_new_orphan("<added>");
341 set_removed
= privilegeset_new_orphan("<removed>");
349 while (new_size
< MAX(old
->size
, new->size
) + 1)
352 if (new_size
> n_privs
)
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
);
359 const char **res_unchanged
= set_unchanged
->privs
;
360 const char **res_added
= set_added
->privs
;
361 const char **res_removed
= set_removed
->privs
;
363 while (i
< old
->size
|| j
< new->size
)
365 const char *oldpriv
= NULL
, *newpriv
= NULL
;
367 oldpriv
= privilegeset_privs(old
)[i
];
368 newpriv
= privilegeset_privs(new)[j
];
370 if (oldpriv
&& newpriv
)
371 ord
= strcmp(oldpriv
, newpriv
);
373 if (newpriv
== NULL
|| ord
< 0)
375 *res_removed
++ = oldpriv
;
378 else if (oldpriv
== NULL
|| ord
> 0)
380 *res_added
++ = newpriv
;
385 *res_unchanged
++ = oldpriv
;
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
;
395 return (struct privset_diff
){
396 .unchanged
= set_unchanged
,
398 .removed
= set_removed
,
403 privilegeset_prepare_rehash()
407 RB_DLINK_FOREACH(iter
, privilegeset_list
.head
)
409 struct PrivilegeSet
*set
= iter
->data
;
411 /* the "default" privset is special and must remain available */
412 if (!strcmp(set
->name
, "default"))
415 set
->status
|= CONF_ILLEGAL
;
416 privilegeset_shade(set
);
421 privilegeset_cleanup_rehash()
423 rb_dlink_node
*iter
, *next
;
425 RB_DLINK_FOREACH_SAFE(iter
, next
, privilegeset_list
.head
)
427 struct PrivilegeSet
*set
= iter
->data
;
431 privilegeset_free(set
->shadow
);
435 privilegeset_ref(set
);
436 privilegeset_unref(set
);
441 privilegeset_report(struct Client
*source_p
)
445 RB_DLINK_FOREACH(ptr
, privilegeset_list
.head
)
447 struct PrivilegeSet
*set
= ptr
->data
;
449 /* use RPL_STATSDEBUG for now -- jilles */
450 send_multiline_init(source_p
, " ", ":%s %03d %s O :%s ",
451 get_id(&me
, source_p
),
453 get_id(source_p
, source_p
),
455 send_multiline_remote_pad(source_p
, &me
);
456 send_multiline_remote_pad(source_p
, source_p
);
457 for (const char *const *s
= privilegeset_privs(set
); *s
!= NULL
; s
++)
458 send_multiline_item(source_p
, "%s", *s
);
459 send_multiline_fini(source_p
, NULL
);