Commit 56b0429c authored by Robert Dubner's avatar Robert Dubner
Browse files

Start implementing multi-packet TLS logic; got single-packet working again

parent a0756b42
......@@ -755,7 +755,7 @@ static int
encrypt_and_send_response( STATE *state,
uint8_t eap_message_type)
{
// There is a message in state->ciphertext_in that needs to be encrypted,
// There is a message in state->cleartext_out that needs to be encrypted,
// packaged in an EAP-Message, and sent to our peer:
int rc;
......@@ -2346,45 +2346,65 @@ process_eap_message_peap_or_ttls( RADIUS_INFO *radius_info,
return FAIL_SILENTLY;
}
uint8_t flags = *peap++;
int length_bytes;
int length_bytes = 0;
state->tls_record_in_total_len = eap_message_length-6;
state->tls_record_in_recvd_len = 0;
// At this point in the transfer process, we are receiving one or
// more RADIUS packets with encrypted data being carried in their
// outer_eap[] buffers, which are each reassembled from the
// often multiple-per-packet RATV_EAP_Message attributes
if( flags & EAP_TLS_FLAG_LENGTH_INCLUDED)
// We build the composite encrypted data in state->ciphertext_in
if( state->tls_record_in_recvd_len == 0 )
{
length_bytes = 4;
Debug( LDAP_DEBUG_ARGS,
DPREFIX "%s(): Got explicit length\n",
__func__);
state->tls_record_in_total_len = 0;
if( (char *)peap - (char *) eap_message + 4 >= eap_message_length )
// This is the length we've received so far. Because it
// is zero, we know that this is the first chunk of
// encrypted data.
// Either the four-byte length explicitly follows the flag
// byte, or else we imply it from the length of the EAP-Message
if( flags & EAP_TLS_FLAG_LENGTH_INCLUDED)
{
Debug( LDAP_DEBUG_ANY,
DPREFIX "%s(): eap_message doesn't have enough bytes(B)\n",
__func__);
return FAIL_SILENTLY;
length_bytes = 4;
state->tls_record_expected_length = 0;
if( (char *)peap - (char *) eap_message + 4 >= eap_message_length )
{
Debug( LDAP_DEBUG_ANY,
DPREFIX "%s(): eap_message doesn't have enough bytes(B)\n",
__func__);
return FAIL_SILENTLY;
}
// Pick up four-byte big-endian length
state->tls_record_expected_length *= 256;
state->tls_record_expected_length += *peap++;
state->tls_record_expected_length *= 256;
state->tls_record_expected_length += *peap++;
state->tls_record_expected_length *= 256;
state->tls_record_expected_length += *peap++;
state->tls_record_expected_length *= 256;
state->tls_record_expected_length += *peap++;
Debug( LDAP_DEBUG_ARGS,
DPREFIX "%s(): Got explicit expected length of %ld bytes\n",
__func__,
state->tls_record_expected_length);
}
// Pick up four-byte big-endian length
state->tls_record_in_total_len *= 256;
state->tls_record_in_total_len += *peap++;
state->tls_record_in_total_len *= 256;
state->tls_record_in_total_len += *peap++;
state->tls_record_in_total_len *= 256;
state->tls_record_in_total_len += *peap++;
state->tls_record_in_total_len *= 256;
state->tls_record_in_total_len += *peap++;
else
{
// We are inferring the length from the eap_message length
state->tls_record_expected_length = eap_message_length-6;
length_bytes = 0;
Debug( LDAP_DEBUG_ARGS,
DPREFIX "%s(): We are using the implicit expected length of %ld bytes\n",
__func__,
state->tls_record_expected_length);
}
state->ciphertext_in.used = 0;
}
// We've been told to expect state->tls_record_in_total_len bytes.
size_t fragment_length = eap_message_length -6 -length_bytes; //
//
// I am forcing ciphertext_in.used to simply be the length
// of the data we are working with. I have not yet seen a
// case where the authenticating peer (the WiFi Access Point,
// or equivalent) has to send me multiple RADIUS packets
// each with EAP-Message fragments. When we do encounter
// that situation we will have to update this code:
state->ciphertext_in.used = 0;
// We calculate the fragment length from the length of the EAP message:
size_t fragment_length = eap_message_length - 6 - length_bytes; //
// We copy that many encrypted bytes from the EAP-Message payload to the ciphertext buffer
memcpy( state->ciphertext_in.data+state->ciphertext_in.used,
peap,
fragment_length);
......@@ -2394,26 +2414,29 @@ process_eap_message_peap_or_ttls( RADIUS_INFO *radius_info,
state->tls_record_in_recvd_len += fragment_length;
Debug( LDAP_DEBUG_ARGS,
DPREFIX "%s() With this %ld-byte fragment, we've received %ld bytes; we are expecting %ld\n",
DPREFIX "%s() With this %ld-byte fragment, we've received %ld bytes; we are expecting a total of %ld\n",
__func__,
fragment_length,
state->tls_record_in_recvd_len,
state->tls_record_in_total_len);
state->tls_record_expected_length);
if( state->tls_record_in_recvd_len > state->tls_record_in_total_len )
if( state->tls_record_in_recvd_len > state->tls_record_expected_length )
{
Debug( LDAP_DEBUG_ANY,
DPREFIX "%s() We received more bytes (%ld) than we "
"were told we were going to receive (%ld)\n",
__func__,
state->tls_record_in_recvd_len,
state->tls_record_in_total_len);
state->tls_record_expected_length);
return FAIL_SILENTLY;
}
if( state->tls_record_in_recvd_len == state->tls_record_in_total_len )
if( state->tls_record_in_recvd_len == state->tls_record_expected_length )
{
// Reset this counter for next time:
state->tls_record_in_recvd_len = 0;
// We have an entire TLS message. Send it to BIO_write
int err = BIO_write( state->into_ssl,
state->ciphertext_in.data,
......@@ -2573,9 +2596,9 @@ process_access_request( RADIUS_INFO *radius_info,
int we_got_message_authenticator = 0;
RADIUS_ATTRIBUTE *ra = fetch_attribute_from_packet( request,
RATV_Message_Authenticator);
if( ra )
RADIUS_ATTRIBUTE *raMsgAuth = fetch_attribute_from_packet( request,
RATV_Message_Authenticator);
if( raMsgAuth )
{
Debug( LDAP_DEBUG_ARGS,
DPREFIX "%s(): RADIUS packet has a RATV_Message_Authenticator\n",
......@@ -2583,8 +2606,8 @@ process_access_request( RADIUS_INFO *radius_info,
// Per RFC2869, we perform an HMAC-MD5 on the whole packet, but with the
// signature bytes set to zero:
uint8_t save_the_signature[AUTHENTICATOR_LENGTH];
memcpy(save_the_signature, ra->Data, AUTHENTICATOR_LENGTH);
memset(ra->Data, 0, AUTHENTICATOR_LENGTH);
memcpy(save_the_signature, raMsgAuth->Data, AUTHENTICATOR_LENGTH);
memset(raMsgAuth->Data, 0, AUTHENTICATOR_LENGTH);
// Calculate the digest
uint8_t digest[MD5_DIGEST_LENGTH];
......@@ -2595,7 +2618,7 @@ process_access_request( RADIUS_INFO *radius_info,
strlen(shared_secret) );
// Restore the original packet:
memcpy(ra->Data, save_the_signature, AUTHENTICATOR_LENGTH);
memcpy(raMsgAuth->Data, save_the_signature, AUTHENTICATOR_LENGTH);
// Check to see if the calculated and provided digests are the same:
if( memcmp(digest, save_the_signature, AUTHENTICATOR_LENGTH) )
......@@ -2621,9 +2644,10 @@ process_access_request( RADIUS_INFO *radius_info,
}
// Let's check for an EAP-message. (RFC3748)
ra = fetch_attribute_from_packet(request, RATV_EAP_Message);
EAP_MESSAGE *eap_message = get_outer_eap_message(request);
size_t eap_message_length = get_eap_length(eap_message);
if( ra )
if( eap_message )
{
Debug( LDAP_DEBUG_ARGS,
DPREFIX "%s(): The RADIUS packet has an EAP-Message:\n",
......@@ -2643,45 +2667,28 @@ process_access_request( RADIUS_INFO *radius_info,
}
Debug( LDAP_DEBUG_ARGS,
DPREFIX "%s(): Incoming EAP-message of %d bytes.\n",
DPREFIX "%s(): Incoming EAP-message of %ld bytes.\n",
__func__,
ra->DataLength);
eap_message_length);
// Your best first bet for looking at the bytes of an EAP-Message is to
// look at a Wireshark capture. But you can activate the following
// code if you want.
#if 0
#if 1
ber_log_bprint( LDAP_DEBUG_ANY,
LDAP_DEBUG_ANY,
(const char *)ra->Data,
ra->DataLength);
(const char *)eap_message,
eap_message_length);
#endif
// Make sure the EAP-message payload is long enough:
if( ra->DataLength < 5 )
if( eap_message_length < 5 )
{
Debug( LDAP_DEBUG_ANY,
DPREFIX "%s(): RADIUS packet has an EAP-Message that's poorly "
"formed; at %d bytes, it is too short\n",
__func__,
ra->DataLength );
return FAIL_SILENTLY;
}
EAP_MESSAGE *eap_message = (EAP_MESSAGE *)(ra->Data);
// Make sure that the EAP-Message's data length fits inside the
// the RADIUS_ATTRIBUTE:
size_t eap_message_length = get_eap_length(eap_message);
if(eap_message_length > ra->DataLength)
{
Debug( LDAP_DEBUG_ANY,
DPREFIX "%s(): The EAP-Message length is %ld bytes, which is too long"
"to fit in the %d bytes of the RADIUS_ATTRIBUTE\n",
"formed; at %ld bytes, it is too short\n",
__func__,
eap_message_length,
ra->DataLength );
eap_message_length );
return FAIL_SILENTLY;
}
......@@ -2769,9 +2776,9 @@ radiusov_protocol( RADIUS_INFO *radius_info,
RADIUS_PACKET request_;
RADIUS_PACKET *request = &request_;
rc = radiusov_get_packet_from_request( request,
incoming_request,
recv_len);
rc = extract_packet_from_buffer(request,
incoming_request,
recv_len);
if( rc == 0 )
{
debugging_display_of(request);
......
......@@ -98,7 +98,7 @@ typedef struct _STATE
size_t mtu; // Current fragment size
size_t tls_record_in_total_len; // peer value for total length of TLS record
size_t tls_record_expected_length; // peer value for total length of TLS record
size_t tls_record_in_recvd_len; // how much of that we've already read in
char const *keying_material_label;
......
......@@ -18,6 +18,7 @@
#include "radiusov.h"
#include "lber-int.h"
#include "rpacket.h"
#include "reap.h"
const RADIUS_ATTRIBUTE_TYPE radius_attribute_types[] =
{
......@@ -352,9 +353,9 @@ radius_attribute_to_text(RADIUS_ATTRIBUTE *ra, char *buffer, size_t buf_len)
}
int
radiusov_get_packet_from_request( RADIUS_PACKET *radius_packet,
uint8_t *incoming_request,
ssize_t recv_len)
extract_packet_from_buffer( RADIUS_PACKET *radius_packet,
uint8_t *incoming_request,
ssize_t recv_len)
{
DENTER;
// This routine dissects the raw incoming_request (which is recv_len bytes
......@@ -417,6 +418,7 @@ radiusov_get_packet_from_request( RADIUS_PACKET *radius_packet,
// an attribute falls off the end of the radius packet, we silently fail,
// discarding the entire incoming packet.
radius_packet->outer_eap_length = 0;
radius_packet->number_of_attributes = 0;
uint8_t *attributes;
......@@ -455,10 +457,24 @@ radiusov_get_packet_from_request( RADIUS_PACKET *radius_packet,
radius_packet->attributes[radius_packet->number_of_attributes].DataLength = alength - 2;
radius_packet->attributes[radius_packet->number_of_attributes].Data = attributes + 2 ;
if( atype == RATV_EAP_Message )
{
// Special processing for EAP-Message, which can arrive as several
// fragments:
memcpy( radius_packet->outer_eap + radius_packet->outer_eap_length,
radius_packet->attributes[radius_packet->number_of_attributes].Data,
radius_packet->attributes[radius_packet->number_of_attributes].DataLength
);
radius_packet->outer_eap_length +=
radius_packet->attributes[radius_packet->number_of_attributes].DataLength;
radius_packet->outer_eap[2] = radius_packet->outer_eap_length >> 8 ;
radius_packet->outer_eap[3] = radius_packet->outer_eap_length ;
}
// Make an entry in the speed_fetch array, to make it easy
// to find this attribute.
radius_packet->speed_fetch[atype] = radius_packet->number_of_attributes;
radius_packet->number_of_attributes += 1;
}
else
......@@ -484,6 +500,13 @@ fetch_attribute_from_packet(RADIUS_PACKET *radius_packet, uint8_t Type)
return &radius_packet->attributes[index];
}
EAP_MESSAGE *
get_outer_eap_message(RADIUS_PACKET *radius_packet)
{
return(EAP_MESSAGE *)radius_packet->outer_eap;
}
void
add_attribute_to_radius_packet( RADIUS_PACKET *radius_packet,
uint8_t Type,
......
......@@ -16,6 +16,8 @@
#ifndef RPACKET_H
#define RPACKET_H
#include "reap.h"
typedef enum RADIUS_PACKET_DATA_TYPE
{
RDP_NONE,
......@@ -282,6 +284,14 @@ typedef struct _RADIUS_PACKET
RADIUS_ATTRIBUTE attributes[STUPID_NUMBER_OF_ATTRIBUTES];
uint8_t speed_fetch[256]; // This is a map of radius_attribute type to its
// index in attributes[]
// The RADIUS packet EAP message can be longer than the 253-byte payload
// of an RADIUS_ATTRIBUTE. When a message with one or more EAP-Message
// attributes comes in, the reconstructed EAP message is placed here:
// This is the data as received.
int outer_eap_length;
uint8_t outer_eap[MAXIMUM_POSSIBLE_LENGTH_OF_PACKET];
} RADIUS_PACKET;
#define RADIUS_ATTRIBUTE_TYPE_MAX 191
......@@ -306,12 +316,12 @@ void build_packet_postamble( RADIUS_PACKET *response,
RADIUS_PACKET *request,
const char *state_string,
const char *shared_secret);
int radiusov_get_packet_from_request( RADIUS_PACKET *radius_packet,
uint8_t *incoming_request,
ssize_t recv_len);
RADIUS_ATTRIBUTE *fetch_attribute_from_packet( RADIUS_PACKET
*radius_packet,
int extract_packet_from_buffer( RADIUS_PACKET *radius_packet,
uint8_t *incoming_request,
ssize_t recv_len);
RADIUS_ATTRIBUTE *fetch_attribute_from_packet( RADIUS_PACKET *radius_packet,
uint8_t Type);
EAP_MESSAGE *get_outer_eap_message(RADIUS_PACKET *radius_packet);
void add_attribute_to_radius_packet(RADIUS_PACKET *radius_packet,
uint8_t Type,
uint8_t DataLength,
......
......@@ -858,34 +858,32 @@ tls_init_ctx(SSL_CONFIGURATION *conf)
/* Set Info callback */
SSL_CTX_set_info_callback(ctx, cbtls_info);
int verify_mode = SSL_VERIFY_NONE; // This is what we use for client mode
int verify_mode = SSL_VERIFY_NONE;
if( conf->is_server )
// It's time to set the verification mode. See SSL_CTX_set_verify()
switch( conf->verify_mode )
{
// It's time to set the verification mode. See SSL_CTX_set_verify()
switch( conf->verify_mode )
{
case VM_NEVER:
verify_mode = SSL_VERIFY_NONE;
break;
case VM_ALLOW:
verify_mode = SSL_VERIFY_PEER;
break;
case VM_TRY:
verify_mode = SSL_VERIFY_PEER;
break;
case VM_DEMAND:
verify_mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
break;
default:
Debug( LDAP_DEBUG_ANY,
"%s(): conf->verify_mode has an unknown value of %d\n",
__func__,
conf->verify_mode );
return NULL;
break;
}
case VM_NEVER:
verify_mode = SSL_VERIFY_NONE;
break;
case VM_ALLOW:
verify_mode = SSL_VERIFY_PEER;
break;
case VM_TRY:
verify_mode = SSL_VERIFY_PEER;
break;
case VM_DEMAND:
verify_mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
break;
default:
Debug( LDAP_DEBUG_ANY,
"%s(): conf->verify_mode has an unknown value of %d\n",
__func__,
conf->verify_mode );
return NULL;
break;
}
SSL_CTX_set_verify( ctx, verify_mode, verify_callback);
SSL_CTX_set_verify_depth(ctx, conf->verify_depth);
......@@ -1416,7 +1414,6 @@ tls_new_session(SSL_CTX *ctx,
// Create a new TLS session
STATE *state = NULL;
SSL *new_tls = NULL;
// int verify_mode = 0;
Debug( LDAP_DEBUG_TRACE, "=> %s()\n", __func__);
......@@ -1532,7 +1529,7 @@ CONFIG_ELEMENT configuration_elements[] =
MAKE_CONFIG_ELEMENT("private_key_password", CT_STRING, private_key_password, "whatever"),
MAKE_CONFIG_ELEMENT("cipher_list", CT_STRING, cipher_list, ""),
MAKE_CONFIG_ELEMENT("auto_chain", CT_BOOLEAN, auto_chain, "yes"),
MAKE_CONFIG_ELEMENT("tlsverifyclient", CT_VERIFICATION, verify_mode, "never"), // allow|try|demand
MAKE_CONFIG_ELEMENT("verify_mode", CT_VERIFICATION, verify_mode, "never"), // never|allow|try|demand
MAKE_CONFIG_ELEMENT("verify_depth", CT_INTEGER, verify_depth, "100"),
};
#define NUMBER_OF_ELEMENTS (sizeof(configuration_elements)/sizeof(configuration_elements[0]))
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment