/* dot1xAuthSvrLib.c - IEEE 802.1X authentication server interface */ /* Copyright 2002-2008, 2010,2012,2014,2016 Wind River Systems, Inc. */ #include "copyright_wrs.h" /* modification history -------------------- 02s,13jun16,chm Fix for VXW6-85382. 02r,18sep14,lzq Fix Coverity issue. 02q,05may12,zan Fix for WIND00340951. Check ip address before remove radius server. 02p,12jan12,zan Fix for WIND00322248 02o,11nov10,hfc fix defect WIND00232734 02n,28may10,hfc EAP-Update ,req:WIND00198105 02m,04apr10,kxr Coverity changes 02l,04mar10,kxr Replace CCI calls with openssl 02k,07oct08,dwb Fix for WIND00136166 02j,10sep08,dwb Restructuring socket code to adjust for ipcom_socket changes. 02i,28aug08,dwb Reverting to real ipcom sockets 02h,19mar08,kjn Remove 2:nd argument from ipcom_pkt_free() 02g,05feb08,dwb Changes for SMP 02f,29oct07,dwb Fix for WIND00109453 02e,23oct07,mry Fixed dual stack support. 02d,27sep07,mry Add fix for WIND00105532 02c,02mar07,dwb Added dot1xAuthSvrLibProcessVendorMsg to properly decrypt vendor specific keys from RADIUS access accept packet, See defect WIND00089466 or Fix WIND00089467 02b,06feb07,dwb Fix for WIND00085529 02a,04dec06,dwb Fixed to walk chained eap segments and various cleanup work 01z,31oct06,dwb Changes for port to ipnet radius client 01y,06arp06,rb Fix for SPR 119970 - Authenticator data abort 01x,05mar06,tat removing include of rwos.h 01x,17mar06,rb Fix for SPR 119191 - Coverity errors 01w,17jun05,eja Fix Trace Bug B0562 01v,12may05,alm Updating comments 01u,10may05,eja Remove dead code 01t,20apr05,eja Make changes for updated dot1xMemFree macro 01s,30mar05,eja Fix DIAB warnings and SWAT bugs 01r,14feb05,eja remove call to clientIpAddrGet(radius_configuration_text) in init() method to get around mem part alloc error 01q,03jan05,eja Fix SWAT warnings 01p,30jun04,dxb Included string.h to fix compile dependency error on Base MSP. 01o,05dec03,dxb Fix for SPR 92134 - RADIUS Server Failover 01n,04sep03,ggg Remove warnings for diab MIPS and SH 01m,05may03,ggg Update documentation 01l,22apr03,ggg Remove Cisco AVP string check 01k,21apr03,ggg File name, location change 01j,14apr03,ggg Fix non-key VSA handling (SPR 87624) 01i,30jul02,ggg Update DBG_PRINT for Diab and Gnu varargs handling 01h,08jul02,ggg Undef debug by default 01g,08jul02,ggg Correct file name in comment 01f,21jun02,myz removed the code referencing RADIUS private header files. 01e,10jun02,myz reworked the request error handling 01d,06jun02,myz removed potential compiler warnings 01c,31may02,myz modified dot1xRadiusCfg() function 01b,30may02,myz change dot1xAuthServersRemoved to dot1xAuthServersRemove 01a,05Feb02,myz written */ /* DESCRIPTION This library provides the routines for delivering the EAP packet to the authentication server and the mechanism for receiving the results from it. The application must provide the required information in one of the arguments when calling the API to deliver the EAP packet. The results from the RADIUS server are sent back through the user-specified callback. */ #include "vxWorks.h" #include "stdio.h" #include "string.h" #include "sllLib.h" #include "netinet/in.h" #include "taskLib.h" #define __EAP__ #define bool BOOL typedef unsigned char BYTE; #include #include #include #include #include #include #include "dot1xAuthSvrLib.h" #include "dot1xKeyLib.h" #include #include #include /* eliminate compiler warning re: redefine of NULL */ #undef IPCRYPTO_USE_TYPE_MAPPING #include /* Make sure we have at least one type of network support */ #if !defined(INET) && !defined(INET6) #error "Must define at least IPCOM_USE_INET or IPCOM_USE_INET6" #endif /* EAP Message defines */ #define DOT1X_AUTH_RADIUS_ATTR_HEADER_LEN 2 #define DOT1X_AUTH_RADIUS_ATTR_TYPE_IDX 0 #define DOT1X_AUTH_RADIUS_ATTR_LEN_IDX 1 #define DOT1X_AUTH_RADIUS_ATTR_DATA_IDX 2 /* Logging defines */ #define DOT1X_LOG_FATAL "(f) " __FILE__ " (s) FATAL (r) " #define DOT1X_LOG_ERROR "(f) " __FILE__ " (s) ERROR (r) " #define DOT1X_LOG_INFO "(f) " __FILE__ " (s) INFO (r) " #define DOT1X_LOG_DEBUG "(f) " __FILE__ " (s) DEBUG (r) " #if defined(_WRS_CONFIG_DOT1X_AS_DEBUG) && !defined(DOT1X_AS_DEBUG) #define DOT1X_AS_DEBUG #endif #ifdef DOT1X_AS_DEBUG static char *svrRsp[] = { "Unknown response", "access accept code", "access reject code", "access challenge code", "accounting response code", "access timeout", "access cancelled", "accounting timeout", "accounting cancelled", "accounting request" }; int dot1xAsDebug = 1; #define DBG_PRINT if (dot1xAsDebug) printf #else #ifdef __DCC__ #define DBG_PRINT(...) #else /* __GNUC__ (and others?) */ #define DBG_PRINT(args...) #endif /* __DCC__*/ #endif /* DOT1X_AS_DEBUG */ /*** Local defines ***/ /* This library's initialization state */ #define DOT1X_AS_FALSE 0 /* not initialized */ #define DOT1X_AS_TRUE 1 /* initialized */ #define DOT1X_AS_IN_PROGRESS 2 /* initialization in progress */ /* Server set state */ #define DOT1X_SS_DONE 0 /* Server info configuration is complete */ #define DOT1X_SS_IN_PROGRESS 1 /* Server info set is in progress */ /* requestMsgNodeGet() retriving type */ #define GET_TYPE_FIND 1 /* simply find the node */ #define GET_TYPE_FIND_AND_REMOVE 2 /* find and remove the node from list */ /* RCVD_ATTR structure index definition */ #define ATTR_EP_INX 0 /* index to store EAP attribute */ #define ATTR_SK_INX 1 /* index to store send key */ #define ATTR_RK_INX 2 /* index to store receive key */ #define ATTR_ST_INX 3 /* index to store STATE attr */ #define NUM_OF_ATTRS_SEND 4 /* the total number of attribute can be sent to user */ /* RADIUS Vendor-Specific attribute defines */ #define VENDOR_ID_CISCO 9 #define VENDOR_ID_MICROSOFT 311 #define VENDOR_TYPE_CISCO_AVPAIR 1 #define VENDOR_TYPE_MS_MPPE_SEND_KEY 16 #define VENDOR_TYPE_MS_MPPE_RECV_KEY 17 /* RADIUS Vendor-Specific processing defines */ #define DOT1X_AUTH_MD5_RESULT_LENGTH 16 #define DOT1X_AUTH_RADIUS_PASSWORD_MODULUS 16 #define DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR 16 #define DOT1X_AUTH_VENDOR_SALT_LENGTH 2 #define DOT1X_AUTH_VENDOR_SPECIFIC_ID_LENGTH 4 #define DOT1X_AUTH_VENDOR_TYPE_LENGTH 1 #define DOT1X_AUTH_VENDOR_LENGTH 1 #define DOT1X_AUTH_VENDOR_KEY_LENGTH 1 /* Misc. defines */ #define CFG_GEN_STR_MAX_LEN 0x20 /* max generic config string length*/ #define CFG_SECRET_STR_MAX_LEN 0x50 /* max secret string length */ /* ipradius packet size */ #define IPRADIUS_PKT_SIZE 1472 /* For splitting up eap packets into multiple attributes */ #define DOT1X_AUTH_MAX_EAP_FRAG_SIZE 253 /* = MAX octets allowed in a RADIUS attribute value field */ /*** typedefs ***/ /* Request message node for holding info before a reply is received */ typedef struct { SL_NODE node; /* single link list node */ UINT32 seqNum; /* requestHandle of radius_server_request() */ FUNCPTR pUserCallback; /* callback for receiving RADIUS server result*/ UINT32 reqHandle; /* User message handle */ } AUTH_REQUEST_MSG_NODE; /* The data structure to control interaction with the RADIUS server */ typedef struct { UINT32 seqNum; /* The last requestHandle */ UINT32 nasIpAddr; /* Client IP address in network byte order */ SEM_ID semId; /* semaphore for controlling the shared data */ BOOL serverError; /* RADIUS server error indicator */ char *pkt; /* RADIUS packet */ Ip_fd radius_fd; /* RADIUS IPv4 socket fd */ /* RADIUS server address */ /* Server address structure */ union Ip_sockaddr_union radius_server_addr[DOT1X_AUTH_MAX_RADIUS_SERVERS]; /* Primary/Secondary server information */ Ip_u32 radius_server_index[DOT1X_AUTH_MAX_RADIUS_SERVERS]; /* Cache for server the user specified server information */ DOT1X_AUTH_RADIUS_SERVER_INFO server_info[DOT1X_AUTH_MAX_RADIUS_SERVERS]; UINT32 num_radius_servers; SL_LIST list; /* request message node list */ } AUTH_SERVER_ACTION_CONTROL; /* RADIUS initialization configuration */ typedef struct { UINT8 * pClientIpAddr; /* client IP address */ UINT8 * pClientPort; /* client UDP port */ UINT8 * pServerIpAddr; /* additional RADIUS server IP address */ UINT8 * pServerPort; /* additional RADIUS server UDP port */ UINT8 * pPrintfEnable; /* print message enable or disable */ UINT8 * pSecret; /* shared secret */ } DOT1X_AS_INIT_CONFIG_STR; /* ipcom IPC Message structures */ #define IPCOM_IPC_MSG_AUTH_INPUT 1001 /* type: Ipcom_ipc_msg_auth */ typedef struct Ipcom_msg_auth_struct { int msgtype; int len; /* Length of data[] contents. */ /* Address from which the data came from */ union Ip_sockaddr_union addr; Ip_u8 data[1]; /* Variable length data holder. */ } Ipcom_msg_auth; /*** local data ***/ LOCAL AUTH_SERVER_ACTION_CONTROL authServerCtrl; LOCAL int authLibInitDone = DOT1X_AS_FALSE; LOCAL int authLibServerSetDone = DOT1X_SS_DONE; /*** globals ***/ DOT1X_AS_INIT_CONFIG_STR dot1xAsInitConfigStr = {0,0,0,0,0,0}; /* forward declarations */ LOCAL Ip_err ipradiusRequestNormalReply(int, struct Ipradius_request_struct *, Ipradius_header *, Ipradius_header *,Ipradius_server *); LOCAL void ipradiusRequestErrorReply(UINT32,UINT32); LOCAL Ip_u8 * dot1xAuthSvrLibProcessEAPMsg(Ipradius_header *response_packet, Ip_u8 *pAttr, int *dataLen,UINT8 *pBuf); LOCAL int vendorAttributeKeyExtract (UINT8 **, int *); LOCAL AUTH_REQUEST_MSG_NODE * requestMsgNodeGet (SL_LIST *, UINT32,int); LOCAL STATUS dot1xAuthSvrLibProcessVendorMsg(UINT8* pVendorMsg, UINT32 pVendorMsgLen, UINT8 *requestAuth, char *secret, UINT32 secretLen); LOCAL BOOL dot1xAuthSvrLibMd5Decryption (unsigned char *cptr_identifier, USHORT identifier_length, unsigned char *p_secret, USHORT secret_length, unsigned char *p_data, USHORT data_length); LOCAL BOOL dot1xAuthSvrLibMd5Digest ( unsigned char *input, ULONG input_length, unsigned char *result, ULONG result_length); LOCAL IPCOM_PROCESS(tDot1xRecv); /* externals */ /* Public methods */ /**************************************************************************** * dot1xAuthServerLibInit - initialize this authentication server library * * This routine performs initialization of this library. It checks * the RADIUS interface configuration, and initializes the control data * structure. This routine is automatically called by the send API * dot1xAuthServerEapMsgSend() if it is not invoked previously. * * RETURNS: OK or ERROR if initialization is not done. */ STATUS dot1xAuthServerLibInit ( DOT1X_AUTH_RADIUS_SERVER_INFO *serverInfo ) { /* Atomically check the initialization flag and update it to * in progress if it is not already. This test is safe with repect to * both interrupts and accesses of other processors. i.e. SMP situations */ if(vxAtomicCas(&authLibInitDone,DOT1X_AS_FALSE,DOT1X_AS_IN_PROGRESS) == TRUE) { /* No server info, so were done */ if ( serverInfo == NULL ) { authLibInitDone = DOT1X_AS_FALSE; return ERROR; } memset(&authServerCtrl,0,sizeof(AUTH_SERVER_ACTION_CONTROL)); authServerCtrl.radius_fd = IP_INVALID_SOCKET; if ((authServerCtrl.semId = semBCreate(SEM_Q_PRIORITY, SEM_FULL)) ==NULL) { DBG_PRINT("RadiusIF: Error: Can't create the" " semaphore\n"); authLibInitDone = DOT1X_AS_FALSE; return ERROR; } authServerCtrl.serverError = FALSE; #ifdef DOT1X_AS_DEBUG { UINT32 ipAddr = ntohl(authServerCtrl.nasIpAddr); DBG_PRINT("RadiusIF: NAS IP Address:%d.%d.%d.%d\n", ipAddr >> 24, (ipAddr >> 16) & 0xFF, (ipAddr >> 8) & 0xFF, ipAddr & 0xFF); } #endif authServerCtrl.seqNum = 1; sllInit(&authServerCtrl.list); authServerCtrl.pkt = malloc(IPRADIUS_PKT_SIZE); if (authServerCtrl.pkt == IP_NULL) { DBG_PRINT("Failed to create packet data buffer\n"); authLibInitDone = DOT1X_AS_FALSE; return ERROR; } authLibInitDone = DOT1X_AS_TRUE; /* Set the server info. Note that this function configures * the socket to communitcate with the RADIUS server */ if( dot1xAuthServerLibRadiusServerSet(serverInfo) == ERROR ) return (ERROR); return OK; } else { if (authLibInitDone == DOT1X_AS_IN_PROGRESS) { return ERROR; } } return OK; } /**************************************************************************** * dot1xAuthServerLibRadiusServerSet - Set the RADIUS server information * * This routine sets the RADIUS server information and configures the socket * to communicate with the RADIUS server * * RETURNS: OK or ERROR if configuration was not completed. */ STATUS dot1xAuthServerLibRadiusServerSet ( DOT1X_AUTH_RADIUS_SERVER_INFO *serverInfo ) { Ipradius_server server; UINT8 svrIdx = 0; union Ip_sockaddr_union addr; Ip_err retval; DBG_PRINT("authLibServerSetDone: %d\n",authLibServerSetDone); /* Return is no server info was specified */ if(serverInfo == NULL) return (OK); /* The user should not be calling this function unless * the server has been initialized */ if ( authLibInitDone != DOT1X_AS_TRUE ) return (ERROR); /* Atomically check the configuration status flag and update it to * in progress if it is not already. This test is safe with repect to * both interrupts and accesses of other processors. i.e. SMP situations */ if(vxAtomicCas(&authLibServerSetDone,DOT1X_SS_DONE,DOT1X_SS_IN_PROGRESS) == TRUE) { authServerCtrl.num_radius_servers = 0; if( authServerCtrl.radius_fd != IP_INVALID_SOCKET) ipcom_socketclose(authServerCtrl.radius_fd); /* Set the server address info for the primary and secondary servers, * and add the servers to ipradius' server list */ while ( svrIdx < DOT1X_AUTH_MAX_RADIUS_SERVERS ) { /* Copy the server info so we have it for later */ authServerCtrl.server_info[svrIdx].timeout = serverInfo[svrIdx].timeout; authServerCtrl.server_info[svrIdx].retry_count = serverInfo[svrIdx].retry_count; authServerCtrl.server_info[svrIdx].domain = serverInfo[svrIdx].domain; authServerCtrl.server_info[svrIdx].port = serverInfo[svrIdx].port; /* Resolve the RADIUS server IPv4/IPv6 address */ if( (serverInfo[svrIdx].domain == IP_AF_INET) || (serverInfo[svrIdx].domain == IP_AF_INET6) ) { retval = ipcom_getsockaddrbyaddrname(serverInfo[svrIdx].domain, IP_FALSE /* use DNS */, serverInfo[svrIdx].server_addr, &authServerCtrl.radius_server_addr[svrIdx].sa); if (retval) { DBG_PRINT("dot1xAuthServerLibInit: invalid" "server address=%s' from domain = %d, err = %d\n", serverInfo[svrIdx].server_addr,serverInfo[svrIdx].domain, retval); authLibServerSetDone = DOT1X_SS_DONE; return OK; /* Do not halt system, just disable RADIUS login. */ } } else { DBG_PRINT("dot1xAuthServerLibInit: " "invalid domain: %d\n",serverInfo[svrIdx].domain); } /* Check if we need to remove the server from the ipradius * server list */ if( authServerCtrl.radius_server_index[svrIdx] != 0 ) { DBG_PRINT("Removing server from index: %d\n", authServerCtrl.radius_server_index[svrIdx]); server.index = authServerCtrl.radius_server_index[svrIdx]; if( ipradius_server_remove(&server) != IPCOM_SUCCESS ) { authLibServerSetDone = DOT1X_SS_DONE; return (ERROR); } authServerCtrl.radius_server_index[svrIdx] = 0; } DBG_PRINT("dot1xAuthServerLibInit:\n" "Configuring server info for server %d:\n" "Domain: %d\nAddress: %s\nPort: %d\nRetry Count: %d\n" "Timeout: %d\n",svrIdx,serverInfo[svrIdx].domain, serverInfo[svrIdx].server_addr,serverInfo[svrIdx].port, serverInfo[svrIdx].retry_count,serverInfo[svrIdx].timeout); /* Cache radius server port. */ authServerCtrl.radius_server_addr[svrIdx].sin.sin_port = ip_htons(serverInfo[svrIdx].port); authServerCtrl.num_radius_servers++; /* Check to if there are less than the number default servers */ if( serverInfo[svrIdx].endTag == DOT1X_AUTH_TAG_LAST_SERVER) break; svrIdx++; } /* while */ #ifdef INET /* Get RADIUS client socket if one has not already been created */ if( serverInfo[0].domain == IP_AF_INET ) { authServerCtrl.radius_fd = ipcom_socket(serverInfo[0].domain, IP_SOCK_DGRAM, 0); if (authServerCtrl.radius_fd == IP_INVALID_SOCKET) { DBG_PRINT("dot1xAuthServerLibInit: ipcom_socket() failed," " errno=%d", ipcom_errno); goto fail; } /* Bind to the socket */ memset(&addr, 0, sizeof(addr)); addr.sin.sin_len = authServerCtrl.radius_server_addr[0].sa.sa_len; addr.sin.sin_family = (Ip_sa_family_t)serverInfo[0].domain; if (ipcom_bind(authServerCtrl.radius_fd, &addr.sa, authServerCtrl.radius_server_addr[0].sa.sa_len) == IP_SOCKERR) { DBG_PRINT("dot1xAuthServerLibInit: ipcom_bind() failed" " errno=%d", ipcom_errno); goto fail; } } else #endif #ifdef INET6 if ( serverInfo[0].domain == IP_AF_INET6 ) { authServerCtrl.radius_fd = ipcom_socket(serverInfo[0].domain, IP_SOCK_DGRAM, 0); if (authServerCtrl.radius_fd == IP_INVALID_SOCKET) { DBG_PRINT("dot1xAuthServerLibInit: ipcom_socket() failed" " errno=%d", ipcom_errno); goto fail; } /* Bind to the socket */ memset(&addr, 0, sizeof(addr)); addr.sin6.sin6_port =authServerCtrl.radius_server_addr[0].sin.sin_port; addr.sin6.sin6_len = authServerCtrl.radius_server_addr[0].sa.sa_len; addr.sin6.sin6_family = (Ip_sa_family_t)serverInfo[0].domain; if (ipcom_bind(authServerCtrl.radius_fd, &addr.sa, authServerCtrl.radius_server_addr[0].sa.sa_len) == IP_SOCKERR) { DBG_PRINT("dot1xAuthServerLibInit: ipcom_bind() failed" " errno=%d", ipcom_errno); goto fail; } } else #endif DBG_PRINT("dot1xAuthServerLibRadiusServerSet: Invalid domain %d\n", serverInfo[0].domain); for( svrIdx = 0; svrIdx < authServerCtrl.num_radius_servers; svrIdx++) { /* Add IPCOM AUTH RADIUS server. */ DBG_PRINT("dot1xAuthServerLibRadiusServerSet: adding servers to" " radius server list\n"); server.index = 0; /* let ipradius choose server index. */ memcpy(&server.server_addr,&authServerCtrl.radius_server_addr[svrIdx], sizeof(union Ip_sockaddr_union)); server.server_type = IPRADIUS_ACCESS_SERVER; server.pollnum = serverInfo[svrIdx].retry_count; server.pollsec = serverInfo[svrIdx].timeout; if (strlen(serverInfo[svrIdx].shared_secret) < IPRADIUS_MAX_SECRET_SIZE) { strncpy((char *)server.shared_secret, serverInfo[svrIdx].shared_secret, IPRADIUS_MAX_SECRET_SIZE); server.shared_secret_len = strlen((char *)server.shared_secret); } else { DBG_PRINT("dot1xAuthServerLibRadiusServerSet: Shared secret too long, length must be < %d\n", IPRADIUS_MAX_SECRET_SIZE); goto fail; } retval = ipradius_server_add(&server); if (retval != IPCOM_SUCCESS) { DBG_PRINT("dot1xAuthServerLibInit: " "ipradius_server_add() failed, err=%ld\n", retval); goto fail; } /* Save index so we can easily remove it from the * ipradius server list later */ authServerCtrl.radius_server_index[svrIdx] = server.index; DBG_PRINT( "dot1xAuthServerLibRadiusServerSet: " " Added RADIUS server at index %d\n", authServerCtrl.radius_server_index[svrIdx]); } authLibServerSetDone = DOT1X_SS_DONE; /* Create the radius receive task */ if( ipcom_proc_acreate("tDot1xRecv", tDot1xRecv, IP_NULL, IP_NULL) != IPCOM_SUCCESS ) goto fail; return (OK); } else { return (ERROR); } fail: if (authServerCtrl.radius_fd != IP_INVALID_SOCKET) ipcom_socketclose(authServerCtrl.radius_fd); authServerCtrl.radius_fd = IP_INVALID_SOCKET; authLibServerSetDone = DOT1X_SS_DONE; return ERROR; } /***************************************************************************** * dot1xAuthServerLibShutDown - shut down the authentication server library * * This routine releases the resources used by this library and resets it to * the uninitialized state. It provides two types of shutdown: normal or * forced. In the normal shutdown, if there are outstanding requests, the * shutdown will not be performed. In the latter case, the shutdown is performed * regardless of outstanding requests. * * RETURNS: OK or ERROR if shutdown is not performed. */ STATUS dot1xAuthServerLibShutDown ( int flag /* 0 controlled shutdown, 1 forced shutdown */ ) { AUTH_REQUEST_MSG_NODE * pMsgNode; Ipradius_server server; UINT8 svrIdx; /* Atomically check the initialization flag and update it to * in progress if it is not already. This test is safe with repect to * both interrupts and accesses of other processors. i.e. SMP situations */ if(vxAtomicCas(&authLibInitDone,DOT1X_AS_TRUE,DOT1X_AS_IN_PROGRESS) == TRUE) { if (flag == DOT1X_AS_NORMAL_SHUTDOWN) { /* if there is outstanding requests, don't do it */ if (SLL_FIRST(&authServerCtrl.list)) { authLibInitDone = DOT1X_AS_TRUE; return ERROR; } } /* free any allocated data structures */ while ( (pMsgNode = (AUTH_REQUEST_MSG_NODE *) sllGet(&authServerCtrl.list)) != NULL ) { DOT1X_FREE(pMsgNode); } /* Remove the servers from the ipradius server list */ for( svrIdx = 0; svrIdx < DOT1X_AUTH_MAX_RADIUS_SERVERS; svrIdx++) { if(authServerCtrl.radius_server_index[svrIdx] == 0) continue; server.index = authServerCtrl.radius_server_index[svrIdx]; DBG_PRINT("Removing server at index: %d\n",server.index); if( ipradius_server_remove(&server) != IPCOM_SUCCESS ) { /* This should never happend */ DBG_PRINT("dot1xAuthServerLibShutDown: " "Failed to remove the server from ipradius"); return (ERROR); } } semDelete(authServerCtrl.semId); /* Free the RADIUS packet buffer */ if (authServerCtrl.pkt != IP_NULL) free(authServerCtrl.pkt); /* Close the radius socket */ if( authServerCtrl.radius_fd != IP_INVALID_SOCKET) ipcom_socketclose(authServerCtrl.radius_fd); bzero((char *)&authServerCtrl,sizeof(AUTH_SERVER_ACTION_CONTROL)); authLibInitDone = DOT1X_AS_FALSE; } else { if (authLibInitDone == DOT1X_AS_IN_PROGRESS) return ERROR; } return OK; } /* Local methods */ /***************************************************************************** * dot1xAuthServerEapMsgSend - send the EAP message to the authentication server * * This routine delivers the EAP packet to the authentication server for the * application. Parameter pInfo points to a DOT1X_AS_REQUIRED_INFO data * structure which the user must provide. Upon receiving the reply from the * RADIUS server, the result is delivered back to the application through the * callback specified in the pInfo. * * RETURNS: OK or ERROR if packet is not sent to the RADIUS interface. * * NOMANUAL */ STATUS dot1xAuthServerEapMsgSend ( UINT32 msgHandle, /* user specified handle */ UINT8 * pEap, /* EAP packet */ int len, /* length of EAP packet */ DOT1X_AS_REQUIRED_INFO * pInfo /* required User information */ ) { Ipradius_header* msg = IP_NULL; Ipradius_request request = {0}; int retval; Ip_u8 errattr = 0; AUTH_REQUEST_MSG_NODE * pNode; int i; UINT8 svrIdx = 0; char strHolder[DOT1X_AS_STATION_ID_LEN*2 + 1]; UINT16 sizeRemaining; UINT8 eapFragLen; /* The user should not be calling this function unless * the server has been initialized */ if ( (authLibInitDone == DOT1X_AS_FALSE) || (authLibInitDone == DOT1X_AS_IN_PROGRESS) ) return (ERROR); /* required user parameter check */ if(pInfo == NULL) { return(ERROR); } /* print user information block */ DBG_PRINT("radiusIF: usrInfoBlock,usrCallBack: 0x%x, mtu: 0x%x, pId:0x%x", (UINT32)pInfo->pUserCallback,pInfo->mtu,(UINT32)pInfo->pId); DBG_PRINT("len: 0x%x, pState: 0x%x, sLen: 0x%x\n",pInfo->len, (UINT32)pInfo->pState, pInfo->sLen); pNode = DOT1X_CALLOC(1,sizeof(AUTH_REQUEST_MSG_NODE)); if(pNode == NULL) { return(ERROR); } msg = ipradius_access_request_pkt_malloc(); if (msg == IP_NULL) { DOT1X_FREE(pNode); return ERROR; } pNode->pUserCallback = pInfo->pUserCallback; pNode->reqHandle = msgHandle; pNode->seqNum = authServerCtrl.seqNum; /* MTU attribute */ if(pInfo->mtu) { if(ipradius_attr_add_u32(msg,IPRADIUS_ATTR_Framed_MTU, (Ip_u32)pInfo->mtu) != IPCOM_SUCCESS) { DBG_PRINT("dot1xAuthServerEapMsgSend:" " ipradius_attr_add_u32 failed\n"); goto errout; } DBG_PRINT("Radius MTU %d",pInfo->mtu); } /* calling station ID */ for(i = 0; i < DOT1X_AS_STATION_ID_LEN; i++) { sprintf(&strHolder[i*2],"%02x",(UINT8)pInfo->callingId[i]); } if(ipradius_attr_add_array(msg,IPRADIUS_ATTR_Calling_Station_Id, (Ip_u8*)strHolder,DOT1X_AS_STATION_ID_LEN*2) != IPCOM_SUCCESS) { DBG_PRINT("dot1xAuthServerEapMsgSend:" " ipradius_attr_add_array failed\n"); goto errout; } /* user name attribute */ if(ipradius_attr_add_array(msg,IPRADIUS_ATTR_User_Name,(Ip_u8*)pInfo->pId, (Ip_u8)pInfo->len) != IPCOM_SUCCESS) { DBG_PRINT("dot1xAuthServerEapMsgSend:" " ipradius_attr_add_array failed\n"); goto errout; } DBG_PRINT("Radius Username %s %d\n", pInfo->pId, pInfo->len); /* for now added the NAS port type as wireless type */ if(pInfo->portType) { if(ipradius_attr_add_u32(msg,IPRADIUS_ATTR_NAS_Port_Type, (Ip_u32)pInfo->portType) != IPCOM_SUCCESS) { DBG_PRINT("dot1xAuthServerEapMsgSend:" " ipradius_attr_add_u32 failed\n"); goto errout; } DBG_PRINT("Radius port type %d\n",pInfo->portType); } /* NAS Port */ if(msgHandle) { /* the low 8 bits should contain the interface number */ UINT32 portNum = 0; *((UINT8 *)(&portNum) + 3) = msgHandle & 0xFF; if(ipradius_attr_add_array(msg,IPRADIUS_ATTR_NAS_Port, (Ip_u8*)&portNum,4) != IPCOM_SUCCESS) { DBG_PRINT("dot1xAuthServerEapMsgSend:" " ipradius_attr_add_array failed\n"); goto errout; } DBG_PRINT("Radius NAS port %d\n", portNum); } /* NAS IP address */ if(ipradius_attr_add_array(msg, IPRADIUS_ATTR_NAS_IP_Address, (Ip_u8*)&authServerCtrl.nasIpAddr,4) != IPCOM_SUCCESS) { DBG_PRINT("dot1xAuthServerEapMsgSend:" " ipradius_attr_add_array failed\n"); goto errout; } /* send the state attribute if there is one */ if(pInfo->pState) { if(ipradius_attr_add_array(msg, IPRADIUS_ATTR_State, (Ip_u8*)pInfo->pState,(Ip_u8)pInfo->sLen) != IPCOM_SUCCESS) { DBG_PRINT("dot1xAuthServerEapMsgSend:" " ipradius_attr_add_array failed\n"); goto errout; } DBG_PRINT("Radius State %d\n", pInfo->pState); } #ifdef INCLUDE_IPEAP_UPDATE /* Add EAP_Key_Name Attribute rfc5247,p67 , for eap-update */ if(ipradius_attr_add_array(msg,IPRADIUS_ATTR_EAP_Key_Name,NULL,0) != IPCOM_SUCCESS) { DBG_PRINT("dot1xAuthServerEapMsgSend:" " ipradius_attr_add_array adding attribute IPRADIUS_ATTR_EAP_Key_Name failed\n"); goto errout; } #endif /* EAP Message */ sizeRemaining = len; while( sizeRemaining > 0 ) { if( sizeRemaining > DOT1X_AUTH_MAX_EAP_FRAG_SIZE ) { eapFragLen = DOT1X_AUTH_MAX_EAP_FRAG_SIZE; sizeRemaining -= DOT1X_AUTH_MAX_EAP_FRAG_SIZE; } else { eapFragLen = sizeRemaining; sizeRemaining = 0; } /* Here hoping that ipradius_access_request_pkt_malloc() allocated enough buffer space */ if(ipradius_attr_add_array(msg, IPRADIUS_ATTR_EAP_Message, (Ip_u8*)pEap, (Ip_u8)eapFragLen) != IPCOM_SUCCESS) { DBG_PRINT("dot1xAuthServerEapMsgSend:" " ipradius_attr_add_array failed\n"); goto errout; } /* Go to the next eap fragment */ if(sizeRemaining > 0) pEap += DOT1X_AUTH_MAX_EAP_FRAG_SIZE; } DBG_PRINT("Radius EAP Msg len %d\n", len); semTake(authServerCtrl.semId,WAIT_FOREVER); authServerCtrl.seqNum++; sllPutAtTail(&authServerCtrl.list,(SL_NODE *)pNode); semGive(authServerCtrl.semId); /* Populate request server list */ for ( svrIdx = 0; svrIdx < authServerCtrl.num_radius_servers; svrIdx++ ) { memcpy(&request.serv_list[svrIdx].server_addr, &authServerCtrl.radius_server_addr[svrIdx], sizeof(union Ip_sockaddr_union)); request.serv_list[svrIdx].server_type = IPRADIUS_ACCESS_SERVER; request.serv_list[svrIdx].pollnum = authServerCtrl.server_info[svrIdx].retry_count; request.serv_list[svrIdx].pollsec = authServerCtrl.server_info[svrIdx].timeout; } /* Make the access request */ #ifdef INET if( authServerCtrl.server_info[0].domain == IP_AF_INET ) request.fd = authServerCtrl.radius_fd; else #endif #ifdef INET6 if( authServerCtrl.server_info[0].domain == IP_AF_INET6 ) request.fd6 = authServerCtrl.radius_fd; else #endif DBG_PRINT("dot1xAuthServerEapMsgSend: Invalid domain %d\n", authServerCtrl.server_info[0].domain); request.status = IPCOM_SUCCESS; request.unique = pNode->seqNum; request.event_callback = ipradiusRequestNormalReply; request.num_servers = authServerCtrl.num_radius_servers; retval = ipradius_access_request(msg, &request, &errattr); if (retval != IPCOM_SUCCESS) { DBG_PRINT("radius access request failed :%d (attr=%d)\n",retval, errattr); return ERROR; } return(OK); errout: ipradius_access_request_pkt_free (msg); return (ERROR); } /*************************************************************************** * ipradiusRequestNormalReply - callback to receive the reply from the RADIUS * server * * This is the pre-installed callback routine. It is invoked by the RADIUS * client to receive a reply from the RADIUS server. This routine further calls * the user specified callback to deliver the result back. * * RETURNS: NONE */ LOCAL Ip_err ipradiusRequestNormalReply ( int event, struct Ipradius_request_struct *request, Ipradius_header *request_packet, Ipradius_header *response_packet, Ipradius_server *server ) { AUTH_REQUEST_MSG_NODE *pMsgNode; enum DOT1X_AS_CODE asCode; UINT8 *pMsg = NULL; UINT8 *pBuf = NULL; Ip_u8 *pAttr; int len = 0; int retCode = -1; #ifdef DOT1X_AS_DEBUG DBG_PRINT("ipradiusRequestNormalReply: " "Got reply packet: %s\n",svrRsp[event]); #endif /* retrive the request node */ semTake(authServerCtrl.semId,WAIT_FOREVER); pMsgNode = requestMsgNodeGet(&authServerCtrl.list,request->unique, GET_TYPE_FIND_AND_REMOVE); semGive(authServerCtrl.semId); if (pMsgNode == NULL) { /* Not an expected reply */ DBG_PRINT("Could not find request node: ID: %d\n", request->unique); return IPCOM_ERR_NOT_FOUND; } /* translate the received code */ if (event == IPRADIUS_EVENT_ACCESS_ACCEPT) asCode = DOT1X_AS_ACCESS_ACCEPT; else if (event == IPRADIUS_EVENT_ACCESS_REJECT) asCode = DOT1X_AS_ACCESS_REJECT; else if (event == IPRADIUS_EVENT_ACCOUNTING_RESPONSE) asCode = DOT1X_AS_ACCOUNTING_RESPONSE; else if (event == IPRADIUS_EVENT_ACCESS_CHALLENGE) asCode = DOT1X_AS_ACCESS_CHALLENGE; else if ((event == IPRADIUS_EVENT_ACCESS_CANCELLED) || (event == IPRADIUS_EVENT_ACCESS_TIMEOUT)) { asCode = DOT1X_AS_GEN_ERROR; ipradiusRequestErrorReply((UINT32)request->unique,(UINT32)event); } else { DBG_PRINT("ipradiusRequestNormalReply: Unknown RADIUS event\n"); return IPCOM_ERR_NOT_FOUND; } if (asCode == DOT1X_AS_ACCESS_ACCEPT || asCode == DOT1X_AS_ACCESS_REJECT || asCode == DOT1X_AS_ACCOUNTING_RESPONSE || asCode == DOT1X_AS_ACCESS_CHALLENGE) { struct RCVD_ATTR { UINT8 * pBuf; int len; } rcvdAttrs[NUM_OF_ATTRS_SEND]; int i = 0; bzero((char *)&rcvdAttrs[0], NUM_OF_ATTRS_SEND*sizeof(struct RCVD_ATTR)); /* Go through the attribute list */ for( pAttr = ipradius_attr_get_next(response_packet, IP_NULL); pAttr; pAttr = ipradius_attr_get_next(response_packet, pAttr) ) { int attrInx = 0; Ip_u8 attrType = pAttr[DOT1X_AUTH_RADIUS_ATTR_TYPE_IDX]; Ip_u8 *pAttrData = &pAttr[DOT1X_AUTH_RADIUS_ATTR_DATA_IDX]; if (attrType == IPRADIUS_ATTR_EAP_Message || attrType == IPRADIUS_ATTR_Vendor_Specific || attrType == IPRADIUS_ATTR_State #ifdef INCLUDE_IPEAP_UPDATE || attrType == IPRADIUS_ATTR_EAP_Key_Name #endif ) { DOT1X_AS_ATTR_HEADER_T *pAttrHeader; /* Only handle the above type of attributes for now */ /* Special processing for EAP messages as they may be * chained attributes */ if(attrType == IPRADIUS_ATTR_EAP_Message) { /* Get the length of data for the entire EAP message chain */ if( (dot1xAuthSvrLibProcessEAPMsg(response_packet, pAttr,&len,NULL)) == NULL ) return IPCOM_ERR_BAD_PACKET; } else len = pAttr[DOT1X_AUTH_RADIUS_ATTR_LEN_IDX] - DOT1X_AUTH_RADIUS_ATTR_HEADER_LEN; if (!len) continue; pBuf = DOT1X_CALLOC(1, len + DOT1X_AS_ATTR_HEADER_LEN); if(pBuf == NULL) { DBG_PRINT("radiusEventCallback: Error getting buffer\n"); return IPCOM_ERR_NO_MEMORY; } /* If its and EAP message we may have to walk the chain */ if (attrType == IPRADIUS_ATTR_EAP_Message) { pMsg = pBuf; /* retrieve the response attributes */ if( (pAttr = dot1xAuthSvrLibProcessEAPMsg(response_packet, pAttr,NULL,pMsg)) == NULL) return IPCOM_ERR_BAD_PACKET; } else { pMsg = pBuf + DOT1X_AS_ATTR_HEADER_LEN; /* retrieve the response attributes */ memcpy(pMsg,pAttrData,len); } pAttrHeader = (DOT1X_AS_ATTR_HEADER_T *)pBuf; /* extract the info from vendor specific message */ if (attrType == IPRADIUS_ATTR_Vendor_Specific) { /* Decrypt the vendor specific packet */ dot1xAuthSvrLibProcessVendorMsg(pMsg,len, (UINT8*)&request_packet->auth[0], (char *)&server->shared_secret[0], server->shared_secret_len); if ((retCode = vendorAttributeKeyExtract(&pMsg,(int *)&len)) > 0) { /* set type */ if (retCode == ATTR_SK_INX) { /* B0562 - Create session key message */ #define WPA_RX_KEY_OFFSET (DOT1X_AS_ATTR_HEADER_LEN + 6) char *p = (char*)pBuf + WPA_RX_KEY_OFFSET; /* Copy the attribute */ pAttrHeader->type = DOT1X_AS_SESSION_SEND_KEY; pAttrHeader->len = len; bcopy((char*)pMsg, p, len); attrInx = retCode; /* deliver this key to dot1xKeyLib. It is used as * the random key for key generation. */ dot1xKeyRandomKeySet(pMsg); } else pAttrHeader->type = DOT1X_AS_SESSION_RCEV_KEY; /* set the length and copy the key */ pAttrHeader->len = len; bcopy((char*)pMsg, (char*)pBuf+DOT1X_AS_ATTR_HEADER_LEN, len); attrInx = retCode; } } else if (attrType == IPRADIUS_ATTR_EAP_Message) { attrInx = ATTR_EP_INX; } else if (attrType == IPRADIUS_ATTR_State) { pAttrHeader->type = 0; /* type = 0 for now */; pAttrHeader->len = len; attrInx = ATTR_ST_INX; } #ifdef INCLUDE_IPEAP_UPDATE else if (attrType == IPRADIUS_ATTR_EAP_Key_Name) { DBG_PRINT("ipradiusRequestNormalReply: ******* EAP_Key_Name Response ******* \n "); { Ip_u8 p; for(p=0;ppUserCallback(pMsgNode->reqHandle, asCode, rcvdAttrs[0].pBuf, rcvdAttrs[0].len, pBuf, len); for (i = 0; i < NUM_OF_ATTRS_SEND; i++) if (rcvdAttrs[i].pBuf) DOT1X_FREE(rcvdAttrs[i].pBuf); } DOT1X_FREE(pMsgNode); return IPCOM_SUCCESS; } /*************************************************************************** * ipradiusRequestErrorReply - error callback * * This routine is called when RADIUS client is unable to send the request * to the RADIUS server. The routine will try to send the request to the * another RADIUS server until successfully sent or the server list is * exhausted. * * RETURNS: NONE * * NOMANUAL */ LOCAL void ipradiusRequestErrorReply ( UINT32 reqHandle, /* RADIUS request handle */ UINT32 code /* RADIUS result error code */ ) { AUTH_REQUEST_MSG_NODE * pMsgNode; enum DOT1X_AS_CODE asCode; DBG_PRINT ("RadiusIF: Callback Error Case.\n"); DBG_PRINT ("RadiusIF: ReqHandle: %d, Code: %d\n", reqHandle, code); /* Set the server error indicator */ authServerCtrl.serverError = TRUE; /* convert the error code */ if (code == IPRADIUS_EVENT_ACCESS_CANCELLED) asCode = DOT1X_AS_ACCESS_CANCELLED; else if (code == IPRADIUS_EVENT_ACCESS_TIMEOUT) asCode = DOT1X_AS_ACCESS_TIMEOUT; else { /* for now, treated as the general error case */ asCode = DOT1X_AS_GEN_ERROR; } semTake(authServerCtrl.semId,WAIT_FOREVER); pMsgNode = requestMsgNodeGet(&authServerCtrl.list,reqHandle, GET_TYPE_FIND_AND_REMOVE); semGive(authServerCtrl.semId); if (pMsgNode != NULL) { /* report the error to the user */ (*pMsgNode->pUserCallback)(pMsgNode->reqHandle, asCode,NULL,0,NULL,0); DOT1X_FREE(pMsgNode); } } /***************************************************************************** * dot1xAuthSvrLibGetEAPMsg - Process the RADIUS EAP attribute chain. * * This routine walks the RADIUS EAP attribute chain to get the length of the * chained EAP message or copies the chained EAP message into the provided * buffer or both. If len is NULL only the data will be copied to the * provided buffer. If pBuf is NULL only the length of the data of the * chained EAP message will be returned. Typically this function is called * twice. The first call is to obtain the length of data for the chained * EAP message and the second call is to copy the chained EAP message into * the provided buffer that was allocated with the length obtained from the * first call. If pBuf is provided, this function assumes that enough buffer * has been allocated to copy the entire EAP message chain into said buffer. * * RETURNS: Pointer to the last RADIUS EAP attribute * * NOMANUAL */ LOCAL Ip_u8 * dot1xAuthSvrLibProcessEAPMsg ( Ipradius_header *response_packet, Ip_u8 *pAttr, int *dataLen, UINT8 *pBuf ) { Ip_u8 *pAttrPrev; UINT32 attrDataLen; UINT32 myDataLen = 0; if(pAttr == NULL) { DBG_PRINT("pAttr is NULL\n"); return NULL; } /* Only process EAP attributes */ if(pAttr[DOT1X_AUTH_RADIUS_ATTR_TYPE_IDX] != IPRADIUS_ATTR_EAP_Message) return NULL; /* Walk the EAP message chain */ while( pAttr[DOT1X_AUTH_RADIUS_ATTR_TYPE_IDX] == IPRADIUS_ATTR_EAP_Message ) { /* The length of the data is the length * of the attribute less the attribute * header */ attrDataLen = pAttr[DOT1X_AUTH_RADIUS_ATTR_LEN_IDX] - DOT1X_AUTH_RADIUS_ATTR_HEADER_LEN; myDataLen += attrDataLen; if(pBuf) { memcpy(pBuf,&pAttr[DOT1X_AUTH_RADIUS_ATTR_DATA_IDX], attrDataLen); pBuf += attrDataLen; } pAttrPrev = pAttr; pAttr = ipradius_attr_get_next(response_packet, pAttr); /* Break out if we got to the last attribute */ if(pAttr == NULL) break; } if(dataLen) *dataLen = myDataLen; return pAttrPrev; } /***************************************************************************** * vendorAttributeKeyExtract - process the vendor specific RADIUS attribute * * This routine mainly handles Cisco and Microsoft session key attributes. * When the key extraction from the original message is done, the *ppMsg will * contain the starting position of the key and *pLen will have the actual * length of the key. * * RETURNS: key index */ LOCAL int vendorAttributeKeyExtract ( UINT8 **ppMsg, /* point to pointer to the first byte of the vendor value */ int *pLen /* point to the len */ ) { char * pMsg = (char *)*ppMsg; UINT32 vendorId; int len = 0; int returnCode = -1; /* the vendor value should start with 4 byte Enterprise code as defined * in SNMP SMI in network byte order. The first byte is 0. */ vendorId = *(UINT32 *)pMsg; vendorId = ntohl(vendorId); if (vendorId == VENDOR_ID_CISCO) /* CISCO */ { DBG_PRINT("RadiusIF: Vendor Specific CISCO attribute received\n"); /* for now, only process the session key */ pMsg += 4; /* advance to sub type */ /* The Cisco AV pair, if leap:session-key, has already been decrpted */ if (*pMsg == VENDOR_TYPE_CISCO_AVPAIR) { ++pMsg; /* advance to length field */ len = *pMsg; /* length */ ++pMsg; /* advance to start of value */ #if 0 /* RADIUS Client has already stripped the "leap:session-key:" */ if(bcmp(pMsg, "leap:session-key=", strlen("leap:session-key=")) != 0) { DBG_PRINT("RadiusIF: Cisco AV-pair not leap:session-key " "not supported\n"); } else #endif { len = len - 2; /* key length is len - 2 */ returnCode = ATTR_SK_INX; } } else { /* other CISCO sub types, not supported yet */ DBG_PRINT("RadiusIF: Cisco non-AV-pair subtype:%x not supported\n", *pMsg); } } else if (vendorId == VENDOR_ID_MICROSOFT) { /* Microsoft has the code 311 */ DBG_PRINT("RadiusIF: Vendor Specific MicroSoft attribute received\n"); pMsg += 4; /* advance to sub type */ if (*pMsg == VENDOR_TYPE_MS_MPPE_SEND_KEY || *pMsg == VENDOR_TYPE_MS_MPPE_RECV_KEY) { returnCode = ((*pMsg) == VENDOR_TYPE_MS_MPPE_SEND_KEY) ? ATTR_SK_INX : ATTR_RK_INX; /* 16 is MS-MPPE-SEND-Key and 17 is MS-MPPE-Recv-Key */ pMsg++; /* advance to Vendor-length */ len = *pMsg++; /* length, and advance to start of key */ /* key length = len - 2 (1 byte type and length fields) */ len = len - 2; } else { /* not the key message*/ DBG_PRINT("RadiusIF: other sub types, not supported\n"); } } else { DBG_PRINT("RadiusIF: Vendor ID %#08x not supported\n", vendorId); } /* store the adjusted message position and length */ if (returnCode > 0) { *ppMsg = (UINT8 *)pMsg; *pLen = len; } return returnCode; } LOCAL STATUS dot1xAuthSvrLibProcessVendorMsg ( UINT8* pVendorMsg, UINT32 pVendorMsgLen, UINT8* requestAuth, char *secret, UINT32 secretLen ) { UINT8 newAuth[DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR + DOT1X_AUTH_VENDOR_SALT_LENGTH]; UINT32 saltPos; UINT32 vendorId; BOOL decrypted; UINT32 headerLen = DOT1X_AUTH_VENDOR_SPECIFIC_ID_LENGTH + DOT1X_AUTH_VENDOR_TYPE_LENGTH + DOT1X_AUTH_VENDOR_LENGTH; /* Retrieve the vendor ID */ bcopy((const char *)pVendorMsg,(char *)&vendorId,4); vendorId = ip_ntohl(vendorId); if (vendorId == VENDOR_ID_CISCO) { char sKeyId[] = "leap:session-key="; /* Calculate the Salt position */ saltPos = headerLen + strlen(sKeyId); } else if (vendorId == VENDOR_ID_MICROSOFT) { /* Get the salt position*/ saltPos = headerLen; } else { /* Not supported for now */ DBG_PRINT("RADIUS: Not supported VendorID\n"); return (ERROR); } /* Zero the new authenticator buffer */ memset ((unsigned char *) newAuth, 0, DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR + DOT1X_AUTH_VENDOR_SALT_LENGTH); /* Copy request authenticator */ memcpy (newAuth, requestAuth, DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR); /* Copy the salt value */ bcopy((const char *)&pVendorMsg[saltPos], (char *)(&newAuth[0] + DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR), DOT1X_AUTH_VENDOR_SALT_LENGTH); if (((pVendorMsgLen - saltPos - DOT1X_AUTH_VENDOR_SALT_LENGTH) % DOT1X_AUTH_RADIUS_PASSWORD_MODULUS) == 0x00) { decrypted = dot1xAuthSvrLibMd5Decryption( (unsigned char *)&newAuth[0], DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR + DOT1X_AUTH_VENDOR_SALT_LENGTH, (unsigned char *)secret, (USHORT) secretLen, (unsigned char *)&pVendorMsg[saltPos + DOT1X_AUTH_VENDOR_SALT_LENGTH], (USHORT) (pVendorMsgLen - saltPos - DOT1X_AUTH_VENDOR_SALT_LENGTH)); if (decrypted == FALSE) { DBG_PRINT("Decryption failed\n"); return (ERROR); } DBG_PRINT("Decryption passed, vendor lenth: %d\n",headerLen + (UINT8)pVendorMsg[saltPos + DOT1X_AUTH_VENDOR_SALT_LENGTH] - DOT1X_AUTH_VENDOR_SPECIFIC_ID_LENGTH); /* Set the vendor length field */ pVendorMsg[DOT1X_AUTH_VENDOR_SPECIFIC_ID_LENGTH + DOT1X_AUTH_VENDOR_TYPE_LENGTH] = headerLen + (UINT8)pVendorMsg[saltPos + DOT1X_AUTH_VENDOR_SALT_LENGTH] - DOT1X_AUTH_VENDOR_SPECIFIC_ID_LENGTH; /* Copy the decrypted key */ bcopy((const char *)&pVendorMsg[saltPos + DOT1X_AUTH_VENDOR_SALT_LENGTH + DOT1X_AUTH_VENDOR_KEY_LENGTH],(char *)&pVendorMsg[headerLen], (UINT8)pVendorMsg[saltPos + DOT1X_AUTH_VENDOR_SALT_LENGTH]); } return (OK); } LOCAL BOOL dot1xAuthSvrLibMd5Decryption ( unsigned char *cptr_identifier, USHORT identifier_length, unsigned char *p_secret, USHORT secret_length, unsigned char *p_data, USHORT data_length ) { unsigned char md5_result[DOT1X_AUTH_MD5_RESULT_LENGTH]; unsigned char *bptr_md5_input; ULONG md5_input_length; USHORT xored_data_length; unsigned char * bptr_data; USHORT md5_result_index; ULONG md5_result_length = DOT1X_AUTH_MD5_RESULT_LENGTH; ULONG temp_value; if ((p_data == NULL) || (p_secret == NULL)) { return (FALSE); } if ((data_length % DOT1X_AUTH_RADIUS_PASSWORD_MODULUS) != 0) { return (FALSE); } md5_input_length = identifier_length + secret_length; bptr_md5_input = (unsigned char *) calloc (1, md5_input_length); if (bptr_md5_input == NULL) { return (FALSE); } memcpy (bptr_md5_input, p_secret, secret_length); memcpy ((bptr_md5_input + secret_length), cptr_identifier, identifier_length); if (!dot1xAuthSvrLibMd5Digest(bptr_md5_input, md5_input_length, &md5_result[0], md5_result_length)) { printf("RADIUS: do_radius_md5_decryption: MD5 error\n"); free (bptr_md5_input); return (FALSE); } xored_data_length = data_length; bptr_data = p_data; md5_input_length = secret_length + DOT1X_AUTH_MD5_RESULT_LENGTH; while (xored_data_length > 0) { for (md5_result_index = 0; (md5_result_index < DOT1X_AUTH_MD5_RESULT_LENGTH) && (xored_data_length > 0); --xored_data_length, ++md5_result_index, ++bptr_data) { temp_value = *bptr_data; temp_value = temp_value ^ md5_result[md5_result_index]; md5_result[md5_result_index] = *bptr_data; *bptr_data = (unsigned char) temp_value; } if (xored_data_length > 0) { memcpy ((bptr_md5_input + secret_length), md5_result, DOT1X_AUTH_MD5_RESULT_LENGTH); if (!dot1xAuthSvrLibMd5Digest(bptr_md5_input, md5_input_length, &md5_result[0], md5_result_length)) { printf("RADIUS: do_radius_md5_decryption: MD5 error\n"); free (bptr_md5_input); return (FALSE); } } } free (bptr_md5_input); return (TRUE); } /**************************************************************************** * dot1xAuthSvrLibMd5Digest - compute MD5 digest. * * RETURNS: TRUE if no errors, FALSE otherwise */ LOCAL BOOL dot1xAuthSvrLibMd5Digest ( unsigned char *input, ULONG input_length, unsigned char *result, ULONG result_length ) { MD5_CTX ctx; if (result_length < MD5_DIGEST_LENGTH) return (FALSE); #ifdef OPENSSL_FIPS /* MD5 cannot be used */ if (FIPS_mode()) { /* Authentication failed. */ printf("dot1x: MD5 is not supported when FIPS mode is enabled!"); return(FALSE); } #endif if (!MD5_Init(&ctx)) return (FALSE); if (!MD5_Update(&ctx, input, input_length)) return (FALSE); if (!MD5_Final(result, &ctx)) return (FALSE); return (TRUE); } /**************************************************************************** * requestMsgNodeGet - get the node matching the value of parameter reqHandle. * * RETURNS: the message node or NULL if not found. */ LOCAL AUTH_REQUEST_MSG_NODE * requestMsgNodeGet ( SL_LIST * pList, /* the searched single link list */ UINT32 reqHandle, /* the unique value identifies the message node */ int getType /* find or find and remove from the list */ ) { SL_NODE * pNode; SL_NODE * pPreNode; pNode = SLL_FIRST(pList); pPreNode = NULL; while (pNode) { if (((AUTH_REQUEST_MSG_NODE *)pNode)->seqNum == (UINT32)reqHandle) { if (getType == GET_TYPE_FIND_AND_REMOVE) sllRemove(pList,pNode,pPreNode); break; } pPreNode = pNode; pNode = SLL_NEXT(pNode); } return (AUTH_REQUEST_MSG_NODE *)pNode; } LOCAL IPCOM_PROCESS(tDot1xRecv) { #ifdef DOT1X_AS_DEBUG Ip_err retval = IPCOM_SUCCESS; #endif Ip_fd_set rd_set; int num_ready_socks; int svrIdx; ipcom_proc_init(); IP_FD_ZERO(&rd_set); /*********** Eternal radius response loop **********/ for(;;) { int radius_sock = authServerCtrl.radius_fd; if (IP_UNLIKELY(radius_sock < 0)) break; IP_FD_SET(radius_sock, &rd_set); num_ready_socks = ipcom_socketselect(radius_sock + 1, &rd_set, IP_NULL, IP_NULL, IP_NULL); if (num_ready_socks < 0) { if (ipcom_errno == IP_ERRNO_EINTR) continue; else { DBG_PRINT("ipcom_authd :: select failed %s\n", ipcom_strerror(ipcom_errno)); break; } } if (IP_FD_ISSET(radius_sock, &rd_set)) { Ipcom_msg_auth *authmsg; int msglen; if (ipcom_socketioctl(radius_sock, IP_FIONREAD, &msglen) < 0) { /* Just drop the message */ (void)ipcom_recv(radius_sock, IP_NULL, 0, 0); continue; } if( (authmsg = ipcom_malloc(sizeof(Ipcom_msg_auth) + msglen)) == IP_NULL ) { DBG_PRINT("tDot1xRecv :: recv() failed: %s\n", ipcom_strerror(ipcom_errno)); continue; } authmsg->len = ipcom_recv(radius_sock, authmsg->data, msglen, 0); if (authmsg->len > 0) { /* Parse RADIUS input packet. */ for (svrIdx = 0; svrIdx < authServerCtrl.num_radius_servers; svrIdx++) { if(ipradius_read(authmsg->data, (Ip_size_t)authmsg->len, &authServerCtrl.radius_server_addr[svrIdx].sa) == IPCOM_SUCCESS) break; } } else { DBG_PRINT("tDot1xRecv :: recv() failed: %s\n", ipcom_strerror(ipcom_errno)); } ipcom_free(authmsg); } } #ifdef DOT1X_AS_DEBUG /* Failure. */ DBG_PRINT("tDot1xRecv: failed, retval = %d", retval); #endif ipcom_proc_exit(); }