/* * Process an EAP request */ fr_tls_status_t eaptls_process(eap_handler_t *handler) { tls_session_t *tls_session = (tls_session_t *) handler->opaque; EAPTLS_PACKET *tlspacket; fr_tls_status_t status; REQUEST *request = handler->request; if (!request) return FR_TLS_FAIL; RDEBUG2("Continuing EAP-TLS"); SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, request); if (handler->certs) fr_pair_add(&request->packet->vps, fr_pair_list_copy(request->packet, handler->certs)); /* * 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); if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) { REDEBUG("[eaptls verify] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); } else { RDEBUG2("[eaptls verify] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); } switch (status) { default: case FR_TLS_INVALID: case FR_TLS_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 FR_TLS_SUCCESS: goto done; /* * Normal TLS request, continue with the "get rest * of fragments" phase. */ case FR_TLS_REQUEST: eaptls_request(handler->eap_ds, tls_session); status = FR_TLS_HANDLED; goto done; /* * The handshake is done, and we're in the "tunnel * data" phase. */ case FR_TLS_OK: RDEBUG2("Done initial handshake"); /* * Get the rest of the fragments. */ case FR_TLS_FIRST_FRAGMENT: case FR_TLS_MORE_FRAGMENTS: case FR_TLS_LENGTH_INCLUDED: break; } /* * Extract the TLS packet from the buffer. */ if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL) { status = FR_TLS_FAIL; goto done; } /* * 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)) { talloc_free(tlspacket); REDEBUG("Exceeded maximum record size"); status = FR_TLS_FAIL; goto done; } /* * No longer needed. */ talloc_free(tlspacket); /* * SSL initalization is done. Return. * * The TLS data will be in the tls_session structure. */ if (SSL_is_init_finished(tls_session->ssl)) { /* * The initialization may be finished, but if * there more fragments coming, then send ACK, * and get the caller to continue the * conversation. */ if ((status == FR_TLS_MORE_FRAGMENTS) || (status == FR_TLS_FIRST_FRAGMENT)) { /* * Send the ACK. */ eaptls_send_ack(handler, tls_session->peap_flag); RDEBUG2("Init is done, but tunneled data is fragmented"); status = FR_TLS_HANDLED; goto done; } status = tls_application_data(tls_session, request); goto done; } /* * Continue the handshake. */ status = eaptls_operation(status, handler); if (status == FR_TLS_SUCCESS) { #define MAX_SESSION_SIZE (256) size_t size; VALUE_PAIR *vps; char buffer[2 * MAX_SESSION_SIZE + 1]; /* * Restore the cached VPs before processing the * application data. */ size = tls_session->ssl->session->session_id_length; if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE; fr_bin2hex(buffer, tls_session->ssl->session->session_id, size); vps = SSL_SESSION_get_ex_data(tls_session->ssl->session, fr_tls_ex_index_vps); if (!vps) { RWDEBUG("No information in cached session %s", buffer); } else { vp_cursor_t cursor; VALUE_PAIR *vp; RDEBUG("Adding cached attributes from session %s", buffer); /* * The cbtls_get_session() function doesn't have * access to sock->certs or handler->certs, which * is where the certificates normally live. So * the certs are all in the VPS list here, and * have to be manually extracted. */ RINDENT(); for (vp = fr_cursor_init(&cursor, &vps); vp; vp = fr_cursor_next(&cursor)) { /* * TLS-* attrs get added back to * the request list. */ if ((vp->da->vendor == 0) && (vp->da->attr >= PW_TLS_CERT_SERIAL) && (vp->da->attr <= PW_TLS_CLIENT_CERT_SUBJECT_ALT_NAME_UPN)) { /* * Certs already exist. Don't re-add them. */ if (!handler->certs) { rdebug_pair(L_DBG_LVL_2, request, vp, "request:"); fr_pair_add(&request->packet->vps, fr_pair_copy(request->packet, vp)); } } else { rdebug_pair(L_DBG_LVL_2, request, vp, "reply:"); fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp)); } } REXDENT(); } } done: SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, NULL); return status; }
/* * Do authentication, by letting EAP-TLS do most of the work. */ static int mod_authenticate(void *arg, eap_handler_t *handler) { int rcode; fr_tls_status_t status; rlm_eap_peap_t *inst = (rlm_eap_peap_t *) arg; tls_session_t *tls_session = (tls_session_t *) handler->opaque; peap_tunnel_t *peap = tls_session->opaque; REQUEST *request = handler->request; /* * Session resumption requires the storage of data, so * allocate it if it doesn't already exist. */ if (!tls_session->opaque) { peap = tls_session->opaque = peap_alloc(tls_session, inst); } status = eaptls_process(handler); RDEBUG2("eaptls_process returned %d\n", status); switch (status) { /* * EAP-TLS handshake was successful, tell the * client to keep talking. * * If this was EAP-TLS, we would just return * an EAP-TLS-Success packet here. */ case FR_TLS_SUCCESS: RDEBUG2("FR_TLS_SUCCESS"); peap->status = PEAP_STATUS_TUNNEL_ESTABLISHED; break; /* * The TLS code is still working on the TLS * exchange, and it's a valid TLS request. * do nothing. */ case FR_TLS_HANDLED: /* * FIXME: If the SSL session is established, grab the state * and EAP id from the inner tunnel, and update it with * the expected EAP id! */ RDEBUG2("FR_TLS_HANDLED"); return 1; /* * Handshake is done, proceed with decoding tunneled * data. */ case FR_TLS_OK: RDEBUG2("FR_TLS_OK"); break; /* * Anything else: fail. */ default: RDEBUG2("FR_TLS_OTHERS"); return 0; } /* * Session is established, proceed with decoding * tunneled data. */ RDEBUG2("Session established. Decoding tunneled attributes"); /* * We may need PEAP data associated with the session, so * allocate it here, if it wasn't already alloacted. */ if (!tls_session->opaque) { tls_session->opaque = peap_alloc(tls_session, inst); } /* * Process the PEAP portion of the request. */ rcode = eappeap_process(handler, tls_session); switch (rcode) { case RLM_MODULE_REJECT: eaptls_fail(handler, 0); return 0; case RLM_MODULE_HANDLED: eaptls_request(handler->eap_ds, tls_session); return 1; case RLM_MODULE_OK: /* * Move the saved VP's from the Access-Accept to * our Access-Accept. */ peap = tls_session->opaque; if (peap->soh_reply_vps) { RDEBUG2("Using saved attributes from the SoH reply"); debug_pair_list(peap->soh_reply_vps); pairfilter(handler->request->reply, &handler->request->reply->vps, &peap->soh_reply_vps, 0, 0, TAG_ANY); } if (peap->accept_vps) { RDEBUG2("Using saved attributes from the original Access-Accept"); debug_pair_list(peap->accept_vps); pairfilter(handler->request->reply, &handler->request->reply->vps, &peap->accept_vps, 0, 0, TAG_ANY); } /* * Success: Automatically return MPPE keys. */ return eaptls_success(handler, 0); /* * No response packet, MUST be proxying it. * The main EAP module will take care of discovering * that the request now has a "proxy" packet, and * will proxy it, rather than returning an EAP packet. */ case RLM_MODULE_UPDATED: #ifdef WITH_PROXY rad_assert(handler->request->proxy != NULL); #endif return 1; break; default: break; } eaptls_fail(handler, 0); return 0; }
/* * 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 fr_tls_status_t eaptls_operation(fr_tls_status_t status, eap_handler_t *handler) { REQUEST *request = handler->request; tls_session_t *tls_session = handler->opaque; if ((status == FR_TLS_MORE_FRAGMENTS) || (status == FR_TLS_FIRST_FRAGMENT)) { /* * Send the ACK. */ eaptls_send_ack(handler, tls_session->peap_flag); return FR_TLS_HANDLED; } /* * 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. */ if (!tls_handshake_recv(handler->request, tls_session)) { REDEBUG("TLS receive handshake failed during operation"); tls_fail(tls_session); return FR_TLS_FAIL; } /* * FIXME: return success/fail. * * TLS proper can decide what to do, then. */ if (tls_session->dirty_out.used > 0) { eaptls_request(handler->eap_ds, tls_session); return FR_TLS_HANDLED; } /* * If there is no data to send i.e * dirty_out.used <=0 and if the SSL * handshake is finished, then return a * EPTLS_SUCCESS */ if (SSL_is_init_finished(tls_session->ssl)) { /* * Init is finished. The rest is * application data. */ tls_session->info.content_type = application_data; return FR_TLS_SUCCESS; } /* * Who knows what happened... */ REDEBUG("TLS failed during operation"); return FR_TLS_FAIL; }
/* * Do authentication, by letting EAP-TLS do most of the work. */ static int eapttls_authenticate(void *arg, EAP_HANDLER *handler) { int rcode; fr_tls_status_t status; rlm_eap_ttls_t *inst = (rlm_eap_ttls_t *) arg; tls_session_t *tls_session = (tls_session_t *) handler->opaque; ttls_tunnel_t *t = (ttls_tunnel_t *) tls_session->opaque; REQUEST *request = handler->request; RDEBUG2("Authenticate"); tls_session->length_flag = inst->include_length; /* * Process TLS layer until done. */ status = eaptls_process(handler); RDEBUG2("eaptls_process returned %d\n", status); switch (status) { /* * EAP-TLS handshake was successful, tell the * client to keep talking. * * If this was EAP-TLS, we would just return * an EAP-TLS-Success packet here. */ case FR_TLS_SUCCESS: if (SSL_session_reused(tls_session->ssl)) { RDEBUG("Skipping Phase2 due to session resumption"); goto do_keys; } if (t && t->authenticated) { RDEBUG2("Using saved attributes from the original Access-Accept"); debug_pair_list(t->accept_vps); pairadd(&handler->request->reply->vps, t->accept_vps); t->accept_vps = NULL; do_keys: /* * Success: Automatically return MPPE keys. */ return eaptls_success(handler, 0); } else { eaptls_request(handler->eap_ds, tls_session); } return 1; /* * The TLS code is still working on the TLS * exchange, and it's a valid TLS request. * do nothing. */ case FR_TLS_HANDLED: return 1; /* * Handshake is done, proceed with decoding tunneled * data. */ case FR_TLS_OK: break; /* * Anything else: fail. */ default: return 0; } /* * Session is established, proceed with decoding * tunneled data. */ RDEBUG2("Session established. Proceeding to decode tunneled attributes."); /* * We may need TTLS data associated with the session, so * allocate it here, if it wasn't already alloacted. */ if (!tls_session->opaque) { tls_session->opaque = ttls_alloc(inst); tls_session->free_opaque = ttls_free; } /* * Process the TTLS portion of the request. */ rcode = eapttls_process(handler, tls_session); switch (rcode) { case PW_AUTHENTICATION_REJECT: eaptls_fail(handler, 0); return 0; /* * Access-Challenge, continue tunneled conversation. */ case PW_ACCESS_CHALLENGE: eaptls_request(handler->eap_ds, tls_session); return 1; /* * Success: Automatically return MPPE keys. */ case PW_AUTHENTICATION_ACK: return eaptls_success(handler, 0); /* * No response packet, MUST be proxying it. * The main EAP module will take care of discovering * that the request now has a "proxy" packet, and * will proxy it, rather than returning an EAP packet. */ case PW_STATUS_CLIENT: #ifdef WITH_PROXY rad_assert(handler->request->proxy != NULL); #endif return 1; break; default: break; } /* * Something we don't understand: Reject it. */ eaptls_fail(handler, 0); return 0; }
/* * Process an EAP request */ fr_tls_status_t eaptls_process(eap_handler_t *handler) { tls_session_t *tls_session = (tls_session_t *) handler->opaque; EAPTLS_PACKET *tlspacket; fr_tls_status_t status; REQUEST *request = handler->request; if (!request) return FR_TLS_FAIL; RDEBUG2("processing EAP-TLS"); SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, request); if (handler->certs) pairadd(&request->packet->vps, paircopy(request->packet, handler->certs)); /* 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); RDEBUG2("eaptls_verify returned %d\n", status); switch (status) { default: case FR_TLS_INVALID: case FR_TLS_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 FR_TLS_SUCCESS: goto done; /* * Normal TLS request, continue with the "get rest * of fragments" phase. */ case FR_TLS_REQUEST: eaptls_request(handler->eap_ds, tls_session); status = FR_TLS_HANDLED; goto done; /* * The handshake is done, and we're in the "tunnel * data" phase. */ case FR_TLS_OK: RDEBUG2("Done initial handshake"); /* * Get the rest of the fragments. */ case FR_TLS_FIRST_FRAGMENT: case FR_TLS_MORE_FRAGMENTS: case FR_TLS_LENGTH_INCLUDED: case FR_TLS_MORE_FRAGMENTS_WITH_LENGTH: break; } /* * Extract the TLS packet from the buffer. */ if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL) { status = FR_TLS_FAIL; goto done; } /* * 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)) { talloc_free(tlspacket); RDEBUG("Exceeded maximum record size"); status =FR_TLS_FAIL; goto done; } /* * No longer needed. */ talloc_free(tlspacket); /* * SSL initalization is done. Return. * * The TLS data will be in the tls_session structure. */ if (SSL_is_init_finished(tls_session->ssl)) { /* * The initialization may be finished, but if * there more fragments coming, then send ACK, * and get the caller to continue the * conversation. */ if ((status == FR_TLS_MORE_FRAGMENTS) || (status == FR_TLS_MORE_FRAGMENTS_WITH_LENGTH) || (status == FR_TLS_FIRST_FRAGMENT)) { /* * Send the ACK. */ eaptls_send_ack(handler->eap_ds, tls_session->peap_flag); RDEBUG2("Init is done, but tunneled data is fragmented"); status = FR_TLS_HANDLED; goto done; } status = tls_application_data(tls_session, request); goto done; } /* * Continue the handshake. */ status = eaptls_operation(status, handler); done: SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, NULL); return status; }
/* * 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; REQUEST *request = handler->request; RDEBUG2("processing EAP-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); RDEBUG2("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: RDEBUG2("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(request, 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); RDEBUG("Exceeded maximum record size"); return EAPTLS_FAIL; } /* * No longer needed. */ eaptls_free(&tlspacket); /* * SSL initalization is done. Return. * * The TLS data will be in the tls_session structure. */ if (SSL_is_init_finished(tls_session->ssl)) { int err; /* * The initialization may be finished, but if * there more fragments coming, then send ACK, * and get the caller to continue the * conversation. */ 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); RDEBUG2("Init is done, but tunneled data is fragmented"); return EAPTLS_HANDLED; } /* * Decrypt the complete record. */ BIO_write(tls_session->into_ssl, tls_session->dirty_in.data, tls_session->dirty_in.used); /* * Clear the dirty buffer now that we are done with it * and init the clean_out buffer to store decrypted data */ (tls_session->record_init)(&tls_session->dirty_in); (tls_session->record_init)(&tls_session->clean_out); /* * Read (and decrypt) the tunneled data from the * SSL session, and put it into the decrypted * data buffer. */ err = SSL_read(tls_session->ssl, tls_session->clean_out.data, sizeof(tls_session->clean_out.data)); if (err < 0) { RDEBUG("SSL_read Error"); switch (SSL_get_error(tls_session->ssl, err)) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: RDEBUG("Error in fragmentation logic"); break; default: /* * FIXME: Call int_ssl_check? */ break; } return EAPTLS_FAIL; } if (err == 0) { RDEBUG("WARNING: No data inside of the tunnel."); } /* * Passed all checks, successfully decrypted data */ tls_session->clean_out.used = err; return EAPTLS_OK; } /* * Continue the handshake. */ return eaptls_operation(status, handler); }
/* * Do post-proxy processing, */ static int CC_HINT(nonnull) eapttls_postproxy(eap_handler_t *handler, void *data) { int rcode; tls_session_t *tls_session = (tls_session_t *) data; REQUEST *fake, *request = handler->request; RDEBUG("Passing reply from proxy back into the tunnel"); /* * If there was a fake request associated with the proxied * request, do more processing of it. */ fake = (REQUEST *) request_data_get(handler->request, handler->request->proxy, REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK); /* * Do the callback, if it exists, and if it was a success. */ if (fake && (handler->request->proxy_reply->code == PW_CODE_ACCESS_ACCEPT)) { /* * Terrible hacks. */ rad_assert(!fake->packet); fake->packet = talloc_steal(fake, request->proxy); fake->packet->src_ipaddr = request->packet->src_ipaddr; request->proxy = NULL; rad_assert(!fake->reply); fake->reply = talloc_steal(fake, request->proxy_reply); request->proxy_reply = NULL; if ((rad_debug_lvl > 0) && fr_log_fp) { fprintf(fr_log_fp, "server %s {\n", (!fake->server) ? "" : fake->server); } /* * Perform a post-auth stage for the tunneled * session. */ fake->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; rcode = rad_postauth(fake); RDEBUG2("post-auth returns %d", rcode); if ((rad_debug_lvl > 0) && fr_log_fp) { fprintf(fr_log_fp, "} # server %s\n", (!fake->server) ? "" : fake->server); RDEBUG("Final reply from tunneled session code %d", fake->reply->code); rdebug_pair_list(L_DBG_LVL_1, request, fake->reply->vps, NULL); } /* * Terrible hacks. */ request->proxy = talloc_steal(request, fake->packet); fake->packet = NULL; request->proxy_reply = talloc_steal(request, fake->reply); fake->reply = NULL; /* * And we're done with this request. */ switch (rcode) { case RLM_MODULE_FAIL: talloc_free(fake); eaptls_fail(handler, 0); return 0; default: /* Don't Do Anything */ RDEBUG2("Got reply %d", request->proxy_reply->code); break; } } talloc_free(fake); /* robust if !fake */ /* * Process the reply from the home server. */ rcode = process_reply(handler, tls_session, handler->request, handler->request->proxy_reply); /* * The proxy code uses the reply from the home server as * the basis for the reply to the NAS. We don't want that, * so we toss it, after we've had our way with it. */ fr_pair_list_free(&handler->request->proxy_reply->vps); switch (rcode) { case RLM_MODULE_REJECT: RDEBUG("Reply was rejected"); break; case RLM_MODULE_HANDLED: RDEBUG("Reply was handled"); eaptls_request(handler->eap_ds, tls_session); request->proxy_reply->code = PW_CODE_ACCESS_CHALLENGE; return 1; case RLM_MODULE_OK: RDEBUG("Reply was OK"); /* * Success: Automatically return MPPE keys. */ return eaptls_success(handler, 0); default: RDEBUG("Reply was unknown"); break; } eaptls_fail(handler, 0); return 0; }