Changeset View
Changeset View
Standalone View
Standalone View
sys/netipsec/ipsec.c
Context not available. | |||||
#include "opt_ipsec.h" | #include "opt_ipsec.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/eventhandler.h> | |||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/lock.h> | |||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <sys/domain.h> | #include <sys/domain.h> | ||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <sys/protosw.h> | #include <sys/protosw.h> | ||||
#include <sys/rmlock.h> | |||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/socketvar.h> | #include <sys/socketvar.h> | ||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
Context not available. | |||||
#include <sys/syslog.h> | #include <sys/syslog.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/refcount.h> | |||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
Context not available. | |||||
VNET_DEFINE(int, ip6_ah_net_deflev) = IPSEC_LEVEL_USE; | VNET_DEFINE(int, ip6_ah_net_deflev) = IPSEC_LEVEL_USE; | ||||
VNET_DEFINE(int, ip6_ipsec_ecn) = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ | VNET_DEFINE(int, ip6_ipsec_ecn) = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ | ||||
static VNET_DEFINE(TAILQ_HEAD(_socketsptree, inpcbpolicy), socketsptree); | |||||
static struct rmlock socksptree_lock; | |||||
#define V_socketsptree VNET(socketsptree) | |||||
#define SOCKSPTREE_LOCK_INIT() rm_init(&socksptree_lock, "socksptree") | |||||
#define SOCKSPTREE_LOCK_DESTROY() rm_destroy(&socksptree_lock) | |||||
#define SOCKSPTREE_RLOCK_TRACKER struct rm_priotracker socksptree_tracker | |||||
#define SOCKSPTREE_RLOCK() rm_rlock(&socksptree_lock, &socksptree_tracker) | |||||
#define SOCKSPTREE_RUNLOCK() rm_runlock(&socksptree_lock, &socksptree_tracker) | |||||
#define SOCKSPTREE_RLOCK_ASSERT() rm_assert(&socksptree_lock, RA_RLOCKED) | |||||
#define SOCKSPTREE_WLOCK() rm_wlock(&socksptree_lock) | |||||
#define SOCKSPTREE_WUNLOCK() rm_wunlock(&socksptree_lock) | |||||
#define SOCKSPTREE_WLOCK_ASSERT() rm_assert(&socksptree_lock, RA_WLOCKED) | |||||
#define SOCKSPTREE_UNLOCK_ASSERT() rm_assert(&socksptree_lock, RA_UNLOCKED) | |||||
SYSCTL_DECL(_net_inet6_ipsec6); | SYSCTL_DECL(_net_inet6_ipsec6); | ||||
/* net.inet6.ipsec6 */ | /* net.inet6.ipsec6 */ | ||||
Context not available. | |||||
static void ipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *, int); | static void ipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *, int); | ||||
static int ipsec6_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *); | static int ipsec6_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *); | ||||
#endif | #endif | ||||
static void ipsec_copy_policy(void *arg, struct inpcb *, struct inpcb *); | |||||
static void ipsec_delpcbpolicy(struct inpcbpolicy *); | static void ipsec_delpcbpolicy(struct inpcbpolicy *); | ||||
static struct secpolicy *ipsec_deepcopy_policy(struct secpolicy *src); | |||||
static void vshiftl(unsigned char *, int, int); | static void vshiftl(unsigned char *, int, int); | ||||
MALLOC_DEFINE(M_IPSEC_INPCB, "inpcbpolicy", "inpcb-resident ipsec policy"); | MALLOC_DEFINE(M_IPSEC_INPCB, "inpcbpolicy", "inpcb-resident ipsec policy"); | ||||
Context not available. | |||||
IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, | IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, | ||||
("invalid direction %u", dir)); | ("invalid direction %u", dir)); | ||||
if (!key_havesp(dir)) { | |||||
/* No SP found, use system default. */ | |||||
sp = KEY_ALLOCSP_DEFAULT(); | |||||
return (sp); | |||||
} | |||||
/* Set spidx in pcb. */ | /* Set spidx in pcb. */ | ||||
*error = ipsec_setspidx_inpcb(m, inp); | *error = ipsec_setspidx_inpcb(m, inp); | ||||
if (*error) | if (*error) | ||||
Context not available. | |||||
struct secpolicy *sp; | struct secpolicy *sp; | ||||
*error = 0; | *error = 0; | ||||
gnn: Don't you mean && here? | |||||
eriAuthorUnsubmitted Not Done Inline ActionsNo its meant as || eri: No its meant as || | |||||
if (inp == NULL) | if (inp == NULL || inp->inp_sp == NULL) | ||||
sp = ipsec_getpolicybyaddr(m, dir, error); | sp = ipsec_getpolicybyaddr(m, dir, error); | ||||
else | else | ||||
sp = ipsec_getpolicybysock(m, dir, inp, error); | sp = ipsec_getpolicybysock(m, dir, inp, error); | ||||
Context not available. | |||||
return (sp); | return (sp); | ||||
} | } | ||||
/* XXX: This seems braindead?! */ | |||||
static int | static int | ||||
ipsec_setspidx_inpcb(struct mbuf *m, struct inpcb *inp) | ipsec_setspidx_inpcb(struct mbuf *m, struct inpcb *inp) | ||||
{ | { | ||||
struct inpcbpolicy *cursp; | |||||
int error; | int error; | ||||
IPSEC_ASSERT(inp != NULL, ("null inp")); | IPSEC_ASSERT(inp != NULL, ("null inp")); | ||||
IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp")); | IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp")); | ||||
IPSEC_ASSERT(inp->inp_sp->sp_out != NULL && inp->inp_sp->sp_in != NULL, | |||||
cursp = (struct inpcbpolicy *)inp->inp_sp; | |||||
IPSEC_ASSERT(cursp->sp_out != NULL && cursp->sp_in != NULL, | |||||
("null sp_in || sp_out")); | ("null sp_in || sp_out")); | ||||
error = ipsec_setspidx(m, &inp->inp_sp->sp_in->spidx, 1); | error = ipsec_setspidx(m, &cursp->sp_in->spidx, 1); | ||||
if (error == 0) { | if (error == 0) { | ||||
inp->inp_sp->sp_in->spidx.dir = IPSEC_DIR_INBOUND; | cursp->sp_in->spidx.dir = IPSEC_DIR_INBOUND; | ||||
inp->inp_sp->sp_out->spidx = inp->inp_sp->sp_in->spidx; | cursp->sp_out->spidx = cursp->sp_in->spidx; | ||||
inp->inp_sp->sp_out->spidx.dir = IPSEC_DIR_OUTBOUND; | cursp->sp_out->spidx.dir = IPSEC_DIR_OUTBOUND; | ||||
} else { | } else { | ||||
bzero(&inp->inp_sp->sp_in->spidx, | bzero(&cursp->sp_in->spidx, | ||||
sizeof (inp->inp_sp->sp_in->spidx)); | sizeof (cursp->sp_in->spidx)); | ||||
bzero(&inp->inp_sp->sp_out->spidx, | bzero(&cursp->sp_out->spidx, | ||||
sizeof (inp->inp_sp->sp_in->spidx)); | sizeof (cursp->sp_in->spidx)); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
Context not available. | |||||
free(p, M_IPSEC_INPCB); | free(p, M_IPSEC_INPCB); | ||||
} | } | ||||
/* Initialize policy in PCB. */ | /* Copy old IPsec policy into new. */ | ||||
int | static void | ||||
ipsec_init_policy(struct socket *so, struct inpcbpolicy **pcb_sp) | ipsec_copy_policy(void *arg, struct inpcb *old, struct inpcb *new) | ||||
{ | { | ||||
struct inpcbpolicy *new; | struct inpcbpolicy *spinpcb; | ||||
SOCKSPTREE_RLOCK_TRACKER; | |||||
/* Sanity check. */ | SOCKSPTREE_RLOCK(); | ||||
if (so == NULL || pcb_sp == NULL) | TAILQ_FOREACH(spinpcb, &V_socketsptree, spentry) { | ||||
panic("%s: NULL pointer was passed.\n", __func__); | if (old->inp_sp == spinpcb) | ||||
break; | |||||
new = (struct inpcbpolicy *) malloc(sizeof(struct inpcbpolicy), | |||||
M_IPSEC_INPCB, M_NOWAIT|M_ZERO); | |||||
if (new == NULL) { | |||||
ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); | |||||
return (ENOBUFS); | |||||
} | } | ||||
SOCKSPTREE_RUNLOCK(); | |||||
if (spinpcb == NULL) | |||||
return; | |||||
new->priv = IPSEC_IS_PRIVILEGED_SO(so); | new->inp_sp = old->inp_sp; | ||||
refcount_acquire(&spinpcb->refcnt); | |||||
if ((new->sp_in = KEY_NEWSP()) == NULL) { | return; | ||||
ipsec_delpcbpolicy(new); | |||||
return (ENOBUFS); | |||||
} | |||||
new->sp_in->policy = IPSEC_POLICY_ENTRUST; | |||||
if ((new->sp_out = KEY_NEWSP()) == NULL) { | |||||
KEY_FREESP(&new->sp_in); | |||||
ipsec_delpcbpolicy(new); | |||||
return (ENOBUFS); | |||||
} | |||||
new->sp_out->policy = IPSEC_POLICY_ENTRUST; | |||||
*pcb_sp = new; | |||||
return (0); | |||||
} | } | ||||
/* Copy old IPsec policy into new. */ | |||||
int | |||||
ipsec_copy_policy(struct inpcbpolicy *old, struct inpcbpolicy *new) | |||||
{ | |||||
struct secpolicy *sp; | |||||
sp = ipsec_deepcopy_policy(old->sp_in); | |||||
if (sp) { | |||||
KEY_FREESP(&new->sp_in); | |||||
new->sp_in = sp; | |||||
} else | |||||
return (ENOBUFS); | |||||
sp = ipsec_deepcopy_policy(old->sp_out); | |||||
if (sp) { | |||||
KEY_FREESP(&new->sp_out); | |||||
new->sp_out = sp; | |||||
} else | |||||
return (ENOBUFS); | |||||
new->priv = old->priv; | |||||
return (0); | |||||
} | |||||
struct ipsecrequest * | struct ipsecrequest * | ||||
ipsec_newisr(void) | ipsec_newisr(void) | ||||
{ | { | ||||
Context not available. | |||||
free(p, M_IPSEC_SR); | free(p, M_IPSEC_SR); | ||||
} | } | ||||
/* Deep-copy a policy in PCB. */ | |||||
static struct secpolicy * | |||||
ipsec_deepcopy_policy(struct secpolicy *src) | |||||
{ | |||||
struct ipsecrequest *newchain = NULL; | |||||
struct ipsecrequest *p; | |||||
struct ipsecrequest **q; | |||||
struct ipsecrequest *r; | |||||
struct secpolicy *dst; | |||||
if (src == NULL) | |||||
return (NULL); | |||||
dst = KEY_NEWSP(); | |||||
if (dst == NULL) | |||||
return (NULL); | |||||
/* | |||||
* Deep-copy IPsec request chain. This is required since struct | |||||
* ipsecrequest is not reference counted. | |||||
*/ | |||||
q = &newchain; | |||||
for (p = src->req; p; p = p->next) { | |||||
*q = ipsec_newisr(); | |||||
if (*q == NULL) | |||||
goto fail; | |||||
(*q)->saidx.proto = p->saidx.proto; | |||||
(*q)->saidx.mode = p->saidx.mode; | |||||
(*q)->level = p->level; | |||||
(*q)->saidx.reqid = p->saidx.reqid; | |||||
bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src)); | |||||
bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst)); | |||||
(*q)->sp = dst; | |||||
q = &((*q)->next); | |||||
} | |||||
dst->req = newchain; | |||||
dst->policy = src->policy; | |||||
/* Do not touch the refcnt fields. */ | |||||
return (dst); | |||||
fail: | |||||
for (p = newchain; p; p = r) { | |||||
r = p->next; | |||||
ipsec_delisr(p); | |||||
p = NULL; | |||||
} | |||||
return (NULL); | |||||
} | |||||
/* Set policy and IPsec request if present. */ | /* Set policy and IPsec request if present. */ | ||||
static int | static int | ||||
ipsec_set_policy_internal(struct secpolicy **pcb_sp, int optname, | ipsec_set_policy_internal(struct secpolicy **pcb_sp, int optname, | ||||
Context not available. | |||||
return (error); | return (error); | ||||
/* Clear old SP and set new SP. */ | /* Clear old SP and set new SP. */ | ||||
KEY_FREESP(pcb_sp); | if (*pcb_sp != NULL) | ||||
KEY_FREESP(pcb_sp); | |||||
*pcb_sp = newsp; | *pcb_sp = newsp; | ||||
KEYDEBUG(KEYDEBUG_IPSEC_DUMP, | KEYDEBUG(KEYDEBUG_IPSEC_DUMP, | ||||
printf("%s: new policy\n", __func__); | printf("%s: new policy\n", __func__); | ||||
Context not available. | |||||
{ | { | ||||
struct sadb_x_policy *xpl; | struct sadb_x_policy *xpl; | ||||
struct secpolicy **pcb_sp; | struct secpolicy **pcb_sp; | ||||
struct inpcbpolicy *cursp; | |||||
int error; | |||||
/* Sanity check. */ | /* Sanity check. */ | ||||
if (inp == NULL || request == NULL) | if (inp == NULL || request == NULL) | ||||
Context not available. | |||||
return (EINVAL); | return (EINVAL); | ||||
xpl = (struct sadb_x_policy *)request; | xpl = (struct sadb_x_policy *)request; | ||||
INP_WLOCK(inp); | |||||
if (inp->inp_sp == NULL) { | |||||
inp->inp_sp = (struct inpcbpolicy *) malloc(sizeof(struct inpcbpolicy), | |||||
M_IPSEC_INPCB, M_NOWAIT|M_ZERO); | |||||
if (inp->inp_sp == NULL) { | |||||
ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); | |||||
INP_WUNLOCK(inp); | |||||
return (ENOBUFS); | |||||
} | |||||
cursp = (struct inpcbpolicy *)inp->inp_sp; | |||||
cursp->priv = IPSEC_IS_PRIVILEGED_SO(inp->inp_socket); | |||||
refcount_init(&cursp->refcnt, 1); | |||||
} | |||||
INP_WUNLOCK(inp); | |||||
/* Select direction. */ | /* Select direction. */ | ||||
switch (xpl->sadb_x_policy_dir) { | switch (xpl->sadb_x_policy_dir) { | ||||
case IPSEC_DIR_INBOUND: | case IPSEC_DIR_INBOUND: | ||||
pcb_sp = &inp->inp_sp->sp_in; | pcb_sp = &cursp->sp_in; | ||||
break; | break; | ||||
case IPSEC_DIR_OUTBOUND: | case IPSEC_DIR_OUTBOUND: | ||||
pcb_sp = &inp->inp_sp->sp_out; | pcb_sp = &cursp->sp_out; | ||||
break; | break; | ||||
default: | default: | ||||
ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, | ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, | ||||
Context not available. | |||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
return (ipsec_set_policy_internal(pcb_sp, optname, request, len, cred)); | error = ipsec_set_policy_internal(pcb_sp, optname, request, len, cred); | ||||
if (error) { | |||||
ipsec_delpcbpolicy(inp->inp_sp); | |||||
return (error); | |||||
} | |||||
SOCKSPTREE_WLOCK(); | |||||
TAILQ_INSERT_HEAD(&V_socketsptree, (struct inpcbpolicy *)inp->inp_sp, spentry); | |||||
SOCKSPTREE_WUNLOCK(); | |||||
return (0); | |||||
} | } | ||||
int | int | ||||
Context not available. | |||||
{ | { | ||||
struct sadb_x_policy *xpl; | struct sadb_x_policy *xpl; | ||||
struct secpolicy *pcb_sp; | struct secpolicy *pcb_sp; | ||||
struct inpcbpolicy *cursp; | |||||
/* Sanity check. */ | /* Sanity check. */ | ||||
if (inp == NULL || request == NULL || mp == NULL) | if (inp == NULL || request == NULL || mp == NULL) | ||||
Context not available. | |||||
IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp")); | IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp")); | ||||
if (len < sizeof(*xpl)) | if (len < sizeof(*xpl)) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (inp->inp_sp == NULL) | |||||
return (ENXIO); | |||||
xpl = (struct sadb_x_policy *)request; | xpl = (struct sadb_x_policy *)request; | ||||
cursp = (struct inpcbpolicy *)inp->inp_sp; | |||||
/* Select direction. */ | /* Select direction. */ | ||||
switch (xpl->sadb_x_policy_dir) { | switch (xpl->sadb_x_policy_dir) { | ||||
case IPSEC_DIR_INBOUND: | case IPSEC_DIR_INBOUND: | ||||
pcb_sp = inp->inp_sp->sp_in; | pcb_sp = cursp->sp_in; | ||||
break; | break; | ||||
case IPSEC_DIR_OUTBOUND: | case IPSEC_DIR_OUTBOUND: | ||||
pcb_sp = inp->inp_sp->sp_out; | pcb_sp = cursp->sp_out; | ||||
break; | break; | ||||
default: | default: | ||||
ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, | ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, | ||||
Context not available. | |||||
} | } | ||||
/* Delete policy in PCB. */ | /* Delete policy in PCB. */ | ||||
int | static void | ||||
ipsec_delete_pcbpolicy(struct inpcb *inp) | ipsec_delete_pcbpolicy(void *arg, struct inpcb *inp) | ||||
{ | { | ||||
struct inpcbpolicy *cursp; | |||||
IPSEC_ASSERT(inp != NULL, ("null inp")); | IPSEC_ASSERT(inp != NULL, ("null inp")); | ||||
if (inp->inp_sp == NULL) | if (inp->inp_sp == NULL) | ||||
return (0); | return; | ||||
if (inp->inp_sp->sp_in != NULL) | cursp = (struct inpcbpolicy *)inp->inp_sp; | ||||
KEY_FREESP(&inp->inp_sp->sp_in); | |||||
if (inp->inp_sp->sp_out != NULL) | if (cursp->sp_in != NULL) | ||||
KEY_FREESP(&inp->inp_sp->sp_out); | KEY_FREESP(&cursp->sp_in); | ||||
ipsec_delpcbpolicy(inp->inp_sp); | if (cursp->sp_out != NULL) | ||||
KEY_FREESP(&cursp->sp_out); | |||||
if (refcount_release(&cursp->refcnt) == 0) { | |||||
SOCKSPTREE_WLOCK(); | |||||
TAILQ_REMOVE(&V_socketsptree, cursp, spentry); | |||||
SOCKSPTREE_WUNLOCK(); | |||||
ipsec_delpcbpolicy(cursp); | |||||
} | |||||
inp->inp_sp = NULL; | inp->inp_sp = NULL; | ||||
return; | |||||
return (0); | |||||
} | } | ||||
/* | /* | ||||
Context not available. | |||||
bzero(&V_def_policy, sizeof(struct secpolicy)); | bzero(&V_def_policy, sizeof(struct secpolicy)); | ||||
V_def_policy.policy = IPSEC_POLICY_NONE; | V_def_policy.policy = IPSEC_POLICY_NONE; | ||||
V_def_policy.refcnt = 1; | V_def_policy.refcnt = 1; | ||||
TAILQ_INIT(&V_socketsptree); | |||||
SOCKSPTREE_LOCK_INIT(); | |||||
} | } | ||||
VNET_SYSINIT(def_policy_init, SI_SUB_PROTO_DOMAININIT, SI_ORDER_ANY, | VNET_SYSINIT(def_policy_init, SI_SUB_PROTO_DOMAININIT, SI_ORDER_ANY, | ||||
def_policy_init, NULL); | def_policy_init, NULL); | ||||
EVENTHANDLER_DEFINE(tcp_syncache_newconn, ipsec_copy_policy, NULL, | |||||
EVENTHANDLER_PRI_LAST); | |||||
EVENTHANDLER_DEFINE(ipsec_delete_pcbpolicy, ipsec_delete_pcbpolicy, | |||||
NULL, EVENTHANDLER_PRI_LAST); | |||||
/* XXX This stuff doesn't belong here... */ | /* XXX This stuff doesn't belong here... */ | ||||
Context not available. |
Don't you mean && here?