#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <syslog.h>
#include <SystemConfiguration/SCValidation.h>
#include <CoreFoundation/CFArray.h>
#include <CoreFoundation/CFDictionary.h>
#include <EAP8021X/EAPClientPlugin.h>
#include <EAP8021X/EAPClientProperties.h>
#include <EAP8021X/EAPTLSUtil.h>
#include <EAP8021X/EAPCertificateUtil.h>
#include <Security/SecureTransport.h>
#include <Security/SecCertificate.h>
#include <Security/SecIdentity.h>
#include <Security/SecIdentitySearch.h>
#include <Security/SecureTransportPriv.h>
#include "myCFUtil.h"
#include "printdata.h"
#define EAPTLS_EAP_TYPE 13
EAPClientPluginFuncIntrospect eaptls_introspect;
static EAPClientPluginFuncVersion eaptls_version;
static EAPClientPluginFuncEAPType eaptls_type;
static EAPClientPluginFuncEAPName eaptls_name;
static EAPClientPluginFuncInit eaptls_init;
static EAPClientPluginFuncFree eaptls_free;
static EAPClientPluginFuncProcess eaptls_process;
static EAPClientPluginFuncFreePacket eaptls_free_packet;
static EAPClientPluginFuncSessionKey eaptls_session_key;
static EAPClientPluginFuncServerKey eaptls_server_key;
static EAPClientPluginFuncRequireProperties eaptls_require_props;
static EAPClientPluginFuncPublishProperties eaptls_publish_props;
static EAPClientPluginFuncPacketDump eaptls_packet_dump;
static EAPClientPluginFuncUserName eaptls_user_name;
typedef enum {
kRequestTypeStart,
kRequestTypeAck,
kRequestTypeData,
} RequestType;
typedef struct {
SSLContextRef ssl_context;
memoryBuffer read_buffer;
memoryBuffer write_buffer;
int last_write_size;
int previous_identifier;
memoryIO mem_io;
EAPClientState plugin_state;
CFArrayRef certs;
int mtu;
OSStatus last_ssl_error;
EAPClientStatus last_client_status;
bool handshake_complete;
OSStatus trust_ssl_error;
EAPClientStatus trust_status;
int32_t trust_proceed_id;
bool trust_proceed;
bool key_data_valid;
char key_data[128];
CFArrayRef last_trusted_server_certs;
CFArrayRef server_certs;
bool resume_sessions;
bool session_was_resumed;
} EAPTLSPluginData, * EAPTLSPluginDataRef;
enum {
kAvoidDenialOfServiceSize = 128 * 1024
};
#define BAD_IDENTIFIER (-1)
#define kEAPTLSClientLabel "client EAP encryption"
#define kEAPTLSClientLabelLength (sizeof(kEAPTLSClientLabel) - 1)
static bool
eaptls_compute_session_key(EAPTLSPluginDataRef context)
{
OSStatus status;
context->key_data_valid = FALSE;
status = EAPTLSComputeKeyData(context->ssl_context,
kEAPTLSClientLabel,
kEAPTLSClientLabelLength,
context->key_data,
sizeof(context->key_data));
if (status != noErr) {
syslog(LOG_NOTICE,
"eaptls_compute_session_key: EAPTLSComputeSessionKey failed, %s",
EAPSSLErrorString(status));
return (FALSE);
}
context->key_data_valid = TRUE;
return (TRUE);
}
static void
eaptls_free_context(EAPTLSPluginDataRef context)
{
if (context->ssl_context != NULL) {
SSLDisposeContext(context->ssl_context);
context->ssl_context = NULL;
}
my_CFRelease(&context->certs);
my_CFRelease(&context->server_certs);
my_CFRelease(&context->last_trusted_server_certs);
memoryIOClearBuffers(&context->mem_io);
free(context);
return;
}
static OSStatus
eaptls_start(EAPClientPluginDataRef plugin)
{
EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
SSLContextRef ssl_context = NULL;
OSStatus status = noErr;
if (context->ssl_context != NULL) {
SSLDisposeContext(context->ssl_context);
context->ssl_context = NULL;
}
my_CFRelease(&context->server_certs);
memoryIOClearBuffers(&context->mem_io);
ssl_context = EAPTLSMemIOContextCreate(FALSE, &context->mem_io, NULL,
&status);
if (ssl_context == NULL) {
syslog(LOG_NOTICE, "eaptls_start: EAPTLSMemIOContextCreate failed, %s",
EAPSSLErrorString(status));
goto failed;
}
status = SSLSetEnableCertVerify(ssl_context, FALSE);
if (status != noErr) {
syslog(LOG_NOTICE, "eaptls_start: SSLSetEnableCertVerify failed, %s",
EAPSSLErrorString(status));
goto failed;
}
if (context->resume_sessions && plugin->unique_id != NULL) {
status = SSLSetPeerID(ssl_context, plugin->unique_id,
plugin->unique_id_length);
if (status != noErr) {
syslog(LOG_NOTICE,
"SSLSetPeerID failed, %s", EAPSSLErrorString(status));
goto failed;
}
}
status = SSLSetCertificate(ssl_context, context->certs);
if (status != noErr) {
syslog(LOG_NOTICE,
"SSLSetCertificate failed, %s", EAPSSLErrorString(status));
goto failed;
}
context->ssl_context = ssl_context;
context->plugin_state = kEAPClientStateAuthenticating;
context->previous_identifier = BAD_IDENTIFIER;
context->last_ssl_error = noErr;
context->last_client_status = kEAPClientStatusOK;
context->handshake_complete = FALSE;
context->trust_proceed = FALSE;
context->trust_proceed_id++;
context->key_data_valid = FALSE;
context->last_write_size = 0;
context->session_was_resumed = FALSE;
return (status);
failed:
if (ssl_context != NULL) {
SSLDisposeContext(ssl_context);
}
return (status);
}
static OSStatus
copy_identity(CFDictionaryRef properties, CFArrayRef * ret_array)
{
EAPSecIdentityHandleRef id_handle = NULL;
if (properties != NULL) {
id_handle = CFDictionaryGetValue(properties,
kEAPClientPropTLSIdentityHandle);
}
return (EAPSecIdentityHandleCreateSecIdentityTrustChain(id_handle,
ret_array));
}
static EAPClientStatus
eaptls_init(EAPClientPluginDataRef plugin, CFArrayRef * required_props,
EAPClientDomainSpecificError * error)
{
EAPTLSPluginDataRef context = NULL;
EAPClientStatus result = kEAPClientStatusOK;
OSStatus status = noErr;
*error = 0;
context = malloc(sizeof(*context));
if (context == NULL) {
result = kEAPClientStatusAllocationFailed;
goto failed;
}
bzero(context, sizeof(*context));
context->mtu = plugin->mtu;
context->trust_proceed_id = random();
status = copy_identity(plugin->properties, &context->certs);
if (status != noErr) {
result = kEAPClientStatusSecurityError;
*error = status;
syslog(LOG_NOTICE,
"eaptls_init: failed to find client cert/identity, %s (%d)",
EAPSSLErrorString(status), status);
goto failed;
}
context->resume_sessions
= my_CFDictionaryGetBooleanValue(plugin->properties,
kEAPClientPropTLSEnableSessionResumption,
TRUE);
memoryIOInit(&context->mem_io, &context->read_buffer,
&context->write_buffer);
plugin->private = context;
status = eaptls_start(plugin);
if (status != noErr) {
result = kEAPClientStatusSecurityError;
*error = status;
goto failed;
}
return (result);
failed:
plugin->private = NULL;
if (context != NULL) {
eaptls_free_context(context);
}
return (result);
}
static void
eaptls_free(EAPClientPluginDataRef plugin)
{
EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
if (context != NULL) {
eaptls_free_context(context);
plugin->private = NULL;
}
return;
}
static void
eaptls_free_packet(EAPClientPluginDataRef plugin, EAPPacketRef arg)
{
if (arg != NULL) {
free(arg);
}
return;
}
static EAPPacketRef
EAPTLSPacketCreateAck(u_char identifier)
{
return (EAPTLSPacketCreate(kEAPCodeResponse, EAPTLS_EAP_TYPE,
identifier, 0, NULL, NULL));
}
static EAPPacketRef
eaptls_verify_server(EAPClientPluginDataRef plugin,
int identifier, EAPClientStatus * client_status)
{
EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
EAPPacketRef pkt = NULL;
memoryBufferRef write_buf = &context->write_buffer;
if (context->last_trusted_server_certs != NULL
&& context->server_certs != NULL
&& EAPSecCertificateListEqual(context->last_trusted_server_certs,
context->server_certs)) {
context->trust_proceed = TRUE;
return (NULL);
}
context->trust_status
= EAPTLSVerifyServerCertificateChain(plugin->properties,
context->trust_proceed_id,
context->server_certs,
&context->trust_ssl_error);
if (context->trust_status != kEAPClientStatusOK) {
syslog(LOG_NOTICE,
"eaptls_verify_server: server certificate not trusted"
", status %d %d", context->trust_status,
context->trust_ssl_error);
}
switch (context->trust_status) {
case kEAPClientStatusOK:
context->trust_proceed = TRUE;
my_CFRelease(&context->last_trusted_server_certs);
context->last_trusted_server_certs = CFRetain(context->server_certs);
break;
case kEAPClientStatusUnknownRootCertificate:
case kEAPClientStatusNoRootCertificate:
case kEAPClientStatusCertificateExpired:
case kEAPClientStatusCertificateNotYetValid:
case kEAPClientStatusServerCertificateNotTrusted:
case kEAPClientStatusCertificateRequiresConfirmation:
*client_status = context->last_client_status
= kEAPClientStatusUserInputRequired;
break;
default:
*client_status = context->trust_status;
context->last_ssl_error = context->trust_ssl_error;
context->plugin_state = kEAPClientStateFailure;
SSLClose(context->ssl_context);
pkt = EAPTLSPacketCreate(kEAPCodeResponse,
kEAPTypeTLS,
identifier,
context->mtu,
write_buf,
&context->last_write_size);
break;
}
return (pkt);
}
static EAPPacketRef
eaptls_pending(EAPClientPluginDataRef plugin,
int identifier, EAPClientStatus * client_status)
{
EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
EAPPacketRef pkt = NULL;
memoryBufferRef read_buf = &context->read_buffer;
memoryBufferRef write_buf = &context->write_buffer;
if (context->trust_proceed == FALSE) {
if (identifier == context->previous_identifier) {
memoryBufferClear(read_buf);
}
pkt = eaptls_verify_server(plugin, identifier, client_status);
if (context->trust_proceed == FALSE) {
return (pkt);
}
}
*client_status = kEAPClientStatusOK;
if (pkt == NULL) {
pkt = EAPTLSPacketCreate(kEAPCodeResponse,
kEAPTypeTLS,
identifier,
context->mtu,
write_buf,
&context->last_write_size);
}
return (pkt);
}
static void
eaptls_set_session_was_resumed(EAPTLSPluginDataRef context)
{
char buf[MAX_SESSION_ID_LENGTH];
size_t buf_len = sizeof(buf);
Boolean resumed = FALSE;
OSStatus status;
status = SSLGetResumableSessionInfo(context->ssl_context,
&resumed, buf, &buf_len);
if (status == noErr) {
context->session_was_resumed = resumed;
}
return;
}
static EAPPacketRef
eaptls_handshake(EAPClientPluginDataRef plugin,
int identifier, EAPClientStatus * client_status)
{
EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
EAPPacketRef eaptls_out = NULL;
memoryBufferRef read_buf = &context->read_buffer;
OSStatus status = noErr;
memoryBufferRef write_buf = &context->write_buffer;
read_buf->offset = 0;
status = SSLHandshake(context->ssl_context);
switch (status) {
case noErr:
context->handshake_complete = TRUE;
eaptls_compute_session_key(context);
eaptls_set_session_was_resumed(context);
my_CFRelease(&context->server_certs);
(void)EAPSSLCopyPeerCertificates(context->ssl_context,
&context->server_certs);
eaptls_out = eaptls_verify_server(plugin, identifier, client_status);
if (context->trust_proceed == FALSE) {
break;
}
eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
kEAPTypeTLS,
identifier,
context->mtu,
write_buf,
&context->last_write_size);
break;
default:
syslog(LOG_NOTICE, "eaptls_handshake: SSLHandshake failed, %s",
EAPSSLErrorString(status));
context->last_ssl_error = status;
my_CFRelease(&context->server_certs);
(void)EAPSSLCopyPeerCertificates(context->ssl_context,
&context->server_certs);
context->plugin_state = kEAPClientStateFailure;
SSLClose(context->ssl_context);
case errSSLWouldBlock:
if (write_buf->data == NULL) {
if (status == errSSLFatalAlert) {
eaptls_out
= EAPTLSPacketCreateAck(identifier);
}
}
else {
eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
kEAPTypeTLS,
identifier,
context->mtu,
write_buf,
&context->last_write_size);
}
break;
}
return (eaptls_out);
}
static EAPPacketRef
eaptls_request(EAPClientPluginDataRef plugin,
SSLSessionState ssl_state,
const EAPPacketRef in_pkt,
EAPClientStatus * client_status)
{
EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
EAPTLSPacket * eaptls_in = (EAPTLSPacket *)in_pkt;
EAPTLSLengthIncludedPacket * eaptls_in_l;
EAPPacketRef eaptls_out = NULL;
int in_data_length;
void * in_data_ptr = NULL;
u_int16_t in_length = EAPPacketGetLength(in_pkt);
memoryBufferRef write_buf = &context->write_buffer;
memoryBufferRef read_buf = &context->read_buffer;
OSStatus status = noErr;
u_int32_t tls_message_length = 0;
RequestType type;
eaptls_in_l = (EAPTLSLengthIncludedPacket *)in_pkt;
if (in_length < sizeof(*eaptls_in)) {
syslog(LOG_NOTICE, "eaptls_request: length %d < %d",
in_length, sizeof(*eaptls_in));
goto done;
}
in_data_ptr = eaptls_in->tls_data;
tls_message_length = in_data_length = in_length - sizeof(EAPTLSPacket);
type = kRequestTypeData;
if ((eaptls_in->flags & kEAPTLSPacketFlagsStart) != 0) {
type = kRequestTypeStart;
switch (ssl_state) {
case kSSLConnected:
case kSSLClosed:
case kSSLAborted:
case kSSLHandshake:
status = eaptls_start(plugin);
if (status != noErr) {
context->last_ssl_error = status;
context->plugin_state = kEAPClientStateFailure;
goto done;
}
ssl_state = kSSLIdle;
break;
default:
case kSSLIdle:
break;
}
}
else if (in_length == sizeof(*eaptls_in)) {
type = kRequestTypeAck;
}
else if ((eaptls_in->flags & kEAPTLSPacketFlagsLengthIncluded) != 0) {
if (in_length < sizeof(EAPTLSLengthIncludedPacket)) {
syslog(LOG_NOTICE,
"eaptls_request: packet too short %d < %d",
in_length, sizeof(EAPTLSLengthIncludedPacket));
goto done;
}
in_data_ptr = eaptls_in_l->tls_data;
in_data_length = in_length - sizeof(EAPTLSLengthIncludedPacket);
tls_message_length
= ntohl(*((u_int32_t *)eaptls_in_l->tls_message_length));
if (tls_message_length > kAvoidDenialOfServiceSize) {
syslog(LOG_NOTICE,
"eaptls_request: received message too large, %ld > %d",
tls_message_length, kAvoidDenialOfServiceSize);
context->plugin_state = kEAPClientStateFailure;
goto done;
}
if (tls_message_length == 0) {
type = kRequestTypeAck;
}
}
switch (ssl_state) {
case kSSLClosed:
case kSSLAborted:
break;
case kSSLIdle:
if (type != kRequestTypeStart) {
syslog(LOG_NOTICE,
"eaptls_request: ignoring non EAP/TLS start frame");
goto done;
}
status = SSLHandshake(context->ssl_context);
if (status != errSSLWouldBlock) {
syslog(LOG_NOTICE,
"eaptls_request: SSLHandshake failed, %s (%d)",
EAPSSLErrorString(status), status);
context->last_ssl_error = status;
context->plugin_state = kEAPClientStateFailure;
goto done;
}
eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
EAPTLS_EAP_TYPE,
eaptls_in->identifier,
context->mtu,
write_buf,
&context->last_write_size);
break;
case kSSLHandshake:
case kSSLConnected:
if (write_buf->data != NULL) {
if (in_pkt->identifier == context->previous_identifier) {
eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
EAPTLS_EAP_TYPE,
in_pkt->identifier,
context->mtu,
write_buf,
&context->last_write_size);
break;
}
if ((write_buf->offset + context->last_write_size)
< write_buf->length) {
write_buf->offset += context->last_write_size;
eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
EAPTLS_EAP_TYPE,
in_pkt->identifier,
context->mtu,
write_buf,
&context->last_write_size);
break;
}
memoryBufferClear(write_buf);
context->last_write_size = 0;
}
if (type != kRequestTypeData) {
syslog(LOG_NOTICE, "eaptls_request: unexpected %s frame",
type == kRequestTypeAck ? "Ack" : "Start");
goto done;
}
if (read_buf->data == NULL) {
read_buf->data = malloc(tls_message_length);
read_buf->length = tls_message_length;
read_buf->offset = 0;
}
else if (in_pkt->identifier == context->previous_identifier) {
if ((eaptls_in->flags & kEAPTLSPacketFlagsMoreFragments) == 0) {
syslog(LOG_NOTICE, "eaptls_request: re-sent packet does not"
" have more fragments bit set, ignoring");
goto done;
}
eaptls_out = EAPTLSPacketCreateAck(eaptls_in->identifier);
break;
}
if ((read_buf->offset + in_data_length) > read_buf->length) {
syslog(LOG_NOTICE,
"eaptls_request: fragment too large %ld + %d > %ld",
read_buf->offset, in_data_length, read_buf->length);
goto done;
}
if ((read_buf->offset + in_data_length) < read_buf->length
&& (eaptls_in->flags & kEAPTLSPacketFlagsMoreFragments) == 0) {
syslog(LOG_NOTICE,
"eaptls_request: expecting more data but "
"more fragments bit is not set, ignoring");
goto done;
}
bcopy(in_data_ptr,
read_buf->data + read_buf->offset, in_data_length);
read_buf->offset += in_data_length;
if (read_buf->offset < read_buf->length) {
eaptls_out = EAPTLSPacketCreateAck(eaptls_in->identifier);
break;
}
if (context->handshake_complete) {
eaptls_out = eaptls_pending(plugin, eaptls_in->identifier,
client_status);
}
else {
eaptls_out = eaptls_handshake(plugin, eaptls_in->identifier,
client_status);
}
break;
default:
break;
}
context->previous_identifier = in_pkt->identifier;
done:
return (eaptls_out);
}
static EAPClientState
eaptls_process(EAPClientPluginDataRef plugin,
const EAPPacketRef in_pkt,
EAPPacketRef * out_pkt_p,
EAPClientStatus * client_status,
EAPClientDomainSpecificError * error)
{
EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
SSLSessionState ssl_state = kSSLIdle;
OSStatus status = noErr;
*client_status = kEAPClientStatusOK;
*error = 0;
status = SSLGetSessionState(context->ssl_context, &ssl_state);
if (status != noErr) {
syslog(LOG_NOTICE, "eaptls_process: SSLGetSessionState failed, %s",
EAPSSLErrorString(status));
context->plugin_state = kEAPClientStateFailure;
goto done;
}
*out_pkt_p = NULL;
switch (in_pkt->code) {
case kEAPCodeRequest:
*out_pkt_p = eaptls_request(plugin, ssl_state, in_pkt, client_status);
break;
case kEAPCodeSuccess:
if (context->trust_proceed) {
context->plugin_state = kEAPClientStateSuccess;
}
else if (context->handshake_complete) {
*out_pkt_p
= eaptls_verify_server(plugin, in_pkt->identifier,
client_status);
if (context->trust_proceed) {
context->plugin_state = kEAPClientStateSuccess;
}
}
break;
case kEAPCodeFailure:
context->plugin_state = kEAPClientStateFailure;
break;
case kEAPCodeResponse:
default:
break;
}
done:
if (context->plugin_state == kEAPClientStateFailure) {
if (context->last_ssl_error == noErr) {
if (context->last_client_status == kEAPClientStatusOK) {
*client_status = kEAPClientStatusFailed;
}
else {
*client_status = context->last_client_status;
}
}
else {
*error = context->last_ssl_error;
*client_status = kEAPClientStatusSecurityError;
}
}
return (context->plugin_state);
}
static const char *
eaptls_failure_string(EAPClientPluginDataRef plugin)
{
return (NULL);
}
static void *
eaptls_session_key(EAPClientPluginDataRef plugin, int * key_length)
{
EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
*key_length = 0;
if (context->key_data_valid == FALSE) {
return (NULL);
}
*key_length = 32;
return (context->key_data);
}
static void *
eaptls_server_key(EAPClientPluginDataRef plugin, int * key_length)
{
EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
*key_length = 0;
if (context->key_data_valid == FALSE) {
return (NULL);
}
*key_length = 32;
return (context->key_data + 32);
}
static CFArrayRef
eaptls_require_props(EAPClientPluginDataRef plugin)
{
CFArrayRef array = NULL;
EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
if (context->last_client_status != kEAPClientStatusUserInputRequired) {
goto done;
}
if (context->trust_proceed == FALSE) {
CFStringRef str = kEAPClientPropTLSUserTrustProceed;
array = CFArrayCreate(NULL, (const void **)&str,
1, &kCFTypeArrayCallBacks);
}
done:
return (array);
}
static CFDictionaryRef
eaptls_publish_props(EAPClientPluginDataRef plugin)
{
CFArrayRef cert_list;
EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
CFMutableDictionaryRef dict;
if (context->server_certs == NULL) {
return (NULL);
}
cert_list = EAPSecCertificateArrayCreateCFDataArray(context->server_certs);
if (cert_list == NULL) {
return (NULL);
}
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(dict, kEAPClientPropTLSServerCertificateChain,
cert_list);
my_CFRelease(&cert_list);
CFDictionarySetValue(dict, kEAPClientPropTLSSessionWasResumed,
context->session_was_resumed
? kCFBooleanTrue
: kCFBooleanFalse);
if (context->last_client_status == kEAPClientStatusUserInputRequired
&& context->trust_proceed == FALSE) {
CFNumberRef num;
num = CFNumberCreate(NULL, kCFNumberSInt32Type,
&context->trust_status);
CFDictionarySetValue(dict, kEAPClientPropTLSTrustClientStatus, num);
num = CFNumberCreate(NULL, kCFNumberSInt32Type,
&context->trust_proceed_id);
CFDictionarySetValue(dict, kEAPClientPropTLSUserTrustProceed, num);
}
return (dict);
}
static bool
eaptls_packet_dump(FILE * out_f, const EAPPacketRef pkt)
{
EAPTLSPacket * eaptls_pkt = (EAPTLSPacket *)pkt;
EAPTLSLengthIncludedPacket * eaptls_pkt_l;
int data_length;
void * data_ptr = NULL;
u_int16_t length = EAPPacketGetLength(pkt);
u_int32_t tls_message_length = 0;
switch (pkt->code) {
case kEAPCodeRequest:
case kEAPCodeResponse:
break;
default:
return (FALSE);
break;
}
if (length < sizeof(*eaptls_pkt)) {
fprintf(out_f, "invalid packet: length %d < min length %ld",
length, sizeof(*eaptls_pkt));
goto done;
}
fprintf(out_f, "EAP/TLS %s: Identifier %d Length %d Flags 0x%x%s",
pkt->code == kEAPCodeRequest ? "Request" : "Response",
pkt->identifier, length, eaptls_pkt->flags,
eaptls_pkt->flags != 0 ? " [" : "");
eaptls_pkt_l = (EAPTLSLengthIncludedPacket *)pkt;
data_ptr = eaptls_pkt->tls_data;
tls_message_length = data_length = length - sizeof(EAPTLSPacket);
if ((eaptls_pkt->flags & kEAPTLSPacketFlagsStart) != 0) {
fprintf(out_f, " start");
}
if ((eaptls_pkt->flags & kEAPTLSPacketFlagsLengthIncluded) != 0) {
if (length < sizeof(EAPTLSLengthIncludedPacket)) {
fprintf(out_f, "\ninvalid packet: length %d < %lu",
length, sizeof(EAPTLSLengthIncludedPacket));
goto done;
}
data_ptr = eaptls_pkt_l->tls_data;
data_length = length - sizeof(EAPTLSLengthIncludedPacket);
tls_message_length
= ntohl(*((u_int32_t *)eaptls_pkt_l->tls_message_length));
fprintf(out_f, " length=%u", tls_message_length);
}
if ((eaptls_pkt->flags & kEAPTLSPacketFlagsMoreFragments) != 0) {
fprintf(out_f, " more");
}
fprintf(out_f, "%s Data Length %d\n", eaptls_pkt->flags != 0 ? " ]" : "",
data_length);
if (tls_message_length > kAvoidDenialOfServiceSize) {
fprintf(out_f, "rejecting packet to avoid DOS attack %u > %d\n",
tls_message_length, kAvoidDenialOfServiceSize);
goto done;
}
fprint_data(out_f, data_ptr, data_length);
done:
return (1);
}
static EAPType
eaptls_type()
{
return (EAPTLS_EAP_TYPE);
}
static const char *
eaptls_name()
{
return ("TLS");
}
static EAPClientPluginVersion
eaptls_version()
{
return (kEAPClientPluginVersion);
}
static CFStringRef
eaptls_user_name(CFDictionaryRef properties)
{
SecCertificateRef cert = NULL;
EAPSecIdentityHandleRef id_handle = NULL;
SecIdentityRef identity = NULL;
OSStatus status;
CFStringRef user_name = NULL;
if (properties != NULL) {
id_handle = CFDictionaryGetValue(properties,
kEAPClientPropTLSIdentityHandle);
}
status = EAPSecIdentityHandleCreateSecIdentity(id_handle, &identity);
if (status != noErr) {
goto done;
}
status = SecIdentityCopyCertificate(identity, &cert);
if (status != noErr) {
goto done;
}
user_name = EAPSecCertificateCopyUserNameString(cert);
done:
my_CFRelease(&cert);
my_CFRelease(&identity);
return (user_name);
}
static struct func_table_ent {
const char * name;
void * func;
} func_table[] = {
#if 0
{ kEAPClientPluginFuncNameIntrospect, eaptls_introspect },
#endif 0
{ kEAPClientPluginFuncNameVersion, eaptls_version },
{ kEAPClientPluginFuncNameEAPType, eaptls_type },
{ kEAPClientPluginFuncNameEAPName, eaptls_name },
{ kEAPClientPluginFuncNameInit, eaptls_init },
{ kEAPClientPluginFuncNameFree, eaptls_free },
{ kEAPClientPluginFuncNameProcess, eaptls_process },
{ kEAPClientPluginFuncNameFreePacket, eaptls_free_packet },
{ kEAPClientPluginFuncNameFailureString, eaptls_failure_string },
{ kEAPClientPluginFuncNameSessionKey, eaptls_session_key },
{ kEAPClientPluginFuncNameServerKey, eaptls_server_key },
{ kEAPClientPluginFuncNameRequireProperties, eaptls_require_props },
{ kEAPClientPluginFuncNamePublishProperties, eaptls_publish_props },
{ kEAPClientPluginFuncNamePacketDump, eaptls_packet_dump },
{ kEAPClientPluginFuncNameUserName, eaptls_user_name },
{ NULL, NULL},
};
EAPClientPluginFuncRef
eaptls_introspect(EAPClientPluginFuncName name)
{
struct func_table_ent * scan;
for (scan = func_table; scan->name != NULL; scan++) {
if (strcmp(name, scan->name) == 0) {
return (scan->func);
}
}
return (NULL);
}