/* * 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; }
/* * 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; }