📄 eap_tls.c
字号:
* 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(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 */ radlog(L_ERR, "rlm_eap_tls: 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) { radlog(L_ERR, "rlm_eap_tls: 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) { radlog(L_ERR, "rlm_eap_tls: 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 */ radlog(L_ERR, "rlm_eap_tls: 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: radlog(L_ERR, "rlm_eap_tls: 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) { radlog(L_ERR, "rlm_eap_tls: out of memory"); eaptls_free(&tlspacket); return NULL; } memcpy(tlspacket->data, data, data_len); } return tlspacket;}/* * To process the TLS, * INCOMING DATA: * 1. EAP-TLS should get the compelete TLS data from the peer. * 2. Store that data in a data structure with any other required info * 3. Handle that data structure to the TLS module. * 4. TLS module will perform its operations on the data and * handle back to EAP-TLS * * OUTGOING DATA: * 1. EAP-TLS if necessary will fragment it and send it to the * destination. * * During EAP-TLS initialization, TLS Context object will be * initialized and stored. For every new authentication * requests, TLS will open a new session object and that session * object should be maintained even after the session is * completed for session resumption. (Probably later as a feature * as we donot know who maintains these session objects ie, * SSL_CTX (internally) or TLS module(explicitly). If TLS module, * then how to let SSL API know about these sessions.) */static void eaptls_operation(EAPTLS_PACKET *eaptls_packet UNUSED, eaptls_status_t status, EAP_HANDLER *handler){ tls_session_t *tls_session; tls_session = (tls_session_t *)handler->opaque; if ((status == EAPTLS_MORE_FRAGMENTS) || (status == EAPTLS_MORE_FRAGMENTS_WITH_LENGTH) || (status == EAPTLS_FIRST_FRAGMENT)) { /* * Send the ACK. */ eaptls_send_ack(handler->eap_ds, tls_session->peap_flag); } else { int rcode; /* * We have the complete TLS-data or TLS-message. * * Clean the dirty message. * * Authenticate the user and send * Success/Failure. * * If more info * is required then send another request. */ rcode = tls_handshake_recv(tls_session); if (rcode == 1) { /* * FIXME: return success/fail. * * TLS proper can decide what to do, then. */ eaptls_request(handler->eap_ds, tls_session); /* * TLS returns 0 or 1. * anything else is application-specific. * * In our code, this means "session * resumption was OK". */ } else if (rcode == 0xea) { /* * FIXME: hard-code key based on EAP type. * Also, this code is duplicated all over * the place... */ eaptls_success(handler->eap_ds, 0); eaptls_gen_mppe_keys(&handler->request->reply->vps, tls_session->ssl, "client EAP encryption"); } else { eaptls_fail(handler->eap_ds, tls_session->peap_flag); } } return;}/* * In the actual authentication first verify the packet and then create the data structure *//* * To process the TLS, * INCOMING DATA: * 1. EAP-TLS should get the compelete TLS data from the peer. * 2. Store that data in a data structure with any other required info * 3. Hand this data structure to the TLS module. * 4. TLS module will perform its operations on the data and hands back to EAP-TLS * OUTGOING DATA: * 1. EAP-TLS if necessary will fragment it and send it to the destination. * * During EAP-TLS initialization, TLS Context object will be * initialized and stored. For every new authentication * requests, TLS will open a new session object and that * session object SHOULD be maintained even after the session * is completed, for session resumption. (Probably later as a * feature, as we do not know who maintains these session * objects ie, SSL_CTX (internally) or TLS module (explicitly). If * TLS module, then how to let SSL API know about these * sessions.) *//* * Process an EAP request */eaptls_status_t eaptls_process(EAP_HANDLER *handler){ tls_session_t *tls_session = (tls_session_t *) handler->opaque; EAPTLS_PACKET *tlspacket; eaptls_status_t status; DEBUG2(" rlm_eap_tls: processing TLS"); /* This case is when SSL generates Alert then we * send that alert to the client and then send the EAP-Failure */ status = eaptls_verify(handler); DEBUG2(" eaptls_verify returned %d\n", status); switch (status) { default: case EAPTLS_INVALID: case EAPTLS_FAIL: /* * Success means that we're done the initial * handshake. For TTLS, this means send stuff * back to the client, and the client sends us * more tunneled data. */ case EAPTLS_SUCCESS: return status; break; /* * Normal TLS request, continue with the "get rest * of fragments" phase. */ case EAPTLS_REQUEST: eaptls_request(handler->eap_ds, tls_session); return EAPTLS_HANDLED; break; /* * The handshake is done, and we're in the "tunnel * data" phase. */ case EAPTLS_OK: DEBUG2(" rlm_eap_tls: Done initial handshake"); /* * Get the rest of the fragments. */ case EAPTLS_FIRST_FRAGMENT: case EAPTLS_MORE_FRAGMENTS: case EAPTLS_LENGTH_INCLUDED: case EAPTLS_MORE_FRAGMENTS_WITH_LENGTH: break; } /* * Extract the TLS packet from the buffer. */ if ((tlspacket = eaptls_extract(handler->eap_ds, status)) == NULL) return EAPTLS_FAIL; /* * Get the session struct from the handler * * update the dirty_in buffer * * NOTE: This buffer will contain partial data when M bit is set. * * CAUTION while reinitializing this buffer, it should be * reinitialized only when this M bit is NOT set. */ if (tlspacket->dlen != (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) { eaptls_free(&tlspacket); radlog(L_ERR, "rlm_eap_tls: Exceeded maximum record size"); return EAPTLS_FAIL; } /* * SSL initalization is done. Return. * * The TLS data will be in the tls_session structure. */ if (SSL_is_init_finished(tls_session->ssl)) { eaptls_free(&tlspacket); return EAPTLS_OK; } /* * Continue the handshake. */ eaptls_operation(tlspacket, status, handler); eaptls_free(&tlspacket); return EAPTLS_HANDLED;}/* * compose the TLS reply packet in the EAP reply typedata */int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply){ uint8_t *ptr; /* * Don't set eap_ds->request->type.type, as the main EAP * handler will do that for us. This allows the TLS * module to be called from TTLS & PEAP. */ /* * 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 until it receives * the EAP-Request before sending another fragment. * * In order to prevent errors in the processing of * fragments, the EAP server MUST use increment the * Identifier value for each fragment ACK contained * within an EAP-Request, and the peer MUST include this * Identifier value in the subsequent fragment contained * within an EAP- Reponse. */ eap_ds->request->type.data = malloc(reply->length - TLS_HEADER_LEN + 1); if (eap_ds->request->type.data == NULL) { radlog(L_ERR, "rlm_eap_tls: out of memory"); return 0; } /* EAPTLS Header length is excluded while computing EAP typelen */ eap_ds->request->type.length = reply->length - TLS_HEADER_LEN; ptr = eap_ds->request->type.data; *ptr++ = (uint8_t)(reply->flags & 0xFF); if (reply->dlen) memcpy(ptr, reply->data, reply->dlen); switch (reply->code) { case EAPTLS_ACK: case EAPTLS_START: case EAPTLS_REQUEST: eap_ds->request->code = PW_EAP_REQUEST; break; case EAPTLS_SUCCESS: eap_ds->request->code = PW_EAP_SUCCESS; break; case EAPTLS_FAIL: eap_ds->request->code = PW_EAP_FAILURE; break; default: /* Should never enter here */ eap_ds->request->code = PW_EAP_FAILURE; break; } return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -