tls认证--ssl处理server端编写

tls session 

复制代码
typedef struct _tls_session_t {
    SSL_CTX        *ctx;
    SSL         *ssl;
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
    SSL_SESSION    *session;
#endif
    tls_info_t    info;

    BIO         *into_ssl;
    BIO         *from_ssl;
    record_t     clean_in;            //!< Data that needs to be sent but only after it is soiled.
    record_t     clean_out;            //!< Data that is cleaned after receiving.
    record_t     dirty_in;            //!< Data  server receives.
    record_t     dirty_out;            //!< Data  server sends.

    void         (*record_init)(record_t *buf);
    void         (*record_close)(record_t *buf);
    unsigned int     (*record_plus)(record_t *buf, void const *ptr, unsigned int size);
    unsigned int     (*record_minus)(record_t *buf, void *ptr, unsigned int size);

    bool        invalid_hb_used;        //!< Whether heartbleed attack was detected.
    bool        connected;            //!< whether the outgoing socket is connected
    bool        is_init_finished;        //!< whether or not init is finished
    bool        client_cert_ok;            //!< whether or not we validated the client certificate
    bool        authentication_success;        //!< whether or not the user was authenticated (cert or PW)

    /*
     *    Framed-MTU attribute in RADIUS, if present, can also be used to set this
     */
    size_t         mtu;                //!< Current fragment size transmitted.
    size_t        tls_msg_len;            //!< Actual/Total TLS message length.
    bool        fragment;            //!< Flag, In fragment mode or not.
    bool        length_flag;            //!< A flag to include length in every TLS Data/Alert packet.
                            //!< If set to no then only the first fragment contains length.
    int        peap_flag;

    size_t        tls_record_in_total_len;    //!< How long the peer indicated the complete tls record
                            //!< would be.
    size_t        tls_record_in_recvd_len;    //!< How much of the record we've received so far.



    char const    *label;
    bool        allow_session_resumption;    //!< Whether session resumption is allowed.
    bool        session_not_resumed;        //!< Whether our session was not resumed.

    fr_tls_server_conf_t const *conf;        //! for better complaints
} tls_session_t;
复制代码

 

Server

1、初始化

复制代码
static void session_close(tls_session_t *ssn)
{
    if (ssn->ssl) {
        SSL_set_quiet_shutdown(ssn->ssl, 1);
        SSL_shutdown(ssn->ssl);

        SSL_free(ssn->ssl);
        ssn->ssl = NULL;
    }

    record_close(&ssn->clean_in);
    record_close(&ssn->clean_out);
    record_close(&ssn->dirty_in);
    record_close(&ssn->dirty_out);
    session_init(ssn);
}

static void record_init(record_t *rec)
{
    rec->used = 0;
}

static void record_close(record_t *rec)
{
    rec->used = 0;
}


/*
 *    Copy data to the intermediate buffer, before we send
 *    it somewhere.
 */
static unsigned int record_plus(record_t *rec, void const *ptr,
                unsigned int size)
{
    unsigned int added = MAX_RECORD_SIZE - rec->used;

    if(added > size)
        added = size;
    if(added == 0)
        return 0;
    memcpy(rec->data + rec->used, ptr, added);
    rec->used += added;
    return added;
}
复制代码

 

 

复制代码
static void session_init(tls_session_t *ssn)
{
    ssn->ssl = NULL;
    ssn->into_ssl = ssn->from_ssl = NULL;
    record_init(&ssn->clean_in);
    record_init(&ssn->clean_out);
    record_init(&ssn->dirty_in);
    record_init(&ssn->dirty_out);

    memset(&ssn->info, 0, sizeof(ssn->info));

    ssn->mtu = 0;
    ssn->fragment = false;
    ssn->tls_msg_len = 0;
    ssn->length_flag = false;
    ssn->opaque = NULL;
    ssn->free_opaque = NULL;
}
复制代码
复制代码
    new_tls = SSL_new(conf->ctx);
/* We use the SSL's "app_data" to indicate a call-back */
    SSL_set_app_data(new_tls, NULL);
    state = alloc_zero(tls_session_t)
    session_init(state);
    state->ctx = conf->ctx;
    state->ssl = new_tls;
    state->conf = conf;

    state->record_init = record_init;
        state->record_close = record_close;
    state->record_plus = record_plus;
    state->record_minus = record_minus;
        /*
     *    Create & hook the BIOs to handle the dirty side of the
     *    SSL.  This is *very important* as we want to handle
     *    the transmission part.  Now the only IO interface
     *    that SSL is aware of, is our defined BIO buffers.
     *
     *    This means that all SSL IO is done to/from memory,
     *    and we can update those BIOs from the packets we've
     *    received.
     */// SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio);
    state->into_ssl = BIO_new(BIO_s_mem());
    state->from_ssl = BIO_new(BIO_s_mem());
    SSL_set_bio(state->ssl, state->into_ssl, state->from_ssl);
/*
     *    Add the message callback to identify what type of
     *    message/handshake is passed
     */
    SSL_set_msg_callback(new_tls, cbtls_msg);
    SSL_set_msg_callback_arg(new_tls, state);
    SSL_set_info_callback(new_tls, cbtls_info);
/*
     *    In Server mode we only accept.
     */
/*
     *    Verify the peer certificate, if asked.
     */
    if (client_cert) {
        verify_mode = SSL_VERIFY_PEER;
        verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
        verify_mode |= SSL_VERIFY_CLIENT_ONCE;
    }
    SSL_set_verify(state->ssl, verify_mode, cbtls_verify);

    SSL_set_ex_data(state->ssl, FR_TLS_EX_INDEX_CONF, (void *)conf);
    SSL_set_ex_data(state->ssl, FR_TLS_EX_INDEX_SSN, (void *)state);
    SSL_set_accept_state(state->ssl);
       /*    i.e. it should REALLY be called MTU, and the code here
     *    should figure out what that means for TLS fragment size.
     *    asking the administrator to know the internal details
     *    of EAP-TLS in order to calculate fragment sizes is
     *    just too much.
     */
    state->mtu = conf->fragment_size;
复制代码

 

主要是:创建ssl 上下文,设置ssl关联bio,设置BIO_mem 内存操作回调读写函数,设置ssl握手时 msg 回调

 

2、server read数据client-hello并handshake

复制代码
(tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) 
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) {
            DEBUG("(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(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;

        
        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;
        }

    }
    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"); }

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) {
            DEBUG("(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);
            DEBUG2("(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 {
        DEBUG2("(TLS) Application data.");
        /* Its clean application data, leave whatever is in the buffer */
    }

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

 

上面的handshake数据 server-hello是怎样产生的呢?

结论是:

err = SSL_read(ssn->ssl, ssn->clean_out.data + ssn->clean_out.used,sizeof(ssn->clean_out.data) - ssn->clean_out.used);
如果读到handshake 数据也会调用
复制代码
static int ssl3_read_internal(SSL *s, void *buf, size_t len, int peek,
                              size_t *readbytes)
{
    int ret;

    clear_sys_error();// 清除错误信息 
    if (s->s3->renegotiate)// 检查协商是否成功
        ssl3_renegotiate_check(s, 0);
    s->s3->in_read_app_data = 1;// 标志现在在读应用层数据
    // ssl3读取数据, 类型是SSL3_RT_APPLICATION_DATA,应用数据
    ret =
        s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, NULL, buf, len,
                                  peek, readbytes);
    // s->s3->in_read_app_data == 2表示要读协商数据
    if ((ret == -1) && (s->s3->in_read_app_data == 2)) {
        /*
         * ssl3_read_bytes decided to call s->handshake_func, which called
         * ssl3_read_bytes to read handshake data. However, ssl3_read_bytes
         * actually found application data and thinks that application data
         * makes sense here; so disable handshake processing and try to read
         * application data again.
         */
        ossl_statem_set_in_handshake(s, 1);
        // 重新读数据
        ret =
            s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, NULL, buf,
                                      len, peek, readbytes);
        ossl_statem_set_in_handshake(s, 0);
    } else
        s->s3->in_read_app_data = 0;
    //读取数据的主要函数实际为ssl3_read_bytes():
    return ret;
}
复制代码
复制代码
ssl3_read_bytes----->调用

 /*
     * Unexpected handshake message (ClientHello, NewSessionTicket (TLS1.3) or
     * protocol violation)
     该报文类型为 SSL3_RT_HANDSHAKE 而不是 SSL3_RT_APPLICATION_DATA 报文,
     说明我们收到了一个握手报文。服务端会尝试收取到足够长度的握手报文,
     并在此进入握手函数 s->handshake_func 恢复握手过程
     */
    if ((s->rlayer.handshake_fragment_len >= 4)
            && !ossl_statem_get_in_handshake(s)) {
        int ined = (s->early_data_state == SSL_EARLY_DATA_READING);

        /* We found handshake data, so we're going back into init */
        ossl_statem_set_in_init(s, 1);

        i = s->handshake_func(s);// 重新握手协商
        /* SSLfatal() already called if appropriate */
        if (i < 0)
            return i;
        if (i == 0) {
            return -1;
        }
复制代码

 

 

检测tls握手阶段是否正常

复制代码
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);
复制代码

 

分析:server读取client-hello   raw数据写入到ssl-into,进行ssl 握手, 并且出现ssl-into解码后的数据, 此时会产生server-hello, 从from-ssl中读取server-hello等数据,后续发送出去。

 

3、server read数据client的 Certificate 、Client Key Exchange等数据

此时server设置为Done initial handshake 状态

复制代码
err = BIO_write(ssn->into_ssl, ssn->dirty_in.data, ssn->dirty_in.used);
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;
    }
------
//再次走一遍上一阶段流程,判断是否建立连接
复制代码

 

响应:err = BIO_ctrl_pending(ssn->from_ssl);

err = BIO_read(ssn->from_ssl, ssn->dirty_out.data,sizeof(ssn->dirty_out.data));

也就是响应 发送Change Cipher Spec   Encrypted Handshake Message 报文

 

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