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