Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/VibeDevelopers/Comet.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462/* * Comet: a slightly advanced ircd * privilege.c: Dynamic privileges API. * * Copyright (c) 2021 Ed Kellett <e@kellett.im> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * Copyright (c) 2008 Ariadne Conill <ariadne@dereferenced.org> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */
#include <stdinc.h>#include "s_conf.h"#include "privilege.h"#include "numeric.h"#include "s_assert.h"#include "logger.h"#include "send.h"
static rb_dlink_list privilegeset_list = {NULL, NULL, 0};
static struct PrivilegeSet *privilegeset_get_any(const char *name){ rb_dlink_node *iter;
s_assert(name != NULL);
RB_DLINK_FOREACH(iter, privilegeset_list.head) { struct PrivilegeSet *set = (struct PrivilegeSet *) iter->data;
if (!rb_strcasecmp(set->name, name)) return set; }
return NULL;}
static intprivilegeset_cmp_priv(const void *a_, const void *b_){ const char *const *a = a_, *const *b = b_; return strcmp(*a, *b);}
static voidprivilegeset_index(struct PrivilegeSet *set){ size_t n; const char *s; const char **p;
rb_free(set->privs);
set->privs = rb_malloc(sizeof *set->privs * (set->size + 1)); p = set->privs;
for (n = 0, s = set->priv_storage; n < set->size; n++, s += strlen(s) + 1) *p++ = s; qsort(set->privs, set->size, sizeof *set->privs, privilegeset_cmp_priv); set->privs[set->size] = NULL;}
voidprivilegeset_add_privs(struct PrivilegeSet *dst, const char *privs){ size_t alloc_size, old_stored_size;
if (dst->priv_storage == NULL) { dst->stored_size = dst->allocated_size = 0; alloc_size = 256; } else { alloc_size = dst->allocated_size; }
old_stored_size = dst->stored_size; dst->stored_size += strlen(privs) + 1;
while (alloc_size < dst->stored_size) alloc_size *= 2;
if (alloc_size > dst->allocated_size) dst->priv_storage = rb_realloc(dst->priv_storage, alloc_size);
dst->allocated_size = alloc_size;
const char *s; char *d;
for (s = privs, d = dst->priv_storage + old_stored_size; s <= privs + strlen(privs); s++, d++) { *d = *s; if (*d == ' ' || *d == '\0') { *d = '\0'; if (s > privs) dst->size += 1; } }
privilegeset_index(dst);}
static voidprivilegeset_add_privilegeset(struct PrivilegeSet *dst, const struct PrivilegeSet *src){ size_t cur_size, alloc_size;
if (dst->priv_storage == NULL) { dst->stored_size = dst->allocated_size = 0; cur_size = 0; alloc_size = 256; } else { cur_size = dst->stored_size; alloc_size = dst->allocated_size; }
dst->stored_size = cur_size + src->stored_size;
while (alloc_size < dst->stored_size) alloc_size *= 2;
if (alloc_size > dst->allocated_size) dst->priv_storage = rb_realloc(dst->priv_storage, alloc_size);
dst->allocated_size = alloc_size;
memcpy(dst->priv_storage + cur_size, src->priv_storage, src->stored_size); dst->size += src->size;
privilegeset_index(dst);}
static struct PrivilegeSet *privilegeset_new_orphan(const char *name){ struct PrivilegeSet *set; set = rb_malloc(sizeof *set); *set = (struct PrivilegeSet) { .size = 0, .privs = NULL, .priv_storage = NULL, .shadow = NULL, .status = 0, .refs = 0, .name = rb_strdup(name), }; return set;}
static voidprivilegeset_free(struct PrivilegeSet *set){ if (set == NULL) return;
privilegeset_free(set->shadow); rb_free(set->name); rb_free(set->privs); rb_free(set->priv_storage); rb_free(set);}
static voidprivilegeset_shade(struct PrivilegeSet *set){ privilegeset_free(set->shadow);
set->shadow = privilegeset_new_orphan(set->name); set->shadow->privs = set->privs; set->shadow->size = set->size; set->shadow->priv_storage = set->priv_storage; set->shadow->stored_size = set->stored_size; set->shadow->allocated_size = set->allocated_size;
set->privs = NULL; set->size = 0; set->priv_storage = NULL; set->stored_size = 0; set->allocated_size = 0;}
static voidprivilegeset_clear(struct PrivilegeSet *set){ rb_free(set->privs); set->privs = NULL; set->size = 0; set->stored_size = 0;}
boolprivilegeset_in_set(const struct PrivilegeSet *set, const char *priv){ s_assert(set != NULL); s_assert(priv != NULL);
const char **found = bsearch(&priv, set->privs, set->size, sizeof *set->privs, privilegeset_cmp_priv); return found != NULL;}
const char *const *privilegeset_privs(const struct PrivilegeSet *set){ static const char *no_privs[] = { NULL }; return set->privs != NULL ? set->privs : no_privs;}
struct PrivilegeSet *privilegeset_set_new(const char *name, const char *privs, PrivilegeFlags flags){ struct PrivilegeSet *set;
set = privilegeset_get_any(name); if (set != NULL) { if (!(set->status & CONF_ILLEGAL)) ilog(L_MAIN, "Duplicate privset %s", name); set->status &= ~CONF_ILLEGAL; privilegeset_clear(set); } else { set = privilegeset_new_orphan(name); rb_dlinkAdd(set, &set->node, &privilegeset_list); } privilegeset_add_privs(set, privs); set->flags = flags;
return set;}
struct PrivilegeSet *privilegeset_extend(const struct PrivilegeSet *parent, const char *name, const char *privs, PrivilegeFlags flags){ struct PrivilegeSet *set;
s_assert(parent != NULL); s_assert(name != NULL); s_assert(privs != NULL);
set = privilegeset_set_new(name, privs, flags); privilegeset_add_privilegeset(set, parent); set->flags = flags;
return set;}
struct PrivilegeSet *privilegeset_get(const char *name){ struct PrivilegeSet *set;
set = privilegeset_get_any(name); if (set != NULL && set->status & CONF_ILLEGAL) set = NULL; return set;}
struct PrivilegeSet *privilegeset_ref(struct PrivilegeSet *set){ s_assert(set != NULL);
set->refs++;
return set;}
voidprivilegeset_unref(struct PrivilegeSet *set){ s_assert(set != NULL);
if (set->refs > 0) set->refs--; else ilog(L_MAIN, "refs on privset %s is already 0", set->name); if (set->refs == 0 && set->status & CONF_ILLEGAL) { rb_dlinkDelete(&set->node, &privilegeset_list);
privilegeset_free(set); }}
struct privset_diffprivilegeset_diff(const struct PrivilegeSet *old, const struct PrivilegeSet *new){ static const char *no_privs[] = { NULL }; static const struct PrivilegeSet empty = { .size = 0, .privs = no_privs }; static struct PrivilegeSet *set_unchanged = NULL, *set_added = NULL, *set_removed = NULL; static size_t n_privs = 0; size_t new_size = n_privs ? n_privs : 32; size_t i = 0, j = 0;
if (set_unchanged == NULL) { set_unchanged = privilegeset_new_orphan("<unchanged>"); set_added = privilegeset_new_orphan("<added>"); set_removed = privilegeset_new_orphan("<removed>"); }
if (old == NULL) old = ∅ if (new == NULL) new = ∅
while (new_size < MAX(old->size, new->size) + 1) new_size *= 2;
if (new_size > n_privs) { set_unchanged->privs = rb_realloc(set_unchanged->privs, sizeof *set_unchanged->privs * new_size); set_added->privs = rb_realloc(set_added->privs, sizeof *set_added->privs * new_size); set_removed->privs = rb_realloc(set_removed->privs, sizeof *set_removed->privs * new_size); }
const char **res_unchanged = set_unchanged->privs; const char **res_added = set_added->privs; const char **res_removed = set_removed->privs;
while (i < old->size || j < new->size) { const char *oldpriv = NULL, *newpriv = NULL; int ord = 0; oldpriv = privilegeset_privs(old)[i]; newpriv = privilegeset_privs(new)[j];
if (oldpriv && newpriv) ord = strcmp(oldpriv, newpriv);
if (newpriv == NULL || ord < 0) { *res_removed++ = oldpriv; i++; } else if (oldpriv == NULL || ord > 0) { *res_added++ = newpriv; j++; } else { *res_unchanged++ = oldpriv; i++; j++; } }
*res_removed = *res_added = *res_unchanged = NULL; set_unchanged->size = res_unchanged - set_unchanged->privs; set_added->size = res_added - set_added->privs; set_removed->size = res_removed - set_removed->privs;
return (struct privset_diff){ .unchanged = set_unchanged, .added = set_added, .removed = set_removed, };}
voidprivilegeset_prepare_rehash(){ rb_dlink_node *iter;
RB_DLINK_FOREACH(iter, privilegeset_list.head) { struct PrivilegeSet *set = iter->data;
/* the "default" privset is special and must remain available */ if (!strcmp(set->name, "default")) continue;
set->status |= CONF_ILLEGAL; privilegeset_shade(set); }}
voidprivilegeset_cleanup_rehash(){ rb_dlink_node *iter, *next;
RB_DLINK_FOREACH_SAFE(iter, next, privilegeset_list.head) { struct PrivilegeSet *set = iter->data;
if (set->shadow) { privilegeset_free(set->shadow); set->shadow = NULL; }
privilegeset_ref(set); privilegeset_unref(set); }}
voidprivilegeset_report(struct Client *source_p){ rb_dlink_node *ptr;
RB_DLINK_FOREACH(ptr, privilegeset_list.head) { struct PrivilegeSet *set = ptr->data;
/* use RPL_STATSDEBUG for now -- jilles */ send_multiline_init(source_p, " ", ":%s %03d %s O :%s ", get_id(&me, source_p), RPL_STATSDEBUG, get_id(source_p, source_p), set->name); send_multiline_remote_pad(source_p, &me); send_multiline_remote_pad(source_p, source_p); for (const char *const *s = privilegeset_privs(set); *s != NULL; s++) send_multiline_item(source_p, "%s", *s); send_multiline_fini(source_p, NULL); }}