X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Frlm_eap%2Flibeap%2Feap_tls.c;h=1a1ad21c89e16eff232ed8f1f1fa9f84f98efcec;hb=94dc4bb60ec649ce899c1d5e32b575d9523a48f2;hp=c7708e92c29b6fdc961b79e2d6f693ed643f8951;hpb=6296f9055cf139e70280187760cb96ea1b5b9b7a;p=freeradius.git diff --git a/src/modules/rlm_eap/libeap/eap_tls.c b/src/modules/rlm_eap/libeap/eap_tls.c index c7708e9..1a1ad21 100644 --- a/src/modules/rlm_eap/libeap/eap_tls.c +++ b/src/modules/rlm_eap/libeap/eap_tls.c @@ -15,10 +15,11 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2001 hereUare Communications, Inc. * Copyright 2003 Alan DeKok + * Copyright 2006 The FreeRADIUS server project */ /* @@ -37,6 +38,11 @@ * */ +#include +RCSID("$Id$") + +#include +#include #include "eap_tls.h" /* @@ -96,32 +102,130 @@ int eaptls_start(EAP_DS *eap_ds, int peap_flag) return 1; } -int eaptls_success(EAP_DS *eap_ds, int peap_flag) +int eaptls_success(EAP_HANDLER *handler, int peap_flag) { EAPTLS_PACKET reply; + VALUE_PAIR *vp, *vps = NULL; + REQUEST *request = handler->request; + tls_session_t *tls_session = handler->opaque; + handler->finished = TRUE; reply.code = EAPTLS_SUCCESS; reply.length = TLS_HEADER_LEN; reply.flags = peap_flag; reply.data = NULL; reply.dlen = 0; - eaptls_compose(eap_ds, &reply); + /* + * If there's no session resumption, delete the entry + * from the cache. This means either it's disabled + * globally for this SSL context, OR we were told to + * disable it for this user. + * + * This also means you can't turn it on just for one + * user. + */ + if ((!tls_session->allow_session_resumption) || + (((vp = pairfind(request->config_items, 1127, 0)) != NULL) && + (vp->vp_integer == 0))) { + SSL_CTX_remove_session(tls_session->ctx, + tls_session->ssl->session); + tls_session->allow_session_resumption = 0; + + /* + * If we're in a resumed session and it's + * not allowed, + */ + if (SSL_session_reused(tls_session->ssl)) { + RDEBUG("FAIL: Forcibly stopping session resumption as it is not allowed."); + return eaptls_fail(handler, peap_flag); + } + + /* + * Else resumption IS allowed, so we store the + * user data in the cache. + */ + } else if (!SSL_session_reused(tls_session->ssl)) { + RDEBUG2("Saving response in the cache"); + + vp = paircopy2(request->reply->vps, PW_USER_NAME, 0); + if (vp) pairadd(&vps, vp); + + vp = paircopy2(request->packet->vps, PW_STRIPPED_USER_NAME, 0); + if (vp) pairadd(&vps, vp); + + vp = paircopy2(request->reply->vps, PW_CACHED_SESSION_POLICY, 0); + if (vp) pairadd(&vps, vp); + + if (vps) { + SSL_SESSION_set_ex_data(tls_session->ssl->session, + eaptls_session_idx, vps); + } else { + RDEBUG2("WARNING: No information to cache: session caching will be disabled for this session."); + SSL_CTX_remove_session(tls_session->ctx, + tls_session->ssl->session); + } + + /* + * Else the session WAS allowed. Copy the cached + * reply. + */ + } else { + + vp = SSL_SESSION_get_ex_data(tls_session->ssl->session, + eaptls_session_idx); + if (!vp) { + RDEBUG("WARNING: No information in cached session!"); + return eaptls_fail(handler, peap_flag); + } else { + RDEBUG("Adding cached attributes to the reply:"); + debug_pair_list(vp); + pairadd(&request->reply->vps, paircopy(vp)); + + /* + * Mark the request as resumed. + */ + vp = pairmake("EAP-Session-Resumed", "1", T_OP_SET); + if (vp) pairadd(&request->packet->vps, vp); + } + } + + /* + * Call compose AFTER checking for cached data. + */ + eaptls_compose(handler->eap_ds, &reply); + + /* + * Automatically generate MPPE keying material. + */ + if (tls_session->prf_label) { + eaptls_gen_mppe_keys(&handler->request->reply->vps, + tls_session->ssl, tls_session->prf_label); + } else { + RDEBUG("WARNING: Not adding MPPE keys because there is no PRF label"); + } return 1; } -int eaptls_fail(EAP_DS *eap_ds, int peap_flag) +int eaptls_fail(EAP_HANDLER *handler, int peap_flag) { EAPTLS_PACKET reply; + tls_session_t *tls_session = handler->opaque; + handler->finished = TRUE; reply.code = EAPTLS_FAIL; reply.length = TLS_HEADER_LEN; reply.flags = peap_flag; reply.data = NULL; reply.dlen = 0; - eaptls_compose(eap_ds, &reply); + /* + * Force the session to NOT be cached. + */ + SSL_CTX_remove_session(tls_session->ctx, tls_session->ssl->session); + + eaptls_compose(handler->eap_ds, &reply); return 1; } @@ -212,44 +316,58 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn) static eaptls_status_t eaptls_ack_handler(EAP_HANDLER *handler) { tls_session_t *tls_session; + REQUEST *request = handler->request; tls_session = (tls_session_t *)handler->opaque; if (tls_session == NULL){ - radlog(L_ERR, "rlm_eap_tls: Unexpected ACK received"); + radlog_request(L_ERR, 0, request, "FAIL: Unexpected ACK received. Could not obtain session information."); return EAPTLS_FAIL; } if (tls_session->info.initialized == 0) { - DEBUG(" rlm_eap_tls: No SSL info available. Waiting for more SSL data."); + RDEBUG("No SSL info available. Waiting for more SSL data."); return EAPTLS_REQUEST; } - if (tls_session->info.origin == 0) { - radlog(L_ERR, "rlm_eap_tls: Unexpected ACK received"); + if ((tls_session->info.content_type == handshake) && + (tls_session->info.origin == 0)) { + radlog_request(L_ERR, 0, request, "FAIL: ACK without earlier message."); return EAPTLS_FAIL; } switch (tls_session->info.content_type) { case alert: - DEBUG2(" rlm_eap_tls: ack alert"); - eaptls_fail(handler->eap_ds, tls_session->peap_flag); + RDEBUG2("ACK alert"); + eaptls_fail(handler, tls_session->peap_flag); return EAPTLS_FAIL; case handshake: - if (tls_session->info.handshake_type == finished) { - DEBUG2(" rlm_eap_tls: ack handshake is finished"); + if ((tls_session->info.handshake_type == finished) && + (tls_session->dirty_out.used == 0)) { + RDEBUG2("ACK handshake is finished"); + + /* + * From now on all the content is + * application data set it here as nobody else + * sets it. + */ + tls_session->info.content_type = application_data; return EAPTLS_SUCCESS; - } + } /* else more data to send */ - DEBUG2(" rlm_eap_tls: ack handshake fragment handler"); + RDEBUG2("ACK handshake fragment handler"); /* Fragmentation handler, send next fragment */ return EAPTLS_REQUEST; + case application_data: + RDEBUG2("ACK handshake fragment handler in application data"); + return EAPTLS_REQUEST; + /* * For the rest of the conditions, switch over * to the default section below. */ default: - DEBUG2(" rlm_eap_tls: ack default"); - radlog(L_ERR, "rlm_eap_tls: Invalid ACK received: %d", + RDEBUG2("ACK default"); + radlog_request(L_ERR, 0, request, "Invalid ACK received: %d", tls_session->info.content_type); return EAPTLS_FAIL; } @@ -299,6 +417,7 @@ static eaptls_status_t eaptls_verify(EAP_HANDLER *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 @@ -330,23 +449,32 @@ static eaptls_status_t eaptls_verify(EAP_HANDLER *handler) ((eap_ds->response->length == EAP_HEADER_LEN + 2) && ((eaptls_packet->flags & 0xc0) == 0x00))) { - if (prev_eap_ds->request->id == eap_ds->response->id) { +#if 0 + /* + * Un-comment this for TLS inside of TTLS/PEAP + */ + RDEBUG2("Received EAP-TLS ACK message"); + return eaptls_ack_handler(handler); +#else + if (prev_eap_ds && + (prev_eap_ds->request->id == eap_ds->response->id)) { /* * Run the ACK handler directly from here. */ - DEBUG2("rlm_eap_tls: Received EAP-TLS ACK message"); + RDEBUG2("Received TLS ACK"); return eaptls_ack_handler(handler); } else { - radlog(L_ERR, "rlm_eap_tls: Received Invalid EAP-TLS ACK message"); + radlog_request(L_ERR, 0, request, "Received Invalid TLS ACK"); return EAPTLS_INVALID; } +#endif } /* * We send TLS_START, but do not receive it. */ if (TLS_START(eaptls_packet->flags)) { - radlog(L_ERR, "rlm_eap_tls: Received unexpected EAP-TLS Start message"); + RDEBUG("Received unexpected EAP-TLS Start message"); return EAPTLS_INVALID; } @@ -364,6 +492,8 @@ static eaptls_status_t eaptls_verify(EAP_HANDLER *handler) * 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 @@ -371,26 +501,26 @@ static eaptls_status_t eaptls_verify(EAP_HANDLER *handler) * 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->response == NULL) || + if (!prev_eap_ds || + (prev_eap_ds->response == NULL) || (eaptls_prev == NULL) || !TLS_MORE_FRAGMENTS(eaptls_prev->flags)) { - DEBUG2("rlm_eap_tls: Received EAP-TLS First Fragment of the message"); + RDEBUG2("Received EAP-TLS First Fragment of the message"); return EAPTLS_FIRST_FRAGMENT; } else { - DEBUG2("rlm_eap_tls: More Fragments with length included"); + RDEBUG2("More Fragments with length included"); return EAPTLS_MORE_FRAGMENTS_WITH_LENGTH; } } else { - - DEBUG2("rlm_eap_tls: Length Included"); + RDEBUG2("Length Included"); return EAPTLS_LENGTH_INCLUDED; } } if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) { - DEBUG2("rlm_eap_tls: More fragments to follow"); + RDEBUG2("More fragments to follow"); return EAPTLS_MORE_FRAGMENTS; } @@ -435,7 +565,7 @@ static eaptls_status_t eaptls_verify(EAP_HANDLER *handler) * packet including the Code, Identifir, Length, Type, and TLS data * fields. */ -static EAPTLS_PACKET *eaptls_extract(EAP_DS *eap_ds, eaptls_status_t status) +static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, eaptls_status_t status) { EAPTLS_PACKET *tlspacket; uint32_t data_len = 0; @@ -458,7 +588,7 @@ static EAPTLS_PACKET *eaptls_extract(EAP_DS *eap_ds, eaptls_status_t status) * MUST have TLS type octet, followed by flags, followed * by data. */ - rad_assert(eap_ds->response->length > 2); + assert(eap_ds->response->length > 2); tlspacket = eaptls_alloc(); if (tlspacket == NULL) return NULL; @@ -481,7 +611,7 @@ static EAPTLS_PACKET *eaptls_extract(EAP_DS *eap_ds, eaptls_status_t status) */ 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.)"); + RDEBUG("Invalid EAP-TLS packet received. (Length bit is set, but no length was found.)"); eaptls_free(&tlspacket); return NULL; } @@ -500,7 +630,7 @@ static EAPTLS_PACKET *eaptls_extract(EAP_DS *eap_ds, eaptls_status_t status) 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."); + RDEBUG("The EAP-TLS packet will contain more data than we can process."); eaptls_free(&tlspacket); return NULL; } @@ -509,7 +639,7 @@ static EAPTLS_PACKET *eaptls_extract(EAP_DS *eap_ds, eaptls_status_t status) 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."); + RDEBUG("EAP-TLS packet claims to be smaller than the encapsulating EAP packet."); eaptls_free(&tlspacket); return NULL; } @@ -530,7 +660,7 @@ static EAPTLS_PACKET *eaptls_extract(EAP_DS *eap_ds, eaptls_status_t status) 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.)"); + RDEBUG("Invalid EAP-TLS packet received. (Expected length, got none.)"); eaptls_free(&tlspacket); return NULL; } @@ -563,7 +693,7 @@ static EAPTLS_PACKET *eaptls_extract(EAP_DS *eap_ds, eaptls_status_t status) break; default: - radlog(L_ERR, "rlm_eap_tls: Invalid EAP-TLS packet received"); + RDEBUG("Invalid EAP-TLS packet received"); eaptls_free(&tlspacket); return NULL; } @@ -572,7 +702,7 @@ static EAPTLS_PACKET *eaptls_extract(EAP_DS *eap_ds, eaptls_status_t status) if (data_len) { tlspacket->data = (unsigned char *)malloc(data_len); if (tlspacket->data == NULL) { - radlog(L_ERR, "rlm_eap_tls: out of memory"); + RDEBUG("out of memory"); eaptls_free(&tlspacket); return NULL; } @@ -606,8 +736,8 @@ static EAPTLS_PACKET *eaptls_extract(EAP_DS *eap_ds, eaptls_status_t status) * 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) +static eaptls_status_t eaptls_operation(eaptls_status_t status, + EAP_HANDLER *handler) { tls_session_t *tls_session; @@ -617,32 +747,61 @@ static void eaptls_operation(EAPTLS_PACKET *eaptls_packet UNUSED, (status == EAPTLS_MORE_FRAGMENTS_WITH_LENGTH) || (status == EAPTLS_FIRST_FRAGMENT)) { /* - * Send the ACK. + * Send the ACK. */ eaptls_send_ack(handler->eap_ds, tls_session->peap_flag); - } else { + return EAPTLS_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)) { + DEBUG2("TLS receive handshake failed during operation"); + eaptls_fail(handler, tls_session->peap_flag); + return EAPTLS_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 EAPTLS_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)) { /* - * 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(tls_session)) { - /* - * FIXME: return success/fail. - * - * TLS proper can decide what to do, then. - */ - eaptls_request(handler->eap_ds, tls_session); - } else { - eaptls_fail(handler->eap_ds, tls_session->peap_flag); - } + * Init is finished. The rest is + * application data. + */ + tls_session->info.content_type = application_data; + return EAPTLS_SUCCESS; } - return; + + /* + * Who knows what happened... + */ + DEBUG2("TLS failed during operation"); + return EAPTLS_FAIL; } @@ -678,14 +837,19 @@ 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; + + rad_assert(request != NULL); - DEBUG2(" rlm_eap_tls: processing TLS"); + RDEBUG2("processing EAP-TLS"); + if (handler->certs) pairadd(&request->packet->vps, + paircopy(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); - DEBUG2(" eaptls_verify returned %d\n", status); + RDEBUG2("eaptls_verify returned %d\n", status); switch (status) { default: @@ -716,7 +880,7 @@ eaptls_status_t eaptls_process(EAP_HANDLER *handler) * data" phase. */ case EAPTLS_OK: - DEBUG2(" rlm_eap_tls: Done initial handshake"); + RDEBUG2("Done initial handshake"); /* * Get the rest of the fragments. @@ -731,7 +895,7 @@ eaptls_status_t eaptls_process(EAP_HANDLER *handler) /* * Extract the TLS packet from the buffer. */ - if ((tlspacket = eaptls_extract(handler->eap_ds, status)) == NULL) + if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL) return EAPTLS_FAIL; /* @@ -747,27 +911,95 @@ eaptls_status_t eaptls_process(EAP_HANDLER *handler) 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"); + 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)) { - eaptls_free(&tlspacket); + 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. */ - eaptls_operation(tlspacket, status, handler); - - eaptls_free(&tlspacket); - return EAPTLS_HANDLED; + return eaptls_operation(status, handler); } @@ -800,7 +1032,7 @@ int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply) */ 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"); + radlog(L_ERR, "out of memory"); return 0; } @@ -832,3 +1064,4 @@ int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply) return 1; } +