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