#pragma module RAD_UTIL "RAD_UTIL-1-K" #define module "RAD_UTIL" /* **++ ** FACILITY: RADIUS-VMS ** ** ** MODULE DESCRIPTION: ** ** This module contains utility routines used in all RADIUS-VMS modules. ** ** AUTHORS: ** ** Ruslan R. Laishev ** ** CREATION DATE: 21-DEC-1999 ** ** ** ** MODIFICATION HISTORY: ** ** 2-FEB-2000 RRL Cosmetic change using LSE. ** 25-MAR-2000 RRL Cosmetic change using LSE. ** 7-APR-2000 RRL Add Full VSA support. ** 1-MAY-2000 RRL Fix put_attribute (). ** 2-MAY-2000 RRL Fix problem with empty value_list in put_attribute (). ** 8-MAY-2000 RRL Fix put_attribute (). ** 8-JUL-2000 RRL Modify radrecv() to handle PW_PASSWORD_REJECT request. ** 11-AUG-2000 RRL Modify put_attribute (), put only minimal INT value ** for attribute PW_SESSION_TIMEOUT. ** 30-SEP-2000 RRL Moved from SMB_UTIL.C strlower(),strupper() to this module. ** 5-OCT-2000 RRL Added tree_node_get_vm(). ** 16-OCT-2000 RRL Fixed problem in radrecv() caused to crash due ATTRTOLONG. ** 1-DEC-2000 RRL radrecv() zero length packet. ** 22-DEC-2000 RRL Add a special handling of the USR VSA. ** 2-FEB-2001 RRL Add an additional checking of an attribute length in the radrecv(). ** 11-APR-2001 RRL Fixed incorrect truncation of the AVP list in the put_attrubute(). ** 11-JAN-2002 RRL Added a fix in rad_attr_out () to printout an ASCIZ string correctly. ** 16-JAN-2002 RRL Removed previous fix. ** 28-FEB-2002 RRL Added get_logname(). ** 20-MAR-2002 RRL Added an additional checking of free space in an output buffer ** to list2pkt(). ** 22-JUL-2003 RRL Added UDP port info in DEBUG output in the radrecv(). ** 18-AUG-2003 RRL Fixed a dumb bug in the list2pkt() added vendor check in ** proxy state branch. ** 21-OCT-2003 RRL Added look4realm() ** 1-OCT-2004 RRL Modified list2pkt, now Calling-Station-Id will be added to responses to ** help Cisco CSG to track users session. ** 17-OCT-2006 RRL Changed: hostname_ip -> name2ipaddr, ip_hostname -> ipaddr2name; ** added local DNS cache into name2ipaddr; ** 25-JUN-2008 RRL Added special check for 255.255.255.255 into ipaddr2name(). ** ** 30-SEP-2008 RRL Merged RAD_UTIL.C & RAD_LOG.C to RAD_UTIL.C ** 6-JAN-2001 RRL Cosmetic changes using LSE. ** 16-FEB-2004 RRL Added logging to SYSLOG. ** 14-JAN-2009 RRL Added copying of the Class attribute to the list2pkt(). ** 22-DEC-2010 RRL Added _rad_trace() routine. ** 8-APR-2011 RRL Added routines to encode/decode Tunnel-Password. ** 8-JUN-2011 RRL Fixed dump bug in the _match_int(): return 0 for the first match. ** 8-NOV-2011 RRL Moved from RAD_USERS.C fieldcpy(). ** 10-NOV-2011 RRL Added support of Template-Id. ** 22-MAR-2012 RRL Fixed a bug with a calling of tmpl_retr() with incorrect aerguments. ** ** ** **-- */ /* ** ** INCLUDE FILES ** */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define __NEW_STARLET 1 #include #include #include "raddef.h" #include "radius.h" #include "syslogdef.h" /* ** ** MACRO DEFINITIONS ** */ #ifdef __VAX #define LONGWORD_SZ 4 #else #define LONGWORD_SZ 8 #endif /* ** ** GLOBALS ** */ void * slogctx; /* **++ ** FUNCTIONAL DESCRIPTION: ** ** A special initialization under VMS prior 7.x ** ** **-- */ static pthread_mutex_t ipaddr2name_lock = PTHREAD_MUTEX_INITIALIZER, name2ipaddr_lock = PTHREAD_MUTEX_INITIALIZER; /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Copying all attributes/value pairs to packet buffer, ** select & copying "Proxy-State" and "Vendor-Specific" attribute/value ** pairs from original list. It's assumed that the size of the buffer is ** RAD_MAXBODYSZ. If Tamplate-Id attribute is take place - add a prepared ** block directly into the output buffer. ** ** FORMAL PARAMETERS: ** ** req: pointer to primary attr/value list ** ptr: buffer for new packet ** oldreq: list with attr/value pairs from the request ** ** ** RETURN VALUE: ** ** longword,length of the packets, ** ptr - buffer with new packet created ** **-- */ int list2pkt ( VALUE_PAIR * req, char * ptr, VALUE_PAIR * oldreq, CLIENT * client, unsigned char * vector ) { unsigned short len = 0, retlen = 0, attlen = 0; unsigned char *lenp,*bufp = ptr; /* Format of VSA, RFC 2138 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type | Length | Vendor-Id +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Vendor-Id (cont) | Vendor type | Vendor length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Attribute-Specific... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+- */ while ( req ) { if ( debug_flag ) rad_attr_out1(req); /* ** Check free space in output buffer */ if ( (retlen + 8) > RAD_MAXBODYSZ ) break; lenp = NULL; attlen = 0; if ( (!req->attr->vendor) && (req->attr->id == ATTR$K_TMPL_ID) ) { int tmplid = req->lvalue; /* ** Special processing of the Template-Id attribute: ** adding attributes to the answer list from the template. */ req = req->next; tmpl_retr(tmplid,&req); continue; } if ( req->attr->vendor ) { *ptr++ = PW_VENDOR_SPECIFIC; lenp = ptr++; *((int *) ptr) = htonl(req->attr->vendor); ptr += 4; attlen += 6; } if ( req->attr->vendor && (req->attr->vendor == RAD_VENDOR_K_USR) ) { *((int *) ptr) = htonl(req->attr->id); ptr += 4; attlen += 2; } else *ptr++ = req->attr->id; switch(req->attr->type) { case PW_TYPE_FILTER: case PW_TYPE_STRING: len = (req->lvalue >= AUTH_STRING_LEN)?AUTH_STRING_LEN:req->lvalue; /* ** Check free space in output buffer */ if ( (retlen + attlen + len) > RAD_MAXBODYSZ ) break; if ( req->attr->vendor != RAD_VENDOR_K_USR ) *ptr++ = len + 2; /* ** Special handling Tunnel attributes */ if ( !req->attr->vendor && _match_int (req->attr->id, ATTR$K_TUNNCLIENDP, ATTR$K_TUNNSRVENDP, ATTR$K_TUNNPWD, ATTR$K_TUNNPVTGID, ATTR$K_TUNNASSID, ATTR$K_TUNNCLIAUTHID, ATTR$K_TUNNSRVAUTHID) ) { unsigned char *__lenp = ptr-1; if ( req->attr->id == ATTR$K_TUNNPWD ) _rad_tpwdenc(req,client,vector); len = (req->lvalue >= AUTH_STRING_LEN)?AUTH_STRING_LEN:req->lvalue; (*__lenp) = len+3; (*ptr) = '\0'; memcpy(ptr+1, req->strvalue,len++); ptr += len; attlen += len + 2; } else { memcpy(ptr, req->strvalue,len); ptr += len; attlen += len + 2; } break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: len = 4; /* ** Check free space in output buffer */ if ( (retlen + attlen + len) > RAD_MAXBODYSZ ) break; if ( req->attr->vendor != RAD_VENDOR_K_USR ) *ptr++ = sizeof(int) + 2; *((int *) ptr) = htonl(req->lvalue); ptr += len; attlen += len + 2; break; default: break; } if ( req->attr->vendor ) *lenp = req->attr->vendor != RAD_VENDOR_K_USR?len + 8:len + 10; /* ** Add a length of a processed attribute to total length of a packet ** and get pointer to next AVP in list. */ retlen += attlen; req = req->next; } /* ** Add Proxy-State, Class & VSA pairs from secondary (old)list */ while ( oldreq ) { /* ** Check free space in output buffer */ if ( (retlen + 8) > RAD_MAXBODYSZ ) break; /* ** Build Proxy-State attribute ** Add Calling-Station-Id */ if ( (!oldreq->attr->vendor) && _match_int(oldreq->attr->id, ATTR$K_PROXY, ATTR$K_CALLING_ID, ATTR$K_CLASS) ) { len = (oldreq->lvalue >= AUTH_STRING_LEN)?AUTH_STRING_LEN:oldreq->lvalue; /* ** Check free space in output buffer */ if ( (retlen + len + 2) > RAD_MAXBODYSZ ) break; *ptr++ = oldreq->attr->id; *ptr++ = len + 2; memcpy(ptr, oldreq->strvalue,len); ptr += len; retlen += len + 2; oldreq = oldreq->next; continue; } else if ( !oldreq->attr->vendor ) { /* ** Skip all attributes other then VSA */ oldreq = oldreq->next; continue; } if ( debug_flag ) rad_attr_out1(oldreq); /* ** Build VSA */ *ptr++ = PW_VENDOR_SPECIFIC; lenp = ptr++; *((int *) ptr) = htonl(oldreq->attr->vendor); ptr += 4; attlen = 6; if ( oldreq->attr->vendor == RAD_VENDOR_K_USR ) { *((int *) ptr) = htonl(oldreq->attr->id); ptr += 4; attlen += 2; } else *ptr++ = oldreq->attr->id; switch(oldreq->attr->type) { case PW_TYPE_FILTER: case PW_TYPE_STRING: len = (oldreq->lvalue >= AUTH_STRING_LEN)?AUTH_STRING_LEN:oldreq->lvalue; /* ** Check free space in output buffer */ if ( (retlen + attlen + len) > RAD_MAXBODYSZ ) break; if ( oldreq->attr->vendor != RAD_VENDOR_K_USR ) *ptr++ = len + 2; memcpy(ptr, oldreq->strvalue,len); ptr += len; attlen += len + 2; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: len = 4; /* ** Check free space in output buffer */ if ( (retlen + attlen + len) > RAD_MAXBODYSZ ) break; if ( oldreq->attr->vendor != RAD_VENDOR_K_USR ) *ptr++ = sizeof(int) + 2; *((int *) ptr) = htonl(oldreq->lvalue); ptr += len; attlen += len + 2; break; default: break; } if ( oldreq->attr->vendor ) *lenp = oldreq->attr->vendor != RAD_VENDOR_K_USR?len + 8:len + 10; /* ** Add a length of a processed attribute to total length of a packet ** and get pointer to next AVP in list. */ retlen += attlen; oldreq = oldreq->next; } #if 0 if ( debug_flag ) { unsigned i,c; printf("\nlist2pkt(): HEX Dump of a packet follows...\n"); for (i = 0; i < retlen; i++) { if ( !(i%16) ) printf("\n"); c = *(bufp+i); c &= 255; printf("%02.2X ",c); } printf("\n\n"); } #endif return retlen; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Return a printable host name (or IP address in dot notation) ** for a supplied IP address. Reverse lookup is perfomed if dns_lookup ** global flag is set. Performs caching resolved IP names for speed-up ** request processing. ** ** FORMAL PARAMETERS: ** ** ipaddr: IP address for conversion. ** outbuf: pointer to output buffer. ** ** RETURN VALUE: ** ** pointer to buffer which contains printable IP address/name in ASCIZ. ** **-- */ #define MAX_IP_NAMES 512 char *ipaddr2name ( int ipaddr, char *outbuf ) { struct hostent *hp; int n_ipaddr,ip_names_idx; static struct ip_names { int ip; char name [128]; } ip_names_table [MAX_IP_NAMES]; static int ip_names_last = 0; /* ** Special check for 255.255.255.255 */ if ( ipaddr == -1 ) return strcpy(outbuf,"255.255.255.255"); *outbuf = '\0'; if ( !ipaddr || !dns_lookup ) return outbuf; /* ** First searching for ipaddr in the cache table */ pthread_mutex_lock (&ipaddr2name_lock); for (ip_names_idx = 0;ip_names_idx <= ip_names_last;ip_names_idx++) { if ( ip_names_table[ip_names_idx].ip == ipaddr ) { strncpy(outbuf,ip_names_table[ip_names_idx].name, sizeof(ip_names_table[0].name)); pthread_mutex_unlock (&ipaddr2name_lock); return outbuf; } } pthread_mutex_unlock (&ipaddr2name_lock); /* ** Missing in cache, requesting to DNS */ n_ipaddr = htonl(ipaddr); if ( !(hp = gethostbyaddr((char *)&n_ipaddr,sizeof(struct in_addr),AF_INET)) ) return outbuf; else strcpy(outbuf,hp->h_name); /* ** Check numbers of entry in the cache table, if cache is full then ** clear last entry and put to first entry new information */ pthread_mutex_lock (&ipaddr2name_lock); if ( ip_names_idx > MAX_IP_NAMES ) { /* ** Shift cache entries to one position to down */ memmove(&ip_names_table[1],&ip_names_table[0],sizeof(struct ip_names)*(MAX_IP_NAMES-1)); ip_names_table[0].ip = ipaddr; strncpy(ip_names_table[0].name,outbuf,sizeof(ip_names_table[0].name)); ip_names_last = (MAX_IP_NAMES - 1); } else { ip_names_table[ip_names_idx].ip = ipaddr; strcpy(ip_names_table[ip_names_idx].name,outbuf); ip_names_last = ip_names_idx; } pthread_mutex_unlock (&ipaddr2name_lock); DEBUG("UTIL:DNS cache status free/busy entry - %u/%u,last inserted - %.*s\n", ip_names_idx,MAX_IP_NAMES,sizeof(ip_names_table[0].name),outbuf); return outbuf; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Performs conversion of IP address (given as ASCIZ string in dot notaion), ** into IP address in binary form in a host byte order. ** ** FORMAL PARAMETERS: ** ** ipbuf: buffer contained IP address ASCIZ string ** ** RETURN VALUE: ** ** longword, IP address in binary form, HBO (Host Byte Order). ** **-- */ int name2ipaddr ( char *ipbuf ) { int ipaddr,ip_names_idx; struct hostent *hp; static struct ip_names { int ip; char name [128]; } ip_names_table [MAX_IP_NAMES]; static int ip_names_last = 0; /* ** Is there IP-address in the numeric notaion ? ** If so - translate and return. */ if ( -1 != (ipaddr = inet_addr(ipbuf)) ) return ntohl(ipaddr); /* ** Try to find firstly IP-name in the local cache */ pthread_mutex_lock (&name2ipaddr_lock); for (ip_names_idx = 0;ip_names_idx <= ip_names_last;ip_names_idx++) { if ( !strncasecmp(ip_names_table[ip_names_idx].name, ipbuf,sizeof(ip_names_table[0].name)) ) { /* ** Found in local cache - return IP-address */ pthread_mutex_unlock (&name2ipaddr_lock); return ip_names_table[ip_names_idx].ip; } } pthread_mutex_unlock (&name2ipaddr_lock); /* ** Missing in cache, requesting to DNS */ if ( !(hp = gethostbyname(ipbuf)) ) return -1; /* ** Ip-name is resolved to IP-address. ** Check numbers of entry in the cache table, if cache is full then ** clear last entry and put to first entry new information */ pthread_mutex_lock (&name2ipaddr_lock); if ( ip_names_idx > MAX_IP_NAMES ) { /* ** Shift cache entries to one position to down */ memmove(&ip_names_table[1],&ip_names_table[0],sizeof(struct ip_names)*(MAX_IP_NAMES-1)); ip_names_table[0].ip = ntohl(*(int *)hp->h_addr); strncpy(ip_names_table[0].name,ipbuf,sizeof(ip_names_table[0].name)); ip_names_last = (MAX_IP_NAMES - 1); } else { ip_names_table[ip_names_idx].ip = ntohl(*(int *)hp->h_addr); strcpy(ip_names_table[ip_names_idx].name,ipbuf); ip_names_last = ip_names_idx; } pthread_mutex_unlock (&name2ipaddr_lock); DEBUG("UTIL:DNS cache status free/busy entry - %u/%u,last inserted - %.*s\n", ip_names_idx,MAX_IP_NAMES,sizeof(ip_names_table[0].name),ipbuf); return ntohl(*((int *)hp->h_addr)); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Performs conversion of an IP address given in binary format to ** ASCIZ string in standard dot notation. ** ** FORMAL PARAMETERS: ** ** buf: pointer to output buffer ** ipaddr: IP address for conversion ** ** RETURN VALUE: ** ** pointer to buffer with IP address in dort notation as ASCIZ string. ** **-- */ char *ipaddr2str ( char *buf, int ipaddr ) { int addr_byte[4]; int i; int xbyte; for (i = 0;i < 4;i++) { xbyte = ipaddr >> (i*8); xbyte = xbyte & (int)0x000000FF; addr_byte[i] = xbyte; } sprintf(buf, "%u.%u.%u.%u", addr_byte[3], addr_byte[2], addr_byte[1], addr_byte[0]); return buf; } $DESCRIPTOR(VMZoneName_REQ,"RADIUS Requests"); $DESCRIPTOR(VMZoneName_PAIR,"RADIUS Pairs"); int VMZoneId_REQ,VMZoneId_PAIR,VMZoneDetail=3; /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Initialization of virtual memory zones for packets, and requests, ** preallocation with number of threads. ** ** FORMAL PARAMETERS: ** ** numthreads: An initial number of threads ** ** RETURN VALUE: ** ** VMS condition code ** ** SIDE EFFECTS: ** ** Performs additional checking of VMS conditions, can crash ** server in case of FATAL, or ERROR status. **-- */ int rad_init_vm ( short numthreads ) { int status; int VMZoneAlg = LIB$K_VM_FIXED, VMZoneFlags = LIB$M_VM_EXTEND_AREA | LIB$M_VM_GET_FILL0, VMZoneBlockSz,VMZoneSz; /* ** */ VMZoneBlockSz = sizeof (VALUE_PAIR); VMZoneSz = (VMZoneBlockSz * numthreads * 8 * 2)/512; status = lib$create_vm_zone(&VMZoneId_PAIR,&VMZoneAlg,&VMZoneBlockSz,&VMZoneFlags,0, &VMZoneSz,0,0,0,0,&VMZoneName_PAIR,0,0); if ( !(1 & status) ) lib$signal(status); /* ** */ VMZoneBlockSz = sizeof (AUTH_REQ); VMZoneSz = (VMZoneBlockSz * numthreads * 2)/512; status = lib$create_vm_zone(&VMZoneId_REQ,&VMZoneAlg,&VMZoneBlockSz,&VMZoneFlags,0, &VMZoneSz,0,0,0,0,&VMZoneName_REQ,0,0); if ( !(1 & status) ) lib$signal(status); return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Allocate a VALUE_PAIR structure in corresponding VM zone. ** ** FORMAL PARAMETERS: ** ** pair: pointer to allocated memory ** ** RETURN VALUE: ** ** VMS Condition code ** ** SIDE EFFECTS: ** ** Performs additional checking of VMS conditions, can crash ** server in case of FATAL, or ERROR status. ** **-- */ int rad_get_vm_pair ( VALUE_PAIR **pair ) { int status; if ( !(1 & (status = lib$get_vm(&sizeof(VALUE_PAIR),pair,&VMZoneId_PAIR))) ) lib$signal(status); return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Release a memory allocated by previous rad_get_vm_pair (). ** ** FORMAL PARAMETERS: ** ** pair - pointer to allocated memory ** ** RETURN VALUE: ** ** longword, VMS condition code ** ** SIDE EFFECTS: ** ** Performs additional checking of VMS conditions, can crash ** server in case of FATAL, or ERROR status. ** **-- */ int rad_free_vm_pair( VALUE_PAIR *pair ) { int status; VALUE_PAIR *next; while (pair) { next = pair->next; if ( !(1 & (status = lib$free_vm(&sizeof(VALUE_PAIR),&pair,&VMZoneId_PAIR))) ) lib$signal(status); pair = next; } return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Allocate a AUTH_REQ structure in corresponding a VM zone. ** ** FORMAL PARAMETERS: ** ** authreq - pointer to allocated memory ** ** RETURN VALUE: ** ** longword, VMS condition ** ** SIDE EFFECTS: ** ** Performs additional checking of VMS conditions, can crash ** server in case of FATAL, or ERROR status. ** **-- */ int rad_get_vm_req ( AUTH_REQ **authreq ) { int status; if ( !(1 & (status = lib$get_vm(&sizeof(AUTH_REQ),authreq,&VMZoneId_REQ))) ) lib$signal(status); return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Release a memory allocated by previous rad_get_vm_req (). ** ** FORMAL PARAMETERS: ** ** authreq - pointer to allocated memory ** ** RETURN VALUE: ** ** longword, VMS condition ** ** SIDE EFFECTS: ** ** Performs additional checking of VMS conditions, can crash ** server in case of FATAL, or ERROR status. ** **-- */ int rad_free_vm_req ( AUTH_REQ *authreq ) { int status; if ( authreq ) { rad_free_vm_pair (authreq->request); if ( !(1 & (status = lib$free_vm(&sizeof(AUTH_REQ),&authreq,&VMZoneId_REQ))) ) lib$signal(status); } return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Performs retrieving of a specific value-pair from a list, with given ** attribut id. ** ** FORMAL PARAMETERS: ** ** value_list - list of VALUE_PAIR structures ** attr_id - attribute for lookup ** ** RETURN VALUE: ** ** pointer to found value_pair, NULL if attribute is not presented ** in the list ** ** **-- */ VALUE_PAIR *get_attribute ( VALUE_PAIR * value_list, int attr_id, int vendor ) { while ( value_list ) { if ( (value_list->attr->vendor == vendor) && (value_list->attr->id == attr_id) ) return(value_list); value_list = value_list->next; } return NULL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Insert or change attribute value in the given list. If the value_list ** is empty then allocate a memory for the new AVP structure and return it in the ** value_list parameter. ** ** FORMAL PARAMETERS: ** ** value_list: A list of VALUE_PAIR structures ** attr_id: An id of attribute to insert ** val: A value ** len: A length of the value (is ignored for nonstring attributes) ** ** ** RETURN VALUE: ** ** None. ** ** **-- */ void put_attribute ( VALUE_PAIR ** value_list, int attr_id, void * val, int len ) { int status,nomatch = 1; VALUE_PAIR *lastpair,*newpair,*curpair = *value_list; DICT_ATTR *attr; /* ** Check an attribute against the dictionary */ if ( !(attr = dict_attrget(attr_id,0,NULL,0)) ) return; /* ** Lookup the added attribute in the AVP list */ while ( curpair ) { lastpair = curpair; if ( !(nomatch = (curpair->attr->id - attr_id)) ) break; curpair = curpair->next; } if ( nomatch ) { /* ** If attribute is not exist in the value_list allocate memory ** for the AVP structure. */ if ( !(1 & (status = rad_get_vm_pair(&newpair))) ) lib$signal(status); /* ** If the value_list is not empty then add the new AVP at begin of the list */ if ( *value_list ) { newpair->next = *value_list; *value_list = newpair; } else *value_list = newpair; /* ** Store the attribute id in the new AVP structure */ newpair->attr = attr; } else newpair = lastpair; /* ** Copy new value to the new AVP structure */ switch(newpair->attr->type) { case PW_TYPE_FILTER: case PW_TYPE_STRING: newpair->lvalue = len; memcpy(newpair->strvalue,val,len); break; case PW_TYPE_INTEGER: if ( attr_id == PW_SESSION_TIMEOUT ) newpair->lvalue = newpair->lvalue?min(*((int *)val),newpair->lvalue):*((int *)val); else newpair->lvalue = *((int *)val); break; case PW_TYPE_IPADDR: newpair->lvalue = *((int *)val); break; default: break; } } $DESCRIPTOR(fao_s_dsc,"\t!AC = \"!AF\""); $DESCRIPTOR(fao_v_dsc,"\t!AC = !AC"); $DESCRIPTOR(fao_z_dsc,"\t!AC = !AZ"); $DESCRIPTOR(fao_i_dsc,"\t!AC = !UL"); /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Performs parsing and format & dump to the SYS$OUTPUT a given list of ** VALUE_PAIR structures. ** ** FORMAL PARAMETERS: ** ** pair - list of VALUE_PAIR to display ** ** RETURN VALUE: ** ** None ** **-- */ void rad_attr_out ( VALUE_PAIR *pair ) { DICT_VALUE *dval; char buf[32]; for (;pair;pair = pair->next) { switch(pair->attr->type) { case PW_TYPE_FILTER: case PW_TYPE_STRING: rad_put_output(&fao_s_dsc,&pair->attr->namelen,pair->lvalue,pair->strvalue); break; case PW_TYPE_INTEGER: if ( dval = dict_valget(pair->attr->id,pair->attr->vendor,pair->lvalue, NULL,0) ) rad_put_output(&fao_v_dsc,&pair->attr->namelen,&dval->namelen); else rad_put_output(&fao_i_dsc,&pair->attr->namelen,pair->lvalue); break; case PW_TYPE_IPADDR: rad_put_output(&fao_z_dsc,&pair->attr->namelen,ipaddr2str(buf, pair->lvalue)); break; case PW_TYPE_DATE: strftime(buf, sizeof(buf), "%b %e %Y", gmtime((time_t *)&pair->lvalue)); rad_put_output(&fao_z_dsc,&pair->attr->namelen,buf); break; } } } void rad_attr_out1 ( VALUE_PAIR *pair ) { DICT_VALUE *dval; char buf[32]; switch(pair->attr->type) { case PW_TYPE_FILTER: case PW_TYPE_STRING: rad_put_output(&fao_s_dsc,&pair->attr->namelen,pair->lvalue,pair->strvalue); break; case PW_TYPE_INTEGER: if ( dval = dict_valget(pair->attr->id,pair->attr->vendor,pair->lvalue, NULL,0) ) rad_put_output(&fao_v_dsc,&pair->attr->namelen,&dval->namelen); else rad_put_output(&fao_i_dsc,&pair->attr->namelen,pair->lvalue); break; case PW_TYPE_IPADDR: rad_put_output(&fao_z_dsc,&pair->attr->namelen,ipaddr2str(buf, pair->lvalue)); break; case PW_TYPE_DATE: strftime(buf, sizeof(buf), "%b %e %Y", gmtime((time_t *)&pair->lvalue)); rad_put_output(&fao_z_dsc,&pair->attr->namelen,buf); break; } } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Parses a UDP packet of a client request, allocates memory for VALUE_PAIR list, ** build an authorization request structure, and attach attribute-value pairs ** contained in the request to the new structure. Performs additional constistence ** checking of information in a packet. ** ** FORMAL PARAMETERS: ** ** host: IP address of a source of a packet ** port: UDP port number of a source of a packet ** buf: buffer with packet to parsing ** length: length of data in buffer ** ** RETURN VALUE: ** pointer to list of VALUE_PAIR structures, NULL if any problem is detected ** ** DESIGN ISSUE: ** The main processing is expect to see a RFC-compliant attribute/value ** pairs incapsulation (VSA too). There is special handling of VSA comming ** from USR device, these VSA not contains a length of a VSA value. We will ** treate these attributes a 32-x bits integer. **-- */ AUTH_REQ *radrecv ( int host, unsigned short port, char *buf, int length ) { int status; AUTH_HDR *auth; DICT_ATTR *attr; VALUE_PAIR *first_pair,*prev,*pair; AUTH_REQ *authreq; unsigned lvalue,vendor,vsa; unsigned short totallen; unsigned char tmpbuf [ 32 ],*ptr,attrlen,attribute,vsalen; #if 0 if ( debug_flag ) { unsigned i,c; printf("\nradrecv(): HEX Dump of a packet follows...\n"); for (i = 0;i < length;i++) { if ( !(i%16) ) printf("\n"); c = *(buf+i); c &= 255; printf("%02.2X ",c); } printf("\n\n"); } #endif /* ** A packet is too shoort ? */ if ( length < AUTH_HDR_LEN ) { rad_log(RAD_RECV_TOOSHORT,ipaddr2str(tmpbuf,host),port,length); return (AUTH_REQ *)NULL; } /* ** Truncated packet? Ignore! */ auth = (AUTH_HDR *)buf; totallen= ntohs(auth->length ); if ( totallen > length) { rad_log(RAD_RECV_INVLEN,ipaddr2str(tmpbuf,host), port,totallen, length); return((AUTH_REQ *)NULL); } else length = min(totallen,length); /* ** Invalid request code, Ignore */ if ( (auth->code < PW_AUTHENTICATION_REQUEST) || (auth->code > PW_PASSWORD_REJECT) ) { rad_log(RAD_RECV_UNKNTYPE,auth->code,ipaddr2str(tmpbuf,host),port); return((AUTH_REQ *)NULL); } /* ** Pre-allocate the new request data structure */ if ( !(1 & (status = rad_get_vm_req(&authreq))) ) lib$signal(status); TRACE "Request from host !AZ:!UL code=!UB, id=!UB, length=!UW\n", ipaddr2str(tmpbuf,host), port,auth->code, auth->id, totallen); /* ** Fill header fields */ authreq->ipaddr = host; authreq->id = auth->id; authreq->code = auth->code; memcpy(authreq->vector, auth->vector, RAD$K_VECTORSZ); /* ** Extract attribute-value pairs */ ptr = auth->data; length = totallen - AUTH_HDR_LEN; first_pair = (VALUE_PAIR *)NULL; prev = (VALUE_PAIR *)NULL; for(int i = 0;(i < 128) && (length >= 3);i++ ) { /* ** A local initalization of work variables */ vendor = vsa = vsalen = 0; attr = (DICT_ATTR *) NULL; attribute= *ptr++; attrlen = *ptr++; /* ** Skip processing of too short attributes */ if ( attrlen < 2 ) { length = 0; continue; } attrlen -= 2; if ( attrlen > min(length,AUTH_STRING_LEN) ) { rad_log(RAD_RECV_ATTRTOLONG,attribute,ipaddr2str(tmpbuf,host),port, attrlen, min(length,AUTH_STRING_LEN) ); /* ** Free memory and return NULL */ rad_free_vm_req(authreq); return (AUTH_REQ *) NULL; } /* ** Special handling of a VSA, if any problem is occured just treat VSA as usual */ if ( attribute == PW_VENDOR_SPECIFIC ) { /* ** Get a vendor code */ vendor = *((int *)ptr); vendor = ntohl(vendor); /* ** Get a VSA itself and length */ if ( vendor != RAD_VENDOR_K_USR ) { vsa = *(ptr+4); /* ** Check a VSA length */ if ( (min(attrlen,AUTH_STRING_LEN) <= (vsalen = *(ptr+5))) || (vsalen < 2) ) vsalen = attrlen - 4; } else { /* ** Special handling USR VSA attributes */ vsa = ntohl(*((int *) (ptr+4))); vsalen = 4; } /* ** Lookup the attribute/VSA in the dictionary */ if ( vsa && vendor ) { if ( !(attr = dict_attrget(vsa,vendor,NULL,0)) ) { if (debug_flag) rad_log(RAD_RECV_INVATTR,vsa,vendor,ipaddr2str(tmpbuf,host),port); vendor = vsa = vsalen = 0; } else { attribute = vsa; attrlen = vendor==RAD_VENDOR_K_USR?4:vsalen - 2; ptr += vendor==RAD_VENDOR_K_USR?8:6; length -= vendor==RAD_VENDOR_K_USR?8:6; } } else { vendor = vsa = vsalen = 0; } } if ( !attr && !(attr = dict_attrget(attribute,vendor,NULL,0)) ) { rad_log(RAD_RECV_INVATTR,attribute,vendor,ipaddr2str(tmpbuf,host),port); ptr += attrlen; length -= attrlen + 2; continue; } if ( !(1 & (status = rad_get_vm_pair(&pair))) ) lib$signal(status); pair->attr = attr; pair->next = (VALUE_PAIR *)NULL; switch(attr->type) { case PW_TYPE_STRING: pair->strvalue[attrlen] = '\0'; case PW_TYPE_FILTER: memcpy(pair->strvalue, ptr, attrlen); pair->lvalue = attrlen; if ( !first_pair ) first_pair = pair; else prev->next = pair; prev = pair; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: memcpy(&lvalue, ptr, sizeof(int)); pair->lvalue = ntohl(lvalue); if ( !first_pair ) first_pair = pair; else prev->next = pair; prev = pair; break; } ptr += attrlen; length -= attrlen + 2; } if (debug_flag) rad_attr_out(first_pair); authreq->request = first_pair; return authreq; } char * strlower (char *s) { char c,*src = s; while (*(s)) {c = tolower(*s); *(s++) = c;}; return src; } char * strupper (char *s) { char c,*src = s; while (*(s)) {c = toupper(*s); *(s++) = c;}; return src; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Allocate memory for a new tree node. ** ** FORMAL PARAMETERS: ** ** key: key of a new node ** node: new node ** data: pointer to data ** ** RETURN VALUE: ** ** VMS condition code,longword ** **-- */ int tree_node_get_vm ( TREE_NODE_KEY *key, TREE_NODE * *node, void *data ) { int status,sz = sizeof(TREE_NODE); if ( !(1 & (status = lib$get_vm(&sz,node))) ) return status; (*node)->ptr = data; return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Get configuration logical name value, the logical must be defined as ** /SYSTEM/EXECUTIVE. ** ** FORMAL PARAMETERS: ** ** lognam: a logical name ** buf: a work buffer ** buflen: a work buffer length ** ** RETURN VALUE: ** ** <> NULL - ok ** NULL - error ** **-- */ $DESCRIPTOR(tabnam, "LNM$SYSTEM_TABLE"); char * get_logname ( char *lognam, char *buf, short buflen ) { int status; short retlen = 0; struct dsc$descriptor log_dsc; char accmode = PSL$C_EXEC; ILE3 itmlst[] = {{buflen,LNM$_STRING,buf,&retlen},{0,0,0,0}}; /* ** Initialization */ INIT_SDESC(log_dsc,strlen(lognam),lognam); /* ** Calling system service to retrieve a value of the given logical name */ if ( !(1 & (status = sys$trnlnm (0,&tabnam,&log_dsc,&accmode,&itmlst))) ) return NULL; /* ** Add a ZERO at and of the string */ *(buf+retlen) = '\0'; return buf; } void hex2bin ( unsigned char * bin, unsigned char * sts, unsigned short len ) { char c = 0; for(;len;len--,sts++) { if ( isdigit(*sts) ) c = *sts - '0'; else if ( isupper(*sts) ) c = *sts - 'A' + 10; else c = *sts - 'a' + 10; if ( len & 1 ) *(bin++)|= c; else *bin = c<<4 ; } } void bin2hex ( unsigned char * sts, unsigned char * bin, unsigned short len ) { char l,h; for(;len;len--,sts += 2,bin++) { h = (*bin) >>4; l = (*bin) & 0x0F; *sts = (h < 10)?h + '0':h + 'a' - 10; *(sts+1)= (l < 10)?l + '0':l + 'a' - 10; } } struct _list * look4realm ( struct _list *realm_list, char *sts, short stslen ) { for (;realm_list;realm_list = realm_list->next) { /* ** Check for DEFAULT realm */ if ( !(stslen + realm_list->data.dsc$w_length) ) return realm_list; if ( (stslen == realm_list->data.dsc$w_length) && !strncasecmp(sts,realm_list->data.dsc$a_pointer,stslen) ) return realm_list; } return NULL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Performs a transformation of the Attribute-Valie Pair (AVP,as it's take ** place in the RADIUS packet) to Attribute-Value Entry (internal ** representaion of the AVP). ** ** FORMAL PARAMETERS: ** ** avp: A pointer to buffer with the AVP ** avplen: A length of the buffer ** ave: A pointer to AVP Entries ** avpcnt: A count of AVP ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int avp2ave ( unsigned char *avp, unsigned short avplen, AVP_ENTRY * *ave ) { int status; DICT_ATTR *attr; AVP_ENTRY *first_pair = NULL,*prev = NULL, *pair = NULL; unsigned lvalue,vendor,vsa; unsigned char tmpbuf [ 32 ],attrlen,attribute,vsalen; if ( !avplen || !ave ) return SS$_NORMAL; for(int i = 0;(i < 128) && (avplen >= 3);i++ ) { /* ** A local initalization of work variables */ vendor = vsa = vsalen = attr = 0; attribute= *avp++; attrlen = *avp++; /* ** Skip processing of too short attributes */ if ( attrlen < 2 ) break; if ( (attrlen -= 2) > min(avplen,RAD$K_MAXATTRSZ) ) return rad_log(RAD_RECV_ATTRTOLONG,attribute,"",0, attrlen, min(avplen,RAD$K_MAXATTRSZ) ); /* ** Special handling of a VSA, if any problem is occured just treat VSA as usual */ if ( attribute == ATTR$K_VSA ) { /* ** Get a vendor code */ vendor = *((int *)avp); vendor = ntohl(vendor); /* ** Get a VSA itself and avplen */ if ( vendor != VEND$K_USR ) { vsa = *(avp+4); /* ** Check a VSA avplen */ if ( (min(attrlen,AUTH_STRING_LEN) <= (vsalen = *(avp+5))) || (vsalen < 2) ) vsalen = attrlen - 4; } else { /* ** Special handling USR VSA attributes */ vsa = ntohl(*((int *) (avp+4))); vsalen = 4; } /* ** Lookup the attribute/VSA in the dictionary */ if ( vsa && vendor ) { if ( !(attr = dict_attrget(vsa,vendor,NULL,0)) ) { rad_log(RAD_RECV_INVATTR,vsa,vendor,"",0); vendor = vsa = vsalen = 0; } else { attribute = vsa; attrlen = vendor==VEND$K_USR?4:vsalen - 2; avp += vendor==VEND$K_USR?8:6; avplen -= vendor==VEND$K_USR?8:6; } } else vendor = vsa = vsalen = 0; } if ( !attr && !(attr = dict_attrget(attribute,vendor,NULL,0)) ) { rad_log(RAD_RECV_INVATTR,attribute,vendor,"",0); avp += attrlen; avplen -= attrlen + 2; continue; } if ( !(1 & (status = rad_get_vm_pair(&pair))) ) lib$signal(status); pair->ave$a_attr = attr; pair->ave$a_next = NULL; switch(attr->type) { case ATTRTYP$K__STRING: pair->ave$b_val[attrlen] = '\0'; case ATTRTYP$K_FILTER: memcpy(pair->ave$b_val, avp, attrlen); pair->ave$l_val = attrlen; if ( !first_pair ) first_pair = pair; else prev->ave$a_next = pair; prev = pair; break; case ATTRTYP$K__INTEGER: case ATTRTYP$K_IPADDR: memcpy(&lvalue, avp, sizeof(int)); pair->ave$l_val = ntohl(lvalue); if ( !first_pair ) first_pair = pair; else prev->ave$a_next = pair; prev = pair; break; } avp += attrlen; avplen -= attrlen + 2; } if ( *ave && pair ) pair->ave$a_next = *ave; *ave = first_pair; return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Make a buffer with AV-Pairs (buf,in wired format) by translating ** AVPair-List (AVE) ** ** FORMAL PARAMETERS: ** ** ave: An pointer to AVP Entries list ** buf: A pointer to buffer to be filled ** bufsz: A size of the buffer ** datalen: An actula data len in the filled buffer ** avpcnt: A number of processed AVP Entries. ** ** ** RETURN VALUE: ** ** VMS Condition code ** **-- */ int ave2avp ( AVP_ENTRY *ave, unsigned char *buf, unsigned short bufsz, unsigned short *datalen, unsigned char *avpcnt ) { short len = 0,retlen = 0,attlen = 0, i; char *lenp,*bufp = buf; /* Format of VSA, RFC 2138 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type | Length | Vendor-Id +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Vendor-Id (cont) | Vendor type | Vendor length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Attribute-Specific... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+- */ for(*datalen = *avpcnt = 0, bufsz = min(bufsz,RADVMS$K_RAD_MAXBODYSZ); ave; *datalen += retlen, (*avpcnt)++,retlen = 0) { /* ** Check free space in output buffer */ if ( (*datalen + 8) > bufsz ) break; lenp = NULL; attlen = 0; if ( ave->ave$a_attr->vendor ) { *buf++ = ATTR$K_VSA; lenp = buf++; *((int *) buf) = htonl(ave->ave$a_attr->vendor); buf += 4; attlen += 6; } if ( ave->ave$a_attr->vendor && (ave->ave$a_attr->vendor == VEND$K_USR) ) { *((int *) buf) = htonl(ave->ave$a_attr->id); buf += 4; attlen += 2; } else *buf++ = ave->ave$a_attr->id; switch(ave->ave$a_attr->type) { case PW_TYPE_FILTER: case PW_TYPE_STRING: len = min(ave->ave$l_val,RAD$K_MAXATTRSZ); /* ** Check free space in output buffer */ if ( (retlen + attlen + len) > bufsz ) break; if ( ave->ave$a_attr->vendor != VEND$K_USR ) *buf++ = len + 2; memcpy(buf,ave->ave$b_val,len); buf += len; attlen += len + 2; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: len = 4; /* ** Check free space in output buffer */ if ( (retlen + attlen + len) > RAD_MAXBODYSZ ) break; if ( ave->ave$a_attr->vendor != RAD_VENDOR_K_USR ) *buf++ = sizeof(int) + 2; *((int *) buf) = htonl(ave->ave$l_val); buf += len; attlen += len + 2; break; default: break; } if ( ave->ave$a_attr->vendor ) *lenp = ave->ave$a_attr->vendor != RAD_VENDOR_K_USR?len + 8:len + 10; /* ** Add a length of a processed attribute to total length of a packet ** and get pointer to next AVP in list. */ retlen += attlen; ave = ave->ave$a_next; } return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Remove attrubute from AVP-pair list, free memory has been allocated for the ** removed attribute. ** ** FORMAL PARAMETERS: ** ** avl: AV-Pair list ** ave: AV-Pair list to be excluded from the 'avl' ** ** RETURN VALUE: ** ** VMS Condition code ** **-- */ int ave_del4list ( AVP_ENTRY * *avl, AVP_ENTRY * ave ) { int status = STR$_NOMATCH; /* ** Run over the exclude list */ for(; ave; ave = ave->ave$a_next) { /* ** Run over the source AV-Pair list */ for (AVP_ENTRY *prev = *avl, *cur = *avl ;cur;) { if ( (cur->ave$a_attr->vendor == ave->ave$a_attr->vendor) && (cur->ave$a_attr->id == ave->ave$a_attr->id) ) { if ( cur == *avl ) *avl = prev = cur->ave$a_next; else if ( !cur->ave$a_next ) prev->ave$a_next = NULL; else prev->ave$a_next = cur->ave$a_next; cur->ave$a_next = NULL; rad_free_vm_pair(cur); cur = prev; status = STR$_MATCH; } else {prev = cur; cur = cur->ave$a_next;} } } return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Add attrubutes to end of AVP-pair list. ** ** FORMAL PARAMETERS: ** ** dst: AV-Pair list ** src: AV-Pair list to be include to the 'avl' ** ** RETURN VALUE: ** ** VMS Condition code ** **-- */ int ave_add2list ( AVP_ENTRY * *dst, AVP_ENTRY * src ) { AVP_ENTRY *cur = *dst; /* ** Run to end of the main AV-Pair list */ for (; cur && cur->ave$a_next; cur = cur->ave$a_next); /* ** Add entries from the 'src' list to the 'dst' list */ for (; src; src = src->ave$a_next) { if ( !(*dst) ) { rad_get_vm_pair(dst); cur = *dst; } else { rad_get_vm_pair(&cur->ave$a_next); cur = cur->ave$a_next; } *cur = *src; } return SS$_NORMAL; } void md5_calc1 ( char *output, char *input, short inlen, char *input1, short inlen1 ) { MD5_CTX context; MD5Init(&context); MD5Update(&context, input, inlen); MD5Update(&context, input1, inlen1); MD5Final(output, &context); } void md5_calc ( char *output, char *input, int inlen ) { MD5_CTX context; MD5Init(&context); MD5Update(&context, input, inlen); MD5Final(output, &context); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Output to SYS$OUTPUT formated string by using $FAOL. ** ** FORMAL PARAMETERS: ** ** fmt: A format string, see $FAO/$FAOL ** ...: A parameter list ** ** RETURN VALUE: ** ** None ** ** SIDE EFFECTS: ** ** Used big buffer to forms a result string, in DECThreads environment it need to ** increase a stack size. ** **-- */ void rad_put_output ( DSC$DESCRIPTOR *fmt, ... ) { int status; char buf [1024]; va_list ap; int argc,argl[32],idx; struct dsc$descriptor buf_dsc; /* ** Extract parameters from a stack and puti its to array */ va_start(ap,fmt); for (idx = 0,va_count(argc); idx < argc; idx++) argl[idx] = va_arg(ap,unsigned); va_end(ap); /* ** Prepare buffer, and formate string */ INIT_SDESC(buf_dsc,sizeof(buf),buf); if ( !(1 & (status = sys$faol(fmt,&buf_dsc.dsc$w_length,&buf_dsc,argl))) ) lib$signal(status); /* ** Push the formated string to SYS$OUTPUT */ if ( !(1 & (status = lib$put_output(&buf_dsc))) ) lib$signal(status); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Fromat a string by using $FAO/$FAOL directives, add time stamp at begin of message ** output to SYS$OUTPUT formated VMS error string by using, send a copy ** of the string to OPCOM. A global variable opcomlvlv are used for filtering ** messages to send to OPCOM. ** ** FORMAL PARAMETERS: ** ** msgid: A message id (VMS condition code) ** ...: A parameter list ** ** RETURN VALUE: ** ** VMS condition code given as input parameter (msgid) ** ** SIDE EFFECTS: ** ** Used big buffer to forms a result string, in DECThreads environment it need to ** increase a stack size. Required an additional priveleges to performs OPCOM ** messaging. ** **-- */ int rad_log ( int msgid, ... ) { int status,retvalue = msgid; va_list args; char buf[1024] = {"!%D "},outadr[4]; struct dsc$descriptor opr_dsc,buf_dsc,fao_dsc; int argc,argl[32] = {0},idx,flag=15,lvl; struct msgbuf { char type; char target[3]; int rqstid; char msg_buf[1024]; } msgbuf = { OPC$_RQ_RQST, OPC$M_NM_CENTRL|OPC$M_NM_CLUSTER|OPC$M_NM_NTWORK}; /* ** Extract a message code by using msgid */ INIT_SDESC(fao_dsc,sizeof(buf)-4,&buf[4]); if ( !(1 & (status = sys$getmsg (msgid,&fao_dsc.dsc$w_length,&fao_dsc,flag,&outadr))) ) lib$signal(status); /* ** Extract a given parameter list from stack, and fill array */ va_start(args,msgid); for (idx = 1,va_count(argc); idx < argc; idx++) argl[idx] = va_arg(args,unsigned); va_end((char *) args); /* ** Format a string with the parameters */ fao_dsc.dsc$a_pointer -=4; fao_dsc.dsc$w_length +=4; INIT_SDESC(buf_dsc, sizeof(msgbuf.msg_buf),msgbuf.msg_buf); if ( !(1 & (status = sys$faol(&fao_dsc,&buf_dsc.dsc$w_length,&buf_dsc,argl))) ) lib$signal(status); /* ** Push the string to SYS$OUTPUT */ lib$put_output(&buf_dsc); if ( slogctx && !(1 & (status = syslog_enq(slogctx,SLOG_FAC$K_AUTH,SLOG_SEV$K_NOTICE,NULL, NULL,&buf_dsc))) ) lib$signal(status); /* ** Comparing a severity level of message with the opcomlvl variable */ flag = $VMS_STATUS_SEVERITY(retvalue); if ( flag < opcomlvl ) return retvalue; /* ** Send message to OPCOM */ INIT_SDESC(opr_dsc,8+buf_dsc.dsc$w_length,(char *) &msgbuf); if ( !(1 & (status = sys$sndopr (&opr_dsc,0))) ) lib$signal(status); return retvalue; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Put formated message to standard output device (SYS$OUTPUT). ** ** FORMAL PARAMETERS: ** ** fao: FAO format string ** ...: FAO's arguments... ** RETURN VALUE: ** ** VMS condition code ** ** **-- */ void _rad_trace ( unsigned char * __module, unsigned line, unsigned char * fao, ... ) { int status,idx = 0; va_list ap; char buf[4096] = {"!%D [!AZ\\!UL] "},out[4096]; struct dsc$descriptor buf_dsc,fao_dsc; int argc,argl[32] = {0}; /* ** Form FAO string */ strcat(buf,fao); status = strnlen(buf,sizeof(buf)); INIT_SDESC(fao_dsc,status,buf); /* ** Reorganize parameters list for $FAOL */ va_start(ap,fao); argl[idx++] = 0; argl[idx++] = __module; argl[idx++] = line; for (va_count(argc); idx < argc; idx++) argl[idx] = va_arg(ap,unsigned); va_end((void *) ap); /* ** Format a message, put it to SYS$OUTPUT */ INIT_SDESC(buf_dsc, sizeof(out),out); if ( !(1 & (status = sys$faol(&fao_dsc,&buf_dsc.dsc$w_length,&buf_dsc,argl))) ) lib$signal(status); lib$put_output(&buf_dsc); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Performs a comparisons given value against the values list. ** ** FORMAL PARAMETERS: ** ** val: a value to compare ** ...: a list of ** RETURN VALUE: ** ** 0: 'val' did not match any value from the list ** i: non-zero index of matched value from the list ** ** **-- */ inline int _match_int ( unsigned int val, ... ) { int idx, argc, matchval; va_list ap; va_start(ap,val); va_count(argc); for (idx = 0, argc--; argc; idx++, argc--) { matchval = va_arg(ap,unsigned); if ( matchval == val ) return (idx+1); } va_end((void *) ap); return 0; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Encode the Tunnel-Password from the text representative into the "wire format" (binary), ** a result of encoding is placed into the 'tpwd'. ** ** FORMAL PARAMETERS: ** ** tpwd: Tunnel-Password AV entry ** client: A CLIENT ** ** ** RETURN VALUE: ** ** None. ** ** **-- */ static unsigned int salt_offset = 0; int _rad_tpwdenc ( AVP_ENTRY * tpass, CLNT_ENTRY * client, unsigned char * vector ) { unsigned status = SS$_NORMAL, len; RAD$_TUNN * tn = (RAD$_TUNN *) &tpass->ave$b_val; MD5_CTX context, old; unsigned __int64 *octap, digest[RAD$K_VECTORSZ/sizeof(unsigned __int64)]; if ( tpass->ave$l_len > 127 ) { status = SS$_BUFFEROVF; len = 127; } else len = tpass->ave$l_len; memmove(&tn->tunn$b_tpwd[1],&tpass->ave$b_val,len); memset(&tn->tunn$b_tpwd[len+1],0,16); tn->tunn$b_len = (unsigned char) len ; len = (1+(len/16))*16; tpass->ave$l_len= (unsigned char) len + 2; /* ** Generate salt. */ __ATOMIC_INCREMENT_LONG(&salt_offset); tn->tunn$b_salt[0] = (0x80 | ( (salt_offset & 0x0f) << 3) | (rand() & 0x07)); tn->tunn$b_salt[1] = (unsigned char) rand(); MD5Init(&old); MD5Update(&old,&client->secret, client->secretlen); for (int n = 0; n < len; n += RAD$K_CHUNKSZ) { context = old; if ( !n ) { MD5Update(&context,vector, RAD$K_VECTORSZ); MD5Update(&context,&tn->tunn$b_salt, 2); } else MD5Update(&context, &tn->tunn$b_tpwd[n - RAD$K_CHUNKSZ], RAD$K_CHUNKSZ); MD5Final(&digest,&context); octap = &tn->tunn$b_tpwd[n]; (*octap) ^= digest[0]; (*(octap+1)) ^= digest[1]; } return status; } /* * Decode Tunnel-Password encrypted attributes. * * Defined in RFC-2868, this uses a two char SALT along with the * initial intermediate value, to differentiate it from the * above. */ int _rad_tpwddec ( AVP_ENTRY * tpass, CLNT_ENTRY * client, unsigned char * vector ) { int status = SS$_NORMAL; MD5_CTX context, old; unsigned i, n, len = tpass->ave$l_len, reallen; RAD$_TUNN *tn = (RAD$_TUNN *) &tpass->ave$b_val; unsigned __int64 *octap, digest[RAD$K_VECTORSZ/sizeof(unsigned __int64)]; /* ** We need at least a salt. */ if ( tpass->ave$l_len < 2 ) return SS$_INVLOGIN; /* ** There's a salt, but no password. Or, there's a salt ** and a 'data_len' octet. It's wrong, but at least we ** can figure out what it means: the password is empty. ** ** Note that this means we ignore the 'data_len' field, ** if the attribute length tells us that there's no ** more data. So the 'data_len' field may be wrong, ** but that's ok... */ if ( tpass->ave$l_len <= 3 ) { tn->tunn$b_len = 0; return SS$_INVLOGIN; } len -= 2; /* discount the salt */ MD5Init(&old); MD5Update(&old,&client->secret, client->secretlen); for (int n = 0; n < len; n += RAD$K_CHUNKSZ) { context = old; if ( !n ) { MD5Update(&context,vector, RAD$K_VECTORSZ); MD5Update(&context,&tn->tunn$b_salt, 2); } else MD5Update(&context,&tn->tunn$b_tpwd[n - RAD$K_CHUNKSZ], RAD$K_CHUNKSZ); MD5Final(&digest,&context); octap = &tn->tunn$b_tpwd[n]; (*octap) ^= digest[0]; (*(octap+1)) ^= digest[1]; } memmove(&tpass->ave$b_val,&tn->tunn$b_tpwd,tn->tunn$b_len); tpass->ave$l_len -= 2; return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Copy a data field from the . Advance the buf ** past the data field. ** ** FORMAL PARAMETERS: ** ** dst: destination string to copy to ** src: source buffer to copy field from ** ** RETURN VALUE: ** ** length of the copyed field ** **-- */ int fieldcpy ( char * dst, char ** src ) { char *psrc = *src, *pdst = dst; if ( *psrc == '"' || *psrc == '\'' ) { psrc++; while ( (*psrc != '"' && *psrc != '\'') && *psrc && *psrc != '\n') *dst++ = *psrc++; *dst = '\0'; if ( *psrc == '"' || *psrc == '\'' ) psrc++; *src = psrc; return (dst - pdst); } while (*psrc != ' ' && *psrc != '\t' && *psrc && *psrc != '\n' && *psrc != '=' && *psrc != ',') *dst++ = *psrc++; *dst = '\0'; *src = psrc; return (dst - pdst); } #if 0 void main (void) { int status; AVP_ENTRY tpass = { .ave$b_val = {"1234567"}, .ave$l_len = 7}; CLNT_ENTRY client = { .secret = {"1234567"}, .secretlen = 7}; char digest [16] = {0,1,2,3,4,5,6,7,8,9,0xA,0xB,0xC,0xD,0xE,0xF}; status = _match_int (ATTR$K_TUNNPWD, ATTR$K_TUNNTYPE, ATTR$K_TUNNMEDTYPE, ATTR$K_TUNNCLIENDP, ATTR$K_TUNNSRVENDP, ATTR$K_TUNNPWD, ATTR$K_TUNNPVTGID, ATTR$K_TUNNASSID, ATTR$K_TUNNPREFS, ATTR$K_TUNNCLIAUTHID, ATTR$K_TUNNSRVAUTHID); status = _rad_tpwdenc(&tpass,&client,&digest); status = _rad_tpwddec(&tpass,&client,&digest); } #endif