1 | /***************************************
2 | $Revision: 1.47 $
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,2000,2001,2002 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 |
31 | /*
32 | test excercises:
33 |
34 | 1. add a function to delete an entry from the acl table,
35 | it should be called from the pc module.
36 |
37 | */
38 |
39 | #define AC_IMPL
40 | #include "rip.h"
41 |
42 | #include <stdio.h>
43 | #include <glib.h>
44 | #include <string.h>
45 | #include <math.h>
46 |
47 | #include <unistd.h>
48 | #include <stdlib.h>
49 |
50 | extern char *suboptarg;
51 | extern int getsubopt(char **optionp, char * const *tokens, char **valuep);
52 |
53 | /* formats for printing the access control list entries */
54 | #define ACL_FORMAT "%10d %10d %10d %10d %10d"
55 | #define ACL_HEADER "%-20s %10s %10s %10s %10s %10s\n"
56 |
57 | /* formats for printing the accounting entries */
58 | #define ACC_FORMAT "%4d %4d %4d %4d %7d %7d %7d %7.2f %7.1f %10.0f"
59 | #define ACC_HEADER "%-20s %4s %4s %4s %4s %7s %7s %7s %7s %7s %s\n"
60 |
61 |
62 | typedef struct {
63 | /* double decay_factor;*/
64 | unsigned newtotal;
65 | GList *prunelist;
66 | } ac_decay_data_t;
67 |
68 | ut_timer_t oldest_timestamp;
69 |
70 | /*++++++++++++++++++++++++++++++++++++++
71 | ac_to_string_header:
72 |
73 | produce a header for the access stats printout
74 |
75 | returns an allocated string
76 | ++++++++++++++++++++++++++++++++++++++*/
77 | static
78 | char *ac_to_string_header(void)
79 | {
80 | char *result_buf;
81 |
82 | result_buf = UT_malloc(256);
83 |
84 | sprintf(result_buf, ACC_HEADER,
85 | "ip", "conn", "pass", "deny", "qry", "refs", "priv_o", "pub_o", "priv_b","pub_b", "ts");
86 |
87 | return result_buf;
88 | }
89 |
90 | /*++++++++++++++++++++++++++++++++++++++
91 | ac_to_string:
92 |
93 | Show an access structure
94 |
95 | returns an allocated string
96 | ++++++++++++++++++++++++++++++++++++++*/
97 | static
98 | char *ac_to_string(GList *leafptr)
99 | {
100 | char *result_buf;
101 | acc_st *a = leafptr->data;
102 |
103 | result_buf = UT_malloc(256);
104 |
105 | if( a == NULL ) {
106 | strcpy(result_buf, "DATA MISSING!");
107 | }
108 | else {
109 | sprintf(result_buf, ACC_FORMAT,
110 | a->connections,
111 | a->addrpasses,
112 | a->denials,
113 | a->queries,
114 | a->referrals,
115 | a->private_objects,
116 | a->public_objects,
117 | a->private_bonus,
118 | a->public_bonus,
119 | UT_time_getvalue(&a->timestamp)
120 | );
121 | }
122 |
123 | return result_buf;
124 | } /* ac_to_string() */
125 |
126 |
127 | /*++++++++++++++++++++++++++++++++++++++
128 | AC_credit_to_string:
129 |
130 | Show credit used (for logging of queries)
131 |
132 | acc_st *a - the credit structure
133 |
134 | returns an allocated string
135 | ++++++++++++++++++++++++++++++++++++++*/
136 | char *AC_credit_to_string(acc_st *a)
137 | {
138 | char *result_buf;
139 |
140 | result_buf = UT_malloc(64);
141 |
142 | dieif( a == NULL );
143 |
144 | sprintf(result_buf,"%d+%d+%d%s",
145 | a->private_objects,
146 | a->public_objects,
147 | a->referrals,
148 | a->denials ? " **DENIED**" : ""
149 | );
150 |
151 | return result_buf;
152 | } /* AC_credit_to_string */
153 |
154 |
155 | /*+++++++++++++++++++++++++++++++++++++++
156 | ac_acl_to_string_header:
157 |
158 | produce a header for the acl printout
159 |
160 | returns an allocated string
161 | ++++++++++++++++++++++++++++++++++++++*/
162 | static char *
163 | ac_acl_to_string_header(void)
164 | {
165 | char *result_buf;
166 |
167 | result_buf = UT_malloc(256);
168 |
169 | sprintf(result_buf, ACL_HEADER, "ip",
170 | /* the names must match those in AC_ar_acl, so just take
171 | them from there */
172 | AC_ar_acl[AC_AR_MAXPRIVATE],
173 | AC_ar_acl[AC_AR_MAXPUBLIC],
174 | AC_ar_acl[AC_AR_MAXDENIALS],
175 | AC_ar_acl[AC_AR_DENY],
176 | AC_ar_acl[AC_AR_TRUSTPASS]
177 | );
178 |
179 |
180 | return result_buf;
181 | }
182 |
183 |
184 |
185 | /*++++++++++++++++++++++++++++++++++++++
186 | ac_acl_to_string:
187 |
188 | Show an access control list structure
189 |
190 | returns an allocated string
191 | ++++++++++++++++++++++++++++++++++++++*/
192 | static
193 | char *ac_acl_to_string(GList *leafptr)
194 | {
195 | char *result_buf;
196 | acl_st *a = leafptr->data;
197 |
198 | result_buf = UT_malloc(256);
199 |
200 | if( a != NULL ) {
201 | sprintf(result_buf, ACL_FORMAT,
202 | a->maxprivate,
203 | a->maxpublic,
204 | a->maxdenials,
205 | a->deny,
206 | a->trustpass
207 | );
208 | }
209 | else {
210 | strcpy(result_buf, "DATA MISSING\n");
211 | }
212 |
213 | return result_buf;
214 | } /* ac_acl_to_string() */
215 |
216 |
217 | /*+++++++++++++++++++++++++++++++++++++++
218 | ac_find_acl_l:
219 |
220 | find the exact or exact/less specific match for the given prefix in the acl tree.
221 |
222 | ip_prefix_t *prefix - prefix to look for
223 |
224 | acl_st *store_acl - pointer to store the output
225 |
226 | returns error code from RX or OK
227 |
228 | MT-Note: assumes locked acl tree
229 | ++++++++++++++++++++++++++++++++++++++*/
230 | static er_ret_t
231 | ac_find_acl_l(rx_srch_mt searchmode, ip_prefix_t *prefix, acl_st *store_acl)
232 | {
233 | GList *datlist=NULL;
234 | er_ret_t ret_err;
235 | rx_datref_t *datref;
236 |
237 | /* accept only RX_SRCH_EXLESS | RX_SRCH_EXACT modes */
238 | dieif( searchmode != RX_SRCH_EXLESS && searchmode != RX_SRCH_EXACT);
239 |
240 | /* it must work */
241 | dieif( (ret_err = RX_bin_search(searchmode, 0, 0, act_acl,
242 | prefix, &datlist, RX_ANS_ALL)
243 | ) != RX_OK );
244 | /* In exless mode, something must be found or the acl tree is not
245 | configured at all !
246 | There always must be a catch-all record with defaults */
247 | dieif( searchmode == RX_SRCH_EXLESS && g_list_length(datlist) == 0 );
248 |
249 |
250 | datref = (rx_datref_t *)g_list_nth_data(datlist,0);
251 |
252 | *store_acl = * ((acl_st *) datref->leafptr);
253 |
254 | wr_clear_list( &datlist );
255 |
256 | #if 0
257 | /* XXX dbg checking tree consistency */
258 | {
259 | rx_treecheck_t errorfound;
260 | er_ret_t err;
261 | if( (err=RX_treecheck(act_acl, 1, &errorfound)) != RX_OK ) {
262 | fprintf(stderr, "Nope! %d returned \n", err);
263 | die;
264 | }
265 | }
266 | #endif
267 |
268 | return ret_err;
269 | }
270 | /* ac_find_acl_l */
271 |
272 |
273 | /*+++++++++++++++++++++++++++++++++++++++
274 | AC_findcreate_acl_l:
275 |
276 | find or create an entry for the given prefix in the acl tree.
277 |
278 | ip_prefix_t *prefix - prefix to look for
279 |
280 | acl_st **store_acl - pointer to store the ptr to the acl struct
281 | (initialised to the values of the parent entry
282 | if just created)
283 |
284 | returns error code from RX or OK
285 |
286 | MT-Note: assumes locked acl tree
287 | ++++++++++++++++++++++++++++++++++++++*/
288 | er_ret_t
289 | AC_findcreate_acl_l(ip_prefix_t *prefix, acl_st **store_acl)
290 | {
291 | GList *datlist=NULL;
292 | er_ret_t ret_err;
293 | acl_st *newacl;
294 | acl_st acl_copy;
295 |
296 | if( NOERR(ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, act_acl,
297 | prefix, &datlist, RX_ANS_ALL)
298 | )) {
299 |
300 | switch( g_list_length(datlist)) {
301 | case 0:
302 | newacl = UT_calloc(sizeof(acl_st), 1);
303 |
304 | /* make the new one inherit all parameters after the old one */
305 |
306 | ac_find_acl_l(RX_SRCH_EXLESS, prefix, &acl_copy);
307 |
308 | *newacl = acl_copy;
309 |
310 | /* link in */
311 | rx_bin_node(RX_OPER_CRE, prefix, act_acl, (rx_dataleaf_t *)newacl);
312 | break;
313 | case 1:
314 | {
315 | /* Uh-oh, the guy is already known ! (or special, in any case) */
316 | rx_datref_t *datref = (rx_datref_t *)g_list_nth_data(datlist,0);
317 | newacl = (acl_st *) datref->leafptr;
318 | }
319 | break;
320 | default:
321 | die;
322 | }
323 | }
324 |
325 | /* free search results */
326 | wr_clear_list( &datlist );
327 |
328 | /* store */
329 | *store_acl = newacl;
330 | return ret_err;
331 | }
332 | /* AC_findcreate_acl_l */
333 |
334 |
335 | /*+++++++++++++++++++++++++++++++++++++++
336 | AC_findcreate_account_l:
337 |
338 | finds exact prefix in the accounting tree
339 | or creates area initialised to zeros + sets ptr to it.
340 |
341 | rx_tree_t *tree - the tree
342 |
343 | ip_prefix_t *prefix - prefix to look for
344 |
345 | acc_st **store_acl - pointer to store the ptr to the account struct
346 |
347 | returns error code from RX or OK
348 |
349 | MT-Note: assumes locked accounting tree
350 | ++++++++++++++++++++++++++++++++++++++*/
351 | er_ret_t
352 | AC_findcreate_account_l(rx_tree_t *tree, ip_prefix_t *prefix,
353 | acc_st **acc_store)
354 | {
355 | GList *datlist=NULL;
356 | er_ret_t ret_err;
357 | acc_st *recacc;
358 |
359 | if( (ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, tree,
360 | prefix, &datlist, RX_ANS_ALL)) == RX_OK ) {
361 | switch( g_list_length(datlist) ) {
362 | case 0:
363 | /* need to create a new accounting record */
364 | recacc = UT_malloc(sizeof(acc_st));
365 |
366 | /* counters = init to zeros */
367 | memset( recacc, 0, sizeof(acc_st));
368 |
369 | recacc->changed = AC_ACC_NEW;
370 |
371 | /* attach. The recacc is to be treated as a dataleaf
372 | (must use lower levels than RX_asc_*)
373 | */
374 | ret_err = rx_bin_node( RX_OPER_CRE, prefix,
375 | act_runtime, (rx_dataleaf_t *)recacc );
376 | if (ret_err != RX_OK) {
377 | ER_perror(FAC_AC, AC_INTR_ERR, "rx_bin_node() returned %d", ret_err);
378 | }
379 | break;
380 | case 1:
381 | {
382 | rx_datref_t *datref = (rx_datref_t *) g_list_nth_data( datlist,0 );
383 | /* OK, there is a record already */
384 | recacc = (acc_st *) datref->leafptr;
385 |
386 | }
387 | break;
388 | default: die; /* there shouldn't be more than 1 entry per IP */
389 | }
390 | } else {
391 | ER_perror(FAC_AC, AC_INTR_ERR, "RX_bin_search() returned %d", ret_err);
392 | }
393 |
394 | wr_clear_list( &datlist );
395 |
396 | *acc_store = recacc;
397 |
398 | #if 0
399 | /* XXX dbg checking tree consistency */
400 | if( act_runtime->top_ptr != NULL ) {
401 | rx_treecheck_t errorfound;
402 | er_ret_t err;
403 | if( (err=RX_treecheck(act_runtime, 1, &errorfound)) != RX_OK ) {
404 | fprintf(stderr, "Nope! %d returned \n", errorfound);
405 | ER_dbg_va( FAC_AC, ASP_AC_DECAY,
406 | "AC: checking access tree consistency: error %d",
407 | errorfound);
408 | die; /* access tree not consistent */
409 | }
410 | }
411 | #endif
412 |
413 | return ret_err;
414 | }
415 |
416 |
417 | /*++++++++++++++++++++++++++++++++++++++
418 | AC_fetch_acc:
419 |
420 | Finds the runtime accounting record for this IP,
421 | stores a copy of it in acc_store.
422 | If not found, then it is created and initialised to zeros in findcreate()
423 |
424 | ip_addr_t *addr - address
425 |
426 | acc_st *acc_store - pointer to store the account struct
427 |
428 | MT-Note: locks/unlocks the accounting tree
429 | ++++++++++++++++++++++++++++++++++++++*/
430 | er_ret_t AC_fetch_acc( ip_addr_t *addr, acc_st *acc_store)
431 | {
432 | er_ret_t ret_err;
433 | ip_prefix_t prefix;
434 | acc_st *ac_ptr;
435 |
436 | prefix.ip = *addr;
437 | prefix.bits = IP_sizebits(addr->space);
438 |
439 | TH_acquire_write_lock( &(act_runtime->rwlock) );
440 |
441 | ret_err = AC_findcreate_account_l(act_runtime, &prefix, &ac_ptr);
442 | *acc_store = *ac_ptr;
443 |
444 | TH_release_write_lock( &(act_runtime->rwlock) );
445 |
446 | return ret_err;
447 | }/* AC_fetch_acc() */
448 |
449 |
450 | /*++++++++++++++++++++++++++++++++++++++
451 | AC_check_acl:
452 |
453 | search for this ip or less specific record in the access control tree
454 |
455 | if( bonus in combined runtime+connection accountings > max_bonus in acl)
456 | set denial in the acl for this ip (create if needed)
457 | if( combined denialcounter > max_denials in acl)
458 | set the permanent ban in acl; save in SQL too
459 | calculate credit if pointer provided
460 | save the access record (ip if created or found/prefix otherwise)
461 | at *acl_store if provided
462 |
463 | ip_addr_t *addr - address
464 |
465 | acc_st *acc_store - pointer to store the *credit* account struct
466 |
467 | acl_st *acl_store - pointer to store the acl struct
468 |
469 | any of the args except address can be NULL
470 |
471 | returns error code from RX or OK
472 |
473 | MT-Note: locks/unlocks the accounting tree
474 | ++++++++++++++++++++++++++++++++++++++*/
475 | er_ret_t AC_check_acl( ip_addr_t *addr,
476 | acc_st *credit_acc,
477 | acl_st *acl_store
478 | )
479 | {
480 | ip_prefix_t prefix;
481 | er_ret_t ret_err = AC_OK;
482 | acl_st acl_record;
483 | acc_st run_acc;
484 |
485 | AC_fetch_acc( addr, &run_acc );
486 |
487 | prefix.ip = *addr;
488 | prefix.bits = IP_sizebits(addr->space);
489 |
490 | /* lock the tree accordingly */
491 | TH_acquire_read_lock( &(act_acl->rwlock) );
492 |
493 | /* find an applicable record */
494 | ac_find_acl_l(RX_SRCH_EXLESS, &prefix, &acl_record);
495 |
496 | /* calculate the credit if pointer given */
497 | if( credit_acc ) {
498 | memset( credit_acc, 0, sizeof(acc_st));
499 |
500 | /* credit = -1 if unlimited, otherwise credit = limit - bonus */
501 | credit_acc->public_objects =
502 | ( acl_record.maxpublic == -1 )
503 | ? -1 /* -1 == unlimited */
504 | : (acl_record.maxpublic - run_acc.public_bonus);
505 |
506 | credit_acc->private_objects =
507 | ( acl_record.maxprivate == -1 )
508 | ? -1 /* -1 == unlimited */
509 | : (acl_record.maxprivate - run_acc.private_bonus);
510 | }
511 |
512 | /* copy the acl record if asked for it*/
513 | if( acl_store ) {
514 | *acl_store = acl_record;
515 | }
516 |
517 | /* release lock */
518 | TH_release_read_lock( &(act_acl->rwlock) );
519 |
520 |
521 | return ret_err;
522 | }
523 |
524 |
525 | void AC_decay_leaf_l(acc_st *leaf) {
526 | double factor;
527 | ut_timer_t current;
528 | float time_diff;
529 |
530 | UT_timeget(¤t);
531 | time_diff = UT_timediff(&leaf->timestamp, ¤t);
532 | if (UT_time_getvalue(&leaf->timestamp) > 1 && abs(time_diff) > 0.2) {
533 | factor = exp (-0.693147180559945 * time_diff / ca_get_ac_decay_halflife);
534 | leaf->private_bonus *= factor;
535 | leaf->public_bonus *= factor;
536 | }
537 | }
538 |
539 | /*++++++++++++++++++++++++++++++++++++++
540 | AC_acc_addup:
541 |
542 | Add/subtract the values from one accounting structure to another
543 |
544 | acc_st *a this one gets changed
545 |
546 | acc_st *b this one provides the values to change a
547 |
548 | int minus triggers subtraction if non-zero
549 |
550 | +++++++++++++++++++++++++++++++++++++++*/
551 | void AC_acc_addup(acc_st *a, acc_st *b, int minus)
552 | {
553 | int mul = minus ? -1 : 1;
554 | time_t current_time;
555 |
556 | current_time = time(NULL);
557 |
558 | AC_decay_leaf_l(a);
559 |
560 |
561 | UT_timeget(&a->timestamp);
562 |
563 | /* add all counters from b to those in a */
564 | a->connections += mul * b->connections;
565 | a->addrpasses += mul * b->addrpasses;
566 |
567 | a->denials += mul * b->denials;
568 | a->queries += mul * b->queries;
569 | a->referrals += mul * b->referrals;
570 | a->public_objects += mul * b->public_objects;
571 | a->private_objects += mul * b->private_objects;
572 | a->private_bonus += mul * b->private_bonus;
573 | a->public_bonus += mul * b->public_bonus;
574 |
575 |
576 | if (a->changed == AC_ACC_NOT_CHANGED) {
577 | a->changed = AC_ACC_CHANGED;
578 | }
579 |
580 |
581 | }/* AC_acc_addup */
582 |
583 | /*++++++++++++++++++++++++++++++++++++++
584 | commit_credit_l:
585 |
586 | performs the commit on an accounting tree (locks them first)
587 | stores a copy of the accounting record at rec_store
588 |
589 | Assumes locked tree.
590 |
591 | rx_tree_t *tree - the tree
592 |
593 | ip_prefix_t *prefix - prefix (usually a /32)
594 |
595 | acc_st *acc_conn - credit used
596 |
597 | acc_st *rec_store - pointer to store the account struct or NULL
598 |
599 | returns error code from AC_findcreate_account_l or OK
600 |
601 | MT-Note: locks/unlocks the accounting tree
602 | +++++++++++++++++++++++++++++++++++++++*/
603 | static
604 | er_ret_t
605 | AC_commit_credit_l(rx_tree_t *tree, ip_prefix_t *prefix,
606 | acc_st *acc_conn, acc_st *rec_store )
607 | {
608 | acc_st *accountrec;
609 | er_ret_t ret_err;
610 | char dbg_pref[50];
611 |
612 |
613 | acc_conn->private_bonus = acc_conn->private_objects;
614 | acc_conn->public_bonus = acc_conn->public_objects;
615 |
616 |
617 | ret_err = AC_findcreate_account_l(act_runtime, prefix, &accountrec);
618 |
619 | if( NOERR(ret_err)) {
620 | // XXX remove debug IP_pref_b2a(prefix, dbg_pref, 50);
621 | AC_acc_addup(accountrec, acc_conn, ACC_PLUS);
622 | }
623 |
624 | if( rec_store ) {
625 | *rec_store = *accountrec;
626 | }
627 |
628 | return ret_err;
629 | }/* AC_commit_credit */
630 |
631 | /*++++++++++++++++++++++++++++++++++++++
632 | AC_dbopen_admin:
633 |
634 | opens the ADMIN database and returns a pointer to the connection structure
635 | (rationale: the opening process became a bit bloated and is done twice,
636 | so I put it into a separate function)
637 | ++++++++++++++++++++++++++++++++++++++*/
638 | SQ_connection_t *
639 | AC_dbopen_admin(void)
640 | {
641 | SQ_connection_t *con=NULL;
642 | char *dbhost = ca_get_ripadminhost;
643 | char *dbname = ca_get_ripadmintable;
644 | char *dbuser = ca_get_ripadminuser;
645 | char *dbpass = ca_get_ripadminpassword;
646 | unsigned dbport = ca_get_ripadminport;
647 |
648 | if( (con = SQ_get_connection(dbhost, dbport, dbname, dbuser, dbpass)
649 | ) == NULL ) {
650 | fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
651 | die;
652 | }
653 |
654 | UT_free(dbhost);
655 | UT_free(dbname);
656 | UT_free(dbuser);
657 | UT_free(dbpass);
658 |
659 | return con;
660 | }
661 |
662 | /*++++++++++++++++++++++++++++++++++++++
663 | AC_acl_sql:
664 |
665 | updates/creates a record for the given prefix in the acl table of
666 | the RIPADMIN database. Adds a comment.
667 |
668 | ip_prefix_t *prefix - prefix
669 |
670 | acl_st *newacl - new values to store in the database
671 |
672 | char *newcomment - comment to be added (must not be NULL)
673 |
674 | placeholder: it may return an error code from SQ - as soon as sq
675 | implements common error scheme
676 |
677 | ++++++++++++++++++++++++++++++++++++++*/
678 | er_ret_t
679 | AC_acl_sql(ip_prefix_t *prefix, acl_st *newacl, char *newcomment )
680 | {
681 | SQ_connection_t *sql_connection = NULL;
682 | SQ_result_set_t *result;
683 | SQ_row_t *row;
684 | char *oldcomment;
685 | char *query;
686 | char querybuf[256];
687 |
688 | sql_connection = AC_dbopen_admin();
689 |
690 | /* get the old entry, extend it */
691 | sprintf(querybuf, "SELECT comment FROM acl WHERE "
692 | "prefix = %u AND prefix_length = %d",
693 | prefix->ip.words[0],
694 | prefix->bits);
695 | dieif( SQ_execute_query(sql_connection, querybuf, &result) == -1 );
696 |
697 | if( SQ_num_rows(result) == 1 ) {
698 | dieif( (row = SQ_row_next(result)) == NULL);
699 | oldcomment = SQ_get_column_string(result, row, 0);
700 | }
701 | else {
702 | oldcomment = "";
703 | }
704 |
705 | SQ_free_result(result);
706 |
707 | /* must hold the thing below (REPLACE..blah blah blah) + text */
708 | query = UT_malloc(strlen(oldcomment) + strlen(newcomment) + 256);
709 |
710 | /* compose new entry and insert it */
711 | sprintf(query, "REPLACE INTO acl VALUES(%u, %d, %d, %d, %d, %d, %d,"
712 | "\"%s%s%s\")",
713 | prefix->ip.words[0],
714 | prefix->bits,
715 | newacl->maxprivate,
716 | newacl->maxpublic,
717 | newacl->maxdenials,
718 | newacl->deny,
719 | newacl->trustpass,
720 | oldcomment,
721 | strlen(oldcomment) > 0 ? "\n" : "",
722 | newcomment
723 | );
724 |
725 | SQ_execute_query(sql_connection, query, NULL);
726 | SQ_close_connection(sql_connection);
727 |
728 | UT_free(query);
729 |
730 | return AC_OK;
731 |
732 | }/* AC_acl_sql */
733 |
734 | /*++++++++++++++++++++++++++++++++++++++
735 | AC_ban_set:
736 |
737 | re/sets the permanent ban flag both in the acl tree in memory
738 | and the sql table. The "text" is appended to the comment
739 | in the sql record (the expected cases are
740 | - "automatic" in case the limit is exceeded and ban is set by s/w
741 | - "manual" in case it is (un)set from the config iface
742 |
743 | ip_prefix_t *prefix - prefix
744 |
745 | char *text - usually "automatic" or "manual"
746 |
747 | int denyflag - new value of the denyflag (ban)
748 |
749 | returns error code from AC_acl_sql or OK
750 | +++++++++++++++++++++++++++++++++++++++*/
751 | er_ret_t
752 | AC_ban_set(ip_prefix_t *prefix, char *text, int denyflag)
753 | {
754 | acl_st *treeacl;
755 | char newcomment[256];
756 | er_ret_t ret_err;
757 | time_t clock;
758 | char timebuf[26];
759 | char prefstr[IP_PREFSTR_MAX];
760 |
761 | time(&clock);
762 | ctime_r(&clock, timebuf);
763 |
764 | sprintf(newcomment,"%s permanent ban set to %d at %s", text,
765 | denyflag, timebuf);
766 |
767 | if( IP_pref_b2a(prefix, prefstr, IP_PREFSTR_MAX) != IP_OK ) {
768 | die; /* program error - this is already converted so must be OK */
769 | }
770 |
771 | ER_inf_va( FAC_AC, ASP_AC_I_PERMBAN,
772 | "%s permanent ban set to %d for %s", text, denyflag, prefstr );
773 |
774 | TH_acquire_write_lock( &(act_acl->rwlock) );
775 |
776 | /* find a record in the tree */
777 | if( NOERR(ret_err = AC_findcreate_acl_l( prefix, &treeacl )) ) {
778 | treeacl->deny = denyflag;
779 | ret_err = AC_acl_sql( prefix, treeacl, newcomment );
780 | }
781 | TH_release_write_lock( &(act_acl->rwlock) );
782 |
783 | return ret_err;
784 | }/* AC_ban_set */
785 |
786 |
787 | /*++++++++++++++++++++++++++++++++++++++
788 | AC_asc_ban_set:
789 |
790 | This is not used, should be removed?
791 |
792 | sets ban on text address/range. Parses the text address/range/prefix
793 | and then calls AC_ban_set on that prefix.
794 |
795 | Precondition: if the key is a range, it must decompose into one prefix
796 |
797 | returns error code from IP_smart_conv, AC_ban_set or
798 | AC_INVARG if range composed
799 | +++++++++++++++++++++++++++++++++++++++*/
800 | er_ret_t
801 | AC_asc_ban_set(char *addrstr, char *text, int denyflag)
802 | {
803 | er_ret_t ret_err;
804 | GList *preflist = NULL;
805 | ip_keytype_t key_type;
806 |
807 | if( (ret_err = IP_smart_conv(addrstr, 0, 0,
808 | &preflist, IP_PLAIN, &key_type)) != IP_OK ) {
809 | return ret_err;
810 | }
811 |
812 | /* allow only one prefix */
813 | /* The argument can be even a range, but must decompose into one prefix */
814 | if( NOERR(ret_err) && g_list_length( preflist ) != 1 ) {
815 | ret_err = AC_INVARG;
816 | }
817 |
818 | if( NOERR(ret_err) ) {
819 | ret_err = AC_ban_set( (g_list_first(preflist)->data), text, denyflag);
820 | }
821 |
822 | wr_clear_list( &preflist );
823 |
824 | return ret_err;
825 | }/* AC_asc_ban_set */
826 |
827 | /*++++++++++++++++++++++++++++++++++++++
828 | AC_asc_all_set:
829 |
830 | take ascii prefix and find/create a new entry, inheriting all parameters
831 | and then set them according to the array of args.
832 |
833 | +*/
834 | er_ret_t
835 | AC_asc_all_set(ip_prefix_t *prefix, char *comment, char * array[])
836 | {
837 | er_ret_t ret_err;
838 | acl_st *treeacl;
839 | int i;
840 |
841 | TH_acquire_write_lock( &(act_acl->rwlock) );
842 |
843 | /* find/create a record in the tree */
844 | if( NOERR(ret_err = AC_findcreate_acl_l( prefix, &treeacl )) ) {
845 |
846 | /* update it from the array */
847 | for(i=0; i<AC_AR_SIZE; i++) {
848 | if(array[i] != NULL) { /* set only those that have been specified */
849 | int val,k;
850 |
851 | if( (k=sscanf( array[i], "%d", &val)) < 1 ) {
852 | ret_err = AC_INVARG;
853 | break; /* quit the for */
854 | }
855 |
856 | /* otherwise, the value makes sense. Put it in the structure. */
857 | switch(i) {
858 | case AC_AR_MAXPRIVATE: treeacl->maxprivate = val; break;
859 | case AC_AR_MAXPUBLIC: treeacl->maxpublic = val; break;
860 | case AC_AR_MAXDENIALS: treeacl->maxdenials = val; break;
861 | case AC_AR_DENY: treeacl->deny = val; break;
862 | case AC_AR_TRUSTPASS: treeacl->trustpass = val; break;
863 | } /* switch */
864 | } /* if array[i] not null */
865 | } /* for each array element */
866 |
867 | if( NOERR(ret_err) ) { /* protect against AC_INVARG */
868 | ret_err = AC_acl_sql( prefix, treeacl, comment );
869 | }
870 | } /* if find/create OK */
871 |
872 | TH_release_write_lock( &(act_acl->rwlock) );
873 |
874 | return ret_err;
875 | }
876 |
877 |
878 | /*++++++++++++++++++++++++++++++++++++++
879 | AC_asc_acl_command_set:
880 |
881 | parse a command and set acl options for an entry.
882 | command syntax:
883 |
884 | <prefix> option=value,option=value,option=value...
885 |
886 | where <option> is defined in AC_ar_acl[] array, value is an integer
887 |
888 | char *command text of the command.
889 | Syntax: ip[/prefixlength] column=value,column=value...
890 | Column names as in acl display. Unset columns are inherited.
891 |
892 | char *comment text to be added to the acl record's comment column.
893 |
894 | ++++++++++++++++++++++++++++++++++++++*/
895 |
896 | er_ret_t
897 | AC_asc_acl_command_set( char *command, char *comment )
898 | {
899 | ip_prefix_t *prefix;
900 | char *eop, *eoc, *value;
901 | char *array[AC_AR_SIZE];
902 | er_ret_t ret_err = AC_OK;
903 | GList *preflist = NULL;
904 | ip_keytype_t key_type;
905 |
906 | char *copy = UT_strdup(command);
907 | char *addrstr = copy;
908 | eoc = strchr(copy, '\0'); /* points to the end of it */
909 |
910 | memset(array, 0 ,sizeof(array));
911 |
912 | /* first comes the prefix. Find the space after it
913 | and break the string there.
914 | */
915 | if( (eop = strchr(copy,' ')) == NULL) {
916 | ret_err = AC_INVARG;
917 | }
918 |
919 | if( NOERR(ret_err) ) {
920 | *eop++ = 0;
921 |
922 | /* now eop points to the rest of the string (if any). Take options.
923 | */
924 | while( eop != eoc && ret_err == AC_OK) {
925 | char *sp;
926 |
927 | /* give getsubopt chunks with no spaces */
928 | if( (sp = strchr(eop, ' ')) != NULL ) {
929 | *sp=0;
930 | }
931 |
932 | while( *eop != '\0' ) {
933 | int k = getsubopt(&eop, AC_ar_acl, &value);
934 | if( k < 0 ) {
935 | ret_err = AC_INVARG;
936 | break;
937 | }
938 |
939 | array[k] = value;
940 | }
941 |
942 | if( eop != eoc ) { /*getsubopt finished but did not consume all string*/
943 | eop ++; /* must have been a space. advance one */
944 | }
945 | }
946 | }
947 |
948 | /* convert the prefix */
949 | if( NOERR(ret_err) ) {
950 | ret_err = IP_smart_conv(addrstr, 0, 0, &preflist, IP_PLAIN, &key_type);
951 |
952 | /* allow only one prefix */
953 | /* The argument can be even a range, but must decompose into one prefix */
954 | if( NOERR(ret_err) && g_list_length( preflist ) == 1 ) {
955 | prefix = (g_list_first(preflist)->data);
956 | }
957 | else {
958 | ret_err = AC_INVARG;
959 | }
960 | }
961 |
962 | /* perform changes */
963 | if( NOERR(ret_err) ) {
964 | ret_err = AC_asc_all_set(prefix, comment, array);
965 | }
966 |
967 | wr_clear_list( &preflist );
968 | UT_free(copy);
969 |
970 | return ret_err;
971 | }/* AC_asc_acl_command_set */
972 |
973 |
974 | /*++++++++++++++++++++++++++++++++++++++
975 | AC_asc_set_nodeny:
976 |
977 | reset the deny counter in the access tree to 0 (after reenabling).
978 |
979 | Operates on the runtime access tree.
980 |
981 | char *ip text IP (ip only, not prefix or range).
982 | +++++++++++++++++++++++++++++++++++++++*/
983 | er_ret_t AC_asc_set_nodeny(char *ip)
984 | {
985 | ip_prefix_t prefix;
986 | er_ret_t ret_err;
987 | acc_st *ac_ptr;
988 |
989 | ret_err = IP_addr_e2b( &(prefix.ip), ip );
990 |
991 | if( NOERR(ret_err)) {
992 | prefix.bits = IP_sizebits(prefix.ip.space);
993 |
994 | TH_acquire_write_lock( &(act_runtime->rwlock) );
995 |
996 | ret_err = AC_findcreate_account_l(act_runtime, &prefix, &ac_ptr);
997 | if( NOERR(ret_err)) {
998 | ac_ptr->denials = 0;
999 | }
1000 |
1001 | TH_release_write_lock( &(act_runtime->rwlock) );
1002 | }
1003 |
1004 | return ret_err;
1005 | }
1006 |
1007 |
1008 |
1009 | /*++++++++++++++++++++++++++++++++++++++
1010 | AC_commit:
1011 |
1012 | commits the credit into all accounting trees, (XXX: only one at the moment)
1013 | checks the limits and sets automatic ban if limit exceeded.
1014 |
1015 | ip_addr_t *addr - user's address
1016 |
1017 | acc_st *acc_conn - credit used
1018 |
1019 | acl_st *acl_copy - pointer to store a copy of the acl
1020 |
1021 | returns error code from AC_commit_credit or AC_ban_set or OK.
1022 |
1023 | outline:
1024 | lock runtime + minute accounting trees
1025 | ----------------------- XXX runtime only for the moment
1026 | find or create entries,
1027 | increase accounting values by the values from passed acc
1028 | check values against acl, see if permanent ban applies
1029 |
1030 | reset the connection acc
1031 | unlock accounting trees
1032 |
1033 | if permanent ban - set it! :
1034 | lock acl
1035 | find/create IP in memory
1036 | set ban
1037 | find/create IP in SQL
1038 | copy old values (if any), set ban, append comment
1039 | unlock acl
1040 |
1041 | +++++++++++++++++++++++++++++++++++++++*/
1042 | er_ret_t AC_commit(ip_addr_t *addr, acc_st *acc_conn, acl_st *acl_copy) {
1043 | acc_st account;
1044 | er_ret_t ret_err;
1045 | ip_prefix_t prefix;
1046 | sk_conn_st condat;
1047 |
1048 | prefix.ip = *addr;
1049 | prefix.bits = IP_sizebits(addr->space);
1050 |
1051 | TH_acquire_write_lock( &(act_runtime->rwlock) );
1052 | ret_err = AC_commit_credit_l(act_runtime, &prefix, acc_conn, &account);
1053 | TH_release_write_lock( &(act_runtime->rwlock) );
1054 |
1055 | memset(acc_conn,0, sizeof(acc_st));
1056 |
1057 | /* set permanent ban if deserved and if not set yet */
1058 | if( account.denials > acl_copy->maxdenials
1059 | && acl_copy->deny == 0
1060 | && NOERR(ret_err) ) {
1061 |
1062 | ret_err = AC_ban_set(&prefix, "Automatic", 1);
1063 | }
1064 |
1065 | /*
1066 | SK_cd_make(&condat, 1, 0);
1067 | rx_tree_print(&condat, act_runtime);
1068 | SK_cd_free(&condat);
1069 | */
1070 |
1071 | return ret_err;
1072 | } /* AC_commit */
1073 |
1074 |
1075 |
1076 | /*++++++++++++++++++++++++++++++++++++++
1077 |
1078 |
1079 | unsigned AC_prune deletes the entries listed in the prunelist
1080 | (this cannot be done from within the rx_walk_tree,
1081 | because the walk would be confused).
1082 | Returns number of nodes deleted.
1083 |
1084 | GList *prunelist list of pointers to nodes that should be deleted.
1085 | the prefixes actually are allocated in the node
1086 | structures, so they must not be dereferenced after
1087 | they are freed here.
1088 |
1089 | ++++++++++++++++++++++++++++++++++++++*/
1090 | unsigned AC_prune(GList *prunelist)
1091 | {
1092 | GList *pitem;
1093 | char prstr[IP_PREFSTR_MAX];
1094 | unsigned count = 0;
1095 | acc_st accu; /* to accumulate the accounting of deleted nodes */
1096 | ip_prefix_t globalpref;
1097 |
1098 | memset( &accu, 0, sizeof(accu));
1099 |
1100 | for( pitem = g_list_first(prunelist);
1101 | pitem != NULL;
1102 | pitem = g_list_next(pitem)) {
1103 |
1104 | rx_node_t *nodeptr = (rx_node_t *) pitem->data;
1105 | ip_prefix_t *prefptr = &(nodeptr->prefix);
1106 | acc_st *nodeacc = nodeptr->leaves_ptr->data;
1107 |
1108 | AC_acc_addup(&accu, nodeacc, ACC_PLUS); /* transfer the account */
1109 | dieif( IP_pref_b2a( prefptr, prstr, IP_PREFSTR_MAX ) != IP_OK );
1110 | ER_dbg_va( FAC_AC, ASP_AC_PRUNE_DET, "AC_prune: entry %s", prstr );
1111 | /* delete the node. Assume there's one and only one dataleaf */
1112 | rx_bin_node( RX_OPER_DEL, prefptr, act_runtime, (void *)nodeacc );
1113 | count ++;
1114 | }
1115 | /* store the accumulated account at 0/0 */
1116 | dieif( !NOERR (IP_pref_a2b( &globalpref, "0/0" )));
1117 | AC_commit_credit_l(act_runtime, &globalpref, &accu, NULL);
1118 |
1119 | return count;
1120 | }
1121 |
1122 |
1123 | char AC_prunable(acc_st *leaf) {
1124 | if( leaf->private_bonus < 0.5
1125 | && leaf->public_bonus < 0.5
1126 | && leaf->denials == 0
1127 | && leaf->addrpasses == 0 ) {
1128 | return 1;
1129 | }
1130 | return 0;
1131 | }
1132 |
1133 | /*++++++++++++++++++++++++++++++++++++++
1134 | AC_decay_hook:
1135 |
1136 | action performed on a single account node during decay (diminishing the
1137 | bonus). Conforms to rx_walk_tree interface, therefore some of the
1138 | arguments do not apply and are not used.
1139 |
1140 | rx_node_t *node - pointer to the node of the radix tree
1141 |
1142 | int level - not used
1143 |
1144 | int nodecounter - not used
1145 |
1146 | void *con - in real life: (double *) - points to the decay factor.
1147 |
1148 | returns always OK
1149 | +++++++++++++++++++++++++++++++++++++++*/
1150 | er_ret_t AC_decay_hook(rx_node_t *node, int level,
1151 | int nodecounter, void *con)
1152 | {
1153 | acc_st *a = node->leaves_ptr->data;
1154 | acc_st clone;
1155 | ac_decay_data_t *dec_dat_p = (ac_decay_data_t *)con;
1156 |
1157 | clone = *a;
1158 |
1159 | AC_decay_leaf_l(&clone);
1160 |
1161 | /* XXX pending: if bonus is close to zero and the node did not hit
1162 | its limit, and it's not an address-passing node
1163 | then add it to the list of nodes for deletion */
1164 |
1165 | /*
1166 | ER_dbg_va( FAC_AC, ASP_AC_PRUNE_DET,
1167 | "%5.2f / %5.2f * %5.2f -> %5.2f / %5.2f ",
1168 | bpr, bpu, factor, a->private_bonus, a->public_bonus);
1169 | */
1170 |
1171 | if (AC_prunable(&clone)) {
1172 | dec_dat_p->prunelist = g_list_append(dec_dat_p->prunelist, node);
1173 | }
1174 |
1175 | /* process accounting - add all queries to the total counter */
1176 | dec_dat_p->newtotal += a->queries;
1177 |
1178 | /* change oldest timestamp*/
1179 | if (UT_timediff(&clone.timestamp, &oldest_timestamp) > 0 &&
1180 | clone.addrpasses==0 && clone.denials==0 ) {
1181 | oldest_timestamp = clone.timestamp;
1182 | }
1183 |
1184 | return RX_OK;
1185 | } /* AC_decay_hook() */
1186 |
1187 |
1188 |
1189 | /*++++++++++++++++++++++++++++++++++++++
1190 | AC_decay:
1191 |
1192 | Every AC_DECAY_TIME goes through the accounting tree(s) and decays the
1193 | bonus values.
1194 |
1195 | returns always OK
1196 |
1197 | MT-Note This should be run as a detached thread.
1198 | +++++++++++++++++++++++++++++++++++++++*/
1199 | er_ret_t AC_decay(void) {
1200 | er_ret_t ret_err = AC_OK;
1201 | ac_decay_data_t dec_dat;
1202 | ut_timer_t begintime, endtime;
1203 | unsigned pruned;
1204 | float elapsed, rate, exactinterval;
1205 | unsigned oldtotal = 0;
1206 | unsigned increase;
1207 | unsigned count;
1208 |
1209 | TA_add(0, "decay");
1210 |
1211 | UT_timeget( &endtime );
1212 |
1213 | /* XXX uses CO_get_do_server() to see when to exit the program.
1214 | this will change */
1215 | while(CO_get_do_server()) {
1216 | GString *gs;
1217 |
1218 |
1219 | UT_timeget( &begintime );
1220 | exactinterval = UT_timediff( &endtime, &begintime ); /* old endtime */
1221 |
1222 |
1223 | /* those values can be changed in runtime - so recalculate
1224 | the decay factor vefore each pass */
1225 | dieif( ca_get_ac_decay_halflife == 0 );
1226 |
1227 |
1228 | dec_dat.prunelist = NULL;
1229 | /* the decay factor of
1230 | f(t) = exp(-a*t)
1231 | a = -ln(0.5) / t
1232 | so for T being the half-life period and v being the sampling interval
1233 | used as the unit of time
1234 | a = -ln(0.5) / T;
1235 | f(t+x) = exp(-a(t+x)) = f(t)*f(x) = f(t)*exp(-ax) =
1236 | = f(t)*exp(ln(0.5)*v/T)
1237 | so you multiply the previous value by exp(ln(0.5)*v/T)
1238 | */
1239 | /*
1240 | dec_dat.decay_factor =
1241 | exp ( -0.693147180559945 * exactinterval / ca_get_ac_decay_halflife) ;
1242 | */
1243 | dec_dat.newtotal = 0;
1244 | TH_acquire_write_lock( &(act_runtime->rwlock) );
1245 |
1246 | UT_timeget(&oldest_timestamp);
1247 | if( act_runtime->top_ptr != NULL ) {
1248 | count = rx_walk_tree(act_runtime->top_ptr, AC_decay_hook,
1249 | RX_WALK_SKPGLU, /* skip glue nodes */
1250 | 255, 0, 0, &dec_dat, &ret_err);
1251 | }
1252 | else {
1253 | count = 0;
1254 | }
1255 |
1256 | /* it should also be as smart as to delete nodes that have reached
1257 | zero, otherwise the whole of memory will be filled.
1258 | Next release :-)
1259 | */
1260 |
1261 | pruned = AC_prune( dec_dat.prunelist );
1262 |
1263 | /*
1264 | gs = g_string_new("");
1265 | AC_print_access(gs);
1266 | printf(gs->str);
1267 | g_list_free( dec_dat.prunelist );
1268 | */
1269 |
1270 | #if 0
1271 | /* XXX dbg checking tree consistency */
1272 | if( act_runtime->top_ptr != NULL ) {
1273 | rx_treecheck_t errorfound;
1274 | er_ret_t err;
1275 | if( (err=RX_treecheck(act_runtime, 1, &errorfound)) != RX_OK ) {
1276 | fprintf(stderr, "Nope! %d returned \n", err);
1277 | ER_dbg_va( FAC_AC, ASP_AC_DECAY,
1278 | "AC: checking access tree consistency: error %d", err);
1279 | die; /* access tree not consistent */
1280 | }
1281 | }
1282 | #endif
1283 |
1284 | TH_release_write_lock( &(act_runtime->rwlock) );
1285 |
1286 | UT_timeget(&endtime);
1287 |
1288 | elapsed = UT_timediff( &begintime, &endtime);
1289 |
1290 | ER_dbg_va( FAC_AC, ASP_AC_DECAY,
1291 | "AC_decay: Pruned %d of %d nodes. Took %5.3fs. Runs every %ds.",
1292 | pruned, count, elapsed, ca_get_ac_decay_interval);
1293 |
1294 | /* number/rate of queries within the last <interval> */
1295 | {
1296 | char actbuf[32];
1297 |
1298 | increase = dec_dat.newtotal - oldtotal;
1299 | rate = increase / exactinterval;
1300 |
1301 | sprintf(actbuf, "%.2f q/s in %.1fs", rate, exactinterval);
1302 | TA_setactivity(actbuf);
1303 |
1304 | oldtotal = dec_dat.newtotal;
1305 | }
1306 |
1307 | SV_sleep(ca_get_ac_decay_interval);
1308 | } /* while */
1309 |
1310 | TA_delete();
1311 |
1312 | return ret_err;
1313 | } /* AC_decay() */
1314 |
1315 |
1316 | /*++++++++++++++++++++++++++++++++++++++
1317 | AC_acc_load:
1318 |
1319 | loads the acl access tree from the acl table of the RIPADMIN database.
1320 | (takes port/host/user/password from the config module).
1321 |
1322 | bails out if encounters problems with the database (logs to stderr).
1323 |
1324 | returns error code from RX_bin_node or wr_malloc.
1325 | ++++++++++++++++++++++++++++++++++++++*/
1326 | er_ret_t AC_acc_load(void)
1327 | {
1328 | SQ_connection_t *con=NULL;
1329 | SQ_result_set_t *result;
1330 | SQ_row_t *row;
1331 | er_ret_t ret_err = RX_OK;
1332 |
1333 | con = AC_dbopen_admin();
1334 |
1335 | if( SQ_execute_query(con, "SELECT * FROM acl", &result) == -1 ) {
1336 | fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
1337 | die;
1338 | }
1339 |
1340 | TH_acquire_write_lock( &(act_acl->rwlock) );
1341 |
1342 | while ( (row = SQ_row_next(result)) != NULL && ret_err == RX_OK) {
1343 | ip_prefix_t mypref;
1344 | acl_st *newacl;
1345 | #define NUMELEM (7)
1346 | char *col[NUMELEM];
1347 | unsigned myint, i;
1348 |
1349 | memset(&mypref, 0, sizeof(ip_prefix_t));
1350 | mypref.ip.space = IP_V4;
1351 |
1352 | newacl = UT_malloc(sizeof(acl_st));
1353 |
1354 | for(i=0; i<NUMELEM; i++) {
1355 | if ( (col[i] = SQ_get_column_string(result, row, i)) == NULL) {
1356 | die;
1357 | }
1358 | }
1359 |
1360 | /* prefix ip */
1361 | if( sscanf(col[0], "%u", &mypref.ip.words[0] ) < 1 ) { die; }
1362 |
1363 | /* prefix length */
1364 | if( sscanf(col[1], "%u", &mypref.bits ) < 1 ) { die; }
1365 |
1366 | /* acl contents */
1367 | if( sscanf(col[2], "%u", & (newacl->maxprivate) ) < 1 ) { die; }
1368 | if( sscanf(col[3], "%u", & (newacl->maxpublic) ) < 1 ) { die; }
1369 | if( sscanf(col[4], "%hd", & (newacl->maxdenials) ) < 1 ) { die; }
1370 |
1371 | /* these are chars therefore cannot read directly */
1372 | if( sscanf(col[5], "%u", &myint ) < 1 ) { die; }
1373 | else {
1374 | newacl->deny = myint;
1375 | }
1376 | if( sscanf(col[6], "%u", &myint ) < 1 ) { die; }
1377 | else {
1378 | newacl->trustpass = myint;
1379 | }
1380 |
1381 | /* free space */
1382 | for(i=0; i<NUMELEM; i++) {
1383 | UT_free(col[i]);
1384 | }
1385 |
1386 | /* now add to the tree */
1387 | ret_err = rx_bin_node( RX_OPER_CRE, &mypref,
1388 | act_acl, (rx_dataleaf_t *) newacl );
1389 | } /* while row */
1390 |
1391 | TH_release_write_lock( &(act_acl->rwlock) );
1392 |
1393 | SQ_free_result(result);
1394 | /* Close connection */
1395 | SQ_close_connection(con);
1396 |
1397 | return ret_err;
1398 | } /* AC_acc_load */
1399 |
1400 |
1401 |
1402 | /*++++++++++++++++++++++++++++++++++++++
1403 | AC_build:
1404 |
1405 | creates empty trees for accounting/acl.
1406 |
1407 | returns error code from RX_tree_cre or OK.
1408 | (XXX): just now only bails out when encounters problems.
1409 | ++++++++++++++++++++++++++++++++++++++*/
1410 | er_ret_t AC_build(void)
1411 | {
1412 | /* create trees */
1413 | if ( RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
1414 | RX_SUB_NONE, &act_runtime) != RX_OK
1415 | || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
1416 | RX_SUB_NONE, &act_hour) != RX_OK
1417 | || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
1418 | RX_SUB_NONE, &act_minute) != RX_OK
1419 | || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
1420 | RX_SUB_NONE, &act_acl) != RX_OK
1421 | )
1422 | die; /*can be changed to an error and handled ... some day */
1423 |
1424 | return RX_OK;
1425 | }
1426 |
1427 | /*++++++++++++++++++++++++++++++++++++++
1428 | ac_rxwalkhook_print:
1429 |
1430 | action performed on a single account node
1431 | when listing the contents of the access tree: format and print the
1432 | data from this node.
1433 |
1434 | Conforms to rx_walk_tree interface, therefore some of the
1435 | arguments do not apply and are not used.
1436 |
1437 | rx_node_t *node - pointer to the node of the radix tree
1438 |
1439 | int level - not used
1440 |
1441 | int nodecounter - not used
1442 |
1443 | void *con - pointer to the target string (prints to it)
1444 |
1445 | returns always OK
1446 | +++++++++++++++++++++++++++++++++++++++*/
1447 | static
1448 | er_ret_t ac_rxwalkhook_print(rx_node_t *node,
1449 | int level, int nodecounter,
1450 | void *outvoid)
1451 | {
1452 | char adstr[IP_ADDRSTR_MAX];
1453 | char *dat;
1454 | GString *output = outvoid;
1455 |
1456 | dieif( IP_addr_b2a(&(node->prefix.ip), adstr, IP_ADDRSTR_MAX) != IP_OK );
1457 | /* program error. */
1458 |
1459 | dat = ac_to_string( node->leaves_ptr );
1460 | g_string_sprintfa(output, "%-20s %s\n", adstr, dat );
1461 | UT_free(dat);
1462 |
1463 | return RX_OK;
1464 | } /* ac_rxwalkhook_print */
1465 |
1466 |
1467 | /*++++++++++++++++++++++++++++++++++++++
1468 | This function displays the access table to the given connection.
1469 |
1470 | unsigned AC_print_access Returns the number of nodes traversed
1471 |
1472 | GString *output target string
1473 | ++++++++++++++++++++++++++++++++++++++*/
1474 | unsigned AC_print_access(GString *output)
1475 | {
1476 | int cnt = 0;
1477 | er_ret_t err;
1478 |
1479 | if( act_runtime->top_ptr != NULL ) {
1480 | char *header = ac_to_string_header();
1481 |
1482 | /* print header */
1483 | g_string_append(output, header);
1484 | UT_free(header);
1485 |
1486 | cnt = rx_walk_tree(act_runtime->top_ptr, ac_rxwalkhook_print,
1487 | RX_WALK_SKPGLU, /* print no glue nodes */
1488 | 255, 0, 0, output, &err);
1489 | }
1490 |
1491 | return cnt;
1492 | } /* show_access() */
1493 |
1494 |
1495 |
1496 | /*++++++++++++++++++++++++++++++++++++++
1497 | ac_rxwalkhook_print_acl:
1498 |
1499 | action performed on a single account node
1500 | when listing the contents of the acl tree: format and print the
1501 | data from this node.
1502 |
1503 | Conforms to rx_walk_tree interface, therefore some of the
1504 | arguments do not apply and are not used.
1505 |
1506 | rx_node_t *node - pointer to the node of the radix tree
1507 |
1508 | int level - not used
1509 |
1510 | int nodecounter - not used
1511 |
1512 | void *con - pointer to the target string (prints to it)
1513 |
1514 | returns always OK
1515 | +++++++++++++++++++++++++++++++++++++++*/
1516 | static
1517 | er_ret_t ac_rxwalkhook_print_acl(rx_node_t *node,
1518 | int level, int nodecounter,
1519 | void *outvoid)
1520 | {
1521 | char prefstr[IP_PREFSTR_MAX];
1522 | char *dat;
1523 | GString *output = outvoid;
1524 |
1525 | dieif( IP_pref_b2a(&(node->prefix), prefstr, IP_PREFSTR_MAX) != IP_OK );
1526 |
1527 | dat = ac_acl_to_string( node->leaves_ptr );
1528 | g_string_sprintfa(output, "%-20s %s\n", prefstr, dat );
1529 | UT_free(dat);
1530 |
1531 | return RX_OK;
1532 | }/* ac_rxwalkhook_print_acl */
1533 |
1534 |
1535 | /*++++++++++++++++++++++++++++++++++++++
1536 | This function writes the acl (access control) table to the given
1537 | Gstring (auto-expandable)
1538 |
1539 | unsigned AC_print_acl Returns the number of nodes traversed
1540 |
1541 | GString *output target string
1542 | ++++++++++++++++++++++++++++++++++++++*/
1543 | unsigned AC_print_acl(GString *output)
1544 | {
1545 | /* Administrator wishes to show access control list. */
1546 | int cnt = 0;
1547 | er_ret_t err;
1548 |
1549 | if( act_acl->top_ptr != NULL ) {
1550 | char *header = ac_acl_to_string_header();
1551 |
1552 | /* print header */
1553 | g_string_append(output, header);
1554 | UT_free(header);
1555 |
1556 | cnt = rx_walk_tree(act_acl->top_ptr, ac_rxwalkhook_print_acl,
1557 | RX_WALK_SKPGLU, /* print no glue nodes */
1558 | 255, 0, 0, output, &err);
1559 | }
1560 |
1561 | return cnt;
1562 | }
1563 |
1564 |
1565 | /*++++++++++++++++++++++++++++++++++++++
1566 | AC_count_object:
1567 |
1568 | accounts an objects in the credit accordingly to its type,
1569 | or sets denial if the limit is defined and the credit is exceeded.
1570 |
1571 | acc_st *acc_credit pointer to the credit structure (gets modified)
1572 |
1573 | acl_st *acl acl, contains the limits for private/public objects
1574 |
1575 | int private indicates if the object type is private
1576 | ++++++++++++++++++++++++++++++++++++++*/
1577 | void
1578 | AC_count_object( acc_st *acc_credit,
1579 | acl_st *acl,
1580 | int private )
1581 | {
1582 | if( private ) {
1583 | if( acc_credit->private_objects <= 0 && acl->maxprivate != -1 ) {
1584 | /* must be negative - will be subtracted */
1585 | acc_credit->denials = -1;
1586 | } else {
1587 | acc_credit->private_objects --;
1588 | }
1589 | }
1590 | else {
1591 | if( acc_credit->public_objects <= 0 && acl->maxpublic != -1 ) {
1592 | acc_credit->denials = -1;
1593 | } else {
1594 | acc_credit->public_objects --;
1595 | }
1596 | }
1597 | } /* AC_count_object */
1598 |
1599 |
1600 | /* AC_credit_isdenied */
1601 | /*++++++++++++++++++++++++++++++++++++++
1602 |
1603 | checks the denied flag in credit (-1 or 1 means denied)
1604 |
1605 | int
1606 | AC_credit_isdenied returns 1 if denied, 0 otherwise
1607 |
1608 | acc_st *acc_credit pointer to the credit structure
1609 | ++++++++++++++++++++++++++++++++++++++*/
1610 | int
1611 | AC_credit_isdenied(acc_st *acc_credit)
1612 | {
1613 | return (acc_credit->denials != 0);
1614 | } /* AC_credit_isdenied */
1615 |
1616 |
1617 | /* AC_get_higher_limit */
1618 | /*++++++++++++++++++++++++++++++++++++++
1619 |
1620 | returns the higher number of the two acl limits: maxprivate & maxpublic
1621 | corrected w.r.t the current credit left,
1622 | or unlimited if any of them is 'unlimited'.
1623 |
1624 | int AC_get_higher_limit returns the higher limit
1625 |
1626 | acc_st *acc_credit current credit left
1627 |
1628 | acl_st *acl acl for that user
1629 | ++++++++++++++++++++++++++++++++++++++*/
1630 | int
1631 | AC_get_higher_limit(acc_st *acc_credit,
1632 | acl_st *acl)
1633 | {
1634 | if( acl->maxprivate == -1 || acl->maxpublic == -1 ) {
1635 | return -1;
1636 | }
1637 | else {
1638 | int a = acc_credit->private_objects;
1639 | int b = acc_credit->public_objects;
1640 |
1641 | return (a > b ? a : b);
1642 | }
1643 | }/* AC_get_higher_limit */