Changeset View
Changeset View
Standalone View
Standalone View
sys/netipsec/ipsec_pcb.c
Show First 20 Lines • Show All 270 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
ipsec_set_pcbpolicy(struct inpcb *inp, struct ucred *cred, | ipsec_set_pcbpolicy(struct inpcb *inp, struct ucred *cred, | ||||
void *request, size_t len) | void *request, size_t len) | ||||
{ | { | ||||
struct sadb_x_policy *xpl; | struct sadb_x_policy *xpl; | ||||
struct secpolicy **spp, *newsp; | struct secpolicy **spp, *newsp; | ||||
int error, flags; | int error, flags; | ||||
INP_WLOCK_ASSERT(inp); | |||||
xpl = (struct sadb_x_policy *)request; | xpl = (struct sadb_x_policy *)request; | ||||
/* Select direction. */ | /* Select direction. */ | ||||
switch (xpl->sadb_x_policy_dir) { | switch (xpl->sadb_x_policy_dir) { | ||||
case IPSEC_DIR_INBOUND: | case IPSEC_DIR_INBOUND: | ||||
case IPSEC_DIR_OUTBOUND: | case IPSEC_DIR_OUTBOUND: | ||||
break; | break; | ||||
default: | default: | ||||
ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, | ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, | ||||
Show All 40 Lines | case IPSEC_POLICY_ENTRUST: | ||||
/* We just use NULL pointer for ENTRUST policy */ | /* We just use NULL pointer for ENTRUST policy */ | ||||
newsp = NULL; | newsp = NULL; | ||||
break; | break; | ||||
default: | default: | ||||
/* Other security policy types aren't allowed for PCB */ | /* Other security policy types aren't allowed for PCB */ | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
INP_WLOCK(inp); | |||||
if (xpl->sadb_x_policy_dir == IPSEC_DIR_INBOUND) { | if (xpl->sadb_x_policy_dir == IPSEC_DIR_INBOUND) { | ||||
spp = &inp->inp_sp->sp_in; | spp = &inp->inp_sp->sp_in; | ||||
flags = INP_INBOUND_POLICY; | flags = INP_INBOUND_POLICY; | ||||
} else { | } else { | ||||
spp = &inp->inp_sp->sp_out; | spp = &inp->inp_sp->sp_out; | ||||
flags = INP_OUTBOUND_POLICY; | flags = INP_OUTBOUND_POLICY; | ||||
} | } | ||||
/* Clear old SP and set new SP. */ | /* Clear old SP and set new SP. */ | ||||
if (*spp != NULL) | if (*spp != NULL) | ||||
key_freesp(spp); | key_freesp(spp); | ||||
*spp = newsp; | *spp = newsp; | ||||
KEYDBG(IPSEC_DUMP, | KEYDBG(IPSEC_DUMP, | ||||
printf("%s: new SP(%p)\n", __func__, newsp)); | printf("%s: new SP(%p)\n", __func__, newsp)); | ||||
if (newsp == NULL) | if (newsp == NULL) | ||||
inp->inp_sp->flags &= ~flags; | inp->inp_sp->flags &= ~flags; | ||||
else { | else { | ||||
inp->inp_sp->flags |= flags; | inp->inp_sp->flags |= flags; | ||||
KEYDBG(IPSEC_DUMP, kdebug_secpolicy(newsp)); | KEYDBG(IPSEC_DUMP, kdebug_secpolicy(newsp)); | ||||
} | } | ||||
INP_WUNLOCK(inp); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
ipsec_get_pcbpolicy(struct inpcb *inp, void *request, size_t *len) | ipsec_get_pcbpolicy(struct inpcb *inp, void *request, size_t *len) | ||||
{ | { | ||||
struct sadb_x_policy *xpl; | struct sadb_x_policy *xpl; | ||||
struct secpolicy *sp; | struct secpolicy *sp; | ||||
int error, flags; | int error, flags; | ||||
xpl = (struct sadb_x_policy *)request; | xpl = (struct sadb_x_policy *)request; | ||||
INP_RLOCK(inp); | INP_WLOCK_ASSERT(inp); | ||||
flags = inp->inp_sp->flags; | flags = inp->inp_sp->flags; | ||||
/* Select direction. */ | /* Select direction. */ | ||||
switch (xpl->sadb_x_policy_dir) { | switch (xpl->sadb_x_policy_dir) { | ||||
case IPSEC_DIR_INBOUND: | case IPSEC_DIR_INBOUND: | ||||
sp = inp->inp_sp->sp_in; | sp = inp->inp_sp->sp_in; | ||||
flags &= INP_INBOUND_POLICY; | flags &= INP_INBOUND_POLICY; | ||||
break; | break; | ||||
case IPSEC_DIR_OUTBOUND: | case IPSEC_DIR_OUTBOUND: | ||||
sp = inp->inp_sp->sp_out; | sp = inp->inp_sp->sp_out; | ||||
flags &= INP_OUTBOUND_POLICY; | flags &= INP_OUTBOUND_POLICY; | ||||
break; | break; | ||||
default: | default: | ||||
INP_RUNLOCK(inp); | |||||
ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, | ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, | ||||
xpl->sadb_x_policy_dir)); | xpl->sadb_x_policy_dir)); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
if (flags == 0) { | if (flags == 0) { | ||||
/* Return ENTRUST policy */ | /* Return ENTRUST policy */ | ||||
INP_RUNLOCK(inp); | |||||
xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY; | xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY; | ||||
xpl->sadb_x_policy_type = IPSEC_POLICY_ENTRUST; | xpl->sadb_x_policy_type = IPSEC_POLICY_ENTRUST; | ||||
xpl->sadb_x_policy_id = 0; | xpl->sadb_x_policy_id = 0; | ||||
xpl->sadb_x_policy_priority = 0; | xpl->sadb_x_policy_priority = 0; | ||||
xpl->sadb_x_policy_len = PFKEY_UNIT64(sizeof(*xpl)); | xpl->sadb_x_policy_len = PFKEY_UNIT64(sizeof(*xpl)); | ||||
*len = sizeof(*xpl); | *len = sizeof(*xpl); | ||||
return (0); | return (0); | ||||
} | } | ||||
IPSEC_ASSERT(sp != NULL, | IPSEC_ASSERT(sp != NULL, | ||||
("sp is NULL, but flags is 0x%04x", inp->inp_sp->flags)); | ("sp is NULL, but flags is 0x%04x", inp->inp_sp->flags)); | ||||
key_addref(sp); | key_addref(sp); | ||||
INP_RUNLOCK(inp); | |||||
error = key_sp2msg(sp, request, len); | error = key_sp2msg(sp, request, len); | ||||
key_freesp(&sp); | key_freesp(&sp); | ||||
if (error == EINVAL) | if (error == EINVAL) | ||||
return (error); | return (error); | ||||
/* | /* | ||||
* We return "success", but user should check *len. | * We return "success", but user should check *len. | ||||
* *len will be set to size of valid data and | * *len will be set to size of valid data and | ||||
* sadb_x_policy_len will contain needed size. | * sadb_x_policy_len will contain needed size. | ||||
*/ | */ | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Handle socket option control request for PCB */ | /* Handle socket option control request for PCB */ | ||||
static int | static int | ||||
ipsec_control_pcbpolicy(struct inpcb *inp, struct sockopt *sopt) | ipsec_control_pcbpolicy(struct inpcb *inp, struct sockopt *sopt) | ||||
{ | { | ||||
void *optdata; | void *optdata; | ||||
size_t optlen; | size_t optlen; | ||||
int error; | int error; | ||||
if (inp->inp_sp == NULL) | INP_WLOCK_ASSERT(inp); | ||||
if (inp->inp_sp == NULL) { | |||||
INP_WUNLOCK(inp); | |||||
return (ENOPROTOOPT); | return (ENOPROTOOPT); | ||||
} | |||||
/* Limit maximum request size to PAGE_SIZE */ | /* Limit maximum request size to PAGE_SIZE */ | ||||
optlen = sopt->sopt_valsize; | optlen = sopt->sopt_valsize; | ||||
if (optlen < sizeof(struct sadb_x_policy) || optlen > PAGE_SIZE) | if (optlen < sizeof(struct sadb_x_policy) || optlen > PAGE_SIZE) { | ||||
INP_WUNLOCK(inp); | |||||
return (EINVAL); | return (EINVAL); | ||||
} | |||||
optdata = malloc(optlen, M_TEMP, sopt->sopt_td ? M_WAITOK: M_NOWAIT); | optdata = malloc(optlen, M_TEMP, sopt->sopt_td ? M_WAITOK: M_NOWAIT); | ||||
if (optdata == NULL) | if (optdata == NULL) { | ||||
INP_WUNLOCK(inp); | |||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | |||||
/* | /* | ||||
* We need a hint from the user, what policy is requested - input | * We need a hint from the user, what policy is requested - input | ||||
* or output? User should specify it in the buffer, even for | * or output? User should specify it in the buffer, even for | ||||
* setsockopt(). | * setsockopt(). | ||||
*/ | */ | ||||
INP_WUNLOCK(inp); | |||||
error = sooptcopyin(sopt, optdata, optlen, optlen); | error = sooptcopyin(sopt, optdata, optlen, optlen); | ||||
INP_WLOCK(inp); | |||||
if (error == 0) { | if (error == 0) { | ||||
if (sopt->sopt_dir == SOPT_SET) | if (sopt->sopt_dir == SOPT_SET) { | ||||
error = ipsec_set_pcbpolicy(inp, | error = ipsec_set_pcbpolicy(inp, | ||||
sopt->sopt_td ? sopt->sopt_td->td_ucred: NULL, | sopt->sopt_td ? sopt->sopt_td->td_ucred: NULL, | ||||
optdata, optlen); | optdata, optlen); | ||||
else { | if (error != 0) | ||||
INP_WUNLOCK(inp); | |||||
ae: It seems you mixed INP_WUNLOCK() here and in some places (not all) in ipsec_set_pcbpolicy. | |||||
Not Done Inline ActionsAgreed, correcting. jason_eggnet.com: Agreed, correcting. | |||||
} else { | |||||
error = ipsec_get_pcbpolicy(inp, optdata, &optlen); | error = ipsec_get_pcbpolicy(inp, optdata, &optlen); | ||||
INP_WUNLOCK(inp); | |||||
if (error == 0) | if (error == 0) | ||||
error = sooptcopyout(sopt, optdata, optlen); | error = sooptcopyout(sopt, optdata, optlen); | ||||
} | } | ||||
} | } | ||||
free(optdata, M_TEMP); | free(optdata, M_TEMP); | ||||
return (error); | return (error); | ||||
} | } | ||||
Show All 28 Lines |
It seems you mixed INP_WUNLOCK() here and in some places (not all) in ipsec_set_pcbpolicy. Probably it will be better to have one wunlock here, than many in ipsec_set_pcbpolicy.