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