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