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