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
;
236 struct PrivilegeSet
*
237 privilegeset_set_new(const char *name
, const char *privs
, PrivilegeFlags flags
)
239 struct PrivilegeSet
*set
;
241 set
= privilegeset_get_any(name
);
244 if (!(set
->status
& CONF_ILLEGAL
))
245 ilog(L_MAIN
, "Duplicate privset %s", name
);
246 set
->status
&= ~CONF_ILLEGAL
;
247 privilegeset_clear(set
);
251 set
= privilegeset_new_orphan(name
);
252 rb_dlinkAdd(set
, &set
->node
, &privilegeset_list
);
254 privilegeset_add_privs(set
, privs
);
260 struct PrivilegeSet
*
261 privilegeset_extend(const struct PrivilegeSet
*parent
, const char *name
, const char *privs
, PrivilegeFlags flags
)
263 struct PrivilegeSet
*set
;
265 s_assert(parent
!= NULL
);
266 s_assert(name
!= NULL
);
267 s_assert(privs
!= NULL
);
269 set
= privilegeset_set_new(name
, privs
, flags
);
270 privilegeset_add_privilegeset(set
, parent
);
276 struct PrivilegeSet
*
277 privilegeset_get(const char *name
)
279 struct PrivilegeSet
*set
;
281 set
= privilegeset_get_any(name
);
282 if (set
!= NULL
&& set
->status
& CONF_ILLEGAL
)
287 struct PrivilegeSet
*
288 privilegeset_ref(struct PrivilegeSet
*set
)
290 s_assert(set
!= NULL
);
298 privilegeset_unref(struct PrivilegeSet
*set
)
300 s_assert(set
!= NULL
);
305 ilog(L_MAIN
, "refs on privset %s is already 0",
307 if (set
->refs
== 0 && set
->status
& CONF_ILLEGAL
)
309 rb_dlinkDelete(&set
->node
, &privilegeset_list
);
311 privilegeset_free(set
);
315 const struct PrivilegeSet
**
316 privilegeset_diff(const struct PrivilegeSet
*old
, const struct PrivilegeSet
*new)
318 static const char *no_privs
[] = { NULL
};
319 static const struct PrivilegeSet empty
= { .size
= 0, .privs
= no_privs
};
320 static struct PrivilegeSet
*set_unchanged
= NULL
,
323 static const struct PrivilegeSet
*result_sets
[3];
324 static size_t n_privs
= 0;
325 size_t new_size
= n_privs
? n_privs
: 32;
328 if (result_sets
[0] == NULL
)
330 result_sets
[0] = set_unchanged
= privilegeset_new_orphan("<unchanged>");
331 result_sets
[1] = set_added
= privilegeset_new_orphan("<added>");
332 result_sets
[2] = set_removed
= privilegeset_new_orphan("<removed>");
340 while (new_size
< MAX(old
->size
, new->size
) + 1)
343 if (new_size
> n_privs
)
345 set_unchanged
->privs
= rb_realloc(set_unchanged
->privs
, sizeof *set_unchanged
->privs
* new_size
);
346 set_added
->privs
= rb_realloc(set_added
->privs
, sizeof *set_added
->privs
* new_size
);
347 set_removed
->privs
= rb_realloc(set_removed
->privs
, sizeof *set_removed
->privs
* new_size
);
350 const char **res_unchanged
= set_unchanged
->privs
;
351 const char **res_added
= set_added
->privs
;
352 const char **res_removed
= set_removed
->privs
;
354 while (i
< old
->size
|| j
< new->size
)
356 const char *oldpriv
= NULL
, *newpriv
= NULL
;
359 oldpriv
= old
->privs
[i
];
361 newpriv
= new->privs
[j
];
363 if (oldpriv
&& newpriv
)
364 ord
= strcmp(oldpriv
, newpriv
);
366 if (newpriv
== NULL
|| ord
< 0)
368 *res_removed
++ = oldpriv
;
371 else if (oldpriv
== NULL
|| ord
> 0)
373 *res_added
++ = newpriv
;
378 *res_unchanged
++ = oldpriv
;
383 *res_removed
= *res_added
= *res_unchanged
= NULL
;
384 set_unchanged
->size
= res_unchanged
- set_unchanged
->privs
;
385 set_added
->size
= res_added
- set_added
->privs
;
386 set_removed
->size
= res_removed
- set_removed
->privs
;
392 privilegeset_prepare_rehash()
396 RB_DLINK_FOREACH(iter
, privilegeset_list
.head
)
398 struct PrivilegeSet
*set
= iter
->data
;
400 /* the "default" privset is special and must remain available */
401 if (!strcmp(set
->name
, "default"))
404 set
->status
|= CONF_ILLEGAL
;
405 privilegeset_shade(set
);
410 privilegeset_cleanup_rehash()
412 rb_dlink_node
*iter
, *next
;
414 RB_DLINK_FOREACH_SAFE(iter
, next
, privilegeset_list
.head
)
416 struct PrivilegeSet
*set
= iter
->data
;
420 privilegeset_free(set
->shadow
);
424 privilegeset_ref(set
);
425 privilegeset_unref(set
);
430 privilegeset_report(struct Client
*source_p
)
434 RB_DLINK_FOREACH(ptr
, privilegeset_list
.head
)
436 struct PrivilegeSet
*set
= ptr
->data
;
438 /* use RPL_STATSDEBUG for now -- jilles */
439 send_multiline_init(source_p
, " ", ":%s %03d %s O :%s ",
440 get_id(&me
, source_p
),
442 get_id(source_p
, source_p
),
444 send_multiline_remote_pad(source_p
, &me
);
445 send_multiline_remote_pad(source_p
, source_p
);
446 for (const char **s
= set
->privs
; s
&& *s
; s
++)
447 send_multiline_item(source_p
, "%s", *s
);
448 send_multiline_fini(source_p
, NULL
);