src/sys/net/pfkeyv2_parsemessage.c

929 lines
28 KiB
C

/* $OpenBSD: pfkeyv2_parsemessage.c,v 1.62 2023/09/29 18:45:42 tobhe Exp $ */
/*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999 Craig Metz. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <netinet/ip_ipsp.h>
#include <net/pfkeyv2.h>
#if NPF > 0
#include <net/if.h>
#include <net/pfvar.h>
#endif
#ifdef ENCDEBUG
#define DPRINTF(fmt, args...) \
do { \
if (encdebug) \
printf("%s: " fmt "\n", __func__, ## args); \
} while (0)
#else
#define DPRINTF(fmt, args...) \
do { } while (0)
#endif
#define BITMAP_SA (1LL << SADB_EXT_SA)
#define BITMAP_LIFETIME_CURRENT (1LL << SADB_EXT_LIFETIME_CURRENT)
#define BITMAP_LIFETIME_HARD (1LL << SADB_EXT_LIFETIME_HARD)
#define BITMAP_LIFETIME_SOFT (1LL << SADB_EXT_LIFETIME_SOFT)
#define BITMAP_ADDRESS_SRC (1LL << SADB_EXT_ADDRESS_SRC)
#define BITMAP_ADDRESS_DST (1LL << SADB_EXT_ADDRESS_DST)
#define BITMAP_ADDRESS_PROXY (1LL << SADB_EXT_ADDRESS_PROXY)
#define BITMAP_KEY_AUTH (1LL << SADB_EXT_KEY_AUTH)
#define BITMAP_KEY_ENCRYPT (1LL << SADB_EXT_KEY_ENCRYPT)
#define BITMAP_IDENTITY_SRC (1LL << SADB_EXT_IDENTITY_SRC)
#define BITMAP_IDENTITY_DST (1LL << SADB_EXT_IDENTITY_DST)
#define BITMAP_SENSITIVITY (1LL << SADB_EXT_SENSITIVITY)
#define BITMAP_PROPOSAL (1LL << SADB_EXT_PROPOSAL)
#define BITMAP_SUPPORTED_AUTH (1LL << SADB_EXT_SUPPORTED_AUTH)
#define BITMAP_SUPPORTED_ENCRYPT (1LL << SADB_EXT_SUPPORTED_ENCRYPT)
#define BITMAP_SPIRANGE (1LL << SADB_EXT_SPIRANGE)
#define BITMAP_LIFETIME (BITMAP_LIFETIME_CURRENT | BITMAP_LIFETIME_HARD | BITMAP_LIFETIME_SOFT)
#define BITMAP_ADDRESS (BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST)
#define BITMAP_KEY (BITMAP_KEY_AUTH | BITMAP_KEY_ENCRYPT)
#define BITMAP_IDENTITY (BITMAP_IDENTITY_SRC | BITMAP_IDENTITY_DST)
#define BITMAP_MSG 1
#define BITMAP_X_SRC_MASK (1LL << SADB_X_EXT_SRC_MASK)
#define BITMAP_X_DST_MASK (1LL << SADB_X_EXT_DST_MASK)
#define BITMAP_X_PROTOCOL (1LL << SADB_X_EXT_PROTOCOL)
#define BITMAP_X_SRC_FLOW (1LL << SADB_X_EXT_SRC_FLOW)
#define BITMAP_X_DST_FLOW (1LL << SADB_X_EXT_DST_FLOW)
#define BITMAP_X_FLOW_TYPE (1LL << SADB_X_EXT_FLOW_TYPE)
#define BITMAP_X_SA2 (1LL << SADB_X_EXT_SA2)
#define BITMAP_X_DST2 (1LL << SADB_X_EXT_DST2)
#define BITMAP_X_POLICY (1LL << SADB_X_EXT_POLICY)
#define BITMAP_X_FLOW (BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE)
#define BITMAP_X_SUPPORTED_COMP (1LL << SADB_X_EXT_SUPPORTED_COMP)
#define BITMAP_X_UDPENCAP (1LL << SADB_X_EXT_UDPENCAP)
#define BITMAP_X_LIFETIME_LASTUSE (1LL << SADB_X_EXT_LIFETIME_LASTUSE)
#define BITMAP_X_TAG (1LL << SADB_X_EXT_TAG)
#define BITMAP_X_TAP (1LL << SADB_X_EXT_TAP)
#define BITMAP_X_SATYPE2 (1LL << SADB_X_EXT_SATYPE2)
#define BITMAP_X_RDOMAIN (1LL << SADB_X_EXT_RDOMAIN)
#define BITMAP_X_COUNTER (1LL << SADB_X_EXT_COUNTER)
#define BITMAP_X_MTU (1LL << SADB_X_EXT_MTU)
#define BITMAP_X_REPLAY (1LL << SADB_X_EXT_REPLAY)
#define BITMAP_X_IFACE (1LL << SADB_X_EXT_IFACE)
uint64_t sadb_exts_allowed_in[SADB_MAX+1] =
{
/* RESERVED */
~0,
/* GETSPI */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_SPIRANGE,
/* UPDATE */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_ADDRESS_PROXY | BITMAP_KEY | BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG | BITMAP_X_TAP | BITMAP_X_RDOMAIN | BITMAP_X_IFACE,
/* ADD */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_KEY | BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_LIFETIME_LASTUSE | BITMAP_X_TAG | BITMAP_X_TAP | BITMAP_X_RDOMAIN | BITMAP_X_IFACE,
/* DELETE */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_RDOMAIN,
/* GET */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_RDOMAIN,
/* ACQUIRE */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_IDENTITY | BITMAP_PROPOSAL,
/* REGISTER */
0,
/* EXPIRE */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
/* FLUSH */
0,
/* DUMP */
0,
/* X_PROMISC */
0,
/* X_ADDFLOW */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_IDENTITY_SRC | BITMAP_IDENTITY_DST | BITMAP_X_FLOW | BITMAP_X_RDOMAIN,
/* X_DELFLOW */
BITMAP_X_FLOW | BITMAP_X_RDOMAIN,
/* X_GRPSPIS */
BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | BITMAP_X_SATYPE2 | BITMAP_X_RDOMAIN,
/* X_ASKPOLICY */
BITMAP_X_POLICY,
};
uint64_t sadb_exts_required_in[SADB_MAX+1] =
{
/* RESERVED */
0,
/* GETSPI */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_SPIRANGE,
/* UPDATE */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
/* ADD */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* DELETE */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* GET */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* ACQUIRE */
0,
/* REGISTER */
0,
/* EXPIRE */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
/* FLUSH */
0,
/* DUMP */
0,
/* X_PROMISC */
0,
/* X_ADDFLOW */
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE,
/* X_DELFLOW */
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE,
/* X_GRPSPIS */
BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | BITMAP_X_SATYPE2,
/* X_ASKPOLICY */
BITMAP_X_POLICY,
};
const uint64_t sadb_exts_allowed_out[SADB_MAX+1] =
{
/* RESERVED */
~0,
/* GETSPI */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
/* UPDATE */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_ADDRESS_PROXY | BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG | BITMAP_X_TAP | BITMAP_X_RDOMAIN | BITMAP_X_IFACE,
/* ADD */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG | BITMAP_X_TAP | BITMAP_X_RDOMAIN | BITMAP_X_IFACE,
/* DELETE */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_RDOMAIN,
/* GET */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_KEY | BITMAP_IDENTITY | BITMAP_X_UDPENCAP | BITMAP_X_LIFETIME_LASTUSE | BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_FLOW_TYPE | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_TAG | BITMAP_X_TAP | BITMAP_X_COUNTER | BITMAP_X_RDOMAIN | BITMAP_X_MTU | BITMAP_X_REPLAY | BITMAP_X_IFACE,
/* ACQUIRE */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_IDENTITY | BITMAP_PROPOSAL,
/* REGISTER */
BITMAP_SUPPORTED_AUTH | BITMAP_SUPPORTED_ENCRYPT | BITMAP_X_SUPPORTED_COMP,
/* EXPIRE */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS,
/* FLUSH */
0,
/* DUMP */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_IDENTITY,
/* X_PROMISC */
0,
/* X_ADDFLOW */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE | BITMAP_IDENTITY_SRC | BITMAP_IDENTITY_DST | BITMAP_X_RDOMAIN,
/* X_DELFLOW */
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE | BITMAP_X_RDOMAIN,
/* X_GRPSPIS */
BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | BITMAP_X_SATYPE2 | BITMAP_X_RDOMAIN,
/* X_ASKPOLICY */
BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_FLOW_TYPE | BITMAP_X_POLICY,
};
const uint64_t sadb_exts_required_out[SADB_MAX+1] =
{
/* RESERVED */
0,
/* GETSPI */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* UPDATE */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* ADD */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* DELETE */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* GET */
BITMAP_SA | BITMAP_LIFETIME_CURRENT | BITMAP_ADDRESS_DST,
/* ACQUIRE */
0,
/* REGISTER */
BITMAP_SUPPORTED_AUTH | BITMAP_SUPPORTED_ENCRYPT | BITMAP_X_SUPPORTED_COMP,
/* EXPIRE */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* FLUSH */
0,
/* DUMP */
0,
/* X_PROMISC */
0,
/* X_ADDFLOW */
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE,
/* X_DELFLOW */
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE,
/* X_GRPSPIS */
BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | BITMAP_X_SATYPE2,
/* X_REPPOLICY */
BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_FLOW_TYPE,
};
int
pfkeyv2_parsemessage(void *p, int len, void **headers)
{
struct sadb_ext *sadb_ext;
int i, left = len;
uint64_t allow, seen = 1;
struct sadb_msg *sadb_msg = (struct sadb_msg *) p;
bzero(headers, (SADB_EXT_MAX + 1) * sizeof(void *));
if (left < sizeof(struct sadb_msg)) {
DPRINTF("message too short");
return (EINVAL);
}
headers[0] = p;
if (sadb_msg->sadb_msg_len * sizeof(uint64_t) != left) {
DPRINTF("length not a multiple of 64");
return (EINVAL);
}
p += sizeof(struct sadb_msg);
left -= sizeof(struct sadb_msg);
if (sadb_msg->sadb_msg_reserved) {
DPRINTF("message header reserved field set");
return (EINVAL);
}
if (sadb_msg->sadb_msg_type > SADB_MAX) {
DPRINTF("message type > %d", SADB_MAX);
return (EINVAL);
}
if (!sadb_msg->sadb_msg_type) {
DPRINTF("message type unset");
return (EINVAL);
}
if (sadb_msg->sadb_msg_pid != curproc->p_p->ps_pid) {
DPRINTF("bad PID value");
return (EINVAL);
}
if (sadb_msg->sadb_msg_errno) {
DPRINTF("errno set");
return (EINVAL);
}
allow = sadb_exts_allowed_in[sadb_msg->sadb_msg_type];
while (left > 0) {
sadb_ext = (struct sadb_ext *)p;
if (left < sizeof(struct sadb_ext)) {
DPRINTF("extension header too short");
return (EINVAL);
}
i = sadb_ext->sadb_ext_len * sizeof(uint64_t);
if (left < i) {
DPRINTF("extension header exceeds message length");
return (EINVAL);
}
if (sadb_ext->sadb_ext_type > SADB_EXT_MAX) {
DPRINTF("unknown extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (!sadb_ext->sadb_ext_type) {
DPRINTF("unset extension header");
return (EINVAL);
}
if (!(allow & (1LL << sadb_ext->sadb_ext_type))) {
DPRINTF("extension header %d not permitted on message "
"type %d",
sadb_ext->sadb_ext_type, sadb_msg->sadb_msg_type);
return (EINVAL);
}
if (headers[sadb_ext->sadb_ext_type]) {
DPRINTF("duplicate extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
seen |= (1LL << sadb_ext->sadb_ext_type);
switch (sadb_ext->sadb_ext_type) {
case SADB_EXT_SA:
case SADB_X_EXT_SA2:
{
struct sadb_sa *sadb_sa = (struct sadb_sa *)p;
if (i != sizeof(struct sadb_sa)) {
DPRINTF("bad header length for SA extension "
"header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_sa->sadb_sa_state > SADB_SASTATE_MAX) {
DPRINTF("unknown SA state %d in SA extension "
"header %d",
sadb_sa->sadb_sa_state,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_sa->sadb_sa_state == SADB_SASTATE_DEAD) {
DPRINTF("cannot set SA state to dead, "
"SA extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_sa->sadb_sa_encrypt > SADB_EALG_MAX) {
DPRINTF("unknown encryption algorithm %d "
"in SA extension header %d",
sadb_sa->sadb_sa_encrypt,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_sa->sadb_sa_auth > SADB_AALG_MAX) {
DPRINTF("unknown authentication algorithm %d "
"in SA extension header %d",
sadb_sa->sadb_sa_auth,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_sa->sadb_sa_replay > 64) {
DPRINTF("unsupported replay window size %d "
"in SA extension header %d",
sadb_sa->sadb_sa_replay,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
}
break;
case SADB_X_EXT_PROTOCOL:
case SADB_X_EXT_FLOW_TYPE:
case SADB_X_EXT_SATYPE2:
if (i != sizeof(struct sadb_protocol)) {
DPRINTF("bad PROTOCOL/FLOW/SATYPE2 header "
"length in extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
break;
case SADB_X_EXT_POLICY:
if (i != sizeof(struct sadb_x_policy)) {
DPRINTF("bad POLICY header length");
return (EINVAL);
}
break;
case SADB_EXT_LIFETIME_CURRENT:
case SADB_EXT_LIFETIME_HARD:
case SADB_EXT_LIFETIME_SOFT:
case SADB_X_EXT_LIFETIME_LASTUSE:
if (i != sizeof(struct sadb_lifetime)) {
DPRINTF("bad header length for LIFETIME "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
break;
case SADB_EXT_ADDRESS_SRC:
case SADB_EXT_ADDRESS_DST:
case SADB_EXT_ADDRESS_PROXY:
case SADB_X_EXT_SRC_MASK:
case SADB_X_EXT_DST_MASK:
case SADB_X_EXT_SRC_FLOW:
case SADB_X_EXT_DST_FLOW:
case SADB_X_EXT_DST2:
{
struct sadb_address *sadb_address =
(struct sadb_address *)p;
struct sockaddr *sa = (struct sockaddr *)(p +
sizeof(struct sadb_address));
if (i < sizeof(struct sadb_address) +
sizeof(struct sockaddr)) {
DPRINTF("bad ADDRESS extension header %d "
"length",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_address->sadb_address_reserved) {
DPRINTF("ADDRESS extension header %d reserved "
"field set",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sa->sa_len &&
(i != sizeof(struct sadb_address) +
PADUP(sa->sa_len))) {
DPRINTF("bad sockaddr length field in ADDRESS "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
switch (sa->sa_family) {
case AF_INET:
if (sizeof(struct sadb_address) +
PADUP(sizeof(struct sockaddr_in)) != i) {
DPRINTF("invalid ADDRESS extension "
"header %d length",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sa->sa_len != sizeof(struct sockaddr_in)) {
DPRINTF("bad sockaddr_in length in "
"ADDRESS extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
/* Only check the right pieces */
switch (sadb_ext->sadb_ext_type)
{
case SADB_X_EXT_SRC_MASK:
case SADB_X_EXT_DST_MASK:
case SADB_X_EXT_SRC_FLOW:
case SADB_X_EXT_DST_FLOW:
break;
default:
if (((struct sockaddr_in *)sa)->sin_port) {
DPRINTF("port field set in "
"sockaddr_in of ADDRESS "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
break;
}
{
char zero[sizeof(((struct sockaddr_in *)sa)->sin_zero)];
bzero(zero, sizeof(zero));
if (bcmp(&((struct sockaddr_in *)sa)->sin_zero, zero, sizeof(zero))) {
DPRINTF("reserved sockaddr_in "
"field non-zero'ed in "
"ADDRESS extension header "
"%d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
}
break;
#ifdef INET6
case AF_INET6:
if (i != sizeof(struct sadb_address) +
PADUP(sizeof(struct sockaddr_in6))) {
DPRINTF("invalid sockaddr_in6 length "
"in ADDRESS extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sa->sa_len !=
sizeof(struct sockaddr_in6)) {
DPRINTF("bad sockaddr_in6 length in "
"ADDRESS extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (((struct sockaddr_in6 *)sa)->sin6_flowinfo) {
DPRINTF("flowinfo field set in "
"sockaddr_in6 of ADDRESS "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
/* Only check the right pieces */
switch (sadb_ext->sadb_ext_type)
{
case SADB_X_EXT_SRC_MASK:
case SADB_X_EXT_DST_MASK:
case SADB_X_EXT_SRC_FLOW:
case SADB_X_EXT_DST_FLOW:
break;
default:
if (((struct sockaddr_in6 *)sa)->sin6_port) {
DPRINTF("port field set in "
"sockaddr_in6 of ADDRESS "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
break;
}
break;
#endif /* INET6 */
default:
if (sadb_msg->sadb_msg_satype ==
SADB_X_SATYPE_TCPSIGNATURE &&
sa->sa_family == 0)
break;
DPRINTF("unknown address family %d in ADDRESS "
"extension header %d",
sa->sa_family, sadb_ext->sadb_ext_type);
return (EINVAL);
}
}
break;
case SADB_EXT_KEY_AUTH:
case SADB_EXT_KEY_ENCRYPT:
{
struct sadb_key *sadb_key = (struct sadb_key *)p;
if (i < sizeof(struct sadb_key)) {
DPRINTF("bad header length in KEY extension "
"header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (!sadb_key->sadb_key_bits) {
DPRINTF("key length unset in KEY extension "
"header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (((sadb_key->sadb_key_bits + 63) / 64) * sizeof(uint64_t) != i - sizeof(struct sadb_key)) {
DPRINTF("invalid key length in KEY extension "
"header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_key->sadb_key_reserved) {
DPRINTF("reserved field set in KEY extension "
"header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
}
break;
case SADB_EXT_IDENTITY_SRC:
case SADB_EXT_IDENTITY_DST:
{
struct sadb_ident *sadb_ident = (struct sadb_ident *)p;
if (i < sizeof(struct sadb_ident)) {
DPRINTF("bad header length of IDENTITY "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_ident->sadb_ident_type > SADB_IDENTTYPE_MAX) {
DPRINTF("unknown identity type %d in IDENTITY "
"extension header %d",
sadb_ident->sadb_ident_type,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_ident->sadb_ident_reserved) {
DPRINTF("reserved field set in IDENTITY "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (i > sizeof(struct sadb_ident)) {
char *c =
(char *)(p + sizeof(struct sadb_ident));
int j;
if (*(char *)(p + i - 1)) {
DPRINTF("non NUL-terminated identity "
"in IDENTITY extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
j = PADUP(strlen(c) + 1) +
sizeof(struct sadb_ident);
if (i != j) {
DPRINTF("actual identity length does "
"not match expected length in "
"identity extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
}
}
break;
case SADB_EXT_SENSITIVITY:
{
struct sadb_sens *sadb_sens = (struct sadb_sens *)p;
if (i < sizeof(struct sadb_sens)) {
DPRINTF("bad header length for SENSITIVITY "
"extension header");
return (EINVAL);
}
if (i != (sadb_sens->sadb_sens_sens_len +
sadb_sens->sadb_sens_integ_len) *
sizeof(uint64_t) +
sizeof(struct sadb_sens)) {
DPRINTF("bad payload length for SENSITIVITY "
"extension header");
return (EINVAL);
}
}
break;
case SADB_EXT_PROPOSAL:
{
struct sadb_prop *sadb_prop = (struct sadb_prop *)p;
if (i < sizeof(struct sadb_prop)) {
DPRINTF("bad PROPOSAL header length");
return (EINVAL);
}
if (sadb_prop->sadb_prop_reserved) {
DPRINTF("reserved fieldset in PROPOSAL "
"extension header");
return (EINVAL);
}
if ((i - sizeof(struct sadb_prop)) %
sizeof(struct sadb_comb)) {
DPRINTF("bad proposal length");
return (EINVAL);
}
{
struct sadb_comb *sadb_comb =
(struct sadb_comb *)(p +
sizeof(struct sadb_prop));
int j;
for (j = 0;
j < (i - sizeof(struct sadb_prop))/
sizeof(struct sadb_comb);
j++) {
if (sadb_comb->sadb_comb_auth >
SADB_AALG_MAX) {
DPRINTF("unknown "
"authentication algorithm "
"%d in PROPOSAL",
sadb_comb->sadb_comb_auth);
return (EINVAL);
}
if (sadb_comb->sadb_comb_encrypt >
SADB_EALG_MAX) {
DPRINTF("unknown encryption "
"algorithm %d in PROPOSAL",
sadb_comb->
sadb_comb_encrypt);
return (EINVAL);
}
if (sadb_comb->sadb_comb_reserved) {
DPRINTF("reserved field set "
"in COMB header");
return (EINVAL);
}
}
}
}
break;
case SADB_EXT_SUPPORTED_AUTH:
case SADB_EXT_SUPPORTED_ENCRYPT:
case SADB_X_EXT_SUPPORTED_COMP:
{
struct sadb_supported *sadb_supported =
(struct sadb_supported *)p;
int j;
if (i < sizeof(struct sadb_supported)) {
DPRINTF("bad header length for SUPPORTED " "extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_supported->sadb_supported_reserved) {
DPRINTF("reserved field set in SUPPORTED "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
{
struct sadb_alg *sadb_alg =
(struct sadb_alg *)(p +
sizeof(struct sadb_supported));
int max_alg;
max_alg = sadb_ext->sadb_ext_type ==
SADB_EXT_SUPPORTED_AUTH ?
SADB_AALG_MAX : SADB_EXT_SUPPORTED_ENCRYPT ?
SADB_EALG_MAX : SADB_X_CALG_MAX;
for (j = 0;
j < sadb_supported->sadb_supported_len - 1;
j++) {
if (sadb_alg->sadb_alg_id > max_alg) {
DPRINTF("unknown algorithm %d "
"in SUPPORTED extension "
"header %d",
sadb_alg->sadb_alg_id,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_alg->sadb_alg_reserved) {
DPRINTF("reserved field set "
"in supported algorithms "
"header inside SUPPORTED "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
sadb_alg++;
}
}
}
break;
case SADB_EXT_SPIRANGE:
{
struct sadb_spirange *sadb_spirange =
(struct sadb_spirange *)p;
if (i != sizeof(struct sadb_spirange)) {
DPRINTF("bad header length of SPIRANGE "
"extension header");
return (EINVAL);
}
if (sadb_spirange->sadb_spirange_min >
sadb_spirange->sadb_spirange_max) {
DPRINTF("bad SPI range");
return (EINVAL);
}
}
break;
case SADB_X_EXT_UDPENCAP:
if (i != sizeof(struct sadb_x_udpencap)) {
DPRINTF("bad UDPENCAP header length");
return (EINVAL);
}
break;
case SADB_X_EXT_RDOMAIN:
if (i != sizeof(struct sadb_x_rdomain)) {
DPRINTF("bad RDOMAIN header length");
return (EINVAL);
}
break;
#if NPF > 0
case SADB_X_EXT_TAG:
if (i < sizeof(struct sadb_x_tag)) {
DPRINTF("TAG extension header too small");
return (EINVAL);
}
if (i > (sizeof(struct sadb_x_tag) +
PF_TAG_NAME_SIZE)) {
DPRINTF("TAG extension header too long");
return (EINVAL);
}
break;
case SADB_X_EXT_TAP:
if (i < sizeof(struct sadb_x_tap)) {
DPRINTF("TAP extension header too small");
return (EINVAL);
}
if (i > sizeof(struct sadb_x_tap)) {
DPRINTF("TAP extension header too long");
return (EINVAL);
}
break;
#endif
case SADB_X_EXT_IFACE:
if (i != sizeof(struct sadb_x_iface)) {
DPRINTF("bad IFACE header length");
return (EINVAL);
}
break;
default:
DPRINTF("unknown extension header type %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
headers[sadb_ext->sadb_ext_type] = p;
p += i;
left -= i;
}
if (left) {
DPRINTF("message too long");
return (EINVAL);
}
{
uint64_t required;
required = sadb_exts_required_in[sadb_msg->sadb_msg_type];
if ((seen & required) != required) {
DPRINTF("required fields missing");
return (EINVAL);
}
}
switch (((struct sadb_msg *)headers[0])->sadb_msg_type) {
case SADB_UPDATE:
if (((struct sadb_sa *)headers[SADB_EXT_SA])->sadb_sa_state !=
SADB_SASTATE_MATURE) {
DPRINTF("updating non-mature SA prohibited");
return (EINVAL);
}
break;
case SADB_ADD:
if (((struct sadb_sa *)headers[SADB_EXT_SA])->sadb_sa_state !=
SADB_SASTATE_MATURE) {
DPRINTF("adding non-mature SA prohibited");
return (EINVAL);
}
break;
}
return (0);
}