Mercurial > hg > waaad
view extensions/radius_gw/rgw_extensions.c @ 385:03b512313cc1
Added code to handle sessions
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Thu, 28 May 2009 13:32:37 +0900 |
parents | f8509d27e453 |
children | 1a4902b216f8 |
line wrap: on
line source
/********************************************************************************************************* * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@nict.go.jp> * * * * Copyright (c) 2009, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * * permitted provided that the following conditions are met: * * * * * Redistributions of source code must retain the above * * copyright notice, this list of conditions and the * * following disclaimer. * * * * * Redistributions in binary form must reproduce the above * * copyright notice, this list of conditions and the * * following disclaimer in the documentation and/or other * * materials provided with the distribution. * * * * * Neither the name of the WIDE Project or NICT nor the * * names of its contributors may be used to endorse or * * promote products derived from this software without * * specific prior written permission of WIDE Project and * * NICT. * * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *********************************************************************************************************/ /* Manage the list of sub-extensions that provide handlers for RADIUS messages / attributes */ #include "radius_gw.h" #include <dlfcn.h> #include <libgen.h> /* List of the extensions, in the order they are written in the configuration file. */ static struct rg_list ext_list; /* Description of an extension entry */ struct ext_descr { struct rg_list chain; /* chaining in the ext_list list */ void * dlo; /* object returned by dlopen for the extension, to use with dlclose later */ struct radius_gw_api api; /* the callbacks registered by rga_register */ struct rga_conf_state * cs; /* the configuration and state returned by rga_conf_parse_cb */ int type; /* this extension is called for messages received on this(these) server(s) only */ unsigned char * cc; /* array of command codes, or NULL for any cc */ size_t cc_len; /* size of the previous array */ char * extname; /* baseame of the extension, for debug messages. To be freed when object is detroyed */ char * conffile; /* configuration file passed to the extension, or "(null)". To be freed when object is destroyed */ }; /* Accelerators for each command code (one for each port). These accelerators are built on-demand, as a cache, after start_cache function has been called. */ static struct rg_list ext_accel_auth, ext_accel_acct; /* accelerator list, one per command code value (only the ones actually used) */ struct ext_accel { struct rg_list chain; /* link in the ext_accel_* list */ unsigned char ccode; /* the command code of this accelerator. The previous list is ordered according to this value. We don't handle extended CC yet */ struct rg_list extensions; /* head for the list of extensions to be called for this command code. List of ext_accel_item items */ }; /* accelerator item, references to extensions */ struct ext_accel_item { struct rg_list chain; /* link in the ext_accel "extensions" list */ struct ext_descr * ext; /* pointer to the extension data */ }; /* Mutex to protect all the previous lists */ static pthread_mutex_t ext_mtx = PTHREAD_MUTEX_INITIALIZER; /* Has start_cache been called? */ static int cache_started = 0; /* The lock must be held before calling this function */ static int get_accelerator(struct rg_list ** list, unsigned char ccode, int type) { struct rg_list *refer, *search; struct ext_accel * accel = NULL; struct ext_accel_item * item = NULL; TRACE_ENTRY("%p %hhu %i", list, ccode, type); CHECK_PARAMS( cache_started && list && ((type == RGW_EXT_TYPE_AUTH) || (type == RGW_EXT_TYPE_ACCT)) ); if (type == RGW_EXT_TYPE_AUTH) refer = &ext_accel_auth; else refer = &ext_accel_acct; /* Check if we have already an accelerator for this ccode */ for (search = refer->next; search != refer; search = search->next) { struct ext_accel * loc = (struct ext_accel *)search; if (loc->ccode < ccode) continue; if (loc->ccode > ccode) break; /* we don't have an accelerator for this value yet */ /* We found the matching accelerator, just return this list */ *list = &loc->extensions; return 0; } /* We must create the accelerator list, then save it just before "search" */ CHECK_MALLOC( accel = malloc(sizeof(struct ext_accel)) ); memset(accel, 0, sizeof(struct ext_accel) ); rg_list_init(&accel->chain); rg_list_init(&accel->extensions); accel->ccode = ccode; /* Check each extension from the global list for this port and ccode */ for (refer = ext_list.next; refer != &ext_list; refer = refer->next) { struct ext_descr * loc = (struct ext_descr *)refer; /* Skip if this extension is not registered for this port */ if (! (loc->type & type) ) continue; /* Check if the ccode is there */ if (loc->cc) { int i; int match = 0; for (i=0; i< loc->cc_len; i++) { if (loc->cc[i] < ccode) continue; if (loc->cc[i] == ccode) match = 1; break; } if (!match) continue; } /* Ok, this extension must be called for this port / ccode, add to the accelerator */ CHECK_MALLOC( item = malloc(sizeof(struct ext_accel_item)) ); memset(item, 0, sizeof(struct ext_accel_item)); rg_list_init(&item->chain); item->ext = loc; /* Add as last element of the accelerator */ rg_list_insert_before(&accel->extensions, &item->chain); } /* Now, save this accelerator entry in the global list */ rg_list_insert_before(search, &accel->chain); *list = &accel->extensions; return 0; } int rgw_extensions_init(void) { TRACE_ENTRY(); rg_list_init(&ext_list); rg_list_init(&ext_accel_auth); rg_list_init(&ext_accel_acct); return 0; } int rgw_extensions_add( char * extfile, char * conffile, int type, unsigned char ** codes_array, size_t codes_sz ) { struct ext_descr * new; int (* ext_rga_register)(int version, waaad_api_t * waaad, struct radius_gw_api * api); int ret = 0; char * myextfile; TRACE_ENTRY(); CHECK_PARAMS( extfile && type && codes_array && (cache_started == 0) ); CHECK_MALLOC( myextfile = strdup(extfile) ); CHECK_MALLOC( new = malloc(sizeof(struct ext_descr)) ); memset(new, 0, sizeof(struct ext_descr)); rg_list_init(&new->chain); /* Copy names, for debug */ CHECK_MALLOC( new->extname = strdup(basename(myextfile)) ); free(myextfile); CHECK_MALLOC( new->conffile = conffile ? conffile : strdup("(null)") ); /* Try and load the extension */ TRACE_DEBUG(FULL, "Loading subextension: %s", extfile); new->dlo = dlopen(extfile, RTLD_LAZY | RTLD_LOCAL); if (new->dlo == NULL) { /* An error occured */ log_error("Loading of subextension '%s' failed:\n %s\n", extfile, dlerror()); goto error; } /* Resolve the entry point */ ext_rga_register = dlsym( new->dlo, "rga_register" ); if (ext_rga_register == NULL) { /* An error occured */ log_error("Unable to resolve 'rga_register' in subextension '%s':\n %s\n", extfile, dlerror()); goto error; } /* Call the entry point */ TRACE_DEBUG(FULL, "Calling subextension entry point..."); CHECK_FCT_DO( (*ext_rga_register) (RADIUS_GW_API_VER, waaad_api, &new->api), goto error ); /* Now parse the configuration file, this will initialize all extension states and store it in the returned pointer (the subextensions must be re-entrant) */ TRACE_DEBUG(FULL, "Parsing subext conf file: %s", new->conffile ); new->cs = (*(new->api.rga_conf_parse_cb))(conffile); if (new->cs == NULL) { log_error("An error occurred while parsing configuration parameter for extension '%s' (%s), aborting...\n", new->extname, new->conffile); goto error; } /* Now sort the array (very naive algorithm, but this list is usually small) of command codes and save */ if (*codes_array && codes_sz) { int i; new->cc = *codes_array; *codes_array = NULL; for (i = 0; i < codes_sz - 1; i++) { int j, idx = i, min = new->cc[i]; /* find the smallest remaining element */ for (j = i + 1; j < codes_sz; j++) { if (min > new->cc[j]) { min = new->cc[j]; idx = j; } } /* swap if needed */ if (idx != i) { int tmp = new->cc[i]; new->cc[i] = new->cc[idx]; new->cc[idx] = tmp; } } new->cc_len = codes_sz; } new->type = type; /* And save this new extension in the list */ CHECK_POSIX( pthread_mutex_lock(&ext_mtx) ); rg_list_insert_before(&ext_list, &new->chain); CHECK_POSIX( pthread_mutex_unlock(&ext_mtx) ); return 0; error: if (new && new->dlo) dlclose(new->dlo); if (new) free(new); return EINVAL; } void rgw_extensions_dump(void) { struct ext_descr * ext; struct rg_list * ptr, *ptraccel; if ( ! TRACE_BOOL(FULL) ) return; CHECK_POSIX_DO( pthread_mutex_lock(&ext_mtx), ); if ( ! rg_list_is_empty( &ext_list ) ) log_debug(" --- List of registered sub-extensions:\n"); for (ptr = ext_list.next; ptr != &ext_list; ptr = ptr->next) { char * codes = NULL; ext = (struct ext_descr *)ptr; if (ext->cc) { int i; char * nxt; CHECK_MALLOC_DO( codes = malloc(ext->cc_len * 3 + 1), break ); nxt = codes; for (i = 0; i < ext->cc_len; i++) { sprintf(nxt, "%02hhx ", ext->cc[i]); nxt += 3; } } log_debug(" %-25s ( %-25s ) - types: %s%s, codes: %s\n", ext->extname, basename(ext->conffile), ext->type & RGW_EXT_TYPE_AUTH ? "Au" : " ", ext->type & RGW_EXT_TYPE_ACCT ? "Ac" : " ", ext->cc ? codes : "*"); free(codes); } CHECK_POSIX_DO( pthread_mutex_unlock(&ext_mtx), ); /* Dump the list of accelerators */ if ( ! TRACE_BOOL(FULL + 1) ) return; CHECK_POSIX_DO( pthread_mutex_lock(&ext_mtx), ); if ( !rg_list_is_empty( &ext_accel_auth ) || !rg_list_is_empty( &ext_accel_acct )) log_debug(" --- Accelerators:\n"); for (ptraccel = ext_accel_auth.next; ptraccel != &ext_accel_auth; ptraccel = ptraccel->next) { struct ext_accel * accel = (struct ext_accel *)ptraccel; log_debug(" auth, code %02hhu:\n", accel->ccode); for (ptr = accel->extensions.next; ptr != &accel->extensions; ptr = ptr->next) { struct ext_accel_item * item = (struct ext_accel_item *)ptr; log_debug(" %-15s (%s)\n", item->ext->extname, basename(item->ext->conffile)); } } for (ptraccel = ext_accel_acct.next; ptraccel != &ext_accel_acct; ptraccel = ptraccel->next) { struct ext_accel * accel = (struct ext_accel *)ptraccel; log_debug(" acct, code %02hhu:\n", accel->ccode); for (ptr = accel->extensions.next; ptr != &accel->extensions; ptr = ptr->next) { struct ext_accel_item * item = (struct ext_accel_item *)ptr; log_debug(" %-15s (%s)\n", item->ext->extname, basename(item->ext->conffile)); } } CHECK_POSIX_DO( pthread_mutex_unlock(&ext_mtx), ); } void rgw_extensions_start_cache(void) { cache_started++; } int rgw_extensions_loop_req(struct rgw_radius_msg_meta **rad, sess_id_t **session, msg_t **diam_msg, struct rgw_client * cli) { int ret = 0; struct rg_list * head = NULL, *li; struct radius_msg * rad_ans = NULL; TRACE_ENTRY("%p %p %p %p", rad, session, diam_msg, cli); CHECK_PARAMS( rad && *rad && session && *session && diam_msg && *diam_msg && cli); /* First, get the list of extensions for this message */ CHECK_FCT( get_accelerator(&head, (*rad)->radius.hdr->code, (*rad)->serv_type) ); /* Loop in the list of extensions */ for (li = head->next; li != head; li = li->next) { struct ext_descr * ext = ((struct ext_accel_item *) li)->ext; TRACE_DEBUG(ANNOYING, "Calling next extension: %s", ext->extname); ret = (*ext->api.rga_rad_req_cb)(ext->cs, session, &(*rad)->radius, &rad_ans, diam_msg, (void *)cli); if (ret) break; } /* If no error encountered, we're done here */ if (ret == 0) return 0; /* Destroy the Diameter temp message, if any */ if (*diam_msg) { CHECK_FCT_DO( msg_free(*diam_msg, 1), ); *diam_msg = NULL; } /* Destroy the session unless instructed to keep it */ if (*session && (ret != -2) && (ret != -4)) { CHECK_FCT_DO( sess_unlink(*session), ); *session = NULL; } /* Send the radius message back if required */ if (((ret == -3) || (ret == -4)) && rad_ans) { /* destination port: (*rad)->port */ /* destination ip: derived from cli */ /* post-processing: depends on (*rad)->serv_type */ /* message content: rad_ans */ /* not implemented */ ASSERT(0); } if (ret > 0) { /* Critical error, log and exit */ log_error("An error occurred while handling a RADIUS message, turn on DEBUG for details: %s\n", strerror(ret)); return ret; } /* Now, discard the message and return */ rgw_msg_free(rad); return 0; } void rgw_extensions_fini(void) { struct rg_list * item, *subitem; TRACE_ENTRY(); CHECK_POSIX_DO( pthread_mutex_lock(&ext_mtx), ); /* Remove all elements from all accelerators */ while ( ! rg_list_is_empty(&ext_accel_auth) ) { item = ext_accel_auth.next; rg_list_unlink(item); { struct ext_accel * accel = (struct ext_accel *)item; while ( ! rg_list_is_empty(&accel->extensions) ) { subitem = accel->extensions.next; rg_list_unlink(subitem); free(subitem); } } free(item); } while ( ! rg_list_is_empty(&ext_accel_acct) ) { item = ext_accel_acct.next; rg_list_unlink(item); { struct ext_accel * accel = (struct ext_accel *)item; while ( ! rg_list_is_empty(&accel->extensions) ) { subitem = accel->extensions.next; rg_list_unlink(subitem); free(subitem); } } free(item); } /* Now destroy all extensions */ while ( ! rg_list_is_empty(&ext_list) ) { struct ext_descr * ext = (struct ext_descr *) ext_list.next; rg_list_unlink(&ext->chain); free(ext->conffile); free(ext->extname); free(ext->cc); if (ext->cs) (*ext->api.rga_conf_free_cb)(ext->cs); dlclose(ext->dlo); free(ext); } CHECK_POSIX_DO( pthread_mutex_unlock(&ext_mtx), ); }