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