修改 OpenVPN 实现加密算法的自动协商

另一篇博中的分析可知,OpenVPN 中有两个加解密通道。一条是标准的 SSL 协议通道,被 OpenVPN 用于协商自己所用的密钥。这个通道的加密算法当然也是通过 SSL 协议来进行协商的,可以通过 --tls-cipher 选项来进行配置。另一条是 OpenVPN 自己的加解密通道,用于交换实际的数据,也就是虚拟网卡抓到的 IP 报文。这个通道的加密算法则是通过 --cipher--auth 两个选项,分别在通调两端指定的。

对于第二条通道的加密算法,必须要同时在两端分别指定一致的选项,有时候不是很方便(当然,我研究的还是 2.1.1 版本的 OpenVPN ,不知道最新的版本还是不是这样)。比如说,我想通过在服务端修改配置,指定加密算法,然后让连接我的客户端自动用同一个算法。最简单的修改思路,就是借用第一条通道中的算法协商机制,从 SSL 对象中取得协商出来的算法。

具体做法就是:

首先,在函数 do_init_crypto_tls_c1() 中,去掉对函数 init_key_type() 的调用。这个调用就是根据 --cipher--auth 选项进行算法配置的地方,我们要动态协商,自然是不需要这个了。

但同时,这会引起接下来一个步骤的错误。在函数 crypto_adjust_frame_parameters() 中,会根据之前配置的算法进行报文中密钥空间的分配。现在还不知道算法,怎么知道要分配多少空间呢?就只能改成最大值了。分别改为 MAX_CIPHER_KEY_LENGTHMAX_HMAC_KEY_LENGTH 就行了。当然,这样改不仅浪费空间,而且也不够严谨,因为 key length 和 IV length 不是一回事,却只能都用 MAX_CIPHER_KEY_LENGTH 来初始化。

最后就是在 SSL 协商好之后,从里面取加密算法了。具体位置在 key_method_2_write()key_method_2_read() 两个函数中,对 generate_key_expansion() 函数的调用之处了。在调用之前,初始化一下 key_type 就行了:

1
2
3
4
5
6
7
8
9
struct key_type *key_type = (struct key_type *) session->opt->key_type;
key_type->cipher = ks->ssl->enc_read_ctx->cipher;
key_type->cipher_length = EVP_CIPHER_key_length (session->opt->key_type->cipher);
#if OPENSSL_VERSION_NUMBER >= 0x010000000L
key_type->digest = ks->ssl->read_hash->digest;
#else
key_type->digest = ks->ssl->read_hash;
#endif
key_type->hmac_length = EVP_MD_size (session->opt->key_type->digest);

这样就行了。

 Share!

 
comments powered by Disqus