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