tls隧道建立+传输数据

建立tls后copy数据到clean_in 然后写入到ssl加密,从from_ssl 读取加密后的数据然后发送

复制代码
/*
 *    Take cleartext user data, and encrypt it into the output buffer,
 *    to send to the client at the other end of the SSL connection.
 */
int tls_handshake_send(tls_session_t *ssn)
{
    int err;

    /*
     *    If there's un-encrypted data in 'clean_in', then write
     *    that data to the SSL session, and then call the BIO function
     *    to get that encrypted data from the SSL session, into
     *    a buffer which we can then package into an EAP packet.
     *
     *    Based on Server's logic this clean_in is expected to
     *    contain the data to send to the client.
     */
    if (ssn->clean_in.used > 0) {
        int written;

        written = SSL_write(ssn->ssl, ssn->clean_in.data, ssn->clean_in.used);
        record_minus(&ssn->clean_in, NULL, written);

        /* Get the dirty data from Bio to send it */
        err = BIO_read(ssn->from_ssl, ssn->dirty_out.data + ssn->dirty_out.used,
                   sizeof(ssn->dirty_out.data) - ssn->dirty_out.used);
        if (err > 0) {
            ssn->dirty_out.used += err;
        } else {
            if (!tls_error_io_log(, ssn, err, "Failed writing to OpenSSL")) {
                return 0
            }
        }
    }

    return 1;
}
复制代码

2、读取加密数据并解密后解析

 

复制代码
tls_application_data(tls_session_t *ssn)

{
    int err;
    VALUE_PAIR **certs;

    /*
     *    Decrypt the complete record.
     */
    if (ssn->dirty_in.used > 0) {
        err = BIO_write(ssn->into_ssl, ssn->dirty_in.data,
                ssn->dirty_in.used);
        if (err != (int) ssn->dirty_in.used) {
            REDEBUG("(TLS) Failed writing %zd bytes to SSL BIO: %d", ssn->dirty_in.used, err);
            record_init(&ssn->dirty_in);
            return FR_TLS_FAIL;
        }

        record_init(&ssn->dirty_in);
    }

    /*
     *    tls_handshake_recv() may read application data.  So
     *    don't touch clean_out.  But only if the BIO_write()
     *    above didn't do anything.
     */
    else if (ssn->clean_out.used > 0) {
        RDEBUG("(TLS) We already have %zd bytes of application data, processing it.",
               (ssn->clean_out.used));
        goto add_certs;
    }

    /*
     *      Read (and decrypt) the tunneled data from the
     *      SSL session, and put it into the decrypted
     *      data buffer.
     */
    err = SSL_read(ssn->ssl, ssn->clean_out.data + ssn->clean_out.used,
               sizeof(ssn->clean_out.data) - ssn->clean_out.used);
    if (err <= 0) {
        int code;

        RDEBUG3("(TLS) SSL_read Error");

        code = SSL_get_error(ssn->ssl, err);
        switch (code) {
        case SSL_ERROR_WANT_READ:
            if (ssn->clean_out.used > 0) { /* just process what application data we have */
                err = 0;
                break;
            }

            RDEBUG("(TLS) OpenSSL says that it needs to read more data.");
            return FR_TLS_MORE_FRAGMENTS;

        case SSL_ERROR_WANT_WRITE:
            if (ssn->clean_out.used > 0) { /* just process what application data we have */
                err = 0;
                break;
            }

            REDEBUG("(TLS) Error in fragmentation logic: SSL_WANT_WRITE");
            return FR_TLS_FAIL;

        case SSL_ERROR_NONE:
            RDEBUG2("(TLS) No application data received.  Assuming handshake is continuing...");
            err = 0;
            break;

        case SSL_ERROR_ZERO_RETURN:
            RDEBUG2("(TLS) Other end closed the TLS tunnel.");
            return FR_TLS_FAIL;

        default:
            REDEBUG("(TLS) Error in fragmentation logic - code %d", code);
            tls_error_io_log(ssn, err, "Failed reading application data from OpenSSL");
            return FR_TLS_FAIL;
        }
    }

    /*
     *    Passed all checks, successfully decrypted data
     */
    ssn->clean_out.used += err;

add_certs:
    /*
     *    Add the certificates to intermediate packets, so that
     *    the inner tunnel policies can use them.
     */
    certs = (VALUE_PAIR **)SSL_get_ex_data(ssn->ssl, fr_tls_ex_index_certs);
return FR_TLS_OK;
}
复制代码

 

对于TCP流来说:

复制代码
int dual_tls_recv(rad_listen_t *listener)
{
    RADIUS_PACKET *packet;
    RAD_REQUEST_FUNP fun = NULL;
    listen_socket_t *sock = listener->data;
    RADCLIENT    *client = sock->client;
    BIO        *rbio;
#ifdef WITH_COA_TUNNEL
    bool        is_reply = false;
#endif

    if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0;

redo:
    if (!tls_socket_recv(listener)) {
        return 0;
    }
//process data

    /*
     *    Check for more application data.
     *
     *    If there is pending SSL data, "peek" at the
     *    application data.  If we get at least one byte of
     *    application data, go back to tls_socket_recv().
     *    SSL_peek() will set SSL_pending(), and
     *    tls_socket_recv() will read another packet.
     */
    rbio = SSL_get_rbio(sock->ssn->ssl);
    if (BIO_ctrl_pending(rbio)) {
        char buf[1];
        int peek = SSL_peek(sock->ssn->ssl, buf, 1);

        if (peek > 0) {
            DEBUG("(TLS) more TLS records after dual_tls_recv");
            goto redo;
        }
    }

    return 1;
}
复制代码

 

 

复制代码
tls_socket_recv(rad_listen_t *listener)
{
    bool doing_init = false, already_read = false;
    ssize_t rcode;
    RADIUS_PACKET *packet;
    REQUEST *request;
    listen_socket_t *sock = listener->data;
    fr_tls_status_t status;
    RADCLIENT *client = sock->client;

    

    /*
     *    Allocate a REQUEST for debugging, and initialize the TLS session.
     */
    if (!sock->request) {
        request->packet = talloc_steal(request, sock->packet);

        request->component = "<tls-connect>";

        request->reply = rad_alloc(request, false);
        if (!request->reply) return 0;

        rad_assert(sock->ssn == NULL);

        sock->ssn = tls_new_session(sock, listener->tls, sock->request,
                        listener->tls->require_client_cert, true);
        if (!sock->ssn) {
            TALLOC_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 *) &sock->certs);
        SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_TALLOC, sock);
        sock->ssn->quick_session_tickets = true; /* we don't have inner-tunnel authentication */

        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;

    if (sock->state == LISTEN_TLS_SETUP) {
        RDEBUG3("(TLS) Setting connection state to RUNNING");
        sock->state = LISTEN_TLS_RUNNING;

        if (sock->ssn->clean_out.used < 20) {
            goto get_application_data;
        }

        goto read_application_data;
    }

    RDEBUG3("(TLS) Reading from socket %d", request->packet->sockfd);
    PTHREAD_MUTEX_LOCK(&sock->mutex);

    /*
     *    If there is pending application data, as set up by
     *    SSL_peek(), read that before reading more data from
     *    the socket.
     */
    if (SSL_pending(sock->ssn->ssl)) {
        RDEBUG3("(TLS) Reading pending buffered data");
        sock->ssn->dirty_in.used = 0;
        goto check_for_setup;
    }

    if (!already_read) {
        rcode = read(request->packet->sockfd,
                 sock->ssn->dirty_in.data,
                 sizeof(sock->ssn->dirty_in.data));
        if ((rcode < 0) && (errno == ECONNRESET)) {
        do_close:
            DEBUG("(TLS) Closing socket from client port %u", sock->other_port);
            tls_socket_close(listener);
            PTHREAD_MUTEX_UNLOCK(&sock->mutex);
            return 0;
        }

        if (rcode < 0) {
            RDEBUG("(TLS) Error reading socket: %s", fr_syserror(errno));
            goto do_close;
        }

        /*
         *    Normal socket close.
         */
        if (rcode == 0) goto do_close;

        sock->ssn->dirty_in.used = rcode;
    }

    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("(TLS) Non-TLS data sent to TLS socket: closing");
        goto do_close;
    }

    /*
     *    If we need to do more initialization, do that here.
     */
check_for_setup:
    if (!sock->ssn->is_init_finished) {
        if (!tls_handshake_recv(request, sock->ssn)) {
            RDEBUG("(TLS) Failed in TLS handshake receive");
            goto do_close;
        }

        /*
         *    More ACK data to send.  Do so.
         */
        if (sock->ssn->dirty_out.used > 0) {
            tls_socket_write(listener, request);
            PTHREAD_MUTEX_UNLOCK(&sock->mutex);
            return 0;
        }

        /*
         *      If SSL handshake still isn't finished, then there
         *      is more data to read.  Release the mutex and
         *      return so this function will be called again
         */
        if (!SSL_is_init_finished(sock->ssn->ssl)) {
            PTHREAD_MUTEX_UNLOCK(&sock->mutex);
            return 0;
        }
    }

    /*
     *    Run the request through a virtual server in
     *    order to see if we like the certificate
     *    presented by the client.
     */
    if (sock->state == LISTEN_TLS_INIT) {
        if (!SSL_is_init_finished(sock->ssn->ssl)) {
            RDEBUG("(TLS) OpenSSL says that the TLS session is still negotiating, but there's no more data to send!");
            goto do_close;
        }

        sock->ssn->is_init_finished = true;
        if (!listener->check_client_connections) {
            sock->state = LISTEN_TLS_RUNNING;
            goto get_application_data;
        }

        request->packet->vps = fr_pair_list_copy(request->packet, sock->certs);

        /*
         *    Fake out a Status-Server packet, which
         *    does NOT have a Message-Authenticator,
         *    or any other contents.
         */
        request->packet->code = PW_CODE_STATUS_SERVER;
        request->packet->data = talloc_zero_array(request->packet, uint8_t, 20);
        request->packet->data[0] = PW_CODE_STATUS_SERVER;
        request->packet->data[3] = 20;
        sock->state = LISTEN_TLS_CHECKING;
        PTHREAD_MUTEX_UNLOCK(&sock->mutex);

        /*
         *    Don't read from the socket until the request
         *    returns.
         */
        listener->status = RAD_LISTEN_STATUS_PAUSE;
        radius_update_listener(listener);

        return 1;
    }

    /*
     *    Try to get application data.
     */
get_application_data:
    status = tls_application_data(sock->ssn, request);
    RDEBUG3("(TLS) 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;
    }

    /*
     *    Hold application data if we're not yet in the RUNNING
     *    state.
     */
    if (sock->state != LISTEN_TLS_RUNNING) {
        RDEBUG3("(TLS) Holding application data until setup is complete");
        return 0;
    }

read_application_data:
    /*
     *    We now have a bunch of application data.
     */
    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("(TLS) Received bad packet: Length %zd 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 = talloc_array(packet, uint8_t, 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);



    FR_STATS_INC(auth, total_requests);

    return 1;
}


复制代码

 

复制代码
/*
 * We are the server, we always get the dirty data
 * (Handshake data is also considered as dirty data)
 * During handshake, since SSL API handles itself,
 * After clean-up, dirty_out will be filled with
 * the data required for handshaking. So we check
 * if dirty_out is empty then we simply send it back.
 * As of now, if handshake is successful, then we keep going,
 * otherwise we fail.
 *
 * Fill the Bio with the dirty data to clean it
 * Get the cleaned data from SSL, if it is not Handshake data
 */
int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
{
    int err;

    if (ssn->invalid_hb_used) {
        REDEBUG("(TLS) OpenSSL Heartbeat attack detected.  Closing connection");
        return 0;
    }

    if (ssn->dirty_in.used > 0) {
        err = BIO_write(ssn->into_ssl, ssn->dirty_in.data, ssn->dirty_in.used);
        if (err != (int) ssn->dirty_in.used) {
            REDEBUG("(TLS) Failed writing %zd bytes to SSL BIO: %d", ssn->dirty_in.used, err);
            record_init(&ssn->dirty_in);
            return 0;
        }
        record_init(&ssn->dirty_in);
    }

    err = SSL_read(ssn->ssl, ssn->clean_out.data + ssn->clean_out.used,
               sizeof(ssn->clean_out.data) - ssn->clean_out.used);
    if (err > 0) {
        ssn->clean_out.used += err;
        return 1;
    }

    if (!tls_error_io_log(request, ssn, err, "Failed reading from OpenSSL")) return 0;

    /* Some Extra STATE information for easy debugging */
    if (!ssn->is_init_finished && SSL_is_init_finished(ssn->ssl)) {
        VALUE_PAIR *vp;
        char const *str_version;

        RDEBUG2("(TLS) Connection Established");
        ssn->is_init_finished = true;

        vp = fr_pair_afrom_num(request->state_ctx, PW_TLS_SESSION_CIPHER_SUITE, 0);
        if (vp) {
            fr_pair_value_strcpy(vp, SSL_CIPHER_get_name(SSL_get_current_cipher(ssn->ssl)));
            fr_pair_add(&request->state, vp);
            RINDENT();
            rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
            REXDENT();
        }

        switch (SSL_version(ssn->ssl)) {
        case SSL2_VERSION:
            str_version = "SSL 2.0";
            break;
        case SSL3_VERSION:
            str_version = "SSL 3.0";
            break;
        case TLS1_VERSION:
            str_version = "TLS 1.0";
            break;
#ifdef TLS1_1_VERSION
        case TLS1_1_VERSION:
            str_version = "TLS 1.1";
            break;
#endif
#ifdef TLS1_2_VERSION
        case TLS1_2_VERSION:
            str_version = "TLS 1.2";
            break;
#endif
#ifdef TLS1_3_VERSION
        case TLS1_3_VERSION:
            str_version = "TLS 1.3";
            break;
#endif
        default:
            str_version = "UNKNOWN";
            break;
        }

        vp = fr_pair_afrom_num(request->state_ctx, PW_TLS_SESSION_VERSION, 0);
        if (vp) {
            fr_pair_value_strcpy(vp, str_version);
            fr_pair_add(&request->state, vp);
            RINDENT();
            rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
            REXDENT();
        }
    }
    else if (SSL_in_init(ssn->ssl)) { RDEBUG2("(TLS) In Handshake Phase"); }
    else if (SSL_in_before(ssn->ssl)) { RDEBUG2("(TLS) Before Handshake Phase"); }
    else if (SSL_in_accept_init(ssn->ssl)) { RDEBUG2("(TLS) In Accept mode"); }
    else if (SSL_in_connect_init(ssn->ssl)) { RDEBUG2("(TLS) In Connect mode"); }

#if OPENSSL_VERSION_NUMBER >= 0x10001000L
    /*
     *    Cache the SSL_SESSION pointer.
     */
    if (!ssn->ssl_session) {
        ssn->ssl_session = SSL_get_session(ssn->ssl);

        /*
         *    Some versions of OpenSSL don't allow you to
         *    get the session before the init is finished.
         *    In that case, this error is a soft fail.
         *
         *    If the session init is finished, then failure
         *    to get the session is a hard fail.
         */
        if (!ssn->ssl_session && ssn->is_init_finished) {
            RDEBUG("(TLS) Failed getting session");
            return 0;
        }
    }

#else
#error You must use a newer version of OpenSSL
#endif

    err = BIO_ctrl_pending(ssn->from_ssl);
    if (err > 0) {
        err = BIO_read(ssn->from_ssl, ssn->dirty_out.data,
                   sizeof(ssn->dirty_out.data));
        if (err > 0) {
            RDEBUG3("(TLS) got %d bytes of data", err);
            ssn->dirty_out.used = err;

        } else if (BIO_should_retry(ssn->from_ssl)) {
            record_init(&ssn->dirty_in);
            RDEBUG2("(TLS) Asking for more data in tunnel.");
            return 1;

        } else {
            tls_error_log(NULL, "Error reading from OpenSSL");
            record_init(&ssn->dirty_in);
            return 0;
        }
    } else {
        RDEBUG2("(TLS) Application data.");
        /* Its clean application data, leave whatever is in the buffer */
#if 0
        record_init(&ssn->clean_out);
#endif
    }

    /* We are done with dirty_in, reinitialize it */
    record_init(&ssn->dirty_in);
    return 1;
}
复制代码

 

posted @   codestacklinuxer  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示