1 | /***************************************
2 | $Revision: 1.23 $
3 |
4 | Access control module (ac) - access control for the query part
5 |
6 | Status: NOT REVIEWED, TESTED
7 |
8 | Design and implementation by: Marek Bukowy
9 |
10 | ******************/ /******************
11 | Copyright (c) 1999 RIPE NCC
12 |
13 | All Rights Reserved
14 |
15 | Permission to use, copy, modify, and distribute this software and its
16 | documentation for any purpose and without fee is hereby granted,
17 | provided that the above copyright notice appear in all copies and that
18 | both that copyright notice and this permission notice appear in
19 | supporting documentation, and that the name of the author not be
20 | used in advertising or publicity pertaining to distribution of the
21 | software without specific, written prior permission.
22 |
23 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
25 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
26 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
28 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29 | ***************************************/
30 | #include <stdio.h>
31 | #include <glib.h>
32 |
33 | #define AC_OK RX_OK
34 | #define AC_INVARG IP_INVARG
35 |
36 | #define AC_IMPL
37 | #include <rxroutines.h>
38 | #include <erroutines.h>
39 | #include <access_control.h>
40 | #include "socket.h"
41 | #include "mysql_driver.h"
42 | #include <constants.h>
43 | #include <server.h>
44 |
45 | #define AC_DECAY_TIME 600
46 |
47 | /* formats for printing the access control list entries */
48 | #define ACL_FORMAT "%10d %10d %10d %10d %10d"
49 | #define ACL_HEADER "%-20s %10s %10s %10s %10s %10s\n"
50 |
51 | /* formats for printing the accounting entries */
52 | #define ACC_FORMAT "%4d %4d %4d %4d %9d %9d %9d %9d"
53 | #define ACC_HEADER "%-20s %4s %4s %4s %4s %9s %9s %9s %9s\n"
54 |
55 |
56 | /*++++++++++++++++++++++++++++++++++++++
57 | AC_to_string_header:
58 |
59 | produce a header for the access stats printout
60 |
61 | returns an allocated string
62 | ++++++++++++++++++++++++++++++++++++++*/
63 | char *AC_to_string_header(void)
64 | {
65 | char *result_buf;
66 |
67 | dieif( wr_malloc( (void **) &result_buf, 256) != UT_OK );
68 |
69 | sprintf(result_buf, ACC_HEADER,
70 | "ip", "conn", "pass", "deny", "qry", "pub", "priv", "prv_bonus","pub_bonus");
71 |
72 | return result_buf;
73 | }
74 |
75 | /*++++++++++++++++++++++++++++++++++++++
76 | AC_to_string:
77 |
78 | Show an access structure
79 |
80 | returns an allocated string
81 | ++++++++++++++++++++++++++++++++++++++*/
82 | char *AC_to_string(GList *leafptr)
83 | {
84 | char *result_buf;
85 | acc_st *a = leafptr->data;
86 |
87 | if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
88 | /* XXX generic malloc handler pending ...*/
89 | return NULL;
90 | }
91 |
92 | if( a == NULL ) {
93 | strcpy(result_buf, "DATA MISSING!");
94 | }
95 | else {
96 | sprintf(result_buf, ACC_FORMAT,
97 | a->connections,
98 | a->addrpasses,
99 | a->denials,
100 | a->queries,
101 | a->public_objects,
102 | a->private_objects,
103 | a->private_bonus,
104 | a->public_bonus
105 | );
106 | }
107 |
108 | return result_buf;
109 | } /* AC_to_string() */
110 |
111 |
112 | /*++++++++++++++++++++++++++++++++++++++
113 | AC_credit_to_string:
114 |
115 | Show credit used (for logging of queries)
116 |
117 | acc_st *a - the credit structure
118 |
119 | returns an allocated string
120 | ++++++++++++++++++++++++++++++++++++++*/
121 | char *AC_credit_to_string(acc_st *a)
122 | {
123 | char *result_buf;
124 |
125 | if( wr_malloc( (void **) &result_buf, 64) != UT_OK ) {
126 | /* XXX generic malloc handler pending ...*/
127 | return NULL;
128 | }
129 |
130 | dieif( a == NULL );
131 |
132 | sprintf(result_buf,"%d+%d%s",
133 | a->private_objects,
134 | a->public_objects,
135 | a->denials ? " **DENIED**" : ""
136 | );
137 |
138 | return result_buf;
139 | } /* AC_credit_to_string */
140 |
141 |
142 | /*+++++++++++++++++++++++++++++++++++++++
143 | AC_acl_to_string_header:
144 |
145 | produce a header for the acl printout
146 |
147 | returns an allocated string
148 | ++++++++++++++++++++++++++++++++++++++*/
149 | char *
150 | AC_acl_to_string_header(void)
151 | {
152 | char *result_buf;
153 | dieif( wr_malloc( (void **) &result_buf, 256) != UT_OK );
154 |
155 | sprintf(result_buf, ACL_HEADER, "ip",
156 | "maxpriv", "maxpub","maxdenials", "ban", "trustpass" );
157 |
158 | return result_buf;
159 | }
160 |
161 |
162 |
163 | /*++++++++++++++++++++++++++++++++++++++
164 | AC_acl_to_string:
165 |
166 | Show an access control list structure
167 |
168 | returns an allocated string
169 | ++++++++++++++++++++++++++++++++++++++*/
170 | char *AC_acl_to_string(GList *leafptr)
171 | {
172 | char *result_buf;
173 | acl_st *a = leafptr->data;
174 |
175 | if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
176 | /* XXX generic malloc handler pending ...*/
177 | return NULL;
178 | }
179 |
180 | if( a != NULL ) {
181 | sprintf(result_buf, ACL_FORMAT,
182 | a->maxprivate,
183 | a->maxpublic,
184 | a->maxdenials,
185 | a->deny,
186 | a->trustpass
187 | );
188 | }
189 | else {
190 | strcpy(result_buf, "DATA MISSING\n");
191 | }
192 |
193 | return result_buf;
194 | } /* AC_acl_to_string() */
195 |
196 |
197 | /*+++++++++++++++++++++++++++++++++++++++
198 | AC_findexless_acl_l:
199 |
200 | find the exact or less specific match for the given prefix in the acl tree.
201 |
202 | ip_prefix_t *prefix - prefix to look for
203 | acl_st *store_acl - pointer to store the output
204 |
205 | returns error code from RX or OK
206 |
207 | MT-Note: assumes locked acl tree
208 | ++++++++++++++++++++++++++++++++++++++*/
209 | er_ret_t
210 | AC_findexless_acl_l(ip_prefix_t *prefix, acl_st *store_acl)
211 | {
212 | GList *datlist=NULL;
213 | er_ret_t ret_err;
214 | rx_datref_t *datref;
215 |
216 | if( (ret_err = RX_bin_search(RX_SRCH_EXLESS, 0, 0, act_acl,
217 | prefix, &datlist, RX_ANS_ALL)
218 | ) != RX_OK || g_list_length(datlist) == 0 ) {
219 | /* acl tree is not configured at all ! There always must be a
220 | catch-all record with defaults */
221 | die;
222 | }
223 |
224 | datref = (rx_datref_t *)g_list_nth_data(datlist,0);
225 |
226 | *store_acl = * ((acl_st *) datref->leafptr);
227 |
228 | wr_clear_list( &datlist );
229 |
230 | /* XXX dbg checking tree consistency */
231 | {
232 | rx_treecheck_t errorfound;
233 | er_ret_t err;
234 | if( (err=RX_treecheck(act_acl, 1, &errorfound)) != RX_OK ) {
235 | fprintf(stderr, "Nope! %d returned \n", err);
236 | die;
237 | }
238 | }
239 |
240 | return ret_err;
241 | }
242 | /* AC_findexless_acl_l */
243 |
244 |
245 | /*+++++++++++++++++++++++++++++++++++++++
246 | AC_findcreate_acl_l:
247 |
248 | find or create an entry for the given prefix in the acl tree.
249 |
250 | ip_prefix_t *prefix - prefix to look for
251 | acl_st **store_acl - pointer to store the ptr to the acl struct
252 | (initialised to the values of the parent entry
253 | if just created)
254 |
255 | returns error code from RX or OK
256 |
257 | MT-Note: assumes locked acl tree
258 | ++++++++++++++++++++++++++++++++++++++*/
259 | er_ret_t
260 | AC_findcreate_acl_l(ip_prefix_t *prefix, acl_st **store_acl)
261 | {
262 | GList *datlist=NULL;
263 | er_ret_t ret_err;
264 | acl_st *newacl;
265 | acl_st acl_copy;
266 |
267 | if( NOERR(ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, act_acl,
268 | prefix, &datlist, RX_ANS_ALL)
269 | )) {
270 |
271 | switch( g_list_length(datlist)) {
272 | case 0:
273 | dieif( wr_calloc((void **)&newacl, 1, sizeof(acl_st)) != UT_OK );
274 |
275 | /* make the new one inherit all parameters after the old one */
276 |
277 | AC_findexless_acl_l(prefix, &acl_copy);
278 |
279 | *newacl = acl_copy;
280 |
281 | /* link in */
282 | rx_bin_node(RX_OPER_CRE, prefix, act_acl, (rx_dataleaf_t *)newacl);
283 | break;
284 | case 1:
285 | {
286 | /* Uh-oh, the guy is already known ! (or special, in any case) */
287 | rx_datref_t *datref = (rx_datref_t *)g_list_nth_data(datlist,0);
288 | newacl = (acl_st *) datref->leafptr;
289 | }
290 | break;
291 | default:
292 | die;
293 | }
294 | }
295 |
296 | /* free search results */
297 | wr_clear_list( &datlist );
298 |
299 | /* store */
300 | *store_acl = newacl;
301 | return ret_err;
302 | }
303 | /* AC_findcreate_acl_l */
304 |
305 |
306 | /*+++++++++++++++++++++++++++++++++++++++
307 | AC_findcreate_account_l:
308 |
309 | finds exact prefix in the accounting tree
310 | or creates area initialised to zeros + sets ptr to it.
311 |
312 | rx_tree_t *tree - the tree
313 | ip_prefix_t *prefix - prefix to look for
314 | acc_st **store_acl - pointer to store the ptr to the account struct
315 |
316 | returns error code from RX or OK
317 |
318 | MT-Note: assumes locked accounting tree
319 | ++++++++++++++++++++++++++++++++++++++*/
320 | er_ret_t
321 | AC_findcreate_account_l(rx_tree_t *tree, ip_prefix_t *prefix,
322 | acc_st **acc_store)
323 | {
324 | GList *datlist=NULL;
325 | er_ret_t ret_err;
326 | acc_st *recacc;
327 |
328 | if( (ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, tree,
329 | prefix, &datlist, RX_ANS_ALL)) == RX_OK ) {
330 | switch( g_list_length(datlist) ) {
331 | case 0:
332 | /* need to create a new accounting record */
333 | if( (ret_err = wr_malloc( (void **)& recacc, sizeof(acc_st))) == UT_OK ) {
334 | /* counters = init to zeros */
335 | memset( recacc, 0, sizeof(acc_st));
336 |
337 | /* attach. The recacc is to be treated as a dataleaf
338 | (must use lower levels than RX_asc_*)
339 | */
340 | ret_err = rx_bin_node( RX_OPER_CRE, prefix,
341 | act_runtime, (rx_dataleaf_t *)recacc );
342 | }
343 | break;
344 | case 1:
345 | {
346 | rx_datref_t *datref = (rx_datref_t *) g_list_nth_data( datlist,0 );
347 |
348 | /* OK, there is a record already */
349 | recacc = (acc_st *) datref->leafptr;
350 |
351 | }
352 | break;
353 | default: die; /* there shouldn't be more than 1 entry per IP */
354 | }
355 | }
356 |
357 | wr_clear_list( &datlist );
358 |
359 | *acc_store = recacc;
360 |
361 | return ret_err;
362 | }
363 |
364 |
365 | /*++++++++++++++++++++++++++++++++++++++
366 | AC_fetch_acc:
367 |
368 | Finds the runtime accounting record for this IP,
369 | stores a copy of it in acc_store.
370 | If not found, then it is created and initialised to zeros in findcreate()
371 |
372 | ip_addr_t *addr - address
373 | acc_st *acc_store - pointer to store the account struct
374 |
375 | MT-Note: locks/unlocks the accounting tree
376 | ++++++++++++++++++++++++++++++++++++++*/
377 | er_ret_t AC_fetch_acc( ip_addr_t *addr, acc_st *acc_store)
378 | {
379 | er_ret_t ret_err;
380 | ip_prefix_t prefix;
381 | acc_st *ac_ptr;
382 |
383 | prefix.ip = *addr;
384 | prefix.bits = IP_sizebits(addr->space);
385 |
386 | TH_acquire_read_lock( &(act_runtime->rwlock) );
387 |
388 | ret_err = AC_findcreate_account_l(act_runtime, &prefix, &ac_ptr);
389 | *acc_store = *ac_ptr;
390 |
391 | TH_release_read_lock( &(act_runtime->rwlock) );
392 |
393 | return ret_err;
394 | }/* AC_fetch_acc() */
395 |
396 |
397 | /*++++++++++++++++++++++++++++++++++++++
398 | AC_check_acl:
399 |
400 | search for this ip or less specific record in the access control tree
401 |
402 | if( bonus in combined runtime+connection accountings > max_bonus in acl)
403 | set denial in the acl for this ip (create if needed)
404 | if( combined denialcounter > max_denials in acl)
405 | set the permanent ban in acl; save in SQL too
406 | calculate credit if pointer provided
407 | save the access record (ip if created or found/prefix otherwise)
408 | at *acl_store if provided
409 |
410 | ip_addr_t *addr - address
411 | acc_st *acc_store - pointer to store the *credit* account struct
412 | acl_st *acl_store - pointer to store the acl struct
413 |
414 | any of the args except address can be NULL
415 |
416 | returns error code from RX or OK
417 |
418 | MT-Note: locks/unlocks the accounting tree
419 | ++++++++++++++++++++++++++++++++++++++*/
420 | er_ret_t AC_check_acl( ip_addr_t *addr,
421 | acc_st *credit_acc,
422 | acl_st *acl_store
423 | )
424 | {
425 | ip_prefix_t prefix;
426 | er_ret_t ret_err = AC_OK;
427 | acl_st acl_record;
428 | acc_st run_acc;
429 |
430 | AC_fetch_acc( addr, &run_acc );
431 |
432 | prefix.ip = *addr;
433 | prefix.bits = IP_sizebits(addr->space);
434 |
435 | /* lock the tree accordingly */
436 | TH_acquire_read_lock( &(act_acl->rwlock) );
437 |
438 | /* find an applicable record */
439 | AC_findexless_acl_l(&prefix, &acl_record);
440 |
441 | /* calculate the credit if pointer given */
442 | if( credit_acc ) {
443 | memset( credit_acc, 0, sizeof(acc_st));
444 |
445 | /* credit = -1 if unlimited, otherwise credit = limit - bonus */
446 | credit_acc->public_objects =
447 | ( acl_record.maxpublic == -1 )
448 | ? -1 /* -1 == unlimited */
449 | : (acl_record.maxpublic - run_acc.public_bonus);
450 |
451 | credit_acc->private_objects =
452 | ( acl_record.maxprivate == -1 )
453 | ? -1 /* -1 == unlimited */
454 | : (acl_record.maxprivate - run_acc.private_bonus);
455 | }
456 |
457 | /* copy the acl record if asked for it*/
458 | if( acl_store ) {
459 | *acl_store = acl_record;
460 | }
461 |
462 | /* release lock */
463 | TH_release_read_lock( &(act_acl->rwlock) );
464 |
465 |
466 | return ret_err;
467 | }
468 |
469 |
470 |
471 | /*++++++++++++++++++++++++++++++++++++++
472 | AC_acc_addup:
473 |
474 | Add/subtract the values from one accounting structure to another
475 |
476 | acc_st *a - this one gets changed
477 | acc_st *b - this one provides the values to change a
478 | int minus - triggers subtraction if non-zero
479 |
480 | +++++++++++++++++++++++++++++++++++++++*/
481 | void AC_acc_addup(acc_st *a, acc_st *b, int minus)
482 | {
483 | int mul = minus ? -1 : 1;
484 |
485 | /* add all counters from b to those in a */
486 | a->connections += mul * b->connections;
487 | a->addrpasses += mul * b->addrpasses;
488 |
489 | a->denials += mul * b->denials;
490 | a->queries += mul * b->queries;
491 | a->public_objects += mul * b->public_objects;
492 | a->private_objects += mul * b->private_objects;
493 | a->private_bonus += mul * b->private_bonus;
494 | a->public_bonus += mul * b->public_bonus;
495 | }/* AC_acc_addup */
496 |
497 | /*++++++++++++++++++++++++++++++++++++++
498 | AC_commit_credit:
499 |
500 | performs the commit on an accounting tree (locks them first)
501 | stores a copy of the accounting record at rec_store
502 |
503 | rx_tree_t *tree - the tree
504 | ip_prefix_t *prefix - prefix (usually a /32)
505 | acc_st *acc_conn - credit used
506 | acc_st *rec_store - pointer to store the account struct
507 |
508 | returns error code from AC_findcreate_account_l or OK
509 |
510 | MT-Note: locks/unlocks the accounting tree
511 | +++++++++++++++++++++++++++++++++++++++*/
512 | er_ret_t
513 | AC_commit_credit(rx_tree_t *tree, ip_prefix_t *prefix,
514 | acc_st *acc_conn, acc_st *rec_store )
515 | {
516 | acc_st *accountrec;
517 | er_ret_t ret_err;
518 |
519 |
520 | acc_conn->private_bonus = acc_conn->private_objects;
521 | acc_conn->public_bonus = acc_conn->public_objects;
522 |
523 | TH_acquire_write_lock( &(tree->rwlock) );
524 |
525 | ret_err = AC_findcreate_account_l(act_runtime, prefix, &accountrec);
526 |
527 | if( NOERR(ret_err)) {
528 | AC_acc_addup(accountrec, acc_conn, ACC_PLUS);
529 | }
530 |
531 | TH_release_write_lock( &(tree->rwlock) );
532 |
533 | *rec_store = *accountrec;
534 |
535 | return ret_err;
536 | }/* AC_commit_credit */
537 |
538 |
539 |
540 | /*++++++++++++++++++++++++++++++++++++++
541 | AC_acl_sql:
542 |
543 | updates/creates a record for the given prefix in the acl table of
544 | the RIPADMIN database. Adds a comment.
545 |
546 | ip_prefix_t *prefix - prefix
547 | acl_st *newacl - new values to store in the database
548 | char *newcomment - comment to be added (must not be NULL)
549 |
550 | placeholder: it may return an error code from SQ - as soon as sq
551 | implements common error scheme
552 |
553 | ++++++++++++++++++++++++++++++++++++++*/
554 | er_ret_t
555 | AC_acl_sql(ip_prefix_t *prefix, acl_st *newacl, char *newcomment )
556 | {
557 | SQ_connection_t *sql_connection = NULL;
558 | SQ_result_set_t *result;
559 | SQ_row_t *row;
560 | char *oldcomment;
561 | char *query;
562 | char querybuf[256];
563 |
564 | sql_connection = SQ_get_connection(CO_get_host(),
565 | CO_get_database_port(),
566 | "RIPADMIN",
567 | CO_get_user(),
568 | CO_get_password() );
569 |
570 | /* get the old entry, extend it */
571 | sprintf(querybuf, "SELECT comment FROM acl WHERE "
572 | "prefix = %u AND prefix_length = %d",
573 | prefix->ip.words[0],
574 | prefix->bits);
575 | dieif( SQ_execute_query(sql_connection, querybuf, &result) == -1 );
576 |
577 | if( SQ_num_rows(result) == 1 ) {
578 | dieif( (row = SQ_row_next(result)) == NULL);
579 | oldcomment = SQ_get_column_string(result, row, 0);
580 | }
581 | else {
582 | oldcomment = "";
583 | }
584 |
585 | SQ_free_result(result);
586 |
587 | /* must hold the thing below (REPLACE..blah blah blah) + text */
588 | dieif( wr_malloc((void **)&query,
589 | strlen(oldcomment) + strlen(newcomment) + 256) != UT_OK );
590 |
591 | /* compose new entry and insert it */
592 | sprintf(query, "REPLACE INTO acl VALUES(%u, %d, %d, %d, %d, %d, %d,"
593 | "\"%s%s%s\")",
594 | prefix->ip.words[0],
595 | prefix->bits,
596 | newacl->maxprivate,
597 | newacl->maxpublic,
598 | newacl->maxdenials,
599 | newacl->deny,
600 | newacl->trustpass,
601 | oldcomment,
602 | strlen(oldcomment) > 0 ? "\n" : "",
603 | newcomment
604 | );
605 |
606 | SQ_execute_query(sql_connection, query, NULL);
607 | SQ_close_connection(sql_connection);
608 |
609 | wr_free(query);
610 |
611 | return AC_OK;
612 |
613 | }/* AC_acl_sql */
614 |
615 | /*++++++++++++++++++++++++++++++++++++++
616 | AC_ban_set:
617 |
618 | re/sets the permanent ban flag both in the acl tree in memory
619 | and the sql table. The "text" is appended to the comment
620 | in the sql record (the expected cases are
621 | - "automatic" in case the limit is exceeded and ban is set by s/w
622 | - "manual" in case it is (un)set from the config iface
623 |
624 | ip_prefix_t *prefix - prefix
625 | char *text - usually "automatic" or "manual"
626 | int denyflag - new value of the denyflag (ban)
627 |
628 | returns error code from AC_acl_sql or OK
629 | +++++++++++++++++++++++++++++++++++++++*/
630 | er_ret_t
631 | AC_ban_set(ip_prefix_t *prefix, char *text, int denyflag)
632 | {
633 | acl_st *treeacl;
634 | char newcomment[256];
635 | er_ret_t ret_err;
636 | time_t clock;
637 | char timebuf[26];
638 |
639 | time(&clock);
640 | ctime_r(&clock, timebuf);
641 |
642 | sprintf(newcomment,"%s permanent ban set to %d at %s", text,
643 | denyflag, timebuf);
644 |
645 | TH_acquire_write_lock( &(act_acl->rwlock) );
646 |
647 | /* find a record in the tree */
648 | if( NOERR(ret_err = AC_findcreate_acl_l( prefix, &treeacl )) ) {
649 | treeacl->deny = denyflag;
650 | ret_err = AC_acl_sql( prefix, treeacl, newcomment );
651 | }
652 | TH_release_write_lock( &(act_acl->rwlock) );
653 |
654 | return ret_err;
655 | }/* AC_ban_set */
656 |
657 |
658 | /*++++++++++++++++++++++++++++++++++++++
659 | AC_asc_ban_set:
660 |
661 | sets ban on text address/range. Parses the text address/range/prefix
662 | and then calls AC_ban_set on that prefix.
663 |
664 | Precondition: if the key is a range, it must decompose into one prefix
665 |
666 | returns error code from IP_smart_conv, AC_ban_set or
667 | AC_INVARG if range composed
668 | +++++++++++++++++++++++++++++++++++++++*/
669 | er_ret_t
670 | AC_asc_ban_set(char *addrstr, char *text, int denyflag)
671 | {
672 | er_ret_t ret_err;
673 | GList *preflist = NULL;
674 | ip_keytype_t key_type;
675 |
676 | if( (ret_err = IP_smart_conv(addrstr, 0, 0,
677 | &preflist, IP_PLAIN, &key_type)) != IP_OK ) {
678 | return ret_err;
679 | }
680 |
681 | /* allow only one prefix */
682 | /* The argument can be even a range, but must decompose into one prefix */
683 | if( NOERR(ret_err) && g_list_length( preflist ) != 1 ) {
684 | ret_err = AC_INVARG;
685 | }
686 |
687 | if( NOERR(ret_err) ) {
688 | ret_err = AC_ban_set( (g_list_first(preflist)->data), text, denyflag);
689 | }
690 |
691 | wr_clear_list( &preflist );
692 |
693 | return ret_err;
694 | }/* AC_asc_ban_set */
695 |
696 |
697 | /*++++++++++++++++++++++++++++++++++++++
698 | AC_commit:
699 |
700 | commits the credit into all accounting trees, (XXX: only one at the moment)
701 | checks the limits and sets automatic ban if limit exceeded.
702 |
703 | ip_addr_t *addr - user's address
704 | acc_st *acc_conn - credit used
705 | acl_st *acl_copy - pointer to store a copy of the acl
706 |
707 | returns error code from AC_commit_credit or AC_ban_set or OK.
708 |
709 | outline:
710 | lock runtime + minute accounting trees
711 | ----------------------- XXX runtime only for the moment
712 | find or create entries,
713 | increase accounting values by the values from passed acc
714 | check values against acl, see if permanent ban applies
715 |
716 | reset the connection acc
717 | unlock accounting trees
718 |
719 | if permanent ban - set it! :
720 | lock acl
721 | find/create IP in memory
722 | set ban
723 | find/create IP in SQL
724 | copy old values (if any), set ban, append comment
725 | unlock acl
726 |
727 | +++++++++++++++++++++++++++++++++++++++*/
728 | er_ret_t AC_commit(ip_addr_t *addr, acc_st *acc_conn, acl_st *acl_copy) {
729 | acc_st account;
730 | er_ret_t ret_err;
731 | ip_prefix_t prefix;
732 |
733 | prefix.ip = *addr;
734 | prefix.bits = IP_sizebits(addr->space);
735 |
736 | ret_err = AC_commit_credit(act_runtime, &prefix, acc_conn, &account);
737 | /* XXX add more trees here */
738 |
739 | memset(acc_conn,0, sizeof(acc_st));
740 |
741 | /* set permanent ban if deserved and if not set yet */
742 | if( account.denials > acl_copy->maxdenials
743 | && acl_copy->deny == 0
744 | && NOERR(ret_err) ) {
745 |
746 | ret_err = AC_ban_set(&prefix, "Automatic", 1);
747 | }
748 |
749 | return ret_err;
750 | } /* AC_commit */
751 |
752 |
753 | /*++++++++++++++++++++++++++++++++++++++
754 | AC_decay_hook:
755 |
756 | action performed on a single account node during decay (diminishing the
757 | bonus). Conforms to rx_walk_tree interface, therefore some of the
758 | arguments do not apply and are not used.
759 |
760 | rx_node_t *node - pointer to the node of the radix tree
761 | int level - n/a
762 | int nodecounter - n/a
763 | void *con - n/a
764 |
765 | returns always OK
766 | +++++++++++++++++++++++++++++++++++++++*/
767 | er_ret_t AC_decay_hook(rx_node_t *node, int level, int nodecounter, void *con)
768 | {
769 | acc_st *a = node->leaves_ptr->data;
770 |
771 | a->private_bonus *= 0.95;
772 | a->public_bonus *= 0.95;
773 |
774 | return RX_OK;
775 | } /* AC_decay_hook() */
776 |
777 |
778 |
779 | /*++++++++++++++++++++++++++++++++++++++
780 | AC_decay:
781 |
782 | Every AC_DECAY_TIME goes through the accounting tree(s) and decays the
783 | bonus values.
784 |
785 | returns always OK
786 |
787 | MT-Note This should be run as a detached thread.
788 | +++++++++++++++++++++++++++++++++++++++*/
789 | er_ret_t AC_decay(void) {
790 | er_ret_t ret_err = AC_OK;
791 |
792 |
793 | while(CO_get_do_server()) {
794 |
795 | TH_acquire_write_lock( &(act_runtime->rwlock) );
796 |
797 | if( act_runtime->top_ptr != NULL ) {
798 | rx_walk_tree(act_runtime->top_ptr, AC_decay_hook,
799 | RX_WALK_SKPGLU, /* skip glue nodes */
800 | 255, 0, 0, NULL, &ret_err);
801 | }
802 |
803 | /* it should also be as smart as to delete nodes that have reached
804 | zero, otherwise the whole of memory will be filled.
805 | Next release :-)
806 | */
807 |
808 | TH_release_write_lock( &(act_runtime->rwlock) );
809 |
810 | printf("AC: decaying access tree. (Every %d seconds)\n", AC_DECAY_TIME);
811 |
812 | SV_sleep(LOCK_SHTDOWN, AC_DECAY_TIME);
813 | }
814 |
815 | return ret_err;
816 | } /* AC_decay() */
817 |
818 |
819 | /*++++++++++++++++++++++++++++++++++++++
820 | AC_acc_load:
821 |
822 | loads the acl access tree from the acl table of the RIPADMIN database.
823 | (takes port/host/user/password from the config module).
824 |
825 | bails out if encounters problems with the database (logs to stderr).
826 |
827 | returns error code from RX_bin_node or wr_malloc.
828 | ++++++++++++++++++++++++++++++++++++++*/
829 | er_ret_t AC_acc_load(void)
830 | {
831 | SQ_connection_t *con=NULL;
832 | SQ_result_set_t *result;
833 | SQ_row_t *row;
834 | er_ret_t ret_err = RX_OK;
835 |
836 | if( (con = SQ_get_connection(CO_get_host(), CO_get_database_port(),
837 | "RIPADMIN", CO_get_user(), CO_get_password() )
838 | ) == NULL ) {
839 | fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
840 | die;
841 | }
842 |
843 | if( SQ_execute_query(con, "SELECT * FROM acl", &result) == -1 ) {
844 | fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
845 | die;
846 | }
847 |
848 | TH_acquire_write_lock( &(act_acl->rwlock) );
849 |
850 | while ( (row = SQ_row_next(result)) != NULL && ret_err == RX_OK) {
851 | ip_prefix_t mypref;
852 | acl_st *newacl;
853 | #define NUMELEM (7)
854 | char *col[NUMELEM];
855 | unsigned myint;
856 | int i;
857 |
858 | memset(&mypref, 0, sizeof(ip_prefix_t));
859 | mypref.ip.space = IP_V4;
860 |
861 | if( (ret_err = wr_malloc( (void **)& newacl, sizeof(acl_st))
862 | ) == UT_OK ) {
863 |
864 | for(i=0; i<NUMELEM; i++) {
865 | if ( (col[i] = SQ_get_column_string(result, row, i)) == NULL) {
866 | die;
867 | }
868 | }
869 |
870 | /* prefix ip */
871 | if( sscanf(col[0], "%u", &mypref.ip.words[0] ) < 1 ) { die; }
872 |
873 | /* prefix length */
874 | if( sscanf(col[1], "%u", &mypref.bits ) < 1 ) { die; }
875 |
876 | /* acl contents */
877 | if( sscanf(col[2], "%u", & (newacl->maxprivate) ) < 1 ) { die; }
878 | if( sscanf(col[3], "%u", & (newacl->maxpublic) ) < 1 ) { die; }
879 | if( sscanf(col[4], "%hd", & (newacl->maxdenials) ) < 1 ) { die; }
880 |
881 | /* these are chars therefore cannot read directly */
882 | if( sscanf(col[5], "%u", &myint ) < 1 ) { die; }
883 | else {
884 | newacl->deny = myint;
885 | }
886 | if( sscanf(col[6], "%u", &myint ) < 1 ) { die; }
887 | else {
888 | newacl->trustpass = myint;
889 | }
890 |
891 | /* free space */
892 | for(i=0; i<NUMELEM; i++) {
893 | wr_free(col[i]);
894 | }
895 |
896 | /* now add to the tree */
897 | ret_err = rx_bin_node( RX_OPER_CRE, &mypref,
898 | act_acl, (rx_dataleaf_t *) newacl );
899 | }
900 | } /* while row */
901 |
902 | TH_release_write_lock( &(act_acl->rwlock) );
903 |
904 | SQ_free_result(result);
905 | /* Close connection */
906 | SQ_close_connection(con);
907 |
908 | return ret_err;
909 | } /* AC_acc_load */
910 |
911 |
912 |
913 | /*++++++++++++++++++++++++++++++++++++++
914 | AC_build:
915 |
916 | creates empty trees for accounting/acl.
917 |
918 | returns error code from RX_tree_cre or OK.
919 | (XXX): just now only bails out when encounters problems.
920 | ++++++++++++++++++++++++++++++++++++++*/
921 | er_ret_t AC_build(void)
922 | {
923 | /* create trees */
924 | if ( RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
925 | RX_SUB_NONE, &act_runtime) != RX_OK
926 | || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
927 | RX_SUB_NONE, &act_hour) != RX_OK
928 | || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
929 | RX_SUB_NONE, &act_minute) != RX_OK
930 | || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
931 | RX_SUB_NONE, &act_acl) != RX_OK
932 | )
933 | die; /*can be changed to an error and handled ... some day */
934 |
935 | return RX_OK;
936 | }
937 |
938 | /*++++++++++++++++++++++++++++++++++++++
939 | AC_rxwalkhook_print:
940 |
941 | action performed on a single account node
942 | when listing the contents of the access tree: format and print the
943 | data from this node.
944 |
945 | Conforms to rx_walk_tree interface, therefore some of the
946 | arguments do not apply and are not used.
947 |
948 | rx_node_t *node - pointer to the node of the radix tree
949 | int level - n/a
950 | int nodecounter - n/a
951 | void *con - pointer to the connection structure (prints to it)
952 |
953 | returns always OK
954 | +++++++++++++++++++++++++++++++++++++++*/
955 | er_ret_t AC_rxwalkhook_print(rx_node_t *node,
956 | int level, int nodecounter,
957 | void *con)
958 | {
959 | char adstr[IP_ADDRSTR_MAX];
960 | char line[1024];
961 | char *dat;
962 |
963 |
964 | if( IP_addr_b2a(&(node->prefix.ip), adstr, IP_ADDRSTR_MAX) != IP_OK ) {
965 | die; /* program error. */
966 | }
967 |
968 | sprintf(line, "%-20s %s\n", adstr,
969 | dat=AC_to_string( node->leaves_ptr ));
970 | wr_free(dat);
971 |
972 | SK_cd_puts((sk_conn_st *)con, line);
973 | return RX_OK;
974 | } /* AC_rxwalkhook_print */
975 |
976 |
977 | /*++++++++++++++++++++++++++++++++++++++
978 | AC_rxwalkhook_print_acl:
979 |
980 | action performed on a single account node
981 | when listing the contents of the acl tree: format and print the
982 | data from this node.
983 |
984 | Conforms to rx_walk_tree interface, therefore some of the
985 | arguments do not apply and are not used.
986 |
987 | rx_node_t *node - pointer to the node of the radix tree
988 | int level - n/a
989 | int nodecounter - n/a
990 | void *con - pointer to the connection structure (prints to it)
991 |
992 | returns always OK
993 | +++++++++++++++++++++++++++++++++++++++*/
994 | er_ret_t AC_rxwalkhook_print_acl(rx_node_t *node,
995 | int level, int nodecounter,
996 | void *con)
997 | {
998 | char prefstr[IP_PREFSTR_MAX];
999 | char line[1024];
1000 | char *dat;
1001 |
1002 |
1003 | if( IP_pref_b2a(&(node->prefix), prefstr, IP_PREFSTR_MAX) != IP_OK ) {
1004 | die; /* program error. */
1005 | }
1006 |
1007 | sprintf(line, "%-20s %s\n", prefstr,
1008 | dat=AC_acl_to_string( node->leaves_ptr ));
1009 | wr_free(dat);
1010 |
1011 | SK_cd_puts((sk_conn_st *)con, line);
1012 | return RX_OK;
1013 | }/* AC_rxwalkhook_print_acl */
1014 |
1015 | /*++++++++++++++++++++++++++++++++++++++
1016 | AC_count_object:
1017 |
1018 | accounts an objects in the credit accordingly to its type,
1019 | or sets denial if the limit is defined and the credit is exceeded.
1020 |
1021 | type - object type
1022 | credit - pointer to the credit structure (gets modified)
1023 |
1024 | */
1025 | void
1026 | AC_count_object( acc_st *acc_credit,
1027 | acl_st *acl,
1028 | int private )
1029 | {
1030 | if( private ) {
1031 | if( acc_credit->private_objects <= 0 && acl->maxprivate != -1 ) {
1032 | /* must be negative - will be subtracted */
1033 | acc_credit->denials = -1;
1034 | } else {
1035 | acc_credit->private_objects --;
1036 | }
1037 | }
1038 | else {
1039 | if( acc_credit->public_objects <= 0 && acl->maxpublic != -1 ) {
1040 | acc_credit->denials = -1;
1041 | } else {
1042 | acc_credit->public_objects --;
1043 | }
1044 | }
1045 | } /* AC_count_object */
1046 |
1047 |
1048 | /*++++++++++++++++++++++++++++++++++++++
1049 | AC_credit_isdenied:
1050 | checks the denied flag in credit (-1 or 1 => denied)
1051 |
1052 | credit - pointer to the credit structure
1053 | +*/
1054 | int
1055 | AC_credit_isdenied(acc_st *acc_credit)
1056 | {
1057 | return (acc_credit->denials != 0);
1058 | } /* AC_credit_isdenied */
1059 |
1060 |
1061 | /*++++++++++++++++++++++++++++++++++++++
1062 | AC_get_higher_limit:
1063 |
1064 | returns the higher number of the two acl limits: maxprivate & maxpublic
1065 | corrected w.r.t the current credit left,
1066 | or unlimited if any of them is 'unlimited'.
1067 | +*/
1068 | int
1069 | AC_get_higher_limit(acc_st *acc_credit,
1070 | acl_st *acl)
1071 | {
1072 | if( acl->maxprivate == -1 && acl->maxpublic == -1 ) {
1073 | return -1;
1074 | }
1075 | else {
1076 | int a = acc_credit->private_objects;
1077 | int b = acc_credit->public_objects;
1078 |
1079 | return (a > b ? a : b);
1080 | }
1081 | }