pluto实现分析(22)
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
18. 密钥处理
pluto的配置文件中支持三种类型的密钥定义: 预共享密钥(PSK), RSA签名和证书,后两者其实是等
价的,只不过证书是经过了CA认证,本质都是RSA,所以在内部看来都是一种。这些密钥本质都是要
根据此密钥来协商出通信时的动态密钥,密钥基本处理函数在programs/pluto/keys.c中定义。
18.1 数据结构
// pluto支持的密钥类型
/* include/pluto_constants.h */
enum PrivateKeyKind {
// 共享密钥
PPK_PSK = 1,
/* PPK_DSS, */ /* not implemented */
// RSA
PPK_RSA = 3,
// Smart card
PPK_PIN = 4
};
其中PPK_PIN类型需要硬件支持, 如果是纯软件, 就用前两个就行了。
// 共享秘密结构
struct secret {
// 指向ID链表
struct id_list *ids;
// 序号
int secretlineno;
// 密钥类型
enum PrivateKeyKind kind;
// 具体密钥联合
union {
// 预共享密钥字符串
chunk_t preshared_secret;
// RSA私钥
struct RSA_private_key RSA_private_key;
// Smart card
smartcard_t *smartcard;
} u;
// 链表下一项
struct secret *next;
};
// id单向链表
struct id_list {
struct id id;
struct id_list *next;
};
// id结构
struct id {
// 类型
int kind; /* ID_* value */
// ip地址
ip_address ip_addr; /* ID_IPV4_ADDR, ID_IPV6_ADDR */
// FQDN名称字符串
chunk_t name; /* ID_FQDN, ID_USER_FQDN (with @) */
/* ID_KEY_ID, ID_DER_ASN_DN */
};
id结构是用来标志IPSec节点的ID信息, 在配置文件中是用leftid和rightid来定义, 最普通的ID标志
就是本方的地址, 所以该配置并不是必须的, 但也可以用FQDN来定义, 这样在处理动态IP连接时可以
根据此来认证对方。
18.2 初始化
在系统初始化或发送whack命令告诉pluto重新读取密钥文件时, 会调用如下的初始化函数:
void
load_preshared_secrets(int whackfd)
{
// 先释放掉当前的密钥链表, 准备重新初始化
free_preshared_secrets();
// 读取密钥文件, 缺省shared_secrets_file是/etc/ipsec.secrets
(void) process_secrets_file(shared_secrets_file, whackfd);
}
// 释放原有密钥
void
free_preshared_secrets(void)
{
// 加锁
lock_certs_and_keys("free_preshared_secrets");
// 密钥链表非空, secrets是全局变量
if (secrets != NULL)
{
struct secret *s, *ns;
openswan_log("forgetting secrets");
// 遍历链表
for (s = secrets; s != NULL; s = ns)
{
struct id_list *i, *ni;
// 保存链表中下一项的指针
ns = s->next; /* grab before freeing s */
// 释放密钥中的ID链表
for (i = s->ids; i != NULL; i = ni)
{
ni = i->next; /* grab before freeing i */
// 释放ID
free_id_content(&i->id);
pfree(i);
}
// 根据密钥类型释放密钥空间
switch (s->kind)
{
case PPK_PSK:
// 对于PSK,直接释放空间
pfree(s->u.preshared_secret.ptr);
break;
case PPK_RSA:
// 释放RSA,释放各相关参数
free_RSA_public_content(&s->u.RSA_private_key.pub);
mpz_clear(&s->u.RSA_private_key.d);
mpz_clear(&s->u.RSA_private_key.p);
mpz_clear(&s->u.RSA_private_key.q);
mpz_clear(&s->u.RSA_private_key.dP);
mpz_clear(&s->u.RSA_private_key.dQ);
mpz_clear(&s->u.RSA_private_key.qInv);
break;
#ifdef SMARTCARD
case PPK_PIN:
scx_release(s->u.smartcard);
break;
#endif
default:
bad_case(s->kind);
}
// 释放密钥本身空间
pfree(s);
}
secrets = NULL;
}
unlock_certs_and_keys("free_preshard_secrets");
}
18.3 读取密钥文件
static void
process_secrets_file(const char *file_pat, int whackfd)
{
struct file_lex_position pos;
char **fnp;
glob_t globbuf;
// 初始化清零
memset(&globbuf, 0, sizeof(glob_t));
// flp是全局变量,因为本函数可能会被递归调用,得控制递归深度
pos.depth = flp == NULL? 0 : flp->depth + 1;
// 如果深度超过10层, 返回
if (pos.depth > 10)
{
loglog(RC_LOG_SERIOUS, "preshared secrets file \"%s\" nested too deeply",
file_pat);
return;
}
/* do globbing */
{
// 处理文件名,根据文件模式返回具体的文件,文件名模式中是允许使用通配符的
int r = glob(file_pat, GLOB_ERR, globugh, &globbuf);
if (r != 0)
{
// 相关错误处理
switch (r)
{
case GLOB_NOSPACE:
loglog(RC_LOG_SERIOUS, "out of space processing secrets filename \"%
s\"", file_pat);
break;
case GLOB_ABORTED:
break; /* already logged */
case GLOB_NOMATCH:
loglog(RC_LOG_SERIOUS, "no secrets filename matched \"%s\"", file_pat);
break;
default:
loglog(RC_LOG_SERIOUS, "unknown glob error %d", r);
break;
}
globfree(&globbuf);
return;
}
}
/* for each file... */
// 处理每个文件
for (fnp = globbuf.gl_pathv; fnp!=NULL && *fnp != NULL; fnp++)
{
// 打开文件
if (lexopen(&pos, *fnp, FALSE))
{
openswan_log("loading secrets from \"%s\"", *fnp);
(void) flushline("file starts with indentation (continuation notation)");
// 处理文件记录, 参数都是通过全局变量flp获取的
process_secret_records(whackfd);
lexclose();
}
}
// 释放资源
globfree(&globbuf);
}
// 读取文件中密钥记录, 数据上下文是通过全局变量flp来获取的
static void
process_secret_records(int whackfd)
{
/* read records from ipsec.secrets and load them into our table */
// 循环读取文件
for (;;)
{
// 读取掉无关数据
(void)flushline(NULL); /* silently ditch leftovers, if any */
if (flp->bdry == B_file)
break;
flp->bdry = B_none; /* eat the Record Boundary */
// 读取第一个标志数据
(void)shift(); /* get real first token */
// include标志,允许再次包含文件
if (tokeqword("include"))
{
/* an include directive */
char fn[MAX_TOK_LEN]; /* space for filename (I hope) */
char *p = fn;
// 文件路径中最后一个'/'的位置
char *end_prefix = strrchr(flp->filename, '/');
// 继续读取下一个数据,也就是文件名
if (!shift())
{
// 读取失败的话跳过这一行
loglog(RC_LOG_SERIOUS, "\"%s\" line %d: unexpected end of include
directive"
, flp->filename, flp->lino);
continue; /* abandon this record */
}
/* if path is relative and including file's pathname has
* a non-empty dirname, prefix this path with that dirname.
*/
// 路径是相对路径
if (tok[0] != '/' && end_prefix != NULL)
{
// 目录部分的长度,包含'/'
size_t pl = end_prefix - flp->filename + 1;
/* "clamp" length to prevent problems now;
* will be rediscovered and reported later.
*/
if (pl > sizeof(fn))
pl = sizeof(fn);
// 拷贝目录部分名称
memcpy(fn, flp->filename, pl);
p += pl;
}
// 检查缓冲区剩余空间是否还能装得下文件名
if (flp->cur - tok >= &fn[sizeof(fn)] - p)
{
loglog(RC_LOG_SERIOUS, "\"%s\" line %d: include pathname too long"
, flp->filename, flp->lino);
continue; /* abandon this record */
}
// 拷贝文件名
strcpy(p, tok);
// 移动到下一字段
(void) shift(); /* move to Record Boundary, we hope */
// 到行末
if (flushline("ignoring malformed INCLUDE -- expected Record Boundary after
filename"))
{
// 递归调用处理文件函数
process_secrets_file(fn, whackfd);
tok = NULL; /* correct, but probably redundant */
}
}
else
{
// 非include项, 是具体的密钥记录
/* expecting a list of indices and then the key info */
// 分配新密钥结构
struct secret *s = alloc_thing(struct secret, "secret");
// 初始化结构中参数
// id_list设为空
s->ids = NULL;
// 共享密钥类型
s->kind = PPK_PSK; /* default */
// 清空密钥存储缓冲区
setchunk(s->u.preshared_secret, NULL, 0);
// 行号
s->secretlineno=flp->lino;
s->next = NULL;
// 准备开始解析
// 格式是: id1 id2 : key
for (;;)
{
// 如果当前字段token为":"
if (tokeq(":"))
{
/* found key part */
// 跳到下一字段: 密钥字段
shift(); /* discard explicit separator */
// 处理具体的密钥
process_secret(s, whackfd);
// 读取完成, 中断循环
break;
}
else
{
// 非":", 准备处理id
/* an id
* See RFC2407 IPsec Domain of Interpretation 4.6.2
*/
struct id id;
err_t ugh;
if (tokeq("%any"))
{
// 任意IPV4类型的id
id = empty_id;
id.kind = ID_IPV4_ADDR;
ugh = anyaddr(AF_INET, &id.ip_addr);
}
else if (tokeq("%any6"))
{
// 任意IPV6类型的id
id = empty_id;
id.kind = ID_IPV6_ADDR;
ugh = anyaddr(AF_INET6, &id.ip_addr);
}
else
{
// 其他类型,解析具体的id
ugh = atoid(tok, &id, FALSE);
}
if (ugh != NULL)
{
// 解析失败,打印信息
loglog(RC_LOG_SERIOUS
, "ERROR \"%s\" line %d: index \"%s\" %s"
, flp->filename, flp->lino, tok, ugh);
}
else
{
// 分配id_list结构
struct id_list *i = alloc_thing(struct id_list
, "id_list");
// 拷贝id结构
i->id = id;
// 如果是名称形式(如FQDN)的ID, 分配名称空间
unshare_id_content(&i->id);
// 将该ID加入到密钥结构的id链表
i->next = s->ids;
s->ids = i;
/* DBG_log("id type %d: %s %.*s", i->kind, ip_str(&i->ip_addr),
(int)i->name.len, i->name.ptr); */
}
// 再移到下一字段, 循环, 移动失败则中断循环
if (!shift())
{
/* unexpected Record Boundary or EOF */
loglog(RC_LOG_SERIOUS, "\"%s\" line %d: unexpected end of id
list"
, flp->filename, flp->lino);
break;
}
}
}
}
}
}
// 处理密钥
static void
process_secret(struct secret *s, int whackfd)
{
err_t ugh = NULL;
// 编译器报啥错误或警告呢?
whackfd = whackfd; /* shut up compiler */
// 初始化密钥缺省类型: 共享密钥
s->kind = PPK_PSK; /* default */
// 如果字段是"或', 也是老的PSK格式
if (*tok == '"' || *tok == '\'')
{
/* old PSK format: just a string */
// 处理PSK密钥
ugh = process_psk_secret(&s->u.preshared_secret);
}
else if (tokeqword("psk"))
// 字段token是PSK
{
/* preshared key: quoted string or ttodata format */
// 跳到下一字段, 处理PSK密钥
ugh = !shift()? "unexpected end of record in PSK"
: process_psk_secret(&s->u.preshared_secret);
}
else if (tokeqword("rsa"))
// 字段token是PSK
{
/* RSA key: the fun begins.
* A braced list of keyword and value pairs.
*/
// 密钥类型设置为RSA
s->kind = PPK_RSA;
// 跳到下一字段
if (!shift())
{
ugh = "bad RSA key syntax";
}
else if (tokeq("{"))
// 如果该字段是{, 处理RSA密钥
{
ugh = process_rsa_secret(&s->u.RSA_private_key);
}
else
{
// 否则处理RSA密钥文件
ugh = process_rsa_keyfile(&s->u.RSA_private_key, whackfd);
}
DBG(DBG_CONTROL,
DBG_log("loaded private key for keyid: %s:%s",
enum_name(&ppk_names, s->kind),
s->u.RSA_private_key.pub.keyid));
}
else if (tokeqword("pin"))
{
// PIN类型密钥
#ifdef SMARTCARD
ugh = process_pin(s, whackfd);
#else
ugh = "Smartcard not supported";
#endif
}
else
{
ugh = builddiag("unrecognized key format: %s", tok);
}
if (ugh != NULL)
{
// 处理失败, 释放分配的密钥结构
loglog(RC_LOG_SERIOUS, "\"%s\" line %d: %s"
, flp->filename, flp->lino, ugh);
pfree(s);
}
// 完成这一行数据
else if (flushline("expected record boundary in key"))
{
/* gauntlet has been run: install new secret */
lock_certs_and_keys("process_secret");
// 将密钥结构插入系统的密钥链表
s->next = secrets;
secrets = s;
unlock_certs_and_keys("process_secrets");
}
}
// 解析PSK密钥
/* parse PSK from file */
static err_t
process_psk_secret(chunk_t *psk)
{
err_t ugh = NULL;
if (*tok == '"' || *tok == '\'')
{
// 如果当前字段是"或', 直接拷贝下一字段作为PSK
clonetochunk(*psk, tok+1, flp->cur - tok - 2, "PSK");
(void) shift();
}
else
{
char buf[RSA_MAX_ENCODING_BYTES]; /* limit on size of binary
representation of key */
size_t sz;
// 将token转换为数据向量, 数据格式可为多种类型, 解析后的数据放buf中
ugh = ttodatav(tok, flp->cur - tok, 0, buf, sizeof(buf), &sz
, diag_space, sizeof(diag_space), TTODATAV_SPACECOUNTS);
if (ugh != NULL)
{
// 错误情况
/* ttodata didn't like PSK data */
ugh = builddiag("PSK data malformed (%s): %s", ugh, tok);
}
else
{
// 数据正确, 拷贝解析出的PSK
clonetochunk(*psk, buf, sz, "PSK");
(void) shift();
}
}
return ugh;
}
// 解析RSA密钥
// RSA密钥包括以下部分:
// 注释的pubkey
// 可公开部分: modules, PublicExpoent
// 必须保密部分: PrivateExponent, Prime1, Prime2, Exponent1, Exponent2, Coefficient
// RSA密钥的生成可通过openswan所提供的rsasigkey工具来生成, 包含了以上各字段
/*
RSA密钥各个域定义如下:
static const struct fld RSA_private_field[] =
{
{ "Modulus", offsetof(struct RSA_private_key, pub.n) },
{ "PublicExponent", offsetof(struct RSA_private_key, pub.e) },
{ "PrivateExponent", offsetof(struct RSA_private_key, d) },
{ "Prime1", offsetof(struct RSA_private_key, p) },
{ "Prime2", offsetof(struct RSA_private_key, q) },
{ "Exponent1", offsetof(struct RSA_private_key, dP) },
{ "Exponent2", offsetof(struct RSA_private_key, dQ) },
{ "Coefficient", offsetof(struct RSA_private_key, qInv) },
};
*/
/* 例子:
# Here is an RSA secret key.
# The empty index list means that it will be used unless a more specific match is found.
# This was generated by "ipsec rsasigkey 1024".
# The pubkey comment is suitable for copying into config.sys.
: RSA
{
# 1024 bits, Fri Feb 4 20:18:49 2000
# for signatures only, UNSAFE FOR ENCRYPTION
#pubkey=0x0103eb25f173b1d08a181e42efa6366973fa32e77f0beaf081ba9e5aad500ac5803cca6e2c61ad
0128e5042b1f77361900e03ec8e5cb9a69bb8355f2bf7c5eeedf187be5f6b4fecc1e84384c892ac6a14b4df5
d142c88a34b94015b92f1a5a9c6c5d94b26677e652bf02ac356bd7760551069abb6e34716ff55f6944bdca77
6d3d01
Modulus:
0xeb25f173b1d08a181e42efa6366973fa32e77f0beaf081ba9e5aad500ac5803cca6e2c61ad0128e5042b1f
77361900e03ec8e5cb9a69bb8355f2bf7c5eeedf187be5f6b4fecc1e84384c892ac6a14b4df5d142c88a34b9
4015b92f1a5a9c6c5d94b26677e652bf02ac356bd7760551069abb6e34716ff55f6944bdca776d3d01
PublicExponent: 0x03
# everything after this point is secret
PrivateExponent:
0x9cc3f64d2135b1656981f519799ba2a6cc9a54b29ca0567c6991c8e0072e557ddc4972ebc8ab7098ad7214
fa2410ab4029db43dd119bd2578ea1d4fd949f3f6460b5a63f0baf9297bbdd53e716488110001bd7b44c0ba4
2a13cd7db2483bc3cab84cad0c7042f482a7fae5eb88c522c41a9af62794df5ee51ab86cdd7dd84ae3
Prime1:
0xfb089c59ac846f6e8208363c1de4febf7dc54e6091a91c6d66bb27b2cddc77141c704d61209c038e00b928
e58874af38d95c7edef488e28e026cdccbf828e6cb
Prime2:
0xefcce0fcc0c053321c785514074f8af677e230d9867a26939149cadc20664f9963cf15841d524cb0af83ea
10a068eda799767e1a1d980479bec33db2427fe5e3
Exponent1:
0xa75b12e67302f4f456b0242813edff2a53d8deeb0bc612f399d21a7733e84f62bda0339615bd57b4007b70
9905a31f7b3b92ff3f4db0970956f33ddd501b4487
Exponent2:
0x9fddeb532b2ae221685038b804dfb1f9a54175e659a6c46260dbdc92c0443510ed34b902be36ddcb1fad46
b5c045f3c510f9a966be65585129d77e76d6ffee97
Coefficient:
0xd737e14baf3d5b51d64c7d0f046596b8e82344831f5041b96ade16106ebaab32a02d64e36295ca67864232
94d1c269c2f16e510d421f3c651d37bdc9eb16ff7d
}
*/
/* Parse fields of RSA private key.
* A braced list of keyword and value pairs.
* At the moment, each field is required, in order.
* The fields come from BIND 8.2's representation
*/
static err_t
process_rsa_secret(struct RSA_private_key *rsak)
{
// RSA密钥以二进制表示时的所需最大空间
char buf[RSA_MAX_ENCODING_BYTES]; /* limit on size of binary representation of key
*/
const struct fld *p;
/* save bytes of Modulus and PublicExponent for keyid calculation */
unsigned char ebytes[sizeof(buf)];
unsigned char *eb_next = ebytes;
chunk_t pub_bytes[2];
chunk_t *pb_next = &pub_bytes[0];
// 遍历RSA域数组, 包括以下部分:
// 可公开部分: modules, PublicExpoent
// 必须保密部分: PrivateExponent, Prime1, Prime2, Exponent1, Exponent2, Coefficient
for (p = RSA_private_field; p < &RSA_private_field[elemsof(RSA_private_field)]; p++)
{
size_t sz;
err_t ugh;
// 移到下一字段, 失败返回
if (!shift())
{
return "premature end of RSA key";
}
// 关键字比较, 失败返回
else if (!tokeqword(p->name))
{
return builddiag("%s keyword not found where expected in RSA key"
, p->name);
}
// 跳过":"字段
else if (!(shift()
&& (!tokeq(":") || shift()))) /* ignore optional ":" */
{
return "premature end of RSA key";
}
// 转换RSA密钥数据部分到buf缓冲区, 失败返回
else if (NULL != (ugh = ttodatav(tok, flp->cur - tok
, 0, buf, sizeof(buf), &sz, diag_space, sizeof(diag_space)
, TTODATAV_SPACECOUNTS)))
{
/* in RSA key, ttodata didn't like */
return builddiag("RSA data malformed (%s): %s", ugh, tok);
}
else
{
// 密钥数据转换成功,
MP_INT *n = (MP_INT *) ((char *)rsak + p->offset);
// 将buf中数据转换为MP_INT数据, 需要mpz库的支持
n_to_mpz(n, buf, sz);
if (pb_next < &pub_bytes[elemsof(pub_bytes)])
{
if (eb_next - ebytes + sz > sizeof(ebytes))
return "public key takes too many bytes";
setchunk(*pb_next, eb_next, sz);
memcpy(eb_next, buf, sz);
eb_next += sz;
pb_next++;
}
#if 0 /* debugging info that compromises security */
{
size_t sz = mpz_sizeinbase(n, 16);
char buf[RSA_MAX_OCTETS * 2 + 2]; /* ought to be big enough */
passert(sz <= sizeof(buf));
mpz_get_str(buf, 16, n);
loglog(RC_LOG_SERIOUS, "%s: %s", p->name, buf);
}
#endif
}
}
/* We require an (indented) '}' and the end of the record.
* We break down the test so that the diagnostic will be
* more helpful. Some people don't seem to wish to indent
* the brace!
*/
if (!shift() || !tokeq("}"))
{
// 没有结束符"}"
return "malformed end of RSA private key -- indented '}' required";
}
else if (shift())
{
// 没有最后结束符"\n"
return "malformed end of RSA private key -- unexpected token after '}'";
}
else
{
// 读取RSA密钥成功结束
// RSA密钥位数
unsigned bits = mpz_sizeinbase(&rsak->pub.n, 2);
rsak->pub.k = (bits + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
rsak->pub.keyid[0] = '\0'; /* in case of splitkeytoid failure */
splitkeytoid(pub_bytes[1].ptr, pub_bytes[1].len
, pub_bytes[0].ptr, pub_bytes[0].len
, rsak->pub.keyid, sizeof(rsak->pub.keyid));
// 检查RSA密钥数据是否合法
return RSA_private_key_sanity(rsak);
}
}
18.4 获取密钥
在开始协商时, 对于客户端发来的数据包, 先要确定是哪个连接的, 然后根据连接查找相关的密钥,
因为密钥文件中不带连接信息, 而是连接双方的ID, 所以需要根据连接信息再查找合适的密钥。
// 以下是读取共享密钥和RSA密钥函数, 函数都比较简单, 主体都是调用get_secret()函数
/* check the existence of an RSA private key matching an RSA public
* key contained in an X.509 or OpenPGP certificate
*/
const chunk_t *
get_preshared_secret(const struct connection *c)
{
// 最后一个参数是FALSE
const struct secret *s = get_secret(c, PPK_PSK, FALSE);
#ifdef DEBUG
DBG(DBG_PRIVATE,
if (s == NULL)
DBG_log("no Preshared Key Found");
else
DBG_dump_chunk("Preshared Key", s->u.preshared_secret);
);
#endif
return s == NULL? NULL : &s->u.preshared_secret;
}
/* find the appropriate RSA private key (see get_secret).
* Failure is indicated by a NULL pointer.
*/
const struct RSA_private_key *
get_RSA_private_key(const struct connection *c)
{
// 最后一个参数是TRUE
const struct secret *s = get_secret(c, PPK_RSA, TRUE);
#ifdef DEBUG
DBG(DBG_PRIVATE,
if (s == NULL)
DBG_log("no RSA key Found");
else
DBG_log("rsa key %s found", s->u.RSA_private_key.pub.keyid);
);
#endif
return s == NULL? NULL : &s->u.RSA_private_key;
}
// 获取密钥基本函数
/* find the struct secret associated with the combination of
* me and the peer. We match the Id (if none, the IP address).
* Failure is indicated by a NULL.
*/
// 参数为: 连接, 密钥类型, 是否是非对称密钥(RSA是非对称的, PSK是对称的)
static const struct secret *
get_secret(const struct connection *c, enum PrivateKeyKind kind, bool asym)
{
enum { /* bits */
match_default = 01,
match_him = 02,
match_me = 04
};
unsigned char idstr1[IDTOA_BUF], idme[IDTOA_BUF]
, idhim[IDTOA_BUF], idhim2[IDTOA_BUF];
unsigned int best_match = 0;
struct secret *best = NULL;
struct secret *s;
// 双方ID
const struct id *my_id = &c->spd.this.id
, *his_id = &c->spd.that.id;
struct id rw_id;
// 将ID转换为字符串
idtoa(my_id, idme, IDTOA_BUF);
idtoa(his_id, idhim, IDTOA_BUF);
strcpy(idhim2, idhim);
DBG(DBG_CONTROL,
DBG_log("started looking for secret for %s->%s of kind %s"
, idme, idhim
, enum_name(&ppk_names, kind)));
/* is there a certificate assigned to this connection? */
// 如果是RSA密钥类型, 连接结构定义的密钥类型也是RSA相关的
if (kind == PPK_RSA
&& c->spd.this.sendcert != cert_forcedtype
&& (c->spd.this.cert.type == CERT_X509_SIGNATURE ||
c->spd.this.cert.type == CERT_PKCS7_WRAPPED_X509 ||
c->spd.this.cert.type == CERT_PGP))
{
// 分配本地公钥临时空间
struct pubkey *my_public_key = allocate_RSA_public_key(c->spd.this.cert);
passert(my_public_key != NULL);
// 遍历当前密钥链表
for (s = secrets; s != NULL; s = s->next)
{
DBG(DBG_CONTROL,
DBG_log("searching for certificate %s:%s vs %s:%s"
, enum_name(&ppk_names, s->kind)
, (s->kind==PPK_RSA ? s->u.RSA_private_key.pub.keyid : "N/A")
, enum_name(&ppk_names, kind)
, my_public_key->u.rsa.keyid)
);
// 匹配类型, 匹配RSA密钥
if (s->kind == kind &&
same_RSA_public_key(&s->u.RSA_private_key.pub
, &my_public_key->u.rsa))
{
// 如果找到, 中断循环
best = s;
break; /* we have found the private key - no sense in searching further
*/
}
}
// 释放公钥空间
free_public_key(my_public_key);
// 返回密钥
return best;
}
#if defined(AGGRESSIVE)
// 策略中没定义野蛮模式, 而且对方ID已经实例化(针对roadwarrior的动态连接)
if (his_id_was_instantiated(c) && !(c->policy & POLICY_AGGRESSIVE))
{
DBG(DBG_CONTROL,
DBG_log("instantiating him to 0.0.0.0"));
/* roadwarrior: replace him with 0.0.0.0 */
// 用对方地址来作为对方的ID
rw_id.kind = addrtypeof(&c->spd.that.host_addr) == AF_INET ?
ID_IPV4_ADDR : ID_IPV6_ADDR;
happy(anyaddr(addrtypeof(&c->spd.that.host_addr), &rw_id.ip_addr));
his_id = &rw_id;
idtoa(his_id, idhim2, IDTOA_BUF);
}
#endif
#ifdef NAT_TRAVERSAL
// 如果允许NAT穿越
else if ((nat_traversal_enabled)
// 连接使用的是PSK
&& (c->policy & POLICY_PSK)
// 要求查找PSK类型密钥
&& (kind == PPK_PSK)
// 连接类型是模板而且没设置对方ID
&& (((c->kind == CK_TEMPLATE)
&& (c->spd.that.id.kind == ID_NONE))
// 连接类型是实例化的连接而且对方ID是IP地址
|| ((c->kind == CK_INSTANCE)
&& (id_is_ipaddr(&c->spd.that.id)))))
{
DBG(DBG_CONTROL,
DBG_log("replace him to 0.0.0.0"));
/* roadwarrior: replace him with 0.0.0.0 */
// 用对方的IP地址作为对方ID
rw_id.kind = ID_IPV4_ADDR;
happy(anyaddr(addrtypeof(&c->spd.that.host_addr), &rw_id.ip_addr));
his_id = &rw_id;
idtoa(his_id, idhim2, IDTOA_BUF);
}
#endif
DBG(DBG_CONTROL,
DBG_log("actually looking for secret for %s->%s of kind %s"
, idme, idhim2
, enum_name(&ppk_names, kind)));
// 遍历密钥链表查找
for (s = secrets; s != NULL; s = s->next)
{
// 类型是否匹配
if (s->kind == kind)
{
unsigned int match = 0;
if (s->ids == NULL)
{
// 密钥对应的ID链表为空, 匹配类型设置为缺省匹配
/* a default (signified by lack of ids):
* accept if no more specific match found
*/
match = match_default;
}
else
{
/* check if both ends match ids */
struct id_list *i;
int idnum = 0;
// 遍历密钥的ID链表
for (i = s->ids; i != NULL; i = i->next)
{
idnum++;
// 将ID转换为字符串
idtoa(&i->id, idstr1, IDTOA_BUF);
// 检查ID是否匹配本地和对方的ID
if (same_id(my_id, &i->id))
match |= match_me;
if (same_id(his_id, &i->id))
match |= match_him;
DBG(DBG_CONTROL,
DBG_log("%d: compared PSK %s to %s / %s -> %d",
idnum, idstr1, idme, idhim, match));
// 这里似乎可以加一个检查,如果match_me和match_him都有了, 就可以中断循环了
}
/* If our end matched the only id in the list,
* default to matching any peer.
* A more specific match will trump this.
*/
// 如果匹配自身
if (match == match_me
// ID链表只有一个ID节点
&& s->ids->next == NULL)
match |= match_default;
}
// 匹配的情况类型
switch (match)
{
// 先检查是否匹配自身
case match_me:
/* if this is an asymmetric (eg. public key) system,
* allow this-side-only match to count, even if
* there are other ids in the list.
*/
if (!asym)
break;
/* FALLTHROUGH */
// 其他需要的匹配条件, 其他情况就属于不匹配
case match_default: /* default all */
case match_me | match_default: /* default peer */
case match_me | match_him: /* explicit */
// best_match初始化为0, 在找到第一个可用的best前该条件是不满足的
if (match == best_match)
{
// 如果满足了该条件, best指针就是非空的了
/* two good matches are equally good:
* do they agree?
*/
bool same;
// 分别检查当前密钥和已找出的最佳密钥是否匹配
switch (kind)
{
case PPK_PSK:
same = s->u.preshared_secret.len == best->u.preshared_secret.len
&& memcmp(s->u.preshared_secret.ptr
, best->u.preshared_secret.ptr
, s->u.preshared_secret.len) == 0;
break;
case PPK_RSA:
/* Dirty trick: since we have code to compare
* RSA public keys, but not private keys, we
* make the assumption that equal public keys
* mean equal private keys. This ought to work.
*/
same = same_RSA_public_key(&s->u.RSA_private_key.pub
, &best->u.RSA_private_key.pub);
break;
default:
bad_case(kind);
}
if (!same)
{
// 不匹配的话说明是相同的ID对使用不同的密钥
loglog(RC_LOG_SERIOUS, "multiple ipsec.secrets entries with
distinct secrets match endpoints:"
" first secret used");
// 用新的密钥替代原来的
best = s; /* list is backwards: take latest in list */
}
}
else if (match > best_match)
{
// 如果更匹配, 更新best密钥
DBG(DBG_CONTROL,
DBG_log("best_match %d>%d best=%p (line=%d)"
, best_match, match
, s, s->secretlineno));
/* this is the best match so far */
best_match = match;
best = s;
} else {
DBG(DBG_CONTROL,
DBG_log("match(%d) was not best_match(%d)"
, match, best_match));
}
}
}
}
DBG(DBG_CONTROL,
DBG_log("concluding with best_match=%d best=%p (lineno=%d)"
, best_match, best, best? best->secretlineno : -1));
return best;
}
...... 待续 ......