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  |