OPENSSL 的代码写的很乱,流程不是十分清楚,TLS1.3 的 EARLY DATA 又是一个穿插错乱的流程,所以写个东西来简单介绍。针对的代码是 S_SERVER 的流程。
如果启动 S_SERVER 时,支持 early_data,那么 OPENSSL 会调用 SSL_read_early_data 函数:
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;
ret = SSL_read_ex(s, buf, num, readbytes);
/*
* State machine will update early_data_state to
* SSL_EARLY_DATA_FINISHED_READING if we get an EndOfEarlyData
* message
*/
很有趣,openssl 先是在 SSL_read_early_data 中调用了 SSL_accept 函数,这个函数本质上就是 state_machine,也就是 read_state_machine 和 write_state_machine,read_state_machine 负责处理读取到的 handshake 握手消息,比方说 client_hello。server 转完了 client_hello 的分析过程,并且把参数都保存下来就转入 write_state_machine,write_state_machine 要做的事情比较多,write_state_machine 分别要 construct_server_hello,construct_change_spec(处于兼容性),construct_encrypted_extension。并且调用 ssl->method->do_write 函数,这个函数模板真正指向 ssl3_do_write 函数。这一大段过程就是 SSL_accept 的过程。也就是说 SSL_accept 函数就是服务端的整个握手流程,请参看 TLS1.3 RFC 的 SERVER STATE MACHINE 流程图 visit https://tools.ietf.org/html/rfc8446#page-121
SSL_accept 握手完成之后,服务端调用 SSL_read_ex(s, buf, num, readbytes)函数读取 early_data 的内容,SSL_read_ex 内部包裹 ssl_read_internal 函数,ssl_read_internal 包裹 s->method->ssl_read,这个函数指针就是 ssl3_read,包裹 ssl3_read_internal 函数,而 ssl3_read_internal 又包裹了 s->method->ssl_read_bytes 也就是 ssl3_read_bytes 函数
int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
size_t len, int peek, size_t *readbytes)
根据 type 的类型读取 payload,存储长度到 len,只有三种 type:SSL3_RT_HANDSHAKE (when ssl3_get_message calls us),SSL3_RT_APPLICATION_DATA (when ssl3_read calls us),0 (during a shutdown, no data has to be returned)。也就是说这个函数只负责读取,更多的内容会由上层负责。值得注意的是,为了能在本层分析报文的 payload,也就是报文的种类( alert,handshake protocol ),该函数会把 payload 的头四个字节存储到 s->rlayer.handshake_fragment 中,然后根据预先假定的 type 类型来分析读到的报文。
回到刚才的场景,SSL_read_ex 的报文类型是 SSL3_RT_APPLICATION_DATA,下面的代码时调用 SSL3_read_bytes 的函数:
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;
ret =
s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, NULL, buf, len,
peek, readbytes);
...
ssl3_read_bytes 函数会不断的读取 payload 到传进去的参数 buf 中,如果这个过程读不到数据,该函数会再次尝试读取出来 pay_load,下面代码在 SSL3_read_bytes 内部:
do {
if (len - totalbytes > SSL3_RECORD_get_length(rr))
n = SSL3_RECORD_get_length(rr);
else
n = len - totalbytes;
memcpy(buf, &(rr->data[rr->off]), n);
buf += n;
if (peek) {
/* Mark any zero length record as consumed CVE-2016-6305 */
if (SSL3_RECORD_get_length(rr) == 0)
SSL3_RECORD_set_read(rr);
} else {
SSL3_RECORD_sub_length(rr, n);
SSL3_RECORD_add_off(rr, n);
if (SSL3_RECORD_get_length(rr) == 0) {
s->rlayer.rstate = SSL_ST_READ_HEADER;
SSL3_RECORD_set_off(rr, 0);
SSL3_RECORD_set_read(rr);
}
}
if (SSL3_RECORD_get_length(rr) == 0
|| (peek && n == SSL3_RECORD_get_length(rr))) {
curr_rec++;
rr++;
}
totalbytes += n;
} while (type == SSL3_RT_APPLICATION_DATA && curr_rec < num_recs
&& totalbytes < len);
if (totalbytes == 0) {
/* We must have read empty records. Get more data */
goto start;
}
if (!peek && curr_rec == num_recs
&& (s->mode & SSL_MODE_RELEASE_BUFFERS)
&& SSL3_BUFFER_get_left(rbuf) == 0)
ssl3_release_read_buffer(s);
*readbytes = totalbytes;
return 1;
如果服务端收到一个 SSL3_RT_APPLICATION_DATA,该消息可能史哥 ALERT 消息,或者 HANDSHAKE 消息,如果是 ALERT 消息,ssl3_read_bytes 会利用 PACKET_get_1 来获取具体的 alert number,alert content。非当服务端收到 End of Early Data 报文时,该报文类型为 SSL3_RT_HANDSHAKE 而不是 SSL3_RT_APPLICATION_DATA 报文,说明我们收到了一个握手报文。服务端会尝试收取到足够长度的握手报文,并在此进入握手函数 s->handshake_func 恢复握手过程,下面的代码在 SSL3_read_bytes 内部。
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;
}
进入 handshake_func 也就是 state_machine 的后续流程不再赘述,参照流程图即可。
握手的函数流程,和一些函数的 API 写不写看心情吧,加上一些链接可以用来学习。 https://www.cnblogs.com/hrhguanli/p/3834585.html https://blog.csdn.net/cwg2552298/article/details/83045448 https://security.stackexchange.com/questions/55667/tls-sequence-number https://tools.ietf.org/html/rfc8446
1
isCyan 2019-01-29 23:12:36 +08:00 via Android
可能是干货,虽然看不懂,先挽尊
|