radius soure code
/* The radiusAAA plugin is a GPL plugin for partysip. Copyright (C) 2002 Aymeric MOIZARD - <jack@atosc.org> Radius client code is taken from XTRADIUS - Cistron project. Remix by Oleksandr Kapitanenko, Porta Software Ltd. - <kapitan@portaone.com> The radiusAAA plugin is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The radiusAAA plugin is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Foobar; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#define _GNU_SOURCE /* needed for asprintf */
#include <partysip/partysip.h> #include "radiusAAA.h" #include <osip/smsg.h> #include <ppl/ppl_uinfo.h> #include <ppl/ppl_dns.h>
#ifdef HAVE_CTYPE_H #include <ctype.h> #endif
/* Radius client stuff */ char *radius_dir = RADIUS_DIR; char *radlog_dir = NULL; int debug_flag = 0; int sockfd;
int i_send_buffer[2048]; int i_recv_buffer[2048]; char *send_buffer = (char *)i_send_buffer; char *recv_buffer = (char *)i_recv_buffer; char vector[AUTH_VECTOR_LEN];
struct sockaddr_in *sin; UINT4 auth_ipaddr; #define MAXPWNAM 32 #define MAXPASS 16
static DICT_ATTR *dictionary_attributes; static DICT_VALUE *dictionary_values; static DICT_VENDOR *dictionary_vendors;
static int vendorno = 1;
#ifdef NOCASE #define DICT_STRCMP strcasecmp #else #define DICT_STRCMP strcmp #endif
radiusAAA_ctx_t *radiusAAA_context = NULL;
extern psp_plugin_t PPL_DECLARE_DATA radiusAAA_plugin;
/* * Find the pair with the mathing attribute */ VALUE_PAIR * pairfind_by_name(VALUE_PAIR *first, char *name) { while(first && strcmp(first->name,name) != 0 ) first = first->next; return first; }
/* * Release the memory used by a list of attribute-value * pairs. */ void pairfree(VALUE_PAIR *pair) { VALUE_PAIR *next; while(pair != NULL) { next = pair->next; free(pair); pair = next; } }
/* * Free an AUTHREQ struct. */ void authfree(AUTH_REQ *authreq) { pairfree(authreq->request); pairfree(authreq->proxy_pairs); pairfree(authreq->server_reply); memset(authreq, 0, sizeof(AUTH_REQ)); free(authreq); }
/* * Write a whole list of A/V pairs. */ void print_attr_list(VALUE_PAIR *pair) { while(pair) { print_attr_val(pair); pair = pair->next; } }
/* * Write a printable version of the attribute-value pair */ void print_attr_val(VALUE_PAIR *request) { DICT_VALUE *dict_valget(); DICT_VALUE *dval; char buffer[32]; u_char *ptr; char *sptr; char decoded[3257]; // buffer for decoding sublist: ~(256-2)/3 * (6+32)+1 + 32 +5 char sub_attr_val[256]; int sub_attr_code, sub_attr_length;
strcpy(decoded,""); switch(request->type) {
/* Sub attributes */ case PW_TYPE_SUB_LIST: ptr = (u_char *)request->strvalue; sptr = (char *)decoded; sptr += sprintf(sptr, "%s = \"", request->name); while( *ptr != '\0' ) { if( ptr != (u_char *)request->strvalue ) sptr += sprintf(sptr, "; "); sub_attr_code = (int)*ptr; sub_attr_length = (int)*(ptr+1); strncpy( sub_attr_val, ptr+2, sub_attr_length-2); *(sub_attr_val+sub_attr_length-2) = '\0'; dval = dict_valget( sub_attr_code, request->name); if(dval != (DICT_VALUE *)NULL) { sptr += sprintf(sptr, "%s = \"%s\"", dval->name, sub_attr_val); } else { sptr += sprintf(sptr, "Unknown-Sub-Attr-%d = \"%s\"", sub_attr_code, sub_attr_val); } ptr += sub_attr_length; } sptr += sprintf(sptr, "\""); break; /* Cisco VoIP vsa support */ case PW_TYPE_CISCO_AVPAIR: ptr = (u_char *)request->strvalue; if( (ptr=(u_char *)strchr((u_char *)request->strvalue,'=')) == NULL ) ptr = (u_char *)request->strvalue; else ptr++; sprintf(decoded,"%s = \"%s\"", request->name, ptr); break; case PW_TYPE_STRING: if( (request->attribute==0x40001 || request->attribute==0x30001) && (ptr=(u_char *)strchr((u_char *)request->strvalue,'='))!=NULL ) { *ptr++='\0'; sprintf(decoded,"%s = \"%s\"", request->strvalue, ptr); *--ptr='='; } else sprintf(decoded,"%s = \"%s\"", request->name, request->strvalue); break; case PW_TYPE_INTEGER: dval = dict_valget(request->lvalue, request->name); if(dval != (DICT_VALUE *)NULL) sprintf(decoded,"%s = \"%s\"", request->name, dval->name); else sprintf(decoded,"%s = \"%ld\"", request->name, (long)request->lvalue); break;
case PW_TYPE_IPADDR: ipaddr2str(buffer, request->lvalue); sprintf(decoded,"%s = \"%s\"", request->name, buffer); break;
case PW_TYPE_DATE: strftime(buffer, sizeof(buffer), "%b %e %Y", localtime((time_t *)&request->lvalue)); sprintf(decoded,"%s = \"%s\"", request->name, buffer); break; default: sprintf(decoded, "Unknown type %d", request->type); break; } OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "radiusAAA plugin: Received attribute: %s\n", decoded)); }
/* * Like strncpy, but always adds \0 */ char *strNcpy(char *dest, char *src, int n) { if (n > 0) strncpy(dest, src, n); else n = 1; dest[n - 1] = 0;
return dest; }
/* * Return an IP address in host long notation from * one supplied in standard dot notation. */ UINT4 ipstr2long(char *ip_str) { char buf[6]; char *ptr; int i; int count; UINT4 ipaddr; int cur_byte; ipaddr = (UINT4)0; for(i = 0;i < 4;i++) { ptr = buf; count = 0; *ptr = '\0'; while(*ip_str != '.' && *ip_str != '\0' && count < 4) { if(!isdigit(*ip_str)) { return((UINT4)0); } *ptr++ = *ip_str++; count++; } if(count >= 4 || count == 0) { return((UINT4)0); } *ptr = '\0'; cur_byte = atoi(buf); if(cur_byte < 0 || cur_byte > 255) { return((UINT4)0); } ip_str++; ipaddr = ipaddr << 8 | (UINT4)cur_byte; } return(ipaddr); }
/* * Check for valid IP address in standard dot notation. */ int good_ipaddr(char *addr) { int dot_count; int digit_count; dot_count = 0; digit_count = 0; while(*addr != '\0' && *addr != ' ') { if(*addr == '.') { dot_count++; digit_count = 0; } else if(!isdigit(*addr)) { dot_count = 5; } else { digit_count++; if(digit_count > 3) { dot_count = 5; } } addr++; } if(dot_count != 3) { return(-1); } else { return(0); } }
/* * Return an IP address in host long notation from a host * name or address in dot notation. */ UINT4 get_ipaddr(char *host) { struct hostent *hp; UINT4 ipstr2long(); if(good_ipaddr(host) == 0) { return(ipstr2long(host)); } else if((hp = gethostbyname(host)) == (struct hostent *)NULL) { return((UINT4)0); } return(ntohl(*(UINT4 *)hp->h_addr)); }
/* * Return an IP address in standard dot notation for the * provided address in host long notation. */ void ipaddr2str(char *buffer, UINT4 ipaddr) { int addr_byte[4]; int i; UINT4 xbyte; for(i = 0;i < 4;i++) { xbyte = ipaddr >> (i*8); xbyte = xbyte & (UINT4)0x000000FF; addr_byte[i] = xbyte; } sprintf(buffer, "%u.%u.%u.%u", addr_byte[3], addr_byte[2], addr_byte[1], addr_byte[0]); }
/* * Free the dictionary_attributes and dictionary_values lists. */ static void dict_free(void) { DICT_ATTR *dattr, *anext; DICT_VALUE *dval, *vnext; DICT_VENDOR *dvend, *enext;
for (dattr = dictionary_attributes; dattr; dattr = anext) { anext = dattr->next; free(dattr); } for (dval = dictionary_values; dval; dval = vnext) { vnext = dval->next; free(dval); } for (dvend = dictionary_vendors; dvend; dvend = enext) { enext = dvend->next; free(dvend); } dictionary_attributes = NULL; dictionary_values = NULL; dictionary_vendors = NULL; vendorno = 1; }
/* * Add vendor to the list. */ static int addvendor(char *name, int value) { DICT_VENDOR *vval;
if ((vval =(DICT_VENDOR *)malloc(sizeof(DICT_VENDOR))) == (DICT_VENDOR *)NULL) {
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: out of memory\n")); return(-1); } strNcpy(vval->vendorname, name, sizeof(vval->vendorname)); vval->vendorpec = value; vval->vendorcode = vendorno++;
/* Insert at front. */ vval->next = dictionary_vendors; dictionary_vendors = vval;
return 0; }
/* * Get the internal code of the vendor based on its PEC. */ int dict_vendorcode(int pec) { DICT_VENDOR *v;
for (v = dictionary_vendors; v; v = v->next) if (v->vendorpec == pec) break;
return v ? v->vendorcode : 0; }
/* * Initialize the dictionary. Read all ATTRIBUTES into * the dictionary_attributes list. Read all VALUES into * the dictionary_values list. */ int dict_init(char *fn) { FILE *dictfd; char dummystr[64]; char namestr[64]; char valstr[64]; char attrstr[64]; char typestr[64]; char vendorstr[64]; int line_no; DICT_ATTR *attr; DICT_VALUE *dval; DICT_VENDOR *v; char buffer[256]; int value; int type; int vendor; int is_attrib; #ifdef ATTRIB_NMC int vendor_usr_seen = 0; int is_nmc = 0; #endif
if (fn == NULL) dict_free();
if (fn) { if (fn[0] == '/') strNcpy(buffer, fn, sizeof(buffer)); else sprintf(buffer, "%.127s/%.127s", radius_dir, fn); } else sprintf(buffer, "%.200s/%.50s", radius_dir, RADIUS_DICTIONARY);
if((dictfd = fopen(buffer, "r")) == (FILE *)NULL) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Couldn't open dictionary: %s\n", buffer)); return(-1); }
line_no = 0; while(fgets(buffer, sizeof(buffer), dictfd) != (char *)NULL) { line_no++;
/* Skip empty space */ if (*buffer == '#' || strlen(buffer) == strspn(buffer, " \t\n\0") ) continue;
if (strncasecmp(buffer, "$INCLUDE", 8) == 0) {
/* Read the $INCLUDE line */ if(sscanf(buffer, "%63s%63s", dummystr, valstr) != 2) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid filename on line %d of dictionary\n", line_no)); return(-1); } if (dict_init(valstr) < 0) return -1; continue; }
is_attrib = 0; if (strncmp(buffer, "ATTRIBUTE", 9) == 0) is_attrib = 1; #ifdef ATTRIB_NMC is_nmc = 0; if (strncmp(buffer, "ATTRIB_NMC", 10) == 0) is_attrib = is_nmc = 1; #endif if (is_attrib) { /* Read the ATTRIBUTE line */ vendor = 0; vendorstr[0] = 0; if(sscanf(buffer, "%63s%63s%63s%63s%63s", dummystr, namestr, valstr, typestr, vendorstr) < 4) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid attribute on line %d of dictionary\n", line_no)); return(-1); }
#ifdef ATTRIB_NMC /* * Convert ATTRIB_NMC into our format. * We might need to add USR to the list of * vendors first. */ if (is_nmc && vendorstr[0] == 0) { if (!vendor_usr_seen) { if (addvendor("USR", VENDORPEC_USR) < 0) return -1; vendor_usr_seen = 1; } strcpy(vendorstr, "USR"); } #endif
/* * Validate all entries */ if(strlen(namestr) > 31) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid name length on line %d of dictionary\n", line_no)); return(-1); }
if(!isdigit(*valstr)) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid value on line %d of dictionary\n", line_no)); return(-1); } if (valstr[0] != '0') value = atoi(valstr); else sscanf(valstr, "%i", &value);
if(strcmp(typestr, "string") == 0) { type = PW_TYPE_STRING; } else if(strcmp(typestr, "integer") == 0) { type = PW_TYPE_INTEGER; } else if(strcmp(typestr, "ipaddr") == 0) { type = PW_TYPE_IPADDR; } else if(strcmp(typestr, "date") == 0) { type = PW_TYPE_DATE; } /* Cisco VoIP vsa support */ else if(strcmp(typestr, "avpair") == 0) { type = PW_TYPE_CISCO_AVPAIR; } /* Sub-attribute list */ else if(strcmp(typestr, "sublist") == 0) { type = PW_TYPE_SUB_LIST; } else { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid type on line %d of dictionary\n", line_no)); return(-1); }
for (v = dictionary_vendors; v; v = v->next) { if (strcmp(vendorstr, v->vendorname) == 0) vendor = v->vendorcode; } if (vendorstr[0] && !vendor) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: unknown vendor %s on line %d of dictionary\n", vendorstr, line_no)); return -1; }
#ifdef COMPAT_1543 /* * Convert old values 221,1036-1039 to the new * ones, in case the dictionary is still the * one from 1.5.4.3 * * XXX - this is a HACK !! */ switch (value) { case 221: value = PW_HUNTGROUP_NAME; break; case 1036: value = PW_FALL_THROUGH; break; case 1037: value = PW_ADD_PORT_TO_IP_ADDRESS; break; case 1038: value = PW_EXEC_PROGRAM; break; case 1039: value = PW_EXEC_PROGRAM_WAIT; break; } #endif /* Create a new attribute for the list */ if((attr = (DICT_ATTR *)malloc(sizeof(DICT_ATTR))) == (DICT_ATTR *)NULL) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: out of memory\n")); return(-1); } strNcpy(attr->name, namestr, sizeof(attr->name)); attr->value = value; attr->type = type; if (vendor) attr->value |= (vendor << 16);
/* * Add to the front of the list, so that * values at the end of the file override * those in the begin. */ attr->next = dictionary_attributes; dictionary_attributes = attr;
} else if (strncmp(buffer, "VALUE", 5) == 0) {
/* Read the VALUE line */ if(sscanf(buffer, "%63s%63s%63s%63s", dummystr, attrstr, namestr, valstr) != 4) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid value entry on line %d of dictionary\n", line_no)); return(-1); }
/* * Validate all entries */ if(strlen(attrstr) > 31) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid attribute length on line %d of dictionary\n", line_no)); return(-1); }
if(strlen(namestr) > 31) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid name length on line %d of dictionary\n", line_no)); return(-1); }
if(!isdigit(*valstr)) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid value on line %d of dictionary\n", line_no)); return(-1); } value = atoi(valstr);
/* Create a new VALUE entry for the list */ if((dval = (DICT_VALUE *)malloc(sizeof(DICT_VALUE))) == (DICT_VALUE *)NULL) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: out of memory\n")); return(-1); } strNcpy(dval->attrname, attrstr, sizeof(dval->attrname)); strNcpy(dval->name, namestr, sizeof(dval->name)); dval->value = value;
/* Insert at front. */ dval->next = dictionary_values; dictionary_values = dval; } else if(strncmp(buffer, "VENDOR", 6) == 0) {
/* Read the VENDOR line */ if(sscanf(buffer, "%63s%63s%63s", dummystr, attrstr, valstr) != 3) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid vendor entry on line %d of dictionary\n", line_no)); return(-1); }
/* * Validate all entries */ if(strlen(attrstr) > 31) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid attribute length on line %d of dictionary\n", line_no)); return(-1); }
if(!isdigit(*valstr)) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Invalid value on line %d of dictionary\n", line_no)); return(-1); } value = atoi(valstr);
/* Create a new VENDOR entry for the list */ if (addvendor(attrstr, value) < 0) return -1; #ifdef ATTRIB_NMC if (value == VENDORPEC_USR) vendor_usr_seen = 1; #endif } } fclose(dictfd); return(0); }
/************************************************************************* * * Function: dict_attrget * * Purpose: Return the full attribute structure based on the * attribute id number. * *************************************************************************/
DICT_ATTR * dict_attrget(attribute) int attribute; { DICT_ATTR *attr;
attr = dictionary_attributes; while(attr != (DICT_ATTR *)NULL) { if(attr->value == attribute) { return(attr); } attr = attr->next; } return((DICT_ATTR *)NULL); }
/************************************************************************* * * Function: dict_valget * * Purpose: Return the full value structure based on the * actual value and the associated attribute name. * *************************************************************************/
DICT_VALUE * dict_valget(value, attrname) UINT4 value; char *attrname; { DICT_VALUE *val; val = dictionary_values; while(val != (DICT_VALUE *)NULL) { if(DICT_STRCMP(val->attrname, attrname) == 0 && val->value == value) { return(val); } val = val->next; } return((DICT_VALUE *)NULL); }
int radiusAAA_ctx_init () { config_element_t *elem; char *p; struct servent *svp; struct sockaddr salocal; u_short local_port; char nasname[256];
radiusAAA_context = (radiusAAA_ctx_t *) smalloc (sizeof (radiusAAA_ctx_t)); if (radiusAAA_context == NULL) return -1;
elem = psp_config_get_sub_element ("server", "radiusAAA", NULL); if (elem == NULL || elem->value == NULL) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "radiusAAA plugin: server is mandatory\n")); radiusAAA_ctx_free (); return -1; } else { if ( (p = strchr(elem->value, ':') ) != NULL) { *p++ = 0; radiusAAA_context->svc_port = satoi(p); } else { radiusAAA_context->svc_port = 0; } radiusAAA_context->server = elem->value; } OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "radiusAAA plugin: server: %s\n", radiusAAA_context->server));
elem = psp_config_get_sub_element ("key", "radiusAAA", NULL); if (elem == NULL || elem->value == NULL) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "radiusAAA plugin: key is mandatory\n")); radiusAAA_ctx_free (); return -1; } else { radiusAAA_context->key = elem->value; }
elem = psp_config_get_sub_element ("retransmit", "radiusAAA", NULL); if (elem == NULL || elem->value == NULL) { radiusAAA_context->retransmit = 5; } else { radiusAAA_context->retransmit = atoi(elem->value); }
elem = psp_config_get_sub_element ("timeout", "radiusAAA", NULL); if (elem == NULL || elem->value == NULL) { radiusAAA_context->timeout = 3; } else { radiusAAA_context->timeout = atoi(elem->value); }
elem = psp_config_get_sub_element ("dictionary_directory", "radiusAAA", NULL); if (elem == NULL || elem->value == NULL) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "radiusAAA plugin: dictionary_directory is mandatory\n")); radiusAAA_ctx_free (); return -1; } else { radiusAAA_context->dictionary = elem->value; radius_dir = elem->value; }
gethostname(nasname, sizeof(nasname)); radiusAAA_context->nas_ipaddr = get_ipaddr(nasname);
if( dict_init(NULL) == -1 ) return -1;
/* * Open a connection to the server. */ if (radiusAAA_context->svc_port == 0) { svp = getservbyname ("radius", "udp"); if (svp == (struct servent *) 0) radiusAAA_context->svc_port = PW_AUTH_UDP_PORT; else radiusAAA_context->svc_port = ntohs((u_short) svp->s_port); }
/* Get the IP address of the authentication server */ if((auth_ipaddr = get_ipaddr(radiusAAA_context->server)) == 0) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "radiusAAA plugin: Couldn't find host %s\n", radiusAAA_context->server)); radiusAAA_ctx_free (); return -1; }
sockfd = socket (AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "radiusAAA plugin: socket")); radiusAAA_ctx_free (); return -1; }
sin = (struct sockaddr_in *) &salocal; memset (sin, 0, sizeof (salocal)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = INADDR_ANY;
local_port = 1025; do { local_port++; sin->sin_port = htons((u_short)local_port); } while((bind(sockfd, &salocal, sizeof (struct sockaddr_in)) < 0) && local_port < 64000); if (local_port >= 64000) { close(sockfd); OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: failed to bind\n")); radiusAAA_ctx_free (); return -1; }
return 0; }
void radiusAAA_ctx_free () { if (radiusAAA_context == NULL) return;
sfree (radiusAAA_context); radiusAAA_context = NULL; }
/* * Generate a random vector. */ static void random_vector(char *vector) { int randno; int i; srand(time(0)); for(i = 0;i < AUTH_VECTOR_LEN;) { randno = rand(); memcpy(vector, &randno, sizeof(int)); vector += sizeof(int); i += sizeof(int); } }
/* * Receive UDP client requests, build an authorization request * structure, and attach attribute-value pairs contained in * the request to the new structure. */ AUTH_REQ *radrecv(UINT4 host, u_short udp_port, u_char *buffer, int length) { u_char *ptr; char hn[16]; AUTH_HDR *auth; int totallen; int attribute; int attrlen; DICT_ATTR *attr; UINT4 lvalue; VALUE_PAIR *first_pair; VALUE_PAIR *prev; VALUE_PAIR *pair; AUTH_REQ *authreq; int vendorlen; UINT4 vendorcode; UINT4 vendorpec;
/* * Pre-allocate the new request data structure */
if((authreq = (AUTH_REQ *)smalloc(sizeof(AUTH_REQ))) == (AUTH_REQ *)NULL) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: smalloc error\n")); return NULL; }
auth = (AUTH_HDR *)buffer; totallen = ntohs(auth->length);
ipaddr2str(hn, ntohl(host)); OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "radiusAAA plugin: Reply from host %s code=%d, id=%d, length=%d\n", hn, auth->code, auth->id, totallen));
/* * Fill header fields */ authreq->ipaddr = host; authreq->udp_port = udp_port; authreq->id = auth->id; authreq->code = auth->code; memcpy(authreq->vector, auth->vector, AUTH_VECTOR_LEN); authreq->proxy_pairs = NULL; authreq->server_reply = NULL; authreq->timestamp = time(NULL);
/* * Extract attribute-value pairs */ ptr = (u_char *)auth->data; length -= AUTH_HDR_LEN; first_pair = (VALUE_PAIR *)NULL; prev = (VALUE_PAIR *)NULL;
vendorcode = 0; vendorlen = 0;
while(length > 0) {
if (vendorlen > 0) { attribute = *ptr++ | (vendorcode << 16); attrlen = *ptr++; } else { attribute = *ptr++; attrlen = *ptr++; } if (attrlen < 2) { length = 0; continue; } attrlen -= 2; length -= 2;
/* * This could be a Vendor-Specific attribute. * */ if (vendorlen <= 0 && attribute == PW_VENDOR_SPECIFIC && attrlen > 6) { memcpy(&lvalue, ptr, 4); vendorpec = ntohl(lvalue); if ((vendorcode = dict_vendorcode(vendorpec)) != 0) { #ifdef ATTRIB_NMC if (vendorpec == VENDORPEC_USR) { ptr += 4; memcpy(&lvalue, ptr, 4); attribute = (ntohl(lvalue) & 0xFFFF) | (vendorcode << 16); ptr += 4; attrlen -= 8; length -= 8; } else #endif { ptr += 4; vendorlen = attrlen - 4; attribute = *ptr++ | (vendorcode << 16); attrlen = *ptr++; attrlen -= 2; length -= 6; } } }
if ((pair = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == (VALUE_PAIR *)NULL) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: malloc error\n")); exit(1); } memset(pair, 0, sizeof(VALUE_PAIR));
if ((attr = dict_attrget(attribute)) == (DICT_ATTR *)NULL) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Received unknown attribute %d\n", attribute)); sprintf(pair->name, "Unknown-Attr-%d", attribute); pair->type = PW_TYPE_STRING; } else { strcpy(pair->name, attr->name); pair->type = attr->type; }
if ( attrlen >= AUTH_STRING_LEN-1 ) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: attribute %d too long, %d >= %d\n", attribute, attrlen, AUTH_STRING_LEN)); free(pair); } else if ( attrlen > length ) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: attribute %d longer as buffer left, %d > %d\n", attribute, attrlen, length)); free(pair); } else { pair->attribute = attribute; pair->length = attrlen; pair->next = (VALUE_PAIR *)NULL; pair->operator = PW_OPERATOR_EQUAL; pair->strvalue[0] = '\0';
switch (pair->type) {
case PW_TYPE_STRING: case PW_TYPE_CISCO_AVPAIR: case PW_TYPE_SUB_LIST: /* attrlen always < AUTH_STRING_LEN */ memset(pair->strvalue, 0, AUTH_STRING_LEN); memcpy(pair->strvalue, ptr, attrlen); if(first_pair == (VALUE_PAIR *)NULL) { first_pair = pair; } else { prev->next = pair; } prev = pair; break;
case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: memcpy(&lvalue, ptr, sizeof(UINT4)); pair->lvalue = ntohl(lvalue); if(first_pair == (VALUE_PAIR *)NULL) { first_pair = pair; } else { prev->next = pair; } prev = pair; break;
default: OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL, "radiusAAA plugin: %s (Unknown Type %d)\n", attr->name,attr->type)); free(pair); break; }
} ptr += attrlen; length -= attrlen; if (vendorlen > 0) vendorlen -= (attrlen + 2); } authreq->request = first_pair; return(authreq); }
/* * Receive and print the result. */ AUTH_REQ *result_recv(UINT4 host, u_short udp_port, char *buffer, int length) { AUTH_HDR *auth; int totallen; char reply_digest[AUTH_VECTOR_LEN]; char calc_digest[AUTH_VECTOR_LEN]; int secretlen; MD5_CTX Md5Ctx; AUTH_REQ *authreq;
auth = (AUTH_HDR *)buffer; totallen = ntohs(auth->length); if(totallen != length) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Received invalid reply length from server (want %d/ got %d)\n", totallen, length)); return NULL; }
/* Verify the reply digest */ memcpy(reply_digest, auth->vector, AUTH_VECTOR_LEN); memcpy(auth->vector, vector, AUTH_VECTOR_LEN); secretlen = strlen(radiusAAA_context->key); memcpy(buffer + length, radiusAAA_context->key, secretlen); ppl_MD5Init (&Md5Ctx); ppl_MD5Update (&Md5Ctx, (unsigned char *)auth, length + secretlen); ppl_MD5Final ((unsigned char *) calc_digest, &Md5Ctx);
if(memcmp(reply_digest, calc_digest, AUTH_VECTOR_LEN) != 0) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: Received invalid reply digest from server\n")); return NULL; }
authreq = radrecv(host, udp_port, (u_char *)buffer, length); print_attr_list(authreq->request); return authreq; }
/* * Encode Attribute. */ int encodeAttribute( char **ptr, int attribute_code, char *attribute_value) { int length; *(*ptr)++ = attribute_code; length = strlen( attribute_value ); if(length > MAXPWNAM) length = MAXPWNAM; *(*ptr)++ = length + 2; memcpy(*ptr, attribute_value, length); (*ptr) += length; return length + 2; }
/* * Encode DigestAttribute with one subattribute. */ int encodeDigestAttribute( char **ptr, int sub_attribute_code, char *sub_attribute_value) { int length;
*(*ptr)++ = PW_DIGEST_ATTRIBUTES; length = strlen( sub_attribute_value ); if(length > MAXPWNAM) length = MAXPWNAM; *(*ptr)++ = length + 2 + 2; *(*ptr)++ = sub_attribute_code; *(*ptr)++ = length + 2; memcpy(*ptr, sub_attribute_value, length); (*ptr) += length; return length + 2 + 2; }
/* * Encode Vendor Specific Attribute. */ int encodeVendorSpecificAttribute( char **ptr, int vendor_code, int attribute_code, char *attribute_value) { int length; UINT4 ui; length = strlen( attribute_value ); *(*ptr)++ = PW_VENDOR_SPECIFIC; *(*ptr)++ = length + 2 + 4 + 2; ui = htonl(vendor_code); memcpy(*ptr, &ui, sizeof(UINT4)); (*ptr) += 4; *(*ptr)++ = attribute_code; *(*ptr)++ = length + 2; memcpy(*ptr, attribute_value, length); (*ptr) += length; return length + 2 + 4 + 2; }
AUTH_REQ *radiusAAA_validate_credential_for_user (sip_t *request, char *user, proxy_authorization_t * p_auth, char *method) { /* find the pending_auth element */ char *response; char *nonce; char *opaque; char *realm;
/* char *qop; */ char *uri;
char *pszResponse, *pszRealm, *pszNonce, *pszURI, *pszQop;
/* Radius client stuff */ AUTH_HDR *auth; char *ptr; struct sockaddr saremote; int result = -1; #ifdef __linux socklen_t salen; #else int salen; #endif struct timeval tv; fd_set readfds; u_short total_length; int i; UINT4 ui; AUTH_REQ *authreq; char *call_id;
nonce = proxy_authorization_getnonce (p_auth); opaque = proxy_authorization_getopaque (p_auth); if (opaque == NULL || nonce == NULL) return NULL;
realm = proxy_authorization_getrealm (p_auth); if (realm == NULL) return NULL; response = proxy_authorization_getresponse (p_auth); if (response == NULL) return NULL; uri = proxy_authorization_geturi (p_auth); if (uri == NULL) return NULL;
/* Create radius Access Request */ /* * Build an authentication request */ auth = (AUTH_HDR *)send_buffer; auth->code = PW_AUTHENTICATION_REQUEST; auth->id = getpid() % 256; random_vector(vector); memcpy(auth->vector, vector, AUTH_VECTOR_LEN); total_length = AUTH_HDR_LEN; ptr = (char *)auth->data;
/* * NAS IP Address */ *ptr++ = PW_NAS_IP_ADDRESS; *ptr++ = 6; ui = htonl(radiusAAA_context->nas_ipaddr); memcpy(ptr, &ui, sizeof(UINT4)); ptr += 4; total_length += 6;
/* * NAS Port Type - Virtual */
*ptr++ = PW_NAS_PORT_TYPE; *ptr++ = 6; ui = htonl(5); memcpy(ptr, &ui, sizeof(UINT4)); ptr += 4; total_length += 6;
/* * User Name */ total_length += encodeAttribute(&ptr, PW_USER_NAME, user); if (!MSG_IS_REGISTER (request)) { /* * Calling Station Id */ total_length += encodeAttribute(&ptr, PW_CALLING_STATION_ID, request->from->url->username ); /* * Called Station Id */ total_length += encodeAttribute(&ptr, PW_CALLED_STATION_ID, request->to->url->username ); }
/* * Cisco AVpair call-id */ call_id = NULL; /* to avoid a warning */ if (request->call_id->host==NULL) asprintf( &call_id, "call-id=%s", request->call_id->number ); else asprintf( &call_id, "call-id=%s@%s", request->call_id->number, request->call_id->host ); total_length += encodeVendorSpecificAttribute(&ptr, 9, 1, call_id); free(call_id); /* * Digest Responce */ pszResponse = sgetcopy_unquoted_string (response); total_length += encodeAttribute(&ptr, PW_DIGEST_RESPONCE, pszResponse);
/* * Digest Attributes * */
pszRealm = sgetcopy_unquoted_string (realm); total_length += encodeDigestAttribute( &ptr, PW_DA_REALM, pszRealm);
pszNonce = sgetcopy_unquoted_string (nonce); total_length += encodeDigestAttribute( &ptr, PW_DA_NONCE, pszNonce);
total_length += encodeDigestAttribute( &ptr, PW_DA_METHOD, method);
pszURI = sgetcopy_unquoted_string (uri); total_length += encodeDigestAttribute( &ptr, PW_DA_URI, pszURI);
total_length += encodeDigestAttribute( &ptr, PW_DA_ALGORITHM, "MD5");
total_length += encodeDigestAttribute( &ptr, PW_DA_USER_NAME, user);
pszQop = NULL;
auth->length = htons(total_length);
sfree (pszRealm); sfree (pszNonce); sfree (pszURI); sfree (pszQop); sfree (pszResponse);
/* * Send the request we've built. */ sin = (struct sockaddr_in *) &saremote; memset (sin, 0, sizeof (saremote)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = htonl(auth_ipaddr); sin->sin_port = htons(radiusAAA_context->svc_port);
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "radiusAAA plugin: Sending request to server %s, port %d.\n", radiusAAA_context->server, radiusAAA_context->svc_port));
for (i = 0; i < radiusAAA_context->retransmit; i++) { if (i > 0) OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL, "radiusAAA plugin: resending request\n")); sendto(sockfd, (char *)auth, total_length, 0, &saremote, sizeof(struct sockaddr_in));
tv.tv_sec = radiusAAA_context->timeout; tv.tv_usec = 0; FD_ZERO(&readfds); FD_SET(sockfd, &readfds); if (select(sockfd + 1, &readfds, NULL, NULL, &tv) == 0) continue; salen = sizeof (saremote); result = recvfrom (sockfd, recv_buffer, sizeof(i_recv_buffer), 0, &saremote, &salen); if (result >= 0) break; sleep(tv.tv_sec); }
if ( result <= 0 || i >= radiusAAA_context->retransmit ) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "radiusAAA plugin: no answer from radius server\n")); return NULL; } authreq = result_recv(sin->sin_addr.s_addr, sin->sin_port, recv_buffer, result);
if( authreq == NULL ) return NULL; switch(authreq->code) { case PW_AUTHENTICATION_ACK: OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "radius plugin: Access granted.\n")); return authreq; default: OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL, "radius plugin: Access denied.\n")); return NULL; } }
char * radiusAAA_ctx_find_private_user (proxy_authorization_t * p_auth) { char *realm = psp_config_get_element ("serverrealm"); char *username; char *qusername;
if (0 != strcmp (p_auth->realm, realm)) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "radiusAAA plugin: authentication header belongs to another proxy!\nlocal:%s remote:%s\n", realm, p_auth->uri)); return NULL; /* this header is not for me */ }
/* search for a user context */ qusername = proxy_authorization_getusername (p_auth); if (qusername == NULL) return NULL; username = sgetcopy_unquoted_string (qusername);
return username; }
/* HOOK METHODS */
/* This method returns: -2 if plugin consider this request should be totally discarded! -1 on error 0 nothing has been done 1 things has been done on psp_req element */ int cb_radiusAAA_validate_credentials (psp_req_t * psp_req) { char *user, *ptr; proxy_authorization_t *p_auth; authorization_t *h_auth; int pos; AUTH_REQ *authreq; VALUE_PAIR *namepair; location_t *loc; url_t *url; int i;
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL, "radiusAAA plugin: Processing authentication in request!\n"));
if (!MSG_IS_REGISTER (psp_req->request)) { /* default OUTPUT */ psp_req_set_uas_status (psp_req, 407); psp_req_set_mode (psp_req, PSP_UAS_MODE); psp_req_set_state (psp_req, PSP_MANDATE);
/* ANY proxy_authorization for us? */ msg_getproxy_authorization (psp_req->request, 0, &p_auth); pos = 0; while (p_auth != NULL) /* find the proxy_authorization for this proxy */ { user = radiusAAA_ctx_find_private_user (p_auth); if (user != NULL) { authreq = radiusAAA_validate_credential_for_user (psp_req->request, user, p_auth, psp_req->request-> strtline->sipmethod); if (authreq == NULL) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL, "radiusAAA plugin: Bad credential for user!\n")); /* may be we should answer 403 forbidden, because we have detected that somebody tried to login! */ return 0; }
psp_req_set_state (psp_req, PSP_CONTINUE); /* remove the proxy_authorization header! */ list_remove (psp_req->request->proxy_authorizations, pos); proxy_authorization_free (p_auth); sfree (p_auth);
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL, "radiusAAA plugin: Valid credential for user!\n")); psp_req_set_mode (psp_req, PSP_SFULL_MODE); psp_req_set_state (psp_req, PSP_CONTINUE);
namepair = pairfind_by_name(authreq->request, "h323-redirect-number" ); if (namepair != NULL) { ptr = namepair->strvalue; if( (ptr=strchr((u_char *)namepair->strvalue,'=')) == NULL ) ptr = namepair->strvalue; else ptr++; url_init(&url); i = url_parse(url, ptr); if (i!=0) { url_free(url); sfree(url); OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_BUG, NULL, "ls_localdb plugin: Could not create url!\n")); } i = location_init (&loc, url, 3600); /* bad configuration?->should be checked in plugin_init so this will never happen */ if (i != 0) { /* This can only happen in case we don't have enough memory */ /* url_free(url); */ /* dangerous? I prefer to leave a memory leak */ /* sfree(url); */ /* as we may never enter this code */ OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_BUG, NULL, "ls_localdb plugin: Could not create location info!\n")); psp_req_set_uas_status (psp_req, 400); psp_req_set_mode (psp_req, PSP_UAS_MODE); psp_req_set_state (psp_req, PSP_MANDATE); return -1; /* error case (process can continue...) */ } ADD_ELEMENT (psp_req->locations, loc);
psp_req_set_uas_status (psp_req, 302); psp_req_set_mode (psp_req, PSP_UAS_MODE); psp_req_set_state (psp_req, PSP_MANDATE); } authfree(authreq);
return 0; /* do nothing.. */ } pos++; msg_getproxy_authorization (psp_req->request, pos, &p_auth); } OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL, "radiusAAA plugin: Bad credential for user!\n")); return 0; }
/* for REGISTER */ /* default OUTPUT */ psp_req_set_uas_status (psp_req, 401); psp_req_set_mode (psp_req, PSP_UAS_MODE); psp_req_set_state (psp_req, PSP_MANDATE);
/* ANY authorization for us? */ msg_getauthorization (psp_req->request, 0, &h_auth); pos = 0; while (h_auth != NULL) /* find the authorization for this proxy */ { user = radiusAAA_ctx_find_private_user (h_auth); if (user != NULL) { authreq = radiusAAA_validate_credential_for_user (psp_req->request, user, h_auth, psp_req->request->strtline-> sipmethod); if (authreq == NULL) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL, "radiusAAA plugin: Bad credential for user!\n")); /* may be we should answer 403 forbidden, because we have detected that somebody tried to login! */ return 0; }
authfree(authreq); /* remove the authorization header! */ list_remove (psp_req->request->authorizations, pos); authorization_free (h_auth); sfree (h_auth);
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL, "radiusAAA plugin: Valid credential for user!\n")); psp_req_set_mode (psp_req, PSP_SFULL_MODE); psp_req_set_state (psp_req, PSP_CONTINUE); return 0; /* do nothing.. */ } pos++; msg_getauthorization (psp_req->request, pos, &h_auth); }
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL, "radiusAAA plugin: Bad credential for user!\n")); return 0; }
int cb_radiusAAA_add_credentials (psp_req_t * psp_req) /* HOOK MIDDLE */ { char *nonce; char *opaque;
/* char *qop; */ char *realm; proxy_authenticate_t *p_auth; www_authenticate_t *w_auth; int status = psp_req_get_uas_status (psp_req);
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL, "radiusAAA plugin: check if we need to add credentials in this 4xx!\n"));
if (status != 407 && status != 401) { psp_req_set_state (psp_req, PSP_CONTINUE); return 0; }
/* The request DOES not contains any credential */ /* We have to challenge user */
/* we just have to challenge the users */ { MD5_CTX Md5Ctx; HASH HTMP; HASHHEX HTMPHex; int time_stamp; char *now; int i;
/* build a nonce string */ nonce = (char *) smalloc (HASHHEXLEN + 1 + 2); /* +2 for the quotes */ now = (char *) smalloc (30); time_stamp = ppl_time (); sprintf (now, "%i", time_stamp); ppl_MD5Init (&Md5Ctx); ppl_MD5Update (&Md5Ctx, (unsigned char *) now, strlen (now)); sfree (now); ppl_MD5Update (&Md5Ctx, (unsigned char *) ":", 1); now = psp_config_get_element ("magicstring"); ppl_MD5Update (&Md5Ctx, (unsigned char *) now, strlen (now)); ppl_MD5Update (&Md5Ctx, (unsigned char *) ":", 1); ppl_MD5Update (&Md5Ctx, (unsigned char *) psp_req->request->cseq->number, strlen (psp_req->request->cseq->number)); ppl_MD5Update (&Md5Ctx, (unsigned char *) ":", 1); ppl_MD5Update (&Md5Ctx, (unsigned char *) psp_req->request->call_id->number, strlen (psp_req->request->call_id->number)); ppl_MD5Final ((unsigned char *) HTMP, &Md5Ctx); ppl_md5_hash_to_hex (HTMP, HTMPHex); sprintf (nonce, "\"%s\"", HTMPHex);
/* build an opaque string */ opaque = (char *) smalloc (HASHHEXLEN + 1 + 2); /* +2 is for the quotes */ now = (char *) smalloc (30); time_stamp = ppl_time (); sprintf (now, "%i", time_stamp); ppl_MD5Init (&Md5Ctx); ppl_MD5Update (&Md5Ctx, (unsigned char *) now, strlen (now)); sfree (now); ppl_MD5Update (&Md5Ctx, (unsigned char *) ":", 1); ppl_MD5Final ((unsigned char *) HTMP, &Md5Ctx); ppl_md5_hash_to_hex (HTMP, HTMPHex); sprintf (opaque, "\"%s\"", HTMPHex);
/* qop = smalloc(9); sprintf(qop, "\"auth\""); */
realm = sgetcopy (psp_config_get_element ("serverrealm"));
/* store data in a radiusAAA_ctx_t context */
if (status == 407) { i = proxy_authenticate_init (&p_auth); if (i != 0) return -1; proxy_authenticate_setauth_type (p_auth, sgetcopy ("Digest")); /* proxy_authenticate_setqop_options(p_auth, qop); */ proxy_authenticate_setnonce (p_auth, nonce); proxy_authenticate_setopaque (p_auth, opaque); proxy_authenticate_setrealm (p_auth, realm);
list_add (psp_req->response->proxy_authenticates, p_auth, -1);
psp_req_set_state (psp_req, PSP_CONTINUE); return 0; } else if (status == 401) { i = www_authenticate_init (&w_auth); if (i != 0) return -1; www_authenticate_setauth_type (w_auth, sgetcopy ("Digest")); /* www_authenticate_setqop_options(w_auth, qop); */ www_authenticate_setnonce (w_auth, nonce); www_authenticate_setopaque (w_auth, opaque); www_authenticate_setrealm (w_auth, realm);
list_add (psp_req->response->www_authenticates, w_auth, -1);
psp_req_set_state (psp_req, PSP_CONTINUE); return 0; } } psp_req_set_state (psp_req, PSP_CONTINUE); return 0; }
浙公网安备 33010602011771号