📄 key.c
字号:
static struct secspacq *key_newspacq __P((struct secpolicyindex *));
static struct secspacq *key_getspacq __P((struct secpolicyindex *));
static int key_acquire2 __P((struct socket *, struct mbuf *,
const struct sadb_msghdr *));
static int key_register __P((struct socket *, struct mbuf *,
const struct sadb_msghdr *));
static int key_expire __P((struct secasvar *));
static int key_flush __P((struct socket *, struct mbuf *,
const struct sadb_msghdr *));
static int key_dump __P((struct socket *, struct mbuf *,
const struct sadb_msghdr *));
static int key_promisc __P((struct socket *, struct mbuf *,
const struct sadb_msghdr *));
static int key_senderror __P((struct socket *, struct mbuf *, int));
static int key_validate_ext __P((const struct sadb_ext *, int));
static int key_align __P((struct mbuf *, struct sadb_msghdr *));
#if 0
static const char *key_getfqdn __P((void));
static const char *key_getuserfqdn __P((void));
#endif
static void key_sa_chgstate __P((struct secasvar *, u_int8_t));
static struct mbuf *key_alloc_mbuf __P((int));
#ifdef __NetBSD__
struct callout key_timehandler_ch;
#endif
/* %%% IPsec policy management */
/*
* allocating a SP for OUTBOUND or INBOUND packet.
* Must call key_freesp() later.
* OUT: NULL: not found
* others: found and return the pointer.
*/
struct secpolicy *
key_allocsp(spidx, dir)
struct secpolicyindex *spidx;
u_int dir;
{
struct secpolicy *sp;
int s;
/* sanity check */
if (spidx == NULL)
panic("key_allocsp: NULL pointer is passed.\n");
/* check direction */
switch (dir) {
case IPSEC_DIR_INBOUND:
case IPSEC_DIR_OUTBOUND:
break;
default:
panic("key_allocsp: Invalid direction is passed.\n");
}
/* get a SP entry */
#ifdef __NetBSD__
s = splsoftnet(); /*called from softclock()*/
#else
s = splnet(); /*called from softclock()*/
#endif
KEYDEBUG(KEYDEBUG_IPSEC_DATA,
printf("*** objects\n");
kdebug_secpolicyindex(spidx));
LIST_FOREACH(sp, &sptree[dir], chain) {
KEYDEBUG(KEYDEBUG_IPSEC_DATA,
printf("*** in SPD\n");
kdebug_secpolicyindex(&sp->spidx));
if (sp->state == IPSEC_SPSTATE_DEAD)
continue;
if (key_cmpspidx_withmask(&sp->spidx, spidx))
goto found;
}
splx(s);
return NULL;
found:
/* sanity check */
KEY_CHKSPDIR(sp->spidx.dir, dir, "key_allocsp");
/* found a SPD entry */
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
sp->lastused = time_second;
#else
sp->lastused = time.tv_sec;
#endif
sp->refcnt++;
splx(s);
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP key_allocsp cause refcnt++:%d SP:%p\n",
sp->refcnt, sp));
return sp;
}
/*
* return a policy that matches this particular inbound packet.
* XXX slow
*/
struct secpolicy *
key_gettunnel(osrc, odst, isrc, idst)
struct sockaddr *osrc, *odst, *isrc, *idst;
{
struct secpolicy *sp;
const int dir = IPSEC_DIR_INBOUND;
int s;
struct ipsecrequest *r1, *r2, *p;
struct sockaddr *os, *od, *is, *id;
struct secpolicyindex spidx;
if (isrc->sa_family != idst->sa_family) {
ipseclog((LOG_ERR, "protocol family mismatched %d != %d\n.",
isrc->sa_family, idst->sa_family));
return NULL;
}
#ifdef __NetBSD__
s = splsoftnet(); /*called from softclock()*/
#else
s = splnet(); /*called from softclock()*/
#endif
LIST_FOREACH(sp, &sptree[dir], chain) {
if (sp->state == IPSEC_SPSTATE_DEAD)
continue;
r1 = r2 = NULL;
for (p = sp->req; p; p = p->next) {
if (p->saidx.mode != IPSEC_MODE_TUNNEL)
continue;
r1 = r2;
r2 = p;
if (!r1) {
/* here we look at address matches only */
spidx = sp->spidx;
if (isrc->sa_len > sizeof(spidx.src) ||
idst->sa_len > sizeof(spidx.dst))
continue;
bcopy(isrc, &spidx.src, isrc->sa_len);
bcopy(idst, &spidx.dst, idst->sa_len);
if (!key_cmpspidx_withmask(&sp->spidx, &spidx))
continue;
} else {
is = (struct sockaddr *)&r1->saidx.src;
id = (struct sockaddr *)&r1->saidx.dst;
if (key_sockaddrcmp(is, isrc, 0) ||
key_sockaddrcmp(id, idst, 0))
continue;
}
os = (struct sockaddr *)&r2->saidx.src;
od = (struct sockaddr *)&r2->saidx.dst;
if (key_sockaddrcmp(os, osrc, 0) ||
key_sockaddrcmp(od, odst, 0))
continue;
goto found;
}
}
splx(s);
return NULL;
found:
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
sp->lastused = time_second;
#else
sp->lastused = time.tv_sec;
#endif
sp->refcnt++;
splx(s);
return sp;
}
/*
* allocating an SA entry for an *OUTBOUND* packet.
* checking each request entries in SP, and acquire an SA if need.
* OUT: 0: there are valid requests.
* ENOENT: policy may be valid, but SA with REQUIRE is on acquiring.
*/
int
key_checkrequest(isr, saidx)
struct ipsecrequest *isr;
struct secasindex *saidx;
{
u_int level;
int error;
/* sanity check */
if (isr == NULL || saidx == NULL)
panic("key_checkrequest: NULL pointer is passed.\n");
/* check mode */
switch (saidx->mode) {
case IPSEC_MODE_TRANSPORT:
case IPSEC_MODE_TUNNEL:
break;
case IPSEC_MODE_ANY:
default:
panic("key_checkrequest: Invalid policy defined.\n");
}
/* get current level */
level = ipsec_get_reqlevel(isr);
#if 0
/*
* We do allocate new SA only if the state of SA in the holder is
* SADB_SASTATE_DEAD. The SA for outbound must be the oldest.
*/
if (isr->sav != NULL) {
if (isr->sav->sah == NULL)
panic("key_checkrequest: sah is null.\n");
if (isr->sav == (struct secasvar *)LIST_FIRST(
&isr->sav->sah->savtree[SADB_SASTATE_DEAD])) {
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP checkrequest calls free SA:%p\n",
isr->sav));
key_freesav(isr->sav);
isr->sav = NULL;
}
}
#else
/*
* we free any SA stashed in the IPsec request because a different
* SA may be involved each time this request is checked, either
* because new SAs are being configured, or this request is
* associated with an unconnected datagram socket, or this request
* is associated with a system default policy.
*
* The operation may have negative impact to performance. We may
* want to check cached SA carefully, rather than picking new SA
* every time.
*/
if (isr->sav != NULL) {
key_freesav(isr->sav);
isr->sav = NULL;
}
#endif
/*
* new SA allocation if no SA found.
* key_allocsa_policy should allocate the oldest SA available.
* See key_do_allocsa_policy(), and draft-jenkins-ipsec-rekeying-03.txt.
*/
if (isr->sav == NULL)
isr->sav = key_allocsa_policy(saidx);
/* When there is SA. */
if (isr->sav != NULL)
return 0;
/* there is no SA */
if ((error = key_acquire(saidx, isr->sp)) != 0) {
/* XXX What should I do ? */
ipseclog((LOG_DEBUG, "key_checkrequest: error %d returned "
"from key_acquire.\n", error));
return error;
}
return level == IPSEC_LEVEL_REQUIRE ? ENOENT : 0;
}
/*
* allocating a SA for policy entry from SAD.
* NOTE: searching SAD of aliving state.
* OUT: NULL: not found.
* others: found and return the pointer.
*/
static struct secasvar *
key_allocsa_policy(saidx)
struct secasindex *saidx;
{
struct secashead *sah;
struct secasvar *sav;
u_int stateidx, state;
LIST_FOREACH(sah, &sahtree, chain) {
if (sah->state == SADB_SASTATE_DEAD)
continue;
if (key_cmpsaidx(&sah->saidx, saidx, CMP_MODE_REQID))
goto found;
}
return NULL;
found:
/* search valid state */
for (stateidx = 0;
stateidx < _ARRAYLEN(saorder_state_valid);
stateidx++) {
state = saorder_state_valid[stateidx];
sav = key_do_allocsa_policy(sah, state);
if (sav != NULL)
return sav;
}
return NULL;
}
/*
* searching SAD with direction, protocol, mode and state.
* called by key_allocsa_policy().
* OUT:
* NULL : not found
* others : found, pointer to a SA.
*/
static struct secasvar *
key_do_allocsa_policy(sah, state)
struct secashead *sah;
u_int state;
{
struct secasvar *sav, *nextsav, *candidate, *d;
/* initilize */
candidate = NULL;
for (sav = LIST_FIRST(&sah->savtree[state]);
sav != NULL;
sav = nextsav) {
nextsav = LIST_NEXT(sav, chain);
/* sanity check */
KEY_CHKSASTATE(sav->state, state, "key_do_allocsa_policy");
/* initialize */
if (candidate == NULL) {
candidate = sav;
continue;
}
/* Which SA is the better ? */
/* sanity check 2 */
if (candidate->lft_c == NULL || sav->lft_c == NULL)
panic("key_do_allocsa_policy: "
"lifetime_current is NULL.\n");
/* What the best method is to compare ? */
if (key_preferred_oldsa) {
if (candidate->lft_c->sadb_lifetime_addtime >
sav->lft_c->sadb_lifetime_addtime) {
candidate = sav;
}
continue;
/*NOTREACHED*/
}
/* preferred new sa rather than old sa */
if (candidate->lft_c->sadb_lifetime_addtime <
sav->lft_c->sadb_lifetime_addtime) {
d = candidate;
candidate = sav;
} else
d = sav;
/*
* prepared to delete the SA when there is more
* suitable candidate and the lifetime of the SA is not
* permanent.
*/
if (d->lft_c->sadb_lifetime_addtime != 0) {
struct mbuf *m, *result;
key_sa_chgstate(d, SADB_SASTATE_DEAD);
key_freesav(d);
m = key_setsadbmsg(SADB_DELETE, 0,
sav->sah->saidx.proto, 0, 0, d->refcnt);
if (!m)
return NULL;
result = m;
/* set sadb_address for saidx's. */
m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC,
(struct sockaddr *)&d->sah->saidx.src,
FULLMASK, IPSEC_ULPROTO_ANY);
if (!m)
return NULL;
m_cat(result, m);
/* set sadb_address for saidx's. */
m = key_setsadbaddr(SADB_EXT_ADDRESS_DST,
(struct sockaddr *)&d->sah->saidx.dst,
FULLMASK, IPSEC_ULPROTO_ANY);
if (!m)
return NULL;
m_cat(result, m);
/* create SA extension */
m = key_setsadbsa(d);
if (!m)
return NULL;
m_cat(result, m);
if (result->m_len < sizeof(struct sadb_msg)) {
result = m_pullup(result,
sizeof(struct sadb_msg));
if (result == NULL)
return NULL;
}
result->m_pkthdr.len = 0;
for (m = result; m; m = m->m_next)
result->m_pkthdr.len += m->m_len;
mtod(result, struct sadb_msg *)->sadb_msg_len =
PFKEY_UNIT64(result->m_pkthdr.len);
if (key_sendup_mbuf(NULL, result,
KEY_SENDUP_REGISTERED))
return NULL;
}
}
if (candidate) {
candidate->refcnt++;
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP allocsa_policy cause "
"refcnt++:%d SA:%p\n",
candidate->refcnt, candidate));
}
return candidate;
}
/*
* allocating a SA entry for a *INBOUND* packet.
* Must call key_freesav() later.
* OUT: positive: pointer to a sav.
* NULL: not found, or error occured.
*
* In the comparison, source address will be ignored for RFC2401 conformance.
* To quote, from section 4.1:
* A security association is uniquely identified by a triple consisting
* of a Security Parameter Index (SPI), an IP Destination Address, and a
* security protocol (AH or ESP) identifier.
* Note that, however, we do need to keep source address in IPsec SA.
* IKE specification and PF_KEY specification do assume that we
* keep source address in IPsec SA. We see a tricky situation here.
*/
struct secasvar *
key_allocsa(family, src, dst, proto, spi)
u_int family, proto;
caddr_t src, dst;
u_int32_t spi;
{
struct secashead *sah;
struct secasvar *sav;
u_int stateidx, state;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
int s;
/* sanity check */
if (src == NULL || dst == NULL)
panic("key_allocsa: NULL pointer is passed.\n");
/*
* searching SAD.
* XXX: to be checked internal IP header somewhere. Also when
* IPsec tunnel packet is received. But ESP tunnel mode is
* encrypted so we can't check internal IP header.
*/
#ifdef __NetBSD__
s = splsoftnet(); /*called from softclock()*/
#else
s = splnet(); /*called from softclock()*/
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -