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 |