/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipike_radius.c,v $ $Revision: 1.37.10.4 $ * $Source: /home/interpeak/CVSRoot/ipike/src/ipike_radius.c,v $ * $Author: zan $ $Date: 2013-08-07 12:43:42 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Kenneth Jonsson ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipike_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #ifdef IPIKE_USE_RADIUS #include "ipike_h.h" #include "ipike_radius.h" #include "ipike_sadb.h" #include #include #include #include /* **************************************************************************** * 4 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 5 DEFINES **************************************************************************** */ #define IPIKE_EAP_MAX_BUF 1400u #define IPIKE_EAP_HEADER_LEN 5u #define IPIKE_RADIUS_VENDOR_ID_MICROSOFT 311 #define IPIKE_RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY 16 #define IPIKE_RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY 17 /* Cisco LEAP */ #define IPIKE_RADIUS_VENDOR_ID_CISCO 9 #define IPIKE_RADIUS_VENDOR_ATTR_CISCO_SESSION_KEY 1 #define IPIKE_RADIUS_CISCO_SESSION_KEY_LEN 34 /* **************************************************************************** * 6 TYPES **************************************************************************** */ typedef struct Ipike_radius_list_elem_st { Ipike_radius_server* v; char* ike_id; } Ipike_radius_list_elem; typedef struct Ipike_radius_list_key_st { Ip_fd fd; const char* ike_id; } Ipike_radius_list_key; typedef struct Ipike_radius_unique_id_struct { Ip_u8 radius_id; Ip_u8* unique_id; Ip_u32 unique_id_len; } Ipike_radius_unique_id; /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* *=========================================================================== * ipike_radius_create_server *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ipike_radius_server* ipike_radius_create_server( union Ip_sockaddr_union* server_addr, union Ip_sockaddr_union* local_addr, const char* shared_secret); /* *=========================================================================== * ipike_radius_free_server *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC void ipike_radius_free_server(Ipike_radius_server* ctx); /* *=========================================================================== * ipike_radius_hash_key *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC unsigned ipike_radius_hash_key(Ipike_radius_list_key* key); /* *=========================================================================== * ipike_radius_hash_obj *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC unsigned ipike_radius_hash_obj(Ipike_radius_list_elem* elem); /* *=========================================================================== * ipike_radius_hash_cmp *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ip_bool ipike_radius_hash_cmp(Ipike_radius_list_elem* elem, Ipike_radius_list_key* key); /* *=========================================================================== * ipike_radius_hash_add *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ipike_error ipike_radius_hash_add(Ipike_radius_server* server, const char* ike_id); /* *=========================================================================== * ipike_sadb_hash_free *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC void ipike_radius_hash_free(Ipike_radius_list_elem* elem, void* unused); /* *=========================================================================== * ipike_radius_hash_get *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ipike_radius_server* ipike_radius_hash_get(Ip_fd fd, const char* ike_id); /* *=========================================================================== * ipike_radius_get_server *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ipike_radius_server* ipike_radius_get_server( const char* ike_id, union Ip_sockaddr_union* server_addr, union Ip_sockaddr_union* local_addr, const char* shared_secret); /* *=========================================================================== * ipike_radius_receive_buffer_size *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ip_u32 ipike_radius_receive_buffer_size(); /* *=========================================================================== * ipike_radius_alloc_receive_buffer *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ipradius_header* ipike_radius_alloc_receive_buffer(); /* *=========================================================================== * ipike_radius_free_receive_buffer *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC void ipike_radius_free_receive_buffer(Ipradius_header** msg); /* *=========================================================================== * ipeap_sim_radius_get_vendor_attr *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ip_u8* ipike_radius_get_vendor_attr( Ipradius_header* msg, Ip_u32 vendor, Ip_u8 subtype, Ip_u32* len); /* *=========================================================================== * ipike_radius_decrypt_microsoft_key *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ip_u8* ipike_radius_decrypt_microsoft_key( const Ip_u8* enc, Ip_u32 enc_len, const Ip_u8* req_authenticator, const Ip_u8* secret, Ip_u32 secret_len, Ip_u32* reslen); /* *=========================================================================== * ipike_radius_get_cisco_session_key *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ipike_error ipike_radius_get_cisco_session_key( Ipike_radius_ctx* ctx, Ip_u8** key, Ip_u32* key_len); /* *=========================================================================== * ipike_radius_get_microsoft_key *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ipike_error ipike_radius_get_microsoft_keys( Ipike_radius_ctx* ctx, Ip_u8** akey_recv, Ip_u8** akey_send, Ip_u32* key_len); /* *=========================================================================== * ipike_radius_event_callback *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ip_err ipike_radius_event_callback( int event, Ipradius_request* request, Ipradius_header* request_packet, Ipradius_header* response_packet, Ipradius_server* server); /* *=========================================================================== * ipike_radius_msg_add_eap *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ipike_error ipike_radius_msg_add_eap(Ipeap_pkt_hdr* hdr, Ipradius_header* msg); /* *=========================================================================== * ipike_radius_msg_extract_eap *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ipike_error ipike_radius_msg_extract_eap( Ipradius_header* msg, Ipike_value* eap); /* *=========================================================================== * ipike_radius_msg_add_cookies *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ipike_error ipike_radius_msg_add_unique_id( Ip_u8* unique_id, Ip_u32 unique_id_len, Ipradius_header* msg); /* *=========================================================================== * ipike_radius_msg_extract_unique_id *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ipike_error ipike_radius_msg_extract_unique_id( Ipradius_header* msg, Ip_u8* unique_id, Ip_u32* unique_id_len); /* *=========================================================================== * ipike_radius_send_message *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ip_err ipike_radius_send_message(Ipike_radius_ctx* ctx, Ipradius_header* msg); /* *=========================================================================== * ipike_radius_receive_message *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC Ip_err ipike_radius_receive_message(Ip_fd fd, Ipradius_header* msg); /* **************************************************************************** * 8 DATA **************************************************************************** */ const Ip_u8 EAP_START[] = { 0x01,0x00,0x00,0x05,0x01 }; const Ip_u32 EAP_START_LEN = 5; /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipike_radius_create_server *=========================================================================== */ IP_STATIC Ipike_radius_server* ipike_radius_create_server( union Ip_sockaddr_union* server_addr, union Ip_sockaddr_union* local_addr, const char* shared_secret) { Ipike_radius_server* ctx = IP_NULL; if ((server_addr == IP_NULL) || (local_addr == IP_NULL) || (shared_secret == IP_NULL)) { IPCOM_LOG0(DEBUG2, "ipike_radius_get_server: Bad radius configuration"); return IP_NULL; } if (!ipike.radius.initialized) (void)ipike_radius_startup(); ctx = (Ipike_radius_server*)ipcom_calloc(1, sizeof(*ctx)); if (ctx != IP_NULL) { IPCOM_LOG1(DEBUG2, "ipike_radius_get_server: Radius server: '%s'", ipike_addr_to_str(server_addr)); IPCOM_LOG1(DEBUG2, "ipike_radius_get_server: Local address: '%s'", ipike_addr_to_str(local_addr)); (void)ipike_addr_copy(&ctx->server.server_addr, server_addr); (void)ipike_addr_copy(&ctx->local_addr, local_addr); if (ipike_addr_get_port(&ctx->server.server_addr) == 0) (void)ipike_addr_set_port(&ctx->server.server_addr, 1812); ctx->fd = IP_INVALID_SOCKET; ctx->server.index = 0; ctx->server.pollnum = 0; ctx->server.pollsec = 3; if (ipcom_strlen(shared_secret) < sizeof(ctx->server.shared_secret)) { ipcom_strcpy((char*)ctx->server.shared_secret, shared_secret); ctx->server.shared_secret_len = (Ip_u32) ipcom_strlen((char*)ctx->server.shared_secret); ctx->server.conf[0] = (unsigned long)-1; (void)ipradius_server_add(&ctx->server); #ifdef IPCOM_USE_INET if (ctx->local_addr.sa.sa_family == IP_AF_INET) { ctx->fd = ipcom_socket(IP_AF_INET, IP_SOCK_DGRAM, 0); if (ctx->fd != IP_INVALID_SOCKET) { if (ipcom_bind(ctx->fd, (struct Ip_sockaddr *)&ctx->local_addr, sizeof(ctx->local_addr)) < 0) { ipcom_socketclose(ctx->fd); ctx->fd = IP_INVALID_SOCKET; } } } #endif #ifdef IPCOM_USE_INET6 if (ctx->local_addr.sa.sa_family == IP_AF_INET6) { ctx->fd = ipcom_socket(IP_AF_INET6, IP_SOCK_DGRAM, 0); if (ctx->fd != IP_INVALID_SOCKET) { if (ipcom_bind(ctx->fd, (struct Ip_sockaddr *)&ctx->local_addr, sizeof(ctx->local_addr)) < 0) { ipcom_socketclose(ctx->fd); ctx->fd = IP_INVALID_SOCKET; } } } #endif } if ((ctx->fd != IP_INVALID_SOCKET) && (ipike_dispatch_add_radius_socket(ctx->fd) != IPIKE_SUCCESS)) { ipcom_socketclose(ctx->fd); ctx->fd = IP_INVALID_SOCKET; } if (ctx->fd == IP_INVALID_SOCKET) { IPCOM_LOG0(ERR, "ipike_radius_create: failed to connect radius server"); ipcom_free(ctx); ctx = IP_NULL; } } return ctx; } /* *=========================================================================== * ipike_radius_free_server *=========================================================================== */ IP_STATIC void ipike_radius_free_server(Ipike_radius_server* ctx) { if (ctx != IP_NULL) { (void)ipradius_server_remove(&ctx->server); if (ctx->fd != IP_INVALID_SOCKET) { ipike_dispatch_remove_radius_socket(ctx->fd); ipcom_socketclose(ctx->fd); } ipcom_free(ctx); } return; } /* *=========================================================================== * ipike_radius_hash_key *=========================================================================== */ IP_STATIC unsigned ipike_radius_hash_key(Ipike_radius_list_key* key) { unsigned val = 0; if (key->ike_id != IP_NULL) val = ipcom_hash_update(key->ike_id, (int)ipcom_strlen(key->ike_id), 0); else val = ipcom_hash_update(&key->fd, sizeof(key->fd), val); return val; } /* *=========================================================================== * ipike_radius_hash_obj *=========================================================================== */ IP_STATIC unsigned ipike_radius_hash_obj(Ipike_radius_list_elem* elem) { Ipike_radius_list_key key; key.fd = elem->v->fd; key.ike_id = elem->ike_id; return ipike_radius_hash_key(&key); } /* *=========================================================================== * ipike_radius_hash_cmp *=========================================================================== */ IP_STATIC Ip_bool ipike_radius_hash_cmp( Ipike_radius_list_elem* elem, Ipike_radius_list_key* key) { if (key->ike_id != IP_NULL) { if ((elem->ike_id != IP_NULL) && (!ipcom_strcmp(elem->ike_id, key->ike_id))) return IP_TRUE; } else { if (key->fd == elem->v->fd) return IP_TRUE; } return IP_FALSE; } /* *=========================================================================== * ipike_radius_hash_add *=========================================================================== */ IP_STATIC Ipike_error ipike_radius_hash_add(Ipike_radius_server* server, const char* ike_id) { Ipike_radius_list_elem* elem; /* Create for serach by both ike_id and fd */ elem = ipcom_malloc(sizeof(*elem)); if (elem == IP_NULL) { IP_PANIC(); return IPIKE_OUT_OF_MEMORY; } elem->v = server; elem->ike_id = ipcom_strdup(ike_id); if (ipcom_hash_add(ipike.radius.hash_table, elem) != IPCOM_SUCCESS) { ipcom_free(elem); return IPIKE_FAILED; } elem = ipcom_malloc(sizeof(*elem)); if (elem == IP_NULL) { IP_PANIC(); return IPIKE_OUT_OF_MEMORY; } elem->v = server; elem->ike_id = IP_NULL; if (ipcom_hash_add(ipike.radius.hash_table, elem) != IPCOM_SUCCESS) { ipcom_free(elem); return IPIKE_FAILED; } return IPIKE_SUCCESS; } /* *=========================================================================== * ipike_sadb_hash_free *=========================================================================== */ IP_STATIC void ipike_radius_hash_free(Ipike_radius_list_elem* elem, void* unused) { (void) unused; /* Always added twice, so only delete server when ike_id present */ if (elem->ike_id != IP_NULL) { ipike_radius_free_server(elem->v); ipcom_free(elem->ike_id); } ipcom_free(elem); return; } /* *=========================================================================== * ipike_radius_hash_get *=========================================================================== */ IP_STATIC Ipike_radius_server* ipike_radius_hash_get(Ip_fd fd, const char* ike_id) { Ipike_radius_list_key key; Ipike_radius_list_elem* elem; if (ipike.radius.hash_table != IP_NULL) { key.fd = fd; key.ike_id = ike_id; elem = (Ipike_radius_list_elem*)ipcom_hash_get(ipike.radius.hash_table, &key); if (elem != IP_NULL) return elem->v; } return IP_NULL; } /* *=========================================================================== * ipike_radius_hash_unique_id_key *=========================================================================== */ IP_STATIC unsigned ipike_radius_hash_unique_id_key(Ip_u8* key) { return ipcom_hash_update((void*)key, 1, 0); } /* *=========================================================================== * ipike_radius_hash_unique_id_obj *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC unsigned ipike_radius_hash_unique_id_obj(Ipike_radius_unique_id* elem) { return ipike_radius_hash_unique_id_key(&elem->radius_id); } /* *=========================================================================== * ipike_radius_hash_unique_id_cmp *=========================================================================== */ IP_STATIC Ip_bool ipike_radius_hash_unique_id_cmp(Ip_u8* key, Ipike_radius_unique_id* elem) { if (*key != elem->radius_id) return IP_FALSE; return IP_TRUE; } /* *=========================================================================== * ipike_radius_hash_unique_id_get *=========================================================================== */ IP_STATIC Ip_bool ipike_radius_hash_unique_id_get(Ip_u8 rad_id, Ip_u8* id, Ip_u32* id_len) { Ipike_radius_unique_id* elem; if (ipike.radius.unique_id_table != IP_NULL) { elem = (Ipike_radius_unique_id*)ipcom_hash_get(ipike.radius.unique_id_table, &rad_id); if (elem == IP_NULL) { return IP_FALSE; } else { if (IP_NULL != id) { if (*id_len < elem->unique_id_len) return IP_FALSE; *id_len = elem->unique_id_len; ipcom_memcpy(id, elem->unique_id, *id_len); } return IP_TRUE; } } return IP_FALSE; } /* *=========================================================================== * ipike_radius_hash_unique_id_free *=========================================================================== */ IP_STATIC void ipike_radius_hash_unique_id_free(Ipike_radius_unique_id* elem, void* unused) { (void) unused; if (IP_NULL != elem) { if (IP_NULL != elem->unique_id) ipcom_free(elem->unique_id); ipcom_free(elem); } return; } /* *=========================================================================== * ipike_radius_hash_unique_id_remove *=========================================================================== */ IP_STATIC void ipike_radius_hash_unique_id_remove(Ip_u8 rad_id) { Ipike_radius_unique_id* elem = IP_NULL; elem = (Ipike_radius_unique_id*)ipcom_hash_get(ipike.radius.unique_id_table, &rad_id); if (elem != IP_NULL) { (void)ipcom_hash_remove(ipike.radius.unique_id_table, (void*)elem); ipike_radius_hash_unique_id_free(elem, IP_NULL); } return; } /* *=========================================================================== * ipike_radius_hash_unique_id_add *=========================================================================== */ IP_STATIC Ip_bool ipike_radius_hash_unique_id_add(Ip_u8 rad_id, Ip_u8* id, Ip_u32 id_len) { Ip_bool success = IP_FALSE; Ipike_radius_unique_id* elem = IP_NULL; if (ipike_radius_hash_unique_id_get(rad_id, IP_NULL, IP_NULL)) { IPCOM_LOG1(DEBUG, "ipike_radius_hash_unique_id_add: id %u already used, will be removed", rad_id); ipike_radius_hash_unique_id_remove(rad_id); } elem = ipcom_malloc(sizeof(*elem)); if (IP_NULL != elem) { elem->unique_id = ipcom_malloc(id_len); if (IP_NULL != elem->unique_id) { elem->radius_id = rad_id; ipcom_memcpy(elem->unique_id, id, id_len); elem->unique_id_len = id_len; if (ipcom_hash_add(ipike.radius.unique_id_table, elem) == IPCOM_SUCCESS) success = IP_TRUE; } if (!success) ipike_radius_hash_unique_id_free(elem, IP_NULL); } return success; } /* *=========================================================================== * ipike_radius_get_server *=========================================================================== */ IP_STATIC Ipike_radius_server* ipike_radius_get_server( const char* ike_id, union Ip_sockaddr_union* server_addr, union Ip_sockaddr_union* local_addr, const char* shared_secret) { Ipike_radius_server* server; /* Try to get radius server by ike_id and create at failure */ server = ipike_radius_hash_get(0, ike_id); if (server == IP_NULL) { server = ipike_radius_create_server(server_addr, local_addr, shared_secret); if (server != IP_NULL) { if (ipike_radius_hash_add(server, ike_id) != IPIKE_SUCCESS) { ipike_radius_free_server(server); server = IP_NULL; } } } return server; } /* *=========================================================================== * ipike_radius_receive_buffer_size *=========================================================================== */ IP_STATIC Ip_u32 ipike_radius_receive_buffer_size() { return (Ip_u32)(IPIKE_EAP_MAX_BUF); } /* *=========================================================================== * ipike_radius_alloc_receive_buffer *=========================================================================== */ IP_STATIC Ipradius_header* ipike_radius_alloc_receive_buffer() { return (Ipradius_header*)ipcom_calloc(1, ipike_radius_receive_buffer_size()); } /* *=========================================================================== * ipike_radius_free_receive_buffer *=========================================================================== */ IP_STATIC void ipike_radius_free_receive_buffer(Ipradius_header** msg) { if ((msg != IP_NULL) && (*msg != IP_NULL)) { ipcom_free(*msg); *msg = IP_NULL; } return; } /* *=========================================================================== * ipeap_sim_radius_get_vendor_attr *=========================================================================== */ IP_STATIC Ip_u8* ipike_radius_get_vendor_attr( Ipradius_header* msg, Ip_u32 vendor, Ip_u8 subtype, Ip_u32* len) { Ip_u8* attr = IP_NULL; Ip_u8* data = IP_NULL; while ((attr = ipradius_attr_get_next(msg, attr)) != IP_NULL) { if ((attr[0] == IPRADIUS_ATTR_Vendor_Specific) && (attr[1] > 7) && (attr[2] == ((vendor >> 24) & 0xff)) && (attr[3] == ((vendor >> 16) & 0xff)) && (attr[4] == ((vendor >> 8) & 0xff)) && (attr[5] == ((vendor) & 0xff)) && (attr[6] == subtype)) { *len = (Ip_u32) (attr[7] - 2); data = &attr[8]; break; } } return data; } /* *=========================================================================== * ipike_radius_decrypt_microsoft_key *=========================================================================== */ IP_STATIC Ip_u8* ipike_radius_decrypt_microsoft_key( const Ip_u8* enc, Ip_u32 enc_len, const Ip_u8* req_authenticator, const Ip_u8* secret, Ip_u32 secret_len, Ip_u32* reslen) { const Ip_u8* salt = IP_NULL; Ip_u8* plain = IP_NULL; Ip_bool first = IP_TRUE; Ip_size_t left = 0; const Ip_u8* pos = IP_NULL; Ip_u8* p = IP_NULL; MD5_CTX ctx = {0}; Ip_u8 digest[16] = {0}; Ip_u32 i = 0; Ip_u8* res = IP_NULL; /* For detail information read RFC 2548 */ /* First two bytes is salt value */ salt = enc; pos = enc + 2; left = enc_len - 2; /* Should be multiple of 16 bytes */ if ((left % 16) != 0) return IP_NULL; plain = ipcom_malloc(left); if (plain == IP_NULL) return IP_NULL; p = plain; while (left > 0) { MD5_Init(&ctx); MD5_Update(&ctx, secret, secret_len); /* round 0: MD5(Secret | Authenticator | Salt) * round >0: MD5(Secret | (round - 1)) */ if (first) { MD5_Update(&ctx, req_authenticator, 16); MD5_Update(&ctx, salt, 2); first = IP_FALSE; } else { MD5_Update(&ctx, pos - sizeof(digest), sizeof(digest)); } MD5_Final(digest, &ctx); /* xor with digest to decrypt */ for (i = 0; i < sizeof(digest); i++) p[i] = (Ip_u8)(pos[i] ^ digest[i]); p += sizeof(digest); pos += sizeof(digest); left -= sizeof(digest); } if (plain[0] >= (enc_len - 2)) { ipcom_free(plain); return IP_NULL; } res = ipcom_malloc(plain[0]); if (res == IP_NULL) { ipcom_free(plain); return IP_NULL; } ipcom_memcpy(res, plain + 1, plain[0]); if (reslen != IP_NULL) *reslen = plain[0]; ipcom_free(plain); return res; } /* *=========================================================================== * ipike_radius_get_microsoft_key *=========================================================================== */ IP_STATIC Ipike_error ipike_radius_get_cisco_session_key( Ipike_radius_ctx* ctx, Ip_u8** key, Ip_u32* key_len) { Ip_u8 *enc_key = IP_NULL; Ip_u32 enc_key_len = 0; Ipike_error err = IPCOM_ERR_FAILED; ip_assert(key); /* Try to get cisco session key */ enc_key = ipike_radius_get_vendor_attr( ctx->last_msg, IPIKE_RADIUS_VENDOR_ID_CISCO, IPIKE_RADIUS_VENDOR_ATTR_CISCO_SESSION_KEY, &enc_key_len); /* Got session key? */ if ( enc_key ) { const char *enc_key_prefix = "leap:session-key="; Ip_u32 enc_key_prefix_len = (Ip_u32) strlen(enc_key_prefix); /* Make sure we have correct prefix */ if ( enc_key_prefix_len > enc_key_len || ipcom_strncmp(enc_key_prefix, (const char *)enc_key, enc_key_prefix_len) != 0 ) { IPCOM_LOG1(ERR, "ipike_radius_get_cisco_session_key() :: expected '%s' as key prefix", enc_key_prefix); return IPCOM_ERR_FAILED; } /* Validate session key length */ if ( enc_key_len - enc_key_prefix_len != IPIKE_RADIUS_CISCO_SESSION_KEY_LEN ) { IPCOM_LOG1(ERR, "ipike_radius_get_cisco_session_key() :: bad key length '%u'", enc_key_len - enc_key_prefix_len); } /* Using the same algorithm as mschap */ *key = ipike_radius_decrypt_microsoft_key(enc_key + enc_key_prefix_len, enc_key_len - enc_key_prefix_len, ctx->last_msg->auth, ctx->server->server.shared_secret, ctx->server->server.shared_secret_len, key_len); /* Decrypt Successful?*/ if ( *key ) { (void)ipike_print_memory(IPCOM_LOG_DEBUG2, "ipike_radius_get_cisco_session_key() ::"\ " Cisco EAP session key:\n", *key, *key_len); err = IPIKE_SUCCESS; } else { IPCOM_LOG0(ERR, "ipike_radius_get_cisco_session_key() ::"\ " failed to decrypt cisco session key"); } } return err; } /* *=========================================================================== * ipike_radius_get_microsoft_key *=========================================================================== */ IP_STATIC Ipike_error ipike_radius_get_microsoft_keys( Ipike_radius_ctx* ctx, Ip_u8** akey_recv, Ip_u8** akey_send, Ip_u32* key_len) { Ip_err err = IPCOM_ERR_FAILED; Ip_u8* key_send = IP_NULL; Ip_u32 key_send_len = 0; Ip_u8* key_recv = IP_NULL; Ip_u32 key_recv_len = 0; *akey_recv = IP_NULL; *akey_send = IP_NULL; *key_len = 0; /* key_recv and key_send will be pointers to existing buffers */ key_recv = ipike_radius_get_vendor_attr( ctx->last_msg, IPIKE_RADIUS_VENDOR_ID_MICROSOFT, IPIKE_RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY, &key_recv_len); key_send = ipike_radius_get_vendor_attr( ctx->last_msg, IPIKE_RADIUS_VENDOR_ID_MICROSOFT, IPIKE_RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, &key_send_len); if ((key_recv != IP_NULL) && (key_send != IP_NULL)) { /* key_recv and key_send will be allocated */ key_recv = ipike_radius_decrypt_microsoft_key( key_recv, key_recv_len, ctx->last_msg->auth, ctx->server->server.shared_secret, ctx->server->server.shared_secret_len, &key_recv_len); key_send = ipike_radius_decrypt_microsoft_key( key_send, key_send_len, ctx->last_msg->auth, ctx->server->server.shared_secret, ctx->server->server.shared_secret_len, &key_send_len); if ((key_send == IP_NULL) || (key_recv == IP_NULL) || (key_recv_len != key_send_len)) { ipcom_free(key_recv); ipcom_free(key_send); } else { *akey_recv = key_recv; *akey_send = key_send; *key_len = key_recv_len; err = IPIKE_SUCCESS; } } return err; } /* *=========================================================================== * ipike_radius_event_callback *=========================================================================== */ IP_STATIC Ip_err ipike_radius_event_callback( int event, Ipradius_request* request, Ipradius_header* request_packet, Ipradius_header* response_packet, Ipradius_server* server) { (void)request; (void)request_packet; (void)response_packet; (void)server; switch(event) { case IPRADIUS_EVENT_ACCESS_ACCEPT: break; case IPRADIUS_EVENT_ACCESS_REJECT: break; case IPRADIUS_EVENT_ACCESS_CHALLENGE: break; case IPRADIUS_EVENT_ACCESS_TIMEOUT: break; case IPRADIUS_EVENT_ACCESS_CANCELLED: break; case IPRADIUS_EVENT_ACCOUNTING_RESPONSE: break; case IPRADIUS_EVENT_ACCOUNTING_REQUEST: break; case IPRADIUS_EVENT_ACCOUNTING_TIMEOUT: break; case IPRADIUS_EVENT_ACCOUNTING_CANCELLED: break; default: IP_PANIC(); break; } return IPCOM_SUCCESS; } /* *=========================================================================== * ipike_radius_msg_add_eap *=========================================================================== */ IP_STATIC Ipike_error ipike_radius_msg_add_eap(Ipeap_pkt_hdr* hdr, Ipradius_header* msg) { Ip_u8* data = IP_NULL; Ip_size_t len = 0; Ip_size_t n = 0; data = (Ip_u8*)hdr; len = ip_htons(hdr->len); while (len > 0) { /* Maximum 255 bytes for each attribute, so split to multiple */ if (len > 253) n = 253; else n = len; if (ipradius_attr_add_array(msg, IPRADIUS_ATTR_EAP_Message, data, (Ip_u8)n)) return IPIKE_FAILED; data += n; len -= n; } return IPIKE_SUCCESS; } /* *=========================================================================== * ipike_radius_msg_extract_eap *=========================================================================== */ IP_STATIC Ipike_error ipike_radius_msg_extract_eap( Ipradius_header* msg, Ipike_value* eap) { Ip_u8* attr = IP_NULL; ipcom_free(eap->value); eap->value = IP_NULL; eap->len = 0; while ((attr = ipradius_attr_get_next(msg, attr)) != IP_NULL) { if (attr[0] == IPRADIUS_ATTR_EAP_Message) { eap->value = ipcom_realloc(eap->value, eap->len + attr[1]); if (eap->value != IP_NULL) { ipcom_memcpy(eap->value + eap->len, &attr[2], (Ip_size_t) (attr[1]-2)); eap->len += (Ip_size_t) attr[1] - 2; } } } return (eap->value != IP_NULL) ? IPIKE_SUCCESS : IPIKE_FAILED; } /* *=========================================================================== * ipike_radius_msg_add_cookies *=========================================================================== */ IP_STATIC Ipike_error ipike_radius_msg_add_unique_id( Ip_u8* unique_id, Ip_u32 unique_id_len, Ipradius_header* msg) { char* state; state = ipcom_malloc(2*unique_id_len+1); if (state == IP_NULL) return IPIKE_FAILED; ipike_bin2hex(unique_id, unique_id_len, state); if (ipradius_attr_add_array(msg, IPRADIUS_ATTR_Proxy_State, (Ip_u8*)state, (Ip_u8)ipcom_strlen(state)) != IPCOM_SUCCESS) { ipcom_free(state); return IPIKE_FAILED; } ipcom_free(state); return IPIKE_SUCCESS; } /* *=========================================================================== * ipike_radius_msg_extract_unique_id *=========================================================================== */ IP_STATIC Ipike_error ipike_radius_msg_extract_unique_id( Ipradius_header* msg, Ip_u8* unique_id, Ip_u32* unique_id_len) { Ip_u8* attr = IP_NULL; Ip_u8* p = IP_NULL; Ip_size_t n = 0; while ((attr = ipradius_attr_get_next(msg, attr)) != IP_NULL) { if (attr[0] == IPRADIUS_ATTR_Proxy_State) { if (attr[1] > ((2 * (*unique_id_len)) + 2)) return IPIKE_FAILED; n = (Ip_size_t) (attr[1] - 2); p = ipike_hex2bin((char*)&attr[2], &n); if (p != IP_NULL) { *unique_id_len = (Ip_u32) n; ipcom_memcpy(unique_id, p, n); ipcom_free(p); } break; } } return ((p != IP_NULL) ? IPIKE_SUCCESS : IPIKE_FAILED); } /* *=========================================================================== * ipike_radius_send_message *=========================================================================== */ IP_STATIC Ip_err ipike_radius_send_message(Ipike_radius_ctx* ctx, Ipradius_header* msg) { Ipradius_request request = {0}; Ip_u8 errattr = 0; request.unique = ctx->server->id; request.pwd_type = IPRADIUS_No_Password; /* Set RADIUS socket descriptor and event callback. */ #ifdef IPCOM_USE_INET if (ctx->server->local_addr.sa.sa_family == IP_AF_INET) request.fd = ctx->server->fd; else request.fd = IP_INVALID_SOCKET; #endif #ifdef IPCOM_USE_INET6 if (ctx->server->local_addr.sa.sa_family == IP_AF_INET6) request.fd6 = ctx->server->fd; else request.fd6 = IP_INVALID_SOCKET; #endif request.event_callback = ipike_radius_event_callback; request.num_servers = 1; ipcom_memcpy(&request.serv_list[0].server_addr, &ctx->server->server.server_addr, sizeof(union Ip_sockaddr_union)); request.serv_list[0].server_type = ctx->server->server.server_type; request.serv_list[0].pollnum = ctx->server->server.pollnum; request.serv_list[0].pollsec = ctx->server->server.pollsec; return ipradius_access_request(msg, &request, &errattr); } /* *=========================================================================== * ipike_radius_receive_message *=========================================================================== */ IP_STATIC Ip_err ipike_radius_receive_message( Ip_fd fd, Ipradius_header* msg) { Ip_s32 maxfd; Ip_s32 num; Ip_fd_set read_set; struct Ip_timeval tmo; Ip_err retval; Ip_ssize_t length; Ip_socklen_t addr_len; union Ip_sockaddr_union addr; Ip_u32 count = 0; again: IP_FD_ZERO(&read_set); IP_FD_SET(fd, &read_set); maxfd = (Ip_s32)fd; tmo.tv_usec = 0; tmo.tv_sec = 3; num = ipcom_socketselect(maxfd + 1, &read_set, IP_NULL, IP_NULL, &tmo); if (num < 0) { IPCOM_LOG0(ERR, "ipike_radius_receive_message - out of memory"); return IPIKE_FAILED; } else if (num == 0) { IPCOM_LOG0(ERR, "ipike_radius_receive_message - no server reply"); return IPIKE_FAILED; } /* Got a packet waiting... */ else if (num == 1) { ipcom_memset(&addr, 0, sizeof(addr)); addr_len = sizeof(addr); /* Read the packet. */ length = ipcom_recvfrom(fd, msg, ipike_radius_receive_buffer_size(), 0, &addr.sa, &addr_len); if (length <= 0) { IPCOM_LOG1(ERR, "ipike_radius_receive_message - recv failed (%d)", ipcom_errno); if (count++ < 10) goto again; return IPIKE_FAILED; } if (ip_ntohs(msg->length)>length || ip_ntohs(msg->length)<=0) { IPCOM_LOG0(ERR, "ipike_radius_receive_message - invalid packet"); return IPIKE_FAILED; } /* Pass packet to RADIUS for input parsing. */ /* Ipradius_header is followed by data payload, accessing data seems over access */ /* coverity[tainted_data] */ retval = ipradius_read(msg, (Ip_size_t) length, &addr.sa); if (retval != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipike_radius_receive_message - input parsing failed"); if (count++ < 10) goto again; return IPIKE_FAILED; } /* Valid packet, return. */ return 0; } IP_PANIC(); IPCOM_LOG1(ERR, "ipike_radius_receive_message - internal error, num = %d", num); return IPIKE_FAILED; } /* **************************************************************************** * 10 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipike_radius_create *=========================================================================== */ IP_GLOBAL Ipike_radius_ctx* ipike_radius_create( const char* ike_id, union Ip_sockaddr_union* server_addr, union Ip_sockaddr_union* local_addr, const char* shared_secret) { Ipike_radius_ctx* ctx = IP_NULL; ctx = (Ipike_radius_ctx*)ipcom_calloc(1, sizeof(*ctx)); if (ctx != IP_NULL) { ctx->server = ipike_radius_get_server(ike_id, server_addr, local_addr, shared_secret); if (ctx->server == IP_NULL) { ipcom_free(ctx); ctx = IP_NULL; } } return ctx; } /* *=========================================================================== * ipike_radius_free *=========================================================================== */ IP_GLOBAL void ipike_radius_free(Ipike_radius_ctx* ctx) { if (ctx != IP_NULL) { if (ctx->last_msg != IP_NULL) ipike_radius_free_receive_buffer(&ctx->last_msg); ipcom_free(ctx); } return; } /* *=========================================================================== * ipike_radius_send *=========================================================================== */ IP_GLOBAL Ipike_error ipike_radius_send( Ipike_radius_ctx* ctx, Ip_u8* unique_id, Ip_u32 unique_id_len, Ipeap_pkt* pkt) { Ip_err err = IPIKE_FAILED; Ipeap_pkt_hdr* hdr = IP_NULL; Ipradius_header* msg = IP_NULL; Ip_u8* data = IP_NULL; Ip_u8 len = 0; msg = ipradius_access_request_pkt_malloc(); if (msg == IP_NULL) { IP_PANIC(); return IPIKE_OUT_OF_MEMORY; } msg->id = ++ctx->server->id; msg->code = IPRADIUS_CODE_Access_Request; hdr = (Ipeap_pkt_hdr*)&pkt->data[pkt->start]; data = (Ip_u8*)hdr + sizeof(*hdr); if (ctx->identity[0] != 0) if (ipradius_attr_add_array(msg, IPRADIUS_ATTR_User_Name, (Ip_u8*)ctx->identity, (Ip_u8)ipcom_strlen(ctx->identity))) goto cleanup; /* Add unique id so we will find the correct exchange at response */ if (ipike_radius_msg_add_unique_id(unique_id, unique_id_len, msg) != IPIKE_SUCCESS) goto cleanup; /* Copy state from last response */ if (ctx->last_msg != IP_NULL) if ((data = ipradius_attr_get_array(ctx->last_msg, IPRADIUS_ATTR_State, &len)) != IP_NULL) if (ipradius_attr_add_array(msg, IPRADIUS_ATTR_State, data, (Ip_u8)(len-2))) goto cleanup; if (ipike_radius_msg_add_eap(hdr, msg)) goto cleanup; if (ipike_radius_send_message(ctx, msg)) goto cleanup; /* Store unique id, so we may handle if state isn't returned */ (void)ipike_radius_hash_unique_id_add(msg->id, unique_id, unique_id_len); msg = IP_NULL; err = IPCOM_SUCCESS; cleanup: if (msg != IP_NULL) (void)ipradius_access_request_pkt_free(msg); return err; } /* *=========================================================================== * ipike_radius_receive *=========================================================================== */ IP_GLOBAL Ipike_error ipike_radius_receive( Ip_fd fd, Ipike_value* eap, Ip_u8* unique_id, Ip_u32* unique_id_len, Ipradius_header** last_msg) { Ipike_error err = IPIKE_FAILED; Ipradius_header* msg = IP_NULL; Ipike_radius_server* ctx = IP_NULL; if ((ctx = ipike_radius_hash_get(fd, IP_NULL)) == IP_NULL) { IPCOM_LOG0(ERR, "ipike_radius_receive - unknown radius server"); goto cleanup; } msg = ipike_radius_alloc_receive_buffer(); if (msg==IP_NULL) { IPCOM_LOG0(ERR, "ipike_radius_receive - mem alloc failed"); goto cleanup; } /* Ipradius_header is followed by data payload, accessing data seems over access */ /* coverity[tainted_data] */ if (ipike_radius_receive_message(fd, msg)) { IPCOM_LOG0(ERR, "ipike_radius_receive - receive failed"); goto cleanup; } if (msg->code != IPRADIUS_CODE_Access_Accept && msg->code != IPRADIUS_CODE_Access_Reject && msg->code != IPRADIUS_CODE_Access_Challenge) { IPCOM_LOG0(ERR, "ipike_radius_receive - unknown message"); goto cleanup; } IPCOM_LOG0(DEBUG2, "ipike_radius_receive - message received"); /* Ipradius_header is followed by data payload, accessing data seems over access */ /* coverity[tainted_data] */ if (ipradius_attr_verify_message_auth(msg, ctx->server.shared_secret, ctx->server.shared_secret_len)) { IPCOM_LOG0(ERR, "ipike_radius_receive - failed to verify message authenticator"); goto cleanup; } /* Ipradius_header is followed by data payload, accessing data seems over access */ /* coverity[tainted_data] */ if (ipike_radius_msg_extract_eap(msg, eap) != IPIKE_SUCCESS) { IPCOM_LOG0(ERR, "ipike_radius_receive - failed to extract eap message"); goto cleanup; } if (ipike_radius_msg_extract_unique_id(msg, unique_id, unique_id_len) != IPIKE_SUCCESS) { if (!ipike_radius_hash_unique_id_get(msg->id, unique_id, unique_id_len)) { IPCOM_LOG0(ERR, "ipike_radius_receive - failed to extract cookies"); goto cleanup; } } err = IPIKE_SUCCESS; *last_msg = msg; msg = IP_NULL; cleanup: if (msg != IP_NULL) ipike_radius_free_receive_buffer(&msg); return err; } /* *=========================================================================== * ipike_radius_process_pkt *=========================================================================== */ IP_GLOBAL Ipike_error ipike_radius_process_pkt( Ipike_radius_ctx* ctx, Ip_u8* unique_id, Ip_u32 unique_id_len, Ipeap_pkt* pkt_in, Ipeap_pkt* pkt_out) { Ipike_error err = IPEAP_FAILURE; Ipeap_pkt_hdr* hdr = IP_NULL; Ip_u8* data = IP_NULL; Ip_u32 len = 0; if ((pkt_in->end - pkt_in->start) == 0) { if (pkt_out->max_len >= (int)EAP_START_LEN) { pkt_out->end = pkt_out->start + (int)EAP_START_LEN; ipcom_memcpy(pkt_out->data+ pkt_out->start, EAP_START, EAP_START_LEN); err = IPEAP_PENDING_SEND_OUT_PKT; } } else { /* Get identity from packet */ hdr = (Ipeap_pkt_hdr*)&pkt_in->data[pkt_in->start]; data = (Ip_u8*)hdr + IPIKE_EAP_HEADER_LEN; if ((hdr->code == IPEAP_PKT_RESPONSE) && (ip_htons(hdr->len) > IPIKE_EAP_HEADER_LEN) && (hdr->type == IPEAP_TYPE_IDENTITY)) { len = ip_htons(hdr->len) - IPIKE_EAP_HEADER_LEN; if (len < sizeof(ctx->identity)) { ipcom_memcpy(ctx->identity, data, len); ctx->identity[len] = 0; IPCOM_LOG1(DEBUG2, "Identity: '%s'", ctx->identity); } } /* Send to radius server */ if (ipike_radius_send(ctx, unique_id, unique_id_len, pkt_in) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipike_radius_process_pkt: Failed to send to radius server"); } else { err = IPCOM_SUCCESS; } } return err; } /* *=========================================================================== * ipike_radius_process_dispatcher_event *=========================================================================== */ IP_GLOBAL void ipike_radius_process_dispatcher_event(Ip_fd fd) { Ip_u8 unique_id[74] = {0}; Ip_u32 unique_id_len = sizeof(unique_id); Ipike_isakmp_exchange* ie = IP_NULL; Ipike_value eap = {0}; Ipradius_header* msg = IP_NULL; if (ipike_radius_receive( fd, &eap, unique_id, &unique_id_len, &msg) != IPIKE_SUCCESS) { IPCOM_LOG0(ERR, "ipike_radius_process_dispatcher_event: Failed to get radius message"); ipike_dispatch_remove_radius_socket(fd); } else if ((ie = ipike_isakmp_get_exchange_from_unique_id( unique_id, unique_id_len)) == IP_NULL) { ipike_radius_free_receive_buffer(&msg); IPCOM_LOG0(ERR, "ipike_radius_process_dispatcher_event: Failed to get exchange"); } else { /* Set last radius buffer, so it may be used later */ ipike_radius_free_receive_buffer(&ie->ike_sa->v2.radius_ctx->last_msg); ie->ike_sa->v2.radius_ctx->last_msg = msg; /* Set EAP message */ ipcom_free(ie->ike_sa->v2.eap.value); ie->ike_sa->v2.eap.value = eap.value; ie->ike_sa->v2.eap.len = eap.len; /* Continue processing of exchange */ ipike_isakmp_process_message_continue(ie); } return; } /* *=========================================================================== * ipike_radius_extract_key *=========================================================================== */ IP_GLOBAL void ipike_radius_extract_key(Ipike_radius_ctx* ctx, Ip_u8* key, Ip_u32* key_size) { Ip_u8* key_recv = IP_NULL; Ip_u8* key_send = IP_NULL; Ip_u32 key_len = 0; Ip_u32 max_len = 0; Ip_u32 len = 0; max_len = *key_size; *key_size = 0; if (ipike_radius_get_microsoft_keys(ctx, &key_recv, &key_send, &key_len) == IPIKE_SUCCESS) { /* Append recv key */ if ((key_len + *key_size) > max_len) len = max_len - *key_size; else len = key_len; ipcom_memcpy(key + *key_size, key_recv, len); *key_size += len; /* Append send key */ if ((key_len + *key_size) > max_len) len = max_len - *key_size; else len = key_len; ipcom_memcpy(key + *key_size, key_send, len); *key_size += len; /* free allocated keys */ ipcom_free(key_recv); ipcom_free(key_send); key_recv = IP_NULL; key_send = IP_NULL; } /* Try to get cisco session key (LEAP) */ else if ( ipike_radius_get_cisco_session_key(ctx, &key_recv, &key_len) == IPIKE_SUCCESS ) { if ( key_len > max_len ) key_len = max_len; /* Copy key */ ipcom_memcpy(key, key_recv, key_len); *key_size = key_len; /* Free allocated bytes */ ipcom_free(key_recv); key_recv = IP_NULL; } return; } /* *=========================================================================== * ipike_radius_startup *=========================================================================== */ IP_GLOBAL Ipike_error ipike_radius_startup(void) { Ipradius_open conf; if (ipike.radius.initialized) ipike_radius_shutdown(); ipike.radius.initialized = IP_TRUE; /* Initialize hash table */ ipike.radius.hash_table = ipcom_hash_new( (Ipcom_hash_obj_func) ipike_radius_hash_obj, (Ipcom_hash_key_func) ipike_radius_hash_key, (Ipcom_hash_cmp_func) ipike_radius_hash_cmp); if (ipike.radius.hash_table == IP_NULL) { IP_PANIC(); return IPIKE_OUT_OF_MEMORY; } /* Initialize hash table */ ipike.radius.unique_id_table = ipcom_hash_new( (Ipcom_hash_obj_func) ipike_radius_hash_unique_id_obj, (Ipcom_hash_key_func) ipike_radius_hash_unique_id_key, (Ipcom_hash_cmp_func) ipike_radius_hash_unique_id_cmp); if (ipike.radius.unique_id_table == IP_NULL) { IP_PANIC(); return IPIKE_OUT_OF_MEMORY; } /* Open IPRADIUS */ ipcom_strcpy(conf.ipradius_NAS_Identifier,"Interpeak IKE"); conf.ipradius_NAS_Identifier_size = (Ip_u32) ipcom_strlen(conf.ipradius_NAS_Identifier); conf.access_request_attribute_size = IPIKE_EAP_MAX_BUF; conf.accounting_request_attribute_size = IPRADIUS_accounting_request_attribute_size; conf.conf[0] = IP_TAG_END; if (ipradius_open(&conf) != IPCOM_SUCCESS) { /* Ignore failure, since no closing */ IPCOM_LOG0(DEBUG2, "ipike_radius_startup: Failed to open radius (ignored)"); } return IPIKE_SUCCESS; } /* *=========================================================================== * ipike_radius_shutdown *=========================================================================== */ IP_GLOBAL void ipike_radius_shutdown(void) { if (!ipike.radius.initialized) return; /* Close hash table */ if (ipike.radius.hash_table != IP_NULL) { ipcom_hash_for_each(ipike.radius.hash_table, (void(*)(void *, void *)) ipike_radius_hash_free, IP_NULL); ipcom_hash_delete(ipike.radius.hash_table); ipike.radius.hash_table = IP_NULL; } if (ipike.radius.unique_id_table != IP_NULL) { ipcom_hash_for_each(ipike.radius.unique_id_table, (void(*)(void *, void *)) ipike_radius_hash_unique_id_free, IP_NULL); ipcom_hash_delete(ipike.radius.unique_id_table); ipike.radius.unique_id_table = IP_NULL; } ipike.radius.initialized = IP_FALSE; return; } #else int ipike_radius_empty_file = 0; #endif /* IPIKE_USE_RADIUS */ /* **************************************************************************** * END OF FILE **************************************************************************** */