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