Introduce the concept of IPsec security policies scope.

Currently are defined three scopes: global, ifnet, and pcb.
Generic security policies that IKE daemon can add via PF_KEY interface
or an administrator creates with setkey(8) utility have GLOBAL scope.
Such policies can be applied by the kernel to outgoing packets and checked
agains inbound packets after IPsec processing.
Security policies created by if_ipsec(4) interfaces have IFNET scope.
Such policies are applied to packets that are passed through if_ipsec(4)
interface.
And security policies created by application using setsockopt()
IP_IPSEC_POLICY option have PCB scope. Such policies are applied to
packets related to specific socket. Currently there is no way to list
PCB policies via setkey(8) utility.

Modify setkey(8) and libipsec(3) to be able distinguish the scope of
security policies in the `setkey -DP` listing. Add two optional flags:
'-t' to list only policies related to virtual *tunneling* interfaces,
i.e. policies with IFNET scope, and '-g' to list only policies with GLOBAL
scope. By default policies from all scopes are listed.

To implement this PF_KEY's sadb_x_policy structure was modified.
sadb_x_policy_reserved field is used to pass the policy scope from the
kernel to userland. SADB_SPDDUMP message extended to support filtering
by scope: sadb_msg_satype field is used to specify bit mask of requested
scopes.

For IFNET policies the sadb_x_policy_priority field of struct sadb_x_policy
is used to pass if_ipsec's interface if_index to the userland. For GLOBAL
policies sadb_x_policy_priority is used only to manage order of security
policies in the SPDB. For IFNET policies it is not used, so it can be used
to keep if_index.

After this change the output of `setkey -DP` now looks like:
# setkey -DPt
0.0.0.0/0[any] 0.0.0.0/0[any] any
	in ipsec
	esp/tunnel/87.250.242.144-87.250.242.145/unique:145
	spid=7 seq=3 pid=58025 scope=ifnet ifname=ipsec0
	refcnt=1
# setkey -DPg
::/0 ::/0 icmp6 135,0
	out none
	spid=5 seq=1 pid=872 scope=global
	refcnt=1

No objection from:	#network
Obtained from:	Yandex LLC
MFC after:	2 weeks
Sponsored by:	Yandex LLC
Differential Revision:	https://reviews.freebsd.org/D9805
This commit is contained in:
Andrey V. Elsukov 2017-03-07 00:13:53 +00:00
parent b440e965da
commit 22986c6740
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=314812
7 changed files with 182 additions and 106 deletions

View file

@ -35,8 +35,9 @@ __FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netipsec/ipsec.h>
#include <net/if.h>
#include <net/pfkeyv2.h>
#include <netipsec/ipsec.h>
#include <netipsec/key_var.h>
#include <netipsec/key_debug.h>
@ -204,6 +205,13 @@ static struct val2str str_alg_comp[] = {
{ -1, NULL, },
};
static struct val2str str_sp_scope[] = {
{ IPSEC_POLICYSCOPE_GLOBAL, "global" },
{ IPSEC_POLICYSCOPE_IFNET, "ifnet" },
{ IPSEC_POLICYSCOPE_PCB, "pcb"},
{ -1, NULL },
};
/*
* dump SADB_MSG formated. For debugging, you should use kdebug_sadb().
*/
@ -398,8 +406,7 @@ pfkey_sadump(m)
}
void
pfkey_spdump(m)
struct sadb_msg *m;
pfkey_spdump(struct sadb_msg *m)
{
char pbuf[NI_MAXSERV];
caddr_t mhp[SADB_EXT_MAX + 1];
@ -507,10 +514,15 @@ pfkey_spdump(m)
}
printf("\tspid=%ld seq=%ld pid=%ld\n",
printf("\tspid=%ld seq=%ld pid=%ld scope=",
(u_long)m_xpl->sadb_x_policy_id,
(u_long)m->sadb_msg_seq,
(u_long)m->sadb_msg_pid);
GETMSGV2S(str_sp_scope, m_xpl->sadb_x_policy_scope);
if (m_xpl->sadb_x_policy_scope == IPSEC_POLICYSCOPE_IFNET &&
if_indextoname(m_xpl->sadb_x_policy_ifindex, pbuf) != NULL)
printf("ifname=%s", pbuf);
printf("\n");
/* XXX TEST */
printf("\trefcnt=%u\n", m->sadb_msg_reserved);

View file

@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd February 27, 2017
.Dd March 7, 2017
.Dt SETKEY 8
.Os
.\"
@ -45,7 +45,7 @@
.Op Fl v
.Fl f Ar filename
.Nm
.Op Fl aPlv
.Op Fl Pgltv
.Fl D
.Nm
.Op Fl Pv
@ -81,18 +81,21 @@ Flush the SAD entries.
If with
.Fl P ,
the SPD entries are flushed.
.It Fl a
The
.Nm
utility
usually does not display dead SAD entries with
.Fl D .
If with
.Fl a ,
the dead SAD entries will be displayed as well.
A dead SAD entry means that
it has been expired but remains in the system
because it is referenced by some SPD entries.
.It Fl g
Only SPD entries with global scope are dumped with
.Fl D
and
.Fl P
flags.
.It Fl t
Only SPD entries with ifnet scope are dumped with
.Fl D
and
.Fl P
flags.
Such SPD entries are linked to the corresponding
.Xr if_ipsec 4
virtual tunneling interface.
.It Fl h
Add hexadecimal dump on
.Fl x
@ -697,6 +700,7 @@ add 10.1.10.34 10.1.10.36 tcp 0x1000 -A tcp-md5 "TCP-MD5 BGP secret" ;
.\"
.Sh SEE ALSO
.Xr ipsec_set_policy 3 ,
.Xr if_ipsec 4 ,
.Xr racoon 8 ,
.Xr sysctl 8
.Rs

View file

@ -56,7 +56,7 @@
void usage(void);
int main(int, char **);
int get_supported(void);
void sendkeyshort(u_int);
void sendkeyshort(u_int, uint8_t);
void promisc(void);
int sendkeymsg(char *, size_t);
int postproc(struct sadb_msg *, int);
@ -81,6 +81,7 @@ int f_cmddump = 0;
int f_policy = 0;
int f_hexdump = 0;
int f_tflag = 0;
int f_scope = 0;
static time_t thiszone;
extern int lineno;
@ -93,7 +94,7 @@ usage()
printf("usage: setkey [-v] -c\n");
printf(" setkey [-v] -f filename\n");
printf(" setkey [-Palv] -D\n");
printf(" setkey [-Pagltv] -D\n");
printf(" setkey [-Pv] -F\n");
printf(" setkey [-h] -x\n");
exit(1);
@ -114,7 +115,7 @@ main(ac, av)
thiszone = gmt2local(0);
while ((c = getopt(ac, av, "acdf:hlvxDFP")) != -1) {
while ((c = getopt(ac, av, "acdf:ghltvxDFP")) != -1) {
switch (c) {
case 'c':
f_mode = MODE_SCRIPT;
@ -149,6 +150,12 @@ main(ac, av)
case 'P':
f_policy = 1;
break;
case 'g': /* global */
f_scope |= IPSEC_POLICYSCOPE_GLOBAL;
break;
case 't': /* tunnel */
f_scope |= IPSEC_POLICYSCOPE_IFNET;
break;
case 'v':
f_verbose = 1;
break;
@ -166,10 +173,12 @@ main(ac, av)
switch (f_mode) {
case MODE_CMDDUMP:
sendkeyshort(f_policy ? SADB_X_SPDDUMP: SADB_DUMP);
sendkeyshort(f_policy ? SADB_X_SPDDUMP: SADB_DUMP,
f_policy ? f_scope: SADB_SATYPE_UNSPEC);
break;
case MODE_CMDFLUSH:
sendkeyshort(f_policy ? SADB_X_SPDFLUSH: SADB_FLUSH);
sendkeyshort(f_policy ? SADB_X_SPDFLUSH: SADB_FLUSH,
SADB_SATYPE_UNSPEC);
break;
case MODE_SCRIPT:
if (get_supported() < 0) {
@ -204,15 +213,14 @@ get_supported()
}
void
sendkeyshort(type)
u_int type;
sendkeyshort(u_int type, uint8_t satype)
{
struct sadb_msg msg;
msg.sadb_msg_version = PF_KEY_V2;
msg.sadb_msg_type = type;
msg.sadb_msg_errno = 0;
msg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
msg.sadb_msg_satype = satype;
msg.sadb_msg_len = PFKEY_UNIT64(sizeof(msg));
msg.sadb_msg_reserved = 0;
msg.sadb_msg_seq = 0;

View file

@ -717,7 +717,7 @@ ipsec_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
* ipsec esp/tunnel/LocalIP-RemoteIP/unique:reqid
*/
static int
ipsec_newpolicies(struct secpolicy *sp[IPSEC_SPCOUNT],
ipsec_newpolicies(struct ipsec_softc *sc, struct secpolicy *sp[IPSEC_SPCOUNT],
const struct sockaddr *src, const struct sockaddr *dst, uint32_t reqid)
{
struct ipsecrequest *isr;
@ -734,6 +734,8 @@ ipsec_newpolicies(struct secpolicy *sp[IPSEC_SPCOUNT],
sp[i]->state = IPSEC_SPSTATE_DEAD;
sp[i]->req[sp[i]->tcount++] = isr;
sp[i]->created = time_second;
/* Use priority field to store if_index */
sp[i]->priority = sc->ifp->if_index;
isr->level = IPSEC_LEVEL_UNIQUE;
isr->saidx.proto = IPPROTO_ESP;
isr->saidx.mode = IPSEC_MODE_TUNNEL;
@ -936,7 +938,7 @@ ipsec_set_tunnel(struct ipsec_softc *sc, struct sockaddr *src,
sx_assert(&ipsec_ioctl_sx, SA_XLOCKED);
/* Allocate SP with new addresses. */
if (ipsec_newpolicies(sp, src, dst, reqid) == 0) {
if (ipsec_newpolicies(sc, sp, src, dst, reqid) == 0) {
/* Add new policies to SPDB */
if (key_register_ifnet(sp, IPSEC_SPCOUNT) != 0) {
for (i = 0; i < IPSEC_SPCOUNT; i++)

View file

@ -223,9 +223,12 @@ struct sadb_x_policy {
u_int16_t sadb_x_policy_exttype;
u_int16_t sadb_x_policy_type; /* See policy type of ipsec.h */
u_int8_t sadb_x_policy_dir; /* direction, see ipsec.h */
u_int8_t sadb_x_policy_reserved;
u_int8_t sadb_x_policy_scope; /* scope, see ipsec.h */
u_int32_t sadb_x_policy_id;
u_int32_t sadb_x_policy_priority;
#define sadb_x_policy_reserved sadb_x_policy_scope
/* Policy with ifnet scope uses priority field to store ifindex */
#define sadb_x_policy_ifindex sadb_x_policy_priority
};
_Static_assert(sizeof(struct sadb_x_policy) == 16, "struct size mismatch");

View file

@ -179,6 +179,12 @@ struct secspacq {
#define IPSEC_POLICY_ENTRUST 3 /* consulting SPD if present. */
#define IPSEC_POLICY_BYPASS 4 /* only for privileged socket. */
/* Policy scope */
#define IPSEC_POLICYSCOPE_ANY 0x00 /* unspecified */
#define IPSEC_POLICYSCOPE_GLOBAL 0x01 /* global scope */
#define IPSEC_POLICYSCOPE_IFNET 0x02 /* if_ipsec(4) scope */
#define IPSEC_POLICYSCOPE_PCB 0x04 /* PCB scope */
/* Security protocol level */
#define IPSEC_LEVEL_DEFAULT 0 /* reference to system default */
#define IPSEC_LEVEL_USE 1 /* use SA if present. */

View file

@ -1660,6 +1660,16 @@ key_sp2msg(struct secpolicy *sp, void *request, size_t *len)
xpl->sadb_x_policy_dir = sp->spidx.dir;
xpl->sadb_x_policy_id = sp->id;
xpl->sadb_x_policy_priority = sp->priority;
switch (sp->state) {
case IPSEC_SPSTATE_IFNET:
xpl->sadb_x_policy_scope = IPSEC_POLICYSCOPE_IFNET;
break;
case IPSEC_SPSTATE_PCB:
xpl->sadb_x_policy_scope = IPSEC_POLICYSCOPE_PCB;
break;
default:
xpl->sadb_x_policy_scope = IPSEC_POLICYSCOPE_GLOBAL;
}
/* if is the policy for ipsec ? */
if (sp->policy == IPSEC_POLICY_IPSEC) {
@ -2388,16 +2398,25 @@ key_spdflush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
return key_sendup_mbuf(so, m, KEY_SENDUP_ALL);
}
static uint8_t
key_satype2scopemask(uint8_t satype)
{
if (satype == IPSEC_POLICYSCOPE_ANY)
return (0xff);
return (satype);
}
/*
* SADB_SPDDUMP processing
* receive
* <base>
* from the user, and dump all SP leaves
* and send,
* from the user, and dump all SP leaves and send,
* <base> .....
* to the ikmpd.
*
* m will always be freed.
* NOTE:
* sadb_msg_satype is considered as mask of policy scopes.
* m will always be freed.
*/
static int
key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
@ -2406,7 +2425,7 @@ key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
struct secpolicy *sp;
struct mbuf *n;
int cnt;
u_int dir;
u_int dir, scope;
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(m != NULL, ("null mbuf"));
@ -2415,13 +2434,16 @@ key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
/* search SPD entry and get buffer size. */
cnt = 0;
scope = key_satype2scopemask(mhp->msg->sadb_msg_satype);
SPTREE_RLOCK();
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
cnt++;
if (scope & IPSEC_POLICYSCOPE_GLOBAL) {
TAILQ_FOREACH(sp, &V_sptree[dir], chain)
cnt++;
}
TAILQ_FOREACH(sp, &V_sptree_ifnet[dir], chain) {
cnt++;
if (scope & IPSEC_POLICYSCOPE_IFNET) {
TAILQ_FOREACH(sp, &V_sptree_ifnet[dir], chain)
cnt++;
}
}
@ -2431,21 +2453,25 @@ key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
}
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
--cnt;
n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt,
mhp->msg->sadb_msg_pid);
if (scope & IPSEC_POLICYSCOPE_GLOBAL) {
TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
--cnt;
n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt,
mhp->msg->sadb_msg_pid);
if (n)
key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
if (n != NULL)
key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
}
}
TAILQ_FOREACH(sp, &V_sptree_ifnet[dir], chain) {
--cnt;
n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt,
mhp->msg->sadb_msg_pid);
if (scope & IPSEC_POLICYSCOPE_IFNET) {
TAILQ_FOREACH(sp, &V_sptree_ifnet[dir], chain) {
--cnt;
n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt,
mhp->msg->sadb_msg_pid);
if (n)
key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
if (n != NULL)
key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
}
}
}
@ -7659,64 +7685,79 @@ key_parse(struct mbuf *m, struct socket *so)
msg = mh.msg;
/* check SA type */
switch (msg->sadb_msg_satype) {
case SADB_SATYPE_UNSPEC:
switch (msg->sadb_msg_type) {
case SADB_GETSPI:
case SADB_UPDATE:
case SADB_ADD:
case SADB_DELETE:
case SADB_GET:
case SADB_ACQUIRE:
case SADB_EXPIRE:
ipseclog((LOG_DEBUG, "%s: must specify satype "
"when msg type=%u.\n", __func__,
msg->sadb_msg_type));
PFKEYSTAT_INC(out_invsatype);
error = EINVAL;
goto senderror;
}
break;
case SADB_SATYPE_AH:
case SADB_SATYPE_ESP:
case SADB_X_SATYPE_IPCOMP:
case SADB_X_SATYPE_TCPSIGNATURE:
switch (msg->sadb_msg_type) {
case SADB_X_SPDADD:
case SADB_X_SPDDELETE:
case SADB_X_SPDGET:
case SADB_X_SPDDUMP:
case SADB_X_SPDFLUSH:
case SADB_X_SPDSETIDX:
case SADB_X_SPDUPDATE:
case SADB_X_SPDDELETE2:
ipseclog((LOG_DEBUG, "%s: illegal satype=%u\n",
__func__, msg->sadb_msg_type));
PFKEYSTAT_INC(out_invsatype);
error = EINVAL;
goto senderror;
}
break;
case SADB_SATYPE_RSVP:
case SADB_SATYPE_OSPFV2:
case SADB_SATYPE_RIPV2:
case SADB_SATYPE_MIP:
ipseclog((LOG_DEBUG, "%s: type %u isn't supported.\n",
__func__, msg->sadb_msg_satype));
PFKEYSTAT_INC(out_invsatype);
error = EOPNOTSUPP;
goto senderror;
case 1: /* XXX: What does it do? */
if (msg->sadb_msg_type == SADB_X_PROMISC)
/* We use satype as scope mask for spddump */
if (msg->sadb_msg_type == SADB_X_SPDDUMP) {
switch (msg->sadb_msg_satype) {
case IPSEC_POLICYSCOPE_ANY:
case IPSEC_POLICYSCOPE_GLOBAL:
case IPSEC_POLICYSCOPE_IFNET:
case IPSEC_POLICYSCOPE_PCB:
break;
/*FALLTHROUGH*/
default:
ipseclog((LOG_DEBUG, "%s: invalid type %u is passed.\n",
__func__, msg->sadb_msg_satype));
PFKEYSTAT_INC(out_invsatype);
error = EINVAL;
goto senderror;
default:
ipseclog((LOG_DEBUG, "%s: illegal satype=%u\n",
__func__, msg->sadb_msg_type));
PFKEYSTAT_INC(out_invsatype);
error = EINVAL;
goto senderror;
}
} else {
switch (msg->sadb_msg_satype) { /* check SA type */
case SADB_SATYPE_UNSPEC:
switch (msg->sadb_msg_type) {
case SADB_GETSPI:
case SADB_UPDATE:
case SADB_ADD:
case SADB_DELETE:
case SADB_GET:
case SADB_ACQUIRE:
case SADB_EXPIRE:
ipseclog((LOG_DEBUG, "%s: must specify satype "
"when msg type=%u.\n", __func__,
msg->sadb_msg_type));
PFKEYSTAT_INC(out_invsatype);
error = EINVAL;
goto senderror;
}
break;
case SADB_SATYPE_AH:
case SADB_SATYPE_ESP:
case SADB_X_SATYPE_IPCOMP:
case SADB_X_SATYPE_TCPSIGNATURE:
switch (msg->sadb_msg_type) {
case SADB_X_SPDADD:
case SADB_X_SPDDELETE:
case SADB_X_SPDGET:
case SADB_X_SPDFLUSH:
case SADB_X_SPDSETIDX:
case SADB_X_SPDUPDATE:
case SADB_X_SPDDELETE2:
ipseclog((LOG_DEBUG, "%s: illegal satype=%u\n",
__func__, msg->sadb_msg_type));
PFKEYSTAT_INC(out_invsatype);
error = EINVAL;
goto senderror;
}
break;
case SADB_SATYPE_RSVP:
case SADB_SATYPE_OSPFV2:
case SADB_SATYPE_RIPV2:
case SADB_SATYPE_MIP:
ipseclog((LOG_DEBUG, "%s: type %u isn't supported.\n",
__func__, msg->sadb_msg_satype));
PFKEYSTAT_INC(out_invsatype);
error = EOPNOTSUPP;
goto senderror;
case 1: /* XXX: What does it do? */
if (msg->sadb_msg_type == SADB_X_PROMISC)
break;
/*FALLTHROUGH*/
default:
ipseclog((LOG_DEBUG, "%s: invalid type %u is passed.\n",
__func__, msg->sadb_msg_satype));
PFKEYSTAT_INC(out_invsatype);
error = EINVAL;
goto senderror;
}
}
/* check field of upper layer protocol and address family */