Mercurial > hg > waaad
view extensions/radius_gw/rgw_extensions.c @ 418:8155408c6dc5
Added early handling of Diameter answers
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Tue, 16 Jun 2009 15:38:18 +0900 |
parents | 540ed390c04f |
children | fc0d723c1f8b |
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_GLOBAL); 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 && 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; if (ext->api.rga_rad_req_cb) { 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; } else { TRACE_DEBUG(ANNOYING, "Skipping extension '%s' (NULL callback)", ext->extname); } } /* 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, there won't be a reply message to retrieve the data */ if (*session) { CHECK_FCT_DO( sess_destroy(session), ); } /* Send the radius message back if required */ if ((ret == -2) && rad_ans && rad) { CHECK_FCT_DO( rgw_client_finish_send(&rad_ans, *rad, cli), /* It failed, it can't be helped... */); } 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; } /* Loop in the extension list (same as req) to convert data from diam_ans to rad_ans */ int rgw_extensions_loop_ans(struct rgw_radius_msg_meta *req, sess_id_t *session, msg_t **diam_ans, struct radius_msg ** rad_ans, struct rgw_client * cli) { int ret = 0; struct rg_list * head = NULL, *li; TRACE_ENTRY("%p %p %p %p %p", req, session, diam_ans, rad_ans, cli); CHECK_PARAMS( req && session && diam_ans && *diam_ans && rad_ans && *rad_ans && cli); /* Get the list of extensions of the RADIUS request */ CHECK_FCT( get_accelerator(&head, req->radius.hdr->code, req->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; if (ext->api.rga_diam_ans_cb) { TRACE_DEBUG(ANNOYING, "Calling next extension: %s", ext->extname); ret = (*ext->api.rga_diam_ans_cb)(ext->cs, session, diam_ans, rad_ans, (void *)cli); if (ret) break; } else { TRACE_DEBUG(ANNOYING, "Skipping extension '%s' (NULL callback)", ext->extname); } } /* If no error encountered, we're done here */ if (ret == 0) return 0; /* Destroy the temporary RADIUS answer */ if (*rad_ans) { radius_msg_free(*rad_ans); free(*rad_ans); *rad_ans = NULL; } if (ret > 0) { /* Critical error, log and exit */ log_error("An error occurred while handling a DIAMETER answer to a converted RADIUS request, turn on DEBUG for details: %s\n", strerror(ret)); return ret; } /* We might define other return values with special meaning here (ret == -1, ...) for example create a new Diameter request */ 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), ); }