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