Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2223439
  • 博文数量: 6
  • 博客积分: 10011
  • 博客等级: 上将
  • 技术积分: 2870
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-21 09:34
文章分类

全部博文(6)

文章存档

2008年(6)

我的朋友

分类: 网络与安全

2008-04-05 23:59:45

pluto实现分析(22)

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
严禁用于任何商业用途。
msn:
来源: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;
}

...... 待续 ......
阅读(5022) | 评论(11) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2009-07-29 16:34:22

这个问题我也遇到了,调了一下发现是SA引用表满了。看了一下SARefTable相关的代码,觉得这东西没什么用,狠了狠心给去掉了。之后就再也没出过这个问题。竭泽而渔,不知道会不会有害处。

clintao2008-12-16 20:47:37

你好 yfydz .下面问题你有没有碰到过 大并发ipsec 连接,一段时间后报错 2008/12/16 04:30:05 INTERNET pluto[1477]: ERROR: "PROFILE_1"[939] 121.229.205.16 #21902: pfkey write() of SADB_X_GRPSA message 70434 for group unk0.471a@218.94.70.147 failed. Errno 28: No space left on device 后面pluto 就不work 了 。 kernel_pfkey.c 中 write(pfkeyfd, pfkey_msg, len); 返回的错误。 rekey太多了, pfkeyfd 堵死了? pfkeyfd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); 有参数调节性能?

yfydz2008-04-21 21:12:13

EFAULT一般是内存空间出问题, 如非法访问, 向关闭的socket发数据后应该不会报EFAULT,而是其他错误 to rejob : 自己去找; L2TP可以实现的,自己试

chinaunix网友2008-04-21 10:53:37

http://blog.chinaunix.net/u/12313/showart_326041.html 发送数据的时候, 如果发现没有sa bundle,或者说没有SA,那么XFRM 如何启动IKE过程呢? 需要额外添加code吗? 我看xfrm没有 sa的时候就返回err。 奇怪哈?

rejob2008-04-16 19:38:56

freeswan中有没有preshared.c文件? openswan中预共享密钥实现可不可以不用L2TP?如何实现呢? 请教 毕业设计用的 急 谢谢