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这块还有啥不明白的直接告诉我就成了