static int tls_socket_recv(rad_listen_t *listener) { int doing_init = FALSE; ssize_t rcode; RADIUS_PACKET *packet; REQUEST *request; listen_socket_t *sock = listener->data; fr_tls_status_t status; RADCLIENT *client = sock->client; if (!sock->packet) { sock->packet = rad_alloc(0); if (!sock->packet) return 0; sock->packet->sockfd = listener->fd; sock->packet->src_ipaddr = sock->other_ipaddr; sock->packet->src_port = sock->other_port; sock->packet->dst_ipaddr = sock->my_ipaddr; sock->packet->dst_port = sock->my_port; if (sock->request) sock->request->packet = sock->packet; } /* * Allocate a REQUEST for debugging. */ if (!sock->request) { sock->request = request = request_alloc(); if (!sock->request) { radlog(L_ERR, "Out of memory"); return 0; } rad_assert(request->packet == NULL); rad_assert(sock->packet != NULL); request->packet = sock->packet; request->component = "<core>"; request->component = "<tls-connect>"; /* * Not sure if we should do this on every packet... */ request->reply = rad_alloc(0); if (!request->reply) return 0; request->options = RAD_REQUEST_OPTION_DEBUG2; rad_assert(sock->ssn == NULL); sock->ssn = tls_new_session(listener->tls, sock->request, listener->tls->require_client_cert); if (!sock->ssn) { request_free(&sock->request); sock->packet = NULL; return 0; } SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_REQUEST, (void *)request); SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_CERTS, (void *)&request->packet->vps); doing_init = TRUE; } rad_assert(sock->request != NULL); rad_assert(sock->request->packet != NULL); rad_assert(sock->packet != NULL); rad_assert(sock->ssn != NULL); request = sock->request; RDEBUG3("Reading from socket %d", request->packet->sockfd); PTHREAD_MUTEX_LOCK(&sock->mutex); rcode = read(request->packet->sockfd, sock->ssn->dirty_in.data, sizeof(sock->ssn->dirty_in.data)); if ((rcode < 0) && (errno == ECONNRESET)) { do_close: PTHREAD_MUTEX_UNLOCK(&sock->mutex); tls_socket_close(listener); return 0; } if (rcode < 0) { RDEBUG("Error reading TLS socket: %s", strerror(errno)); goto do_close; } /* * Normal socket close. */ if (rcode == 0) goto do_close; sock->ssn->dirty_in.used = rcode; memset(sock->ssn->dirty_in.data + sock->ssn->dirty_in.used, 0, 16); dump_hex("READ FROM SSL", sock->ssn->dirty_in.data, sock->ssn->dirty_in.used); /* * Catch attempts to use non-SSL. */ if (doing_init && (sock->ssn->dirty_in.data[0] != handshake)) { RDEBUG("Non-TLS data sent to TLS socket: closing"); goto do_close; } /* * Skip ahead to reading application data. */ if (SSL_is_init_finished(sock->ssn->ssl)) goto app; if (!tls_handshake_recv(request, sock->ssn)) { RDEBUG("FAILED in TLS handshake receive"); goto do_close; } if (sock->ssn->dirty_out.used > 0) { tls_socket_write(listener, request); PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } app: /* * FIXME: Run the packet through a virtual server in * order to see if we like the certificate presented by * the client. */ status = tls_application_data(sock->ssn, request); RDEBUG("Application data status %d", status); if (status == FR_TLS_MORE_FRAGMENTS) { PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } if (sock->ssn->clean_out.used == 0) { PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } dump_hex("TUNNELED DATA", sock->ssn->clean_out.data, sock->ssn->clean_out.used); /* * If the packet is a complete RADIUS packet, return it to * the caller. Otherwise... */ if ((sock->ssn->clean_out.used < 20) || (((sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]) != (int) sock->ssn->clean_out.used)) { RDEBUG("Received bad packet: Length %d contents %d", sock->ssn->clean_out.used, (sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]); goto do_close; } packet = sock->packet; packet->data = rad_malloc(sock->ssn->clean_out.used); packet->data_len = sock->ssn->clean_out.used; sock->ssn->record_minus(&sock->ssn->clean_out, packet->data, packet->data_len); packet->vps = NULL; PTHREAD_MUTEX_UNLOCK(&sock->mutex); if (!rad_packet_ok(packet, 0)) { RDEBUG("Received bad packet: %s", fr_strerror()); tls_socket_close(listener); return 0; /* do_close unlocks the mutex */ } /* * Copied from src/lib/radius.c, rad_recv(); */ if (fr_debug_flag) { char host_ipaddr[128]; if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) { RDEBUG("tls_recv: %s packet from host %s port %d, id=%d, length=%d", fr_packet_codes[packet->code], inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), packet->src_port, packet->id, (int) packet->data_len); } else { RDEBUG("tls_recv: Packet from host %s port %d code=%d, id=%d, length=%d", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), packet->src_port, packet->code, packet->id, (int) packet->data_len); } } FR_STATS_INC(auth, total_requests); return 1; }
/* * 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 */ 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; }
static int tls_socket_recv(rad_listen_t *listener) { bool doing_init = false; ssize_t rcode; RADIUS_PACKET *packet; REQUEST *request; listen_socket_t *sock = listener->data; fr_tls_status_t status; RADCLIENT *client = sock->client; if (!sock->packet) { sock->packet = fr_radius_alloc(sock, false); if (!sock->packet) return 0; sock->packet->sockfd = listener->fd; sock->packet->src_ipaddr = sock->other_ipaddr; sock->packet->src_port = sock->other_port; sock->packet->dst_ipaddr = sock->my_ipaddr; sock->packet->dst_port = sock->my_port; if (sock->request) sock->request->packet = talloc_steal(sock->request, sock->packet); } /* * Allocate a REQUEST for debugging, and initialize the TLS session. */ if (!sock->request) { sock->request = request = request_alloc(sock); if (!sock->request) { ERROR("Out of memory"); return 0; } rad_assert(request->packet == NULL); rad_assert(sock->packet != NULL); request->packet = talloc_steal(request, sock->packet); request->component = "<tls-connect>"; request->reply = fr_radius_alloc(request, false); if (!request->reply) return 0; rad_assert(sock->tls_session == NULL); sock->tls_session = tls_session_init_server(sock, listener->tls, sock->request, listener->tls->require_client_cert); if (!sock->tls_session) { TALLOC_FREE(sock->request); sock->packet = NULL; return 0; } SSL_set_ex_data(sock->tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, (void *)request); doing_init = true; } rad_assert(sock->request != NULL); rad_assert(sock->request->packet != NULL); rad_assert(sock->packet != NULL); rad_assert(sock->tls_session != NULL); request = sock->request; RDEBUG3("Reading from socket %d", request->packet->sockfd); PTHREAD_MUTEX_LOCK(&sock->mutex); rcode = read(request->packet->sockfd, sock->tls_session->dirty_in.data, sizeof(sock->tls_session->dirty_in.data)); if ((rcode < 0) && (errno == ECONNRESET)) { do_close: PTHREAD_MUTEX_UNLOCK(&sock->mutex); DEBUG("Closing TLS socket from client port %u", sock->other_port); tls_socket_close(listener); PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } if (rcode < 0) { RDEBUG("Error reading TLS socket: %s", fr_syserror(errno)); goto do_close; } /* * Normal socket close. */ if (rcode == 0) goto do_close; sock->tls_session->dirty_in.used = rcode; dump_hex("READ FROM SSL", sock->tls_session->dirty_in.data, sock->tls_session->dirty_in.used); /* * Catch attempts to use non-SSL. */ if (doing_init && (sock->tls_session->dirty_in.data[0] != handshake)) { RDEBUG("Non-TLS data sent to TLS socket: closing"); goto do_close; } /* * If we need to do more initialization, do that here. */ if (!SSL_is_init_finished(sock->tls_session->ssl)) { if (!tls_handshake_recv(request, sock->tls_session)) { RDEBUG("FAILED in TLS handshake receive"); goto do_close; } /* * More ACK data to send. Do so. */ if (sock->tls_session->dirty_out.used > 0) { tls_socket_write(listener, request); PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } /* * FIXME: Run the request through a virtual * server in order to see if we like the * certificate presented by the client. */ } /* * Try to get application data. */ status = tls_application_data(sock->tls_session, request); RDEBUG("Application data status %d", status); if (status == FR_TLS_RECORD_FRAGMENT_MORE) { PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } if (sock->tls_session->clean_out.used == 0) { PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } /* * We now have a bunch of application data. */ dump_hex("TUNNELED DATA > ", sock->tls_session->clean_out.data, sock->tls_session->clean_out.used); /* * If the packet is a complete RADIUS packet, return it to * the caller. Otherwise... */ if ((sock->tls_session->clean_out.used < 20) || (((sock->tls_session->clean_out.data[2] << 8) | sock->tls_session->clean_out.data[3]) != (int) sock->tls_session->clean_out.used)) { RDEBUG("Received bad packet: Length %zd contents %d", sock->tls_session->clean_out.used, (sock->tls_session->clean_out.data[2] << 8) | sock->tls_session->clean_out.data[3]); goto do_close; } packet = sock->packet; packet->data = talloc_array(packet, uint8_t, sock->tls_session->clean_out.used); packet->data_len = sock->tls_session->clean_out.used; sock->tls_session->record_to_buff(&sock->tls_session->clean_out, packet->data, packet->data_len); packet->vps = NULL; PTHREAD_MUTEX_UNLOCK(&sock->mutex); if (!fr_radius_ok(packet, 0, NULL)) { if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); DEBUG("Closing TLS socket from client"); PTHREAD_MUTEX_LOCK(&sock->mutex); tls_socket_close(listener); PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; /* do_close unlocks the mutex */ } /* * Copied from src/lib/radius.c, fr_radius_recv(); */ if (fr_debug_lvl) { char host_ipaddr[INET6_ADDRSTRLEN]; if (is_radius_code(packet->code)) { RDEBUG("tls_recv: %s packet from host %s port %d, id=%d, length=%d", fr_packet_codes[packet->code], inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), packet->src_port, packet->id, (int) packet->data_len); } else { RDEBUG("tls_recv: Packet from host %s port %d code=%d, id=%d, length=%d", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), packet->src_port, packet->code, packet->id, (int) packet->data_len); } } FR_STATS_INC(auth, total_requests); return 1; }