1 | /*************************************** 2 | $Revision: 1.27 $ 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 | if( (eop = strchr(copy,' ')) == NULL) { 819 | ret_err = AC_INVARG; 820 | } 821 | 822 | if( NOERR(ret_err) ) { 823 | *eop++ = 0; 824 | 825 | /* now eop points to the rest of the string (if any). Take options. 826 | */ 827 | while( eop != eoc && ret_err == AC_OK) { 828 | char *sp; 829 | 830 | /* give getsubopt chunks with no spaces */ 831 | if( (sp = strchr(eop, ' ')) != NULL ) { 832 | *sp=0; 833 | } 834 | 835 | while( *eop != '\0' ) { 836 | int k = getsubopt(&eop, AC_ar_acl, &value); 837 | if( k < 0 ) { 838 | ret_err = AC_INVARG; 839 | break; 840 | } 841 | 842 | array[k] = value; 843 | } 844 | 845 | if( eop != eoc ) { /*getsubopt finished but did not consume all string*/ 846 | eop ++; /* must have been a space. advance one */ 847 | } 848 | } 849 | } 850 | 851 | /* convert the prefix */ 852 | if( NOERR(ret_err) ) { 853 | ret_err = IP_smart_conv(addrstr, 0, 0, &preflist, IP_PLAIN, &key_type); 854 | 855 | /* allow only one prefix */ 856 | /* The argument can be even a range, but must decompose into one prefix */ 857 | if( NOERR(ret_err) && g_list_length( preflist ) == 1 ) { 858 | prefix = (g_list_first(preflist)->data); 859 | } 860 | else { 861 | ret_err = AC_INVARG; 862 | } 863 | } 864 | 865 | /* perform changes */ 866 | if( NOERR(ret_err) ) { 867 | ret_err = AC_asc_all_set(prefix, comment, array); 868 | } 869 | 870 | wr_clear_list( &preflist ); 871 | free(copy); 872 | 873 | return ret_err; 874 | } 875 | 876 | 877 | /*++++++++++++++++++++++++++++++++++++++ 878 | AC_commit: 879 | 880 | commits the credit into all accounting trees, (XXX: only one at the moment) 881 | checks the limits and sets automatic ban if limit exceeded. 882 | 883 | ip_addr_t *addr - user's address 884 | acc_st *acc_conn - credit used 885 | acl_st *acl_copy - pointer to store a copy of the acl 886 | 887 | returns error code from AC_commit_credit or AC_ban_set or OK. 888 | 889 | outline: 890 | lock runtime + minute accounting trees 891 | ----------------------- XXX runtime only for the moment 892 | find or create entries, 893 | increase accounting values by the values from passed acc 894 | check values against acl, see if permanent ban applies 895 | 896 | reset the connection acc 897 | unlock accounting trees 898 | 899 | if permanent ban - set it! : 900 | lock acl 901 | find/create IP in memory 902 | set ban 903 | find/create IP in SQL 904 | copy old values (if any), set ban, append comment 905 | unlock acl 906 | 907 | +++++++++++++++++++++++++++++++++++++++*/ 908 | er_ret_t AC_commit(ip_addr_t *addr, acc_st *acc_conn, acl_st *acl_copy) { 909 | acc_st account; 910 | er_ret_t ret_err; 911 | ip_prefix_t prefix; 912 | 913 | prefix.ip = *addr; 914 | prefix.bits = IP_sizebits(addr->space); 915 | 916 | ret_err = AC_commit_credit(act_runtime, &prefix, acc_conn, &account); 917 | /* XXX add more trees here */ 918 | 919 | memset(acc_conn,0, sizeof(acc_st)); 920 | 921 | /* set permanent ban if deserved and if not set yet */ 922 | if( account.denials > acl_copy->maxdenials 923 | && acl_copy->deny == 0 924 | && NOERR(ret_err) ) { 925 | 926 | ret_err = AC_ban_set(&prefix, "Automatic", 1); 927 | } 928 | 929 | return ret_err; 930 | } /* AC_commit */ 931 | 932 | 933 | /*++++++++++++++++++++++++++++++++++++++ 934 | AC_decay_hook: 935 | 936 | action performed on a single account node during decay (diminishing the 937 | bonus). Conforms to rx_walk_tree interface, therefore some of the 938 | arguments do not apply and are not used. 939 | 940 | rx_node_t *node - pointer to the node of the radix tree 941 | int level - n/a 942 | int nodecounter - n/a 943 | void *con - n/a 944 | 945 | returns always OK 946 | +++++++++++++++++++++++++++++++++++++++*/ 947 | er_ret_t AC_decay_hook(rx_node_t *node, int level, int nodecounter, void *con) 948 | { 949 | acc_st *a = node->leaves_ptr->data; 950 | 951 | a->private_bonus *= 0.95; 952 | a->public_bonus *= 0.95; 953 | 954 | return RX_OK; 955 | } /* AC_decay_hook() */ 956 | 957 | 958 | 959 | /*++++++++++++++++++++++++++++++++++++++ 960 | AC_decay: 961 | 962 | Every AC_DECAY_TIME goes through the accounting tree(s) and decays the 963 | bonus values. 964 | 965 | returns always OK 966 | 967 | MT-Note This should be run as a detached thread. 968 | +++++++++++++++++++++++++++++++++++++++*/ 969 | er_ret_t AC_decay(void) { 970 | er_ret_t ret_err = AC_OK; 971 | 972 | 973 | while(CO_get_do_server()) { 974 | 975 | TH_acquire_write_lock( &(act_runtime->rwlock) ); 976 | 977 | if( act_runtime->top_ptr != NULL ) { 978 | rx_walk_tree(act_runtime->top_ptr, AC_decay_hook, 979 | RX_WALK_SKPGLU, /* skip glue nodes */ 980 | 255, 0, 0, NULL, &ret_err); 981 | } 982 | 983 | /* it should also be as smart as to delete nodes that have reached 984 | zero, otherwise the whole of memory will be filled. 985 | Next release :-) 986 | */ 987 | 988 | TH_release_write_lock( &(act_runtime->rwlock) ); 989 | 990 | printf("AC: decaying access tree. (Every %d seconds)\n", AC_DECAY_TIME); 991 | 992 | SV_sleep(LOCK_SHTDOWN, AC_DECAY_TIME); 993 | } 994 | 995 | return ret_err; 996 | } /* AC_decay() */ 997 | 998 | 999 | /*++++++++++++++++++++++++++++++++++++++ 1000 | AC_acc_load: 1001 | 1002 | loads the acl access tree from the acl table of the RIPADMIN database. 1003 | (takes port/host/user/password from the config module). 1004 | 1005 | bails out if encounters problems with the database (logs to stderr). 1006 | 1007 | returns error code from RX_bin_node or wr_malloc. 1008 | ++++++++++++++++++++++++++++++++++++++*/ 1009 | er_ret_t AC_acc_load(void) 1010 | { 1011 | SQ_connection_t *con=NULL; 1012 | SQ_result_set_t *result; 1013 | SQ_row_t *row; 1014 | er_ret_t ret_err = RX_OK; 1015 | 1016 | con = AC_dbopen_admin(); 1017 | 1018 | if( SQ_execute_query(con, "SELECT * FROM acl", &result) == -1 ) { 1019 | fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con)); 1020 | die; 1021 | } 1022 | 1023 | TH_acquire_write_lock( &(act_acl->rwlock) ); 1024 | 1025 | while ( (row = SQ_row_next(result)) != NULL && ret_err == RX_OK) { 1026 | ip_prefix_t mypref; 1027 | acl_st *newacl; 1028 | #define NUMELEM (7) 1029 | char *col[NUMELEM]; 1030 | unsigned myint; 1031 | int i; 1032 | 1033 | memset(&mypref, 0, sizeof(ip_prefix_t)); 1034 | mypref.ip.space = IP_V4; 1035 | 1036 | if( (ret_err = wr_malloc( (void **)& newacl, sizeof(acl_st)) 1037 | ) == UT_OK ) { 1038 | 1039 | for(i=0; i<NUMELEM; i++) { 1040 | if ( (col[i] = SQ_get_column_string(result, row, i)) == NULL) { 1041 | die; 1042 | } 1043 | } 1044 | 1045 | /* prefix ip */ 1046 | if( sscanf(col[0], "%u", &mypref.ip.words[0] ) < 1 ) { die; } 1047 | 1048 | /* prefix length */ 1049 | if( sscanf(col[1], "%u", &mypref.bits ) < 1 ) { die; } 1050 | 1051 | /* acl contents */ 1052 | if( sscanf(col[2], "%u", & (newacl->maxprivate) ) < 1 ) { die; } 1053 | if( sscanf(col[3], "%u", & (newacl->maxpublic) ) < 1 ) { die; } 1054 | if( sscanf(col[4], "%hd", & (newacl->maxdenials) ) < 1 ) { die; } 1055 | 1056 | /* these are chars therefore cannot read directly */ 1057 | if( sscanf(col[5], "%u", &myint ) < 1 ) { die; } 1058 | else { 1059 | newacl->deny = myint; 1060 | } 1061 | if( sscanf(col[6], "%u", &myint ) < 1 ) { die; } 1062 | else { 1063 | newacl->trustpass = myint; 1064 | } 1065 | 1066 | /* free space */ 1067 | for(i=0; i<NUMELEM; i++) { 1068 | wr_free(col[i]); 1069 | } 1070 | 1071 | /* now add to the tree */ 1072 | ret_err = rx_bin_node( RX_OPER_CRE, &mypref, 1073 | act_acl, (rx_dataleaf_t *) newacl ); 1074 | } 1075 | } /* while row */ 1076 | 1077 | TH_release_write_lock( &(act_acl->rwlock) ); 1078 | 1079 | SQ_free_result(result); 1080 | /* Close connection */ 1081 | SQ_close_connection(con); 1082 | 1083 | return ret_err; 1084 | } /* AC_acc_load */ 1085 | 1086 | 1087 | 1088 | /*++++++++++++++++++++++++++++++++++++++ 1089 | AC_build: 1090 | 1091 | creates empty trees for accounting/acl. 1092 | 1093 | returns error code from RX_tree_cre or OK. 1094 | (XXX): just now only bails out when encounters problems. 1095 | ++++++++++++++++++++++++++++++++++++++*/ 1096 | er_ret_t AC_build(void) 1097 | { 1098 | /* create trees */ 1099 | if ( RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 1100 | RX_SUB_NONE, &act_runtime) != RX_OK 1101 | || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 1102 | RX_SUB_NONE, &act_hour) != RX_OK 1103 | || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 1104 | RX_SUB_NONE, &act_minute) != RX_OK 1105 | || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 1106 | RX_SUB_NONE, &act_acl) != RX_OK 1107 | ) 1108 | die; /*can be changed to an error and handled ... some day */ 1109 | 1110 | return RX_OK; 1111 | } 1112 | 1113 | /*++++++++++++++++++++++++++++++++++++++ 1114 | AC_rxwalkhook_print: 1115 | 1116 | action performed on a single account node 1117 | when listing the contents of the access tree: format and print the 1118 | data from this node. 1119 | 1120 | Conforms to rx_walk_tree interface, therefore some of the 1121 | arguments do not apply and are not used. 1122 | 1123 | rx_node_t *node - pointer to the node of the radix tree 1124 | int level - n/a 1125 | int nodecounter - n/a 1126 | void *con - pointer to the connection structure (prints to it) 1127 | 1128 | returns always OK 1129 | +++++++++++++++++++++++++++++++++++++++*/ 1130 | er_ret_t AC_rxwalkhook_print(rx_node_t *node, 1131 | int level, int nodecounter, 1132 | void *con) 1133 | { 1134 | char adstr[IP_ADDRSTR_MAX]; 1135 | char line[1024]; 1136 | char *dat; 1137 | 1138 | 1139 | if( IP_addr_b2a(&(node->prefix.ip), adstr, IP_ADDRSTR_MAX) != IP_OK ) { 1140 | die; /* program error. */ 1141 | } 1142 | 1143 | sprintf(line, "%-20s %s\n", adstr, 1144 | dat=AC_to_string( node->leaves_ptr )); 1145 | wr_free(dat); 1146 | 1147 | SK_cd_puts((sk_conn_st *)con, line); 1148 | return RX_OK; 1149 | } /* AC_rxwalkhook_print */ 1150 | 1151 | 1152 | /*++++++++++++++++++++++++++++++++++++++ 1153 | AC_rxwalkhook_print_acl: 1154 | 1155 | action performed on a single account node 1156 | when listing the contents of the acl tree: format and print the 1157 | data from this node. 1158 | 1159 | Conforms to rx_walk_tree interface, therefore some of the 1160 | arguments do not apply and are not used. 1161 | 1162 | rx_node_t *node - pointer to the node of the radix tree 1163 | int level - n/a 1164 | int nodecounter - n/a 1165 | void *con - pointer to the connection structure (prints to it) 1166 | 1167 | returns always OK 1168 | +++++++++++++++++++++++++++++++++++++++*/ 1169 | er_ret_t AC_rxwalkhook_print_acl(rx_node_t *node, 1170 | int level, int nodecounter, 1171 | void *con) 1172 | { 1173 | char prefstr[IP_PREFSTR_MAX]; 1174 | char line[1024]; 1175 | char *dat; 1176 | 1177 | 1178 | if( IP_pref_b2a(&(node->prefix), prefstr, IP_PREFSTR_MAX) != IP_OK ) { 1179 | die; /* program error. */ 1180 | } 1181 | 1182 | sprintf(line, "%-20s %s\n", prefstr, 1183 | dat=AC_acl_to_string( node->leaves_ptr )); 1184 | wr_free(dat); 1185 | 1186 | SK_cd_puts((sk_conn_st *)con, line); 1187 | return RX_OK; 1188 | }/* AC_rxwalkhook_print_acl */ 1189 | 1190 | /*++++++++++++++++++++++++++++++++++++++ 1191 | AC_count_object: 1192 | 1193 | accounts an objects in the credit accordingly to its type, 1194 | or sets denial if the limit is defined and the credit is exceeded. 1195 | 1196 | type - object type 1197 | credit - pointer to the credit structure (gets modified) 1198 | 1199 | */ 1200 | void 1201 | AC_count_object( acc_st *acc_credit, 1202 | acl_st *acl, 1203 | int private ) 1204 | { 1205 | if( private ) { 1206 | if( acc_credit->private_objects <= 0 && acl->maxprivate != -1 ) { 1207 | /* must be negative - will be subtracted */ 1208 | acc_credit->denials = -1; 1209 | } else { 1210 | acc_credit->private_objects --; 1211 | } 1212 | } 1213 | else { 1214 | if( acc_credit->public_objects <= 0 && acl->maxpublic != -1 ) { 1215 | acc_credit->denials = -1; 1216 | } else { 1217 | acc_credit->public_objects --; 1218 | } 1219 | } 1220 | } /* AC_count_object */ 1221 | 1222 | 1223 | /*++++++++++++++++++++++++++++++++++++++ 1224 | AC_credit_isdenied: 1225 | checks the denied flag in credit (-1 or 1 => denied) 1226 | 1227 | credit - pointer to the credit structure 1228 | +*/ 1229 | int 1230 | AC_credit_isdenied(acc_st *acc_credit) 1231 | { 1232 | return (acc_credit->denials != 0); 1233 | } /* AC_credit_isdenied */ 1234 | 1235 | 1236 | /*++++++++++++++++++++++++++++++++++++++ 1237 | AC_get_higher_limit: 1238 | 1239 | returns the higher number of the two acl limits: maxprivate & maxpublic 1240 | corrected w.r.t the current credit left, 1241 | or unlimited if any of them is 'unlimited'. 1242 | +*/ 1243 | int 1244 | AC_get_higher_limit(acc_st *acc_credit, 1245 | acl_st *acl) 1246 | { 1247 | if( acl->maxprivate == -1 || acl->maxpublic == -1 ) { 1248 | return -1; 1249 | } 1250 | else { 1251 | int a = acc_credit->private_objects; 1252 | int b = acc_credit->public_objects; 1253 | 1254 | return (a > b ? a : b); 1255 | } 1256 | }