1 /*
2 * atheme-services: A collection of minimalist IRC services
3 * privs.c: Fine grained services operator privileges
4 *
5 * Copyright (c) 2005-2007 Atheme Project (http://www.atheme.org)
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
12 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
13 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
15 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
16 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
17 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
19 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
20 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21 * POSSIBILITY OF SUCH DAMAGE.
22 */
23
24 #include "atheme.h"
25 #include "privs.h"
26
27 mowgli_list_t operclasslist;
28 mowgli_list_t soperlist;
29
30 mowgli_heap_t *operclass_heap;
31 mowgli_heap_t *soper_heap;
32
33 static operclass_t *user_r = NULL;
34 static operclass_t *authenticated_r = NULL;
35 static operclass_t *ircop_r = NULL;
36
init_privs(void)37 void init_privs(void)
38 {
39 operclass_heap = sharedheap_get(sizeof(operclass_t));
40 soper_heap = sharedheap_get(sizeof(soper_t));
41
42 if (!operclass_heap || !soper_heap)
43 {
44 slog(LG_INFO, "init_privs(): block allocator failed.");
45 exit(EXIT_FAILURE);
46 }
47
48 /* create built-in operclasses. */
49 user_r = operclass_add("user", "", OPERCLASS_BUILTIN);
50 authenticated_r = operclass_add("authenticated", AC_AUTHENTICATED, OPERCLASS_BUILTIN);
51 ircop_r = operclass_add("ircop", "", OPERCLASS_BUILTIN);
52 }
53
54 /*************************
55 * O P E R C L A S S E S *
56 *************************/
57 /*
58 * operclass_add(const char *name, const char *privs)
59 *
60 * Add or override an operclass.
61 */
operclass_add(const char * name,const char * privs,int flags)62 operclass_t *operclass_add(const char *name, const char *privs, int flags)
63 {
64 operclass_t *operclass;
65 mowgli_node_t *n;
66
67 operclass = operclass_find(name);
68
69 if (operclass != NULL)
70 {
71 bool builtin = operclass->flags & OPERCLASS_BUILTIN;
72
73 slog(LG_DEBUG, "operclass_add(): update %s [%s]", name, privs);
74
75 free(operclass->privs);
76 operclass->privs = sstrdup(privs);
77 operclass->flags = flags | (builtin ? OPERCLASS_BUILTIN : 0);
78
79 return operclass;
80 }
81
82 slog(LG_DEBUG, "operclass_add(): create %s [%s]", name, privs);
83
84 operclass = mowgli_heap_alloc(operclass_heap);
85 operclass->name = sstrdup(name);
86 operclass->privs = sstrdup(privs);
87 operclass->flags = flags;
88
89 mowgli_node_add(operclass, &operclass->node, &operclasslist);
90
91 cnt.operclass++;
92
93 MOWGLI_ITER_FOREACH(n, soperlist.head)
94 {
95 soper_t *soper = n->data;
96 if (soper->operclass == NULL &&
97 !strcasecmp(name, soper->classname))
98 soper->operclass = operclass;
99 }
100
101 return operclass;
102 }
103
operclass_delete(operclass_t * operclass)104 void operclass_delete(operclass_t *operclass)
105 {
106 mowgli_node_t *n;
107
108 if (operclass == NULL)
109 return;
110
111 if (operclass->flags & OPERCLASS_BUILTIN)
112 return;
113
114 slog(LG_DEBUG, "operclass_delete(): %s", operclass->name);
115 mowgli_node_delete(&operclass->node, &operclasslist);
116
117 MOWGLI_ITER_FOREACH(n, soperlist.head)
118 {
119 soper_t *soper = n->data;
120 if (soper->operclass == operclass)
121 soper->operclass = NULL;
122 }
123
124 free(operclass->name);
125 free(operclass->privs);
126
127 mowgli_heap_free(operclass_heap, operclass);
128 cnt.operclass--;
129 }
130
operclass_find(const char * name)131 operclass_t *operclass_find(const char *name)
132 {
133 operclass_t *operclass;
134 mowgli_node_t *n;
135
136 MOWGLI_ITER_FOREACH(n, operclasslist.head)
137 {
138 operclass = (operclass_t *)n->data;
139
140 if (!strcasecmp(operclass->name, name))
141 return operclass;
142 }
143
144 return NULL;
145 }
146
147 /***************
148 * S O P E R S *
149 ***************/
150
soper_add(const char * name,const char * classname,int flags,const char * password)151 soper_t *soper_add(const char *name, const char *classname, int flags, const char *password)
152 {
153 soper_t *soper;
154 myuser_t *mu = myuser_find(name);
155 mowgli_node_t *n;
156 operclass_t *operclass = operclass_find(classname);
157
158 soper = mu ? soper_find(mu) : soper_find_named(name);
159
160 if (soper != NULL)
161 {
162 if (flags & SOPER_CONF && !(soper->flags & SOPER_CONF))
163 {
164 slog(LG_INFO, "soper_add(): conf soper %s (%s) is replacing DB soper with class %s", name, classname, soper->classname);
165 soper_delete(soper);
166 }
167 else if (!(flags & SOPER_CONF) && soper->flags & SOPER_CONF)
168 {
169 slog(LG_INFO, "soper_add(): ignoring DB soper %s (%s) because of conf soper with class %s", name, classname, soper->classname);
170 return NULL;
171 }
172 else
173 {
174 slog(LG_INFO, "soper_add(): duplicate soper %s", name);
175 return NULL;
176 }
177 }
178
179 slog(LG_DEBUG, "soper_add(): %s -> %s", (mu) ? entity(mu)->name : name, operclass ? operclass->name : "<null>");
180
181 soper = mowgli_heap_alloc(soper_heap);
182 n = mowgli_node_create();
183
184 mowgli_node_add(soper, n, &soperlist);
185
186 if (mu)
187 {
188 soper->myuser = mu;
189 mu->soper = soper;
190 soper->name = NULL;
191 }
192 else
193 {
194 soper->name = sstrdup(name);
195 soper->myuser = NULL;
196 }
197
198 soper->operclass = operclass;
199 soper->classname = sstrdup(classname);
200 soper->flags = flags;
201 soper->password = sstrdup(password);
202
203 cnt.soper++;
204
205 return soper;
206 }
207
soper_delete(soper_t * soper)208 void soper_delete(soper_t *soper)
209 {
210 mowgli_node_t *n;
211
212 if (!soper)
213 {
214 slog(LG_DEBUG, "soper_delete(): called for null soper");
215
216 return;
217 }
218
219 slog(LG_DEBUG, "soper_delete(): %s", (soper->myuser) ? entity(soper->myuser)->name : soper->name);
220
221 n = mowgli_node_find(soper, &soperlist);
222 mowgli_node_delete(n, &soperlist);
223 mowgli_node_free(n);
224
225 if (soper->myuser)
226 soper->myuser->soper = NULL;
227
228 if (soper->name)
229 free(soper->name);
230
231 free(soper->classname);
232 free(soper->password);
233
234 mowgli_heap_free(soper_heap, soper);
235
236 cnt.soper--;
237 }
238
soper_find(myuser_t * myuser)239 soper_t *soper_find(myuser_t *myuser)
240 {
241 soper_t *soper;
242 mowgli_node_t *n;
243
244 MOWGLI_ITER_FOREACH(n, soperlist.head)
245 {
246 soper = (soper_t *)n->data;
247
248 if (soper->myuser && soper->myuser == myuser)
249 return soper;
250 }
251
252 return NULL;
253 }
254
soper_find_named(const char * name)255 soper_t *soper_find_named(const char *name)
256 {
257 soper_t *soper;
258 mowgli_node_t *n;
259
260 MOWGLI_ITER_FOREACH(n, soperlist.head)
261 {
262 soper = (soper_t *)n->data;
263
264 if (soper->name && !irccasecmp(soper->name, name))
265 return soper;
266 }
267
268 return NULL;
269 }
270
is_soper(myuser_t * myuser)271 bool is_soper(myuser_t *myuser)
272 {
273 if (!myuser)
274 return false;
275
276 if (myuser->soper)
277 return true;
278
279 return false;
280 }
281
is_conf_soper(myuser_t * myuser)282 bool is_conf_soper(myuser_t *myuser)
283 {
284 if (!myuser)
285 return false;
286
287 if (myuser->soper && myuser->soper->flags & SOPER_CONF)
288 return true;
289
290 return false;
291 }
292
293 /* name1 name2 name3... */
string_in_list(const char * str,const char * name)294 static bool string_in_list(const char *str, const char *name)
295 {
296 char *p;
297 int l;
298
299 if (str == NULL)
300 return false;
301 l = strlen(name);
302 while (*str != '\0')
303 {
304 p = strchr(str, ' ');
305 if (p != NULL ? p - str == l && !strncasecmp(str, name, p - str) : !strcasecmp(str, name))
306 return true;
307 if (p == NULL)
308 return false;
309 str = p;
310 while (*str == ' ')
311 str++;
312 }
313 return false;
314 }
315
has_priv_operclass(operclass_t * operclass,const char * priv)316 bool has_priv_operclass(operclass_t *operclass, const char *priv)
317 {
318 if (operclass == NULL)
319 return false;
320 if (string_in_list(operclass->privs, priv))
321 return true;
322 return false;
323 }
324
has_any_privs(sourceinfo_t * si)325 bool has_any_privs(sourceinfo_t *si)
326 {
327 if (si->su != NULL && is_ircop(si->su))
328 return true;
329 if (si->smu && is_soper(si->smu))
330 return true;
331 return false;
332 }
333
has_any_privs_user(user_t * u)334 bool has_any_privs_user(user_t *u)
335 {
336 if (u == NULL)
337 return false;
338 if (is_ircop(u))
339 return true;
340 if (u->myuser && is_soper(u->myuser))
341 return true;
342 return false;
343 }
344
has_priv(sourceinfo_t * si,const char * priv)345 bool has_priv(sourceinfo_t *si, const char *priv)
346 {
347 return si->su != NULL ? has_priv_user(si->su, priv) :
348 has_priv_myuser(si->smu, priv);
349 }
350
has_priv_user(user_t * u,const char * priv)351 bool has_priv_user(user_t *u, const char *priv)
352 {
353 operclass_t *operclass;
354
355 if (priv == NULL)
356 return true;
357
358 if (u == NULL)
359 return false;
360
361 if (has_priv_operclass(user_r, priv))
362 return true;
363
364 if (is_ircop(u) && has_priv_operclass(ircop_r, priv))
365 return true;
366
367 if (u->myuser != NULL && has_priv_operclass(authenticated_r, priv))
368 return true;
369
370 if (u->myuser && is_soper(u->myuser))
371 {
372 operclass = u->myuser->soper->operclass;
373 if (operclass == NULL)
374 return false;
375 if (operclass->flags & OPERCLASS_NEEDOPER && !is_ircop(u))
376 return false;
377 if (u->myuser->soper->password != NULL && !(u->flags & UF_SOPER_PASS))
378 return false;
379 if (has_priv_operclass(operclass, priv))
380 return true;
381 }
382
383 return false;
384 }
385
has_priv_myuser(myuser_t * mu,const char * priv)386 bool has_priv_myuser(myuser_t *mu, const char *priv)
387 {
388 operclass_t *operclass;
389
390 if (priv == NULL)
391 return true;
392 if (mu == NULL)
393 return false;
394
395 if (has_priv_operclass(authenticated_r, priv))
396 return true;
397
398 if (!is_soper(mu))
399 return false;
400 operclass = mu->soper->operclass;
401 if (operclass == NULL)
402 return false;
403 if (has_priv_operclass(operclass, priv))
404 return true;
405
406 return false;
407 }
408
has_all_operclass(sourceinfo_t * si,operclass_t * operclass)409 bool has_all_operclass(sourceinfo_t *si, operclass_t *operclass)
410 {
411 char *privs2;
412 char *priv;
413
414 privs2 = sstrdup(operclass->privs);
415 priv = strtok(privs2, " ");
416 while (priv != NULL)
417 {
418 if (!has_priv(si, priv))
419 {
420 free(privs2);
421 return false;
422 }
423 priv = strtok(NULL, " ");
424 }
425 free(privs2);
426 return true;
427 }
428
429 /**********************************************************************************/
430
get_sourceinfo_soper(sourceinfo_t * si)431 const soper_t *get_sourceinfo_soper(sourceinfo_t *si)
432 {
433 if (si->smu != NULL && is_soper(si->smu))
434 return si->smu->soper;
435
436 return NULL;
437 }
438
get_sourceinfo_operclass(sourceinfo_t * si)439 const operclass_t *get_sourceinfo_operclass(sourceinfo_t *si)
440 {
441 operclass_t *out = NULL;
442 const soper_t *soper;
443
444 if ((soper = get_sourceinfo_soper(si)) != NULL)
445 return soper->operclass;
446
447 if (si->su != NULL && is_ircop(si->su))
448 {
449 if ((out = operclass_find("ircop")) != NULL)
450 return out;
451 }
452
453 if ((out = operclass_find("user")) != NULL)
454 return out;
455
456 return NULL;
457 }
458
459 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
460 * vim:ts=8
461 * vim:sw=8
462 * vim:noexpandtab
463 */
464