]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/lists.c
Fix use-after-free on Lists_CheckReason()
[ngircd-alex.git] / src / ngircd / lists.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * Management of IRC lists: ban, invite, etc.
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21
22 #include "defines.h"
23 #include "conn.h"
24 #include "channel.h"
25 #include "log.h"
26 #include "match.h"
27 #include "messages.h"
28 #include "irc-write.h"
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <strings.h>
33
34 #include "exp.h"
35 #include "lists.h"
36
37 #define MASK_LEN        (2*CLIENT_HOST_LEN)
38
39 struct list_elem {
40         struct list_elem *next; /** pointer to next list element */
41         char mask[MASK_LEN];    /** IRC mask */
42         char *reason;           /** Optional "reason" text */
43         time_t valid_until;     /** 0: unlimited; 1: once; t(>1): until t */
44 };
45
46 /**
47  * Get IRC mask stored in list element.
48  *
49  * @param list_elem List element.
50  * @return Pointer to IRC mask
51  */
52 GLOBAL const char *
53 Lists_GetMask(const struct list_elem *e)
54 {
55         assert(e != NULL);
56         return e->mask;
57 }
58
59 /**
60  * Get optional "reason" text stored in list element.
61  *
62  * @param list_elem List element.
63  * @return Pointer to "reason" text or empty string ("").
64  */
65 GLOBAL const char *
66 Lists_GetReason(const struct list_elem *e)
67 {
68         assert(e != NULL);
69         return e->reason ? e->reason : "";
70 }
71
72 /**
73  * Get "validity" value stored in list element.
74  *
75  * @param list_elem List element.
76  * @return Validity: 0=unlimited, 1=once, >1 until this time stamp.
77  */
78 GLOBAL time_t
79 Lists_GetValidity(const struct list_elem *e)
80 {
81         assert(e != NULL);
82         return e->valid_until;
83 }
84
85 /**
86  * Get first list element of a list.
87  *
88  * @param h List head.
89  * @return Pointer to first list element.
90  */
91 GLOBAL struct list_elem*
92 Lists_GetFirst(const struct list_head *h)
93 {
94         assert(h != NULL);
95         return h->first;
96 }
97
98 /**
99  * Get next list element of a list.
100  *
101  * @param e Current list element.
102  * @return Pointer to next list element.
103  */
104 GLOBAL struct list_elem*
105 Lists_GetNext(const struct list_elem *e)
106 {
107         assert(e != NULL);
108         return e->next;
109 }
110
111 /**
112  * Add a new mask to a list.
113  *
114  * @param h List head.
115  * @param Mask The IRC mask to add to the list.
116  * @param ValidUntil 0: unlimited, 1: only once, t>1: until given time_t.
117  * @param Reason Reason string or NULL, if no reason should be saved.
118  * @return true on success, false otherwise.
119  */
120 bool
121 Lists_Add(struct list_head *h, const char *Mask, time_t ValidUntil,
122           const char *Reason)
123 {
124         struct list_elem *e, *newelem;
125
126         assert(h != NULL);
127         assert(Mask != NULL);
128
129         e = Lists_CheckDupeMask(h, Mask);
130         if (e) {
131                 e->valid_until = ValidUntil;
132                 if (Reason) {
133                         if (e->reason)
134                                 free(e->reason);
135                         e->reason = strdup(Reason);
136                 }
137                 return true;
138         }
139
140         e = Lists_GetFirst(h);
141
142         newelem = malloc(sizeof(struct list_elem));
143         if (!newelem) {
144                 Log(LOG_EMERG,
145                     "Can't allocate memory for new list entry!");
146                 return false;
147         }
148
149         strlcpy(newelem->mask, Mask, sizeof(newelem->mask));
150         if (Reason) {
151                 newelem->reason = strdup(Reason);
152                 if (!newelem->reason)
153                         Log(LOG_EMERG,
154                             "Can't allocate memory for new list reason text!");
155         }
156         else
157                 newelem->reason = NULL;
158         newelem->valid_until = ValidUntil;
159         newelem->next = e;
160         h->first = newelem;
161
162         return true;
163 }
164
165 /**
166  * Delete a list element from a list.
167  *
168  * @param h List head.
169  * @param p Pointer to previous list element or NULL, if there is none.
170  * @param victim List element to delete.
171  */
172 static void
173 Lists_Unlink(struct list_head *h, struct list_elem *p, struct list_elem *victim)
174 {
175         assert(victim != NULL);
176         assert(h != NULL);
177
178         if (p)
179                 p->next = victim->next;
180         else
181                 h->first = victim->next;
182
183         if (victim->reason)
184                 free(victim->reason);
185
186         free(victim);
187 }
188
189 /**
190  * Delete a given IRC mask from a list.
191  *
192  * @param h List head.
193  * @param Mask IRC mask to delete from the list.
194  */
195 GLOBAL void
196 Lists_Del(struct list_head *h, const char *Mask)
197 {
198         struct list_elem *e, *last, *victim;
199
200         assert(h != NULL);
201         assert(Mask != NULL);
202
203         last = NULL;
204         e = Lists_GetFirst(h);
205         while (e) {
206                 if (strcasecmp(e->mask, Mask) == 0) {
207                         LogDebug("Deleted \"%s\" from list", e->mask);
208                         victim = e;
209                         e = victim->next;
210                         Lists_Unlink(h, last, victim);
211                         continue;
212                 }
213                 last = e;
214                 e = e->next;
215         }
216 }
217
218 /**
219  * Free a complete list.
220  *
221  * @param head List head.
222  */
223 GLOBAL void
224 Lists_Free(struct list_head *head)
225 {
226         struct list_elem *e, *victim;
227
228         assert(head != NULL);
229
230         e = head->first;
231         head->first = NULL;
232         while (e) {
233                 LogDebug("Deleted \"%s\" from list" , e->mask);
234                 victim = e;
235                 e = e->next;
236                 if (victim->reason)
237                         free(victim->reason);
238                 free(victim);
239         }
240 }
241
242 /**
243  * Check if an IRC mask is already contained in a list.
244  *
245  * @param h List head.
246  * @param Mask IRC mask to test.
247  * @return true if mask is already stored in the list, false otherwise.
248  */
249 GLOBAL struct list_elem *
250 Lists_CheckDupeMask(const struct list_head *h, const char *Mask )
251 {
252         struct list_elem *e;
253         e = h->first;
254         while (e) {
255                 if (strcasecmp(e->mask, Mask) == 0)
256                         return e;
257                 e = e->next;
258         }
259         return NULL;
260 }
261
262 /**
263  * Generate a valid IRC mask from "any" string given.
264  *
265  * Attention: This mask is only valid until the next call to Lists_MakeMask(),
266  * because a single global buffer ist used! You have to copy the generated
267  * mask to some sane location yourself!
268  *
269  * @param Pattern Source string to generate an IRC mask for.
270  * @return Pointer to global result buffer.
271  */
272 GLOBAL const char *
273 Lists_MakeMask(const char *Pattern)
274 {
275         static char TheMask[MASK_LEN];
276         char *excl, *at;
277
278         assert(Pattern != NULL);
279
280         excl = strchr(Pattern, '!');
281         at = strchr(Pattern, '@');
282
283         if (at && at < excl)
284                 excl = NULL;
285
286         if (!at && !excl) {
287                 /* Neither "!" nor "@" found: use string as nickname */
288                 strlcpy(TheMask, Pattern, sizeof(TheMask) - 5);
289                 strlcat(TheMask, "!*@*", sizeof(TheMask));
290                 return TheMask;
291         }
292
293         if (!at && excl) {
294                 /* Domain part is missing */
295                 strlcpy(TheMask, Pattern, sizeof(TheMask) - 3);
296                 strlcat(TheMask, "@*", sizeof(TheMask));
297                 return TheMask;
298         }
299
300         if (at && !excl) {
301                 /* User name is missing */
302                 *at = '\0'; at++;
303                 strlcpy(TheMask, Pattern, sizeof(TheMask) - 5);
304                 strlcat(TheMask, "!*@", sizeof(TheMask));
305                 strlcat(TheMask, at, sizeof(TheMask));
306                 return TheMask;
307         }
308
309         /* All parts (nick, user and domain name) are given */
310         strlcpy(TheMask, Pattern, sizeof(TheMask));
311         return TheMask;
312 } /* Lists_MakeMask */
313
314 /**
315  * Check if a client is listed in a list.
316  *
317  * @param h List head.
318  * @param Client Client to check.
319  * @return true if client is listed, false if not.
320  */
321 bool
322 Lists_Check(struct list_head *h, CLIENT *Client)
323 {
324         return Lists_CheckReason(h, Client, NULL, 0);
325 }
326
327 /**
328  * Check if a client is listed in a list and store the reason if a buffer
329  * is provided.
330  *
331  * @param h      List head.
332  * @param Client Client to check.
333  * @param reason Result buffer to store the reason.
334  * @param len    Size of the buffer.
335  * @return true if client is listed, false if not.
336  */
337 bool
338 Lists_CheckReason(struct list_head *h, CLIENT *Client, char *reason, size_t len)
339 {
340         struct list_elem *e, *last, *next;
341
342         assert(h != NULL);
343
344         e = h->first;
345         last = NULL;
346
347         while (e) {
348                 next = e->next;
349                 if (Match(e->mask, Client_MaskCloaked(Client))) {
350                         if (len && e->reason)
351                                 strlcpy(reason, e->reason, len);
352                         if (e->valid_until == 1) {
353                                 /* Entry is valid only once, delete it */
354                                 LogDebug("Deleted \"%s\" from list (used).",
355                                          e->mask);
356                                 Lists_Unlink(h, last, e);
357                         }
358                         return true;
359                 }
360                 last = e;
361                 e = next;
362         }
363
364         return false;
365 }
366
367 /**
368  * Check list and purge expired entries.
369  *
370  * @param h List head.
371  */
372 GLOBAL void
373 Lists_Expire(struct list_head *h, const char *ListName)
374 {
375         struct list_elem *e, *last, *next;
376         time_t now;
377
378         assert(h != NULL);
379
380         e = h->first;
381         last = NULL;
382         now = time(NULL);
383
384         while (e) {
385                 next = e->next;
386                 if (e->valid_until > 1 && e->valid_until < now) {
387                         /* Entry is expired, delete it */
388                         if (e->reason)
389                                 Log(LOG_INFO,
390                                     "Deleted \"%s\" (\"%s\") from %s list (expired).",
391                                     e->mask, e->reason, ListName);
392                         else
393                                 Log(LOG_INFO,
394                                     "Deleted \"%s\" from %s list (expired).",
395                                     e->mask, ListName);
396                         Lists_Unlink(h, last, e);
397                         e = next;
398                         continue;
399                 }
400                 last = e;
401                 e = next;
402         }
403 }
404
405 /**
406  * Return the number of entries of a list.
407  *
408  * @param h List head.
409  * @return Number of items.
410  */
411 GLOBAL unsigned long
412 Lists_Count(struct list_head *h)
413 {
414         struct list_elem *e;
415         unsigned long count = 0;
416
417         assert(h != NULL);
418
419         e = h->first;
420         while (e) {
421                 count++;
422                 e = e->next;
423         }
424         return count;
425 }
426
427 /* -eof- */