closed some memory leaks
[umurmur.git] / src / ban.c
1 /* Copyright (C) 2009-2014, Martin Johansson <martin@fatbob.nu>
2    Copyright (C) 2005-2014, Thorvald Natvig <thorvald@natvig.com>
3
4    All rights reserved.
5
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9
10    - Redistributions of source code must retain the above copyright notice,
11          this list of conditions and the following disclaimer.
12    - Redistributions in binary form must reproduce the above copyright notice,
13      this list of conditions and the following disclaimer in the documentation
14      and/or other materials provided with the distribution.
15    - Neither the name of the Developers nor the names of its contributors may
16      be used to endorse or promote products derived from this software without
17      specific prior written permission.
18
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30    */
31
32 #include <stdlib.h>
33 #include <time.h>
34 #include <string.h>
35 #include "log.h"
36 #include "list.h"
37 #include "ban.h"
38 #include "conf.h"
39 #include "ssl.h"
40 #include "util.h"
41
42 static void Ban_saveBanFile(void);
43 static void Ban_readBanFile(void);
44
45
46 declare_list(banlist);
47 static int bancount; /* = 0 */
48 static int ban_duration;
49 static bool_t banlist_changed;
50
51 void Ban_init(void)
52 {
53         ban_duration = getIntConf(BAN_LENGTH);
54         /* Read ban file here */
55         if (getStrConf(BANFILE) != NULL)
56                 Ban_readBanFile();
57 }
58
59 void Ban_deinit(void)
60 {
61         /* Save banlist */
62         if (getStrConf(BANFILE) != NULL)
63                 Ban_saveBanFile();
64
65         Ban_clearBanList();
66 }
67
68 void Ban_UserBan(client_t *client, char *reason)
69 {
70         ban_t *ban;
71         char hexhash[41];
72
73         ban = calloc(1, sizeof(ban_t));
74         if (ban == NULL)
75                 Log_fatal("Out of memory");
76
77         memcpy(ban->hash, client->hash, 20);
78
79         ban->address = client->remote_tcp;
80         ban->mask = (ban->address.ss_family == AF_INET) ? 32 : 128;
81         ban->reason = strdup(reason);
82         ban->name = strdup(client->username);
83         ban->time = time(NULL);
84         ban->duration = ban_duration;
85         list_add_tail(&ban->node, &banlist);
86         bancount++;
87         banlist_changed = true;
88         if(getBoolConf(SYNC_BANFILE))
89                 Ban_saveBanFile();
90
91         SSLi_hash2hex(ban->hash, hexhash);
92
93         char *clientAddressString = Util_clientAddressToString(client);
94
95         Log_info_client(client, "User kickbanned. Reason: '%s' Hash: %s IP: %s Banned for: %d seconds",
96                 ban->reason, hexhash, clientAddressString, ban->duration);
97
98         free(clientAddressString);
99 }
100
101
102 void Ban_pruneBanned()
103 {
104         struct dlist *itr;
105         ban_t *ban;
106
107         list_iterate(itr, &banlist) {
108                 ban = list_get_entry(itr, ban_t, node);
109 #ifdef DEBUG
110                 SSLi_hash2hex(ban->hash, hexhash);
111                 Log_debug("BL: User %s Reason: '%s' Hash: %s IP: %s Time left: %d",
112                         ban->name, ban->reason, hexhash, Util_addressToString(&ban->address)),
113                         ban->time + ban->duration - time(NULL));
114 #endif
115                 /* Duration of 0 = forever */
116                 if (ban->duration != 0 && ban->time + ban->duration - time(NULL) <= 0) {
117                         free(ban->name);
118                         free(ban->reason);
119                         list_del(&ban->node);
120                         free(ban);
121                         bancount--;
122                         banlist_changed = true;
123                         if(getBoolConf(SYNC_BANFILE))
124                                 Ban_saveBanFile();
125                 }
126         }
127 }
128
129 bool_t Ban_isBanned(client_t *client)
130 {
131         struct dlist *itr;
132         ban_t *ban;
133         list_iterate(itr, &banlist) {
134                 ban = list_get_entry(itr, ban_t, node);
135                 if (memcmp(ban->hash, client->hash, 20) == 0)
136                         return true;
137         }
138         return false;
139
140 }
141
142 bool_t Ban_isBannedAddr(struct sockaddr_storage *address)
143 {
144         struct dlist *itr;
145         ban_t *ban;
146
147         list_iterate(itr, &banlist) {
148                 ban = list_get_entry(itr, ban_t, node);
149                 if (ban->address.ss_family == address->ss_family) {
150                         if (address->ss_family == AF_INET) {
151                                 uint32_t a1, a2, mask;
152                                 mask = (ban->mask == 32) ? UINT32_MAX : (1u << ban->mask) - 1;
153                                 a1 = (uint32_t)((struct sockaddr_in *)&ban->address)->sin_addr.s_addr & mask;
154                                 a2 = (uint32_t)((struct sockaddr_in *)address)->sin_addr.s_addr & mask;
155                                 if (a1 == a2)
156                                         return true;
157                         } else {
158                                 uint64_t mask[2];
159                                 uint64_t *a1 = (uint64_t *) &((struct sockaddr_in6 *)&ban->address)->sin6_addr.s6_addr;
160                                 uint64_t *a2 = (uint64_t *) &((struct sockaddr_in6 *)address)->sin6_addr.s6_addr;
161
162                                 if (ban->mask == 128)
163                                         mask[0] = mask[1] = 0xffffffffffffffffULL;
164                                 else if (ban->mask > 64) {
165                                         mask[0] = 0xffffffffffffffffULL;
166                                         mask[1] = SWAPPED(~((1ULL << (128 - ban->mask)) - 1));
167                                 } else {
168                                         mask[0] = SWAPPED(~((1ULL << (64 - ban->mask)) - 1));
169                                         mask[1] = 0ULL;
170                                 }
171                                 if ((a1[0] & mask[0]) == (a2[0] & mask[0]) &&
172                                     (a1[1] & mask[1]) == (a2[1] & mask[1]))
173                                     return true;
174                         }
175                 }
176         }
177
178         return false;
179 }
180
181 int Ban_getBanCount(void)
182 {
183         return bancount;
184 }
185
186 message_t *Ban_getBanList(void)
187 {
188         int i = 0;
189         struct dlist *itr;
190         ban_t *ban;
191         message_t *msg;
192         struct tm timespec;
193         char timestr[32];
194         char hexhash[41];
195         uint8_t address[16];
196
197         msg = Msg_banList_create(bancount);
198         list_iterate(itr, &banlist) {
199                 ban = list_get_entry(itr, ban_t, node);
200                 gmtime_r(&ban->time, &timespec);
201                 strftime(timestr, 32, "%Y-%m-%dT%H:%M:%SZ", &timespec);
202                 SSLi_hash2hex(ban->hash, hexhash);
203                 memset(address, 0, 16);
204
205                 if(ban->address.ss_family == AF_INET) {
206                         memcpy(&address[12], &((struct sockaddr_in *)&ban->address)->sin_addr, 4);
207                         memset(&address[10], 0xff, 2);
208                         Msg_banList_addEntry(msg, i++, address, ban->mask + 96, ban->name, hexhash, ban->reason, timestr, ban->duration);
209                 } else {
210                         memcpy(&address, &((struct sockaddr_in6 *)&ban->address)->sin6_addr, 16);
211                         Msg_banList_addEntry(msg, i++, address, ban->mask, ban->name, hexhash, ban->reason, timestr, ban->duration);
212                 }
213
214         }
215         return msg;
216 }
217
218 void Ban_clearBanList(void)
219 {
220         ban_t *ban;
221         struct dlist *itr, *save;
222         list_iterate_safe(itr, save, &banlist) {
223                 ban = list_get_entry(itr, ban_t, node);
224                 free(ban->name);
225                 free(ban->reason);
226                 list_del(&ban->node);
227                 free(ban);
228                 bancount--;
229         }
230 }
231
232 void Ban_putBanList(message_t *msg, int n_bans)
233 {
234         int i = 0;
235         struct tm timespec;
236         ban_t *ban;
237         char *hexhash, *name, *reason, *start;
238         uint32_t duration, mask;
239         uint8_t *address;
240         char mappedBytes[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff};
241         char *tz;
242
243         for (i = 0; i < n_bans; i++) {
244                 Msg_banList_getEntry(msg, i, &address, &mask, &name, &hexhash, &reason, &start, &duration);
245                 ban = malloc(sizeof(ban_t));
246                 if (ban == NULL)
247                         Log_fatal("Out of memory");
248                 SSLi_hex2hash(hexhash, ban->hash);
249
250                 if(memcmp(address, mappedBytes, 12) == 0) {
251                         memcpy(&((struct sockaddr_in *)&ban->address)->sin_addr, &address[12], 4);
252                         ban->address.ss_family = AF_INET;
253                         if (mask > 32) {
254                                 mask = 32;
255                         }
256                 } else {
257                         memcpy(&((struct sockaddr_in6 *)&ban->address)->sin6_addr, address, 16);
258                         ban->address.ss_family = AF_INET6;
259                 }
260
261                 ban->mask = mask;
262                 ban->reason = strdup(reason);
263                 ban->name = strdup(name);
264
265                 /*
266                  * Parse the timestring. We need to set TZ to UTC so that mktime() knows that the info in
267                  * struct tm indeed is given in UTC. Otherwise it will use the current locale. There's
268                  * apparently no other way to do this...
269                  */
270                 memset(&timespec, 0, sizeof(struct tm));
271                 strptime(start, "%Y-%m-%dT%H:%M:%S", &timespec);
272                 tz = getenv("TZ");
273                 setenv("TZ", "UTC", 1);
274                 tzset();
275                 ban->time = mktime(&timespec);
276                 if (tz)
277                         setenv("TZ", tz, 1);
278                 else
279                         unsetenv("TZ");
280                 tzset();
281
282                 ban->duration = duration;
283                 list_add_tail(&ban->node, &banlist);
284                 bancount++;
285         }
286         banlist_changed = true;
287         if(getBoolConf(SYNC_BANFILE))
288                 Ban_saveBanFile();
289 }
290
291 static void Ban_saveBanFile(void)
292 {
293         struct dlist *itr;
294         ban_t *ban;
295         char hexhash[41];
296         FILE *file;
297
298         if (!banlist_changed)
299                 return;
300         file = fopen(getStrConf(BANFILE), "w");
301         if (file == NULL) {
302                 Log_warn("Could not save banlist to file %s: %s", getStrConf(BANFILE), strerror(errno));
303                 return;
304         }
305         list_iterate(itr, &banlist) {
306                 ban = list_get_entry(itr, ban_t, node);
307                 SSLi_hash2hex(ban->hash, hexhash);
308
309                 fprintf(file, "%s,%s,%d,%ld,%d,%s,%s\n", hexhash, Util_addressToString(&ban->address),ban->mask, (long int)ban->time, ban->duration, ban->name, ban->reason);
310         }
311         fclose(file);
312         banlist_changed = false;
313         Log_info("Banlist file '%s': %d entries written", getStrConf(BANFILE), bancount);
314 }
315
316 static void Ban_readBanFile(void)
317 {
318         ban_t *ban;
319         char line[1024], *hexhash, *address, *name, *reason;
320         uint32_t mask, duration;
321         time_t time;
322         char *p;
323         FILE *file;
324
325         file = fopen(getStrConf(BANFILE), "r");
326         if (file == NULL) {
327                 Log_warn("Could not read banlist file %s: %s", getStrConf(BANFILE), strerror(errno));
328                 return;
329         }
330         while (fgets(line, 1024, file) != NULL) {
331                 p = strtok(line, ",");
332                 hexhash = p;
333                 p = strtok(NULL, ",");
334                 if (p == NULL) break;
335                 address = p;
336                 p = strtok(NULL, ",");
337                 if (p == NULL) break;
338                 mask = strtoul(p, NULL, 0);
339                 p = strtok(NULL, ",");
340                 if (p == NULL) break;
341                 time = strtoul(p, NULL, 0);
342                 p = strtok(NULL, ",");
343                 if (p == NULL) break;
344                 duration = strtoul(p, NULL, 0);
345                 p = strtok(NULL, ",");
346                 if (p == NULL) break;
347                 name = p;
348                 p = strtok(NULL, "\n");
349                 if (p == NULL) break;
350                 reason = p;
351
352                 ban = malloc(sizeof(ban_t));
353                 if (ban == NULL)
354                         Log_fatal("Out of memory");
355                 memset(ban, 0, sizeof(ban_t));
356                 SSLi_hex2hash(hexhash, ban->hash);
357                 if (inet_pton(AF_INET, address, &ban->address) == 0) {
358                         if (inet_pton(AF_INET6, address, &ban->address) == 0) {
359                                 Log_warn("Address \"%s\" is illegal!", address);
360                         } else {
361                                 ban->address.ss_family = AF_INET6;
362                         }
363                 } else {
364                         ban->address.ss_family = AF_INET;
365                 }
366                 ban->name = strdup(name);
367                 ban->reason = strdup(reason);
368                 if (ban->name == NULL || ban->reason == NULL)
369                         Log_fatal("Out of memory");
370                 ban->time = time;
371                 ban->duration = duration;
372                 ban->mask = mask;
373                 list_add_tail(&ban->node, &banlist);
374                 bancount++;
375                 Log_debug("Banfile: H = '%s' A = '%s' M = %d U = '%s' R = '%s'", hexhash, address, ban->mask, ban->name, ban->reason);
376         }
377         fclose(file);
378         Log_info("Banlist file '%s': %d entries read", getStrConf(BANFILE), bancount);
379 }