Use Memory_safeCalloc() to allocate zeroed memory.
[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 "memory.h"
37 #include "list.h"
38 #include "ban.h"
39 #include "conf.h"
40 #include "ssl.h"
41 #include "util.h"
42
43 static void Ban_saveBanFile(void);
44 static void Ban_readBanFile(void);
45
46
47 declare_list(banlist);
48 static int bancount; /* = 0 */
49 static int ban_duration;
50 static bool_t banlist_changed;
51
52 void Ban_init(void)
53 {
54         ban_duration = getIntConf(BAN_LENGTH);
55         /* Read ban file here */
56         if (getStrConf(BANFILE) != NULL)
57                 Ban_readBanFile();
58 }
59
60 void Ban_deinit(void)
61 {
62         /* Save banlist */
63         if (getStrConf(BANFILE) != NULL)
64                 Ban_saveBanFile();
65
66         Ban_clearBanList();
67 }
68
69 void Ban_UserBan(client_t *client, char *reason)
70 {
71         ban_t *ban;
72         char hexhash[41];
73
74         ban = Memory_safeCalloc(1, sizeof(ban_t));
75
76         memcpy(ban->hash, client->hash, 20);
77
78         ban->address = client->remote_tcp;
79         ban->mask = (ban->address.ss_family == AF_INET) ? 32 : 128;
80         ban->reason = strdup(reason);
81         ban->name = strdup(client->username);
82         ban->time = time(NULL);
83         ban->duration = ban_duration;
84         list_add_tail(&ban->node, &banlist);
85         bancount++;
86         banlist_changed = true;
87         if(getBoolConf(SYNC_BANFILE))
88                 Ban_saveBanFile();
89
90         SSLi_hash2hex(ban->hash, hexhash);
91
92         char *clientAddressString = Util_clientAddressToString(client);
93
94         Log_info_client(client, "User kickbanned. Reason: '%s' Hash: %s IP: %s Banned for: %d seconds",
95                 ban->reason, hexhash, clientAddressString, ban->duration);
96
97         free(clientAddressString);
98 }
99
100
101 void Ban_pruneBanned()
102 {
103         struct dlist *itr;
104         ban_t *ban;
105
106         list_iterate(itr, &banlist) {
107                 ban = list_get_entry(itr, ban_t, node);
108 #ifdef DEBUG
109                 char hexhash[41];
110                 SSLi_hash2hex(ban->hash, hexhash);
111                 char *addressString = Util_addressToString(&ban->address);
112                 Log_debug("BL: User %s Reason: '%s' Hash: %s IP: %s Time left: %d",
113                         ban->name, ban->reason, hexhash, addressString,
114                         ban->time + ban->duration - time(NULL));
115                 free(addressString);
116 #endif
117                 /* Duration of 0 = forever */
118                 if (ban->duration != 0 && ban->time + ban->duration - time(NULL) <= 0) {
119                         free(ban->name);
120                         free(ban->reason);
121                         list_del(&ban->node);
122                         free(ban);
123                         bancount--;
124                         banlist_changed = true;
125                         if(getBoolConf(SYNC_BANFILE))
126                                 Ban_saveBanFile();
127                 }
128         }
129 }
130
131 bool_t Ban_isBanned(client_t *client)
132 {
133         struct dlist *itr;
134         ban_t *ban;
135         list_iterate(itr, &banlist) {
136                 ban = list_get_entry(itr, ban_t, node);
137                 if (memcmp(ban->hash, client->hash, 20) == 0)
138                         return true;
139         }
140         return false;
141
142 }
143
144 bool_t Ban_isBannedAddr(struct sockaddr_storage *address)
145 {
146         struct dlist *itr;
147         ban_t *ban;
148
149         list_iterate(itr, &banlist) {
150                 ban = list_get_entry(itr, ban_t, node);
151                 if (ban->address.ss_family == address->ss_family) {
152                         if (address->ss_family == AF_INET) {
153                                 uint32_t a1, a2, mask;
154                                 mask = (ban->mask == 32) ? UINT32_MAX : (1u << ban->mask) - 1;
155                                 a1 = (uint32_t)((struct sockaddr_in *)&ban->address)->sin_addr.s_addr & mask;
156                                 a2 = (uint32_t)((struct sockaddr_in *)address)->sin_addr.s_addr & mask;
157                                 if (a1 == a2)
158                                         return true;
159                         } else {
160                                 uint64_t mask[2];
161                                 uint64_t *a1 = (uint64_t *) &((struct sockaddr_in6 *)&ban->address)->sin6_addr.s6_addr;
162                                 uint64_t *a2 = (uint64_t *) &((struct sockaddr_in6 *)address)->sin6_addr.s6_addr;
163
164                                 if (ban->mask == 128)
165                                         mask[0] = mask[1] = 0xffffffffffffffffULL;
166                                 else if (ban->mask > 64) {
167                                         mask[0] = 0xffffffffffffffffULL;
168                                         mask[1] = SWAPPED(~((1ULL << (128 - ban->mask)) - 1));
169                                 } else {
170                                         mask[0] = SWAPPED(~((1ULL << (64 - ban->mask)) - 1));
171                                         mask[1] = 0ULL;
172                                 }
173                                 if ((a1[0] & mask[0]) == (a2[0] & mask[0]) &&
174                                     (a1[1] & mask[1]) == (a2[1] & mask[1]))
175                                     return true;
176                         }
177                 }
178         }
179
180         return false;
181 }
182
183 int Ban_getBanCount(void)
184 {
185         return bancount;
186 }
187
188 message_t *Ban_getBanList(void)
189 {
190         int i = 0;
191         struct dlist *itr;
192         ban_t *ban;
193         message_t *msg;
194         struct tm timespec;
195         char timestr[32];
196         char hexhash[41];
197         uint8_t address[16];
198
199         msg = Msg_banList_create(bancount);
200         list_iterate(itr, &banlist) {
201                 ban = list_get_entry(itr, ban_t, node);
202                 gmtime_r(&ban->time, &timespec);
203                 strftime(timestr, 32, "%Y-%m-%dT%H:%M:%SZ", &timespec);
204                 SSLi_hash2hex(ban->hash, hexhash);
205                 memset(address, 0, 16);
206
207                 if(ban->address.ss_family == AF_INET) {
208                         memcpy(&address[12], &((struct sockaddr_in *)&ban->address)->sin_addr, 4);
209                         memset(&address[10], 0xff, 2);
210                         Msg_banList_addEntry(msg, i++, address, ban->mask + 96, ban->name, hexhash, ban->reason, timestr, ban->duration);
211                 } else {
212                         memcpy(&address, &((struct sockaddr_in6 *)&ban->address)->sin6_addr, 16);
213                         Msg_banList_addEntry(msg, i++, address, ban->mask, ban->name, hexhash, ban->reason, timestr, ban->duration);
214                 }
215
216         }
217         return msg;
218 }
219
220 void Ban_clearBanList(void)
221 {
222         ban_t *ban;
223         struct dlist *itr, *save;
224         list_iterate_safe(itr, save, &banlist) {
225                 ban = list_get_entry(itr, ban_t, node);
226                 free(ban->name);
227                 free(ban->reason);
228                 list_del(&ban->node);
229                 free(ban);
230                 bancount--;
231         }
232 }
233
234 void Ban_putBanList(message_t *msg, int n_bans)
235 {
236         int i = 0;
237         struct tm timespec;
238         ban_t *ban;
239         char *hexhash, *name, *reason, *start;
240         uint32_t duration, mask;
241         uint8_t *address;
242         char mappedBytes[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff};
243         char *tz;
244
245         for (i = 0; i < n_bans; i++) {
246                 Msg_banList_getEntry(msg, i, &address, &mask, &name, &hexhash, &reason, &start, &duration);
247                 ban = Memory_safeMalloc(1, sizeof(ban_t));
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                 char *addressString = Util_addressToString(&ban->address);
310                 fprintf(file, "%s,%s,%d,%ld,%d,%s,%s\n", hexhash, addressString,ban->mask, (long int)ban->time, ban->duration, ban->name, ban->reason);
311                 free(addressString);
312         }
313         fclose(file);
314         banlist_changed = false;
315         Log_info("Banlist file '%s': %d entries written", getStrConf(BANFILE), bancount);
316 }
317
318 static void Ban_readBanFile(void)
319 {
320         ban_t *ban;
321         char line[1024], *hexhash, *address, *name, *reason;
322         uint32_t mask, duration;
323         time_t time;
324         char *p;
325         FILE *file;
326
327         file = fopen(getStrConf(BANFILE), "r");
328         if (file == NULL) {
329                 Log_warn("Could not read banlist file %s: %s", getStrConf(BANFILE), strerror(errno));
330                 return;
331         }
332         while (fgets(line, 1024, file) != NULL) {
333                 p = strtok(line, ",");
334                 hexhash = p;
335                 p = strtok(NULL, ",");
336                 if (p == NULL) break;
337                 address = p;
338                 p = strtok(NULL, ",");
339                 if (p == NULL) break;
340                 mask = strtoul(p, NULL, 0);
341                 p = strtok(NULL, ",");
342                 if (p == NULL) break;
343                 time = strtoul(p, NULL, 0);
344                 p = strtok(NULL, ",");
345                 if (p == NULL) break;
346                 duration = strtoul(p, NULL, 0);
347                 p = strtok(NULL, ",");
348                 if (p == NULL) break;
349                 name = p;
350                 p = strtok(NULL, "\n");
351                 if (p == NULL) break;
352                 reason = p;
353
354                 ban = Memory_safeCalloc(1, sizeof(ban_t));
355                 SSLi_hex2hash(hexhash, ban->hash);
356                 if (inet_pton(AF_INET, address, &ban->address) == 0) {
357                         if (inet_pton(AF_INET6, address, &ban->address) == 0) {
358                                 Log_warn("Address \"%s\" is illegal!", address);
359                         } else {
360                                 ban->address.ss_family = AF_INET6;
361                         }
362                 } else {
363                         ban->address.ss_family = AF_INET;
364                 }
365                 ban->name = strdup(name);
366                 ban->reason = strdup(reason);
367                 if (ban->name == NULL || ban->reason == NULL)
368                         Log_fatal("Out of memory");
369                 ban->time = time;
370                 ban->duration = duration;
371                 ban->mask = mask;
372                 list_add_tail(&ban->node, &banlist);
373                 bancount++;
374                 Log_debug("Banfile: H = '%s' A = '%s' M = %d U = '%s' R = '%s'", hexhash, address, ban->mask, ban->name, ban->reason);
375         }
376         fclose(file);
377         Log_info("Banlist file '%s': %d entries read", getStrConf(BANFILE), bancount);
378 }