在OpenSSL心脏出血之后,我相信很多人都出了血,并且流了泪...网上瞬间出现了大量吐嘈OpenSSL的文章或段子,仿佛内心的窝火一瞬间被释放了出来,跟着这场疯闹,我也吐一下嘈,以雪这些年被OpenSSL蹂躏之辱,也许可以顺便展现一下我的无知与愚昧,但只是也许...
首先声明的一点是,我并没有恶意诋毁的意思,也并没有针对什么,比起生活中的大喜大悲,比起工作中的大起大落,比起追求理想过程中的遭遇坎坷,OpenSSL的折磨其实是一种幸福,只是对幸福的解读,有时可以认为是,痛并快乐着,齐秦如是说...
OpenSSL代码真的很烂,太烂,毫无章法的乱。
实用主义者,或者中毒已深的人总是能给出一段代码之所以这么写而不那么写的理由,并且理由还特别充分,以至于你也会认为这么写,写成这么烂是有理由的,其中一定藏着什么不易理解的玄机,但是,作为非神学的世俗作品,它不是圣经,不易理解本身就是一个过错,当然,也许是我水平太水太菜,没有达到OpenSSL要求的那种深度,如果这样,这篇吐嘈就是写给和我相同水平的菜鸟看的,高手请默默离开,不要带走一点悲哀,留下的这些悲哀,让我们这些菜鸟的眼泪洗刷刷吧...存在就是合理的,好吧,西西弗斯的神话表示人生就是一场悲哀,收成抵不上成本,它是存在的,因此是合理的,请不要报怨OpenSSL,它也是合理的,是的,完全正确。
开源是伟大的,至少曾经是伟大的,质疑它的人,一定没有体验过Linus Torvalds爆米且口的那份激动与听众受虐般的激情,也不一定拥有站在Richard Stallman或者极端的Eric S. Raymond脚下的那份敬畏和感动。但是OpenSSL出现以后,表明开源所表达的自由还有另外一层意思,那就是代码拥有不受审查的自由,有烂的自由,更多的,每个人都有使用烂代码的自由,更进一步的,每个人都有把烂代码说成艺术的自由,而这份自由,被OpenSSL那黑翼般的力量煽动,带给了每一个人,于是,心脏流血的时候,我攥起了拳头...
说多了都是泪...突然看到了一个项目,OpenBSD发起一个清理OpenSSL代码的项目,就想继续泪下去,等看完我这篇吐嘈,请带着泪去欣赏吧,链接在下面:
清爽链接1
清爽链接2
同样值得欣赏的是,Open×××的代码,同样狠烂!欣赏链接之前,请让我抛块砖,来点小菜。我们开始吧!{
if ()
return ret;
else if ()
return ret2;
}
for (...) {
if () {
flag = 1;
}
...
if (flag2 == 2) {
flag = 2;
}
...
}
if (flag == 3 || flag2 == 1) {
...
}
some_function(...)
{
...
return(n);
}
/* If we get here, then type != rr->type; if we have a handshake
* message, then it was unexpected (Hello Request or Client Hello). */
/* In case of record types for which we have 'fragment' storage,
* fill that so that we can process the data at a fixed place.
*/
{
unsigned int dest_maxlen = 0;
unsigned char *dest = NULL;
unsigned int *dest_len = NULL;
if (rr->type == SSL3_RT_HANDSHAKE)
{
dest_maxlen = sizeof s->s3->handshake_fragment;
dest = s->s3->handshake_fragment;
dest_len = &s->s3->handshake_fragment_len;
}
...
}
#define ARGV Argv
int
main(int Argc, char *ARGV[])
#if 0 /* worked only because C operator preferences are not as expected (and
* because this is not really needed for clients except for detecting
* protocol violations): */
s->state=SSL_ST_BEFORE|(s->server)
?SSL_ST_ACCEPT
:SSL_ST_CONNECT;
#else
s->state = s->server ? SSL_ST_ACCEPT : SSL_ST_CONNECT;
#endif
int func()
{
...
a=b;
c = d;
}
some_function(...)
{
...
if (s->session->sess_cert != NULL)
{
#ifndef OPENSSL_NO_RSA
if (s->session->sess_cert->peer_rsa_tmp != NULL)
{
...
}
#endif
...
}
else
{
...;
}
...
#ifndef OPENSSL_NO_RSA
if (alg & SSL_kRSA)
{
...
}
#else /* OPENSSL_NO_RSA */
if (0)
;
#endif
#ifndef OPENSSL_NO_DH
else if (alg & SSL_kEDH)
{
...
#ifndef OPENSSL_NO_RSA
if (alg & SSL_aRSA)
...
#else
if (0)
;
#endif
#ifndef OPENSSL_NO_DSA
else if (alg & SSL_aDSS)
...;
#endif
/* else anonymous DH, so no certificate or pkey. */
...
}
else if ((alg & SSL_kDHr) || (alg & SSL_kDHd))
{
...
goto f_err;
}
#endif /* !OPENSSL_NO_DH */
#ifndef OPENSSL_NO_ECDH
else if (alg & SSL_kECDHE)
{
...
if (0) ;
#ifndef OPENSSL_NO_RSA
else if (alg & SSL_aRSA)
...;
#endif
#ifndef OPENSSL_NO_ECDSA
else if (alg & SSL_aECDSA)
...;
#endif
/* else anonymous ECDH, so no certificate or pkey. */
...
}
else if (alg & SSL_kECDH)
{
...
goto f_err;
}
#endif /* !OPENSSL_NO_ECDH */
if (alg & SSL_aFZA)
{
...
goto f_err;
}
/* p points to the next byte, there are 'n' bytes left */
/* if it was signed, check the signature */
if (pkey != NULL)
{
...
if ((i != n) || (n > j) || (n <= 0))
{
/* wrong packet length */
...
goto f_err;
}
#ifndef OPENSSL_NO_RSA
if (pkey->type == EVP_PKEY_RSA)
{
...
for (num=2; num > 0; num--)
{
...
}
...
if (i < 0)
{
...
goto f_err;
}
if (i == 0)
{
/* bad signature */
...
goto f_err;
}
}
else
#endif
#ifndef OPENSSL_NO_DSA
if (pkey->type == EVP_PKEY_DSA)
{
/* lets do DSS */
...
if (EVP_VerifyFinal(&md_ctx,p,(int)n,pkey) <= 0)
{
/* bad signature */
...
goto f_err;
}
}
else
#endif
#ifndef OPENSSL_NO_ECDSA
if (pkey->type == EVP_PKEY_EC)
{
/* let's do ECDSA */
...
if (EVP_VerifyFinal(&md_ctx,p,(int)n,pkey) <= 0)
{
/* bad signature */
...
goto f_err;
}
}
else
#endif
{
...
goto err;
}
}
else
{
/* still data left over */
if (!(alg & SSL_aNULL))
{
...
goto err;
}
if (n != 0)
{
...
goto f_err;
}
}
...
return(1);
f_err:
...;
err:
...;
#ifndef OPENSSL_NO_RSA
if (rsa != NULL)
RSA_free(rsa);
#endif
#ifndef OPENSSL_NO_DH
if (dh != NULL)
DH_free(dh);
#endif
#ifndef OPENSSL_NO_ECDH
...;
if (ecdh != NULL)
EC_KEY_free(ecdh);
#endif
...;
return(-1);
}
if(!ok) goto end;
if (0)
{
end:
X509_get_pubkey_parameters(NULL,ctx->chain);
}
do {
...
if (...)
break;
...
}while(0);
int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len)
{
...
n=(len-tot);
for (;;)
{
if (n > SSL3_RT_MAX_PLAIN_LENGTH)
nw=SSL3_RT_MAX_PLAIN_LENGTH;
else
nw=n;
// 我觉得这是个核心函数
i=do_ssl3_write(s, type, &(buf[tot]), nw, 0);
if (i <= 0)
{
s->s3->wnum=tot;
return i;
}
if ((i == (int)n) ||
(type == SSL3_RT_APPLICATION_DATA &&
(s->mode & SSL_MODE_ENABLE_PARTIAL_WRITE)))
{
/* next chunk of data should get another prepended empty fragment
* in ciphersuites with known-IV weakness: */
s->s3->empty_fragment_done = 0;
return tot+i;
}
n-=i;
tot+=i;
}
}
static int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
unsigned int len, int create_empty_fragment)
{
unsigned char *p,*plen;
int i,mac_size,clear=0;
int prefix_len = 0;
SSL3_RECORD *wr;
SSL3_BUFFER *wb;
SSL_SESSION *sess;
/* first check if there is a SSL3_BUFFER still being written
* out. This will happen with non blocking IO */
if (s->s3->wbuf.left != 0) // 在一开始的位置,处理逻辑就被劫持了,因此我就必须注意left在什么情况下不为0
// 这个执行流跳转得很诡异!太诡异!
return(ssl3_write_pending(s,type,buf,len));
/* If we have an alert to send, lets send it */
if (s->s3->alert_dispatch)
{
i=s->method->ssl_dispatch_alert(s);
if (i <= 0)
return(i);
/* if it went, fall through and send more stuff */
}
// create_empty_fragment?难道还有不这样做的?Fxxxing,在上层调用的时候,这个参数为0,这就意味着
// 肯定有什么地方以1为参数调用了本函数。这个empty fragment我后面会解释。
if (len == 0 && !create_empty_fragment)
return 0;
wr= &(s->s3->wrec);
wb= &(s->s3->wbuf);
sess=s->session;
...
if (clear)
mac_size=0;
else
mac_size=EVP_MD_size(s->write_hash);
/* 'create_empty_fragment' is true only when this function calls itself */
if (!clear && !create_empty_fragment && !s->s3->empty_fragment_done)
{
/* countermeasure against known-IV weakness in CBC ciphersuites
* (see http://www.openssl.org/~bodo/tls-cbc.txt) */
if (s->s3->need_empty_fragments && type == SSL3_RT_APPLICATION_DATA)
{
/* recursive function call with 'create_empty_fragment' set;
* this prepares and buffers the data for an empty fragment
* (these 'prefix_len' bytes are sent out later
* together with the actual payload) */
// 递归调用?我kao,这个函数竟然有两段逻辑:
// 1.默默创建一个新的record;
// 2.创建封装buf的record并和递归调用中默默创建的那个record一起发送
prefix_len = do_ssl3_write(s, type, buf, 0, 1);
if (prefix_len <= 0)
goto err;
if (s->s3->wbuf.len < (size_t)prefix_len + SSL3_RT_MAX_PACKET_SIZE)
{
/* insufficient space */
SSLerr(SSL_F_DO_SSL3_WRITE, ERR_R_INTERNAL_ERROR);
goto err;
}
}
s->s3->empty_fragment_done = 1;
}
// wb->buf是和SSL绑定的一个发送buf,事先已经malloc好了内存,真TM慷慨!
// 一个prefix_len表示在真正的record发送前紧接着的那个默默创建的record,调用者并不知道
// 会创建并发送这样一个record
p = wb->buf + prefix_len;
/* write the header */
// 这段代码还算清晰
// 但是,记住,在需要empty fragment的情况下会跑到这里两次
*(p++)=type&0xff;
wr->type=type;
*(p++)=(s->version>>8);
*(p++)=s->version&0xff;
/* field where we are to write out packet length */
plen=p;
p+=2;
/* lets setup the record stuff. */
wr->data=p;
wr->length=(int)len;
wr->input=(unsigned char *)buf;
/* we now 'read' from wr->input, wr->length bytes into
* wr->data */
/* first we compress */
if (s->compress != NULL)
{
if (!ssl3_do_compress(s))
{
SSLerr(SSL_F_DO_SSL3_WRITE,SSL_R_COMPRESSION_FAILURE);
goto err;
}
}
else
{
memcpy(wr->data,wr->input,wr->length);
wr->input=wr->data;
}
/* we should still have the output to wr->data and the input
* from wr->input. Length should be wr->length.
* wr->data still points in the wb->buf */
if (mac_size != 0)
{
s->method->ssl3_enc->mac(s,&(p[wr->length]),1);
wr->length+=mac_size;
wr->input=p;
wr->data=p;
}
/* ssl3_enc can only have an error on read */
s->method->ssl3_enc->enc(s,1);
/* record length after mac and block padding */
s2n(wr->length,plen);
/* we should now have
* wr->data pointing to the encrypted data, which is
* wr->length long */
wr->type=type; /* not needed but helps for debugging */
wr->length+=SSL3_RT_HEADER_LENGTH;
if (create_empty_fragment)
{
/* we are in a recursive call;
* just return the length, don't write out anything here
*/
// 如果是默默创建的那个record,则并不直接发送,目的是想将真实的record在内存上
// 紧随这个默默构造好的record作为一个buffer直接发送给下层BIO。为何不分别发送两个
// record呢?我想是为了紧凑使用SSL的s3->wbuf缓冲区吧,该缓冲区事先建立,而且还
// 真不小:16K+!唉,真不觉得实现者想不出更好的办法了啊
return wr->length;
}
/* now let's set up wb */
wb->left = prefix_len + wr->length;
wb->offset = 0;
/* memorize arguments so that ssl3_write_pending can detect bad write retries later */
s->s3->wpend_tot=len;
s->s3->wpend_buf=buf;
s->s3->wpend_type=type;
s->s3->wpend_ret=len;
/* we now just need to write the buffer */
return ssl3_write_pending(s,type,buf,len);
err:
return -1;
}
build_record {
操作SSL的s3->wbuf。我觉得好,就继续用
}
write_raw {
往下层BIO写入SSL的s3->wbuf.buf的某一段
}
do_ssl3_build {
if (need_empty) {
build_record;
}
build_record;
...
}
ssl3_write_bytes { if (left) { write_pending } do_ssl3_build for (;;) { write_raw; } }
ssl_write()
{
if( ssl->state != SSL_HANDSHAKE_OVER ) {
handshack;
}
if (left) {
flush_pending and return <=0
}
build and write record, return num
}
static int write_ssl_data( ssl_context *ssl, unsigned char *buf, size_t len )
{
int ret;
printf("\n%s", buf);
while( len && ( ret = ssl_write( ssl, buf, len ) ) <= 0 )
{
if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE )
{
printf( " failed\n ! ssl_write returned %d\n\n", ret );
return -1;
}
}
return( 0 );
}
亿速云提供多种品牌、不同类型SSL证书签发服务,包含:域名型、企业型、企业型专业版、增强型以及增强型专业版,单域名SSL证书300元/年起。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。