星期五, 五月 25, 2007

THe reason of iconv segment fault

#include "iconv.h"
#include "stdio.h"
#include "string.h"

int main()
{
iconv_t cd = iconv_open("UCS-2","GB2312");
char *p = "中文";
unsigned short int dst[100] = {0};
size_t leni = strlen(p);
size_t leno = sizeof(unsigned short int)*100;
char* pi = p;
char* po = (char*)&dst;

printf("Input:\t%d:%p:%s->%d:%p:%s\n", leni, p,p, leno, (char*)dst, dst);
printf("Iconv Input\t:%d:%p:%s->%d:%p:%s\n", leni, pi,pi, leno, po,po);
size_t ret = iconv(cd,π,&leni,&po,&leno);
printf("Output\t:%d:%p:%s->%d:%p:%s\n", leni, p,p, leno, (char*)dst, dst);
printf("Iconv Output\t:%d:%p:%s->%d:%p:%s\n", leni, pi,pi, leno, po,po);
printf("Ret=%d\n",ret);
iconv_close(cd);
return ret;
}
--Output
Input: 4:0x804872d:中文->200:0xbf86db84:
Iconv Input :4:0x804872d:中文->200:0xbf86db84:
Output :0:0x804872d:中文->196:0xbf86db84:-N噀
Iconv Output :0:0x8048731:->196:0xbf86db88:
Ret=0
iconv函数会造成指针的修改,如果直接操作&p,会将p直到别的地方!所以出现Segment Fault!

星期三, 五月 23, 2007

Windows 2003 R2 是什么?

R2是 Relase 2地缩写
微软日前表示,Windows Server 2003 R2是Windows Server 2003系列的最新版本,在分公司服务器管理、跨组织的身份认证、以及网络储存管理等三个领域进行了强化,并增加了许多新功能。

  下一代Windows服务器“Longhorn”前的重要版本

   依据 Windows Server 产品上市时间的规划,Windows Server 2003 R2是推进到下一代服务器“Longhorn”前的重要改版,它除了继承Windows Server 2003 SP1安全、稳定的优点之外,另外在以下的应用领域提供了新的技术与改善:

  分行服务器管理:R2 包含了新的数据压缩技术(Remote Differential Compression),更新的分布式档案系统(DFS),以及新的集中式打印机管理接口,让技术人员能更有效率的管理分公司的数据与打印机装置,透过 快速数据复写降低跨广域网络(WAN)传输所需的频宽,进而降低整体分公司的运营成本。

  身份与存取管理:新的联邦式的身份认证服务 (Active Directory Federation Service,ADFS)能在 Web Services 架构下安全地建立联邦式信任关系,在不同系统间交换身份识别及认证,协助IT人员迅速开发跨组织的身分管理机制,以及Web单一签入(Single Sign On,SSO)的解决方案。

  网络储存管理:新的档案服务器资源管理员(File Server Resource Manager,FSRM)能让系统管理者规划及最佳化储存装置的容量管制、提供过滤档案及周期性的报表产出。另外针对储域网络(SAN)R2 也提供了新的管理工具,同时支持光纤信道及iSCSI等技术,能让企业以最低的成本管理网络上的储存子系统。

  支持 64 位及与 UNIX 系统做更佳的整合

  Windows Server 2003 R2也将提供x64版本,支持日益采用的64位处理器,此外,与UNIX系统做更好的互通、通过SharePoint Services提高储存、分享及搜寻信息的能力等,都是R2改进过的重要功能。

  Windows Server 2003 R2 以 Windows Server 2003 SP1为基础,极易整合于目前Windows Server 2003 的环境中,升级不会影响既有的IT基础架构。

星期日, 五月 20, 2007

感觉最近的互联网很热闹

1. Google, MicroSoft, Yahoo广告公司收购!
2. Google与Sogou拼音输入法之争
3. MicroSoft称Linux侵权公司235专利
4. Norton错误删除用户系统文件
5. Kaspersky删除瑞信卡卡,杀的好啊!

Kaspersky申明->反思软件企业的作风,与输入法事件因该时不同的俩回事!

IT168 新闻快车】当诺顿的误杀事件还一波未平,另一边,瑞星卡巴斯基两位业界老大又因为“瑞星卡卡是否应该被列入卡巴斯基病毒查杀黑名单”一事发生了争论。5月20日下午,卡巴斯基官方通过IT168软件频道率先发布了如下声明:

2007年5月19日上午,卡巴斯基公司接到瑞星公司来电,称卡巴斯基反病毒软件将瑞星卡卡当作病毒“查杀”,要求卡巴斯基解决这一问题。卡巴斯基公司当 即表示,卡巴斯基将在保障用户安全的前提下,立即安排病毒分析工程师对瑞星卡卡在用户计算机内的行为进行全面评估。鉴于卡巴斯基在全球均奉行“用户安全第 一”的保护原则,对任何可能对计算机安全造成威胁的进程都会向用户提出警告,即使这些进程来自于商业软件。事实上,绝大多数全球领先的商业软件厂商在将其 软件推向市场时,都很重视其软件与卡巴斯基反病毒软件的兼容性。

令人吃惊的是,卡巴斯基公司负责媒体宣传的部门很快发现互联网上出现了多篇以“卡巴斯基查杀瑞星卡卡 导致大量用户无法正常升级”为题、内容完全一样的新闻稿,最早的一篇于2007年5月19日15点05分43秒发布在瑞星官方网站上。

为避免用户、公众受到不准确信息的干扰,切实保障用户网络安全,卡巴斯基公司特就瑞星公司指称卡巴斯基反病毒软件“查杀”瑞星卡卡一事,发表如下声明:

  一、卡巴斯基是全球技术领先 的安全软件厂商,卡巴斯基反病毒软件是专业而领先的安全软件,卡巴斯基将用户安全放在第一位,专注于应对病毒、木马、蠕虫、恶意程序等任何可能影响计算机 安全的威胁,并保护用户远离这些威胁。卡巴斯基并不是针对任何一款特定软件的卸载工具,也不事先假定任何软件是恶意软件或流氓软件;当然,如果某一软件包含上述任何可能对用户安全造成影响的威胁,卡巴斯基会实时的、负责任的提醒用户注意这些威胁,直到这些威胁在那些软件进行相应修正后消失。

二、卡巴斯基将传统的病毒特征码技术与最新的主动防御技术相结合,能够处理进出计算机的所有数据,包括电子邮件、互联网数据流和网络互动;能够监控在内存中运行的所有程序与进程,可在任何危险的、可疑的或隐藏的进程(例如Rootkits)出现时发出警报,阻断对系统的所有可能的有害更改,并可在恶意行为出现后将系统复原。

三、尽管卡巴斯基拥有全球范围内“百科全书”般丰富而庞大的病毒样本、恶意程序库和令人惊叹的主动防御能力,卡巴斯基依然十分尊重最终用户的选择权。在卡 巴斯基提出威胁警告后,用户可以根据自己的知识、经验或任何其它理由自主的决定是“删除”还是“跳过”这些威胁。是否“查杀”某个软件由用户来决定,如果 用户非常确信运行某个软件或进行某个操作不会产生任何威胁,卡巴斯基欢迎用户将其提交给卡巴斯基的病毒分析中心,并将由病毒分析工程师对其进行重新分析评 估。

四、作为致力于保障包括中国用户在内的全球用户计算机安全的公司,卡巴斯基愿意再次重申,卡巴斯基来中国是为了向中国用户提供更好、更完美的网络安全解决 方案,我们在全球都没有炒作概念,打口水战的传统,只懂得专注于提升技术实力以应对日益增长的安全威胁,只会低调前行。在发布这份官方声明之后,卡巴斯基 公司将不会再就此事件发表任何的评论。我们相信,在中国这样一个有着五千年文明的伟大而智慧的国度,所有用户都能看清楚所谓的“卡巴斯基查杀瑞星卡卡”事 件的真相。

鉴于网络安全形势越来越严峻,用户面临的威胁越来越多,卡巴斯基呼吁所有安全软件厂商,无论是本土的还是全球的,集中精力研发更好的产品,向用户提供尽可能准确的信息,为用户提供更好的服务。

特此声明
卡巴斯基公司

北京时间:2007年5月20日

星期五, 五月 18, 2007

win2003版本区别、四个版本的区别

-- 作者:黑鹰
-- 发布时间:2006-7-9 13:28:00

-- win2003版本区别、四个版本的区别

1.win2003版本区别

1)Windows Server 2003, Standard Edition (标准版)

针对中小型企业的核心产品,他也是支持双路处理器,4GB的内存。它除了具备 Windows Server 2003 Web Edition 所有功能外,还支持像证书服务、UDDI服务、传真服务、IAS因特网验证服务、可移动存储、RIS、智能卡、终端服务、WMS和 Services for Macintosh。

支持文件和打印机共享。 提供安全的网络联接。

2)Windows Server 2003, Enterprise Edition (企业版)

这个产品被定义为新一带高端产品,它最多能够支持8路处理器,32 GB内存,和28个节点的集群。它是 Windows Server 2003 Standard Edition 的扩展版本,增加了 Metadirectory Services Support、终端服务会话目录、集群、热添加( Hot-Add)内存和 NUMA非统一内存访问存取技术。这个版本还另外增加了一个支持64位计算的版本。

全功能的操作系统支持多达8个处理器。 提供企业级的功能例如8节点的集群,支持32GB内存。 支持英特尔 安腾Itanium 处理器。 将推出支持64位计算机的版本,可以支持8个64位处理器以及64GB的内存。

3)Windows Server 2003, Datacenter Edition (数据中心)

像以往一样,这是个一直代表微软产品最高性能的产品,他的市场对象一直定位在最高端应用上,有着极其可靠的稳定性和扩展性能。他支持高达8-32路处理器, 64GB的内存、2-8节点的集群。与 Windows Server 2003 Enterprise Edition 相比, Windows Server 2003 Datacenter Edition 增加了一套 Windows Datacenter Program 程序包。这个产品同样也为另外一个64位版本做了支持。

微软迄今为止提供的最强大、功能最为强劲的服务器操作系统。 支持32路处理器和64GB内存。 同时提供8点集群和负载均衡。 提供64位处理器平台,可支持惊人的64路处理器和512GB的内存。

4)Windows Server 2003, Web Edition (Web版)

这个版本是专门针对Web服务优化的,它支持双路处理器,2GB的内存。该产品同时支持ASP.NET、DFS分布式文件系统、EFS文件加密系统、 IIS6.0、智能镜像、ICF因特网防火墙、IPv6、Mircrosoft.Net Framework、NLB网络负载均衡、PKI、Print Services for UNIX、RDP、远程OS安装(非RIS服务)、RSoP策略的结果集、影子拷贝恢复(Shadow Copy Restore)、VPN和WMI命令行模式等功能。Windows Server 2003 Web Edition 唯一和其他版本不同的是它仅能够在AD域中做成员服务器,而不能够做DC域控制器。

可以架构各种网页应用,XML页面服务。 IIS 6.0。 轻松迅速开发各种基于 XML以及 ASP.NET服务项目的平台。


5)Windows Server 2003,64-bit Edition (64位版本)

专门针对64位处理器 安腾Itanium而开发的版本。
包括两个版本:

Windows Server 2003 Enterprise Server
64-bit Edition。

Windows Server 2003 Datacenter Server
64-bit Edition。

星期三, 五月 16, 2007

星期六, 五月 12, 2007

Fedora 7 -- what, when, and why

Fedora 7 -- what, when, and why


  • From: Max Spevack
  • To: fedora-announce-list redhat com, fedora-list redhat com
  • Cc:
  • Subject: Fedora 7 -- what, when, and why
  • Date: Tue, 8 May 2007 18:09:45 -0400 (EDT)

With the Red Hat Summit kicking off, I'd like to take a few minutes to discuss some of the recent happenings in the Fedora Project, particularly around Fedora 7. This email is being sent both to public Fedora mailing lists, and to Red Hat mailing lists, so that folks in both the external and internal Fedora communities can have a chance to read it, and people can all sort of consistently spread the same message about Fedora.

FEDORA 7

The next version of Fedora will be released on May 24th. It will be called "Fedora 7" -- not "Fedora Core 7". It's the most ambitious release of Fedora that we've undertaken, and I hope that when we look back at Fedora 7 one or two years down the road, the decisions that we made for this release will have proven to be as impactful as anything we've done in the Fedora space since the start of the Fedora Project. In one sentence: "Fedora 7 has been about improving the manner in which all future Fedora releases will be made."

(1) The entire toolchain is free. Every step in the distro creation process is free software, and can take place on hardware that is accessible both to Red Hat employees and the general Fedora community.
 Source code in an external version control system.
RPMs built on an external, open source build system.
Distributions built with an external, open source compose tool.

Why is this important? Because Fedora's ultimate goal over the past few years has been to allow proven non-Red Hat contributors to have greater influence and access to the Fedora Project. From the technical side, this goal has been pushed forward by the Fedora Extras project and the Fedora Infrastructure projects, especially. One of the Fedora Project's success metrics is building and running itself in a way such that no single entity can completely control Fedora's fate. Fedora 7 gets us there, insofar as there is no "secret sauce" in the ability to spin a Fedora distribution. Nothing is hidden. Balanced against these goals of increased openness has been the need to create systems and infrastructure that continue to allow RHEL or other Red Hat (not Fedora) branded products to be built and to be more firmly controlled by Red Hat than Fedora is. Fedora serves as an upstream for various Red Hat products, and Fedora has a responsibility to provide a good "service" to those downstream "customers".

(2) Custom spins of Fedora. The primary consequence of (1) is that customized versions of Fedora are now possible to an extent that was not available previously. User-generated Fedora, if you're looking for a buzzword. :-P
Think about some of the possibilities:

+ People in various countries *directly* managing localized spins of Fedora, customized both for language requirements and bandwidth requirements. + "Competing" spins of the Fedora Desktop, or server-ready package sets, allowing the best ones to gain popularity and be shared. + The ability for a business or a university that uses Fedora to take their own third-party RPMs and create a Fedora-derived distribution that integrates them at build time.

(3) Live CD, DVD, and USB technology. A Fedora spin can be loaded onto various forms of bootable media, which allows users to run their OS without hard disk installation, and gives users the ability to launch the installer with a simple double click. As with what is written above, the tools used for this are all free software, and therefore everything in this space is also fully customizable by users.

(4) Kernel-based Virtual Machine (KVM) technology has been integrated with the Fedora graphical virtualization manager tool. KVM provides a full virtualization solution, and users have a choice between KVM and Xen, along with Qemu, in this release.

(5) The usual set of upstream changes and improvements that are a part of any Fedora release.

RED HAT SUMMIT

Fedora will have a good presence at the Red Hat Summit, beginning on May 9th. There are four talks specifically about Fedora -- a general Q&A, a talk about Fedora Infrastructure, a talk about building custom versions of Fedora, and a talk about the Live CD technology. Furthermore, there will be a Fedora booth in the main area, where Fedora folks will be able to have general conversations with folks. If you are looking for me, this is a good place to start! Additionally, we will be able to give folks who attend the Summit a Fedora 7 Preview Live DVD. This is a special spin of Fedora that we did for the RH Summit, with custom artwork, Firefox start page, and various other goodies. We'll also have some of the bootable USB keys around for demonstration purposes, as well as demonstrations going on showing folks how to build custom spins of Fedora. I'm putting the finishing touches on this note on a plane somewhere between Raleigh and San Diego. :-)

FEDORA 7 LAUNCH

As stated earlier, the Fedora 7 release date is May 24th. This is one week before LinuxTag 2007 in Berlin, which we are using as the "European Launch" of Fedora 7. Our LinuxTag presence is being organized by Gerold Kassube, one of our Fedora Ambassadors. The community of folks in Europe who care deeply about Fedora is definitely a bright spot. For those of you in Europe who will attend LinuxTag, I look forward to seeing you there.
--
Max Spevack
+ http://fedoraproject.org/wiki/MaxSpevack
+ gpg key -- http://spevack.org/max.asc
+ fingerprint -- CD52 5E72 369B B00D 9E9A 773E 2FDB CB46 5A17 CF21

星期三, 五月 09, 2007

OpenSSL协议实现分析


1. 应用程序接口
1.1 SSL初始化
SSL_CTX* InitSSL(int server, char *cert, char *key, char *pw)
{
SSL_CTX* ctx;
SSL_METHOD *meth;
int status;
// 算法初始化
// 加载SSL错误信息
SSL_load_error_strings();
// 添加SSL的加密/HASH算法
SSLeay_add_ssl_algorithms();
// 服务器还是客户端
If(server)
meth = SSLv23_server_method();
else
meth = SSLv23_client_method();
// 建立新的SSL上下文
ctx = SSL_CTX_new (meth);
if(!ctx) return NULL;
// 设置证书文件的口令
SSL_CTX_set_default_passwd_cb_userdata(ctx, pw);
//加载本地证书文件
status=SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_ASN1);
if (status <= 0) {
frintf(stderr, "Use cert fail, status=%dn", status);
goto bad;
}
// 加载私钥文件
if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) <= 0) {
fprintf(stderr, "Use private key failn");
goto bad;
}
// 检查证书和私钥是否匹配
if (!SSL_CTX_check_private_key(ctx)) {
fprintf("Private key does not match the certificate public keyn");
goto bad;
}
fprintf("Cert and key OKn");
return ctx;
bad:
SSL_CTX_free (ctx);
return NULL;
}
1.2 建立SSL新连接
服务器:
// 建立SSL
ssl = SSL_new (ctx);
// 将SSL与TCP socket连接
SSL_set_fd (ssl, sd);
//接受新SSL连接
err = SSL_accept (ssl);
客户端:
// 建立SSL
ssl = SSL_new (ctx);
// 将SSL与TCP socket连接
SSL_set_fd (ssl, sd);
// SSL连接
err = SSL_connect (ssl);

服务器的SSL_accept()和客户端的SSL_connect()函数共同完成SSL的握手协商过程。
1.3 SSL通信
和普通的read()/write()调用一样,用下面的函数完成数据的SSL发送和接收,函数输入数据是明文,SSL自动将数据封装进SSL中:
读/接收:SSL_read()
写/发送:SSL_write()
1.4 SSL释放
SSL释放很简单:
SSL_free (ssl);
2. SSL实现分析
以下SSL源代码取自openssl-0.9.7b。

2.1 SSL_load_error_strings
该函数加载错误字符串信息:
void SSL_load_error_strings(void)
{
#ifndef OPENSSL_NO_ERR
ERR_load_crypto_strings();
ERR_load_SSL_strings();
#endif
}
最后将会进入函数:
static void err_load_strings(int lib, ERR_STRING_DATA *str)
{
while (str->error)
{
str->error|=ERR_PACK(lib,0,0);
ERRFN(err_set_item)(str);
str++;
}
}
其中:
#define ERR_PACK(l,f,r) (((((unsigned long)l)&0xffL)*0x1000000)|
((((unsigned long)f)&0xfffL)*0x1000)|
((((unsigned long)r)&0xfffL)))
#define ERRFN(a) err_fns->cb_##a
ERRFN(err_set_item)(str)的实际函数实现为:
static ERR_STRING_DATA *int_err_set_item(ERR_STRING_DATA *d)
{
ERR_STRING_DATA *p;
LHASH *hash;
err_fns_check();
hash = ERRFN(err_get)(1);
if (!hash)
return NULL;
CRYPTO_w_lock(CRYPTO_LOCK_ERR);
p = (ERR_STRING_DATA *)lh_insert(hash, d);
CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
return p;
}
Lh_insert()将错误信息插入到一个链表中
如关于加密算法的错误信息:
/* crypto/err/err.c */
static ERR_STRING_DATA ERR_str_functs[]=
……
static ERR_STRING_DATA ERR_str_libraries[]=
……
static ERR_STRING_DATA ERR_str_reasons[]=
……
2.2 SSLeay_add_ssl_algorithms()
这实际是个宏:
#define OpenSSL_add_ssl_algorithms() SSL_library_init()
#define SSLeay_add_ssl_algorithms() SSL_library_init()
实际函数为SSL_library_init(),函数比较简单,就是加载各种加密和HASH算法:
/* ssl/ssl_algs.c */
int SSL_library_init(void)
{
#ifndef OPENSSL_NO_DES
EVP_add_cipher(EVP_des_cbc());
EVP_add_cipher(EVP_des_ede3_cbc());
#endif
#ifndef OPENSSL_NO_IDEA
EVP_add_cipher(EVP_idea_cbc());
#endif
#ifndef OPENSSL_NO_RC4
EVP_add_cipher(EVP_rc4());
#endif
#ifndef OPENSSL_NO_RC2
EVP_add_cipher(EVP_rc2_cbc());
#endif
#ifndef OPENSSL_NO_AES
EVP_add_cipher(EVP_aes_128_cbc());
EVP_add_cipher(EVP_aes_192_cbc());
EVP_add_cipher(EVP_aes_256_cbc());
#endif
#ifndef OPENSSL_NO_MD2
EVP_add_digest(EVP_md2());
#endif
#ifndef OPENSSL_NO_MD5
EVP_add_digest(EVP_md5());
EVP_add_digest_alias(SN_md5,"ssl2-md5");
EVP_add_digest_alias(SN_md5,"ssl3-md5");
#endif
#ifndef OPENSSL_NO_SHA
EVP_add_digest(EVP_sha1()); /* RSA with sha1 */
EVP_add_digest_alias(SN_sha1,"ssl3-sha1");
EVP_add_digest_alias(SN_sha1WithRSAEncryption,SN_sha1WithRSA);
#endif
#if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_DSA)
EVP_add_digest(EVP_dss1()); /* DSA with sha1 */
EVP_add_digest_alias(SN_dsaWithSHA1,SN_dsaWithSHA1_2);
EVP_add_digest_alias(SN_dsaWithSHA1,"DSS1");
EVP_add_digest_alias(SN_dsaWithSHA1,"dss1");
#endif
/* If you want support for phased out ciphers, add the following */
#if 0
EVP_add_digest(EVP_sha());
EVP_add_digest(EVP_dss());
#endif
return(1);
}

2.3 SSL23_server_method()
建立服务器端的方法库,这是个通用函数,可动态选择SSL协议。如果想固定协议,可以只用SSLv2_server_method(), SSLv3_server_method() 等函数来初始化,该函数返回一个SSL_METHOD结构:
/* ssl/ssl.h */
/* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
typedef struct ssl_method_st
{
int version; // 版本号
int (*ssl_new)(SSL *s); // 建立新SSL
void (*ssl_clear)(SSL *s); // 清除SSL
void (*ssl_free)(SSL *s); // 释放SSL
int (*ssl_accept)(SSL *s); // 服务器接受SSL连接
int (*ssl_connect)(SSL *s); // 客户端的SSL连接
int (*ssl_read)(SSL *s,void *buf,int len); // SSL读
int (*ssl_peek)(SSL *s,void *buf,int len); // SSL查看数据
int (*ssl_write)(SSL *s,const void *buf,int len); // SSL写
int (*ssl_shutdown)(SSL *s); // SSL半关闭
int (*ssl_renegotiate)(SSL *s); // SSL重协商
int (*ssl_renegotiate_check)(SSL *s); // SSL重协商检查
long (*ssl_ctrl)(SSL *s,int cmd,long larg,void *parg); // SSL控制
long (*ssl_ctx_ctrl)(SSL_CTX *ctx,int cmd,long larg,void *parg); //SSL上下文控制
SSL_CIPHER *(*get_cipher_by_char)(const unsigned char *ptr); // 通过名称获取SSL的算法
int (*put_cipher_by_char)(const SSL_CIPHER *cipher,unsigned char *ptr);
int (*ssl_pending)(SSL *s);
int (*num_ciphers)(void); // 算法数
SSL_CIPHER *(*get_cipher)(unsigned ncipher); // 获取算法
struct ssl_method_st *(*get_ssl_method)(int version);
long (*get_timeout)(void); // 超时
struct ssl3_enc_method *ssl3_enc; /* Extra SSLv3/TLS stuff */ // SSL3加密
int (*ssl_version)(); // SSL版本
long (*ssl_callback_ctrl)(SSL *s, int cb_id, void (*fp)()); // SSL控制回调函数
long (*ssl_ctx_callback_ctrl)(SSL_CTX *s, int cb_id, void (*fp)()); //SSL上下文控制回调函数
} SSL_METHOD;

/* ssl/s23_srvr.c */
SSL_METHOD *SSLv23_server_method(void)
{
static int init=1;
// 静态量,每个进程只初始化一次
static SSL_METHOD SSLv23_server_data;
if (init)
{
CRYPTO_w_lock(CRYPTO_LOCK_SSL_METHOD);
if (init)
{
// ssl23的基本方法结构
memcpy((char *)&SSLv23_server_data,
(char *)sslv23_base_method(),sizeof(SSL_METHOD));
// 服务器,所以要定义accept方法
SSLv23_server_data.ssl_accept=ssl23_accept;
// 根据SSL的版本设置SSL的具体方法函数
SSLv23_server_data.get_ssl_method=ssl23_get_server_method;
init=0;
}
CRYPTO_w_unlock(CRYPTO_LOCK_SSL_METHOD);
}
return(&SSLv23_server_data);
}

static SSL_METHOD *ssl23_get_server_method(int ver)
{
#ifndef OPENSSL_NO_SSL2
if (ver == SSL2_VERSION)
return(SSLv2_server_method());
#endif
if (ver == SSL3_VERSION)
return(SSLv3_server_method());
else if (ver == TLS1_VERSION)
return(TLSv1_server_method());
// 随着TLS1.1(RFC4346)的推出,估计不久将出现TLSv1_1_server_method()
else
return(NULL);
}
// SSL23的方法基本数据定义
/* ssl/s23_lib.c */
SSL_METHOD *sslv23_base_method(void)
{
return(&SSLv23_data);
}
static SSL_METHOD SSLv23_data= {
TLS1_VERSION,
tls1_new,
tls1_clear,
tls1_free,
ssl_undefined_function,
ssl_undefined_function,
ssl23_read,
ssl23_peek,
ssl23_write,
ssl_undefined_function,
ssl_undefined_function,
ssl_ok,
ssl3_ctrl,
ssl3_ctx_ctrl,
ssl23_get_cipher_by_char,
ssl23_put_cipher_by_char,
ssl_undefined_function,
ssl23_num_ciphers,
ssl23_get_cipher,
ssl_bad_method,
ssl23_default_timeout,
&ssl3_undef_enc_method,
ssl_undefined_function,
ssl3_callback_ctrl,
ssl3_ctx_callback_ctrl,
};
以SSL3的服务器方法函数为例,其他方法类似:
/* ssl/s3_srvr.c */
SSL_METHOD *SSLv3_server_method(void)
{
static int init=1;
static SSL_METHOD SSLv3_server_data;
// 只初始化一次
if (init)
{
CRYPTO_w_lock(CRYPTO_LOCK_SSL_METHOD);
if (init)
{
// ssl3的基本方法结构
memcpy((char *)&SSLv3_server_data,(char *)sslv3_base_method(),
sizeof(SSL_METHOD));
// ssl3的接受方法
SSLv3_server_data.ssl_accept=ssl3_accept;
// ssl3获取服务器的方法函数
SSLv3_server_data.get_ssl_method=ssl3_get_server_method;
init=0;
}

CRYPTO_w_unlock(CRYPTO_LOCK_SSL_METHOD);
}
return(&SSLv3_server_data);
}
// SSL3的方法基本数据定义
/* ssl/s3_lib.c */
static SSL_METHOD SSLv3_data= {
SSL3_VERSION,
ssl3_new,
ssl3_clear,
ssl3_free,
ssl_undefined_function,
ssl_undefined_function,
ssl3_read,
ssl3_peek,
ssl3_write,
ssl3_shutdown,
ssl3_renegotiate,
ssl3_renegotiate_check,
ssl3_ctrl,
ssl3_ctx_ctrl,
ssl3_get_cipher_by_char,
ssl3_put_cipher_by_char,
ssl3_pending,
ssl3_num_ciphers,
ssl3_get_cipher,
ssl_bad_method,
ssl3_default_timeout,
&SSLv3_enc_data,
ssl_undefined_function,
ssl3_callback_ctrl,
ssl3_ctx_callback_ctrl,
};
2.4 SSL23_client_method()

和服务器端的其实是相同的,只是不定义结构中的ssl_accept而是定义ssl_connnect:
SSL_METHOD *SSLv23_client_method(void)
{
static int init=1;
static SSL_METHOD SSLv23_client_data;
if (init)
{
CRYPTO_w_lock(CRYPTO_LOCK_SSL_METHOD);
if (init)
{
memcpy((char *)&SSLv23_client_data,
(char *)sslv23_base_method(),sizeof(SSL_METHOD));
SSLv23_client_data.ssl_connect=ssl23_connect;
SSLv23_client_data.get_ssl_method=ssl23_get_client_method;
init=0;
}
CRYPTO_w_unlock(CRYPTO_LOCK_SSL_METHOD);
}
return(&SSLv23_client_data);
}
2.5 SSL_CTX_new ()
该函数根据SSL方法获取一个SSL上下文结构,该结构定义为:
/* ssl/ssl.h */
struct ssl_ctx_st
{
SSL_METHOD *method;
STACK_OF(SSL_CIPHER) *cipher_list;
/* same as above but sorted for lookup */
STACK_OF(SSL_CIPHER) *cipher_list_by_id;
struct x509_store_st /* X509_STORE */ *cert_store;
struct lhash_st /* LHASH */ *sessions; /* a set of SSL_SESSIONs */
/* Most session-ids that will be cached, default is
* SSL_SESSION_CACHE_MAX_SIZE_DEFAULT. 0 is unlimited. */
unsigned long session_cache_size;
struct ssl_session_st *session_cache_head;
struct ssl_session_st *session_cache_tail;
/* This can have one of 2 values, ored together,
* SSL_SESS_CACHE_CLIENT,
* SSL_SESS_CACHE_SERVER,
* Default is SSL_SESSION_CACHE_SERVER, which means only
* SSL_accept which cache SSL_SESSIONS. */
int session_cache_mode;
/* If timeout is not 0, it is the default timeout value set
* when SSL_new() is called. This has been put in to make
* life easier to set things up */
long session_timeout;
/* If this callback is not null, it will be called each
* time a session id is added to the cache. If this function
* returns 1, it means that the callback will do a
* SSL_SESSION_free() when it has finished using it. Otherwise,
* on 0, it means the callback has finished with it.
* If remove_session_cb is not null, it will be called when
* a session-id is removed from the cache. After the call,
* OpenSSL will SSL_SESSION_free() it. */
int (*new_session_cb)(struct ssl_st *ssl,SSL_SESSION *sess);
void (*remove_session_cb)(struct ssl_ctx_st *ctx,SSL_SESSION *sess);
SSL_SESSION *(*get_session_cb)(struct ssl_st *ssl,
unsigned char *data,int len,int *copy);
struct
{
int sess_connect; /* SSL new conn - started */
int sess_connect_renegotiate;/* SSL reneg - requested */
int sess_connect_good; /* SSL new conne/reneg - finished */
int sess_accept; /* SSL new accept - started */
int sess_accept_renegotiate;/* SSL reneg - requested */
int sess_accept_good; /* SSL accept/reneg - finished */
int sess_miss; /* session lookup misses */
int sess_timeout; /* reuse attempt on timeouted session */
int sess_cache_full; /* session removed due to full cache */
int sess_hit; /* session reuse actually done */
int sess_cb_hit; /* session-id that was not
* in the cache was
* passed back via the callback. This
* indicates that the application is
* supplying session-id's from other
* processes - spooky :-) */
} stats;
int references;
/* if defined, these override the X509_verify_cert() calls */
int (*app_verify_callback)(X509_STORE_CTX *, void *);
void *app_verify_arg;
/* before OpenSSL 0.9.7, 'app_verify_arg' was ignored
* ('app_verify_callback' was called with just one argument) */
/* Default password callback. */
pem_password_cb *default_passwd_callback;
/* Default password callback user data. */
void *default_passwd_callback_userdata;
/* get client cert callback */
int (*client_cert_cb)(SSL *ssl, X509 **x509, EVP_PKEY **pkey);
CRYPTO_EX_DATA ex_data;
const EVP_MD *rsa_md5;/* For SSLv2 - name is 'ssl2-md5' */
const EVP_MD *md5; /* For SSLv3/TLSv1 'ssl3-md5' */
const EVP_MD *sha1; /* For SSLv3/TLSv1 'ssl3->sha1' */
STACK_OF(X509) *extra_certs;
STACK_OF(SSL_COMP) *comp_methods; /* stack of SSL_COMP, SSLv3/TLSv1 */

/* Default values used when no per-SSL value is defined follow */
void (*info_callback)(const SSL *ssl,int type,int val); /* used if SSL's info_callback is NULL */
/* what we put in client cert requests */
STACK_OF(X509_NAME) *client_CA;

/* Default values to use in SSL structures follow (these are copied by SSL_new) */
unsigned long options;
unsigned long mode;
long max_cert_list;
struct cert_st /* CERT */ *cert;
int read_ahead;
/* callback that allows applications to peek at protocol messages */
void (*msg_callback)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg);
void *msg_callback_arg;
int verify_mode;
int verify_depth;
unsigned int sid_ctx_length;
unsigned char sid_ctx[SSL_MAX_SID_CTX_LENGTH];
int (*default_verify_callback)(int ok,X509_STORE_CTX *ctx); /* called 'verify_callback' in the SSL */
/* Default generate session ID callback. */
GEN_SESSION_CB generate_session_id;
int purpose; /* Purpose setting */
int trust; /* Trust setting */
int quiet_shutdown;
};

typedef struct ssl_ctx_st SSL_CTX;
/* ssl/ssl_lib.h */
SSL_CTX *SSL_CTX_new(SSL_METHOD *meth)
{
SSL_CTX *ret=NULL;

if (meth == NULL)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_NULL_SSL_METHOD_PASSED);
return(NULL);
}
if (SSL_get_ex_data_X509_STORE_CTX_idx() < 0)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_X509_VERIFICATION_SETUP_PROBLEMS);
goto err;
}
// 分配上下文的内存空间
ret=(SSL_CTX *)OPENSSL_malloc(sizeof(SSL_CTX));
if (ret == NULL)
goto err;
memset(ret,0,sizeof(SSL_CTX));
// 初始化上下文的结构参数
ret->method=meth;
ret->cert_store=NULL;
ret->session_cache_mode=SSL_SESS_CACHE_SERVER;
ret->session_cache_size=SSL_SESSION_CACHE_MAX_SIZE_DEFAULT;
ret->session_cache_head=NULL;
ret->session_cache_tail=NULL;
/* We take the system default */
ret->session_timeout=meth->get_timeout();
ret->new_session_cb=0;
ret->remove_session_cb=0;
ret->get_session_cb=0;
ret->generate_session_id=0;
memset((char *)&ret->stats,0,sizeof(ret->stats));
ret->references=1;
ret->quiet_shutdown=0;
/* ret->cipher=NULL;*/
/* ret->s2->challenge=NULL;
ret->master_key=NULL;
ret->key_arg=NULL;
ret->s2->conn_id=NULL; */
ret->info_callback=NULL;
ret->app_verify_callback=0;
ret->app_verify_arg=NULL;
ret->max_cert_list=SSL_MAX_CERT_LIST_DEFAULT;
ret->read_ahead=0;
ret->msg_callback=0;
ret->msg_callback_arg=NULL;
ret->verify_mode=SSL_VERIFY_NONE;
ret->verify_depth=-1; /* Don't impose a limit (but x509_lu.c does) */
ret->sid_ctx_length=0;
ret->default_verify_callback=NULL;
if ((ret->cert=ssl_cert_new()) == NULL)
goto err;
ret->default_passwd_callback=0;
ret->default_passwd_callback_userdata=NULL;
ret->client_cert_cb=0;
ret->sessions=lh_new(LHASH_HASH_FN(SSL_SESSION_hash),
LHASH_COMP_FN(SSL_SESSION_cmp));
if (ret->sessions == NULL) goto err;
ret->cert_store=X509_STORE_new();
if (ret->cert_store == NULL) goto err;
// 建立加密算法链表
ssl_create_cipher_list(ret->method,
&ret->cipher_list,&ret->cipher_list_by_id,
SSL_DEFAULT_CIPHER_LIST);
if (ret->cipher_list == NULL
|| sk_SSL_CIPHER_num(ret->cipher_list) <= 0)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_LIBRARY_HAS_NO_CIPHERS);
goto err2;
}
// 定义上下文结构中HASH算法
if ((ret->rsa_md5=EVP_get_digestbyname("ssl2-md5")) == NULL)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_UNABLE_TO_LOAD_SSL2_MD5_ROUTINES);
goto err2;
}
if ((ret->md5=EVP_get_digestbyname("ssl3-md5")) == NULL)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES);
goto err2;
}
if ((ret->sha1=EVP_get_digestbyname("ssl3-sha1")) == NULL)
{
SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES);
goto err2;
}
if ((ret->client_CA=sk_X509_NAME_new_null()) == NULL)
goto err;
CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_CTX, ret, &ret->ex_data);
ret->extra_certs=NULL;
// 压缩算法
ret->comp_methods=SSL_COMP_get_compression_methods();
return(ret);
err:
SSLerr(SSL_F_SSL_CTX_NEW,ERR_R_MALLOC_FAILURE);
err2:
if (ret != NULL) SSL_CTX_free(ret);
return(NULL);
}
2.6 SSL_CTX_set_default_passwd_cb[_userdata]()

这个函数比较简单,就是设置SSL要加载的证书的口令,如果不设置的话加载证书时会出提示符要求输入口令的,这样在程序中使用就比较麻烦,该函数就是预先将口令保存,在读证书时自动使用。

实现该功能的有两个函数SSL_CTX_set_default_passwd_cb()和SSL_CTX_set_default_passwd_cb_userdata(),前者是定义一个口令回调函数,要获取口令时口令由该函数获取;后者是直接将口令设置好。

/* ssl/ssl_lib.c */
void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb)
{
ctx->default_passwd_callback=cb;
}
void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx,void *u)
{
ctx->default_passwd_callback_userdata=u;
}

举例:

static int
pass_cb(char *buf, int len, int verify, void *password)
{
snprintf(buf,len, "123456");
return strlen(buf);
}
SSL_CTX_set_default_passwd_cb(ctx, pass_cb);
等价于:
SSL_CTX_set_default_passwd_cb_userdata(ctx, "123456");
2.7 SSL_CTX_use_certificate_file()

该函数读取证书文件,证书文件通常都进行了加密保护。普及一下,证书文件里肯定是有公钥的,一般没私钥,某些情况会把私钥也包含进去,但那样作太不安全了,原则上私钥是永远不会给别人看到的,就算是进行了加密保护。
/* ssl/ssl_rsa.c */
int SSL_use_certificate_file(SSL *ssl, const char *file, int type)
{
int j;
BIO *in;
int ret=0;
X509 *x=NULL;
in=BIO_new(BIO_s_file_internal());
if (in == NULL)
{
SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,ERR_R_BUF_LIB);
goto end;
}
if (BIO_read_filename(in,file) <= 0)
{
SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,ERR_R_SYS_LIB);
goto end;
}
// 根据证书是PEM还是DER分别读取进行解码
// DER是二进制格式,PEM则是对DER用BASE64编码的后的文本格式
if (type == SSL_FILETYPE_ASN1)
{
j=ERR_R_ASN1_LIB;
x=d2i_X509_bio(in,NULL);
}
else if (type == SSL_FILETYPE_PEM)
{
j=ERR_R_PEM_LIB;
x=PEM_read_bio_X509(in,NULL,ssl->ctx->default_passwd_callback,ssl->ctx->default_passwd_callback_userdata);
}
else
{
SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,SSL_R_BAD_SSL_FILETYPE);
goto end;
}
if (x == NULL)
{
SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,j);
goto end;
}
// 加载解码后后的证书
ret=SSL_use_certificate(ssl,x);
end:
if (x != NULL) X509_free(x);
if (in != NULL) BIO_free(in);
return(ret);
}

2.8 SSL_CTX_use_PrivateKey_file()

该函数加载私钥文件,和SSL_CTX_use_certificate_file()是类似的,因为RSA算法的公钥私钥是对称的,刚生成密钥时谁作私钥都行。
SSL_CTX_use_PrivateKey_file()只加载PEM格式私钥,DER格式的用函数SSL_use_PrivateKey_ASN1()加载。

/* ssl/ssl_rsa.c */
int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type)
{
int j,ret=0;
BIO *in;
EVP_PKEY *pkey=NULL;
in=BIO_new(BIO_s_file_internal());
if (in == NULL)
{
SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,ERR_R_BUF_LIB);
goto end;
}
if (BIO_read_filename(in,file) <= 0)
{
SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,ERR_R_SYS_LIB);
goto end;
}
// 私钥只支持PEM格式
if (type == SSL_FILETYPE_PEM)
{
j=ERR_R_PEM_LIB;
pkey=PEM_read_bio_PrivateKey(in,NULL,
ssl->ctx->default_passwd_callback,ssl->ctx->default_passwd_callback_userdata);
}
else
{
SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,SSL_R_BAD_SSL_FILETYPE);
goto end;
}
if (pkey == NULL)
{
SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,j);
goto end;
}
// 加载私钥
ret=SSL_use_PrivateKey(ssl,pkey);
EVP_PKEY_free(pkey);
end:
if (in != NULL) BIO_free(in);
return(ret);
}

2.9 SSL_CTX_check_private_key()

该函数检查所用的公钥私钥是否是匹配的
int SSL_CTX_check_private_key(SSL_CTX *ctx)
{
if ( (ctx == NULL) ||
(ctx->cert == NULL) ||
(ctx->cert->key->x509 == NULL))
{
SSLerr(SSL_F_SSL_CTX_CHECK_PRIVATE_KEY,SSL_R_NO_CERTIFICATE_ASSIGNED);
return(0);
}
if (ctx->cert->key->privatekey == NULL)
{
SSLerr(SSL_F_SSL_CTX_CHECK_PRIVATE_KEY,SSL_R_NO_PRIVATE_KEY_ASSIGNED);
return(0);
}
// 这才是真正比较函数,在crypto/x509/x509_cmp.c中定义
return(X509_check_private_key(ctx->cert->key->x509, ctx->cert->key->privatekey));
}
2.10 SSL_new
该函数根据SSL_CTX实现一个SSL结构实例,SSL结构是个很复杂的结构,定义如下:

/* ssl/ssl.h */
typedef struct ssl_st SSL;
struct ssl_st
{
/* protocol version
* (one of SSL2_VERSION, SSL3_VERSION, TLS1_VERSION)
*/
int version;
int type; /* SSL_ST_CONNECT or SSL_ST_ACCEPT */
SSL_METHOD *method; /* SSLv3 */
/* There are 2 BIO's even though they are normally both the
* same. This is so data can be read and written to different
* handlers */
#ifndef OPENSSL_NO_BIO
BIO *rbio; /* used by SSL_read */
BIO *wbio; /* used by SSL_write */
BIO *bbio; /* used during session-id reuse to concatenate
* messages */
#else
char *rbio; /* used by SSL_read */
char *wbio; /* used by SSL_write */
char *bbio;
#endif
/* This holds a variable that indicates what we were doing
* when a 0 or -1 is returned. This is needed for
* non-blocking IO so we know what request needs re-doing when
* in SSL_accept or SSL_connect */
int rwstate;
/* true when we are actually in SSL_accept() or SSL_connect() */
int in_handshake;
int (*handshake_func)();
/* Imagine that here's a boolean member "init" that is
* switched as soon as SSL_set_{accept/connect}_state
* is called for the first time, so that "state" and
* "handshake_func" are properly initialized. But as
* handshake_func is == 0 until then, we use this
* test instead of an "init" member.
*/
int server; /* are we the server side? - mostly used by SSL_clear*/
int new_session;/* 1 if we are to use a new session.
* 2 if we are a server and are inside a handshake
* (i.e. not just sending a HelloRequest)
* NB: For servers, the 'new' session may actually be a previously
* cached session or even the previous session unless
* SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION is set */
int quiet_shutdown;/* don't send shutdown packets */
int shutdown; /* we have shut things down, 0x01 sent, 0x02
* for received */
int state; /* where we are */
int rstate; /* where we are when reading */
BUF_MEM *init_buf; /* buffer used during init */
void *init_msg; /* pointer to handshake message body, set by ssl3_get_message() */
int init_num; /* amount read/written */
int init_off; /* amount read/written */
/* used internally to point at a raw packet */
unsigned char *packet;
unsigned int packet_length;
struct ssl2_state_st *s2; /* SSLv2 variables */
struct ssl3_state_st *s3; /* SSLv3 variables */
int read_ahead; /* Read as many input bytes as possible
* (for non-blocking reads) */
/* callback that allows applications to peek at protocol messages */
void (*msg_callback)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg);
void *msg_callback_arg;
int hit; /* reusing a previous session */
int purpose; /* Purpose setting */
int trust; /* Trust setting */
/* crypto */
STACK_OF(SSL_CIPHER) *cipher_list;
STACK_OF(SSL_CIPHER) *cipher_list_by_id;
/* These are the ones being used, the ones in SSL_SESSION are
* the ones to be 'copied' into these ones */
EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */
const EVP_MD *read_hash; /* used for mac generation */
#ifndef OPENSSL_NO_COMP
COMP_CTX *expand; /* uncompress */
#else
char *expand;
#endif
EVP_CIPHER_CTX *enc_write_ctx; /* cryptographic state */
const EVP_MD *write_hash; /* used for mac generation */
#ifndef OPENSSL_NO_COMP
COMP_CTX *compress; /* compression */
#else
char *compress;
#endif
/* session info */
/* client cert? */
/* This is used to hold the server certificate used */
struct cert_st /* CERT */ *cert;
/* the session_id_context is used to ensure sessions are only reused
* in the appropriate context */
unsigned int sid_ctx_length;
unsigned char sid_ctx[SSL_MAX_SID_CTX_LENGTH];
/* This can also be in the session once a session is established */
SSL_SESSION *session;
/* Default generate session ID callback. */
GEN_SESSION_CB generate_session_id;
/* Used in SSL2 and SSL3 */
int verify_mode; /* 0 don't care about verify failure.
* 1 fail if verify fails */
int verify_depth;
int (*verify_callback)(int ok,X509_STORE_CTX *ctx); /* fail if callback returns 0 */
void (*info_callback)(const SSL *ssl,int type,int val); /* optional informational callback */
int error; /* error bytes to be written */
int error_code; /* actual code */
#ifndef OPENSSL_NO_KRB5
KSSL_CTX *kssl_ctx; /* Kerberos 5 context */
#endif /* OPENSSL_NO_KRB5 */
SSL_CTX *ctx;
/* set this flag to 1 and a sleep(1) is put into all SSL_read()
* and SSL_write() calls, good for nbio debuging :-) */
int debug;
/* extra application data */
long verify_result;
CRYPTO_EX_DATA ex_data;
/* for server side, keep the list of CA_dn we can use */
STACK_OF(X509_NAME) *client_CA;
int references;
unsigned long options; /* protocol behaviour */
unsigned long mode; /* API behaviour */
long max_cert_list;
int first_packet;
int client_version; /* what was passed, used for
* SSLv3/TLS rollback check */
};

/* ssl/ssl_lib.c */
SSL *SSL_new(SSL_CTX *ctx)
{
SSL *s;
// 一些必要检查
if (ctx == NULL)
{
SSLerr(SSL_F_SSL_NEW,SSL_R_NULL_SSL_CTX);
return(NULL);
}
if (ctx->method == NULL)
{
SSLerr(SSL_F_SSL_NEW,SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION);
return(NULL);
}
// 分配SSL实例的空间
s=(SSL *)OPENSSL_malloc(sizeof(SSL));
if (s == NULL) goto err;
memset(s,0,sizeof(SSL));
// 初始化SSL结构参数
#ifndef OPENSSL_NO_KRB5
s->kssl_ctx = kssl_ctx_new();
#endif /* OPENSSL_NO_KRB5 */
s->options=ctx->options;
s->mode=ctx->mode;
s->max_cert_list=ctx->max_cert_list;
if (ctx->cert != NULL)
{
/* Earlier library versions used to copy the pointer to
* the CERT, not its contents; only when setting new
* parameters for the per-SSL copy, ssl_cert_new would be
* called (and the direct reference to the per-SSL_CTX
* settings would be lost, but those still were indirectly
* accessed for various purposes, and for that reason they
* used to be known as s->ctx->default_cert).
* Now we don't look at the SSL_CTX's CERT after having
* duplicated it once. */
s->cert = ssl_cert_dup(ctx->cert);
if (s->cert == NULL)
goto err;
}
else
s->cert=NULL; /* Cannot really happen (see SSL_CTX_new) */
s->read_ahead=ctx->read_ahead;
s->msg_callback=ctx->msg_callback;
s->msg_callback_arg=ctx->msg_callback_arg;
s->verify_mode=ctx->verify_mode;
s->verify_depth=ctx->verify_depth;
s->sid_ctx_length=ctx->sid_ctx_length;
OPENSSL_assert(s->sid_ctx_length <= sizeof s->sid_ctx);
memcpy(&s->sid_ctx,&ctx->sid_ctx,sizeof(s->sid_ctx));
s->verify_callback=ctx->default_verify_callback;
s->generate_session_id=ctx->generate_session_id;
s->purpose = ctx->purpose;
s->trust = ctx->trust;
s->quiet_shutdown=ctx->quiet_shutdown;
CRYPTO_add(&ctx->references,1,CRYPTO_LOCK_SSL_CTX);
s->ctx=ctx;
s->verify_result=X509_V_OK;
s->method=ctx->method;
if (!s->method->ssl_new(s))
goto err;
s->references=1;
s->server=(ctx->method->ssl_accept == ssl_undefined_function)?0:1;
SSL_clear(s);
CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL, s, &s->ex_data);
return(s);
err:
if (s != NULL)
{
if (s->cert != NULL)
ssl_cert_free(s->cert);
if (s->ctx != NULL)
SSL_CTX_free(s->ctx); /* decrement reference count */
OPENSSL_free(s);
}
SSLerr(SSL_F_SSL_NEW,ERR_R_MALLOC_FAILURE);
return(NULL);
}

2.11 SSL_set_fd

SSL_set_fd()函数将建立的SSL结构与TCP套接字联系,使SSL结构对套接字中的TCP数据进行SSL封装。
/* ssl/ssl_lib.c */
int SSL_set_fd(SSL *s,int fd)
{
int ret=0;
BIO *bio=NULL;
// 建立一个BIO,BIO是OpenSSL提供的用来进行算法封装的处理结构,还可以将多个算法
// 串联起来,这样可以很方便地实现数据的封装
bio=BIO_new(BIO_s_socket());
if (bio == NULL)
{
SSLerr(SSL_F_SSL_SET_FD,ERR_R_BUF_LIB);
goto err;
}
// 把套接字和BIO联系
BIO_set_fd(bio,fd,BIO_NOCLOSE);
// 把SSL和BIO联系起来,包括读写操作
SSL_set_bio(s,bio,bio);
ret=1;
err:
return(ret);
}

void SSL_set_bio(SSL *s,BIO *rbio,BIO *wbio)
{
/* If the output buffering BIO is still in place, remove it
*/
if (s->bbio != NULL)
{
if (s->wbio == s->bbio)
{
s->wbio=s->wbio->next_bio;
s->bbio->next_bio=NULL;
}
}
if ((s->rbio != NULL) && (s->rbio != rbio))
BIO_free_all(s->rbio);
if ((s->wbio != NULL) && (s->wbio != wbio) && (s->rbio != s->wbio))
BIO_free_all(s->wbio);
// 设置SSL读写BIO
s->rbio=rbio;
s->wbio=wbio;
}

SSL_set_fd()还有两个类似函数:
SSL_set_wfd():对写的数据进行SSL封装
SSL_set_rfd():对都的数据进行SSL封装
不过一般情况下用得比较少。
2.12 SSL_accept
SSL_accept()函数完成SSL协商的服务器端操作:
/* ssl/ssl_lib.c */
int SSL_accept(SSL *s)
{
if (s->handshake_func == 0)
/* Not properly initialized yet */
SSL_set_accept_state(s);
return(s->method->ssl_accept(s));
}
其中SSL_set_accept_state(s)函数初始化SSL协商处理:
void SSL_set_accept_state(SSL *s)
{
// 服务器端
s->server=1;
s->shutdown=0;
// 初始化服务器端状态值
s->state=SSL_ST_ACCEPT|SSL_ST_BEFORE;
// 握手函数即是ssl_accept函数
s->handshake_func=s->method->ssl_accept;
/* clear the current cipher */
// 清除SSL读写加密算法上下文
ssl_clear_cipher_ctx(s);
}
因此最重要的就是ssl_accept()这个成员函数,是前面SSLv[2][3]_server_method()中定义的,如对于 SSLv23方法,处理函数分别为ssl23_accept()函数,其它SSLv2和SSLv3方法分别对应ssl2_accept()和 ssl3_accept(),后两者就没有协商过程了,ssl23_accept()实际在协商确定协议版本后也是调用ssl2[3]_accept ()。

SSL很多状态都分A,B两种,A状态表示刚进入该状态还没有收发数据,B状态表示进行的收发数据处理但还没完成善后操作。

/* ssl/s23_srvr.c */
int ssl23_accept(SSL *s)
{
BUF_MEM *buf;
unsigned long Time=time(NULL);
void (*cb)(const SSL *ssl,int type,int val)=NULL;
int ret= -1;
int new_state,state;
// 用当前时间作为随机种子
RAND_add(&Time,sizeof(Time),0);
ERR_clear_error();
clear_sys_error();
// 在SSL_new()函数中,s->info_callback并没有定义
// 是通过SSL_set_info_callback()函数单独定义的
if (s->info_callback != NULL)
cb=s->info_callback;
// SSL_CTX_new()函数中,ctx->info_callback也没定义
// 是通过SSL_CTX_set_info_callback()宏单独定义的
else if (s->ctx->info_callback != NULL)
cb=s->ctx->info_callback;
// 握手计数
s->in_handshake++;
// 如果SSL已用,清除SSL原来的值
if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s);
for (;;)
{
// 保存SSL当前状态
state=s->state;
// 在SSL_set_accept_state中s->state被初始化为SSL_ST_ACCEPT|SSL_ST_BEFORE
switch(s->state)
{
case SSL_ST_BEFORE:
case SSL_ST_ACCEPT:
case SSL_ST_BEFORE|SSL_ST_ACCEPT:
case SSL_ST_OK|SSL_ST_ACCEPT:
s->server=1;
if (cb != NULL) cb(s,SSL_CB_HANDSHAKE_START,1);
/* s->version=SSL3_VERSION; */
s->type=SSL_ST_ACCEPT;
if (s->init_buf == NULL)
{
// 生成一个SSL缓冲区
if ((buf=BUF_MEM_new()) == NULL)
{
ret= -1;
goto end;
}
if (!BUF_MEM_grow(buf,SSL3_RT_MAX_PLAIN_LENGTH))
{
ret= -1;
goto end;
}
s->init_buf=buf;
}
// 初始化认证码MAC
ssl3_init_finished_mac(s);
// SSL状态设置为SSL23_ST_SR_CLNT_HELLO_A,进入客户端的HELLO A状态
s->state=SSL23_ST_SR_CLNT_HELLO_A;
// 接受的SSL会话统计
s->ctx->stats.sess_accept++;
s->init_num=0;
// 重新进行循环接收客户端数据
break;
case SSL23_ST_SR_CLNT_HELLO_A:
case SSL23_ST_SR_CLNT_HELLO_B:
s->shutdown=0;
// 获取对方的HELLO信息,也就是进行SSL握手协议
ret=ssl23_get_client_hello(s);
if (ret >= 0) cb=NULL;
goto end;
/* break; */
default:
SSLerr(SSL_F_SSL23_ACCEPT,SSL_R_UNKNOWN_STATE);
ret= -1;
goto end;
/* break; */
}
// 如果SSL状态改变,而又定义了信息回调函数,执行之
if ((cb != NULL) && (s->state != state))
{
new_state=s->state;
s->state=state;
cb(s,SSL_CB_ACCEPT_LOOP,1);
s->state=new_state;
}
}
end:
s->in_handshake--;
if (cb != NULL)
cb(s,SSL_CB_ACCEPT_EXIT,ret);
return(ret);
}

可见,SSL握手协议是在ssl23_get_client_hello(s)函数中完成,也算个很复杂的函数:

int ssl23_get_client_hello(SSL *s)
{
//
// SSL握手协议头首部空间,11字节
// 客户端发出的HELLO,如果第一字节最高位为1
// 头两字节是包长度,不包括第一字节的第一位;
// 第3字节是握手类型类型,取值如下:
// enum {
// hello_request(0), client_hello(1), server_hello(2),
// certificate(11), server_key_exchange (12), certificate_request(13),
// server_done(14), certificate_verify(15), client_key_exchange(16),
// finished(20), (255)
// } HandshakeType;
// 第4,5字节是版本类型,TLS1为0301,SSL3为0300,SSL2为0002
// 第6,7字节是加密算法部分(cipher_specs)信息长度
// 第8,9字节是会话ID(session id)
// 第10,11字节是挑战信息长度(challenge)
//
//
// 如果第一字节最高位不为1或者非客户端发出的HELLO
// 第一字节为类型,取值为:
// enum {
// change_cipher_spec(20), alert(21), handshake(22),
// application_data(23), (255)
// } ContentType
// 第2,3字节是服务器端SSL版本类型,TLS1为0301,SSL3为0300,SSL2为0002
// 第4,5字节为握手部分长度
// 第6字节为消息类型
// 第7,8,9字节为握手信息长度
// 第10,11字节为客户端SSL版本
//
// 本函数的主要功能是识别客户端SSL版本,根据服务器自身支持的SSL版本,选定合适的SSL
// 版本进行下一步的accept,即ssl2_accept或ssl3_accept
//
char buf_space[11]; /* Request this many bytes in initial read.
* We can detect SSL 3.0/TLS 1.0 Client Hellos
* ('type == 3') correctly only when the following
* is in a single record, which is not guaranteed by
* the protocol specification:
* Byte Content
* 0 type
* 1/2 version > record header
* 3/4 length /
* 5 msg_type
* 6-8 length > Client Hello message
* 9/10 client_version /
*/
char *buf= &(buf_space[0]);
unsigned char *p,*d,*d_len,*dd;
unsigned int i;
unsigned int csl,sil,cl;
int n=0,j;
int type=0;
int v[2];
#ifndef OPENSSL_NO_RSA
int use_sslv2_strong=0;
#endif
if (s->state == SSL23_ST_SR_CLNT_HELLO_A)
{
/* read the initial header */
v[0]=v[1]=0;
if (!ssl3_setup_buffers(s)) goto err;
// 读取首部空间长度的数据
n=ssl23_read_bytes(s, sizeof buf_space);
if (n != sizeof buf_space) return(n); /* n == -1 || n == 0 */
// 数据保存在s->packet缓冲区中
p=s->packet;
// 拷贝到buf_space
memcpy(buf,p,n);
if ((p[0] & 0x80) && (p[2] == SSL2_MT_CLIENT_HELLO))
{
/*
* SSLv2 header
*/
if ((p[3] == 0x00) && (p[4] == 0x02))
{
// 客户端为SSLv2
v[0]=p[3]; v[1]=p[4];
/* SSLv2 */
if (!(s->options & SSL_OP_NO_SSLv2))
type=1;
}
else if (p[3] == SSL3_VERSION_MAJOR)
{
// 客户端主版本SSLv3
v[0]=p[3]; v[1]=p[4];
/* SSLv3/TLSv1 */
if (p[4] >= TLS1_VERSION_MINOR)
{
// 次版本表明是客户端TLS1.0, 服务器为SSL3或TLS1时type设为2,为SSL2时设为1
if (!(s->options & SSL_OP_NO_TLSv1))
{
// 服务器支持TLS1.0,SSL类型设置为TLS1
s->version=TLS1_VERSION;
/* type=2; */ /* done later to survive restarts */
s->state=SSL23_ST_SR_CLNT_HELLO_B;
}
else if (!(s->options & SSL_OP_NO_SSLv3))
{
// 服务器不支持TLS,支持SSL3,SSL类型设置为SSL3
s->version=SSL3_VERSION;
/* type=2; */
s->state=SSL23_ST_SR_CLNT_HELLO_B;
}
else if (!(s->options & SSL_OP_NO_SSLv2))
{
// 服务器这边不支持SSL3,TLS1,协商为SSL2, type为1
type=1;
}
}
else if (!(s->options & SSL_OP_NO_SSLv3))
{
// 次版本号表明客户端是SSLv3
s->version=SSL3_VERSION;
/* type=2; */
s->state=SSL23_ST_SR_CLNT_HELLO_B;
}
else if (!(s->options & SSL_OP_NO_SSLv2))
type=1;
}
}
else if ((p[0] == SSL3_RT_HANDSHAKE) &&
// p[1]为SSL3主版本号
(p[1] == SSL3_VERSION_MAJOR) &amp;&
// p[5]为消息类型
(p[5] == SSL3_MT_CLIENT_HELLO) &amp;&
// p[3],p[4]为握手部分长度,如果只是记录头部分,长度小于5,
((p[3] == 0 && p[4] < 5 /* silly record length? */)
// p[9]是客户端主版本号
|| (p[9] == p[1])))
{
/*
* SSLv3 or tls1 header
*/
// 主版本为SSL3
v[0]=p[1]; /* major version (= SSL3_VERSION_MAJOR) */
/* We must look at client_version inside the Client Hello message
* to get the correct minor version.
* However if we have only a pathologically small fragment of the
* Client Hello message, this would be difficult, and we'd have
* to read more records to find out.
* No known SSL 3.0 client fragments ClientHello like this,
* so we simply assume TLS 1.0 to avoid protocol version downgrade
* attacks. */
if (p[3] == 0 && p[4] < 6)
{
// 如果握手长度小于6认为就是TLS1
#if 0
SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,SSL_R_RECORD_TOO_SMALL);
goto err;
#else
v[1] = TLS1_VERSION_MINOR;
#endif
}
else
v[1]=p[10]; /* minor version according to client_version */
if (v[1] >= TLS1_VERSION_MINOR)
{
// 客户端为TLS1.0,按上面相同的方法设置服务器端的版本
// 注意这时的type设置为3
if (!(s->options & SSL_OP_NO_TLSv1))
{
s->version=TLS1_VERSION;
type=3;
}
else if (!(s->options & SSL_OP_NO_SSLv3))
{
s->version=SSL3_VERSION;
type=3;
}
}
else
{
/* client requests SSL 3.0 */
// 客户端为SSL3,设置服务器段SSL版本
// type为3
if (!(s->options & SSL_OP_NO_SSLv3))
{
s->version=SSL3_VERSION;
type=3;
}
else if (!(s->options & SSL_OP_NO_TLSv1))
{
/* we won't be able to use TLS of course,
* but this will send an appropriate alert */
s->version=TLS1_VERSION;
type=3;
}
}
}
else if ((strncmp("GET ", (char *)p,4) == 0) ||
(strncmp("POST ",(char *)p,5) == 0) ||
(strncmp("HEAD ",(char *)p,5) == 0) ||
(strncmp("PUT ", (char *)p,4) == 0))
{
// 在SSL通道中走HTTP的明文数据,出错
SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,SSL_R_HTTP_REQUEST);
goto err;
}
else if (strncmp("CONNECT",(char *)p,7) == 0)
{
SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,SSL_R_HTTPS_PROXY_REQUEST);
goto err;
}
}
// 进入HELLO B状态,也就是客户端数据是SSL3或TLS,而且(p[0] & 0x80) &&
// (p[2] == SSL2_MT_CLIENT_HELLO),已经找出服务器端的对应版本
if (s->state == SSL23_ST_SR_CLNT_HELLO_B)
{
/* we have SSLv3/TLSv1 in an SSLv2 header
* (other cases skip this state) */
// 服务器是SSL3或TLS1,类型为2
type=2;
p=s->packet;
v[0] = p[3]; /* == SSL3_VERSION_MAJOR */
v[1] = p[4];
// p[0],p[1]是HELLO包长
n=((p[0]&0x7f)<<8)|p[1];
if (n > (1024*4))
{
// 一个SSL段不能超过4096字节
SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,SSL_R_RECORD_TOO_LARGE);
goto err;
}
// 读取整个包长数据,"2"是因为p[0],p[1]表示包长不包括自身长度(2字节)
// 这个读操作数据初始指针是不移动的,注意前面已经用这函数读了11字节了
j=ssl23_read_bytes(s,n+2);
if (j <= 0) return(j);
// MAC认证
ssl3_finish_mac(s, s->packet+2, s->packet_length-2);
if (s->msg_callback)
s->msg_callback(0, SSL2_VERSION, 0, s->packet+2, s->packet_length-2, s, s->msg_callback_arg); /* CLIENT-HELLO */

// 回到数据头
p=s->packet;
// 跳过前面的5字节,长度、类型、版本信息
p+=5;
// cipher_specs的长度
n2s(p,csl);
// session id
n2s(p,sil);
// challenge长度
n2s(p,cl);
// SSL缓冲区头
d=(unsigned char *)s->init_buf->data;
if ((csl+sil+cl+11) != s->packet_length)
{
// 检查包长是否正确
SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,SSL_R_RECORD_LENGTH_MISMATCH);
goto err;
}
// 以下开始填充作为ssl3_accept定义的客户端SSL握手包
/* record header: msg_type ... */
// 数据类型
*(d++) = SSL3_MT_CLIENT_HELLO;
/* ... and length (actual value will be written later) */
d_len = d;
// 数据类型1字节,长度2字节
d += 3;
/* client_version */
// 版本号
*(d++) = SSL3_VERSION_MAJOR; /* == v[0] */
*(d++) = v[1];
/* lets populate the random area */
/* get the challenge_length */
// 拷贝挑战信息,最多SSL3_RANDOM_SIZE(32)
i=(cl > SSL3_RANDOM_SIZE)?SSL3_RANDOM_SIZE:cl;
memset(d,0,SSL3_RANDOM_SIZE);
// 如果挑战信息长度不到SSL3_RANDOM_SIZE,相当于前面多余字节补0,不是在后面
memcpy(&(d[SSL3_RANDOM_SIZE-i]),&(p[csl+sil]),i);
d+=SSL3_RANDOM_SIZE;
/* no session-id reuse */
// 会话ID没用
*(d++)=0;
/* ciphers */
// cipher_specs域
j=0;
// 头指针备份
dd=d;
// 留出长度空间
d+=2;
for (i=0; i<csl; i+=3)
{
// p[0]位置现在是收到包中cipher_specs数据头
if (p[i] != 0) continue;
// 每3字节为一个单位,拷贝后两字节,第1字节忽略
*(d++)=p[i+1];
*(d++)=p[i+2];
j+=2;
}
// 写cipher_specs长度,网络序
s2n(j,dd);
/* COMPRESSION */
*(d++)=1;
*(d++)=0;
// 实际数据长度
i = (d-(unsigned char *)s->init_buf->data) - 4;
l2n3((long)i, d_len);
/* get the data reused from the init_buf */
s->s3->tmp.reuse_message=1;
s->s3->tmp.message_type=SSL3_MT_CLIENT_HELLO;
s->s3->tmp.message_size=i;
}
/* imaginary new state (for program structure): */
/* s->state = SSL23_SR_CLNT_HELLO_C */
if (type == 1)
{
// 服务器只支持SSL2的情况,实际已经很少见了
#ifdef OPENSSL_NO_SSL2
SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,SSL_R_UNSUPPORTED_PROTOCOL);
goto err;
#else
/* we are talking sslv2 */
/* we need to clean up the SSLv3/TLSv1 setup and put in the
* sslv2 stuff. */
if (s->s2 == NULL)
{
// 新分配一个SSL2结构
if (!ssl2_new(s))
goto err;
}
else
ssl2_clear(s);
// 释放SSL3结构
if (s->s3 != NULL) ssl3_free(s);
// 将缓冲区扩到SSL2的最大记录情况
if (!BUF_MEM_grow_clean(s->init_buf,
SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER))
{
goto err;
}
// 这个状态是"SSL2_ST"系列(SSL2服务器端)的
s->state=SSL2_ST_GET_CLIENT_HELLO_A;
if ((s->options & SSL_OP_MSIE_SSLV2_RSA_PADDING) ||
use_sslv2_strong ||
(s->options & SSL_OP_NO_TLSv1 &amp;& s->options & SSL_OP_NO_SSLv3))
s->s2->ssl2_rollback=0;
else
/* reject SSL 2.0 session if client supports SSL 3.0 or TLS 1.0
* (SSL 3.0 draft/RFC 2246, App. E.2) */
s->s2->ssl2_rollback=1;
/* setup the n bytes we have read so we get them from
* the sslv2 buffer */
s->rstate=SSL_ST_READ_HEADER;
s->packet_length=n;
s->packet= &(s->s2->rbuf[0]);
// buf是接收数据缓冲区头,n正常的话是11
memcpy(s->packet,buf,n);
s->s2->rbuf_left=n;
s->s2->rbuf_offs=0;
// SSL封装方法是SSL2
s->method=SSLv2_server_method();
// 实际函数为ssl2_accept
s->handshake_func=s->method->ssl_accept;
#endif
}
if ((type == 2) || (type == 3))
{
// 服务器自身可以支持SSL3或TLS1
/* we have SSLv3/TLSv1 (type 2: SSL2 style, type 3: SSL3/TLS style) */
// 初始化写缓冲区
if (!ssl_init_wbio_buffer(s,1)) goto err;
/* we are in this state */
// SSL3_ST类,SSL3服务器收到客户端的HELLO的A状态
s->state=SSL3_ST_SR_CLNT_HELLO_A;
// 进行一些初始化操作
if (type == 3)
{
/* put the 'n' bytes we have read into the input buffer
* for SSLv3 */
s->rstate=SSL_ST_READ_HEADER;
s->packet_length=n;
s->packet= &(s->s3->rbuf.buf[0]);
memcpy(s->packet,buf,n);
s->s3->rbuf.left=n;
s->s3->rbuf.offset=0;
}
else
{
s->packet_length=0;
s->s3->rbuf.left=0;
s->s3->rbuf.offset=0;
}
if (s->version == TLS1_VERSION)
// 实际上TLS1中的accept方法也就是ssl3_accept
s->method = TLSv1_server_method();
else
// 就是ssl3_accept
s->method = SSLv3_server_method();
s->handshake_func=s->method->ssl_accept;
}

if ((type < 1) || (type > 3))
{
/* bad, very bad */
SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,SSL_R_UNKNOWN_PROTOCOL);
goto err;
}
s->init_num=0;
if (buf != buf_space) OPENSSL_free(buf);
s->first_packet=1;
// 递归调用SSL_accept(),这时方法是固定的,就是调用ssl2_accept()或ssl3_accept()
return(SSL_accept(s));
err:
if (buf != buf_space) OPENSSL_free(buf);
return(-1);
}
ssl23_get_client_hello()函数最后就是确定了服务器端的方法类型,然后再进行SSL_accept(),实际就是调用ssl2_accept()或ssl3_accept()。

举例ssl3_accept()函数定义如下,ssl2_accept()就不分析了:
/* ssl/s3_srvr.c */
int ssl3_accept(SSL *s)
{
BUF_MEM *buf;
unsigned long l,Time=time(NULL);
void (*cb)(const SSL *ssl,int type,int val)=NULL;
long num1;
int ret= -1;
int new_state,state,skip=0;
// 和前面ssl23_accpet一样进行初始化
RAND_add(&Time,sizeof(Time),0);
ERR_clear_error();
clear_sys_error();
if (s->info_callback != NULL)
cb=s->info_callback;
else if (s->ctx->info_callback != NULL)
cb=s->ctx->info_callback;
/* init things to blank */
s->in_handshake++;
if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s);
if (s->cert == NULL)
{
SSLerr(SSL_F_SSL3_ACCEPT,SSL_R_NO_CERTIFICATE_SET);
return(-1);
}
for (;;)
{
state=s->state;
switch (s->state)
{
case SSL_ST_RENEGOTIATE:
s->new_session=1;
/* s->state=SSL_ST_ACCEPT; */
case SSL_ST_BEFORE:
case SSL_ST_ACCEPT:
case SSL_ST_BEFORE|SSL_ST_ACCEPT:
case SSL_ST_OK|SSL_ST_ACCEPT:
// 这些是客户端服务器固定就用SSL3进行连接时进入的初始状态,如果是从ssl23_accpet
// 过来的是进不到这状态的
// 下面是ssl23_accept时类似的初始化
s->server=1;
if (cb != NULL) cb(s,SSL_CB_HANDSHAKE_START,1);
if ((s->version>>8) != 3)
{
SSLerr(SSL_F_SSL3_ACCEPT, ERR_R_INTERNAL_ERROR);
return -1;
}
s->type=SSL_ST_ACCEPT;
if (s->init_buf == NULL)
{
if ((buf=BUF_MEM_new()) == NULL)
{
ret= -1;
goto end;
}
if (!BUF_MEM_grow(buf,SSL3_RT_MAX_PLAIN_LENGTH))
{
ret= -1;
goto end;
}
s->init_buf=buf;
}
if (!ssl3_setup_buffers(s))
{
ret= -1;
goto end;
}
s->init_num=0;
if (s->state != SSL_ST_RENEGOTIATE)
{
/* Ok, we now need to push on a buffering BIO so that
* the output is sent in a way that TCP likes :-)
*/
if (!ssl_init_wbio_buffer(s,1)) { ret= -1; goto end; }

ssl3_init_finished_mac(s);
s->state=SSL3_ST_SR_CLNT_HELLO_A;
s->ctx->stats.sess_accept++;
}
else
{
/* s->state == SSL_ST_RENEGOTIATE,
* we will just send a HelloRequest */
s->ctx->stats.sess_accept_renegotiate++;
s->state=SSL3_ST_SW_HELLO_REQ_A;
}
break;
case SSL3_ST_SW_HELLO_REQ_A:
case SSL3_ST_SW_HELLO_REQ_B:
// 此状态是是写服务器端的回应的HELLO请求信息
s->shutdown=0;
// 发送服务器端的HELLO
ret=ssl3_send_hello_request(s);
if (ret <= 0) goto end;
// 转入REQ_C状态
s->s3->tmp.next_state=SSL3_ST_SW_HELLO_REQ_C;
s->state=SSL3_ST_SW_FLUSH;
s->init_num=0;
ssl3_init_finished_mac(s);
break;
case SSL3_ST_SW_HELLO_REQ_C:
s->state=SSL_ST_OK;
break;
// 从ssl23_accept过来时的状态是SSL3_ST_SR_CLNT_HELLO_A,属于读数据状态
case SSL3_ST_SR_CLNT_HELLO_A:
case SSL3_ST_SR_CLNT_HELLO_B:
case SSL3_ST_SR_CLNT_HELLO_C:
s->shutdown=0;
// 读取客户端数据,如果是ssl23_accept过来的话数据是由ssl23_get_client_hello()
// 函数自己构造的,而不是实际收到的
ret=ssl3_get_client_hello(s);
if (ret <= 0) goto end;
s->new_session = 2;
// 状态转为服务器准备写HELLO的A状态
s->state=SSL3_ST_SW_SRVR_HELLO_A;
s->init_num=0;
break;
case SSL3_ST_SW_SRVR_HELLO_A:
case SSL3_ST_SW_SRVR_HELLO_B:
// 此状态是是写服务器端的HELLO信息
ret=ssl3_send_server_hello(s);
if (ret <= 0) goto end;
// s->hit用来标志该ssl会话是否是重用(reuse)的,在ssl3_get_client_hello()函数
// 中检查客户端的hello信息后设置
if (s->hit)
// 如果会话是reuse的,状态为CHANGE
s->state=SSL3_ST_SW_CHANGE_A;
else
// 否则为新SSL会话,进入证书处理A状态
s->state=SSL3_ST_SW_CERT_A;
s->init_num=0;
break;
case SSL3_ST_SW_CERT_A:
case SSL3_ST_SW_CERT_B:
// 该状态下进行证书交换,用来计算连接共享密钥
/* Check if it is anon DH */
if (!(s->s3->tmp.new_cipher->algorithms & SSL_aNULL))
{
// 非NULL加密的话发送服务器端的证书
ret=ssl3_send_server_certificate(s);
if (ret <= 0) goto end;
}
else
skip=1;
// 进入密钥交换状态
s->state=SSL3_ST_SW_KEY_EXCH_A;
s->init_num=0;
break;
case SSL3_ST_SW_KEY_EXCH_A:
case SSL3_ST_SW_KEY_EXCH_B:
// 该状态下进行数据加密密钥的交换操作
// 算法类型,由一个常数表示
l=s->s3->tmp.new_cipher->algorithms;
/* clear this, it may get reset by
* send_server_key_exchange */
if ((s->options & SSL_OP_EPHEMERAL_RSA)
#ifndef OPENSSL_NO_KRB5
&amp;& !(l & SSL_KRB5)
#endif /* OPENSSL_NO_KRB5 */
)
// 临时性RSA密钥交换
/* option SSL_OP_EPHEMERAL_RSA sends temporary RSA key
* even when forbidden by protocol specs
* (handshake may fail as clients are not required to
* be able to handle this) */
s->s3->tmp.use_rsa_tmp=1;
else
s->s3->tmp.use_rsa_tmp=0;
/* only send if a DH key exchange, fortezza or
* RSA but we have a sign only certificate */
if (s->s3->tmp.use_rsa_tmp
|| (l & (SSL_DH|SSL_kFZA))
|| ((l & SSL_kRSA)
&& (s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey == NULL
|| (SSL_C_IS_EXPORT(s->s3->tmp.new_cipher)
&& EVP_PKEY_size(s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey)*8 > SSL_C_EXPORT_PKEYLENGTH(s->s3->tmp.new_cipher)
)
)
)
)
{
// 进行RSA密钥交换
ret=ssl3_send_server_key_exchange(s);
if (ret <= 0) goto end;
}
else
skip=1;
// 转入证书请求阶段
s->state=SSL3_ST_SW_CERT_REQ_A;
s->init_num=0;
break;
case SSL3_ST_SW_CERT_REQ_A:
case SSL3_ST_SW_CERT_REQ_B:
// 此阶段进入对方证书请求
if (/* don't request cert unless asked for it: */
!(s->verify_mode & SSL_VERIFY_PEER) ||
/* if SSL_VERIFY_CLIENT_ONCE is set,
* don't request cert during re-negotiation: */
((s->session->peer != NULL) &&
(s->verify_mode & SSL_VERIFY_CLIENT_ONCE)) ||
/* never request cert in anonymous ciphersuites
* (see section "Certificate request" in SSL 3 drafts
* and in RFC 2246): */
((s->s3->tmp.new_cipher->algorithms & SSL_aNULL) &amp;&
/* ... except when the application insists on verification
* (against the specs, but s3_clnt.c accepts this for SSL 3) */
!(s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) ||
/* never request cert in Kerberos ciphersuites */
(s->s3->tmp.new_cipher->algorithms & SSL_aKRB5))
{
// 在大多数情况下不需要客户端的证书
// 如果想认证对方,只要以上条件之一不满足就可以认证对方
// CTX的verify_mode则通过SSL_CTX_set_verify()来修改
// s->verify_mode可通过函数SSL_set_verify()来修改,
// s->verify_mode的初始值是ctx->verify_mode赋予的
/* no cert request */
skip=1;
s->s3->tmp.cert_request=0;
// 服务器端协商发送结束
s->state=SSL3_ST_SW_SRVR_DONE_A;
}
else
{
// 发送要获取对方证书的请求
s->s3->tmp.cert_request=1;
ret=ssl3_send_certificate_request(s);
if (ret <= 0) goto end;
#ifndef NETSCAPE_HANG_BUG
// 没预定义HANG_BUG的话服务器端协商写数据应该完成了
s->state=SSL3_ST_SW_SRVR_DONE_A;
#else
// 否则进入清除写缓冲状态
// 下一个状态是准备接收证书A
s->state=SSL3_ST_SW_FLUSH;
s->s3->tmp.next_state=SSL3_ST_SR_CERT_A;
#endif
s->init_num=0;
}
break;
case SSL3_ST_SW_SRVR_DONE_A:
case SSL3_ST_SW_SRVR_DONE_B:
// 发送服务器协商数据完成信息
ret=ssl3_send_server_done(s);
if (ret <= 0) goto end;
// 下一个状态将是接收证书A状态
s->s3->tmp.next_state=SSL3_ST_SR_CERT_A;
// 转入写缓冲清除状态
s->state=SSL3_ST_SW_FLUSH;
s->init_num=0;
break;

case SSL3_ST_SW_FLUSH:
// 清除写缓冲区
/* number of bytes to be flushed */
num1=BIO_ctrl(s->wbio,BIO_CTRL_INFO,0,NULL);
if (num1 > 0)
{
s->rwstate=SSL_WRITING;
num1=BIO_flush(s->wbio);
if (num1 <= 0) { ret= -1; goto end; }
s->rwstate=SSL_NOTHING;
}
// 进入预先保存的下一状态
s->state=s->s3->tmp.next_state;
break;
case SSL3_ST_SR_CERT_A:
case SSL3_ST_SR_CERT_B:
// 此状态下接收对方证书
/* Check for second client hello (MS SGC) */
// 检查对方的HELLO信息
ret = ssl3_check_client_hello(s);
if (ret <= 0)
goto end;
if (ret == 2)
s->state = SSL3_ST_SR_CLNT_HELLO_C;
else {
/* could be sent for a DH cert, even if we
* have not asked for it :-) */
// 获取对方证书
ret=ssl3_get_client_certificate(s);
if (ret <= 0) goto end;
s->init_num=0;
// 准备进入密钥交换状态
s->state=SSL3_ST_SR_KEY_EXCH_A;
}
break;
case SSL3_ST_SR_KEY_EXCH_A:
case SSL3_ST_SR_KEY_EXCH_B:
// 该状态处理密钥交换
ret=ssl3_get_client_key_exchange(s);
if (ret <= 0) goto end;
// 准备进入证书验证状态
s->state=SSL3_ST_SR_CERT_VRFY_A;
s->init_num=0;
/* We need to get hashes here so if there is
* a client cert, it can be verified */
// 验证证书的MAC码
s->method->ssl3_enc->cert_verify_mac(s,
&(s->s3->finish_dgst1),
&(s->s3->tmp.cert_verify_md[0]));
s->method->ssl3_enc->cert_verify_mac(s,
&(s->s3->finish_dgst2),
&(s->s3->tmp.cert_verify_md[MD5_DIGEST_LENGTH]));
break;
case SSL3_ST_SR_CERT_VRFY_A:
case SSL3_ST_SR_CERT_VRFY_B:
// 验证证书
/* we should decide if we expected this one */
ret=ssl3_get_cert_verify(s);
if (ret <= 0) goto end;
// 状态转为接收结束A状态
s->state=SSL3_ST_SR_FINISHED_A;
s->init_num=0;
break;
case SSL3_ST_SR_FINISHED_A:
case SSL3_ST_SR_FINISHED_B:
// 本状态为服务器端接收结束
// 获取结束信息
ret=ssl3_get_finished(s,SSL3_ST_SR_FINISHED_A,
SSL3_ST_SR_FINISHED_B);
if (ret <= 0) goto end;
if (s->hit)
// 如果会话是reuse的, 连接已经建立
s->state=SSL_ST_OK;
else
// 转CHANGE_A
s->state=SSL3_ST_SW_CHANGE_A;
s->init_num=0;
break;
case SSL3_ST_SW_CHANGE_A:
case SSL3_ST_SW_CHANGE_B:
// 本状态为服务器发送修改信息
// SSL加密算法
s->session->cipher=s->s3->tmp.new_cipher;
if (!s->method->ssl3_enc->setup_key_block(s))
{ ret= -1; goto end; }
// 发送修改加密算法信息
ret=ssl3_send_change_cipher_spec(s,
SSL3_ST_SW_CHANGE_A,SSL3_ST_SW_CHANGE_B);
if (ret <= 0) goto end;
// 转发送结束
s->state=SSL3_ST_SW_FINISHED_A;
s->init_num=0;
if (!s->method->ssl3_enc->change_cipher_state(s,
SSL3_CHANGE_CIPHER_SERVER_WRITE))
{
ret= -1;
goto end;
}
break;
case SSL3_ST_SW_FINISHED_A:
case SSL3_ST_SW_FINISHED_B:
// 服务器发送结束,SSL握手完成
ret=ssl3_send_finished(s,
SSL3_ST_SW_FINISHED_A,SSL3_ST_SW_FINISHED_B,
s->method->ssl3_enc->server_finished_label,
s->method->ssl3_enc->server_finished_label_len);
if (ret <= 0) goto end;
// 清除SSL写缓冲
s->state=SSL3_ST_SW_FLUSH;
if (s->hit)
// 如果会话是reuse的,状态转为接收结束
s->s3->tmp.next_state=SSL3_ST_SR_FINISHED_A;
else
// SSL连接成功完成
s->s3->tmp.next_state=SSL_ST_OK;
s->init_num=0;
break;
case SSL_ST_OK:
// 清除连接过程中分配的资源
/* clean a few things up */
ssl3_cleanup_key_block(s);
BUF_MEM_free(s->init_buf);
s->init_buf=NULL;
/* remove buffering on output */
ssl_free_wbio_buffer(s);
s->init_num=0;
if (s->new_session == 2) /* skipped if we just sent a HelloRequest */
{
/* actually not necessarily a 'new' session unless
* SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION is set */

s->new_session=0;

ssl_update_cache(s,SSL_SESS_CACHE_SERVER);

s->ctx->stats.sess_accept_good++;
/* s->server=1; */
s->handshake_func=ssl3_accept;
if (cb != NULL) cb(s,SSL_CB_HANDSHAKE_DONE,1);
}

ret = 1;
goto end;
/* break; */
default:
SSLerr(SSL_F_SSL3_ACCEPT,SSL_R_UNKNOWN_STATE);
ret= -1;
goto end;
/* break; */
}

if (!s->s3->tmp.reuse_message && !skip)
{
if (s->debug)
{
if ((ret=BIO_flush(s->wbio)) <= 0)
goto end;
}

if ((cb != NULL) && (s->state != state))
{
new_state=s->state;
s->state=state;
cb(s,SSL_CB_ACCEPT_LOOP,1);
s->state=new_state;
}
}
skip=0;
}
end:
/* BIO_flush(s->wbio); */
// accept结束, ret=1
s->in_handshake--;
if (cb != NULL)
cb(s,SSL_CB_ACCEPT_EXIT,ret);
return(ret);
}
2.13 SSL_connect
SSL_connect()这个函数完成SSL协商的客户端操作:

/* ssl/ssl_lib.c */
int SSL_connect(SSL *s)
{
if (s->handshake_func == 0)
/* Not properly initialized yet */
SSL_set_connect_state(s);
return(s->method->ssl_connect(s));
}

其中SSL_set_connect_state(s)函数初始化SSL协商处理:

void SSL_set_connect_state(SSL *s)
{
// 客户端
s->server=0;
s->shutdown=0;
// 初始化客户端状态值
s->state=SSL_ST_CONNECT|SSL_ST_BEFORE;
// 握手函数即是ssl_connect函数
s->handshake_func=s->method->ssl_connect;
/* clear the current cipher */
// 清除SSL读写加密算法上下文
ssl_clear_cipher_ctx(s);
}

因此最重要的就是ssl_connect()这两个成员函数,是前面SSLv[2][3]_client_method()函数中定义 的,如对于SSLv23方法,处理函数分别为ssl23_connect()函数,其它SSLv2和SSLv3方法分别对应ssl2_connect() 和ssl3_connect(),后两者就没有协商过程了,ssl23_connect()实际在协商确定协议版本后也是调用ssl2[3] _connect()。掌握了服务器端的accept过程,理解客户端的connect过程就简单了。

/* ssl/s23_clnt.c */
int ssl23_connect(SSL *s)
{
BUF_MEM *buf=NULL;
unsigned long Time=time(NULL);
void (*cb)(const SSL *ssl,int type,int val)=NULL;
int ret= -1;
int new_state,state;
// 和服务器端一样进行基本的初始化
RAND_add(&Time,sizeof(Time),0);
ERR_clear_error();
clear_sys_error();
if (s->info_callback != NULL)
cb=s->info_callback;
else if (s->ctx->info_callback != NULL)
cb=s->ctx->info_callback;

s->in_handshake++;
if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s);
for (;;)
{
state=s->state;
// 在SSL_set_connect_state中s->state被初始化为SSL_ST_CONNECT|SSL_ST_BEFORE
switch(s->state)
{
case SSL_ST_BEFORE:
case SSL_ST_CONNECT:
case SSL_ST_BEFORE|SSL_ST_CONNECT:
case SSL_ST_OK|SSL_ST_CONNECT:
// 进行基本初始化,分配缓冲区
if (s->session != NULL)
{
SSLerr(SSL_F_SSL23_CONNECT,SSL_R_SSL23_DOING_SESSION_ID_REUSE);
ret= -1;
goto end;
}
s->server=0;
if (cb != NULL) cb(s,SSL_CB_HANDSHAKE_START,1);
/* s->version=TLS1_VERSION; */
s->type=SSL_ST_CONNECT;
if (s->init_buf == NULL)
{
if ((buf=BUF_MEM_new()) == NULL)
{
ret= -1;
goto end;
}
if (!BUF_MEM_grow(buf,SSL3_RT_MAX_PLAIN_LENGTH))
{
ret= -1;
goto end;
}
s->init_buf=buf;
buf=NULL;
}
if (!ssl3_setup_buffers(s)) { ret= -1; goto end; }
ssl3_init_finished_mac(s);
// SSL客户端状态转为准备发送HELLO信息
s->state=SSL23_ST_CW_CLNT_HELLO_A;
s->ctx->stats.sess_connect++;
s->init_num=0;
break;
case SSL23_ST_CW_CLNT_HELLO_A:
case SSL23_ST_CW_CLNT_HELLO_B:
// 发送客户端的HELLO信息
s->shutdown=0;
ret=ssl23_client_hello(s);
if (ret <= 0) goto end;
// 转为准备接收服务器端的HELLO状态
s->state=SSL23_ST_CR_SRVR_HELLO_A;
s->init_num=0;
break;
case SSL23_ST_CR_SRVR_HELLO_A:
case SSL23_ST_CR_SRVR_HELLO_B:
// 读取服务器端的HELLO信息,完成SSL握手协商
ret=ssl23_get_server_hello(s);
if (ret >= 0) cb=NULL;
goto end;
/* break; */
default:
SSLerr(SSL_F_SSL23_CONNECT,SSL_R_UNKNOWN_STATE);
ret= -1;
goto end;
/* break; */
}
if (s->debug) { (void)BIO_flush(s->wbio); }
if ((cb != NULL) && (s->state != state))
{
new_state=s->state;
s->state=state;
cb(s,SSL_CB_CONNECT_LOOP,1);
s->state=new_state;
}
}
end:
s->in_handshake--;
if (buf != NULL)
BUF_MEM_free(buf);
if (cb != NULL)
cb(s,SSL_CB_CONNECT_EXIT,ret);
return(ret);
}
ssl23_client_hello()函数发送客户端的HELLO信息:

/* ssl/s23_clnt.c */
static int ssl23_client_hello(SSL *s)
{
unsigned char *buf;
unsigned char *p,*d;
int i,ch_len;
int ret;
buf=(unsigned char *)s->init_buf->data;
if (s->state == SSL23_ST_CW_CLNT_HELLO_A)
{
// 准备发送HELLO, 构造发送缓冲区的数据,数据格式见SSL_accept一节中的说明
p=s->s3->client_random;
RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE);
/* Do the message type and length last */
d= &(buf[2]);
// p指向SSL握手记录头(11字节长)后的第一字节处
p=d+9;
// 消息类型
*(d++)=SSL2_MT_CLIENT_HELLO;
// 填写客户端版本号
if (!(s->options & SSL_OP_NO_TLSv1))
{
*(d++)=TLS1_VERSION_MAJOR;
*(d++)=TLS1_VERSION_MINOR;
s->client_version=TLS1_VERSION;
}
else if (!(s->options & SSL_OP_NO_SSLv3))
{
*(d++)=SSL3_VERSION_MAJOR;
*(d++)=SSL3_VERSION_MINOR;
s->client_version=SSL3_VERSION;
}
else if (!(s->options & SSL_OP_NO_SSLv2))
{
*(d++)=SSL2_VERSION_MAJOR;
*(d++)=SSL2_VERSION_MINOR;
s->client_version=SSL2_VERSION;
}
else
{
SSLerr(SSL_F_SSL23_CLIENT_HELLO,SSL_R_NO_PROTOCOLS_AVAILABLE);
return(-1);
}
// 填写cipher_specs信息
/* Ciphers supported */
i=ssl_cipher_list_to_bytes(s,SSL_get_ciphers(s),p);
if (i == 0)
{
/* no ciphers */
SSLerr(SSL_F_SSL23_CLIENT_HELLO,SSL_R_NO_CIPHERS_AVAILABLE);
return(-1);
}
s2n(i,d);
p+=i;
// 填写会话ID
/* put in the session-id, zero since there is no
* reuse. */
s2n(0,d);
if (s->options & SSL_OP_NETSCAPE_CHALLENGE_BUG)
ch_len=SSL2_CHALLENGE_LENGTH;
else
ch_len=SSL2_MAX_CHALLENGE_LENGTH;
// 填写挑战信息
/* write out sslv2 challenge */
if (SSL3_RANDOM_SIZE < ch_len)
i=SSL3_RANDOM_SIZE;
else
i=ch_len;
s2n(i,d);
memset(&(s->s3->client_random[0]),0,SSL3_RANDOM_SIZE);
RAND_pseudo_bytes(&(s->s3->client_random[SSL3_RANDOM_SIZE-i]),i);
memcpy(p,&(s->s3->client_random[SSL3_RANDOM_SIZE-i]),i);
p+=i;
// 数据长度
i= p- &(buf[2]);
buf[0]=((i>>8)&0xff)|0x80;
buf[1]=(i&0xff);
// 数据完成,状态转为HELLO B准备发送
s->state=SSL23_ST_CW_CLNT_HELLO_B;
/* number of bytes to write */
s->init_num=i+2;
s->init_off=0;
ssl3_finish_mac(s,&(buf[2]),i);
}
/* SSL3_ST_CW_CLNT_HELLO_B */
// 发送HELLO数据
ret = ssl23_write_bytes(s);
if (ret >= 2)
if (s->msg_callback)
s->msg_callback(1, SSL2_VERSION, 0, s->init_buf->data+2, ret-2, s, s->msg_callback_arg); /* CLIENT-HELLO */
return ret;
}

ssl23_get_server_hello()接收服务器端的回应,识别服务器的SSL版本,最后再相应调用ssl2_connect()或ssl3_connect()函数。

/* ssl/s23_clnt.c */
static int ssl23_get_server_hello(SSL *s)
{
char buf[8];
unsigned char *p;
int i;
int n;
// 读7字节的服务器端数据
n=ssl23_read_bytes(s,7);
if (n != 7) return(n);
p=s->packet;
memcpy(buf,p,n);
if ((p[0] & 0x80) && (p[2] == SSL2_MT_SERVER_HELLO) &&
(p[5] == 0x00) && (p[6] == 0x02))
{
// 服务器是SSL2版本
#ifdef OPENSSL_NO_SSL2
SSLerr(SSL_F_SSL23_GET_SERVER_HELLO,SSL_R_UNSUPPORTED_PROTOCOL);
goto err;
#else
/* we are talking sslv2 */
/* we need to clean up the SSLv3 setup and put in the
* sslv2 stuff. */
int ch_len;
if (s->options & SSL_OP_NO_SSLv2)
{
SSLerr(SSL_F_SSL23_GET_SERVER_HELLO,SSL_R_UNSUPPORTED_PROTOCOL);
goto err;
}
// 初始化SSL结构中的SSL2支持,ssl_connect将为ssl2_connect
if (s->s2 == NULL)
{
if (!ssl2_new(s))
goto err;
}
else
ssl2_clear(s);
if (s->options & SSL_OP_NETSCAPE_CHALLENGE_BUG)
ch_len=SSL2_CHALLENGE_LENGTH;
else
ch_len=SSL2_MAX_CHALLENGE_LENGTH;
/* write out sslv2 challenge */
i=(SSL3_RANDOM_SIZE < ch_len)
?SSL3_RANDOM_SIZE:ch_len;
s->s2->challenge_length=i;
memcpy(s->s2->challenge,
&(s->s3->client_random[SSL3_RANDOM_SIZE-i]),i);
if (s->s3 != NULL) ssl3_free(s);
if (!BUF_MEM_grow_clean(s->init_buf,
SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER))
{
SSLerr(SSL_F_SSL23_GET_SERVER_HELLO,ERR_R_BUF_LIB);
goto err;
}
s->state=SSL2_ST_GET_SERVER_HELLO_A;
if (!(s->client_version == SSL2_VERSION))
/* use special padding (SSL 3.0 draft/RFC 2246, App. E.2) */
s->s2->ssl2_rollback=1;
/* setup the 5 bytes we have read so we get them from
* the sslv2 buffer */
s->rstate=SSL_ST_READ_HEADER;
s->packet_length=n;
s->packet= &(s->s2->rbuf[0]);
memcpy(s->packet,buf,n);
s->s2->rbuf_left=n;
s->s2->rbuf_offs=0;
/* we have already written one */
s->s2->write_sequence=1;
s->method=SSLv2_client_method();
s->handshake_func=s->method->ssl_connect;
#endif
}
else if ((p[0] == SSL3_RT_HANDSHAKE) &&
(p[1] == SSL3_VERSION_MAJOR) &amp;&
((p[2] == SSL3_VERSION_MINOR) ||
(p[2] == TLS1_VERSION_MINOR)) &amp;&
(p[5] == SSL3_MT_SERVER_HELLO))
{
// 服务器版本为SSL3或TLS1,ssl_connect将为ssl3_connect
/* we have sslv3 or tls1 */
if (!ssl_init_wbio_buffer(s,1)) goto err;
/* we are in this state */
s->state=SSL3_ST_CR_SRVR_HELLO_A;
/* put the 5 bytes we have read into the input buffer
* for SSLv3 */
s->rstate=SSL_ST_READ_HEADER;
s->packet_length=n;
s->packet= &(s->s3->rbuf.buf[0]);
memcpy(s->packet,buf,n);
s->s3->rbuf.left=n;
s->s3->rbuf.offset=0;
if ((p[2] == SSL3_VERSION_MINOR) &&
!(s->options & SSL_OP_NO_SSLv3))
{
s->version=SSL3_VERSION;
s->method=SSLv3_client_method();
}
else if ((p[2] == TLS1_VERSION_MINOR) &&
!(s->options & SSL_OP_NO_TLSv1))
{
s->version=TLS1_VERSION;
s->method=TLSv1_client_method();
}
else
{
SSLerr(SSL_F_SSL23_GET_SERVER_HELLO,SSL_R_UNSUPPORTED_PROTOCOL);
goto err;
}

s->handshake_func=s->method->ssl_connect;
}
else if ((p[0] == SSL3_RT_ALERT) &&
(p[1] == SSL3_VERSION_MAJOR) &amp;&
((p[2] == SSL3_VERSION_MINOR) ||
(p[2] == TLS1_VERSION_MINOR)) &amp;&
(p[3] == 0) &&
(p[4] == 2))
{
// SSL告警信息
void (*cb)(const SSL *ssl,int type,int val)=NULL;
int j;
/* An alert */
if (s->info_callback != NULL)
cb=s->info_callback;
else if (s->ctx->info_callback != NULL)
cb=s->ctx->info_callback;

i=p[5];
if (cb != NULL)
{
j=(i<<8)|p[6];
cb(s,SSL_CB_READ_ALERT,j);
}
s->rwstate=SSL_NOTHING;
SSLerr(SSL_F_SSL23_GET_SERVER_HELLO,SSL_AD_REASON_OFFSET+p[6]);
goto err;
}
else
{
SSLerr(SSL_F_SSL23_GET_SERVER_HELLO,SSL_R_UNKNOWN_PROTOCOL);
goto err;
}
s->init_num=0;
/* Since, if we are sending a ssl23 client hello, we are not
* reusing a session-id */
if (!ssl_get_new_session(s,0))
goto err;
s->first_packet=1;
// 递归调用SSL_connect,实际将执行ssl2_connect()或ssl3_connect()
return(SSL_connect(s));
err:
return(-1);
}
举例ssl3_conect()函数定义如下,ssl2_connect()就不分析了:
/* ssl/s3_clnt.c */
int ssl3_connect(SSL *s)
{
BUF_MEM *buf=NULL;
unsigned long Time=time(NULL),l;
long num1;
void (*cb)(const SSL *ssl,int type,int val)=NULL;
int ret= -1;
int new_state,state,skip=0;;
// 对SSL3连接的初始化
RAND_add(&Time,sizeof(Time),0);
ERR_clear_error();
clear_sys_error();
if (s->info_callback != NULL)
cb=s->info_callback;
else if (s->ctx->info_callback != NULL)
cb=s->ctx->info_callback;

s->in_handshake++;
if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s);
for (;;)
{
state=s->state;
switch(s->state)
{
case SSL_ST_RENEGOTIATE:
// 重协商的话从头开始, 注意break是注释掉的
s->new_session=1;
s->state=SSL_ST_CONNECT;
s->ctx->stats.sess_connect_renegotiate++;
/* break */
// 初始状态应该是SSL_ST_BEFORE|SSL_ST_CONNECT
case SSL_ST_BEFORE:
case SSL_ST_CONNECT:
case SSL_ST_BEFORE|SSL_ST_CONNECT:
case SSL_ST_OK|SSL_ST_CONNECT:
// server=0表示是客户端
s->server=0;
if (cb != NULL) cb(s,SSL_CB_HANDSHAKE_START,1);
// 如果本地版本不是SSL3则出错
if ((s->version & 0xff00 ) != 0x0300)
{
SSLerr(SSL_F_SSL3_CONNECT, ERR_R_INTERNAL_ERROR);
ret = -1;
goto end;
}
/* s->version=SSL3_VERSION; */
s->type=SSL_ST_CONNECT;
// 初始化连接缓冲区
if (s->init_buf == NULL)
{
if ((buf=BUF_MEM_new()) == NULL)
{
ret= -1;
goto end;
}
if (!BUF_MEM_grow(buf,SSL3_RT_MAX_PLAIN_LENGTH))
{
ret= -1;
goto end;
}
s->init_buf=buf;
buf=NULL;
}
if (!ssl3_setup_buffers(s)) { ret= -1; goto end; }
/* setup buffing BIO */
if (!ssl_init_wbio_buffer(s,0)) { ret= -1; goto end; }
/* don't push the buffering BIO quite yet */
ssl3_init_finished_mac(s);
// 准备发送HELLO信息
s->state=SSL3_ST_CW_CLNT_HELLO_A;
s->ctx->stats.sess_connect++;
s->init_num=0;
break;
case SSL3_ST_CW_CLNT_HELLO_A:
case SSL3_ST_CW_CLNT_HELLO_B:
// 该状态发送客户端SSL3信息
s->shutdown=0;
ret=ssl3_client_hello(s);
if (ret <= 0) goto end;
// 发送成功,状态转为准备接收服务器的HELLO回应
s->state=SSL3_ST_CR_SRVR_HELLO_A;
s->init_num=0;
/* turn on buffering for the next lot of output */
if (s->bbio != s->wbio)
s->wbio=BIO_push(s->bbio,s->wbio);
break;
case SSL3_ST_CR_SRVR_HELLO_A:
case SSL3_ST_CR_SRVR_HELLO_B:
// 该状态下接收服务器发送的HELLO信息
ret=ssl3_get_server_hello(s);
if (ret <= 0) goto end;
if (s->hit)
// 如果是reuse的连接,状态转为接收结束
s->state=SSL3_ST_CR_FINISHED_A;
else
// 否则状态转为准备接收服务器的证书
s->state=SSL3_ST_CR_CERT_A;
s->init_num=0;
break;
case SSL3_ST_CR_CERT_A:
case SSL3_ST_CR_CERT_B:
// 该状态下接收服务器发送的证书
/* Check if it is anon DH */
if (!(s->s3->tmp.new_cipher->algorithms & SSL_aNULL))
{
ret=ssl3_get_server_certificate(s);
if (ret <= 0) goto end;
}
else
skip=1;
// 状态转为接收密钥交换信息
s->state=SSL3_ST_CR_KEY_EXCH_A;
s->init_num=0;
break;
case SSL3_ST_CR_KEY_EXCH_A:
case SSL3_ST_CR_KEY_EXCH_B:
// 该状态下接收密钥交换信息
ret=ssl3_get_key_exchange(s);
if (ret <= 0) goto end;
// 状态转为接收证书请求
s->state=SSL3_ST_CR_CERT_REQ_A;
s->init_num=0;
/* at this point we check that we have the
* required stuff from the server */
if (!ssl3_check_cert_and_algorithm(s))
{
ret= -1;
goto end;
}
break;
case SSL3_ST_CR_CERT_REQ_A:
case SSL3_ST_CR_CERT_REQ_B:
// 该状态下接收服务器的证书请求
ret=ssl3_get_certificate_request(s);
if (ret <= 0) goto end;
s->state=SSL3_ST_CR_SRVR_DONE_A;
s->init_num=0;
break;
case SSL3_ST_CR_SRVR_DONE_A:
case SSL3_ST_CR_SRVR_DONE_B:
// 该状态下接收服务器信息结束
ret=ssl3_get_server_done(s);
if (ret <= 0) goto end;
if (s->s3->tmp.cert_req)
// 发送客户端证书
s->state=SSL3_ST_CW_CERT_A;
else
// 发送密钥交换信息
s->state=SSL3_ST_CW_KEY_EXCH_A;
s->init_num=0;
break;
case SSL3_ST_CW_CERT_A:
case SSL3_ST_CW_CERT_B:
case SSL3_ST_CW_CERT_C:
case SSL3_ST_CW_CERT_D:
// 该状态下发送客户端证书
ret=ssl3_send_client_certificate(s);
if (ret <= 0) goto end;
// 状态转为发送密钥交换信息
s->state=SSL3_ST_CW_KEY_EXCH_A;
s->init_num=0;
break;
case SSL3_ST_CW_KEY_EXCH_A:
case SSL3_ST_CW_KEY_EXCH_B:
// 该状态下发送密钥交换信息
ret=ssl3_send_client_key_exchange(s);
if (ret <= 0) goto end;
// 加密算法
l=s->s3->tmp.new_cipher->algorithms;
/* EAY EAY EAY need to check for DH fix cert
* sent back */
/* For TLS, cert_req is set to 2, so a cert chain
* of nothing is sent, but no verify packet is sent */
if (s->s3->tmp.cert_req == 1)
{
// 发送证书验证
s->state=SSL3_ST_CW_CERT_VRFY_A;
}
else
{
// TLS, 不用发送证书认证信息,直接转为修改算法状态
s->state=SSL3_ST_CW_CHANGE_A;
s->s3->change_cipher_spec=0;
}
s->init_num=0;
break;
case SSL3_ST_CW_CERT_VRFY_A:
case SSL3_ST_CW_CERT_VRFY_B:
// 该状态发送证书验证信息
ret=ssl3_send_client_verify(s);
if (ret <= 0) goto end;
// 状态转为发送修改算法信息
s->state=SSL3_ST_CW_CHANGE_A;
s->init_num=0;
s->s3->change_cipher_spec=0;
break;
case SSL3_ST_CW_CHANGE_A:
case SSL3_ST_CW_CHANGE_B:
// 该状态下发送修改算法信息
ret=ssl3_send_change_cipher_spec(s,
SSL3_ST_CW_CHANGE_A,SSL3_ST_CW_CHANGE_B);
if (ret <= 0) goto end;
// 状态转为发送结束
s->state=SSL3_ST_CW_FINISHED_A;
s->init_num=0;
// 设置会话加密算法
s->session->cipher=s->s3->tmp.new_cipher;
if (s->s3->tmp.new_compression == NULL)
s->session->compress_meth=0;
else
s->session->compress_meth=
s->s3->tmp.new_compression->id;
if (!s->method->ssl3_enc->setup_key_block(s))
{
ret= -1;
goto end;
}
if (!s->method->ssl3_enc->change_cipher_state(s,
SSL3_CHANGE_CIPHER_CLIENT_WRITE))
{
ret= -1;
goto end;
}
break;
case SSL3_ST_CW_FINISHED_A:
case SSL3_ST_CW_FINISHED_B:
// 该状态下发送协商结束信息
ret=ssl3_send_finished(s,
SSL3_ST_CW_FINISHED_A,SSL3_ST_CW_FINISHED_B,
s->method->ssl3_enc->client_finished_label,
s->method->ssl3_enc->client_finished_label_len);
if (ret <= 0) goto end;
// 转为刷新写缓冲状态
s->state=SSL3_ST_CW_FLUSH;
/* clear flags */
s->s3->flags&= ~SSL3_FLAGS_POP_BUFFER;
if (s->hit)
{
// 如果是reuse的会话, FLUSH后的下一个状态转为协商成功
s->s3->tmp.next_state=SSL_ST_OK;
if (s->s3->flags & SSL3_FLAGS_DELAY_CLIENT_FINISHED)
{
s->state=SSL_ST_OK;
s->s3->flags|=SSL3_FLAGS_POP_BUFFER;
s->s3->delay_buf_pop_ret=0;
}
}
else
{
// 否则状态转为准备接收服务器的协商结束信息
s->s3->tmp.next_state=SSL3_ST_CR_FINISHED_A;
}
s->init_num=0;
break;
case SSL3_ST_CR_FINISHED_A:
case SSL3_ST_CR_FINISHED_B:
// 该状态接收服务器发送的协商结束信息
ret=ssl3_get_finished(s,SSL3_ST_CR_FINISHED_A,
SSL3_ST_CR_FINISHED_B);
if (ret <= 0) goto end;
if (s->hit)
// 如果会话是reuse的,状态转为发送算法
s->state=SSL3_ST_CW_CHANGE_A;
else
// 否则协商成功
s->state=SSL_ST_OK;
s->init_num=0;
break;
case SSL3_ST_CW_FLUSH:
// 该状态下刷新写缓冲区
/* number of bytes to be flushed */
num1=BIO_ctrl(s->wbio,BIO_CTRL_INFO,0,NULL);
if (num1 > 0)
{
s->rwstate=SSL_WRITING;
num1=BIO_flush(s->wbio);
if (num1 <= 0) { ret= -1; goto end; }
s->rwstate=SSL_NOTHING;
}
s->state=s->s3->tmp.next_state;
break;
case SSL_ST_OK:
// 该状态下协商成功结束
// 释放协商时分配的资源
/* clean a few things up */
ssl3_cleanup_key_block(s);
if (s->init_buf != NULL)
{
BUF_MEM_free(s->init_buf);
s->init_buf=NULL;
}
/* If we are not 'joining' the last two packets,
* remove the buffering now */
if (!(s->s3->flags & SSL3_FLAGS_POP_BUFFER))
ssl_free_wbio_buffer(s);
/* else do it later in ssl3_write */
s->init_num=0;
s->new_session=0;
ssl_update_cache(s,SSL_SESS_CACHE_CLIENT);
if (s->hit) s->ctx->stats.sess_hit++;
ret=1;
/* s->server=0; */
s->handshake_func=ssl3_connect;
s->ctx->stats.sess_connect_good++;
if (cb != NULL) cb(s,SSL_CB_HANDSHAKE_DONE,1);
goto end;
/* break; */

default:
SSLerr(SSL_F_SSL3_CONNECT,SSL_R_UNKNOWN_STATE);
ret= -1;
goto end;
/* break; */
}
/* did we do anything */
if (!s->s3->tmp.reuse_message && !skip)
{
if (s->debug)
{
if ((ret=BIO_flush(s->wbio)) <= 0)
goto end;
}
if ((cb != NULL) && (s->state != state))
{
new_state=s->state;
s->state=state;
cb(s,SSL_CB_CONNECT_LOOP,1);
s->state=new_state;
}
}
skip=0;
}
end:
s->in_handshake--;
if (buf != NULL)
BUF_MEM_free(buf);
if (cb != NULL)
cb(s,SSL_CB_CONNECT_EXIT,ret);
return(ret);
}
2.14 SSL_read

SSL结构(struct ssl_st)中的s2,s3指针分别指向SSL2和SSL3的状态结构,这些状态结构中都有用于读的rbuf,其中保存SSL会话接收到的原始数据,另 外还有保存记录的rrec,用来保存解码后的数据。在SSL结构中有个指针packet是指向原始数据的。

SSL_read()实现从SSL通道中读取数据,送到应用程序的数据是已经经过SSL解封装的了。

/* ssl/ssl_lib.c */
int SSL_read(SSL *s,void *buf,int num)
{
if (s->handshake_func == 0)
{
SSLerr(SSL_F_SSL_READ, SSL_R_UNINITIALIZED);
return -1;
}
// 发现接收shutdown标志,接收结束
if (s->shutdown & SSL_RECEIVED_SHUTDOWN)
{
s->rwstate=SSL_NOTHING;
return(0);
}
// 调用具体方法的接收函数, 如ssl3_read(), ssl2_read()等
return(s->method->ssl_read(s,buf,num));
}

下面以ssl3_read()函数进行详细说明,ssl3_read()函数本质是调用ssl3_read_internal()
/* ssl/s3_lib.c */
int ssl3_read(SSL *s, void *buf, int len)
{
// 最后一个参数为0,表示是实实在在的读数据,不是偷看(peek)
// peek的意思是读数据,但不会把已读数据从缓冲区中去掉,因此下一次读还会读到
return ssl3_read_internal(s, buf, len, 0);
}

static int ssl3_read_internal(SSL *s, void *buf, int len, int peek)
{
int ret;
// 清除错误信息
clear_sys_error();
// 检查协商是否成功
if (s->s3->renegotiate) ssl3_renegotiate_check(s);
// 标志现在在读应用层数据
s->s3->in_read_app_data=1;
// ssl3读取数据, 类型是SSL3_RT_APPLICATION_DATA,应用数据
ret=ssl3_read_bytes(s,SSL3_RT_APPLICATION_DATA,buf,len,peek);
if ((ret == -1) && (s->s3->in_read_app_data == 2))
{
// 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. */
s->in_handshake++;
// 重新读数据
ret=ssl3_read_bytes(s,SSL3_RT_APPLICATION_DATA,buf,len,peek);
s->in_handshake--;
}
else
s->s3->in_read_app_data=0;
return(ret);
}

读取数据的主要函数实际为ssl3_read_bytes():
/* ssl/ssl3_pkt.c */
int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
{
int al,i,j,ret;
unsigned int n;
SSL3_RECORD *rr;
void (*cb)(const SSL *ssl,int type2,int val)=NULL;
// 检查是否分配了接收缓冲区
if (s->s3->rbuf.buf == NULL) /* Not initialized yet */
if (!ssl3_setup_buffers(s))
return(-1);
// 只处理两种数据,或是应用层数据或者是SSL握手协商数据,
// peek只处理应用层数据,其他则出错
if ((type && (type != SSL3_RT_APPLICATION_DATA) &amp;&
(type != SSL3_RT_HANDSHAKE) &amp;& type) ||
(peek && (type != SSL3_RT_APPLICATION_DATA)))
{
SSLerr(SSL_F_SSL3_READ_BYTES, ERR_R_INTERNAL_ERROR);
return -1;
}
if ((type == SSL3_RT_HANDSHAKE) && (s->s3->handshake_fragment_len > 0))
/* (partially) satisfy request from storage */
{
// 握手数据碎片, 处理完直到不再是碎片
unsigned char *src = s->s3->handshake_fragment;
unsigned char *dst = buf;
unsigned int k;
/* peek == 0 */
n = 0;
while ((len > 0) && (s->s3->handshake_fragment_len > 0))
{
*dst++ = *src++;
len--; s->s3->handshake_fragment_len--;
n++;
}
/* move any remaining fragment bytes: */
for (k = 0; k < s->s3->handshake_fragment_len; k++)
s->s3->handshake_fragment[k] = *src++;
return n;
}
/* Now s->s3->handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE. */
if (!s->in_handshake && SSL_in_init(s))
{
// 没进握手阶段但属于SSL初始化阶段, 调用SSL握手处理
/* type == SSL3_RT_APPLICATION_DATA */
i=s->handshake_func(s);
if (i < 0) return(i);
if (i == 0)
{
SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
return(-1);
}
}
start:
// 正常的接收数据开始, 开始读写状态为NOTHING
s->rwstate=SSL_NOTHING;
/* s->s3->rrec.type - is the type of record
* s->s3->rrec.data, - data
* s->s3->rrec.off, - offset into 'data' for next read
* s->s3->rrec.length, - number of bytes. */
// 记录类型指针
rr = &(s->s3->rrec);
/* get new packet if necessary */
if ((rr->length == 0) || (s->rstate == SSL_ST_READ_BODY))
{
// 当前没记录,获取新的记录信息, 该函数获取SSL记录数据
ret=ssl3_get_record(s);
if (ret <= 0) return(ret);
}
/* we now have a packet which can be read and processed */
if (s->s3->change_cipher_spec /* set when we receive ChangeCipherSpec,
* reset by ssl3_get_finished */
&& (rr->type != SSL3_RT_HANDSHAKE))
{
// 只允许在握手状态下修改当前的算法信息,否则出错
al=SSL_AD_UNEXPECTED_MESSAGE;
SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_DATA_BETWEEN_CCS_AND_FINISHED);
goto err;
}
/* If the other end has shut down, throw anything we read away
* (even in 'peek' mode) */
if (s->shutdown & SSL_RECEIVED_SHUTDOWN)
{
// 接收到对方的FIN信息, 接收结束
rr->length=0;
s->rwstate=SSL_NOTHING;
return(0);
}

if (type == rr->type) /* SSL3_RT_APPLICATION_DATA or SSL3_RT_HANDSHAKE */
{
// 读到是数据类型和期待读到的数据类型一致
// 这是正常大多数的情况
/* make sure that we are not getting application data when we
* are doing a handshake for the first time */
if (SSL_in_init(s) && (type == SSL3_RT_APPLICATION_DATA) &amp;&
(s->enc_read_ctx == NULL))
{
// 在初始阶段只应该读握手信息而不该读应用信息
al=SSL_AD_UNEXPECTED_MESSAGE;
SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_APP_DATA_IN_HANDSHAKE);
goto f_err;
}
if (len <= 0) return(len);
// 在期待读取的数据长度len和已经读到的数据记录长度rr->length取小值作为返回的数据长度
if ((unsigned int)len > rr->length)
n = rr->length;
else
n = (unsigned int)len;
memcpy(buf,&(rr->data[rr->off]),n);
if (!peek)
{
// 如果不是peek,移动记录缓冲的数据偏移指针,长度减
rr->length-=n;
rr->off+=n;
if (rr->length == 0)
{
s->rstate=SSL_ST_READ_HEADER;
rr->off=0;
}
}
// 正常返回
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;
}
else if (rr->type == SSL3_RT_ALERT)
{
// 告警信息存到告警碎片区
dest_maxlen = sizeof s->s3->alert_fragment;
dest = s->s3->alert_fragment;
dest_len = &s->s3->alert_fragment_len;
}
if (dest_maxlen > 0)
{
// 有缓冲区, 计算可用的最大缓冲区
n = dest_maxlen - *dest_len; /* available space in 'dest' */
// 如果要写入的长度小于可用空间大小,可全部写入
if (rr->length < n)
n = rr->length; /* available bytes */
// 写入缓冲区
/* now move 'n' bytes: */
while (n-- > 0)
{
dest[(*dest_len)++] = rr->data[rr->off++];
rr->length--;
}
if (*dest_len < dest_maxlen)
goto start; /* fragment was too small */
}
}
/* s->s3->handshake_fragment_len == 4 iff rr->type == SSL3_RT_HANDSHAKE;
* s->s3->alert_fragment_len == 2 iff rr->type == SSL3_RT_ALERT.
* (Possibly rr is 'empty' now, i.e. rr->length may be 0.) */
/* If we are a client, check for an incoming 'Hello Request': */
if ((!s->server) &&
(s->s3->handshake_fragment_len >= 4) &&
(s->s3->handshake_fragment[0] == SSL3_MT_HELLO_REQUEST) &&
(s->session != NULL) && (s->session->cipher != NULL))
{
// 本地是客户端
// 握手碎片长度至少是4字节, 类型是HELLO的握手信息时进行处理
s->s3->handshake_fragment_len = 0;
if ((s->s3->handshake_fragment[1] != 0) ||
(s->s3->handshake_fragment[2] != 0) ||
(s->s3->handshake_fragment[3] != 0))
{
// SSL3_MT_HELLO_REQUEST类型后三个字节都要是0
al=SSL_AD_DECODE_ERROR;
SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_BAD_HELLO_REQUEST);
goto err;
}
if (s->msg_callback)
s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->s3->handshake_fragment, 4, s, s->msg_callback_arg);
if (SSL_is_init_finished(s) &&
// 协商结束但没设置不需重协商算法标志
!(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS) &amp;&
// 没设置该ssl3会话的重协商标志
!s->s3->renegotiate)
{
// 进行ssl3重协商,实际是将s->s3->renegotiate置1
ssl3_renegotiate(s);
if (ssl3_renegotiate_check(s))
{
// 进行重协商
i=s->handshake_func(s);
if (i < 0) return(i);
if (i == 0)
{
SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
return(-1);
}
if (!(s->mode & SSL_MODE_AUTO_RETRY))
// 该SSL会话不是自动重试模式
{
if (s->s3->rbuf.left == 0) /* no read-ahead left? */
{
BIO *bio;
/* In the case where we try to read application data,
* but we trigger an SSL handshake, we return -1 with
* the retry option set. Otherwise renegotiation may
* cause nasty problems in the blocking world */
s->rwstate=SSL_READING;
bio=SSL_get_rbio(s);
// 清除读BIO重试标志
BIO_clear_retry_flags(bio);
// 设置读BIO重试标志
BIO_set_retry_read(bio);
return(-1);
}
}
}
}
/* we either finished a handshake or ignored the request,
* now try again to obtain the (application) data we were asked for */
// 重新读数据
goto start;
}
if (s->s3->alert_fragment_len >= 2)
{
// 处理告警碎片信息, 长度大于等于2字节时就处理
int alert_level = s->s3->alert_fragment[0];
int alert_descr = s->s3->alert_fragment[1];
s->s3->alert_fragment_len = 0;
// 分别处理msg和info的回调
if (s->msg_callback)
s->msg_callback(0, s->version, SSL3_RT_ALERT, s->s3->alert_fragment, 2, s, s->msg_callback_arg);
if (s->info_callback != NULL)
cb=s->info_callback;
else if (s->ctx->info_callback != NULL)
cb=s->ctx->info_callback;
if (cb != NULL)
{
j = (alert_level << 8) | alert_descr;
cb(s, SSL_CB_READ_ALERT, j);
}
if (alert_level == 1) /* warning */
{
// 普通报警信息
s->s3->warn_alert = alert_descr;
if (alert_descr == SSL_AD_CLOSE_NOTIFY)
{
s->shutdown |= SSL_RECEIVED_SHUTDOWN;
return(0);
}
}
else if (alert_level == 2) /* fatal */
{
// 严重错误信息, 断开SSL会话
char tmp[16];
s->rwstate=SSL_NOTHING;
s->s3->fatal_alert = alert_descr;
SSLerr(SSL_F_SSL3_READ_BYTES, SSL_AD_REASON_OFFSET + alert_descr);
BIO_snprintf(tmp,sizeof tmp,"%d",alert_descr);
ERR_add_error_data(2,"SSL alert number ",tmp);
s->shutdown|=SSL_RECEIVED_SHUTDOWN;
SSL_CTX_remove_session(s->ctx,s->session);
return(0);
}
else
{
al=SSL_AD_ILLEGAL_PARAMETER;
SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNKNOWN_ALERT_TYPE);
goto f_err;
}
goto start;
}
if (s->shutdown & SSL_SENT_SHUTDOWN) /* but we have not received a shutdown */
{
// SSL会话设置发送SHUTDOWN, 表示不再发送数据
s->rwstate=SSL_NOTHING;
rr->length=0;
return(0);
}
if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC)
{
// 数据记录类型是更改算法参数
/* 'Change Cipher Spec' is just a single byte, so we know
* exactly what the record payload has to look like */
if ( (rr->length != 1) || (rr->off != 0) ||
(rr->data[0] != SSL3_MT_CCS))
{
// 错误检查
i=SSL_AD_ILLEGAL_PARAMETER;
SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_BAD_CHANGE_CIPHER_SPEC);
goto err;
}
rr->length=0;
if (s->msg_callback)
s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, 1, s, s->msg_callback_arg);
// 加密算法更改处理,成功时转到重新接收数据
s->s3->change_cipher_spec=1;
if (!do_change_cipher_spec(s))
goto err;
else
goto start;
}
/* Unexpected handshake message (Client Hello, or protocol violation) */
if ((s->s3->handshake_fragment_len >= 4) && !s->in_handshake)
{
// 异常的握手信息
// SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS标志表示不需要重新协商加密算法
if (((s->state&SSL_ST_MASK) == SSL_ST_OK) &amp;&
!(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS))
{
#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
// 如果是服务器端,SSL状态转为接受;如果是客户端, 转为连接
s->state = s->server ? SSL_ST_ACCEPT : SSL_ST_CONNECT;
#endif
// 重新进行新会话
s->new_session=1;
}
// 重新握手协商
i=s->handshake_func(s);
if (i < 0) return(i);
if (i == 0)
{
SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
return(-1);
}
if (!(s->mode & SSL_MODE_AUTO_RETRY))
{
if (s->s3->rbuf.left == 0) /* no read-ahead left? */
{
BIO *bio;
/* In the case where we try to read application data,
* but we trigger an SSL handshake, we return -1 with
* the retry option set. Otherwise renegotiation may
* cause nasty problems in the blocking world */
s->rwstate=SSL_READING;
bio=SSL_get_rbio(s);
BIO_clear_retry_flags(bio);
BIO_set_retry_read(bio);
return(-1);
}
}
goto start;
}
// 最后再根据记录类型进行处理
switch (rr->type)
{
default:
#ifndef OPENSSL_NO_TLS
/* TLS just ignores unknown message types */
// 如果会话是TLS1版本转为重新接收数据
// 其他版本则认为出错
if (s->version == TLS1_VERSION)
{
rr->length = 0;
goto start;
}
#endif
al=SSL_AD_UNEXPECTED_MESSAGE;
SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNEXPECTED_RECORD);
goto f_err;
case SSL3_RT_CHANGE_CIPHER_SPEC:
case SSL3_RT_ALERT:
case SSL3_RT_HANDSHAKE:
// 这几种类型前面已经处理过了, 到这还有的话是哪出错了
/* we already handled all of these, with the possible exception
* of SSL3_RT_HANDSHAKE when s->in_handshake is set, but that
* should not happen when type != rr->type */
al=SSL_AD_UNEXPECTED_MESSAGE;
SSLerr(SSL_F_SSL3_READ_BYTES,ERR_R_INTERNAL_ERROR);
goto f_err;
case SSL3_RT_APPLICATION_DATA:
// 这时情况是收到的是应用数据,而期望接收的是握手数据
/* At this point, we were expecting handshake data,
* but have application data. If the library was
* running inside ssl3_read() (i.e. in_read_app_data
* is set) and it makes sense to read application data
* at this point (session renegotiation not yet started),
* we will indulge it.
*/
// 要接收应用数据
if (s->s3->in_read_app_data &&
// 需要重协商
(s->s3->total_renegotiations != 0) &&
((
(s->state & SSL_ST_CONNECT) &amp;&
(s->state >= SSL3_ST_CW_CLNT_HELLO_A) &&
(s->state <= SSL3_ST_CR_SRVR_HELLO_A)
) || (
(s->state & SSL_ST_ACCEPT) &amp;&
(s->state <= SSL3_ST_SW_HELLO_REQ_A) &&
(s->state >= SSL3_ST_SR_CLNT_HELLO_A)
)
))
{
// in_read_app_data设置为2,会重新执行ssl3_read_bytes()
s->s3->in_read_app_data=2;
return(-1);
}
else
{
al=SSL_AD_UNEXPECTED_MESSAGE;
SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNEXPECTED_RECORD);
goto f_err;
}
}
/* not reached */
f_err:
// 错误时发送告警信息
ssl3_send_alert(s,SSL3_AL_FATAL,al);
err:
return(-1);
}

可以看出接收数据的重点函数是ssl3_get_record():
/* ssl/s3_pkt.c */
static int ssl3_get_record(SSL *s)
{
int ssl_major,ssl_minor,al;
int enc_err,n,i,ret= -1;
SSL3_RECORD *rr;
SSL_SESSION *sess;
unsigned char *p;
unsigned char md[EVP_MAX_MD_SIZE];
short version;
unsigned int mac_size;
int clear=0;
size_t extra;
int decryption_failed_or_bad_record_mac = 0;
unsigned char *mac = NULL;
// 所读数据的记录指针
rr= &(s->s3->rrec);
sess=s->session;
if (s->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER)
// MS的SSL实现可附带较大数据
extra=SSL3_RT_MAX_EXTRA;
else
extra=0;
if (extra != s->s3->rbuf.len - SSL3_RT_MAX_PACKET_SIZE)
// 普通情况下读缓冲大小就是SSL3_RT_MAX_PACKET_SIZE
{
/* actually likely an application error: SLS_OP_MICROSOFT_BIG_SSLV3_BUFFER
* set after ssl3_setup_buffers() was done */
SSLerr(SSL_F_SSL3_GET_RECORD, ERR_R_INTERNAL_ERROR);
return -1;
}
again:
/* check if we have the header */
if ( (s->rstate != SSL_ST_READ_BODY) ||
// 接收的数据长度还不够记录长度
(s->packet_length < SSL3_RT_HEADER_LENGTH))
{
// 读至少SSL3_RT_HEADER_LENGTH长度,最大s->s3->rbuf.len
n=ssl3_read_n(s, SSL3_RT_HEADER_LENGTH, s->s3->rbuf.len, 0);
if (n <= 0) return(n); /* error or non-blocking */
s->rstate=SSL_ST_READ_BODY;
// p现在是SSL收到的原始数据头
p=s->packet;
/* Pull apart the header into the SSL3_RECORD */
// SSL记录类型
rr->type= *(p++);
// 版本信息
ssl_major= *(p++);
ssl_minor= *(p++);
version=(ssl_major<<8)|ssl_minor;
// 数据长度
n2s(p,rr->length);
/* Lets check version */
if (s->first_packet)
{
s->first_packet=0;
}
else
{
// 已经不是第一个包了
// 检查版本是否等于SSL会话版本,这时应该协商好应该是相同的了
// 如果不同就是错的
if (version != s->version)
{
SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_WRONG_VERSION_NUMBER);
/* Send back error using their
* version number :-) */
s->version=version;
al=SSL_AD_PROTOCOL_VERSION;
goto f_err;
}
}
// 检查版本是否是SSL3
if ((version>>8) != SSL3_VERSION_MAJOR)
{
SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_WRONG_VERSION_NUMBER);
goto err;
}
// 接收数据过长出错
if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH+extra)
{
al=SSL_AD_RECORD_OVERFLOW;
SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_PACKET_LENGTH_TOO_LONG);
goto f_err;
}
/* now s->rstate == SSL_ST_READ_BODY */
}
/* s->rstate == SSL_ST_READ_BODY, get and decode the data */
// SSL3_RT_HEADER_LENGTH = 5
if (rr->length > s->packet_length-SSL3_RT_HEADER_LENGTH)
{
// 已收到的数据还不够记录长度, 继续读够记录长度
/* now s->packet_length == SSL3_RT_HEADER_LENGTH */
i=rr->length;
n=ssl3_read_n(s,i,i,1);
if (n <= 0) return(n); /* error or non-blocking io */
/* now n == rr->length,
* and s->packet_length == SSL3_RT_HEADER_LENGTH + rr->length */
}
//
// 状态转为已经接收到SSL头信息
s->rstate=SSL_ST_READ_HEADER; /* set state for later operations */
/* At this point, s->packet_length == SSL3_RT_HEADER_LNGTH + rr->length,
* and we have that many bytes in s->packet
*/
// input指向SSL原始数据头后的数据
rr->input= &(s->packet[SSL3_RT_HEADER_LENGTH]);
/* ok, we can now read from 's->packet' data into 'rr'
* rr->input points at rr->length bytes, which
* need to be copied into rr->data by either
* the decryption or by the decompression
* When the data is 'copied' into the rr->data buffer,
* rr->input will be pointed at the new buffer */
/* We now have - encrypted [ MAC [ compressed [ plain ] ] ]
* rr->length bytes of encrypted compressed stuff. */
/* check is not needed I believe */
if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH+extra)
{
// 数据又读多了
al=SSL_AD_RECORD_OVERFLOW;
SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_ENCRYPTED_LENGTH_TOO_LONG);
goto f_err;
}
/* decrypt in place in 'rr->input' */
// 记录数据
rr->data=rr->input;
// 对数据进行解密
enc_err = s->method->ssl3_enc->enc(s,0);
if (enc_err <= 0)
{
if (enc_err == 0)
/* SSLerr() and ssl3_send_alert() have been called */
goto err;
/* Otherwise enc_err == -1, which indicates bad padding
* (rec->length has not been changed in this case).
* To minimize information leaked via timing, we will perform
* the MAC computation anyway. */
// 注意 enc_err<0 时并不立即转到错误处理,只是作个标志
decryption_failed_or_bad_record_mac = 1;
}

/* r->length is now the compressed data plus mac */
if ( (sess == NULL) ||
(s->enc_read_ctx == NULL) ||
(s->read_hash == NULL))
// 会话,加密上下文,读认证码操作都为空的情况,设置标志clear
clear=1;
if (!clear)
{
// HASH算法的认证码长度
mac_size=EVP_MD_size(s->read_hash);
if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH+extra+mac_size)
{
#if 0 /* OK only for stream ciphers (then rr->length is visible from ciphertext anyway) */
al=SSL_AD_RECORD_OVERFLOW;
SSLerr(SSL_F_SSL3_GET_RECORD,
SSL_R_PRE_MAC_LENGTH_TOO_LONG);
goto f_err;
#else
// 长度过长, 设置出错标志
decryption_failed_or_bad_record_mac = 1;
#endif
}
/* check the MAC for rr->input (it's in mac_size bytes at the tail) */
if (rr->length >= mac_size)
{
// 记录长度中减去认证码长度
rr->length -= mac_size;
// 认证码数据指针
mac = &rr->data[rr->length];
}
else
{
/* record (minus padding) is too short to contain a MAC */
#if 0 /* OK only for stream ciphers */
al=SSL_AD_DECODE_ERROR;
SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_LENGTH_TOO_SHORT);
goto f_err;
#else
// 记录长度还没认证码长度长, 设置出错标志
decryption_failed_or_bad_record_mac = 1;
rr->length = 0;
#endif
}
// 用认证码对数据进行认证
i=s->method->ssl3_enc->mac(s,md,0);
if (mac == NULL || memcmp(md, mac, mac_size) != 0)
{
decryption_failed_or_bad_record_mac = 1;
}
}
if (decryption_failed_or_bad_record_mac)
{
// 解密错误, 跳出
/* A separate 'decryption_failed' alert was introduced with TLS 1.0,
* SSL 3.0 only has 'bad_record_mac'. But unless a decryption
* failure is directly visible from the ciphertext anyway,
* we should not reveal which kind of error occured -- this
* might become visible to an attacker (e.g. via a logfile) */
al=SSL_AD_BAD_RECORD_MAC;
SSLerr(SSL_F_SSL3_GET_RECORD,
SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
goto f_err;
}
/* r->length is now just compressed */
if (s->expand != NULL)
// 现在数据是经过压缩处理的
{
if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH+extra)
{
// 压缩数据长度又太长了
al=SSL_AD_RECORD_OVERFLOW;
SSLerr(SSL_F_SSL3_GET_RECORD,
SSL_R_COMPRESSED_LENGTH_TOO_LONG);
goto f_err;
}
// 对数据解压
if (!do_uncompress(s))
{
// 解压失败, 跳出
al=SSL_AD_DECOMPRESSION_FAILURE;
SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_BAD_DECOMPRESSION);
goto f_err;
}
}
if (rr->length > SSL3_RT_MAX_PLAIN_LENGTH+extra)
{
// 解压缩后的明文数据长度过长, 跳出
al=SSL_AD_RECORD_OVERFLOW;
SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_DATA_LENGTH_TOO_LONG);
goto f_err;
}
rr->off=0;
/* So at this point the following is true
* ssl->s3->rrec.type is the type of record
* ssl->s3->rrec.length == number of bytes in record
* ssl->s3->rrec.off == offset to first valid byte
* ssl->s3->rrec.data == where to take bytes from, increment
* after use :-).
*/
/* we have pulled in a full packet so zero things */
s->packet_length=0;
/* just read a 0 length packet */
// 如果记录长度为0, 转到继续接收数据
if (rr->length == 0) goto again;
return(1);
f_err:
// 错误发送告警信息
ssl3_send_alert(s,SSL3_AL_FATAL,al);
err:
return(ret);
}

最后看一下ssl3_read_n()函数,接收SSL原始数据, 该函数只被ssl3_get_record()函数调用,是static的:
/* ssl/s3_pkt.c */
static int ssl3_read_n(SSL *s, int n, int max, int extend)
{
// extend为0时表示要读全新数据,非0表示可以用现在缓冲中的数据
/* If extend == 0, obtain new n-byte packet; if extend == 1, increase
* packet by another n bytes.
* The packet will be in the sub-array of s->s3->rbuf.buf specified
* by s->packet and s->packet_length.
* (If s->read_ahead is set, 'max' bytes may be stored in rbuf
* [plus s->packet_length bytes if extend == 1].)
*/
int i,off,newb;
if (!extend)
{
/* start with empty packet ... */
// 确定包缓冲地址, 置当前的包长为0
if (s->s3->rbuf.left == 0)
s->s3->rbuf.offset = 0;
s->packet = s->s3->rbuf.buf + s->s3->rbuf.offset;
s->packet_length = 0;
/* ... now we can act as if 'extend' was set */
}
/* if there is enough in the buffer from a previous read, take some */
if (s->s3->rbuf.left >= (int)n)
{
// 如果已读缓冲区中剩余数据超过要读的数据,直接移动指针即可
s->packet_length+=n;
s->s3->rbuf.left-=n;
s->s3->rbuf.offset+=n;
return(n);
}
/* else we need to read more data */
// 预读数据长度为0
if (!s->read_ahead)
max=n;
{
/* avoid buffer overflow */
// 当前缓冲区最大空闲值
int max_max = s->s3->rbuf.len - s->packet_length;
if (max > max_max)
max = max_max;
}
if (n > max) /* does not happen */
{
// 要读的数据量超过了缓冲区量,出错
SSLerr(SSL_F_SSL3_READ_N,ERR_R_INTERNAL_ERROR);
return -1;
}
off = s->packet_length;
// newb为读缓冲区中剩下的有效数据长度
newb = s->s3->rbuf.left;
/* Move any available bytes to front of buffer:
* 'off' bytes already pointed to by 'packet',
* 'newb' extra ones at the end */
if (s->packet != s->s3->rbuf.buf)
{
/* off > 0 */
memmove(s->s3->rbuf.buf, s->packet, off+newb);
s->packet = s->s3->rbuf.buf;
}
while (newb < n)
{
/* Now we have off+newb bytes at the front of s->s3->rbuf.buf and need
* to read in more until we have off+n (up to off+max if possible) */
clear_sys_error();
if (s->rbio != NULL)
{
s->rwstate=SSL_READING;
// 通过SSL的读BIO读取数据
i=BIO_read(s->rbio, &(s->s3->rbuf.buf[off+newb]), max-newb);
}
else
{
SSLerr(SSL_F_SSL3_READ_N,SSL_R_READ_BIO_NOT_SET);
i = -1;
}
if (i <= 0)
{
// 读完或出错, newb为缓冲区剩余有效数据长度
s->s3->rbuf.left = newb;
return(i);
}
newb+=i;
}
/* done reading, now the book-keeping */
// 读数据完成, 修改偏移量和剩余数据长度
s->s3->rbuf.offset = off + n;
s->s3->rbuf.left = newb - n;
s->packet_length += n;
s->rwstate=SSL_NOTHING;
return(n);
}
2.15 SSL_write
SSL结构(struct ssl_st)中的s2,s3指针分别指向SSL2和SSL3的状态结构,这些状态结构中都有用于写的wbuf,写操作相对读操作要简单一些。

SSL_write()实现向SSL通道中写数据,应用程序只需要向里写入明文数据,SSL通道自动对这些数据进行加密封装。
/* ssl/ssl_lib.c */
int SSL_write(SSL *s,const void *buf,int num)
{
if (s->handshake_func == 0)
{
SSLerr(SSL_F_SSL_WRITE, SSL_R_UNINITIALIZED);
return -1;
}
// 发现发送shutdown标志,发送失败
if (s->shutdown & SSL_SENT_SHUTDOWN)
{
s->rwstate=SSL_NOTHING;
SSLerr(SSL_F_SSL_WRITE,SSL_R_PROTOCOL_IS_SHUTDOWN);
return(-1);
}
// 调用具体方法的发送函数, 如ssl3_write(), ssl2_write()等
return(s->method->ssl_write(s,buf,num));
}
下面以ssl3_write()函数进行详细说明,
/* ssl/s3_lib.c */
int ssl3_write(SSL *s, const void *buf, int len)
{
int ret,n;
#if 0
if (s->shutdown & SSL_SEND_SHUTDOWN)
{
s->rwstate=SSL_NOTHING;
return(0);
}
#endif
// 和read操作类似的一些检查工作
clear_sys_error();
if (s->s3->renegotiate) ssl3_renegotiate_check(s);
/* This is an experimental flag that sends the
* last handshake message in the same packet as the first
* use data - used to see if it helps the TCP protocol during
* session-id reuse */
/* The second test is because the buffer may have been removed */
if ((s->s3->flags & SSL3_FLAGS_POP_BUFFER) &amp;& (s->wbio == s->bbio))
{
// 这个标志导致的操作更多的是实验性功能
/* First time through, we write into the buffer */
if (s->s3->delay_buf_pop_ret == 0)
{
ret=ssl3_write_bytes(s,SSL3_RT_APPLICATION_DATA,
buf,len);
if (ret <= 0) return(ret);
s->s3->delay_buf_pop_ret=ret;
}
s->rwstate=SSL_WRITING;
n=BIO_flush(s->wbio);
if (n <= 0) return(n);
s->rwstate=SSL_NOTHING;
/* We have flushed the buffer, so remove it */
ssl_free_wbio_buffer(s);
s->s3->flags&= ~SSL3_FLAGS_POP_BUFFER;
ret=s->s3->delay_buf_pop_ret;
s->s3->delay_buf_pop_ret=0;
}
else
{
// 正常的SSL3写数据,类型为SSL3_RT_APPLICATION_DATA,应用层数据
ret=ssl3_write_bytes(s,SSL3_RT_APPLICATION_DATA,
buf,len);
if (ret <= 0) return(ret);
}
return(ret);
}

写数据操作主要由ssl3_write_bytes()完成:

/* ssl/s3_pkt.c */
/* Call this to write data in records of type 'type'
* It will return <= 0 if not all data has been sent or non-blocking IO.
*/
int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len)
{
const unsigned char *buf=buf_;
unsigned int tot,n,nw;
int i;
// 状态参数初始化
s->rwstate=SSL_NOTHING;
// s3->wnum是写缓冲区中还没写完的数据长度
tot=s->s3->wnum;
s->s3->wnum=0;
if (SSL_in_init(s) && !s->in_handshake)
{
// 检查是否需要协商
i=s->handshake_func(s);
if (i < 0) return(i);
if (i == 0)
{
SSLerr(SSL_F_SSL3_WRITE_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
return -1;
}
}
// 实际要写的数据量
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;
}
}
do_ssl3_write()完成对应用层数据的SSL封装,再调用底层发送函数发送数据, 这是一个static的内部函数:
/* ssl/s3_pkt.c */
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)
return(ssl3_write_pending(s,type,buf,len));
/* If we have an alert to send, lets send it */
if (s->s3->alert_dispatch)
{
// 要发送告警信息
i=ssl3_dispatch_alert(s);
if (i <= 0)
return(i);
/* if it went, fall through and send more stuff */
}
if (len == 0 && !create_empty_fragment)
return 0;
// wr为写的数据记录
wr= &(s->s3->wrec);
// wb指向要写的数据缓冲
wb= &(s->s3->wbuf);
sess=s->session;
if ( (sess == NULL) ||
(s->enc_write_ctx == NULL) ||
(s->write_hash == NULL))
clear=1;
// 实际发送的数据总长要追加的认证码长度
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) */
// 以len为0,create_empty_fragment为1递归调用本函数建立空碎片数据,
// 基本就只是IV,供后续的实际数据使用
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=&(s->s3->wbuf)
p = wb->buf + prefix_len;
/* write the header */
// 类型
*(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)
{
// 需要压缩的话先对数据进行压缩,明文压缩率才比较大,密文的压缩率几乎为0
if (!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
*/
// 如果是空碎片,直接就返回了,不实际发送
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;
// 要发送明文的缓冲区, 实际是不发送的, 实际发送的是wb指向的缓冲区
s->s3->wpend_buf=buf;
// 数据类型
s->s3->wpend_type=type;
// 如果发送成功返回的数据长度, 这是指明文数据的长度, 供应用层程序判断用的
// 不是实际发送的压缩加密后的数据长度
s->s3->wpend_ret=len;
/* we now just need to write the buffer */
// 调用ssl3_write_pending()发送数据
return ssl3_write_pending(s,type,buf,len);
err:
return -1;
}

ssl3_write_pending()完成实际的数据发送, 这也是个static的函数, 和和普通write()一样, 这个函数可能会阻塞, 而如果套接字是NON_BLOCK的发送不出去会直接返回:

/* if s->s3->wbuf.left != 0, we need to call this */
static int ssl3_write_pending(SSL *s, int type, const unsigned char *buf,
unsigned int len)
{
int i;
/* XXXX */
// 判断数据长度是否出错用的是wpend_buf
if ((s->s3->wpend_tot > (int)len)
|| ((s->s3->wpend_buf != buf) &&
!(s->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER))
|| (s->s3->wpend_type != type))
{
SSLerr(SSL_F_SSL3_WRITE_PENDING,SSL_R_BAD_WRITE_RETRY);
return(-1);
}
for (;;)
// 循环直到全部数据发送完
{
clear_sys_error();
if (s->wbio != NULL)
{
s->rwstate=SSL_WRITING;
// 实际进行BIO写操作的是s3->wbuf中的数据,这是已经进行了压缩加密了的数据
i=BIO_write(s->wbio,
(char *)&(s->s3->wbuf.buf[s->s3->wbuf.offset]),
(unsigned int)s->s3->wbuf.left);
}
else
{
SSLerr(SSL_F_SSL3_WRITE_PENDING,SSL_R_BIO_NOT_SET);
i= -1;
}
if (i == s->s3->wbuf.left)
{
s->s3->wbuf.left=0;
s->rwstate=SSL_NOTHING;
// 发送完实际数据,返回的是原始明文数据的长度
return(s->s3->wpend_ret);
}
else if (i <= 0)
return(i);
s->s3->wbuf.offset+=i;
s->s3->wbuf.left-=i;
}
}

2.16 SSL_free

SSL_free()函数释放SSL结构:
/* ssl/ssl_lib.c */
void SSL_free(SSL *s)
{
int i;
if(s == NULL)
return;
// 加密锁引用减1
i=CRYPTO_add(&s->references,-1,CRYPTO_LOCK_SSL);
#ifdef REF_PRINT
REF_PRINT("SSL",s);
#endif
if (i > 0) return;
#ifdef REF_CHECK
if (i < 0)
{
fprintf(stderr,"SSL_free, bad reference countn");
abort(); /* ok */
}
#endif
// 释放加密库所需附加数据
CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL, s, &s->ex_data);
if (s->bbio != NULL)
{
// 释放BIO缓冲
/* If the buffering BIO is in place, pop it off */
if (s->bbio == s->wbio)
{
s->wbio=BIO_pop(s->wbio);
}
BIO_free(s->bbio);
s->bbio=NULL;
}
// 释放读BIO
if (s->rbio != NULL)
BIO_free_all(s->rbio);
// 释放写BIO
if ((s->wbio != NULL) && (s->wbio != s->rbio))
BIO_free_all(s->wbio);
// 释放初始化缓冲区
if (s->init_buf != NULL) BUF_MEM_free(s->init_buf);

/* add extra stuff */
// 释放加密库链表
if (s->cipher_list != NULL) sk_SSL_CIPHER_free(s->cipher_list);
if (s->cipher_list_by_id != NULL) sk_SSL_CIPHER_free(s->cipher_list_by_id);
/* Make the next call work :-) */
// 清除SSL的会话
if (s->session != NULL)
{
ssl_clear_bad_session(s);
SSL_SESSION_free(s->session);
}
// 释放SSL的读写上下文
ssl_clear_cipher_ctx(s);
// 释放证书
if (s->cert != NULL) ssl_cert_free(s->cert);
/* Free up if allocated */
// 释放加密算法上下文
if (s->ctx) SSL_CTX_free(s->ctx);
if (s->client_CA != NULL)
sk_X509_NAME_pop_free(s->client_CA,X509_NAME_free);
// 释放SSL方法
if (s->method != NULL) s->method->ssl_free(s);
// 释放SSL结构本身
OPENSSL_free(s);
}