key.c
来自「eCos操作系统源码」· C语言 代码 · 共 2,476 行 · 第 1/5 页
C
2,476 行
#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. */intkey_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 LIST_FOREACH(sah, &sahtree, chain) { /* search valid state */ for (stateidx = 0; stateidx < _ARRAYLEN(saorder_state_valid); stateidx++) { state = saorder_state_valid[stateidx]; LIST_FOREACH(sav, &sah->savtree[state], chain) { /* sanity check */ KEY_CHKSASTATE(sav->state, state, "key_allocsav"); if (proto != sav->sah->saidx.proto) continue; if (spi != sav->spi) continue; if (family != sav->sah->saidx.src.ss_family || family != sav->sah->saidx.dst.ss_family) continue;#if 0 /* don't check src */ /* check src address */ switch (family) { case AF_INET: bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(sin); bcopy(src, &sin.sin_addr, sizeof(sin.sin_addr)); if (key_sockaddrcmp((struct sockaddr*)&sin, (struct sockaddr *)&sav->sah->saidx.src, 0) != 0) continue; break; case AF_INET6: bzero(&sin6, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(sin6); bcopy(src, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { /* kame fake scopeid */ sin6.sin6_scope_id = ntohs(sin6.sin6_addr.s6_addr16[1]); sin6.sin6_addr.s6_addr16[1] = 0; } if (key_sockaddrcmp((struct sockaddr*)&sin6,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?