Merge pull request #34 from fmorgner/master
[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 "log.h"
35 #include "list.h"
36 #include "ban.h"
37 #include "conf.h"
38 #include "ssl.h"
39
40 static void Ban_saveBanFile(void);
41 static void Ban_readBanFile(void);
42
43
44 declare_list(banlist);
45 static int bancount; /* = 0 */
46 static int ban_duration;
47 static bool_t banlist_changed;
48
49 void Ban_init(void)
50 {
51         ban_duration = getIntConf(BAN_LENGTH);
52         /* Read ban file here */
53         if (getStrConf(BANFILE) != NULL)
54                 Ban_readBanFile();
55 }
56
57 void Ban_deinit(void)
58 {
59         /* Save banlist */
60         if (getStrConf(BANFILE) != NULL)
61                 Ban_saveBanFile();
62
63         Ban_clearBanList();
64 }
65
66 void Ban_UserBan(client_t *client, char *reason)
67 {
68         ban_t *ban;
69         char hexhash[41];
70
71         ban = malloc(sizeof(ban_t));
72         if (ban == NULL)
73                 Log_fatal("Out of memory");
74         memset(ban, 0, sizeof(ban_t));
75
76         memcpy(ban->hash, client->hash, 20);
77         memcpy(&ban->address, &client->remote_tcp.sin_addr, sizeof(in_addr_t));
78         ban->mask = 128;
79         ban->reason = strdup(reason);
80         ban->name = strdup(client->username);
81         ban->time = time(NULL);
82         ban->duration = ban_duration;
83         Timer_init(&ban->startTime);
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         Log_info_client(client, "User kickbanned. Reason: '%s' Hash: %s IP: %s Banned for: %d seconds",
92                         ban->reason, hexhash, inet_ntoa(*((struct in_addr *)&ban->address)), ban->duration);
93 }
94
95
96 void Ban_pruneBanned()
97 {
98         struct dlist *itr;
99         ban_t *ban;
100         char hexhash[41];
101         uint64_t bantime_long;
102
103         list_iterate(itr, &banlist) {
104                 ban = list_get_entry(itr, ban_t, node);
105                 bantime_long = ban->duration * 1000000LL;
106 #ifdef DEBUG
107                 SSLi_hash2hex(ban->hash, hexhash);
108                 Log_debug("BL: User %s Reason: '%s' Hash: %s IP: %s Time left: %d",
109                           ban->name, ban->reason, hexhash, inet_ntoa(*((struct in_addr *)&ban->address)),
110                           bantime_long / 1000000LL - Timer_elapsed(&ban->startTime) / 1000000LL);
111 #endif
112                 /* Duration of 0 = forever */
113                 if (ban->duration != 0 && Timer_isElapsed(&ban->startTime, bantime_long)) {
114                         free(ban->name);
115                         free(ban->reason);
116                         list_del(&ban->node);
117                         free(ban);
118                         bancount--;
119                         banlist_changed = true;
120                         if(getBoolConf(SYNC_BANFILE))
121                                 Ban_saveBanFile();
122                 }
123         }
124 }
125
126 bool_t Ban_isBanned(client_t *client)
127 {
128         struct dlist *itr;
129         ban_t *ban;
130         list_iterate(itr, &banlist) {
131                 ban = list_get_entry(itr, ban_t, node);
132                 if (memcmp(ban->hash, client->hash, 20) == 0)
133                         return true;
134         }
135         return false;
136
137 }
138
139 bool_t Ban_isBannedAddr(in_addr_t *addr)
140 {
141         struct dlist *itr;
142         ban_t *ban;
143         int mask;
144         in_addr_t tempaddr1, tempaddr2;
145
146         list_iterate(itr, &banlist) {
147                 ban = list_get_entry(itr, ban_t, node);
148                 mask = ban->mask - 96;
149                 if (mask < 32) { /* XXX - only ipv4 support */
150                         memcpy(&tempaddr1, addr, sizeof(in_addr_t));
151                         memcpy(&tempaddr2, &ban->address, sizeof(in_addr_t));
152                         tempaddr1 &= (2 ^ mask) - 1;
153                         tempaddr2 &= (2 ^ mask) - 1;
154                 }
155                 if (memcmp(&tempaddr1, &tempaddr2, sizeof(in_addr_t)) == 0)
156                         return true;
157         }
158         return false;
159 }
160
161 int Ban_getBanCount(void)
162 {
163         return bancount;
164 }
165
166 message_t *Ban_getBanList(void)
167 {
168         int i = 0;
169         struct dlist *itr;
170         ban_t *ban;
171         message_t *msg;
172         struct tm timespec;
173         char timestr[32];
174         char hexhash[41];
175         uint8_t address[16];
176
177         msg = Msg_banList_create(bancount);
178         list_iterate(itr, &banlist) {
179                 ban = list_get_entry(itr, ban_t, node);
180                 gmtime_r(&ban->time, &timespec);
181                 strftime(timestr, 32, "%Y-%m-%dT%H:%M:%S", &timespec);
182                 SSLi_hash2hex(ban->hash, hexhash);
183                 /* ipv4 representation as ipv6 address. */
184                 memset(address, 0, 16);
185                 memcpy(&address[12], &ban->address, 4);
186                 memset(&address[10], 0xff, 2); /* IPv4 */
187                 Msg_banList_addEntry(msg, i++, address, ban->mask, ban->name,
188                                      hexhash, ban->reason, timestr, ban->duration);
189         }
190         return msg;
191 }
192
193 void Ban_clearBanList(void)
194 {
195         ban_t *ban;
196         struct dlist *itr, *save;
197         list_iterate_safe(itr, save, &banlist) {
198                 ban = list_get_entry(itr, ban_t, node);
199                 free(ban->name);
200                 free(ban->reason);
201                 list_del(&ban->node);
202                 free(ban);
203                 bancount--;
204         }
205 }
206
207 void Ban_putBanList(message_t *msg, int n_bans)
208 {
209         int i = 0;
210         struct tm timespec;
211         ban_t *ban;
212         char *hexhash, *name, *reason, *start;
213         uint32_t duration, mask;
214         uint8_t *address;
215
216         for (i = 0; i < n_bans; i++) {
217                 Msg_banList_getEntry(msg, i, &address, &mask, &name, &hexhash, &reason, &start, &duration);
218                 ban = malloc(sizeof(ban_t));
219                 if (ban == NULL)
220                         Log_fatal("Out of memory");
221                 memset(ban, 0, sizeof(ban_t));
222                 SSLi_hex2hash(hexhash, ban->hash);
223                 memcpy(&ban->address, &address[12], 4);
224                 ban->mask = mask;
225                 ban->reason = strdup(reason);
226                 ban->name = strdup(name);
227                 strptime(start, "%Y-%m-%dT%H:%M:%S", &timespec);
228                 ban->time = mktime(&timespec);
229                 ban->startTime = ban->time * 1000000LL;
230                 ban->duration = duration;
231                 list_add_tail(&ban->node, &banlist);
232                 bancount++;
233         }
234         banlist_changed = true;
235         if(getBoolConf(SYNC_BANFILE))
236                 Ban_saveBanFile();
237 }
238
239 static void Ban_saveBanFile(void)
240 {
241         struct dlist *itr;
242         ban_t *ban;
243         char hexhash[41];
244         FILE *file;
245
246         if (!banlist_changed)
247                 return;
248         file = fopen(getStrConf(BANFILE), "w");
249         if (file == NULL) {
250                 Log_warn("Could not save banlist to file %s: %s", getStrConf(BANFILE), strerror(errno));
251                 return;
252         }
253         list_iterate(itr, &banlist) {
254                 ban = list_get_entry(itr, ban_t, node);
255                 SSLi_hash2hex(ban->hash, hexhash);
256                 fprintf(file, "%s,%s,%d,%ld,%d,%s,%s\n", hexhash, inet_ntoa(*((struct in_addr *)&ban->address)),
257                         ban->mask, (long int)ban->time, ban->duration, ban->name, ban->reason);
258         }
259         fclose(file);
260         banlist_changed = false;
261         Log_info("Banlist file '%s': %d entries written", getStrConf(BANFILE), bancount);
262 }
263
264 static void Ban_readBanFile(void)
265 {
266         struct dlist *itr;
267         ban_t *ban;
268         char line[1024], *hexhash, *address, *name, *reason;
269         uint32_t mask, duration;
270         time_t time;
271         char *p;
272         FILE *file;
273
274         file = fopen(getStrConf(BANFILE), "r");
275         if (file == NULL) {
276                 Log_warn("Could not read banlist file %s: %s", getStrConf(BANFILE), strerror(errno));
277                 return;
278         }
279         while (fgets(line, 1024, file) != NULL) {
280                 p = strtok(line, ",");
281                 hexhash = p;
282                 p = strtok(NULL, ",");
283                 if (p == NULL) break;
284                 address = p;
285                 p = strtok(NULL, ",");
286                 if (p == NULL) break;
287                 mask = strtoul(p, NULL, 0);
288                 p = strtok(NULL, ",");
289                 if (p == NULL) break;
290                 time = strtoul(p, NULL, 0);
291                 p = strtok(NULL, ",");
292                 if (p == NULL) break;
293                 duration = strtoul(p, NULL, 0);
294                 p = strtok(NULL, ",");
295                 if (p == NULL) break;
296                 name = p;
297                 p = strtok(NULL, "\n");
298                 if (p == NULL) break;
299                 reason = p;
300
301                 ban = malloc(sizeof(ban_t));
302                 if (ban == NULL)
303                         Log_fatal("Out of memory");
304                 memset(ban, 0, sizeof(ban_t));
305                 SSLi_hex2hash(hexhash, ban->hash);
306                 inet_aton(address, (struct in_addr *)&ban->address);
307                 ban->name = strdup(name);
308                 ban->reason = strdup(reason);
309                 if (ban->name == NULL || ban->reason == NULL)
310                         Log_fatal("Out of memory");
311                 ban->time = time;
312                 ban->duration = duration;
313                 ban->mask = mask;
314                 ban->startTime = ban->time * 1000000LL;
315                 list_add_tail(&ban->node, &banlist);
316                 bancount++;
317                 Log_debug("Banfile: H = '%s' A = '%s' M = %d U = '%s' R = '%s'", hexhash, address, ban->mask, ban->name, ban->reason);
318         }
319         fclose(file);
320         Log_info("Banlist file '%s': %d entries read", getStrConf(BANFILE), bancount);
321 }