1 /*
2  * libmavis_limit.c
3  *
4  * (C)1998-2011 by Marc Huber <Marc.Huber@web.de>
5  * All rights reserved.
6  *
7  * $Id: libmavis_limit.c,v 1.25 2015/11/01 09:20:27 marc Exp $
8  *
9  */
10 
11 #define __MAVIS_limit
12 #include <sys/types.h>
13 #include <netinet/in.h>
14 #include <arpa/inet.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <time.h>
18 #include <stdio.h>
19 #include <sys/types.h>
20 #include <dlfcn.h>
21 
22 #include "misc/sysconf.h"
23 
24 #include "log.h"
25 #include "debug.h"
26 #include "misc/memops.h"
27 #include "misc/net.h"
28 #include "misc/rb.h"
29 
30 static const char rcsid[] __attribute__ ((used)) = "$Id: libmavis_limit.c,v 1.25 2015/11/01 09:20:27 marc Exp $";
31 
32 #define MAVIS_CTX_PRIVATE		\
33 	int initialized;		\
34 	time_t lastpurge;		\
35 	time_t ip_timeout;		\
36 	u_int ip_blacklist_count;	\
37 	time_t ip_blacklist_time;	\
38 	time_t purge_outdated;		\
39 	struct cache *cache_blacklist;
40 
41 #include "mavis.h"
42 
43 struct item {
44     time_t expire;
45     u_int count;
46     struct in6_addr addr;
47     char user[1];
48 };
49 
50 struct cache {
51     time_t maxage;
52     rb_tree_t *items;
53 };
54 
free_payload(void * payload)55 static void free_payload(void *payload)
56 {
57     free(payload);
58 }
59 
cache_new(int (* compar)(const void *,const void *),void (* freenode)(void *),time_t maxage)60 static struct cache *cache_new(int (*compar) (const void *, const void *), void (*freenode) (void *), time_t maxage)
61 {
62     struct cache *cache;
63 
64     cache = Xcalloc(1, sizeof(struct cache));
65     cache->items = RB_tree_new(compar, freenode);
66     cache->maxage = maxage;
67     return cache;
68 }
69 
cache_add_addr(struct cache * cache,char * user,char * addr)70 static void cache_add_addr(struct cache *cache, char *user, char *addr)
71 {
72     struct item item, *newitem = NULL;
73     rb_node_t *t;
74 
75     if (!user)
76 	return;
77 
78     if (v6_ptoh(&item.addr, NULL, addr))
79 	return;
80 
81     t = RB_search(cache->items, &item);
82 
83     if (!t || strcmp(RB_payload(t, struct item *)->user, user)) {
84 	if (t)
85 	    RB_delete(cache->items, t);
86 	newitem = Xcalloc(1, sizeof(struct item) + strlen(user));
87 	strcpy(newitem->user, user);
88 	newitem->count = 1;
89 	newitem->addr = item.addr;
90 	newitem->expire = io_now.tv_sec + cache->maxage;
91 	RB_insert(cache->items, newitem);
92     } else {
93 	struct item *ti = RB_payload(t, struct item *);
94 	if (io_now.tv_sec > ti->expire)
95 	    ti->count = 0;
96 	ti->expire = io_now.tv_sec + cache->maxage;
97 	ti->count++;
98     }
99 }
100 
cache_find_addr(struct cache * cache,char * addr)101 static struct item *cache_find_addr(struct cache *cache, char *addr)
102 {
103     struct item item;
104     rb_node_t *t = NULL;
105 
106     DebugIn(DEBUG_PROC);
107 
108     if (!v6_ptoh(&item.addr, NULL, addr))
109 	t = RB_search(cache->items, &item);
110     DebugOut(DEBUG_PROC);
111     return t ? RB_payload(t, struct item *) : NULL;
112 }
113 
garbage_collection_one(struct cache * cache)114 static void garbage_collection_one(struct cache *cache)
115 {
116     rb_node_t *t, *u;
117 
118     if (cache && cache->items)
119 	for (t = RB_first(cache->items); t; t = u)
120 	    if (u = RB_next(t), RB_payload(t, struct item *)->expire < io_now.tv_sec)
121 		 RB_delete(cache->items, t);
122 }
123 
garbage_collection(mavis_ctx * mcx)124 static void garbage_collection(mavis_ctx * mcx)
125 {
126     DebugIn(DEBUG_PROC);
127 
128     garbage_collection_one(mcx->cache_blacklist);
129 
130     DebugOut(DEBUG_PROC);
131 }
132 
133 #define HAVE_mavis_drop_in
mavis_drop_in(mavis_ctx * mcx)134 static void mavis_drop_in(mavis_ctx * mcx)
135 {
136     RB_tree_delete(mcx->cache_blacklist->items);
137     Xfree(&mcx->cache_blacklist);
138 }
139 
140 /*
141 purge period =...blacklist time =...blacklist count =...
142 */
143 #define HAVE_mavis_parse_in
mavis_parse_in(mavis_ctx * mcx,struct sym * sym)144 static int mavis_parse_in(mavis_ctx * mcx, struct sym *sym)
145 {
146     while (1) {
147 	switch (sym->code) {
148 	case S_script:
149 	    mavis_script_parse(mcx, sym);
150 	    continue;
151 	case S_purge:
152 	    sym_get(sym);
153 	    parse(sym, S_period);
154 	    parse(sym, S_equal);
155 	    mcx->purge_outdated = (time_t) parse_int(sym);
156 	    continue;
157 	case S_blacklist:
158 	    sym_get(sym);
159 	    switch (sym->code) {
160 	    case S_time:
161 		sym_get(sym);
162 		parse(sym, S_equal);
163 		mcx->ip_blacklist_time = (time_t) parse_int(sym);;
164 		break;
165 	    case S_count:
166 		sym_get(sym);
167 		parse(sym, S_equal);
168 		mcx->ip_blacklist_count = (u_int) parse_int(sym);;
169 		break;
170 	    default:
171 		parse_error_expect(sym, S_time, S_count, S_unknown);
172 	    }
173 	    continue;
174 	case S_eof:
175 	case S_closebra:
176 	    return MAVIS_CONF_OK;
177 	default:
178 	    parse_error_expect(sym, S_script, S_purge, S_expire, S_closebra, S_unknown);
179 	}
180     }
181 }
182 
183 
compare_addr(const void * a,const void * b)184 static int compare_addr(const void *a, const void *b)
185 {
186     return v6_cmp(&((struct item *) a)->addr, &((struct item *) b)->addr);
187 }
188 
189 #define HAVE_mavis_init_in
mavis_init_in(mavis_ctx * mcx)190 static int mavis_init_in(mavis_ctx * mcx)
191 {
192     if (!mcx->initialized) {
193 	mcx->initialized++;
194 	mcx->cache_blacklist = cache_new(compare_addr, free_payload, mcx->ip_blacklist_time);
195 	mcx->lastpurge = io_now.tv_sec;
196     }
197     return MAVIS_INIT_OK;
198 }
199 
200 #define HAVE_mavis_send_in
mavis_send_in(mavis_ctx * mcx,av_ctx ** ac)201 static int mavis_send_in(mavis_ctx * mcx, av_ctx ** ac)
202 {
203     char *t, *addr;
204     t = av_get(*ac, AV_A_TYPE);
205     if (!t)
206 	return MAVIS_FINAL;
207     if (io_now.tv_sec > mcx->lastpurge + mcx->purge_outdated) {
208 	garbage_collection(mcx);
209 	mcx->lastpurge = io_now.tv_sec;
210     }
211 
212     addr = av_get(*ac, AV_A_IPADDR);
213     if (addr) {
214 	struct item *item;
215 	item = cache_find_addr(mcx->cache_blacklist, addr);
216 	if (mcx->ip_blacklist_count && item && item->count >= mcx->ip_blacklist_count && item->expire > io_now.tv_sec) {
217 	    av_set(*ac, AV_A_RESULT, AV_V_RESULT_FAIL);
218 	    av_setf(*ac, AV_A_COMMENT, "client ip blacklisted for %ld seconds", (long) item->expire - io_now.tv_sec);
219 	    return MAVIS_FINAL;
220 	}
221 
222     }
223 
224     return MAVIS_DOWN;
225 }
226 
227 #define HAVE_mavis_recv_out
mavis_recv_out(mavis_ctx * mcx,av_ctx ** ac)228 static int mavis_recv_out(mavis_ctx * mcx, av_ctx ** ac)
229 {
230     char *t = av_get(*ac, AV_A_TYPE);
231     char *u = av_get(*ac, AV_A_USER);
232     char *i = av_get(*ac, AV_A_IPADDR);
233     char *r = av_get(*ac, AV_A_RESULT);
234     if (!r)
235 	r = AV_V_RESULT_FAIL;
236     if (t && i && u) {
237 	if (!strcmp(r, AV_V_RESULT_FAIL)
238 	    && (!strcmp(t, AV_V_TYPE_TACPLUS)
239 		|| !strcmp(t, AV_V_TYPE_FTP)
240 		|| !strcmp(t, AV_V_TYPE_WWW)
241 		|| !strcmp(t, AV_V_TYPE_POP3)))
242 	    cache_add_addr(mcx->cache_blacklist, "", i);
243     }
244 
245     return MAVIS_FINAL;
246 }
247 
248 #define HAVE_mavis_new
mavis_new(mavis_ctx * mcx)249 static void mavis_new(mavis_ctx * mcx)
250 {
251     mcx->ip_blacklist_time = 300;
252     mcx->purge_outdated = 300;
253 }
254 
255 #define MAVIS_name "limit"
256 #include "mavis_glue.c"
257