1 | /***************************************
2 | $Revision: 1.5 $
3 |
4 | Access control module (ac).
5 |
6 | Status: NOT REVUED, NOT TESTED
7 |
8 | ******************/ /******************
9 | Filename : access_control.c
10 | Author : ottrey@ripe.net
11 | OSs Tested : Solaris
12 | ******************/ /******************
13 | Copyright (c) 1999 RIPE NCC
14 |
15 | All Rights Reserved
16 |
17 | Permission to use, copy, modify, and distribute this software and its
18 | documentation for any purpose and without fee is hereby granted,
19 | provided that the above copyright notice appear in all copies and that
20 | both that copyright notice and this permission notice appear in
21 | supporting documentation, and that the name of the author not be
22 | used in advertising or publicity pertaining to distribution of the
23 | software without specific, written prior permission.
24 |
25 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
26 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
27 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
28 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
29 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
30 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
31 | ***************************************/
32 | #include <stdio.h>
33 | #include <glib.h>
34 |
35 | #define AC_IMPL
36 | #include "rxroutines.h"
37 | #include "erroutines.h"
38 | #include "access_control.h"
39 | #include "socket.h"
40 | #include "mysql_driver.h"
41 | #include "constants.h"
42 |
43 | #define AC_DECAY_TIME 10
44 | /* #define AC_DECAY_TIME 3600 */
45 |
46 | /* AC_to_string() */
47 | /*++++++++++++++++++++++++++++++++++++++
48 | Show an access structure
49 |
50 | More:
51 | +html+ <PRE>
52 | Authors:
53 | marek
54 | +html+ </PRE><DL COMPACT>
55 | +html+ <DT>Online References:
56 | +html+ </UL></DL>
57 |
58 | ++++++++++++++++++++++++++++++++++++++*/
59 | char *AC_to_string(GList *leafptr)
60 | {
61 | char *result_buf;
62 | acc_st *a = leafptr->data;
63 |
64 | if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
65 | /* do many bad things...*/
66 | return NULL;
67 | }
68 |
69 | if( a != NULL ) {
70 | sprintf(result_buf,
71 | "conn %d\tden %d\tqrs %d\tpub %d\tpriv %d\tbonus %d",
72 | a->connections,
73 | a->denials,
74 | a->queries,
75 | a->public_objects,
76 | a->private_objects,
77 | a->private_bonus
78 | );
79 | }
80 | else {
81 | strcpy(result_buf, "DATA MISSING\n");
82 | }
83 |
84 | return result_buf;
85 | } /* AC_to_string() */
86 |
87 | /* AC_acl_to_string() */
88 | /*++++++++++++++++++++++++++++++++++++++
89 | Show an access control list structure
90 |
91 | More:
92 | +html+ <PRE>
93 | Authors:
94 | marek
95 | +html+ </PRE><DL COMPACT>
96 | +html+ <DT>Online References:
97 | +html+ </UL></DL>
98 |
99 | ++++++++++++++++++++++++++++++++++++++*/
100 | char *AC_acl_to_string(GList *leafptr)
101 | {
102 | char *result_buf;
103 | acl_st *a = leafptr->data;
104 |
105 | if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
106 | /* do many bad things...*/
107 | return NULL;
108 | }
109 |
110 | if( a != NULL ) {
111 | sprintf(result_buf,
112 | "maxbonus %d\tmaxdenials %d\tdeny %d\ttrustpass %d",
113 | a->maxbonus,
114 | a->maxdenials,
115 | a->deny,
116 | a->trustpass
117 | );
118 | }
119 | else {
120 | strcpy(result_buf, "DATA MISSING\n");
121 | }
122 |
123 | return result_buf;
124 | } /* AC_acl_to_string() */
125 |
126 | /* AC_fetch_acc() */
127 | /*++++++++++++++++++++++++++++++++++++++
128 | Find the runtime accounting record for this IP,
129 | store a copy of it in acc_store.
130 |
131 | More:
132 | +html+ <PRE>
133 | Authors:
134 | marek
135 | +html+ </PRE><DL COMPACT>
136 | +html+ <DT>Online References:
137 | +html+ </UL></DL>
138 |
139 | ++++++++++++++++++++++++++++++++++++++*/
140 | er_ret_t AC_fetch_acc( ip_addr_t *addr, acc_st *acc_store, int tmout)
141 | {
142 | GList *datlist=NULL;
143 | rx_datref_t *datref;
144 | er_ret_t ret_err;
145 | ip_prefix_t prefix;
146 |
147 | prefix.ip = *addr;
148 | prefix.bits = IP_sizebits(addr->space);
149 | TH_acquire_read_lock( &(act_runtime->rwlock) );
150 |
151 | if( (ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, act_runtime,
152 | &prefix, &datlist, RX_ANS_ALL)) == RX_OK ) {
153 | switch( g_list_length(datlist) ) {
154 | case 0:
155 | memset(acc_store, 0, sizeof(acc_st));
156 | break;
157 | case 1:
158 | datref = (rx_datref_t *) g_list_nth_data(datlist,0);
159 | memcpy(acc_store, (acc_st *) datref->leafptr, sizeof(acc_st));
160 | break;
161 | default: die;
162 | }
163 | }
164 |
165 | TH_release_read_lock( &(act_runtime->rwlock) );
166 |
167 | return -1;
168 | }/* AC_fetch_acc() */
169 |
170 | /* AC_check_acl() */
171 | /*++++++++++++++++++++++++++++++++++++++
172 |
173 | AC_check_acl:
174 |
175 | search for this ip or other applicable record in the access control tree
176 |
177 | if( bonus in combined runtime+connection accountings > max_bonus in acl)
178 | set denial in the acl for this ip (create if needed)
179 | if( combined denialcounter > max_denials in acl)
180 | set the permanent ban in acl; save in SQL too
181 | calculate credit if pointer provided
182 | save the access record (ip if created or found/prefix otherwise)
183 | at *acl_store if provided
184 |
185 | any of the args except address can be NULL
186 |
187 | More:
188 | +html+ <PRE>
189 | Authors:
190 | marek
191 | +html+ </PRE><DL COMPACT>
192 | +html+ <DT>Online References:
193 | +html+ </UL></DL>
194 |
195 | ++++++++++++++++++++++++++++++++++++++*/
196 | er_ret_t AC_check_acl( ip_addr_t *addr,
197 | acc_st *run_acc,
198 | acc_st *query_acc,
199 | acc_st *credit_acc,
200 | acl_st *acl_store
201 | )
202 | {
203 | GList *datlist=NULL;
204 | ip_prefix_t prefix;
205 | er_ret_t ret_err;
206 | acl_st *acl_record;
207 | rx_datref_t *datref;
208 | /* will write to the tree only if run_acc or query_acc are provided */
209 | int writetoacl = (run_acc != NULL || query_acc != NULL);
210 |
211 | prefix.ip = *addr;
212 | prefix.bits = IP_sizebits(addr->space);
213 |
214 | /* lock the tree accordingly */
215 | if(writetoacl) {
216 | TH_acquire_write_lock( &(act_acl->rwlock) );
217 | } else {
218 | TH_acquire_read_lock( &(act_acl->rwlock) );
219 | }
220 |
221 | /* find a record */
222 | if( (ret_err = RX_bin_search(RX_SRCH_EXLESS, 0, 0, act_acl,
223 | &prefix, &datlist, RX_ANS_ALL)
224 | ) != RX_OK || g_list_length(datlist) == 0 ) {
225 | /* acl tree is not configured at all ! There always must be a
226 | catch-all record with defaults */
227 | die;
228 | }
229 |
230 |
231 | datref = (rx_datref_t *)g_list_nth_data(datlist,0);
232 | acl_record = (acl_st *) datref->leafptr;
233 |
234 | if( run_acc && credit_acc ) {
235 | memset( credit_acc, 0, sizeof(acc_st));
236 | credit_acc->public_objects = -1; /* unlimited */
237 | credit_acc->private_objects
238 | = acl_record->maxbonus - run_acc->private_bonus;
239 | }
240 |
241 | /* copy the acl record if asked for it*/
242 | if( acl_store ) {
243 | *acl_store = *acl_record;
244 | }
245 |
246 | /* release lock */
247 | if(writetoacl) {
248 | TH_release_write_lock( &(act_acl->rwlock) );
249 | } else {
250 | TH_release_read_lock( &(act_acl->rwlock) );
251 | }
252 |
253 | /*
254 | if( ret_err == RX_OK ) {
255 | ret_err = AC_OK;
256 | }
257 | */
258 | return ret_err;
259 | }
260 |
261 | void AC_acc_addup(acc_st *a, acc_st *b, int minus)
262 | {
263 | int mul = minus ? -1 : 1;
264 |
265 | /* add all counters from b to those in a */
266 | a->connections += mul * b->connections;
267 | a->denials += mul * b->denials;
268 | a->queries += mul * b->queries;
269 | a->public_objects += mul * b->public_objects;
270 | a->private_objects += mul * b->private_objects;
271 | a->private_bonus += mul * b->private_bonus;
272 | }
273 |
274 |
275 | er_ret_t AC_commit(ip_addr_t *addr, acc_st *acc_conn) {
276 | /* for all accounting trees: XXX runtime only for the moment
277 | lock tree (no mercy :-)
278 | find or create entries,
279 | increase accounting values by the values from connection acc
280 | reset the connection acc
281 | unlock accounting trees
282 | */
283 | GList *datlist=NULL;
284 | acc_st *recacc;
285 | er_ret_t ret_err;
286 | ip_prefix_t prefix;
287 |
288 | prefix.ip = *addr;
289 | prefix.bits = IP_sizebits(addr->space);
290 |
291 | TH_acquire_write_lock( &(act_runtime->rwlock) );
292 |
293 | if( (ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, act_runtime,
294 | &prefix, &datlist, RX_ANS_ALL)) == RX_OK ) {
295 | switch( g_list_length(datlist) ) {
296 | case 0:
297 | /* need to create a new accounting record */
298 | if( (ret_err = wr_malloc( (void **)& recacc, sizeof(acc_st))) == UT_OK ) {
299 | /* counters = connection counters */
300 | memcpy( recacc, acc_conn, sizeof(acc_st));
301 |
302 | /* attach. The recacc is to be treated as a dataleaf
303 | (should work on lower levels than RX_asc_*)
304 | */
305 | ret_err = RX_bin_node( RX_OPER_CRE, &prefix,
306 | act_runtime, (rx_dataleaf_t *)recacc );
307 | }
308 | break;
309 | case 1:
310 | {
311 | rx_datref_t *datref = (rx_datref_t *) g_list_nth_data( datlist,0 );
312 |
313 | /* OK, there is a record already, add to it */
314 | recacc = (acc_st *) datref->leafptr;
315 | AC_acc_addup(recacc, acc_conn, ACC_PLUS);
316 | }
317 | break;
318 | default: die; /* there shouldnt be more than 1 entry per IP */
319 | }
320 | }
321 |
322 | TH_release_write_lock( &(act_runtime->rwlock) );
323 | return ret_err;
324 | }
325 |
326 | er_ret_t AC_decay_hook(rx_node_t *node, int level, int nodecounter, void *con) {
327 | acc_st *a = node->leaves_ptr->data;
328 |
329 | a->private_bonus *= 0.95;
330 |
331 | return RX_OK;
332 | } /* AC_decay_hook() */
333 |
334 | er_ret_t AC_decay(void) {
335 | er_ret_t ret_err;
336 |
337 | /* XXX
338 | This should be run as a detatched thread.
339 | Yes the while(1) is crappy b/c there's no way of stopping it,
340 | but it's Friday night & everyone has either gone off for
341 | Christmas break or is down at the pub so it's staying as a while(1)!
342 | And I'm not sure what effect the sleep() will have on the thread.
343 | */
344 | while(1) {
345 |
346 | TH_acquire_write_lock( &(act_runtime->rwlock) );
347 |
348 | if( act_runtime->top_ptr != NULL ) {
349 | rx_walk_tree(act_runtime->top_ptr, AC_decay_hook,
350 | RX_WALK_SKPGLU, /* skip glue nodes */
351 | 255, 0, 0, NULL, &ret_err);
352 | }
353 |
354 | /* it should also be as smart as to delete nodes that have reached
355 | zero, otherwise the whole of memory will be filled.
356 | Next release :-)
357 | */
358 |
359 | TH_release_write_lock( &(act_runtime->rwlock) );
360 |
361 | printf("AC: decaying access tree. (Every %d seconds)\n", AC_DECAY_TIME);
362 |
363 | sleep(AC_DECAY_TIME);
364 | }
365 |
366 | return ret_err;
367 | } /* AC_decay() */
368 |
369 | er_ret_t AC_acc_load(void)
370 | {
371 | SQ_connection_t *con=NULL;
372 | SQ_result_set_t *result;
373 | SQ_row_t *row;
374 | er_ret_t ret_err = RX_OK;
375 |
376 | if( (con = SQ_get_connection(CO_get_host(), CO_get_database_port(),
377 | "RIPADMIN", CO_get_user(), CO_get_password() )
378 | ) == NULL ) {
379 | fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
380 | die;
381 | }
382 |
383 | if( (result = SQ_execute_query(SQ_STORE, con, "SELECT * FROM acl"))
384 | == NULL ) {
385 | fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
386 | die;
387 | }
388 |
389 | TH_acquire_write_lock( &(act_acl->rwlock) );
390 |
391 | while ( (row = SQ_row_next(result)) != NULL && ret_err == RX_OK) {
392 | ip_prefix_t mypref;
393 | acl_st *newacl;
394 | char *col[6];
395 | unsigned myint;
396 | int i;
397 |
398 | memset(&mypref, 0, sizeof(ip_prefix_t));
399 | mypref.ip.space = IP_V4;
400 |
401 | if( (ret_err = wr_malloc( (void **)& newacl, sizeof(acl_st))
402 | ) == UT_OK ) {
403 |
404 | for(i=0; i<6; i++) {
405 | if ( (col[i] = SQ_get_column_string(result, row, i)) == NULL) {
406 | die;
407 | }
408 | }
409 |
410 | /* prefix ip */
411 | if( sscanf(col[0], "%u", &mypref.ip.words[0] ) < 1 ) { die; }
412 |
413 | /* prefix length */
414 | if( sscanf(col[1], "%u", &mypref.bits ) < 1 ) { die; }
415 |
416 | /* acl contents */
417 | if( sscanf(col[2], "%u", & (newacl->maxbonus) ) < 1 ) { die; }
418 | if( sscanf(col[3], "%hd", & (newacl->maxdenials) ) < 1 ) { die; }
419 |
420 | /* these are chars therefore cannot read directly */
421 | if( sscanf(col[4], "%u", &myint ) < 1 ) { die; }
422 | else {
423 | newacl->deny = myint;
424 | }
425 | if( sscanf(col[5], "%u", &myint ) < 1 ) { die; }
426 | else {
427 | newacl->trustpass = myint;
428 | }
429 |
430 | /* now add to the tree */
431 |
432 | ret_err = RX_bin_node( RX_OPER_CRE, &mypref,
433 | act_acl, (rx_dataleaf_t *) newacl );
434 | }
435 | } /* while row */
436 |
437 | TH_release_write_lock( &(act_acl->rwlock) );
438 |
439 | SQ_free_result(result);
440 | /* Close connection */
441 | SQ_close_connection(con);
442 |
443 | /* Start the decay thread. */
444 | TH_run2((void *)AC_decay);
445 |
446 | return ret_err;
447 | }
448 |
449 | er_ret_t AC_build(void)
450 | {
451 | /* create trees */
452 | if ( RX_tree_cre(0, IP_V4, RX_FAM_IP, "0.0.0.0/0", RX_MEM_RAMONLY,
453 | RX_SUB_NONE, &act_runtime) != RX_OK
454 | || RX_tree_cre(0, IP_V4, RX_FAM_IP, "0.0.0.0/0", RX_MEM_RAMONLY,
455 | RX_SUB_NONE, &act_hour) != RX_OK
456 | || RX_tree_cre(0, IP_V4, RX_FAM_IP, "0.0.0.0/0", RX_MEM_RAMONLY,
457 | RX_SUB_NONE, &act_minute) != RX_OK
458 | || RX_tree_cre(0, IP_V4, RX_FAM_IP, "0.0.0.0/0", RX_MEM_RAMONLY,
459 | RX_SUB_NONE, &act_acl) != RX_OK
460 | )
461 | die;
462 | }
463 |
464 | er_ret_t AC_rxwalkhook_print(rx_node_t *node,
465 | int level, int nodecounter,
466 | void *con)
467 | {
468 | char adstr[IP_ADDRSTR_MAX];
469 | char line[1024];
470 | char *dat;
471 |
472 |
473 | if( IP_addr_b2a(&(node->prefix.ip), adstr, IP_ADDRSTR_MAX) != IP_OK ) {
474 | die; /* program error. */
475 | }
476 |
477 | sprintf(line, "%-20s %s\n", adstr,
478 | dat=AC_to_string( node->leaves_ptr ));
479 | wr_free(dat);
480 |
481 | SK_cd_puts((sk_conn_st *)con, line);
482 | return RX_OK;
483 | }
484 |
485 | er_ret_t AC_rxwalkhook_print_acl(rx_node_t *node,
486 | int level, int nodecounter,
487 | void *con)
488 | {
489 | char prefstr[IP_PREFSTR_MAX];
490 | char line[1024];
491 | char *dat;
492 |
493 |
494 | if( IP_pref_b2a(&(node->prefix), prefstr, IP_PREFSTR_MAX) != IP_OK ) {
495 | die; /* program error. */
496 | }
497 |
498 | sprintf(line, "%-20s %s\n", prefstr,
499 | dat=AC_acl_to_string( node->leaves_ptr ));
500 | wr_free(dat);
501 |
502 | SK_cd_puts((sk_conn_st *)con, line);
503 | return RX_OK;
504 | }
505 |