Source File modules/ac/access_control.c

    1    | /***************************************
    2    |   $Revision: 1.5 $
    3    |
    4    |   Access control module (ac).
    5    |
    6    |   Status: NOT REVUED, NOT TESTED
    7    |
    8    |   ******************/ /******************
    9    |   Filename            : access_control.c
    10   |   Author              : ottrey@ripe.net
    11   |   OSs Tested          : Solaris
    12   |   ******************/ /******************
    13   |   Copyright (c) 1999                              RIPE NCC
    14   |
    15   |   All Rights Reserved
    16   |
    17   |   Permission to use, copy, modify, and distribute this software and its
    18   |   documentation for any purpose and without fee is hereby granted,
    19   |   provided that the above copyright notice appear in all copies and that
    20   |   both that copyright notice and this permission notice appear in
    21   |   supporting documentation, and that the name of the author not be
    22   |   used in advertising or publicity pertaining to distribution of the
    23   |   software without specific, written prior permission.
    24   |
    25   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
    26   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
    27   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
    28   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
    29   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    30   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    31   |   ***************************************/
    32   | #include <stdio.h>
    33   | #include <glib.h>
    34   |
    35   | #define AC_IMPL
    36   | #include "rxroutines.h"
    37   | #include "erroutines.h"
    38   | #include "access_control.h"
    39   | #include "socket.h"
    40   | #include "mysql_driver.h"
    41   | #include "constants.h"
    42   |
    43   | #define AC_DECAY_TIME 10
    44   | /* #define AC_DECAY_TIME 3600 */
    45   |
    46   | /* AC_to_string() */
    47   | /*++++++++++++++++++++++++++++++++++++++
    48   |   Show an access structure
    49   |
    50   |   More:
    51   |   +html+ <PRE>
    52   |   Authors:
    53   |         marek
    54   |   +html+ </PRE><DL COMPACT>
    55   |   +html+ <DT>Online References:
    56   |   +html+ </UL></DL>
    57   |
    58   |   ++++++++++++++++++++++++++++++++++++++*/
    59   | char *AC_to_string(GList *leafptr)
    60   | {
    61   |   char *result_buf;
    62   |   acc_st *a = leafptr->data;
    63   |
    64   |   if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
    65   |       /* do many bad things...*/
    66   |       return NULL;
    67   |     }
    68   |
    69   |   if( a != NULL ) {
    70   |     sprintf(result_buf,
    71   |             "conn %d\tden %d\tqrs %d\tpub %d\tpriv %d\tbonus %d",
    72   |             a->connections,
    73   |             a->denials,
    74   |             a->queries,
    75   |             a->public_objects,
    76   |             a->private_objects,
    77   |             a->private_bonus
    78   |             );
    79   |   }
    80   |   else {
    81   |     strcpy(result_buf, "DATA MISSING\n");
    82   |   }
    83   |
    84   |   return result_buf;
    85   | } /* AC_to_string() */
    86   |
    87   | /* AC_acl_to_string() */
    88   | /*++++++++++++++++++++++++++++++++++++++
    89   |   Show an access control list structure
    90   |
    91   |   More:
    92   |   +html+ <PRE>
    93   |   Authors:
    94   |         marek
    95   |   +html+ </PRE><DL COMPACT>
    96   |   +html+ <DT>Online References:
    97   |   +html+ </UL></DL>
    98   |
    99   |   ++++++++++++++++++++++++++++++++++++++*/
    100  | char *AC_acl_to_string(GList *leafptr)
    101  | {
    102  |   char *result_buf;
    103  |   acl_st *a = leafptr->data;
    104  |
    105  |   if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
    106  |       /* do many bad things...*/
    107  |       return NULL;
    108  |     }
    109  |
    110  |   if( a != NULL ) {
    111  |     sprintf(result_buf,
    112  |             "maxbonus %d\tmaxdenials %d\tdeny %d\ttrustpass %d",
    113  |             a->maxbonus,
    114  |             a->maxdenials,
    115  |             a->deny,
    116  |             a->trustpass
    117  |             );
    118  |   }
    119  |   else {
    120  |     strcpy(result_buf, "DATA MISSING\n");
    121  |   }
    122  |
    123  |   return result_buf;
    124  | } /* AC_acl_to_string() */
    125  |
    126  | /* AC_fetch_acc() */
    127  | /*++++++++++++++++++++++++++++++++++++++
    128  |   Find the runtime accounting record for this IP,
    129  |   store a copy of it in acc_store.
    130  |
    131  |   More:
    132  |   +html+ <PRE>
    133  |   Authors:
    134  |         marek
    135  |   +html+ </PRE><DL COMPACT>
    136  |   +html+ <DT>Online References:
    137  |   +html+ </UL></DL>
    138  |
    139  |   ++++++++++++++++++++++++++++++++++++++*/
    140  | er_ret_t AC_fetch_acc( ip_addr_t *addr, acc_st *acc_store, int tmout)
    141  | {
    142  |   GList    *datlist=NULL;
    143  |   rx_datref_t *datref;
    144  |   er_ret_t ret_err;
    145  |   ip_prefix_t prefix;
    146  |
    147  |   prefix.ip = *addr;
    148  |   prefix.bits = IP_sizebits(addr->space);
    149  |   TH_acquire_read_lock( &(act_runtime->rwlock) );
    150  |
    151  |   if( (ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, act_runtime,
    152  |                                &prefix, &datlist, RX_ANS_ALL)) == RX_OK ) {
    153  |     switch( g_list_length(datlist) ) {
    154  |     case 0:
    155  |       memset(acc_store, 0, sizeof(acc_st));
    156  |       break;
    157  |     case 1:
    158  |       datref = (rx_datref_t *) g_list_nth_data(datlist,0);
    159  |       memcpy(acc_store, (acc_st *) datref->leafptr, sizeof(acc_st));
    160  |       break;
    161  |     default: die;
    162  |     }
    163  |   }
    164  |
    165  |   TH_release_read_lock( &(act_runtime->rwlock) );
    166  |
    167  | return -1;
    168  | }/* AC_fetch_acc() */
    169  |
    170  | /* AC_check_acl() */
    171  | /*++++++++++++++++++++++++++++++++++++++
    172  |
    173  |   AC_check_acl:
    174  |
    175  |   search for this ip or other applicable record in the access control tree
    176  |
    177  |   if( bonus in combined runtime+connection accountings > max_bonus in acl)
    178  |             set denial in the acl for this ip (create if needed)
    179  |   if( combined denialcounter > max_denials in acl)
    180  |             set the permanent ban in acl; save in SQL too
    181  |   calculate credit if pointer provided
    182  |   save the access record (ip if created or found/prefix otherwise)
    183  |             at *acl_store if provided
    184  |
    185  |   any of the args except address can be NULL
    186  |
    187  |   More:
    188  |   +html+ <PRE>
    189  |   Authors:
    190  |         marek
    191  |   +html+ </PRE><DL COMPACT>
    192  |   +html+ <DT>Online References:
    193  |   +html+ </UL></DL>
    194  |
    195  |   ++++++++++++++++++++++++++++++++++++++*/
    196  | er_ret_t AC_check_acl( ip_addr_t *addr,
    197  |                        acc_st *run_acc,
    198  |                        acc_st *query_acc,
    199  |                        acc_st *credit_acc,
    200  |                        acl_st *acl_store
    201  |                        )
    202  | {
    203  |   GList    *datlist=NULL;
    204  |   ip_prefix_t prefix;
    205  |   er_ret_t ret_err;
    206  |   acl_st *acl_record;
    207  |   rx_datref_t *datref;
    208  |   /* will write to the tree only if run_acc or query_acc are provided */
    209  |   int writetoacl = (run_acc != NULL  ||  query_acc != NULL);
    210  |
    211  |   prefix.ip = *addr;
    212  |   prefix.bits = IP_sizebits(addr->space);
    213  |
    214  |   /* lock the tree accordingly */
    215  |   if(writetoacl) {
    216  |     TH_acquire_write_lock( &(act_acl->rwlock) );
    217  |   } else {
    218  |     TH_acquire_read_lock( &(act_acl->rwlock) );
    219  |   }
    220  |
    221  |   /* find a record */
    222  |   if( (ret_err = RX_bin_search(RX_SRCH_EXLESS, 0, 0, act_acl,
    223  |                                &prefix, &datlist, RX_ANS_ALL)
    224  |        ) != RX_OK   ||  g_list_length(datlist) == 0 ) {
    225  |     /* acl tree is not configured at all ! There always must be a
    226  |        catch-all record with defaults */
    227  |     die;
    228  |   }
    229  |
    230  |
    231  |   datref = (rx_datref_t *)g_list_nth_data(datlist,0);
    232  |   acl_record = (acl_st *)  datref->leafptr;
    233  |
    234  |   if( run_acc && credit_acc ) {
    235  |     memset( credit_acc, 0, sizeof(acc_st));
    236  |     credit_acc->public_objects = -1; /* unlimited */
    237  |     credit_acc->private_objects
    238  |       = acl_record->maxbonus - run_acc->private_bonus;
    239  |   }
    240  |
    241  |   /* copy the acl record if asked for it*/
    242  |   if( acl_store ) {
    243  |     *acl_store =  *acl_record;
    244  |   }
    245  |
    246  |   /* release lock */
    247  |   if(writetoacl) {
    248  |     TH_release_write_lock( &(act_acl->rwlock) );
    249  |   } else {
    250  |     TH_release_read_lock( &(act_acl->rwlock) );
    251  |   }
    252  |
    253  |   /*
    254  |     if( ret_err == RX_OK ) {
    255  |     ret_err = AC_OK;
    256  |     }
    257  |   */
    258  |   return ret_err;
    259  | }
    260  |
    261  | void AC_acc_addup(acc_st *a, acc_st *b, int minus)
    262  | {
    263  |   int mul = minus ? -1 : 1;
    264  |
    265  |   /* add all counters from b to those in a */
    266  |   a->connections     +=  mul * b->connections;
    267  |   a->denials         +=  mul * b->denials;
    268  |   a->queries         +=  mul * b->queries;
    269  |   a->public_objects  +=  mul * b->public_objects;
    270  |   a->private_objects +=  mul * b->private_objects;
    271  |   a->private_bonus   +=  mul * b->private_bonus;
    272  | }
    273  |
    274  |
    275  | er_ret_t AC_commit(ip_addr_t *addr, acc_st *acc_conn) {
    276  |   /* for all accounting trees: XXX runtime only for the moment
    277  |      lock tree (no mercy :-)
    278  |        find or create entries,
    279  |        increase accounting values by the values from connection acc
    280  |        reset the connection acc
    281  |      unlock accounting trees
    282  |   */
    283  |   GList    *datlist=NULL;
    284  |   acc_st   *recacc;
    285  |   er_ret_t ret_err;
    286  |   ip_prefix_t prefix;
    287  |
    288  |   prefix.ip = *addr;
    289  |   prefix.bits = IP_sizebits(addr->space);
    290  |
    291  |   TH_acquire_write_lock( &(act_runtime->rwlock) );
    292  |
    293  |   if( (ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, act_runtime,
    294  |                                &prefix, &datlist, RX_ANS_ALL)) == RX_OK ) {
    295  |     switch( g_list_length(datlist) ) {
    296  |     case 0:
    297  |       /* need to create a new accounting record */
    298  |       if( (ret_err = wr_malloc( (void **)& recacc, sizeof(acc_st))) == UT_OK ) {
    299  |         /*  counters = connection counters */
    300  |         memcpy( recacc, acc_conn, sizeof(acc_st));
    301  |
    302  |         /* attach. The recacc is to be treated as a dataleaf
    303  |            (should work on lower levels than RX_asc_*)
    304  |         */
    305  |         ret_err = RX_bin_node( RX_OPER_CRE, &prefix,
    306  |                                act_runtime, (rx_dataleaf_t *)recacc );
    307  |       }
    308  |       break;
    309  |     case 1:
    310  |       {
    311  |         rx_datref_t *datref = (rx_datref_t *) g_list_nth_data( datlist,0 );
    312  |
    313  |         /* OK, there is a record already, add to it */
    314  |         recacc = (acc_st *) datref->leafptr;
    315  |         AC_acc_addup(recacc, acc_conn, ACC_PLUS);
    316  |       }
    317  |       break;
    318  |     default: die; /* there shouldnt be more than 1 entry per IP */
    319  |     }
    320  |   }
    321  |
    322  |   TH_release_write_lock( &(act_runtime->rwlock) );
    323  |   return ret_err;
    324  | }
    325  |
    326  | er_ret_t AC_decay_hook(rx_node_t *node, int level, int nodecounter, void *con) {
    327  |   acc_st *a = node->leaves_ptr->data;
    328  |
    329  |   a->private_bonus *= 0.95;
    330  |
    331  |   return RX_OK;
    332  | } /* AC_decay_hook() */
    333  |
    334  | er_ret_t AC_decay(void) {
    335  |   er_ret_t ret_err;
    336  |
    337  |   /* XXX
    338  |      This should be run as a detatched thread.
    339  |      Yes the while(1) is crappy b/c there's no way of stopping it,
    340  |      but it's Friday night & everyone has either gone off for
    341  |      Christmas break or is down at the pub so it's staying as a while(1)!
    342  |      And I'm not sure what effect the sleep() will have on the thread.
    343  |   */
    344  |   while(1) {
    345  |
    346  |     TH_acquire_write_lock( &(act_runtime->rwlock) );
    347  |
    348  |     if( act_runtime->top_ptr != NULL ) {
    349  |        rx_walk_tree(act_runtime->top_ptr, AC_decay_hook,
    350  |                          RX_WALK_SKPGLU,  /* skip glue nodes */
    351  |                          255, 0, 0, NULL, &ret_err);
    352  |     }
    353  |
    354  |     /* it should also be as smart as to delete nodes that have reached
    355  |        zero, otherwise the whole of memory will be filled.
    356  |        Next release :-)
    357  |     */
    358  |
    359  |     TH_release_write_lock( &(act_runtime->rwlock) );
    360  |
    361  |     printf("AC: decaying access tree. (Every %d seconds)\n", AC_DECAY_TIME);
    362  |
    363  |     sleep(AC_DECAY_TIME);
    364  |   }
    365  |
    366  |   return ret_err;
    367  | } /* AC_decay() */
    368  |
    369  | er_ret_t AC_acc_load(void)
    370  | {
    371  |   SQ_connection_t *con=NULL;
    372  |   SQ_result_set_t *result;
    373  |   SQ_row_t *row;
    374  |   er_ret_t ret_err = RX_OK;
    375  |
    376  |   if( (con = SQ_get_connection(CO_get_host(), CO_get_database_port(),
    377  |                         "RIPADMIN", CO_get_user(), CO_get_password() )
    378  |        ) == NULL ) {
    379  |     fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
    380  |     die;
    381  |   }
    382  |
    383  |   if( (result = SQ_execute_query(SQ_STORE, con, "SELECT * FROM acl"))
    384  |       == NULL ) {
    385  |     fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
    386  |     die;
    387  |   }
    388  |
    389  |   TH_acquire_write_lock( &(act_acl->rwlock) );
    390  |
    391  |   while ( (row = SQ_row_next(result)) != NULL && ret_err == RX_OK) {
    392  |     ip_prefix_t mypref;
    393  |     acl_st *newacl;
    394  |     char *col[6];
    395  |     unsigned myint;
    396  |     int i;
    397  |
    398  |     memset(&mypref, 0, sizeof(ip_prefix_t));
    399  |     mypref.ip.space = IP_V4;
    400  |
    401  |     if( (ret_err = wr_malloc( (void **)& newacl, sizeof(acl_st))
    402  |          ) == UT_OK ) {
    403  |
    404  |       for(i=0; i<6; i++) {
    405  |         if ( (col[i] = SQ_get_column_string(result, row, i)) == NULL) {
    406  |           die;
    407  |         }
    408  |       }
    409  |
    410  |       /* prefix ip */
    411  |       if( sscanf(col[0], "%u", &mypref.ip.words[0] ) < 1 ) { die; }
    412  |
    413  |       /* prefix length */
    414  |       if( sscanf(col[1], "%u", &mypref.bits ) < 1 ) { die; }
    415  |
    416  |       /* acl contents */
    417  |       if( sscanf(col[2], "%u",  & (newacl->maxbonus)   ) < 1 ) { die; }
    418  |       if( sscanf(col[3], "%hd", & (newacl->maxdenials) ) < 1 ) { die; }
    419  |
    420  |       /* these are chars therefore cannot read directly */
    421  |       if( sscanf(col[4], "%u", &myint              ) < 1 ) { die; }
    422  |       else {
    423  |         newacl->deny = myint;
    424  |       }
    425  |       if( sscanf(col[5], "%u", &myint  ) < 1 ) { die; }
    426  |       else {
    427  |         newacl->trustpass = myint;
    428  |       }
    429  |
    430  |       /* now add to the tree */
    431  |
    432  |       ret_err = RX_bin_node( RX_OPER_CRE, &mypref,
    433  |                              act_acl, (rx_dataleaf_t *) newacl );
    434  |     }
    435  |   } /* while row */
    436  |
    437  |   TH_release_write_lock( &(act_acl->rwlock) );
    438  |
    439  |   SQ_free_result(result);
    440  |   /* Close connection */
    441  |   SQ_close_connection(con);
    442  |
    443  |   /* Start the decay thread. */
    444  |   TH_run2((void *)AC_decay);
    445  |
    446  |   return ret_err;
    447  | }
    448  |
    449  | er_ret_t AC_build(void)
    450  | {
    451  |   /* create trees */
    452  |   if (   RX_tree_cre(0, IP_V4, RX_FAM_IP, "0.0.0.0/0", RX_MEM_RAMONLY,
    453  |                   RX_SUB_NONE, &act_runtime) != RX_OK
    454  |       || RX_tree_cre(0, IP_V4, RX_FAM_IP, "0.0.0.0/0", RX_MEM_RAMONLY,
    455  |                   RX_SUB_NONE, &act_hour) != RX_OK
    456  |       || RX_tree_cre(0, IP_V4, RX_FAM_IP, "0.0.0.0/0", RX_MEM_RAMONLY,
    457  |                   RX_SUB_NONE, &act_minute) != RX_OK
    458  |       || RX_tree_cre(0, IP_V4, RX_FAM_IP, "0.0.0.0/0", RX_MEM_RAMONLY,
    459  |                   RX_SUB_NONE, &act_acl) != RX_OK
    460  |          )
    461  |     die;
    462  | }
    463  |
    464  | er_ret_t AC_rxwalkhook_print(rx_node_t *node,
    465  |                              int level, int nodecounter,
    466  |                              void *con)
    467  | {
    468  |   char adstr[IP_ADDRSTR_MAX];
    469  |   char line[1024];
    470  |   char *dat;
    471  |
    472  |
    473  |     if( IP_addr_b2a(&(node->prefix.ip), adstr, IP_ADDRSTR_MAX) != IP_OK ) {
    474  |       die; /* program error. */
    475  |     }
    476  |
    477  |     sprintf(line, "%-20s %s\n", adstr,
    478  |             dat=AC_to_string( node->leaves_ptr ));
    479  |     wr_free(dat);
    480  |
    481  |     SK_cd_puts((sk_conn_st *)con, line);
    482  |     return RX_OK;
    483  | }
    484  |
    485  | er_ret_t AC_rxwalkhook_print_acl(rx_node_t *node,
    486  |                              int level, int nodecounter,
    487  |                              void *con)
    488  | {
    489  |   char prefstr[IP_PREFSTR_MAX];
    490  |   char line[1024];
    491  |   char *dat;
    492  |
    493  |
    494  |     if( IP_pref_b2a(&(node->prefix), prefstr, IP_PREFSTR_MAX) != IP_OK ) {
    495  |       die; /* program error. */
    496  |     }
    497  |
    498  |     sprintf(line, "%-20s %s\n", prefstr,
    499  |             dat=AC_acl_to_string( node->leaves_ptr ));
    500  |     wr_free(dat);
    501  |
    502  |     SK_cd_puts((sk_conn_st *)con, line);
    503  |     return RX_OK;
    504  | }
    505  |