/* * EAPTLS_PACKET * code = EAP-code * id = EAP-id * length = code + id + length + flags + tlsdata * = 1 + 1 + 2 + 1 + X * length = EAP-length - 1(EAP-Type = 1 octet) * flags = EAP-typedata[0] (1 octet) * dlen = EAP-typedata[1-4] (4 octets), if L flag set * = length - 5(code+id+length+flags), otherwise * data = EAP-typedata[5-n], if L flag set * = EAP-typedata[1-n], otherwise * packet = EAP-typedata (complete typedata) * * Points to consider during EAP-TLS data extraction * 1. In the received packet, No data will be present incase of ACK-NAK * 2. Incase if more fragments need to be received then ACK after retreiving this fragment. * * RFC 2716 Section 4.2. PPP EAP TLS Request Packet * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Code | Identifier | Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | Flags | TLS Message Length * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | TLS Message Length | TLS Data... * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * The Length field is two octets and indicates the length of the EAP * packet including the Code, Identifir, Length, Type, and TLS data * fields. */ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_status_t status) { EAPTLS_PACKET *tlspacket; uint32_t data_len = 0; uint32_t len = 0; uint8_t *data = NULL; if (status == FR_TLS_INVALID) return NULL; /* * The main EAP code & eaptls_verify() take care of * ensuring that the packet is OK, and that we can * extract the various fields we want. * * e.g. a TLS packet with zero data is allowed as an ACK, * but we will never see it here, as we will simply * send another fragment, instead of trying to extract * the data. * * MUST have TLS type octet, followed by flags, followed * by data. */ assert(eap_ds->response->length > 2); tlspacket = talloc(eap_ds, EAPTLS_PACKET); if (!tlspacket) return NULL; /* * Code & id for EAPTLS & EAP are same * but eaptls_length = eap_length - 1(EAP-Type = 1 octet) * * length = code + id + length + type + tlsdata * = 1 + 1 + 2 + 1 + X */ tlspacket->code = eap_ds->response->code; tlspacket->id = eap_ds->response->id; tlspacket->length = eap_ds->response->length - 1; /* EAP type */ tlspacket->flags = eap_ds->response->type.data[0]; /* * A quick sanity check of the flags. If we've been told * that there's a length, and there isn't one, then stop. */ if (TLS_LENGTH_INCLUDED(tlspacket->flags) && (tlspacket->length < 5)) { /* flags + TLS message length */ REDEBUG("Invalid EAP-TLS packet received: Length bit is set, " "but packet too short to contain length field"); talloc_free(tlspacket); return NULL; } /* * If the final TLS packet is larger than we can handle, die * now. * * Likewise, if the EAP packet says N bytes, and the TLS * packet says there's fewer bytes, it's a problem. */ if (TLS_LENGTH_INCLUDED(tlspacket->flags)) { memcpy(&data_len, &eap_ds->response->type.data[1], 4); data_len = ntohl(data_len); if (data_len > MAX_RECORD_SIZE) { REDEBUG("Reassembled TLS record will be %u bytes, " "greater than our maximum record size (" STRINGIFY(MAX_RECORD_SIZE) " bytes)", data_len); talloc_free(tlspacket); return NULL; } } switch (status) { /* * The TLS Message Length field is four octets, and * provides the total length of the TLS message or set of * messages that is being fragmented; this simplifies * buffer allocation. * * Dynamic allocation of buffers as & when we know the * length should solve the problem. */ case FR_TLS_FIRST_FRAGMENT: case FR_TLS_LENGTH_INCLUDED: if (tlspacket->length < 5) { /* flags + TLS message length */ REDEBUG("Invalid EAP-TLS packet received: Expected length, got none"); talloc_free(tlspacket); return NULL; } /* * Extract all the TLS fragments from the * previous eap_ds Start appending this * fragment to the above ds */ memcpy(&data_len, &eap_ds->response->type.data[1], sizeof(uint32_t)); data_len = ntohl(data_len); data = (eap_ds->response->type.data + 5/*flags+TLS-Length*/); len = eap_ds->response->type.length - 5/*flags+TLS-Length*/; /* * Hmm... this should be an error, too. */ if (data_len > len) { data_len = len; } break; /* * Data length is implicit, from the EAP header. */ case FR_TLS_MORE_FRAGMENTS: case FR_TLS_OK: data_len = eap_ds->response->type.length - 1/*flags*/; data = eap_ds->response->type.data + 1/*flags*/; break; default: REDEBUG("Invalid EAP-TLS packet received"); talloc_free(tlspacket); return NULL; } tlspacket->dlen = data_len; if (data_len) { tlspacket->data = talloc_array(tlspacket, uint8_t, data_len); if (!tlspacket->data) { talloc_free(tlspacket); return NULL; } memcpy(tlspacket->data, data, data_len); } return tlspacket; }
/* * The S flag is set only within the EAP-TLS start message sent * from the EAP server to the peer. * * Similarly, when the EAP server receives an EAP-Response with * the M bit set, it MUST respond with an EAP-Request with * EAP-Type=EAP-TLS and no data. This serves as a fragment * ACK. The EAP peer MUST wait. */ static fr_tls_status_t eaptls_verify(eap_handler_t *handler) { EAP_DS *eap_ds = handler->eap_ds; EAP_DS *prev_eap_ds = handler->prev_eapds; eaptls_packet_t *eaptls_packet, *eaptls_prev = NULL; REQUEST *request = handler->request; /* * We don't check ANY of the input parameters. It's all * code which works together, so if something is wrong, * we SHOULD core dump. * * e.g. if eap_ds is NULL, of if eap_ds->response is * NULL, of if it's NOT an EAP-Response, or if the packet * is too short. See eap_validation()., in ../../eap.c * * Also, eap_method_select() takes care of selecting the * appropriate type, so we don't need to check * eap_ds->response->type.num == PW_EAP_TLS, or anything * else. */ eaptls_packet = (eaptls_packet_t *)eap_ds->response->type.data; if (prev_eap_ds && prev_eap_ds->response) eaptls_prev = (eaptls_packet_t *)prev_eap_ds->response->type.data; /* * check for ACK * * If there's no TLS data, or there's 1 byte of TLS data, * with the flags set to zero, then it's an ACK. * * Find if this is a reply to the previous request sent */ if ((!eaptls_packet) || ((eap_ds->response->length == EAP_HEADER_LEN + 2) && ((eaptls_packet->flags & 0xc0) == 0x00))) { if (prev_eap_ds && (prev_eap_ds->request->id == eap_ds->response->id)) { /* * Run the ACK handler directly from here. */ RDEBUG2("Received TLS ACK"); return tls_ack_handler(handler->opaque, request); } else { RERROR("Received Invalid TLS ACK"); return FR_TLS_INVALID; } } /* * We send TLS_START, but do not receive it. */ if (TLS_START(eaptls_packet->flags)) { RDEBUG("Received unexpected EAP-TLS Start message"); return FR_TLS_INVALID; } /* * The L bit (length included) is set to indicate the * presence of the four octet TLS Message Length field, * and MUST be set for the first fragment of a fragmented * TLS message or set of messages. * * The M bit (more fragments) is set on all but the last * fragment. * * The S bit (EAP-TLS start) is set in an EAP-TLS Start * message. This differentiates the EAP-TLS Start message * from a fragment acknowledgement. */ if (TLS_LENGTH_INCLUDED(eaptls_packet->flags)) { DEBUG2(" TLS Length %d", eaptls_packet->data[2] * 256 | eaptls_packet->data[3]); if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) { /* * FIRST_FRAGMENT is identified * 1. If there is no previous EAP-response received. * 2. If EAP-response received, then its M bit not set. * (It is because Last fragment will not have M bit set) */ if (!prev_eap_ds || (!prev_eap_ds->response) || (!eaptls_prev) || !TLS_MORE_FRAGMENTS(eaptls_prev->flags)) { RDEBUG2("Received EAP-TLS First Fragment of the message"); return FR_TLS_FIRST_FRAGMENT; } else { RDEBUG2("More Fragments with length included"); return FR_TLS_MORE_FRAGMENTS_WITH_LENGTH; } } else { RDEBUG2("Length Included"); return FR_TLS_LENGTH_INCLUDED; } } if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) { RDEBUG2("More fragments to follow"); return FR_TLS_MORE_FRAGMENTS; } /* * None of the flags are set, but it's still a valid * EAPTLS packet. */ return FR_TLS_OK; }
/* * The S flag is set only within the EAP-TLS start message sent * from the EAP server to the peer. * * Similarly, when the EAP server receives an EAP-Response with * the M bit set, it MUST respond with an EAP-Request with * EAP-Type=EAP-TLS and no data. This serves as a fragment * ACK. The EAP peer MUST wait. */ static fr_tls_status_t eaptls_verify(eap_handler_t *handler) { EAP_DS *eap_ds = handler->eap_ds; tls_session_t *tls_session = handler->opaque; EAP_DS *prev_eap_ds = handler->prev_eap_ds; eaptls_packet_t *eaptls_packet; REQUEST *request = handler->request; size_t frag_len; /* * We don't check ANY of the input parameters. It's all * code which works together, so if something is wrong, * we SHOULD core dump. * * e.g. if eap_ds is NULL, of if eap_ds->response is * NULL, of if it's NOT an EAP-Response, or if the packet * is too short. See eap_validation()., in ../../eap.c */ eaptls_packet = (eaptls_packet_t *)eap_ds->response->type.data; /* * First output the flags (for debugging) */ RDEBUG3("Peer sent flags %c%c%c", TLS_START(eaptls_packet->flags) ? 'S' : '-', TLS_MORE_FRAGMENTS(eaptls_packet->flags) ? 'M' : '-', TLS_LENGTH_INCLUDED(eaptls_packet->flags) ? 'L' : '-'); /* * check for ACK * * If there's no TLS data, or there's 1 byte of TLS data, * with the flags set to zero, then it's an ACK. * * Find if this is a reply to the previous request sent */ if ((!eaptls_packet) || ((eap_ds->response->length == EAP_HEADER_LEN + 2) && ((eaptls_packet->flags & 0xc0) == 0x00))) { if (prev_eap_ds && (prev_eap_ds->request->id == eap_ds->response->id)) { return tls_ack_handler(handler->opaque, request); } else { REDEBUG("Received Invalid TLS ACK"); return FR_TLS_INVALID; } } /* * We send TLS_START, but do not receive it. */ if (TLS_START(eaptls_packet->flags)) { REDEBUG("Peer sent EAP-TLS Start message (only the server is allowed to do this)"); return FR_TLS_INVALID; } /* * Calculate this fragment's length */ frag_len = eap_ds->response->length - (EAP_HEADER_LEN + (TLS_LENGTH_INCLUDED(eaptls_packet->flags) ? 6 : 2)); /* * The L bit (length included) is set to indicate the * presence of the four octet TLS Message Length field, * and MUST be set for the first fragment of a fragmented * TLS message or set of messages. * * The M bit (more fragments) is set on all but the last * fragment. * * The S bit (EAP-TLS start) is set in an EAP-TLS Start * message. This differentiates the EAP-TLS Start message * from a fragment acknowledgement. */ if (TLS_LENGTH_INCLUDED(eaptls_packet->flags)) { size_t total_len = eaptls_packet->data[2] * 256 | eaptls_packet->data[3]; if (frag_len > total_len) { REDEBUG("TLS fragment length (%zu bytes) greater than TLS record length (%zu bytes)", frag_len, total_len); return FR_TLS_INVALID; } if (tls_session->tls_record_transfer_started) { REDEBUG("TLS Length Included (L) flag set, which indicates a new fragment transfer, " "but previous transfer was not complete"); return FR_TLS_INVALID; } /* * This is the first fragment of a fragmented TLS record transfer. */ RDEBUG2("Peer indicated complete TLS record size will be %zu bytes", total_len); if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) { /* * The supplicant is free to send fragments of wildly varying * lengths, but the vast majority won't. * * In this calculation we take into account the fact that the future * fragments are likely to be 4 bytes larger than the initial one * as they won't contain the length field. */ if (frag_len + 4) { /* check for wrap, else clang scan gets excited */ RDEBUG2("Expecting %i TLS record fragments", (int)((((total_len - frag_len) + ((frag_len + 4) - 1)) / (frag_len + 4)) + 1)); } /* * First fragment. tls_record_transfer_started bool was false, * and we received a length included + more fragments packet. */ RDEBUG2("Got first TLS record fragment (%zu bytes). Peer indicated more fragments " "to follow", frag_len); tls_session->tls_record_in_total_len = total_len; tls_session->tls_record_in_recvd_len = frag_len; tls_session->tls_record_transfer_started = true; return FR_TLS_FIRST_FRAGMENT; } /* * Else this is the complete TLS record. */ if (total_len != frag_len) { REDEBUG("Peer indicated no more fragments, but TLS record length (%zu bytes) " "does not match EAP-TLS data length (%zu bytes)", total_len, frag_len); return FR_TLS_INVALID; } /* * RFC5216 doesn't specify explicitly whether a non-fragmented * packet should include the length or not. * * We support both options for maximum compatibility. */ RDEBUG2("Got complete TLS record, with length (%zu bytes)", frag_len); return FR_TLS_LENGTH_INCLUDED; } if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) { /* * If this is not an ongoing transfer, and we have the M flag * then this record transfer is invalid. */ if (!tls_session->tls_record_transfer_started) { REDEBUG("TLS More (M) flag set, but no fragmented record transfer was in progress"); return FR_TLS_INVALID; } /* * If this is an ongoing transfer, and we have the M flag, * then this is just an additional fragment. */ RDEBUG2("Got additional TLS record fragment (%zu bytes). Peer indicated more fragments to follow", frag_len); tls_session->tls_record_in_recvd_len += frag_len; if (tls_session->tls_record_in_recvd_len > tls_session->tls_record_in_total_len) { REDEBUG("Total received TLS record fragments (%zu bytes), exceeds " "indicated TLS record length (%zu bytes)", tls_session->tls_record_in_recvd_len, tls_session->tls_record_in_total_len); return FR_TLS_INVALID; } return FR_TLS_MORE_FRAGMENTS; } /* * No L flag and no M flag. This is either the final fragment, * or a new transfer that was not started with a L flag, which * RFC5216 hints, may be acceptable. * * If it's an in-progress record transfer, check we now have * the complete record. */ if (tls_session->tls_record_transfer_started) { tls_session->tls_record_transfer_started = false; RDEBUG2("Got final TLS record fragment (%zu bytes)", frag_len); tls_session->tls_record_in_recvd_len += frag_len; if (tls_session->tls_record_in_recvd_len != tls_session->tls_record_in_total_len) { REDEBUG("Total received TLS record fragments (%zu bytes), does not equal indicated " "TLS record length (%zu bytes)", tls_session->tls_record_in_recvd_len, tls_session->tls_record_in_total_len); return FR_TLS_INVALID; } return FR_TLS_OK; } /* * None of the flags are set, it wasn't an in progress transfer, * but it's still a valid EAP-TLS packet. */ RDEBUG2("Got complete TLS record (%zu bytes)", frag_len); return FR_TLS_OK; }
/* * EAPTLS_PACKET * code = EAP-code * id = EAP-id * length = code + id + length + flags + tlsdata * = 1 + 1 + 2 + 1 + X * length = EAP-length - 1(EAP-Type = 1 octet) * flags = EAP-typedata[0] (1 octet) * dlen = EAP-typedata[1-4] (4 octets), if L flag set * = length - 5(code+id+length+flags), otherwise * data = EAP-typedata[5-n], if L flag set * = EAP-typedata[1-n], otherwise * packet = EAP-typedata (complete typedata) * * Points to consider during EAP-TLS data extraction * 1. In the received packet, No data will be present incase of ACK-NAK * 2. Incase if more fragments need to be received then ACK after retreiving this fragment. * * RFC 2716 Section 4.2. PPP EAP TLS Request Packet * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Code | Identifier | Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | Flags | TLS Message Length * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | TLS Message Length | TLS Data... * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * The Length field is two octets and indicates the length of the EAP * packet including the Code, Identifir, Length, Type, and TLS data * fields. */ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, eaptls_status_t status) { EAPTLS_PACKET *tlspacket; uint32_t data_len = 0; uint32_t len = 0; uint8_t *data = NULL; if (status == EAPTLS_INVALID) return NULL; /* * The main EAP code & eaptls_verify() take care of * ensuring that the packet is OK, and that we can * extract the various fields we want. * * e.g. a TLS packet with zero data is allowed as an ACK, * but we will never see it here, as we will simply * send another fragment, instead of trying to extract * the data. * * MUST have TLS type octet, followed by flags, followed * by data. */ assert(eap_ds->response->length > 2); tlspacket = eaptls_alloc(); if (tlspacket == NULL) return NULL; /* * Code & id for EAPTLS & EAP are same * but eaptls_length = eap_length - 1(EAP-Type = 1 octet) * * length = code + id + length + type + tlsdata * = 1 + 1 + 2 + 1 + X */ tlspacket->code = eap_ds->response->code; tlspacket->id = eap_ds->response->id; tlspacket->length = eap_ds->response->length - 1; /* EAP type */ tlspacket->flags = eap_ds->response->type.data[0]; /* * A quick sanity check of the flags. If we've been told * that there's a length, and there isn't one, then stop. */ if (TLS_LENGTH_INCLUDED(tlspacket->flags) && (tlspacket->length < 5)) { /* flags + TLS message length */ RDEBUG("Invalid EAP-TLS packet received. (Length bit is set, but no length was found.)"); eaptls_free(&tlspacket); return NULL; } /* * If the final TLS packet is larger than we can handle, die * now. * * Likewise, if the EAP packet says N bytes, and the TLS * packet says there's fewer bytes, it's a problem. * * FIXME: Try to ensure that the claimed length is * consistent across multiple TLS fragments. */ if (TLS_LENGTH_INCLUDED(tlspacket->flags)) { memcpy(&data_len, &eap_ds->response->type.data[1], 4); data_len = ntohl(data_len); if (data_len > MAX_RECORD_SIZE) { RDEBUG("The EAP-TLS packet will contain more data than we can process."); eaptls_free(&tlspacket); return NULL; } #if 0 DEBUG2(" TLS: %d %d\n", data_len, tlspacket->length); if (data_len < tlspacket->length) { RDEBUG("EAP-TLS packet claims to be smaller than the encapsulating EAP packet."); eaptls_free(&tlspacket); return NULL; } #endif } switch (status) { /* * The TLS Message Length field is four octets, and * provides the total length of the TLS message or set of * messages that is being fragmented; this simplifies * buffer allocation. * * Dynamic allocation of buffers as & when we know the * length should solve the problem. */ case EAPTLS_FIRST_FRAGMENT: case EAPTLS_LENGTH_INCLUDED: case EAPTLS_MORE_FRAGMENTS_WITH_LENGTH: if (tlspacket->length < 5) { /* flags + TLS message length */ RDEBUG("Invalid EAP-TLS packet received. (Expected length, got none.)"); eaptls_free(&tlspacket); return NULL; } /* * Extract all the TLS fragments from the * previous eap_ds Start appending this * fragment to the above ds */ memcpy(&data_len, &eap_ds->response->type.data[1], sizeof(uint32_t)); data_len = ntohl(data_len); data = (eap_ds->response->type.data + 5/*flags+TLS-Length*/); len = eap_ds->response->type.length - 5/*flags+TLS-Length*/; /* * Hmm... this should be an error, too. */ if (data_len > len) { data_len = len; } break; /* * Data length is implicit, from the EAP header. */ case EAPTLS_MORE_FRAGMENTS: case EAPTLS_OK: data_len = eap_ds->response->type.length - 1/*flags*/; data = eap_ds->response->type.data + 1/*flags*/; break; default: RDEBUG("Invalid EAP-TLS packet received"); eaptls_free(&tlspacket); return NULL; } tlspacket->dlen = data_len; if (data_len) { tlspacket->data = (unsigned char *)malloc(data_len); if (tlspacket->data == NULL) { RDEBUG("out of memory"); eaptls_free(&tlspacket); return NULL; } memcpy(tlspacket->data, data, data_len); } return tlspacket; }