pluto实现分析(22)
生活随笔
收集整理的這篇文章主要介紹了
pluto实现分析(22)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文檔的Copyleft歸yfydz所有,使用GPL發布,可以自由拷貝,轉載,轉載時請保持文檔的完整性, 嚴禁用于任何商業用途。
msn: yfydz_no1@hotmail.com
來源:http://yfydz.cublog.cn
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;
}
...... 待續 ......
msn: yfydz_no1@hotmail.com
來源:http://yfydz.cublog.cn
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;
}
...... 待續 ......
轉載于:https://blog.51cto.com/enchen/157926
總結
以上是生活随笔為你收集整理的pluto实现分析(22)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决我的windows不是正版的方法?
- 下一篇: Liaoning Province--保