/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* ----------------------------------------------------------------------------- * * Theory of operation : * * performs EAP Radius proxy operations. * ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- Includes ----------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include "../../Helpers/pppd/eap.h" #include "../../Helpers/pppd/eap_plugin.h" #include "radius.h" #include "radlib.h" #include "radlib_vs.h" /* ----------------------------------------------------------------------------- Forward declarations ----------------------------------------------------------------------------- */ static int radius_eap_init __P((struct EAP_Input *eap_in, void **context)); static int radius_eap_dispose __P((void *context)); static int radius_eap_process __P((void *context, EAP_Input *eap_in, EAP_Output *eap_out)); static int radius_eap_attribute __P((void *context, EAP_Attribute *eap_attr)); static void makePacket(struct EAP_Output *eap_out, u_int8_t code, u_int8_t id, u_int8_t *data, u_int16_t datalen, u_int16_t action); /* ----------------------------------------------------------------------------- Definitions ----------------------------------------------------------------------------- */ static u_char eap_mppe_send_key[MPPE_MAX_KEY_LEN]; static u_char eap_mppe_recv_key[MPPE_MAX_KEY_LEN]; static int eap_mppe_keys_set = 0; struct rad_handle *rad_handle = 0; static u_int8_t current_id = 0; static u_int16_t mtu = 0; #define MAX_RETRANSMITS 10 static int retransmits = 0; static unsigned char last_state_attr[256]; static int last_state_attr_len = 0; static char hostname[256]; static struct in_addr nas_ip_address_val; void (*log_debug) __P((char *, ...)) = 0; void (*log_error) __P((char *, ...)) = 0; static eap_ext *eap_handle = NULL; // EAP proxy handler static u_int8_t output_buffer[1500]; static u_int16_t output_buffer_len = 0; /* ----------------------------------------------------------------------------- install eap handler ----------------------------------------------------------------------------- */ int radius_eap_install() { eap_handle = (eap_ext *)malloc(sizeof(eap_ext)); if (eap_handle == 0) return -1; bzero(eap_handle, sizeof(eap_ext)); eap_handle->type = 0; // catch all eap_handle->name = "EAP-Radius"; eap_handle->init = radius_eap_init; eap_handle->dispose = radius_eap_dispose; eap_handle->process = radius_eap_process; eap_handle->attribute = radius_eap_attribute; if (EapExtAdd(eap_handle)) return -1; return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ static int radius_eap_init (struct EAP_Input *eap_in, void **context) { int err, i; log_debug = eap_in->log_debug; log_error = eap_in->log_error; if (eap_in->mode != 1) { (*log_error)("Radius : Can't open Radius handler context.\n"); goto fail; } mtu = eap_in->mtu; current_id = eap_in->initial_id; last_state_attr_len = 0; eap_mppe_keys_set = 0; if (gethostname(hostname, sizeof(hostname)) < 0 ) strlcpy(hostname, "Apple", sizeof(hostname)); hostname[sizeof(hostname) - 1] = 0; // gethostname() does not always terminate the name nas_ip_address_val.s_addr = 0; if (nas_ip_address) ascii2addr(AF_INET, nas_ip_address, &nas_ip_address_val); rad_handle = rad_auth_open(); if (rad_handle == NULL) { (*log_error)("Radius : Can't open Radius handler context.\n"); goto fail; } for (i = 0; i < nb_auth_servers; i++) { struct auth_server *server = auth_servers[i]; if (server->proto & RADIUS_USE_EAP) { err = rad_add_server(rad_handle, server->address, server->port, server->secret, server->timeout, server->retries); if (err != 0) { (*log_error)("Radius : Can't use server '%s'\n", server->address); if (i == 0) goto fail; } } } return EAP_NO_ERROR; fail: if (rad_handle) { rad_close(rad_handle); rad_handle = 0; } return EAP_ERROR_GENERIC; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ static int radius_eap_dispose (void *context) { if (rad_handle) { rad_close(rad_handle); rad_handle = 0; } return EAP_NO_ERROR; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ static int radius_eap_process (void *context, struct EAP_Input *eap_in, struct EAP_Output *eap_out) { struct EAP_Packet *pkt_in; int err = 0, attr_type, len, n; unsigned char *attr_value; size_t attr_len; unsigned char *p; u_int32_t attr_vendor; char auth[MD4_SIGNATURE_SIZE + 1]; // by default, ignore the message eap_out->action = EAP_ACTION_NONE; switch (eap_in->notification) { case EAP_NOTIFICATION_START: case EAP_NOTIFICATION_PACKET: rad_create_request(rad_handle, RAD_ACCESS_REQUEST); rad_put_int(rad_handle, RAD_FRAMED_MTU, mtu); rad_put_int(rad_handle, RAD_SERVICE_TYPE, RAD_FRAMED); rad_put_int(rad_handle, RAD_FRAMED_PROTOCOL, RAD_PPP); rad_put_int(rad_handle, RAD_NAS_PORT_TYPE, nas_port_type); if (tunnel_type) rad_put_int(rad_handle, RAD_TUNNEL_TYPE, tunnel_type); if (nas_ip_address) rad_put_int(rad_handle, RAD_NAS_IP_ADDRESS, ntohl(nas_ip_address_val.s_addr)); if (nas_identifier) rad_put_string(rad_handle, RAD_NAS_IDENTIFIER, nas_identifier); /* if there is no nas_ip_address or no nas_identifier, use a hostname() as nas_identifier */ if (nas_identifier == NULL && nas_ip_address == NULL) rad_put_string(rad_handle, RAD_NAS_IDENTIFIER, hostname); /* if username is not present, Windows will reject the access request */ rad_put_attr(rad_handle, RAD_USER_NAME, eap_in->identity, strlen(eap_in->identity)); if (eap_in->notification == EAP_NOTIFICATION_START) { /* build a Identity Response and add the EAP Message attribute */ output_buffer_len = strlen(eap_in->identity) + (EAP_HEADERLEN + 1); output_buffer[0] = EAP_RESPONSE; output_buffer[1] = current_id - 1; output_buffer[2] = output_buffer_len >> 8; output_buffer[3] = output_buffer_len & 0xFF; output_buffer[4] = EAP_TYPE_IDENTITY; strlcpy((char*)&output_buffer[5], eap_in->identity, sizeof(output_buffer) - (EAP_HEADERLEN + 1)); rad_put_attr(rad_handle, RAD_EAP_MESSAGE, output_buffer, output_buffer_len); } else { /* add state sttribute if present */ if (last_state_attr_len) { rad_put_attr(rad_handle, RAD_STATE, last_state_attr, last_state_attr_len); } /* forward the EAP packet received asseveral EAP Message attributes */ pkt_in = (struct EAP_Packet *)eap_in->data; if (pkt_in->code != EAP_RESPONSE) { (*log_error)("Radius: Didn't receive an EAP response packet. (received %d)\n", pkt_in->code); makePacket(eap_out, EAP_FAILURE, current_id++, 0, 0, EAP_ACTION_SEND_AND_DONE); break; } p = (unsigned char *)pkt_in; len = ntohs(pkt_in->len); while (len > 0) { n = len > RAD_MAX_ATTR_LEN ? RAD_MAX_ATTR_LEN : len; rad_put_attr(rad_handle, RAD_EAP_MESSAGE, p, n); p += n; len -= n; } } err = rad_send_request(rad_handle); switch (err) { case RAD_ACCESS_ACCEPT: /* authentication succeeded, retrieve keys attributes if present */ eap_mppe_keys_set = 0; while ((attr_type = rad_get_attr(rad_handle, (const void **)&attr_value, &attr_len)) > 0 ) { switch (attr_type) { case RAD_VENDOR_SPECIFIC: attr_type = rad_get_vendor_attr(&attr_vendor, (const void **)&attr_value, &attr_len); switch (attr_type) { case RAD_MICROSOFT_MS_MPPE_SEND_KEY: len = rad_request_authenticator(rad_handle, auth, sizeof(auth)); if(len != -1) { radius_decryptmppekey((char*)eap_mppe_send_key, attr_value, attr_len, (char*)rad_server_secret(rad_handle), auth, len); eap_mppe_keys_set = 1; } else (*log_error)("Radius: rad-eap-mppe-send-key: could not get authenticator!\n"); break; case RAD_MICROSOFT_MS_MPPE_RECV_KEY: len = rad_request_authenticator(rad_handle, auth, sizeof(auth)); if(len != -1) { radius_decryptmppekey((char*)eap_mppe_recv_key, attr_value, attr_len, (char*)rad_server_secret(rad_handle), auth, len); eap_mppe_keys_set = 1; } else (*log_error)("Radius: rad-eap-mppe-recv-key: could not get authenticator!\n"); break; } break; } } makePacket(eap_out, EAP_SUCCESS, current_id++, 0, 0, EAP_ACTION_SEND_AND_DONE); break; case RAD_ACCESS_REJECT: /* pretty clear, the server don't like us ... */ makePacket(eap_out, EAP_FAILURE, current_id++, 0, 0, EAP_ACTION_SEND_AND_DONE); break; case RAD_ACCESS_CHALLENGE: /* concatenate all EAP message attributes, and build an EAP packet */ output_buffer_len = 0; last_state_attr_len = 0; while ((attr_type = rad_get_attr(rad_handle, (const void **)&attr_value, &attr_len)) > 0 ) { switch (attr_type) { case RAD_EAP_MESSAGE: if (output_buffer_len == 0) current_id = attr_value[1]; if ((output_buffer_len + attr_len) <= sizeof(output_buffer)) { bcopy(attr_value, output_buffer + output_buffer_len, attr_len); output_buffer_len += attr_len; } break; case RAD_STATE: /* memorize server state for next access request */ last_state_attr_len = attr_len; bcopy(attr_value, last_state_attr, attr_len); break; } } if (!output_buffer_len) { (*log_error)("Radius : Incorrect Access Challenge received\n"); makePacket(eap_out, EAP_FAILURE, current_id++, 0, 0, EAP_ACTION_SEND_AND_DONE); break; } retransmits = 0; eap_out->action = EAP_ACTION_SEND_WITH_TIMEOUT; eap_out->data = output_buffer; eap_out->data_len = output_buffer_len; break; default: (*log_error)("Radius : Authentication error %d. %s.\n", err, rad_strerror(rad_handle)); makePacket(eap_out, EAP_FAILURE, current_id++, 0, 0, EAP_ACTION_SEND_AND_DONE); break; } break; case EAP_NOTIFICATION_TIMEOUT: /* retransmit same EAP packet on timeout */ if (++retransmits >= MAX_RETRANSMITS) { makePacket(eap_out, EAP_FAILURE, current_id++, 0, 0, EAP_ACTION_SEND_AND_DONE); break; } eap_out->action = EAP_ACTION_SEND_WITH_TIMEOUT; eap_out->data = output_buffer; eap_out->data_len = output_buffer_len; break; } return EAP_NO_ERROR; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ static int radius_eap_attribute (void *context, struct EAP_Attribute *eap_attr) { void *data = NULL; int len = 0; eap_attr->data = 0; switch (eap_attr->type) { case EAP_ATTRIBUTE_MPPE_SEND_KEY: if (eap_mppe_keys_set) { data = eap_mppe_send_key; len = sizeof(eap_mppe_send_key); } break; case EAP_ATTRIBUTE_MPPE_RECV_KEY: if (eap_mppe_keys_set) { data = eap_mppe_recv_key; len = sizeof(eap_mppe_recv_key); } break; } if (data == NULL) return -1; eap_attr->data = data; eap_attr->data_len = len; return 0; } /* ------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------ */ static void makePacket(struct EAP_Output *eap_out, u_int8_t code, u_int8_t id, u_int8_t *data, u_int16_t datalen, u_int16_t action) { int i; switch (code) { case EAP_FAILURE: case EAP_SUCCESS: output_buffer_len = EAP_HEADERLEN; break; case EAP_REQUEST: case EAP_RESPONSE: output_buffer_len = datalen + EAP_HEADERLEN; break; } if (output_buffer_len > (mtu - PPP_HDRLEN)) { // need to handle fragmentation eap_out->action = EAP_ACTION_NONE; return; } i = 0; output_buffer[i++] = code; output_buffer[i++] = id; output_buffer[i++] = output_buffer_len >> 8; output_buffer[i++] = output_buffer_len & 0xFF; if (output_buffer_len > EAP_HEADERLEN) { bcopy(data, &output_buffer[EAP_HEADERLEN], datalen); } eap_out->action = action; eap_out->data = output_buffer; eap_out->data_len = output_buffer_len; }