Edit

IABSD.fr/src/sys/net/pfkeyv2_parsemessage.c

Branch :

  • Show log

    Commit

  • Author : tobhe
    Date : 2024-07-23 20:04:51
    Hash : 26723e1a
    Message : Accept and ignore SADB_X_EXT_REPLAY and SADB_X_EXT_COUNTER payloads for incoming SADB_ADD and SADB_UPDATE message. Since we send them as part of the SADB_GET reply we must also accept them on SADB_ADD/UPDATE as sasyncd will forward payloads previously received in SADB_GET. Fixes a bug where sasync can't restore SAs because pfkey returns EINVAL. From Rafa\xc5\x82 Ramocki ok bluhm@

  • sys/net/pfkeyv2_parsemessage.c
  • /*	$OpenBSD: pfkeyv2_parsemessage.c,v 1.63 2024/07/23 20:04:51 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_COUNTER | BITMAP_X_REPLAY | 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_COUNTER | BITMAP_X_REPLAY | 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;
    		case SADB_X_EXT_REPLAY:
    			if (i != sizeof(struct sadb_x_replay)) {
    				DPRINTF("bad REPLAY header length");
    				return (EINVAL);
    			}
    			break;
    		case SADB_X_EXT_COUNTER:
    			if (i != sizeof(struct sadb_x_counter)) {
    				DPRINTF("bad COUNTER 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);
    }