OPENSSL源码阅读(2)

SV_BODY

Posted by 大狗 on September 4, 2020

OPENSSL源码阅读(2)

前言

SV_BODY本身就是一个包裹的层面,我不喜欢这块的函数就是本身应该上来是握手,然后根据握手包决定是不是接受early_data。但openssl的代码就是上来就写SSL_read_early_data,明明是握手却搞得好像early_data读取一样,令人贼尴尬。

SV_BODY的解析

我们这里直接分析允许early_data的情况,首先调用SSL_set_accept_state(con)函数,来初始化当前ssl连接自动机的状态:标记当前ssl连接为server端,同时设置s->statem的各种状态。也就是尚未握手,尚在初始化,还没verify证书,自动机还没转起来。这些状态设置好了。再向下继续阅读。

	void SSL_set_accept_state(SSL *s)
	{
		s->server = 1;
		s->shutdown = 0;
		ossl_statem_clear(s);
		s->handshake_func = s->method->ssl_accept;
		clear_ciphers(s);
	}
	
	void ossl_statem_clear(SSL *s)
	{
		s->statem.state = MSG_FLOW_UNINITED;
		s->statem.hand_state = TLS_ST_BEFORE;
		s->statem.in_init = 1;
		s->statem.no_cert_verify = 0;
	}

之后会不断的尝试读取early data,你可能有疑问,握手还没开始呢?怎么就读取early_data了?实际上这里的读取并不是说上来就是读取的early data,只不过这个函数包含了握手的自动机。如果不支持early data的话,那么sv_body的函数体会继续运行到下面进行握手。

	SSL_set_accept_state(con);
	...
	
	if (early_data) {
        int write_header = 1, edret = SSL_READ_EARLY_DATA_ERROR;
        size_t readbytes;

        while (edret != SSL_READ_EARLY_DATA_FINISH) {
            for (;;) {
                edret = SSL_read_early_data(con, buf, bufsize, &readbytes);
                if (edret != SSL_READ_EARLY_DATA_ERROR)
                    break;
		...

下面看下SSL_read_early_data函数,s实际上就是当前的ssl连接对应的数据结构,初始化的时候,s的数据结构通过zalloc初始化,所以s->early_data_state是0,也就是SSL_EARLY_DATA_NONE。代码因此判断SSL_in_before(s)的返回值是否为0,该函数用于判断当前连接是否已经初始化了,换句人能听懂的话就是:当前连接还没有进行握手,那就返回1,反之返回0。如果返回0,就发生一个不可能发生的错误,这大概率是内存写错。代码会接着运行到SSL_EARLY_DATA_ACCEPT_RETRY的case,直接接受s->early_data_state,进入到SSL_accept函数了。此时代码进入了SSL的握手流程。

	int SSL_read_early_data(SSL *s, void *buf, size_t num, size_t *readbytes)
{
    int ret;

    if (!s->server) {
        SSLerr(SSL_F_SSL_READ_EARLY_DATA, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
        return SSL_READ_EARLY_DATA_ERROR;
    }

    switch (s->early_data_state) {
    case SSL_EARLY_DATA_NONE:
        if (!SSL_in_before(s)) {
            SSLerr(SSL_F_SSL_READ_EARLY_DATA,
                   ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
            return SSL_READ_EARLY_DATA_ERROR;
        }
        /* fall through */

    case SSL_EARLY_DATA_ACCEPT_RETRY:
        s->early_data_state = SSL_EARLY_DATA_ACCEPTING;
        ret = SSL_accept(s);
        if (ret <= 0) {
            /* NBIO or error */
            s->early_data_state = SSL_EARLY_DATA_ACCEPT_RETRY;
            return SSL_READ_EARLY_DATA_ERROR;
        }
        /* fall through */

    case SSL_EARLY_DATA_READ_RETRY:
        if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED) {
            s->early_data_state = SSL_EARLY_DATA_READING;

我们下面接着阅读SSL_accept函数,本质上SSL_do_handshake的一层包裹。我们看SSL_do_handshake的意义,首先在ossl_statem_check_finish_init函数当中判断当前s的握手状态是否处于读取early data状态/等待end_of_early_data消息接断。上面初始化状态的时候已经写的很清楚了,这里不会改变hand_state的值,那么函数会继续进行。进入到判断SSL_in_init(s) || SSL_in_before(s),还是上面状态赋值那里已经讲ssl连接的in_init状态赋值为1,所以代码会继续进行。我们这里是同步模式,所以代码继续致性s->handshake_func(s).问题来了,握手函数是什么?

	int SSL_accept(SSL *s)
{
    if (s->handshake_func == NULL) {
        /* Not properly initialized yet */
        SSL_set_accept_state(s);
    }

    return SSL_do_handshake(s);
}

int SSL_do_handshake(SSL *s)
{
    int ret = 1;

    if (s->handshake_func == NULL) {
        SSLerr(SSL_F_SSL_DO_HANDSHAKE, SSL_R_CONNECTION_TYPE_NOT_SET);
        return -1;
    }

    ossl_statem_check_finish_init(s, -1);

    s->method->ssl_renegotiate_check(s, 0);

    if (SSL_in_init(s) || SSL_in_before(s)) {
        if ((s->mode & SSL_MODE_ASYNC) && ASYNC_get_current_job() == NULL) {
            struct ssl_async_args args;

            args.s = s;

            ret = ssl_start_async_job(s, &args, ssl_do_handshake_intern);
        } else {
            ret = s->handshake_func(s);
        }
    }
    return ret;
}

void ossl_statem_check_finish_init(SSL *s, int sending)
{
    if (sending == -1) {
        if (s->statem.hand_state == TLS_ST_PENDING_EARLY_DATA_END
                || s->statem.hand_state == TLS_ST_EARLY_DATA) {
            ossl_statem_set_in_init(s, 1);
            if (s->early_data_state == SSL_EARLY_DATA_WRITE_RETRY) {
                /*
                 * SSL_connect() or SSL_do_handshake() has been called directly.
                 * We don't allow any more writing of early data.
                 */
                s->early_data_state = SSL_EARLY_DATA_FINISHED_WRITING;
            }
        }

握手函数是什么呢?handshake_func实际上就是函数int ossl_statem_accept即握手自动机。看这个实现实际就是包裹的state_machine

	int ossl_statem_accept(SSL *s)
{
    return state_machine(s, 1);
}

对于state_machine的分析我们下回再说

结尾的闲言碎语

写到这里差不多就可以结束了,实际上SV_BODY这里代码也没特别复杂,就不多说了。TLS这块还有啥不明白的直接告诉我就成了 狗头的赞赏码.jpg