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 William Pitcock <nenolod@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
)
100 if (dst
->priv_storage
== NULL
)
102 dst
->stored_size
= dst
->allocated_size
= 0;
107 alloc_size
= dst
->allocated_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
;
122 for (s
= privs
, d
= dst
->priv_storage
; s
< privs
+ strlen(privs
); s
+= n
, d
+= n
)
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
));
132 privilegeset_index(dst
);
136 privilegeset_add_privilegeset(struct PrivilegeSet
*dst
, const struct PrivilegeSet
*src
)
138 size_t cur_size
, alloc_size
;
140 if (dst
->priv_storage
== NULL
)
142 dst
->stored_size
= dst
->allocated_size
= 0;
148 cur_size
= dst
->stored_size
;
149 alloc_size
= dst
->allocated_size
;
152 dst
->stored_size
= cur_size
+ src
->stored_size
;
154 while (alloc_size
< dst
->stored_size
)
157 if (alloc_size
> dst
->allocated_size
)
158 dst
->priv_storage
= rb_realloc(dst
->priv_storage
, alloc_size
);
160 dst
->allocated_size
= alloc_size
;
162 memcpy(dst
->priv_storage
+ cur_size
, src
->priv_storage
, src
->stored_size
);
163 dst
->size
+= src
->size
;
165 privilegeset_index(dst
);
168 static struct PrivilegeSet
*
169 privilegeset_new_orphan(const char *name
)
171 struct PrivilegeSet
*set
;
172 set
= rb_malloc(sizeof *set
);
173 *set
= (struct PrivilegeSet
) {
176 .priv_storage
= NULL
,
180 .name
= rb_strdup(name
),
186 privilegeset_free(struct PrivilegeSet
*set
)
191 privilegeset_free(set
->shadow
);
194 rb_free(set
->priv_storage
);
199 privilegeset_shade(struct PrivilegeSet
*set
)
201 privilegeset_free(set
->shadow
);
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
;
212 set
->priv_storage
= NULL
;
213 set
->stored_size
= 0;
214 set
->allocated_size
= 0;
218 privilegeset_clear(struct PrivilegeSet
*set
)
223 set
->stored_size
= 0;
227 privilegeset_in_set(const struct PrivilegeSet
*set
, const char *priv
)
229 s_assert(set
!= NULL
);
230 s_assert(priv
!= NULL
);
232 const char **found
= bsearch(&priv
, set
->privs
, set
->size
, sizeof *set
->privs
, privilegeset_cmp_priv
);
233 return found
!= NULL
;
237 privilegeset_privs(const struct PrivilegeSet
*set
)
239 static const char *no_privs
[] = { NULL
};
240 return set
->privs
!= NULL
? set
->privs
: no_privs
;
243 struct PrivilegeSet
*
244 privilegeset_set_new(const char *name
, const char *privs
, PrivilegeFlags flags
)
246 struct PrivilegeSet
*set
;
248 set
= privilegeset_get_any(name
);
251 if (!(set
->status
& CONF_ILLEGAL
))
252 ilog(L_MAIN
, "Duplicate privset %s", name
);
253 set
->status
&= ~CONF_ILLEGAL
;
254 privilegeset_clear(set
);
258 set
= privilegeset_new_orphan(name
);
259 rb_dlinkAdd(set
, &set
->node
, &privilegeset_list
);
261 privilegeset_add_privs(set
, privs
);
267 struct PrivilegeSet
*
268 privilegeset_extend(const struct PrivilegeSet
*parent
, const char *name
, const char *privs
, PrivilegeFlags flags
)
270 struct PrivilegeSet
*set
;
272 s_assert(parent
!= NULL
);
273 s_assert(name
!= NULL
);
274 s_assert(privs
!= NULL
);
276 set
= privilegeset_set_new(name
, privs
, flags
);
277 privilegeset_add_privilegeset(set
, parent
);
283 struct PrivilegeSet
*
284 privilegeset_get(const char *name
)
286 struct PrivilegeSet
*set
;
288 set
= privilegeset_get_any(name
);
289 if (set
!= NULL
&& set
->status
& CONF_ILLEGAL
)
294 struct PrivilegeSet
*
295 privilegeset_ref(struct PrivilegeSet
*set
)
297 s_assert(set
!= NULL
);
305 privilegeset_unref(struct PrivilegeSet
*set
)
307 s_assert(set
!= NULL
);
312 ilog(L_MAIN
, "refs on privset %s is already 0",
314 if (set
->refs
== 0 && set
->status
& CONF_ILLEGAL
)
316 rb_dlinkDelete(&set
->node
, &privilegeset_list
);
318 privilegeset_free(set
);
323 privilegeset_diff(const struct PrivilegeSet
*old
, const struct PrivilegeSet
*new)
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
,
330 static size_t n_privs
= 0;
331 size_t new_size
= n_privs
? n_privs
: 32;
334 if (set_unchanged
== NULL
)
336 set_unchanged
= privilegeset_new_orphan("<unchanged>");
337 set_added
= privilegeset_new_orphan("<added>");
338 set_removed
= privilegeset_new_orphan("<removed>");
346 while (new_size
< MAX(old
->size
, new->size
) + 1)
349 if (new_size
> n_privs
)
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
);
356 const char **res_unchanged
= set_unchanged
->privs
;
357 const char **res_added
= set_added
->privs
;
358 const char **res_removed
= set_removed
->privs
;
360 while (i
< old
->size
|| j
< new->size
)
362 const char *oldpriv
= NULL
, *newpriv
= NULL
;
364 oldpriv
= privilegeset_privs(old
)[i
];
365 newpriv
= privilegeset_privs(new)[j
];
367 if (oldpriv
&& newpriv
)
368 ord
= strcmp(oldpriv
, newpriv
);
370 if (newpriv
== NULL
|| ord
< 0)
372 *res_removed
++ = oldpriv
;
375 else if (oldpriv
== NULL
|| ord
> 0)
377 *res_added
++ = newpriv
;
382 *res_unchanged
++ = oldpriv
;
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
;
392 return (struct privset_diff
){
393 .unchanged
= set_unchanged
,
395 .removed
= set_removed
,
400 privilegeset_prepare_rehash()
404 RB_DLINK_FOREACH(iter
, privilegeset_list
.head
)
406 struct PrivilegeSet
*set
= iter
->data
;
408 /* the "default" privset is special and must remain available */
409 if (!strcmp(set
->name
, "default"))
412 set
->status
|= CONF_ILLEGAL
;
413 privilegeset_shade(set
);
418 privilegeset_cleanup_rehash()
420 rb_dlink_node
*iter
, *next
;
422 RB_DLINK_FOREACH_SAFE(iter
, next
, privilegeset_list
.head
)
424 struct PrivilegeSet
*set
= iter
->data
;
428 privilegeset_free(set
->shadow
);
432 privilegeset_ref(set
);
433 privilegeset_unref(set
);
438 privilegeset_report(struct Client
*source_p
)
442 RB_DLINK_FOREACH(ptr
, privilegeset_list
.head
)
444 struct PrivilegeSet
*set
= ptr
->data
;
446 /* use RPL_STATSDEBUG for now -- jilles */
447 send_multiline_init(source_p
, " ", ":%s %03d %s O :%s ",
448 get_id(&me
, source_p
),
450 get_id(source_p
, source_p
),
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
);