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