modules/ac/access_control.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. AC_to_string_header
  2. AC_to_string
  3. AC_credit_to_string
  4. AC_acl_to_string_header
  5. AC_acl_to_string
  6. AC_findexless_acl_l
  7. AC_findcreate_acl_l
  8. AC_findcreate_account_l
  9. AC_fetch_acc
  10. AC_check_acl
  11. AC_acc_addup
  12. AC_commit_credit
  13. AC_dbopen_admin
  14. AC_acl_sql
  15. AC_ban_set
  16. AC_asc_ban_set
  17. AC_asc_all_set
  18. AC_asc_acl_command_set
  19. AC_commit
  20. AC_decay_hook
  21. AC_decay
  22. AC_acc_load
  23. AC_build
  24. AC_rxwalkhook_print
  25. AC_rxwalkhook_print_acl
  26. AC_count_object
  27. AC_credit_isdenied
  28. AC_get_higher_limit

   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) 
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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 )
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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[])
     /* [<][>][^][v][top][bottom][index][help] */
 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 )
     /* [<][>][^][v][top][bottom][index][help] */
 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) { 
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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) {
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
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) 
     /* [<][>][^][v][top][bottom][index][help] */
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, 
     /* [<][>][^][v][top][bottom][index][help] */
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, 
     /* [<][>][^][v][top][bottom][index][help] */
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, 
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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, 
     /* [<][>][^][v][top][bottom][index][help] */
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 }

/* [<][>][^][v][top][bottom][index][help] */