Merge pull request #49 from fmorgner/master
[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         Log_info_client(client, "User kickbanned. Reason: '%s' Hash: %s IP: %s Banned for: %d seconds",
94                 ban->reason, hexhash, Util_clientAddressToString(client), ban->duration);
95 }
96
97
98 void Ban_pruneBanned()
99 {
100         struct dlist *itr;
101         ban_t *ban;
102
103         list_iterate(itr, &banlist) {
104                 ban = list_get_entry(itr, ban_t, node);
105 #ifdef DEBUG
106                 SSLi_hash2hex(ban->hash, hexhash);
107                 Log_debug("BL: User %s Reason: '%s' Hash: %s IP: %s Time left: %d",
108                         ban->name, ban->reason, hexhash, Util_addressToString(&ban->address)),
109                         ban->time + ban->duration - time(NULL));
110 #endif
111                 /* Duration of 0 = forever */
112                 if (ban->duration != 0 && ban->time + ban->duration - time(NULL) <= 0) {
113                         free(ban->name);
114                         free(ban->reason);
115                         list_del(&ban->node);
116                         free(ban);
117                         bancount--;
118                         banlist_changed = true;
119                         if(getBoolConf(SYNC_BANFILE))
120                                 Ban_saveBanFile();
121                 }
122         }
123 }
124
125 bool_t Ban_isBanned(client_t *client)
126 {
127         struct dlist *itr;
128         ban_t *ban;
129         list_iterate(itr, &banlist) {
130                 ban = list_get_entry(itr, ban_t, node);
131                 if (memcmp(ban->hash, client->hash, 20) == 0)
132                         return true;
133         }
134         return false;
135
136 }
137
138 bool_t Ban_isBannedAddr(struct sockaddr_storage *address)
139 {
140         struct dlist *itr;
141         ban_t *ban;
142
143         list_iterate(itr, &banlist) {
144                 ban = list_get_entry(itr, ban_t, node);
145                 if (ban->address.ss_family == address->ss_family) {
146                         if (address->ss_family == AF_INET) {
147                                 uint32_t a1, a2, mask;
148                                 mask = (ban->mask == 32) ? UINT32_MAX : (1u << ban->mask) - 1;
149                                 a1 = (uint32_t)((struct sockaddr_in *)&ban->address)->sin_addr.s_addr & mask;
150                                 a2 = (uint32_t)((struct sockaddr_in *)address)->sin_addr.s_addr & mask;
151                                 if (a1 == a2)
152                                         return true;
153                         } else {
154                                 uint64_t mask[2];
155                                 uint64_t *a1 = (uint64_t *) &((struct sockaddr_in6 *)&ban->address)->sin6_addr.s6_addr;
156                                 uint64_t *a2 = (uint64_t *) &((struct sockaddr_in6 *)address)->sin6_addr.s6_addr;
157
158                                 if (ban->mask == 128)
159                                         mask[0] = mask[1] = 0xffffffffffffffffULL;
160                                 else if (ban->mask > 64) {
161                                         mask[0] = 0xffffffffffffffffULL;
162                                         mask[1] = SWAPPED(~((1ULL << (128 - ban->mask)) - 1));
163                                 } else {
164                                         mask[0] = SWAPPED(~((1ULL << (64 - ban->mask)) - 1));
165                                         mask[1] = 0ULL;
166                                 }
167                                 if ((a1[0] & mask[0]) == (a2[0] & mask[0]) &&
168                                     (a1[1] & mask[1]) == (a2[1] & mask[1]))
169                                     return true;
170                         }
171                 }
172         }
173
174         return false;
175 }
176
177 int Ban_getBanCount(void)
178 {
179         return bancount;
180 }
181
182 message_t *Ban_getBanList(void)
183 {
184         int i = 0;
185         struct dlist *itr;
186         ban_t *ban;
187         message_t *msg;
188         struct tm timespec;
189         char timestr[32];
190         char hexhash[41];
191         uint8_t address[16];
192
193         msg = Msg_banList_create(bancount);
194         list_iterate(itr, &banlist) {
195                 ban = list_get_entry(itr, ban_t, node);
196                 gmtime_r(&ban->time, &timespec);
197                 strftime(timestr, 32, "%Y-%m-%dT%H:%M:%SZ", &timespec);
198                 SSLi_hash2hex(ban->hash, hexhash);
199                 memset(address, 0, 16);
200
201                 if(ban->address.ss_family == AF_INET) {
202                         memcpy(&address[12], &((struct sockaddr_in *)&ban->address)->sin_addr, 4);
203                         memset(&address[10], 0xff, 2);
204                         Msg_banList_addEntry(msg, i++, address, ban->mask + 96, ban->name, hexhash, ban->reason, timestr, ban->duration);
205                 } else {
206                         memcpy(&address, &((struct sockaddr_in6 *)&ban->address)->sin6_addr, 16);
207                         Msg_banList_addEntry(msg, i++, address, ban->mask, ban->name, hexhash, ban->reason, timestr, ban->duration);
208                 }
209
210         }
211         return msg;
212 }
213
214 void Ban_clearBanList(void)
215 {
216         ban_t *ban;
217         struct dlist *itr, *save;
218         list_iterate_safe(itr, save, &banlist) {
219                 ban = list_get_entry(itr, ban_t, node);
220                 free(ban->name);
221                 free(ban->reason);
222                 list_del(&ban->node);
223                 free(ban);
224                 bancount--;
225         }
226 }
227
228 void Ban_putBanList(message_t *msg, int n_bans)
229 {
230         int i = 0;
231         struct tm timespec;
232         ban_t *ban;
233         char *hexhash, *name, *reason, *start;
234         uint32_t duration, mask;
235         uint8_t *address;
236         char mappedBytes[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff};
237         char *tz;
238
239         for (i = 0; i < n_bans; i++) {
240                 Msg_banList_getEntry(msg, i, &address, &mask, &name, &hexhash, &reason, &start, &duration);
241                 ban = malloc(sizeof(ban_t));
242                 if (ban == NULL)
243                         Log_fatal("Out of memory");
244                 SSLi_hex2hash(hexhash, ban->hash);
245
246                 if(memcmp(address, mappedBytes, 12) == 0) {
247                         memcpy(&((struct sockaddr_in *)&ban->address)->sin_addr, &address[12], 4);
248                         ban->address.ss_family = AF_INET;
249                         if (mask > 32) {
250                                 mask = 32;
251                         }
252                 } else {
253                         memcpy(&((struct sockaddr_in6 *)&ban->address)->sin6_addr, address, 16);
254                         ban->address.ss_family = AF_INET6;
255                 }
256
257                 ban->mask = mask;
258                 ban->reason = strdup(reason);
259                 ban->name = strdup(name);
260
261                 /*
262                  * Parse the timestring. We need to set TZ to UTC so that mktime() knows that the info in
263                  * struct tm indeed is given in UTC. Otherwise it will use the current locale. There's
264                  * apparently no other way to do this...
265                  */
266                 memset(&timespec, 0, sizeof(struct tm));
267                 strptime(start, "%Y-%m-%dT%H:%M:%S", &timespec);
268                 tz = getenv("TZ");
269                 setenv("TZ", "UTC", 1);
270                 tzset();
271                 ban->time = mktime(&timespec);
272                 if (tz)
273                         setenv("TZ", tz, 1);
274                 else
275                         unsetenv("TZ");
276                 tzset();
277
278                 ban->duration = duration;
279                 list_add_tail(&ban->node, &banlist);
280                 bancount++;
281         }
282         banlist_changed = true;
283         if(getBoolConf(SYNC_BANFILE))
284                 Ban_saveBanFile();
285 }
286
287 static void Ban_saveBanFile(void)
288 {
289         struct dlist *itr;
290         ban_t *ban;
291         char hexhash[41];
292         FILE *file;
293
294         if (!banlist_changed)
295                 return;
296         file = fopen(getStrConf(BANFILE), "w");
297         if (file == NULL) {
298                 Log_warn("Could not save banlist to file %s: %s", getStrConf(BANFILE), strerror(errno));
299                 return;
300         }
301         list_iterate(itr, &banlist) {
302                 ban = list_get_entry(itr, ban_t, node);
303                 SSLi_hash2hex(ban->hash, hexhash);
304
305                 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);
306         }
307         fclose(file);
308         banlist_changed = false;
309         Log_info("Banlist file '%s': %d entries written", getStrConf(BANFILE), bancount);
310 }
311
312 static void Ban_readBanFile(void)
313 {
314         ban_t *ban;
315         char line[1024], *hexhash, *address, *name, *reason;
316         uint32_t mask, duration;
317         time_t time;
318         char *p;
319         FILE *file;
320
321         file = fopen(getStrConf(BANFILE), "r");
322         if (file == NULL) {
323                 Log_warn("Could not read banlist file %s: %s", getStrConf(BANFILE), strerror(errno));
324                 return;
325         }
326         while (fgets(line, 1024, file) != NULL) {
327                 p = strtok(line, ",");
328                 hexhash = p;
329                 p = strtok(NULL, ",");
330                 if (p == NULL) break;
331                 address = p;
332                 p = strtok(NULL, ",");
333                 if (p == NULL) break;
334                 mask = strtoul(p, NULL, 0);
335                 p = strtok(NULL, ",");
336                 if (p == NULL) break;
337                 time = strtoul(p, NULL, 0);
338                 p = strtok(NULL, ",");
339                 if (p == NULL) break;
340                 duration = strtoul(p, NULL, 0);
341                 p = strtok(NULL, ",");
342                 if (p == NULL) break;
343                 name = p;
344                 p = strtok(NULL, "\n");
345                 if (p == NULL) break;
346                 reason = p;
347
348                 ban = malloc(sizeof(ban_t));
349                 if (ban == NULL)
350                         Log_fatal("Out of memory");
351                 memset(ban, 0, sizeof(ban_t));
352                 SSLi_hex2hash(hexhash, ban->hash);
353                 if (inet_pton(AF_INET, address, &ban->address) == 0) {
354                         if (inet_pton(AF_INET6, address, &ban->address) == 0) {
355                                 Log_warn("Address \"%s\" is illegal!", address);
356                         } else {
357                                 ban->address.ss_family = AF_INET6;
358                         }
359                 } else {
360                         ban->address.ss_family = AF_INET;
361                 }
362                 ban->name = strdup(name);
363                 ban->reason = strdup(reason);
364                 if (ban->name == NULL || ban->reason == NULL)
365                         Log_fatal("Out of memory");
366                 ban->time = time;
367                 ban->duration = duration;
368                 ban->mask = mask;
369                 list_add_tail(&ban->node, &banlist);
370                 bancount++;
371                 Log_debug("Banfile: H = '%s' A = '%s' M = %d U = '%s' R = '%s'", hexhash, address, ban->mask, ban->name, ban->reason);
372         }
373         fclose(file);
374         Log_info("Banlist file '%s': %d entries read", getStrConf(BANFILE), bancount);
375 }