ipsecconf.c revision c758f97ff8b15826787cfb3cd4ebd6251a90bf57
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <strings.h>
#include <stropts.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <locale.h>
#include <syslog.h>
#include <pwd.h>
#include <sys/param.h>
#include <sys/sysmacros.h> /* MIN, MAX */
#include <sys/sockio.h>
#include <net/pfkeyv2.h>
#include <net/pfpolicy.h>
#include <inet/ipsec_impl.h>
#include <signal.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/systeminfo.h>
#include <nss_dbdefs.h> /* NSS_BUFLEN_HOSTS */
#include <netinet/in.h>
#include <assert.h>
#include <inet/ip.h>
#include <ipsec_util.h>
#include <netinet/in_systm.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
/*
* Buffer length to read in pattern/properties.
*/
#define MAXLEN 1024
/*
* Used by parse_one and parse/parse_action to communicate
* the errors. -1 is failure, which is not defined here.
*/
enum parse_errors {PARSE_SUCCESS, PARSE_EOF};
/*
* For spdsock_get_ext() diagnostics.
*/
#define SPDSOCK_DIAG_BUF_LEN 128
static char spdsock_diag_buf[SPDSOCK_DIAG_BUF_LEN];
/*
* Define CURL here so that while you are reading
* this code, it does not affect "vi" in pattern
* matching.
*/
#define CURL_BEGIN '{'
#define CURL_END '}'
#define MAXARGS 20
#define NOERROR 0
/*
* IPSEC_CONF_ADD should start with 1, so that when multiple commands
* are given, we can fail the request.
*/
enum ipsec_cmds {IPSEC_CONF_ADD = 1, IPSEC_CONF_DEL, IPSEC_CONF_VIEW,
IPSEC_CONF_FLUSH, IPSEC_CONF_LIST, IPSEC_CONF_SUB};
static const char policy_conf_file[] = "/var/run/ipsecpolicy.conf";
static const char lock_file[] = "/var/run/ipsecconf.lock";
static const char index_tag[] = "#INDEX";
#define POLICY_CONF_FILE policy_conf_file
#define LOCK_FILE lock_file
#define INDEX_TAG index_tag
/*
* Valid algorithm length.
*/
#define VALID_ALG_LEN 40
/* Types of Error messages */
typedef enum error_type {BAD_ERROR, DUP_ERROR, REQ_ERROR} error_type_t;
static int cmd;
static char *filename;
static char lo_buf[MAXLEN]; /* Leftover buffer */
/* Error reporting stuff */
#define CBUF_LEN 4096 /* Maximum size of the cmd */
/*
* Following are used for reporting errors with arguments.
* We store the line numbers of each argument as we parse them,
* so that the error reporting is more specific. We can have only
* MAXARGS -1 for pattern and properties and one for action.
*/
#define ARG_BUF_LEN ((2 * (MAXARGS - 1)) + 1)
static int arg_indices[ARG_BUF_LEN];
static int argindex;
static int linecount;
static char cbuf[CBUF_LEN]; /* Command buffer */
static int cbuf_offset;
#define BYPASS_POLICY_BOOST 0x00800000
#define ESP_POLICY_BOOST 0x00400000
#define AH_POLICY_BOOST 0x00200000
#define INITIAL_BASE_PRIORITY 0x000fffff
/*
* the number used to order the
* rules starts at a certain base and
* goes down. i.e. rules earlier in
* the file are checked first
*/
static uint32_t priority = INITIAL_BASE_PRIORITY;
#define AH_AUTH 0
#define ESP_ENCR 1
#define ESP_AUTH 2
/*
* for deleting adds on error
*/
typedef struct d_list_s
{
struct d_list_s *next;
int index;
} d_list_t;
static d_list_t *d_list = NULL;
static d_list_t *d_tail = NULL;
/*
* Used for multi-homed source/dest hosts.
*/
static struct hostent *shp, *dhp;
static unsigned int splen, dplen;
static boolean_t has_saprefix, has_daprefix;
static uint32_t seq_cnt = 0;
/* lexxed out action and related properties */
typedef struct ap_s
{
char *act;
char *prop[MAXARGS + 1];
} ap_t;
/* one lexxed out rule */
typedef struct act_prop_s {
char *pattern[MAXARGS+1];
ap_t ap[MAXARGS + 1];
} act_prop_t;
typedef struct
{
uint8_t alg_id;
uint32_t alg_minbits;
uint32_t alg_maxbits;
} algreq_t;
/* structure to hold all information for one act_prop_t */
typedef struct ips_act_props_s {
struct ips_act_props_s *iap_next;
struct ips_conf_s *iap_head;
/*
* IPsec action types (in SPD_ATTR_TYPE attribute)
* SPD_ACTTYPE_DROP 0x0001
* SPD_ACTTYPE_PASS 0x0002
* SPD_ACTTYPE_IPSEC 0x0003
*/
uint16_t iap_action;
uint16_t iap_act_tok;
/*
* Action ATTR flags (in SPD_ATTR_FLAGS attribute)
* SPD_APPLY_AH 0x0001
* SPD_APPLY_ESP 0x0002
* SPD_APPLY_SE 0x0004 * self-encapsulation *
* SPD_APPLY_COMP 0x0008 * compression; NYI *
* SPD_APPLY_UNIQUE 0x0010 * unique per-flow SA *
* SPD_APPLY_BYPASS 0x0020 * bypass policy *
*/
uint16_t iap_attr;
uint16_t iap_attr_tok[5];
algreq_t iap_aauth;
algreq_t iap_eencr;
algreq_t iap_eauth;
uint32_t iap_life_soft_time;
uint32_t iap_life_hard_time;
uint32_t iap_life_soft_bytes;
uint32_t iap_life_hard_bytes;
} ips_act_props_t;
#define V4_PART_OF_V6(v6) v6._S6_un._S6_u32[3]
typedef struct ips_conf_s {
/* selector */
uint16_t patt_tok[8];
uint8_t has_saddr;
uint8_t has_daddr;
uint8_t has_smask;
uint8_t has_dmask;
uint8_t has_type;
uint8_t has_code;
uint16_t swap;
struct in6_addr ips_src_addr_v6;
struct in6_addr ips_src_mask_v6;
struct in6_addr ips_dst_addr_v6;
struct in6_addr ips_dst_mask_v6;
uint8_t ips_src_mask_len;
uint8_t ips_dst_mask_len;
in_port_t ips_src_port_min;
in_port_t ips_src_port_max;
in_port_t ips_dst_port_min;
in_port_t ips_dst_port_max;
uint8_t ips_icmp_type;
uint8_t ips_icmp_type_end;
uint8_t ips_icmp_code;
uint8_t ips_icmp_code_end;
uint8_t ips_ulp_prot;
uint8_t ips_ipsec_prot;
uint8_t ips_isv4;
/*
* SPD_RULE_FLAG_INBOUND 0x0001
* SPD_RULE_FLAG_OUTBOUND 0x0002
*/
uint8_t ips_dir;
uint64_t ips_policy_index;
uint32_t ips_act_cnt;
ips_act_props_t *ips_acts;
} ips_conf_t;
#define ips_src_addr V4_PART_OF_V6(ips_src_addr_v6)
#define ips_dst_addr V4_PART_OF_V6(ips_dst_addr_v6)
static int ipsecconf_nflag; /* Used only with -l option */
static int ipsecconf_qflag; /* Used only with -a|-r option */
typedef struct str_val {
const char *string;
int value;
} str_val_t;
typedef struct str_tval {
const char *string;
int tok_val;
int value;
} str_tval_t;
static int parse_int(const char *);
static void usage(void);
static int ipsec_conf_del(int, boolean_t);
static int ipsec_conf_add(void);
static int ipsec_conf_sub(void);
static int ipsec_conf_flush(int);
static int ipsec_conf_view(void);
static int ipsec_conf_list(void);
static int lock(void);
static int unlock(int);
static int parse_one(FILE *, act_prop_t *);
static void reconfigure();
static void in_prefixlentomask(unsigned int, uchar_t *);
static unsigned int in_getprefixlen(char *);
static int in_masktoprefix(uint8_t *, boolean_t);
static int parse_address(int, char *);
#ifdef DEBUG_HEAVY
static void pfpol_msg_dump(spd_msg_t *msg, char *);
#endif /* DEBUG_HEAVY */
static void print_pfpol_msg(spd_msg_t *);
static int pfp_delete_rule(uint64_t);
static void ipsec_conf_admin(uint8_t);
static void print_bit_range(int, int);
static void nuke_adds();
#ifdef DEBUG
static void dump_conf(ips_conf_t *);
#endif
typedef struct
{
uint32_t id;
uint32_t minkeybits;
uint32_t maxkeybits;
uint32_t defkeybits;
uint32_t incr;
} alginfo_t;
static int ipsec_nalgs[3];
static alginfo_t known_algs[3][256];
#define IPS_SRC_MASK SPD_EXT_LCLADDR + 100
#define IPS_DST_MASK SPD_EXT_REMADDR + 100
/*
* if inbound, src=remote, dst=local
* if outbound, src=local, dst=remote
*/
#define TOK_saddr 1
#define TOK_daddr 2
#define TOK_sport 3
#define TOK_dport 4
#define TOK_smask 5
#define TOK_dmask 6
#define TOK_ulp 7
#define TOK_local 8
#define TOK_lport 9
#define TOK_remote 10
#define TOK_rport 11
#define TOK_dir 12
#define TOK_type 13
#define TOK_code 14
#define IPS_SA SPD_ATTR_END
#define IPS_DIR SPD_ATTR_EMPTY
static str_tval_t pattern_table[] = {
{"saddr", TOK_saddr, SPD_EXT_LCLADDR},
{"src", TOK_saddr, SPD_EXT_LCLADDR},
{"srcaddr", TOK_saddr, SPD_EXT_LCLADDR},
{"daddr", TOK_daddr, SPD_EXT_REMADDR},
{"dst", TOK_daddr, SPD_EXT_REMADDR},
{"dstaddr", TOK_daddr, SPD_EXT_REMADDR},
{"sport", TOK_sport, SPD_EXT_LCLPORT},
{"dport", TOK_dport, SPD_EXT_REMPORT},
{"smask", TOK_smask, IPS_SRC_MASK},
{"dmask", TOK_dmask, IPS_DST_MASK},
{"ulp", TOK_ulp, SPD_EXT_PROTO},
{"proto", TOK_ulp, SPD_EXT_PROTO},
{"local", TOK_local, SPD_EXT_LCLADDR},
{"laddr", TOK_local, SPD_EXT_LCLADDR},
{"lport", TOK_lport, SPD_EXT_LCLPORT},
{"remote", TOK_remote, SPD_EXT_REMADDR},
{"raddr", TOK_remote, SPD_EXT_REMADDR},
{"rport", TOK_rport, SPD_EXT_REMPORT},
{"dir", TOK_dir, IPS_DIR},
{"type", TOK_type, SPD_EXT_ICMP_TYPECODE},
{"code", TOK_code, SPD_EXT_ICMP_TYPECODE},
{NULL, 0, 0},
};
#define TOK_apply 1
#define TOK_permit 2
#define TOK_ipsec 3
#define TOK_bypass 4
#define TOK_drop 5
#define TOK_or 6
static str_tval_t action_table[] = {
{"apply", TOK_apply, SPD_ACTTYPE_IPSEC},
{"permit", TOK_permit, SPD_ACTTYPE_IPSEC},
{"ipsec", TOK_ipsec, SPD_ACTTYPE_IPSEC},
{"bypass", TOK_bypass, SPD_ACTTYPE_PASS},
{"pass", TOK_bypass, SPD_ACTTYPE_PASS},
{"drop", TOK_drop, SPD_ACTTYPE_DROP},
{"or", TOK_or, 0},
{NULL, 0, 0},
};
static str_val_t property_table[] = {
{"auth_algs", SPD_ATTR_AH_AUTH},
{"encr_algs", SPD_ATTR_ESP_ENCR},
{"encr_auth_algs", SPD_ATTR_ESP_AUTH},
{"sa", IPS_SA},
{"dir", IPS_DIR},
{NULL, 0},
};
static str_val_t icmp_type_table[] = {
{"unreach", ICMP_UNREACH},
{"echo", ICMP_ECHO},
{"echorep", ICMP_ECHOREPLY},
{"squench", ICMP_SOURCEQUENCH},
{"redir", ICMP_REDIRECT},
{"timex", ICMP_TIMXCEED},
{"paramprob", ICMP_PARAMPROB},
{"timest", ICMP_TSTAMP},
{"timestrep", ICMP_TSTAMPREPLY},
{"inforeq", ICMP_IREQ},
{"inforep", ICMP_IREQREPLY},
{"maskreq", ICMP_MASKREQ},
{"maskrep", ICMP_MASKREPLY},
{"unreach6", ICMP6_DST_UNREACH},
{"pkttoobig6", ICMP6_PACKET_TOO_BIG},
{"timex6", ICMP6_TIME_EXCEEDED},
{"paramprob6", ICMP6_PARAM_PROB},
{"echo6", ICMP6_ECHO_REQUEST},
{"echorep6", ICMP6_ECHO_REPLY},
{"router-sol6", ND_ROUTER_SOLICIT},
{"router-ad6", ND_ROUTER_ADVERT},
{"neigh-sol6", ND_NEIGHBOR_SOLICIT},
{"neigh-ad6", ND_NEIGHBOR_ADVERT},
{"redir6", ND_REDIRECT},
{NULL, 0},
};
static str_val_t icmp_code_table[] = {
{"net-unr", ICMP_UNREACH_NET},
{"host-unr", ICMP_UNREACH_HOST},
{"proto-unr", ICMP_UNREACH_PROTOCOL},
{"port-unr", ICMP_UNREACH_PORT},
{"needfrag", ICMP_UNREACH_NEEDFRAG},
{"srcfail", ICMP_UNREACH_SRCFAIL},
{"net-unk", ICMP_UNREACH_NET_UNKNOWN},
{"host-unk", ICMP_UNREACH_HOST_UNKNOWN},
{"isolate", ICMP_UNREACH_ISOLATED},
{"net-prohib", ICMP_UNREACH_NET_PROHIB},
{"host-prohib", ICMP_UNREACH_HOST_PROHIB},
{"net-tos", ICMP_UNREACH_TOSNET},
{"host-tos", ICMP_UNREACH_TOSHOST},
{"filter-prohib", ICMP_UNREACH_FILTER_PROHIB},
{"host-preced", ICMP_UNREACH_HOST_PRECEDENCE},
{"cutoff-preced", ICMP_UNREACH_PRECEDENCE_CUTOFF},
{"no-route6", ICMP6_DST_UNREACH_NOROUTE},
{"adm-prohib6", ICMP6_DST_UNREACH_ADMIN},
{"addr-unr6", ICMP6_DST_UNREACH_ADDR},
{"port-unr6", ICMP6_DST_UNREACH_NOPORT},
{"hop-limex6", ICMP6_TIME_EXCEED_TRANSIT},
{"frag-re-timex6", ICMP6_TIME_EXCEED_REASSEMBLY},
{"err-head6", ICMP6_PARAMPROB_HEADER},
{"unrec-head6", ICMP6_PARAMPROB_NEXTHEADER},
{"unreq-opt6", ICMP6_PARAMPROB_OPTION},
{NULL, 0},
};
static sigset_t set, oset;
static boolean_t
add_index(int index)
{
d_list_t *temp = malloc(sizeof (d_list_t));
if (temp == NULL) {
warn("malloc");
return (B_TRUE);
}
temp->index = index;
temp->next = NULL;
if (d_tail == NULL) {
d_list = d_tail = temp;
return (B_FALSE);
}
d_tail->next = temp;
d_tail = temp;
return (B_FALSE);
}
static int
block_all_signals()
{
if (sigfillset(&set) == -1) {
warn("sigfillset");
return (-1);
}
if (sigprocmask(SIG_SETMASK, &set, &oset) == -1) {
warn("sigprocmask");
return (-1);
}
return (0);
}
static int
restore_all_signals()
{
if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1) {
warn("sigprocmask");
return (-1);
}
return (0);
}
/* allocate an ips_act_props_t and link it in correctly */
static ips_act_props_t *
alloc_iap(ips_conf_t *parent)
{
ips_act_props_t *ret;
ips_act_props_t *next = parent->ips_acts;
ips_act_props_t *current = NULL;
ret = (ips_act_props_t *)calloc(sizeof (ips_act_props_t), 1);
if (ret == NULL)
return (NULL);
ret->iap_head = parent;
while (next != NULL) {
current = next;
next = next->iap_next;
}
if (current != NULL)
current->iap_next = ret;
else
parent->ips_acts = ret;
parent->ips_act_cnt++;
return (ret);
}
/*
* This function exit()s if it fails.
*/
static void
fetch_algorithms()
{
struct spd_msg msg;
struct spd_ext_actions *actp;
struct spd_attribute *attr, *endattr;
spd_ext_t *exts[SPD_EXT_MAX+1];
uint64_t reply_buf[256];
int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
int cnt, retval;
uint64_t *start, *end;
alginfo_t alg = {0, 0, 0, 0, 0};
uint_t algtype;
static boolean_t has_run = B_FALSE;
if (has_run)
return;
else
has_run = B_TRUE;
if (sfd < 0) {
err(-1, gettext("unable to open policy socket"));
}
(void) memset(&msg, 0, sizeof (msg));
msg.spd_msg_version = PF_POLICY_V1;
msg.spd_msg_type = SPD_ALGLIST;
msg.spd_msg_len = SPD_8TO64(sizeof (msg));
cnt = write(sfd, &msg, sizeof (msg));
if (cnt != sizeof (msg)) {
if (cnt < 0) {
err(-1, gettext("alglist failed: write"));
} else {
errx(-1, gettext("admin failed: short write"));
}
}
cnt = read(sfd, reply_buf, sizeof (reply_buf));
retval = spdsock_get_ext(exts, (spd_msg_t *)reply_buf, SPD_8TO64(cnt),
spdsock_diag_buf, SPDSOCK_DIAG_BUF_LEN);
if (retval == KGE_LEN && exts[0]->spd_ext_len == 0) {
/*
* No algorithms are defined in the kernel, which caused
* the extension length to be zero, and spdsock_get_ext()
* to fail with a KGE_LEN error. This is not an error
* condition, so we return nicely.
*/
return;
} else if (retval != 0) {
if (strlen(spdsock_diag_buf) != 0)
warnx(spdsock_diag_buf);
err(1, gettext("fetch_algorithms failed"));
}
if (!exts[SPD_EXT_ACTION]) {
errx(1, gettext("fetch_algorithms: action missing?!"));
}
actp = (struct spd_ext_actions *)exts[SPD_EXT_ACTION];
start = (uint64_t *)actp;
end = (start + actp->spd_actions_len);
endattr = (struct spd_attribute *)end;
attr = (struct spd_attribute *)&actp[1];
algtype = 0;
while (attr < endattr) {
switch (attr->spd_attr_tag) {
case SPD_ATTR_NOP:
case SPD_ATTR_EMPTY:
break;
case SPD_ATTR_END:
attr = endattr;
/* FALLTHRU */
case SPD_ATTR_NEXT:
known_algs[algtype][ipsec_nalgs[algtype]] = alg;
ipsec_nalgs[algtype]++;
break;
case SPD_ATTR_ENCR_MINBITS:
case SPD_ATTR_AH_MINBITS:
case SPD_ATTR_ESPA_MINBITS:
alg.minkeybits = attr->spd_attr_value;
break;
case SPD_ATTR_ENCR_MAXBITS:
case SPD_ATTR_AH_MAXBITS:
case SPD_ATTR_ESPA_MAXBITS:
alg.maxkeybits = attr->spd_attr_value;
break;
case SPD_ATTR_ENCR_DEFBITS:
case SPD_ATTR_AH_DEFBITS:
case SPD_ATTR_ESPA_DEFBITS:
alg.defkeybits = attr->spd_attr_value;
break;
case SPD_ATTR_ENCR_INCRBITS:
case SPD_ATTR_AH_INCRBITS:
case SPD_ATTR_ESPA_INCRBITS:
alg.incr = attr->spd_attr_value;
break;
case SPD_ATTR_AH_AUTH:
case SPD_ATTR_ESP_AUTH:
case SPD_ATTR_ESP_ENCR:
alg.id = attr->spd_attr_value;
algtype = attr->spd_attr_tag - SPD_ATTR_AH_AUTH;
break;
}
attr++;
}
(void) close(sfd);
}
/* data dependant transform (act_cnt) */
#define ATTR(ap, tag, value) \
do { (ap)->spd_attr_tag = (tag); \
(ap)->spd_attr_value = (value); \
ap++; } while (0)
static struct spd_attribute *
emit_alg(struct spd_attribute *ap, int type, const algreq_t *ar,
int algattr, int minbitattr, int maxbitattr)
{
int id = ar->alg_id;
int minbits, i;
if (id != 0) {
/* LINTED E_CONST_COND */
ATTR(ap, algattr, ar->alg_id);
minbits = ar->alg_minbits;
if (minbits == 0) {
for (i = 0; i < ipsec_nalgs[type]; i++) {
if (known_algs[type][i].id == id)
break;
}
if (i < ipsec_nalgs[type])
minbits = known_algs[type][i].defkeybits;
}
if (minbits != 0)
/* LINTED E_CONST_COND */
ATTR(ap, minbitattr, minbits);
if (ar->alg_maxbits != SPD_MAX_MAXBITS)
/* LINTED E_CONST_COND */
ATTR(ap, maxbitattr, ar->alg_maxbits);
}
return (ap);
}
static struct spd_attribute *
ips_act_props_to_action(struct spd_attribute *ap, uint32_t *rule_priorityp,
const ips_act_props_t *act_ptr)
{
uint32_t rule_priority = *rule_priorityp;
/* LINTED E_CONST_COND */
ATTR(ap, SPD_ATTR_EMPTY, 0);
/* type */
/* LINTED E_CONST_COND */
ATTR(ap, SPD_ATTR_TYPE, act_ptr->iap_action);
if (act_ptr->iap_action == SPD_ACTTYPE_PASS)
rule_priority |= BYPASS_POLICY_BOOST;
/* flags */
if (act_ptr->iap_attr != 0)
/* LINTED E_CONST_COND */
ATTR(ap, SPD_ATTR_FLAGS, act_ptr->iap_attr);
/* esp */
if (act_ptr->iap_attr & SPD_APPLY_ESP) {
rule_priority |= ESP_POLICY_BOOST;
/* encr */
ap = emit_alg(ap, ESP_ENCR, &act_ptr->iap_eencr,
SPD_ATTR_ESP_ENCR,
SPD_ATTR_ENCR_MINBITS, SPD_ATTR_ENCR_MAXBITS);
/* auth */
ap = emit_alg(ap, ESP_AUTH, &act_ptr->iap_eauth,
SPD_ATTR_ESP_AUTH,
SPD_ATTR_ESPA_MINBITS, SPD_ATTR_ESPA_MAXBITS);
}
/* ah */
if (act_ptr->iap_attr & SPD_APPLY_AH) {
rule_priority |= AH_POLICY_BOOST;
/* auth */
ap = emit_alg(ap, AH_AUTH, &act_ptr->iap_aauth,
SPD_ATTR_AH_AUTH,
SPD_ATTR_AH_MINBITS, SPD_ATTR_AH_MAXBITS);
}
/* lifetimes */
if (act_ptr->iap_life_soft_time != 0)
/* LINTED E_CONST_COND */
ATTR(ap, SPD_ATTR_LIFE_SOFT_TIME, act_ptr->iap_life_soft_time);
if (act_ptr->iap_life_hard_time != 0)
/* LINTED E_CONST_COND */
ATTR(ap, SPD_ATTR_LIFE_HARD_TIME, act_ptr->iap_life_hard_time);
if (act_ptr->iap_life_soft_bytes != 0)
/* LINTED E_CONST_COND */
ATTR(ap, SPD_ATTR_LIFE_SOFT_BYTES,
act_ptr->iap_life_soft_bytes);
if (act_ptr->iap_life_hard_bytes != 0)
/* LINTED E_CONST_COND */
ATTR(ap, SPD_ATTR_LIFE_HARD_BYTES,
act_ptr->iap_life_hard_bytes);
/* LINTED E_CONST_COND */
ATTR(ap, SPD_ATTR_NEXT, 0);
*rule_priorityp = rule_priority;
return (ap);
}
static boolean_t
alg_rangecheck(uint_t type, uint_t algid, const algreq_t *ar)
{
int i;
uint_t minbits = ar->alg_minbits;
uint_t maxbits = ar->alg_maxbits;
for (i = 0; i < ipsec_nalgs[type]; i++) {
if (known_algs[type][i].id == algid)
break;
}
if (i >= ipsec_nalgs[type]) {
/*
* The kernel (where we populate known_algs from) doesn't
* return the id's associated with NONE algorithms so we
* test here if this was the reason the algorithm wasn't
* found before wrongly failing.
*/
if (((type == ESP_ENCR) && (algid == SADB_EALG_NONE)) ||
((type == ESP_AUTH) && (algid == SADB_AALG_NONE)) ||
((type == AH_AUTH) && (algid == SADB_AALG_NONE))) {
return (B_TRUE);
} else {
return (B_FALSE); /* not found */
}
}
if ((minbits == 0) && (maxbits == 0))
return (B_TRUE);
minbits = MAX(minbits, known_algs[type][i].minkeybits);
maxbits = MIN(maxbits, known_algs[type][i].maxkeybits);
/* we could also check key increments here.. */
return (minbits <= maxbits); /* non-null intersection */
}
/*
* Inspired by uts/common/inet/spd.c:ipsec_act_wildcard_expand()
*/
static struct spd_attribute *
ips_act_wild_props_to_action(struct spd_attribute *ap,
uint32_t *rule_priorityp, uint16_t *act_cntp,
const ips_act_props_t *act_ptr)
{
ips_act_props_t tact = *act_ptr;
boolean_t use_ah, use_esp, use_espa;
boolean_t wild_auth, wild_encr, wild_eauth;
uint_t auth_alg, auth_idx, auth_min, auth_max;
uint_t eauth_alg, eauth_idx, eauth_min, eauth_max;
uint_t encr_alg, encr_idx, encr_min, encr_max;
use_ah = !!(act_ptr->iap_attr & SPD_APPLY_AH);
use_esp = !!(act_ptr->iap_attr & SPD_APPLY_ESP);
use_espa = !!(act_ptr->iap_attr & SPD_APPLY_ESPA);
auth_alg = act_ptr->iap_aauth.alg_id;
eauth_alg = act_ptr->iap_eauth.alg_id;
encr_alg = act_ptr->iap_eencr.alg_id;
wild_auth = use_ah && (auth_alg == SADB_AALG_NONE);
wild_eauth = use_espa && (eauth_alg == SADB_AALG_NONE);
wild_encr = use_esp && (encr_alg == SADB_EALG_NONE);
auth_min = auth_max = auth_alg;
eauth_min = eauth_max = eauth_alg;
encr_min = encr_max = encr_alg;
/*
* set up for explosion.. for each dimension, expand output
* size by the explosion factor.
*/
if (wild_auth) {
auth_min = 0;
auth_max = ipsec_nalgs[AH_AUTH] - 1;
}
if (wild_eauth) {
eauth_min = 0;
eauth_max = ipsec_nalgs[ESP_AUTH] - 1;
}
if (wild_encr) {
encr_min = 0;
encr_max = ipsec_nalgs[ESP_ENCR] - 1;
}
#define WHICH_ALG(type, wild, idx) ((wild)?(known_algs[type][idx].id):(idx))
for (encr_idx = encr_min; encr_idx <= encr_max; encr_idx++) {
encr_alg = WHICH_ALG(ESP_ENCR, wild_encr, encr_idx);
if (use_esp &&
!alg_rangecheck(ESP_ENCR, encr_alg, &act_ptr->iap_eencr))
continue;
for (auth_idx = auth_min; auth_idx <= auth_max; auth_idx++) {
auth_alg = WHICH_ALG(AH_AUTH, wild_auth, auth_idx);
if (use_ah &&
!alg_rangecheck(AH_AUTH, auth_alg,
&act_ptr->iap_aauth))
continue;
for (eauth_idx = eauth_min; eauth_idx <= eauth_max;
eauth_idx++) {
eauth_alg = WHICH_ALG(ESP_AUTH, wild_eauth,
eauth_idx);
if (use_espa &&
!alg_rangecheck(ESP_AUTH, eauth_alg,
&act_ptr->iap_eauth))
continue;
tact.iap_eencr.alg_id = encr_alg;
tact.iap_eauth.alg_id = eauth_alg;
tact.iap_aauth.alg_id = auth_alg;
(*act_cntp)++;
ap = ips_act_props_to_action(ap,
rule_priorityp, &tact);
}
}
}
#undef WHICH_ALG
return (ap);
}
/* huge, but not safe since no length checking is done */
#define MAX_POL_MSG_LEN 16384
/*
* hand in some ips_conf_t's, get back an
* iovec of pfpol messages.
* this function converts the internal ips_conf_t into
* a form that pf_pol can use.
* return 0 on success, 1 on failure
*/
static int
ips_conf_to_pfpol_msg(int ipsec_cmd, ips_conf_t *inConf, int num_ips,
struct iovec *msg)
{
int i;
ips_conf_t *conf;
uint64_t *scratch = NULL;
for (i = 0; i < num_ips; i++) {
uint16_t *msg_len;
uint16_t act_cnt = 0;
uint64_t *next = NULL;
spd_msg_t *spd_msg;
spd_address_t *spd_address;
struct spd_rule *spd_rule;
struct spd_proto *spd_proto;
struct spd_portrange *spd_portrange;
struct spd_ext_actions *spd_ext_actions;
struct spd_attribute *ap;
struct spd_typecode *spd_typecode;
ips_act_props_t *act_ptr;
uint32_t rule_priority = 0;
scratch = calloc(1, MAX_POL_MSG_LEN);
msg[i].iov_base = (char *)scratch;
if (scratch == NULL) {
warn(gettext("memory"));
return (1);
}
conf = &(inConf[i]);
spd_msg = (spd_msg_t *)scratch;
next = (uint64_t *)&(spd_msg[1]);
msg_len = &(spd_msg->spd_msg_len);
spd_msg->spd_msg_version = PF_POLICY_V1;
spd_msg->spd_msg_pid = getpid();
spd_msg->spd_msg_seq = ++seq_cnt;
switch (ipsec_cmd) {
case SPD_ADDRULE:
spd_msg->spd_msg_type = SPD_ADDRULE;
break;
default:
warnx("%s %d", gettext("bad command:"), ipsec_cmd);
spd_msg->spd_msg_type = SPD_ADDRULE;
break;
}
/*
* SELECTOR
*/
spd_msg->spd_msg_spdid = SPD_STANDBY;
/* rule */
spd_rule = (struct spd_rule *)next;
spd_rule->spd_rule_len = SPD_8TO64(sizeof (struct spd_rule));
spd_rule->spd_rule_type = SPD_EXT_RULE;
spd_rule->spd_rule_flags = conf->ips_dir;
next = (uint64_t *)&(spd_rule[1]);
/* proto */
if (conf->ips_ulp_prot != 0) {
spd_proto = (struct spd_proto *)next;
spd_proto->spd_proto_len =
SPD_8TO64(sizeof (struct spd_proto));
spd_proto->spd_proto_exttype = SPD_EXT_PROTO;
spd_proto->spd_proto_number = conf->ips_ulp_prot;
next = (uint64_t *)&(spd_proto[1]);
}
/* icmp type/code */
if (conf->ips_ulp_prot == IPPROTO_ICMP ||
conf->ips_ulp_prot == IPPROTO_ICMPV6) {
if (conf->has_type) {
spd_typecode = (struct spd_typecode *)next;
spd_typecode->spd_typecode_len =
SPD_8TO64(sizeof (struct spd_typecode));
spd_typecode->spd_typecode_exttype =
SPD_EXT_ICMP_TYPECODE;
spd_typecode->spd_typecode_type =
conf->ips_icmp_type;
spd_typecode->spd_typecode_type_end =
conf->ips_icmp_type_end;
if (conf->has_code) {
spd_typecode->spd_typecode_code =
conf->ips_icmp_code;
spd_typecode->spd_typecode_code_end =
conf->ips_icmp_code_end;
} else {
spd_typecode->spd_typecode_code = 255;
spd_typecode->spd_typecode_code_end
= 255;
}
next = (uint64_t *)&(spd_typecode[1]);
}
}
/* src port */
if (conf->ips_src_port_min != 0 ||
conf->ips_src_port_max != 0) {
spd_portrange = (struct spd_portrange *)next;
spd_portrange->spd_ports_len =
SPD_8TO64(sizeof (struct spd_portrange));
spd_portrange->spd_ports_exttype =
(conf->swap)?SPD_EXT_REMPORT:SPD_EXT_LCLPORT;
spd_portrange->spd_ports_minport =
conf->ips_src_port_min;
spd_portrange->spd_ports_maxport =
conf->ips_src_port_max;
next = (uint64_t *)&(spd_portrange[1]);
}
/* dst port */
if (conf->ips_dst_port_min != 0 ||
conf->ips_dst_port_max != 0) {
spd_portrange = (struct spd_portrange *)next;
spd_portrange->spd_ports_len =
SPD_8TO64(sizeof (struct spd_portrange));
spd_portrange->spd_ports_exttype =
(conf->swap)?SPD_EXT_LCLPORT:SPD_EXT_REMPORT;
spd_portrange->spd_ports_minport =
conf->ips_dst_port_min;
spd_portrange->spd_ports_maxport =
conf->ips_dst_port_max;
next = (uint64_t *)&(spd_portrange[1]);
}
/* saddr */
if (conf->has_saddr) {
spd_address = (spd_address_t *)next;
next = (uint64_t *)(spd_address + 1);
spd_address->spd_address_exttype =
(conf->swap)?SPD_EXT_REMADDR:SPD_EXT_LCLADDR;
spd_address->spd_address_prefixlen =
conf->ips_src_mask_len;
if (conf->ips_isv4) {
spd_address->spd_address_af = AF_INET;
(void) memcpy(next, &(conf->ips_src_addr),
sizeof (ipaddr_t));
spd_address->spd_address_len = 2;
next += SPD_8TO64(sizeof (ipaddr_t) + 4);
if (!conf->has_smask)
spd_address->spd_address_prefixlen = 32;
} else {
spd_address->spd_address_af = AF_INET6;
(void) memcpy(next, &(conf->ips_src_addr_v6),
sizeof (in6_addr_t));
spd_address->spd_address_len = 3;
next += SPD_8TO64(sizeof (in6_addr_t));
if (!conf->has_smask)
spd_address->spd_address_prefixlen
= 128;
}
}
/* daddr */
if (conf->has_daddr) {
spd_address = (spd_address_t *)next;
next = (uint64_t *)(spd_address + 1);
spd_address->spd_address_exttype =
(conf->swap)?SPD_EXT_LCLADDR:SPD_EXT_REMADDR;
spd_address->spd_address_prefixlen =
conf->ips_dst_mask_len;
if (conf->ips_isv4) {
spd_address->spd_address_af = AF_INET;
(void) memcpy(next, &conf->ips_dst_addr,
sizeof (ipaddr_t));
spd_address->spd_address_len = 2;
/* "+ 4" below is for padding. */
next += SPD_8TO64(sizeof (ipaddr_t) + 4);
if (!conf->has_dmask)
spd_address->spd_address_prefixlen = 32;
} else {
spd_address->spd_address_af = AF_INET6;
(void) memcpy(next, &(conf->ips_dst_addr_v6),
sizeof (in6_addr_t));
spd_address->spd_address_len = 3;
next += SPD_8TO64(sizeof (in6_addr_t));
if (!conf->has_dmask)
spd_address->spd_address_prefixlen
= 128;
}
}
/* actions */
spd_ext_actions = (struct spd_ext_actions *)next;
spd_ext_actions->spd_actions_exttype = SPD_EXT_ACTION;
act_ptr = conf->ips_acts;
ap = (struct spd_attribute *)(&spd_ext_actions[1]);
rule_priority = priority--;
for (act_ptr = conf->ips_acts; act_ptr != NULL;
act_ptr = act_ptr->iap_next) {
ap = ips_act_wild_props_to_action(ap, &rule_priority,
&act_cnt, act_ptr);
}
ap[-1].spd_attr_tag = SPD_ATTR_END;
next = (uint64_t *)ap;
spd_rule->spd_rule_priority = rule_priority;
msg[i].iov_len = (uintptr_t)next - (uintptr_t)msg[i].iov_base;
*msg_len = (uint16_t)SPD_8TO64(msg[i].iov_len);
spd_ext_actions->spd_actions_count = act_cnt;
spd_ext_actions->spd_actions_len =
SPD_8TO64((uintptr_t)next - (uintptr_t)spd_ext_actions);
#ifdef DEBUG_HEAVY
printf("pfpol msg len in uint64_t's = %d\n", *msg_len);
printf("pfpol test_len in bytes = %d\n", msg[i].iov_len);
pfpol_msg_dump((spd_msg_t *)scratch,
"ips_conf_to_pfpol_msg");
#endif
}
#undef ATTR
return (0);
}
static int
get_pf_pol_socket(void)
{
int s = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
if (s < 0) {
warn(gettext("(loading pf_policy) socket:"));
}
return (s);
}
static int
send_pf_pol_message(int ipsec_cmd, ips_conf_t *conf)
{
int retval;
int cnt;
int total_len;
struct iovec polmsg;
spd_msg_t *return_buf;
spd_ext_t *exts[SPD_EXT_MAX+1];
int fd = get_pf_pol_socket();
if (fd < 0)
return (EBADF);
retval = ips_conf_to_pfpol_msg(ipsec_cmd, conf, 1, &polmsg);
if (retval) {
(void) close(fd);
return (ENOMEM);
}
total_len = polmsg.iov_len;
cnt = writev(fd, &polmsg, 1);
#ifdef DEBUG_HEAVY
(void) printf("cnt = %d\n", cnt);
#endif
if (cnt < 0) {
warn(gettext("pf_pol write"));
} else {
return_buf = (spd_msg_t *)calloc(total_len, 1);
if (return_buf == NULL) {
warn(gettext("memory"));
} else {
cnt = read(fd, (void*)return_buf, total_len);
#ifdef DEBUG_HEAVY
(void) printf("pf_pol read: cnt = %d(%d)\n", cnt,
total_len);
#endif
if (cnt > 8 && return_buf->spd_msg_errno) {
int diag = return_buf->spd_msg_diagnostic;
if (!ipsecconf_qflag) {
warnx("%s: %s",
gettext("spd_msg return"),
strerror(
return_buf->spd_msg_errno));
}
if (diag != 0)
(void) printf("%s\n",
spdsock_diag(diag));
#ifdef DEBUG_HEAVY
pfpol_msg_dump((spd_msg_t *)polmsg.iov_base,
"message in");
pfpol_msg_dump(return_buf,
"send_pf_pol_message");
#endif
retval = return_buf->spd_msg_errno;
free(return_buf);
free(polmsg.iov_base);
(void) close(fd);
return (retval);
}
retval = spdsock_get_ext(exts, return_buf,
return_buf->spd_msg_len, NULL, 0);
/* ignore retval */
if (exts[SPD_EXT_RULE]) {
conf->ips_policy_index =
((struct spd_rule *)
exts[SPD_EXT_RULE])->spd_rule_index;
if (add_index(conf->ips_policy_index)) {
free(return_buf);
free(polmsg.iov_base);
(void) close(fd);
return (ENOMEM);
}
}
free(return_buf);
}
}
free(polmsg.iov_base);
(void) close(fd);
return (0);
}
int
main(int argc, char *argv[])
{
int ret, flushret;
int c;
int index;
int lfd;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
openlog("ipsecconf", LOG_CONS, LOG_AUTH);
/*
* We don't immediately check for privilege here. This is done by IP
* when we open /dev/ip below.
*/
if (argc == 1) {
cmd = IPSEC_CONF_VIEW;
goto done;
}
while ((c = getopt(argc, argv, "nlfa:qd:r:")) != EOF) {
switch (c) {
case 'f':
/* Only one command at a time */
if (cmd != 0) {
usage();
exit(1);
}
cmd = IPSEC_CONF_FLUSH;
break;
case 'l':
/* Only one command at a time */
if (cmd != 0) {
usage();
exit(1);
}
cmd = IPSEC_CONF_LIST;
break;
case 'a':
/* Only one command at a time */
if (cmd != 0) {
usage();
exit(1);
}
cmd = IPSEC_CONF_ADD;
filename = optarg;
break;
case 'd':
/* Only one command at a time */
if (cmd != 0) {
usage();
exit(1);
}
cmd = IPSEC_CONF_DEL;
index = parse_int(optarg);
break;
case 'n' :
ipsecconf_nflag++;
break;
case 'q' :
ipsecconf_qflag++;
break;
case 'r' :
/* only one command at a time */
if (cmd != 0) {
usage();
exit(1);
}
cmd = IPSEC_CONF_SUB;
filename = optarg;
break;
default :
usage();
exit(1);
}
}
done:
ret = 0;
lfd = lock();
if (lfd == -1) {
exit(1);
}
/*
* ADD, FLUSH, DELETE needs to do two operations.
*
* 1) Update/delete/empty the POLICY_CONF_FILE.
* 2) Make an ioctl and tell IP to update its state.
*
* We already lock()ed so that only one instance of this
* program runs. We also need to make sure that the above
* operations are atomic i.e we don't want to update the file
* and get interrupted before we could tell IP. To make it
* atomic we block all the signals and restore them.
*/
switch (cmd) {
case IPSEC_CONF_LIST:
fetch_algorithms();
ret = ipsec_conf_list();
break;
case IPSEC_CONF_FLUSH:
if ((ret = block_all_signals()) == -1) {
break;
}
ret = ipsec_conf_flush(SPD_ACTIVE);
(void) restore_all_signals();
break;
case IPSEC_CONF_VIEW:
ret = ipsec_conf_view();
break;
case IPSEC_CONF_DEL:
if (index == -1) {
warnx(gettext("Invalid index"));
ret = -1;
break;
}
if ((ret = block_all_signals()) == -1) {
break;
}
ret = ipsec_conf_del(index, B_FALSE);
(void) restore_all_signals();
break;
case IPSEC_CONF_ADD:
fetch_algorithms();
if ((ret = block_all_signals()) == -1) {
break;
}
ret = ipsec_conf_add();
(void) restore_all_signals();
break;
case IPSEC_CONF_SUB:
fetch_algorithms();
if ((ret = block_all_signals()) == -1) {
break;
}
ret = ipsec_conf_sub();
(void) restore_all_signals();
break;
default :
/* If no argument is given but a "-" */
usage();
exit(1);
}
/* flush standby db */
flushret = (cmd != IPSEC_CONF_VIEW) ?
ipsec_conf_flush(SPD_STANDBY) : 0;
(void) unlock(lfd);
if (ret != 0 || flushret != 0)
ret = 1;
return (ret);
}
static int
perm_check(void)
{
if (errno != EACCES)
warn(gettext("Cannot open lock file %s"), LOCK_FILE);
else
warnx(gettext("You must be root to run ipsecconf."));
return (-1);
}
static int
lock()
{
int fd;
struct stat sbuf1;
struct stat sbuf2;
/*
* Open the file with O_CREAT|O_EXCL. If it exists already, it
* will fail. If it already exists, check whether it looks like
* the one we created.
*/
(void) umask(0077);
if ((fd = open(LOCK_FILE, O_EXCL|O_CREAT|O_RDWR, S_IRUSR|S_IWUSR))
== -1) {
if (errno != EEXIST) {
/* Some other problem. */
return (perm_check());
}
/*
* open() returned an EEXIST error. We don't fail yet
* as it could be a residual from a previous
* execution.
* File exists. make sure it is OK. We need to lstat()
* as fstat() stats the file pointed to by the symbolic
* link.
*/
if (lstat(LOCK_FILE, &sbuf1) == -1) {
warn(gettext("Cannot lstat lock file %s"), LOCK_FILE);
return (-1);
}
/*
* Check whether it is a regular file and not a symbolic
* link. Its link count should be 1. The owner should be
* root and the file should be empty.
*/
if (!S_ISREG(sbuf1.st_mode) ||
sbuf1.st_nlink != 1 ||
sbuf1.st_uid != 0 ||
sbuf1.st_size != 0) {
warnx(gettext("Bad lock file %s"), LOCK_FILE);
return (-1);
}
if ((fd = open(LOCK_FILE, O_CREAT|O_RDWR,
S_IRUSR|S_IWUSR)) == -1) {
return (perm_check());
}
/*
* Check whether we opened the file that we lstat()ed.
*/
if (fstat(fd, &sbuf2) == -1) {
warn(gettext("Cannot fstat lock file %s"), LOCK_FILE);
return (-1);
}
if (sbuf1.st_dev != sbuf2.st_dev ||
sbuf1.st_ino != sbuf2.st_ino) {
/* File changed after we did the lstat() above */
warnx(gettext("Bad lock file %s"), LOCK_FILE);
return (-1);
}
}
if (lockf(fd, F_LOCK, 0) == -1) {
warn("lockf");
return (-1);
}
return (fd);
}
static int
unlock(int fd)
{
if (lockf(fd, F_ULOCK, 0) == -1) {
warn("lockf");
return (-1);
}
return (0);
}
/* send in TOK_* */
static void
print_pattern_string(int type)
{
int j;
for (j = 0; pattern_table[j].string != NULL; j++) {
if (type == pattern_table[j].tok_val) {
(void) printf("%s ", pattern_table[j].string);
return;
}
}
}
static void
print_icmp_typecode(uint8_t type, uint8_t type_end, uint8_t code,
uint8_t code_end)
{
(void) printf("type %d", type);
if (type_end != type)
(void) printf("-%d ", type_end);
else
(void) printf(" ");
if (code != 255) {
(void) printf("code %d", code);
if (code_end != code)
(void) printf("-%d ", code_end);
else
(void) printf(" ");
}
}
static void
print_spd_flags(uint32_t flags)
{
flags &= (SPD_RULE_FLAG_INBOUND|SPD_RULE_FLAG_OUTBOUND);
if (flags == SPD_RULE_FLAG_OUTBOUND)
(void) printf("dir out ");
else if (flags == SPD_RULE_FLAG_INBOUND)
(void) printf("dir in ");
else if (flags == (SPD_RULE_FLAG_INBOUND|SPD_RULE_FLAG_OUTBOUND))
(void) printf("dir both ");
}
static void
print_bit_range(int min, int max)
{
if (min != 0 || (max != 0 && max != SPD_MAX_MAXBITS)) {
(void) printf("(");
if (min != 0)
(void) printf("%d", min);
if (min != 0 && max != 0 && min != max) {
(void) printf("..");
if (max != 0 && max != SPD_MAX_MAXBITS)
(void) printf("%d", max);
}
(void) printf(")");
}
}
static void
print_alg(const char *tag, algreq_t *algreq, int proto_num)
{
int min = algreq->alg_minbits;
int max = algreq->alg_maxbits;
struct ipsecalgent *alg;
/*
* This function won't be called with alg_id == 0, so we don't
* have to worry about ANY vs. NONE here.
*/
(void) printf("%s ", tag);
alg = getipsecalgbynum(algreq->alg_id, proto_num, NULL);
if (alg == NULL) {
(void) printf("%d", algreq->alg_id);
} else {
(void) printf("%s", alg->a_names[0]);
freeipsecalgent(alg);
}
print_bit_range(min, max);
(void) printf(" ");
}
static void
print_ulp(uint8_t proto)
{
struct protoent *pe;
if (proto == 0)
return;
print_pattern_string(TOK_ulp);
pe = NULL;
if (!ipsecconf_nflag) {
pe = getprotobynumber(proto);
}
if (pe != NULL)
(void) printf("%s ", pe->p_name);
else
(void) printf("%d ", proto);
}
/* needs to do ranges */
static void
print_port(uint16_t in_port, int type)
{
in_port_t port = ntohs(in_port);
struct servent *sp;
if (port == 0)
return;
print_pattern_string(type);
sp = NULL;
if (!ipsecconf_nflag)
sp = getservbyport(port, NULL);
if (sp != NULL)
(void) printf("%s ", sp->s_name);
else
(void) printf("%d ", port);
}
#if 0
/*
* Print the mask (source or destination depending on the specified type)
* defined in the policy pointed to by cptr.
* We follow ifconfig's lead, i.e. we use the decimal dot notation for IPv4
* masks and the /N prefix length form for IPv6.
*/
static void
print_mask(ips_conf_t *cptr, int type)
{
struct in_addr addr;
struct in6_addr addr6;
struct in_addr mask;
char buf[INET6_ADDRSTRLEN];
boolean_t isv4;
struct in6_addr *in_addr_ptr;
struct in6_addr *in_mask_ptr;
if (type == IPS_SRC_MASK) {
in_addr_ptr = &cptr->ips_src_addr_v6;
in_mask_ptr = &cptr->ips_src_mask_v6;
} else {
in_addr_ptr = &cptr->ips_dst_addr_v6;
in_mask_ptr = &cptr->ips_dst_mask_v6;
}
isv4 = cptr->ips_isv4;
/*
* If the address is INADDR_ANY, don't print the mask.
*/
if (isv4) {
IN6_V4MAPPED_TO_INADDR(in_addr_ptr, &addr);
if (addr.s_addr == INADDR_ANY)
return;
} else {
addr6 = *in_addr_ptr;
if (IN6_IS_ADDR_UNSPECIFIED(&addr6))
return;
}
if (isv4) {
(void) printf(" ");
print_pattern_string(type);
IN6_V4MAPPED_TO_INADDR(in_mask_ptr, &mask);
(void) printf("%s ",
inet_ntop(AF_INET, (uchar_t *)&mask.s_addr, buf,
INET6_ADDRSTRLEN));
} else {
(void) printf("/%d ",
in_masktoprefix((uint8_t *)&in_mask_ptr->s6_addr, B_FALSE));
}
}
/*
* Print the address and mask.
*/
static void
print_address(ips_conf_t *cptr, int type)
{
char *cp;
struct hostent *hp;
char domain[MAXHOSTNAMELEN + 1];
struct in_addr addr;
struct in6_addr addr6;
char abuf[INET6_ADDRSTRLEN];
int error_num;
struct in6_addr *in_addr_ptr;
uchar_t *addr_ptr;
sa_family_t af;
int addr_len;
if (type == SPD_EXT_LCLADDR)
in_addr_ptr = &cptr->ips_src_addr_v6;
else
in_addr_ptr = &cptr->ips_dst_addr_v6;
if (cptr->ips_isv4) {
af = AF_INET;
/* we don't print unspecified addresses */
IN6_V4MAPPED_TO_INADDR(in_addr_ptr, &addr);
if (addr.s_addr == INADDR_ANY)
return;
addr_ptr = (uchar_t *)&addr.s_addr;
addr_len = IPV4_ADDR_LEN;
} else {
af = AF_INET6;
addr6 = *in_addr_ptr;
/* we don't print unspecified addresses */
if (IN6_IS_ADDR_UNSPECIFIED(&addr6))
return;
addr_ptr = (uchar_t *)&addr6.s6_addr;
addr_len = sizeof (struct in6_addr);
}
print_pattern_string(type);
if (!ipsecconf_nflag) {
if (sysinfo(SI_HOSTNAME, domain, MAXHOSTNAMELEN) != -1 &&
(cp = strchr(domain, '.')) != NULL) {
(void) strcpy(domain, cp + 1);
} else {
domain[0] = 0;
}
hp = getipnodebyaddr(addr_ptr, addr_len, af, &error_num);
if (hp == NULL)
cp = NULL;
else {
if ((cp = strchr(hp->h_name, '.')) != 0 &&
strcasecmp(cp + 1, domain) == 0)
*cp = 0;
cp = hp->h_name;
}
}
if (cp) {
(void) printf("%s", cp);
} else {
(void) printf("%s", inet_ntop(af, addr_ptr, abuf,
INET6_ADDRSTRLEN));
}
}
#endif
/*
* Print the address and mask.
*/
static void
print_address2(void *input, int isv4)
{
char *cp;
struct hostent *hp;
char domain[MAXHOSTNAMELEN + 1];
struct in_addr addr;
struct in6_addr addr6;
char abuf[INET6_ADDRSTRLEN];
int error_num;
struct in6_addr in_addr;
uchar_t *addr_ptr;
sa_family_t af;
int addr_len;
if (isv4) {
af = AF_INET;
(void) memcpy(&V4_PART_OF_V6(in_addr), input, 4);
/* we don't print unspecified addresses */
IN6_V4MAPPED_TO_INADDR(&in_addr, &addr);
if (addr.s_addr == INADDR_ANY)
return;
addr_ptr = (uchar_t *)&addr.s_addr;
addr_len = IPV4_ADDR_LEN;
} else {
(void) memcpy(&addr6, input, 16);
af = AF_INET6;
/* we don't print unspecified addresses */
if (IN6_IS_ADDR_UNSPECIFIED(&addr6))
return;
addr_ptr = (uchar_t *)&addr6.s6_addr;
addr_len = sizeof (struct in6_addr);
}
cp = NULL;
if (!ipsecconf_nflag) {
if (sysinfo(SI_HOSTNAME, domain, MAXHOSTNAMELEN) != -1 &&
(cp = strchr(domain, '.')) != NULL) {
(void) strcpy(domain, cp + 1);
} else {
domain[0] = 0;
}
hp = getipnodebyaddr(addr_ptr, addr_len, af, &error_num);
if (hp) {
if ((cp = strchr(hp->h_name, '.')) != 0 &&
strcasecmp(cp + 1, domain) == 0)
*cp = 0;
cp = hp->h_name;
}
}
if (cp) {
(void) printf("%s", cp);
} else {
(void) printf("%s", inet_ntop(af, addr_ptr, abuf,
INET6_ADDRSTRLEN));
}
}
/*
* Get the next SPD_DUMP message from the PF_POLICY socket. A single
* read may contain multiple messages. This function uses static buffers,
* and is therefore non-reentrant, so if you lift it for an MT application,
* be careful.
*
* Return NULL if there's an error.
*/
static spd_msg_t *
ipsec_read_dump(int pfd)
{
static uint64_t buf[SADB_8TO64(CBUF_LEN)];
static uint64_t *offset;
static int len; /* In uint64_t units. */
spd_msg_t *retval;
/* Assume offset and len are initialized to NULL and 0. */
if ((offset - len == buf) || (offset == NULL)) {
/* read a new block from the socket. */
len = read(pfd, &buf, sizeof (buf));
if (len == -1) {
warn(gettext("rule dump: bad read"));
return (NULL);
}
offset = buf;
len = SADB_8TO64(len);
} /* Else I still have more messages from a previous read. */
retval = (spd_msg_t *)offset;
offset += retval->spd_msg_len;
if (offset > buf + len) {
warnx(gettext("dump read: message corruption,"
" %d len exceeds %d boundary."),
SADB_64TO8((uintptr_t)(offset - buf)),
SADB_64TO8((uintptr_t)(buf + len)));
return (NULL);
}
return (retval);
}
/*
* returns 0 on success
* -1 on read error
* >0 on invalid returned message
*/
static int
ipsec_conf_list(void)
{
int ret;
int pfd;
struct spd_msg msg;
int cnt;
spd_msg_t *rmsg;
spd_ext_t *exts[SPD_EXT_MAX+1];
pfd = get_pf_pol_socket();
if (pfd == -1) {
warnx(gettext("Error getting list of policies from kernel"));
return (-1);
}
(void) memset(&msg, 0, sizeof (msg));
msg.spd_msg_version = PF_POLICY_V1;
msg.spd_msg_type = SPD_DUMP;
msg.spd_msg_len = SPD_8TO64(sizeof (msg));
cnt = write(pfd, &msg, sizeof (msg));
if (cnt < 0) {
warn(gettext("dump: invalid write() return"));
(void) close(pfd);
return (-1);
}
rmsg = ipsec_read_dump(pfd);
if (rmsg == NULL || rmsg->spd_msg_errno != 0) {
warnx("%s: %s", gettext("ruleset dump failed"),
(rmsg == NULL ?
gettext("read error") : strerror(rmsg->spd_msg_errno)));
(void) close(pfd);
return (-1);
}
for (;;) {
/* read rule */
rmsg = ipsec_read_dump(pfd);
if (rmsg == NULL) {
(void) close(pfd);
return (-1);
}
if (rmsg->spd_msg_errno != 0) {
warnx("%s: %s", gettext("dump read: bad message"),
strerror(rmsg->spd_msg_errno));
(void) close(pfd);
return (-1);
}
ret = spdsock_get_ext(exts, rmsg, rmsg->spd_msg_len,
spdsock_diag_buf, SPDSOCK_DIAG_BUF_LEN);
if (ret != 0) {
if (strlen(spdsock_diag_buf) != 0)
warnx(spdsock_diag_buf);
warnx("%s: %s", gettext("dump read: bad message"),
strerror(rmsg->spd_msg_errno));
(void) close(pfd);
return (ret);
}
/*
* End of dump..
*/
if (exts[SPD_EXT_RULESET] != NULL)
break; /* and return 0. */
print_pfpol_msg(rmsg);
}
(void) close(pfd);
return (0);
}
static void
print_iap(ips_act_props_t *iap)
{
/* action */
switch (iap->iap_action) {
case SPD_ACTTYPE_PASS:
(void) printf("pass ");
break;
case SPD_ACTTYPE_DROP:
(void) printf("drop ");
break;
case SPD_ACTTYPE_IPSEC:
(void) printf("ipsec ");
break;
}
/* properties */
(void) printf("%c ", CURL_BEGIN);
if (iap->iap_action == SPD_ACTTYPE_IPSEC) {
if (iap->iap_attr & SPD_APPLY_AH &&
iap->iap_aauth.alg_id != 0)
print_alg("auth_algs", &iap->iap_aauth,
IPSEC_PROTO_AH);
if (iap->iap_attr & SPD_APPLY_ESP) {
print_alg("encr_algs", &iap->iap_eencr,
IPSEC_PROTO_ESP);
if (iap->iap_eauth.alg_id != 0)
print_alg("encr_auth_algs", &iap->iap_eauth,
IPSEC_PROTO_AH);
}
if (iap->iap_attr & SPD_APPLY_UNIQUE)
(void) printf("sa unique ");
else
(void) printf("sa shared ");
}
(void) printf("%c ", CURL_END);
}
static void
print_pfpol_msg(spd_msg_t *msg)
{
spd_ext_t *exts[SPD_EXT_MAX+1];
spd_address_t *spd_address;
struct spd_rule *spd_rule;
struct spd_proto *spd_proto;
struct spd_portrange *spd_portrange;
struct spd_ext_actions *spd_ext_actions;
struct spd_typecode *spd_typecode;
struct spd_attribute *app;
uint32_t rv;
uint16_t act_count;
rv = spdsock_get_ext(exts, msg, msg->spd_msg_len, spdsock_diag_buf,
SPDSOCK_DIAG_BUF_LEN);
if (rv == KGE_OK && exts[SPD_EXT_RULE] != NULL) {
spd_rule = (struct spd_rule *)exts[SPD_EXT_RULE];
(void) printf("%s %lld\n", INDEX_TAG, spd_rule->spd_rule_index);
} else {
if (strlen(spdsock_diag_buf) != 0)
warnx(spdsock_diag_buf);
warnx(gettext("print_pfpol_msg: malformed PF_POLICY message."));
return;
}
(void) printf("%c ", CURL_BEGIN);
if (exts[SPD_EXT_PROTO] != NULL) {
spd_proto = (struct spd_proto *)exts[SPD_EXT_PROTO];
print_ulp(spd_proto->spd_proto_number);
}
if (exts[SPD_EXT_LCLADDR] != NULL) {
spd_address = (spd_address_t *)exts[SPD_EXT_LCLADDR];
(void) printf("laddr ");
if (spd_address->spd_address_len == 2)
print_address2((spd_address+1), 1);
else
print_address2((spd_address+1), 0);
(void) printf("/%d ", spd_address->spd_address_prefixlen);
}
if (exts[SPD_EXT_LCLPORT] != NULL) {
spd_portrange = (struct spd_portrange *)exts[SPD_EXT_LCLPORT];
if (spd_portrange->spd_ports_minport != 0) {
print_port(spd_portrange->spd_ports_minport,
TOK_lport);
}
}
if (exts[SPD_EXT_REMADDR] != NULL) {
spd_address = (spd_address_t *)exts[SPD_EXT_REMADDR];
(void) printf("raddr ");
if (spd_address->spd_address_len == 2)
print_address2((spd_address+1), 1);
else
print_address2((spd_address+1), 0);
(void) printf("/%d ", spd_address->spd_address_prefixlen);
}
if (exts[SPD_EXT_REMPORT] != NULL) {
spd_portrange =
(struct spd_portrange *)exts[SPD_EXT_REMPORT];
if (spd_portrange->spd_ports_minport != 0) {
print_port(
spd_portrange->spd_ports_minport,
TOK_rport);
}
}
if (exts[SPD_EXT_ICMP_TYPECODE] != NULL) {
spd_typecode =
(struct spd_typecode *)exts[SPD_EXT_ICMP_TYPECODE];
print_icmp_typecode(spd_typecode->spd_typecode_type,
spd_typecode->spd_typecode_type_end,
spd_typecode->spd_typecode_code,
spd_typecode->spd_typecode_code_end);
}
if (exts[SPD_EXT_RULE] != NULL) {
spd_rule = (struct spd_rule *)exts[SPD_EXT_RULE];
print_spd_flags(spd_rule->spd_rule_flags);
}
(void) printf("%c ", CURL_END);
if (exts[SPD_EXT_ACTION] != NULL) {
ips_act_props_t iap;
int or_needed = 0;
(void) memset(&iap, 0, sizeof (iap));
spd_ext_actions =
(struct spd_ext_actions *)exts[SPD_EXT_ACTION];
app = (struct spd_attribute *)(spd_ext_actions + 1);
for (act_count = 0;
act_count < spd_ext_actions->spd_actions_len -1;
act_count++) {
switch (app->spd_attr_tag) {
case SPD_ATTR_NOP:
break;
case SPD_ATTR_END:
/* print */
if (or_needed) {
(void) printf("or ");
} else {
or_needed = 1;
}
print_iap(&iap);
break;
case SPD_ATTR_EMPTY:
/* clear */
(void) memset(&iap, 0, sizeof (iap));
break;
case SPD_ATTR_NEXT:
/* print */
if (or_needed) {
(void) printf("or ");
} else {
or_needed = 1;
}
print_iap(&iap);
break;
case SPD_ATTR_TYPE:
iap.iap_action = app->spd_attr_value;
break;
case SPD_ATTR_FLAGS:
iap.iap_attr = app->spd_attr_value;
break;
case SPD_ATTR_AH_AUTH:
iap.iap_aauth.alg_id = app->spd_attr_value;
break;
case SPD_ATTR_ESP_ENCR:
iap.iap_eencr.alg_id = app->spd_attr_value;
break;
case SPD_ATTR_ESP_AUTH:
iap.iap_eauth.alg_id = app->spd_attr_value;
break;
case SPD_ATTR_ENCR_MINBITS:
iap.iap_eencr.alg_minbits = app->spd_attr_value;
break;
case SPD_ATTR_ENCR_MAXBITS:
iap.iap_eencr.alg_maxbits = app->spd_attr_value;
break;
case SPD_ATTR_AH_MINBITS:
iap.iap_aauth.alg_minbits = app->spd_attr_value;
break;
case SPD_ATTR_AH_MAXBITS:
iap.iap_aauth.alg_maxbits = app->spd_attr_value;
break;
case SPD_ATTR_ESPA_MINBITS:
iap.iap_eauth.alg_minbits = app->spd_attr_value;
break;
case SPD_ATTR_ESPA_MAXBITS:
iap.iap_eauth.alg_maxbits = app->spd_attr_value;
break;
case SPD_ATTR_LIFE_SOFT_TIME:
case SPD_ATTR_LIFE_HARD_TIME:
case SPD_ATTR_LIFE_SOFT_BYTES:
case SPD_ATTR_LIFE_HARD_BYTES:
default:
(void) printf("\tattr %d: %X-%d\n",
act_count,
app->spd_attr_tag,
app->spd_attr_value);
break;
}
app++;
}
}
(void) printf("\n");
}
#ifdef DEBUG_HEAVY
static void
pfpol_msg_dump(spd_msg_t *msg, char *tag)
{
spd_ext_t *exts[SPD_EXT_MAX+1];
uint32_t i;
spd_address_t *spd_address;
struct spd_rule *spd_rule;
struct spd_proto *spd_proto;
struct spd_portrange *spd_portrange;
struct spd_typecode *spd_typecode;
struct spd_ext_actions *spd_ext_actions;
struct spd_attribute *app;
char abuf[INET6_ADDRSTRLEN];
uint32_t rv;
uint16_t act_count;
rv = spdsock_get_ext(exts, msg, msg->spd_msg_len, NULL, 0);
if (rv != KGE_OK)
return;
(void) printf("===========%s==============\n", tag);
(void) printf("pfpol_msg_dump %d\n-------------------\n", rv);
(void) printf("spd_msg_version:%d\n", msg->spd_msg_version);
(void) printf("spd_msg_type:%d\n", msg->spd_msg_type);
(void) printf("spd_msg_errno:%d\n", msg->spd_msg_errno);
(void) printf("spd_msg_spdid:%d\n", msg->spd_msg_spdid);
(void) printf("spd_msg_len:%d\n", msg->spd_msg_len);
(void) printf("spd_msg_diagnostic:%d\n", msg->spd_msg_diagnostic);
(void) printf("spd_msg_seq:%d\n", msg->spd_msg_seq);
(void) printf("spd_msg_pid:%d\n", msg->spd_msg_pid);
for (i = 1; i <= SPD_EXT_MAX; i++) {
if (exts[i] == NULL) {
printf("skipped %d\n", i);
continue;
}
switch (i) {
case SPD_EXT_ICMP_TYPECODE:
spd_typecode = (struct spd_typecode *)exts[i];
(void) printf("icmp type %d-%d code %d-%d\n",
spd_typecode->spd_typecode_type,
spd_typecode->spd_typecode_type_end,
spd_typecode->spd_typecode_code,
spd_typecode->spd_typecode_code_end);
break;
case SPD_EXT_LCLPORT:
spd_portrange = (struct spd_portrange *)exts[i];
(void) printf("local ports %d-%d\n",
spd_portrange->spd_ports_minport,
spd_portrange->spd_ports_maxport);
break;
case SPD_EXT_REMPORT:
spd_portrange = (struct spd_portrange *)exts[i];
(void) printf("remote ports %d-%d\n",
spd_portrange->spd_ports_minport,
spd_portrange->spd_ports_maxport);
break;
case SPD_EXT_PROTO:
spd_proto = (struct spd_proto *)exts[i];
(void) printf("proto:spd_proto_exttype %d\n",
spd_proto->spd_proto_exttype);
(void) printf("proto:spd_proto_number %d\n",
spd_proto->spd_proto_number);
break;
case SPD_EXT_LCLADDR:
case SPD_EXT_REMADDR:
spd_address = (spd_address_t *)exts[i];
if (i == SPD_EXT_LCLADDR)
(void) printf("local addr ");
else
(void) printf("remote addr ");
(void) printf("%s\n",
inet_ntop(spd_address->spd_address_af,
(void *) (spd_address +1), abuf,
INET6_ADDRSTRLEN));
(void) printf("prefixlen: %d\n",
spd_address->spd_address_prefixlen);
break;
case SPD_EXT_ACTION:
spd_ext_actions = (struct spd_ext_actions *)exts[i];
(void) printf("spd_ext_action\n");
(void) printf("spd_actions_count %d\n",
spd_ext_actions->spd_actions_count);
app = (struct spd_attribute *)(spd_ext_actions + 1);
for (act_count = 0;
act_count < spd_ext_actions->spd_actions_len -1;
act_count++) {
(void) printf("\tattr %d: %X-%d\n", act_count,
app->spd_attr_tag, app->spd_attr_value);
app++;
}
break;
case SPD_EXT_RULE:
spd_rule = (struct spd_rule *)exts[i];
(void) printf("spd_rule_priority: 0x%x\n",
spd_rule->spd_rule_priority);
(void) printf("spd_rule_flags: %d\n",
spd_rule->spd_rule_flags);
break;
case SPD_EXT_RULESET:
(void) printf("spd_ext_ruleset\n");
break;
default:
(void) printf("default\n");
break;
}
}
(void) printf("-------------------\n");
(void) printf("=========================\n");
}
#endif /* DEBUG_HEAVY */
static int
ipsec_conf_view()
{
char buf[MAXLEN];
FILE *fp;
fp = fopen(POLICY_CONF_FILE, "r");
if (fp == NULL) {
if (errno == ENOENT) {
/*
* The absence of POLICY_CONF_FILE should
* not cause the command to exit with a
* non-zero status, since this condition
* is valid when no policies were previously
* defined.
*/
return (0);
}
warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
return (-1);
}
while (fgets(buf, MAXLEN, fp) != NULL) {
/* Don't print removed entries */
if (*buf == ';')
continue;
if (strlen(buf) != 0)
buf[strlen(buf) - 1] = '\0';
(void) puts(buf);
}
return (0);
}
/*
* Delete nlines from start in the POLICY_CONF_FILE.
*/
static int
delete_from_file(int start, int nlines)
{
FILE *fp;
char ibuf[MAXLEN];
int len;
if ((fp = fopen(POLICY_CONF_FILE, "r+b")) == NULL) {
warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
return (-1);
}
/*
* Insert a ";", read the line and discard it. Repeat
* this logic nlines - 1 times. For the last line there
* is just a newline character. We can't just insert a
* single ";" character instead of the newline character
* as it would affect the next line. Thus when we comment
* the last line we seek one less and insert a ";"
* character, which will replace the newline of the
* penultimate line with ; and newline of the last line
* will become part of the previous line.
*/
do {
/*
* It is not enough to seek just once and expect the
* subsequent fgets below to take you to the right
* offset of the next line. fgets below seems to affect
* the offset. Thus we need to seek, replace with ";",
* and discard a line using fgets for every line.
*/
if (fseek(fp, start, SEEK_SET) == -1) {
warn("fseek");
return (-1);
}
if (fputc(';', fp) < 0) {
warn("fputc");
return (-1);
}
/*
* Flush the above ";" character before we do the fgets().
* Without this, fgets() gets confused with offsets.
*/
(void) fflush(fp);
len = 0;
while (fgets(ibuf, MAXLEN, fp) != NULL) {
len += strlen(ibuf);
if (ibuf[len - 1] == '\n') {
/*
* We have read a complete line.
*/
break;
}
}
/*
* We read the line after ";" character has been inserted.
* Thus len does not count ";". To advance to the next line
* increment by 1.
*/
start += (len + 1);
/*
* If nlines == 2, we will be commenting out the last
* line next, which has only one newline character.
* If we blindly replace it with ";", it will be
* read as part of the next line which could have
* a INDEX string and thus confusing ipsec_conf_view.
* Thus, we seek one less and replace the previous
* line's newline character with ";", and the
* last line's newline character will become part of
* the previous line.
*/
if (nlines == 2)
start--;
} while (--nlines != 0);
(void) fclose(fp);
if (nlines != 0)
return (-1);
else
return (0);
}
/*
* Delete an entry from the file by inserting a ";" at the
* beginning of the lines to be removed.
*/
static int
ipsec_conf_del(int policy_index, boolean_t ignore_spd)
{
act_prop_t *act_props = malloc(sizeof (act_prop_t));
char *buf;
FILE *fp;
char ibuf[MAXLEN];
int ibuf_len, index_len, index;
int ret = 0;
int offset, prev_offset;
int nlines;
if (act_props == NULL) {
warn(gettext("memory"));
return (-1);
}
fp = fopen(POLICY_CONF_FILE, "r");
if (fp == NULL) {
warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
free(act_props);
return (-1);
}
index_len = strlen(INDEX_TAG);
index = 0;
for (offset = prev_offset = 0; fgets(ibuf, MAXLEN, fp) != NULL;
offset += ibuf_len) {
prev_offset = offset;
ibuf_len = strlen(ibuf);
if (strncmp(ibuf, INDEX_TAG, index_len) != 0) {
continue;
}
/*
* This line contains INDEX_TAG
*/
buf = ibuf + index_len;
buf++; /* Skip the space */
index = parse_int(buf);
if (index == -1) {
warnx(gettext("Invalid index in the file"));
free(act_props);
return (-1);
}
if (index == policy_index) {
if (!ignore_spd) {
ret = parse_one(fp, act_props);
if (ret == -1) {
warnx(gettext("Invalid policy entry "
"in the file"));
free(act_props);
return (-1);
}
}
/*
* nlines is the number of lines we should comment
* out. linecount tells us how many lines this command
* spans. And we need to remove the line with INDEX
* and an extra line we added during ipsec_conf_add.
*
* NOTE : If somebody added a policy entry which does
* not have a newline, ipsec_conf_add() fills in the
* newline. Hence, there is always 2 extra lines
* to delete.
*/
nlines = linecount + 2;
goto delete;
}
}
if (!ignore_spd)
ret = pfp_delete_rule(policy_index);
if (ret != 0) {
warnx(gettext("Deletion incomplete. Please "
"flush all the entries and re-configure :"));
reconfigure();
free(act_props);
return (ret);
}
free(act_props);
return (ret);
delete:
/* Delete nlines from prev_offset */
(void) fclose(fp);
ret = delete_from_file(prev_offset, nlines);
if (ret != 0) {
warnx(gettext("Deletion incomplete. Please "
"flush all the entries and re-configure :"));
reconfigure();
free(act_props);
return (ret);
}
if (!ignore_spd)
ret = pfp_delete_rule(policy_index);
if (ret != 0) {
warnx(gettext("Deletion incomplete. Please "
"flush all the entries and re-configure :"));
reconfigure();
free(act_props);
return (ret);
}
free(act_props);
return (0);
}
static int
pfp_delete_rule(uint64_t index)
{
struct spd_msg *msg;
struct spd_rule *rule;
int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
int cnt;
if (sfd < 0) {
warn(gettext("unable to open policy socket"));
return (-1);
}
msg = (spd_msg_t *)malloc(sizeof (spd_msg_t)
+ sizeof (struct spd_rule));
if (msg == NULL) {
warn("malloc");
return (-1);
}
rule = (struct spd_rule *)(msg + 1);
(void) memset(msg, 0, sizeof (spd_msg_t) + sizeof (struct spd_rule));
msg->spd_msg_version = PF_POLICY_V1;
msg->spd_msg_type = SPD_DELETERULE;
msg->spd_msg_len = SPD_8TO64(sizeof (spd_msg_t)
+ sizeof (struct spd_rule));
rule->spd_rule_type = SPD_EXT_RULE;
rule->spd_rule_len = SPD_8TO64(sizeof (struct spd_rule));
rule->spd_rule_index = index;
cnt = write(sfd, msg,
sizeof (spd_msg_t) + sizeof (struct spd_rule));
if (cnt != sizeof (spd_msg_t) + sizeof (struct spd_rule)) {
if (cnt < 0) {
(void) close(sfd);
free(msg);
warn(gettext("Delete failed: write"));
return (-1);
} else {
(void) close(sfd);
free(msg);
warnx(gettext("Delete failed: short write"));
return (-1);
}
}
cnt = read(sfd, msg,
sizeof (spd_msg_t) + sizeof (struct spd_rule));
if (cnt != sizeof (spd_msg_t) + sizeof (struct spd_rule)) {
if (cnt < 0) {
(void) close(sfd);
free(msg);
warn(gettext("Delete failed: read"));
return (-1);
} else {
(void) close(sfd);
free(msg);
warnx(gettext("Delete failed while reading reply"));
return (-1);
}
}
(void) close(sfd);
if (msg->spd_msg_errno != 0) {
free(msg);
errno = msg->spd_msg_errno;
warn(gettext("Delete failed: SPD_FLUSH"));
return (-1);
}
free(msg);
return (0);
}
static int
ipsec_conf_flush(int db)
{
int pfd, cnt;
int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
struct spd_msg msg;
if (sfd < 0) {
warn(gettext("unable to open policy socket"));
return (-1);
}
(void) memset(&msg, 0, sizeof (msg));
msg.spd_msg_version = PF_POLICY_V1;
msg.spd_msg_type = SPD_FLUSH;
msg.spd_msg_len = SPD_8TO64(sizeof (msg));
msg.spd_msg_spdid = db;
cnt = write(sfd, &msg, sizeof (msg));
if (cnt != sizeof (msg)) {
if (cnt < 0) {
warn(gettext("Flush failed: write"));
return (-1);
} else {
warnx(gettext("Flush failed: short write"));
return (-1);
}
}
cnt = read(sfd, &msg, sizeof (msg));
if (cnt != sizeof (msg)) {
if (cnt < 0) {
warn(gettext("Flush failed: read"));
return (-1);
} else {
warnx(gettext("Flush failed while reading reply"));
return (-1);
}
}
(void) close(sfd);
if (msg.spd_msg_errno != 0) {
warnx("%s: %s", gettext("Flush failed: SPD_FLUSH"),
strerror(msg.spd_msg_errno));
return (-1);
}
/* Truncate the file */
if (db == SPD_ACTIVE) {
if ((pfd = open(POLICY_CONF_FILE, O_TRUNC|O_RDWR)) == -1) {
if (errno == ENOENT) {
/*
* The absence of POLICY_CONF_FILE should
* not cause the command to exit with a
* non-zero status, since this condition
* is valid when no policies were previously
* defined.
*/
return (0);
}
warn(gettext("%s cannot be truncated"),
POLICY_CONF_FILE);
return (-1);
}
(void) close(pfd);
}
return (0);
}
/* function to send SPD_FLIP and SPD_CLONE messages */
static void
ipsec_conf_admin(uint8_t type)
{
int cnt;
int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
struct spd_msg msg;
if (sfd < 0) {
err(-1, gettext("unable to open policy socket"));
}
(void) memset(&msg, 0, sizeof (msg));
msg.spd_msg_version = PF_POLICY_V1;
msg.spd_msg_type = type;
msg.spd_msg_len = SPD_8TO64(sizeof (msg));
cnt = write(sfd, &msg, sizeof (msg));
if (cnt != sizeof (msg)) {
if (cnt < 0) {
err(-1, gettext("admin failed: write"));
} else {
errx(-1, gettext("admin failed: short write"));
}
}
cnt = read(sfd, &msg, sizeof (msg));
if (cnt != sizeof (msg)) {
if (cnt < 0) {
err(-1, gettext("admin failed: read"));
} else {
errx(-1, gettext("admin failed while reading reply"));
}
}
(void) close(sfd);
if (msg.spd_msg_errno != 0) {
errno = msg.spd_msg_errno;
err(-1, gettext("admin failed"));
}
}
static void
reconfigure()
{
(void) fprintf(stderr, gettext(
"\tipsecconf -f \n "
"\tipsecconf -a policy_file\n"));
}
static void
usage(void)
{
(void) fprintf(stderr, gettext(
"Usage: ipsecconf\n"
"\tipsecconf -a ([-]|<filename>) [-q]\n"
"\tipsecconf -r ([-]|<filename>) [-q]\n"
"\tipsecconf -d <index>\n"
"\tipsecconf -l [-n]\n"
"\tipsecconf -f\n"));
}
/*
* a type consists of
* "type" <int>{ "-" <int>}
* or
* "type" keyword
*
* a code consists of
* "code" <int>{ "-" <int>}
* or
* "code" keyword
*/
static int
parse_type_code(const char *str, const str_val_t *table)
{
char *end1, *end2;
int res1 = 0, res2 = 0;
int i;
if (isdigit(str[0])) {
res1 = strtol(str, &end1, 0);
if (end1 == str) {
return (-1);
}
if (res1 > 255 || res1 < 0) {
return (-1);
}
if (*end1 == '-') {
end1++;
res2 = strtol(end1, &end2, 0);
if (res2 > 255 || res2 < 0) {
return (-1);
}
} else {
end2 = end1;
}
while (isspace(*end2))
end2++;
if (*end2 != '\0') {
return (-1);
}
return (res1 + (res2 << 8));
}
for (i = 0; table[i].string; i++) {
if (strcmp(str, table[i].string) == 0) {
return (table[i].value);
}
}
return (-1);
}
static int
parse_int(const char *str)
{
char *end;
int res;
res = strtol(str, &end, 0);
if (end == str)
return (-1);
while (isspace(*end))
end++;
if (*end != '\0')
return (-1);
return (res);
}
/*
* Convert a mask to a prefix length.
* Returns prefix length on success, 0 otherwise.
*/
static unsigned int
in_getprefixlen(char *mask)
{
long prefixlen;
char *end;
prefixlen = strtol(mask, &end, 10);
if (prefixlen < 0) {
return (0);
}
if (mask == end) {
return (0);
}
if (*end != '\0') {
return (0);
}
return ((unsigned int)prefixlen);
}
/*
* Convert an IPv6 mask to a prefix len. I assume all IPv6 masks are
* contiguous, so I stop at the first bit!
*/
static int
in_masktoprefix(uint8_t *mask, boolean_t is_v4mapped)
{
int rc = 0;
uint8_t last;
int limit = IPV6_ABITS;
if (is_v4mapped) {
mask += ((IPV6_ABITS - IP_ABITS)/8);
limit = IP_ABITS;
}
while (*mask == 0xff) {
rc += 8;
if (rc == limit)
return (limit);
mask++;
}
last = *mask;
while (last != 0) {
rc++;
last = (last << 1) & 0xff;
}
return (rc);
}
/*
* Convert a prefix length to a mask.
* Assumes the mask array is zero'ed by the caller.
*/
static void
in_prefixlentomask(unsigned int prefixlen, uchar_t *mask)
{
while (prefixlen > 0) {
if (prefixlen >= 8) {
*mask++ = 0xFF;
prefixlen -= 8;
continue;
}
*mask |= 1 << (8 - prefixlen);
prefixlen--;
}
}
static int
parse_address(int type, char *addr_str)
{
char *ptr;
unsigned int prefix_len = 0;
struct netent *ne = NULL;
struct hostent *hp = NULL;
int h_errno;
struct in_addr netaddr;
struct in6_addr *netaddr6;
struct hostent *ne_hent;
boolean_t has_mask = B_FALSE;
ptr = strchr(addr_str, '/');
if (ptr != NULL) {
has_mask = B_TRUE;
*ptr++ = NULL;
prefix_len = in_getprefixlen(ptr);
if (prefix_len == 0)
return (-1);
}
/*
* getipnodebyname() is thread safe. This allows us to hold on to the
* returned hostent structure, which is pointed to by the shp and
* dhp globals for the source and destination addresses, respectively.
*/
hp = getipnodebyname(addr_str, AF_INET6, AI_DEFAULT | AI_ALL, &h_errno);
if (hp != NULL) {
/*
* We come here for both a hostname and
* any host address /network address.
*/
assert(hp->h_addrtype == AF_INET6);
} else if ((ne = getnetbyname(addr_str)) != NULL) {
switch (ne->n_addrtype) {
case AF_INET:
/*
* Allocate a struct hostent and initialize
* it with the address corresponding to the
* network number previously returned by
* getnetbyname(). Freed by do_address_adds()
* once the policy is defined.
*/
ne_hent = malloc(sizeof (struct hostent));
if (ne_hent == NULL) {
warn("malloc");
return (-1);
}
ne_hent->h_addr_list = malloc(2*sizeof (char *));
if (ne_hent->h_addr_list == NULL) {
warn("malloc");
free(ne_hent);
return (-1);
}
netaddr6 = malloc(sizeof (struct in6_addr));
if (netaddr6 == NULL) {
warn("malloc");
free(ne_hent->h_addr_list);
free(ne_hent);
return (-1);
}
ne_hent->h_addr_list[0] = (char *)netaddr6;
ne_hent->h_addr_list[1] = NULL;
netaddr = inet_makeaddr(ne->n_net, INADDR_ANY);
IN6_INADDR_TO_V4MAPPED(&netaddr, netaddr6);
hp = ne_hent;
break;
default:
warnx("Address type %d not supported.", ne->n_addrtype);
return (-1);
}
} else {
return (-1);
}
if (type == IPSEC_CONF_SRC_ADDRESS) {
shp = hp;
if (has_mask)
splen = prefix_len;
has_saprefix = has_mask;
} else {
dhp = hp;
if (has_mask)
dplen = prefix_len;
has_daprefix = has_mask;
}
return (0);
}
/*
* Add port-only entries. Make sure to add them in both the V6 and V4 tables!
*/
static int
do_port_adds(ips_conf_t *cptr)
{
int ret;
assert(IN6_IS_ADDR_UNSPECIFIED(&cptr->ips_src_addr_v6));
assert(IN6_IS_ADDR_UNSPECIFIED(&cptr->ips_dst_addr_v6));
#ifdef DEBUG_HEAVY
(void) dump_conf(cptr);
#endif
ret = send_pf_pol_message(SPD_ADDRULE, cptr);
if (ret != 0 && !ipsecconf_qflag) {
warnx(
gettext("Could not add IPv4 policy for sport %d, dport %d"),
ntohs(cptr->ips_src_port_min),
ntohs(cptr->ips_dst_port_min));
}
return (ret);
}
/*
* Nuke a list of policy entries.
* rewrite this to use flipping
* d_list isn't freed because we will be
* exiting the program soon.
*/
static void
nuke_adds()
{
d_list_t *temp = d_list;
FILE *policy_fp;
policy_fp = fopen(POLICY_CONF_FILE, "a");
if (policy_fp == NULL) {
warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
}
(void) fprintf(policy_fp, "\n\n");
(void) fflush(policy_fp);
while (temp != NULL) {
(void) ipsec_conf_del(temp->index, B_TRUE);
temp = temp->next;
}
}
/*
* Set mask info from the specified prefix len. Fail if multihomed.
*/
static int
set_mask_info(struct hostent *hp, unsigned int plen, struct in6_addr *mask_v6)
{
struct in6_addr addr;
struct in_addr mask_v4;
if (hp->h_addr_list[1] != NULL) {
return (EINVAL);
}
if (!IN6_IS_ADDR_UNSPECIFIED(mask_v6)) {
return (EBUSY);
}
bcopy(hp->h_addr_list[0], &addr, sizeof (struct in6_addr));
if (IN6_IS_ADDR_V4MAPPED(&addr)) {
if (plen > IP_ABITS) {
return (ERANGE);
}
(void) memset(&mask_v4, 0, sizeof (mask_v4));
in_prefixlentomask(plen, (uchar_t *)&mask_v4);
IN6_INADDR_TO_V4MAPPED(&mask_v4, mask_v6);
} else {
if (plen > IPV6_ABITS) {
return (ERANGE);
}
/* mask_v6 is already zero (unspecified), see test above */
in_prefixlentomask(plen, (uchar_t *)mask_v6);
}
return (0);
}
/*
* Initialize the specified IPv6 address with all f's.
*/
static void
init_addr_wildcard(struct in6_addr *addr_v6, boolean_t isv4)
{
if (isv4) {
uint32_t addr_v4 = 0xffffffff;
IN6_INADDR_TO_V4MAPPED((struct in_addr *)&addr_v4, addr_v6);
} else {
(void) memset(addr_v6, 0xff, sizeof (struct in6_addr));
}
}
/*
* Called at the end to actually add policy. Handles single and multi-homed
* cases.
*/
static int
do_address_adds(ips_conf_t *cptr)
{
int i, j;
int ret = 0; /* For ioctl() call. */
int rc = 0; /* My own return code. */
struct in6_addr zeroes = {0, 0, 0, 0};
char *ptr[2];
struct hostent hent;
boolean_t isv4;
int add_count = 0;
/*
* dst_hent may not be initialized if a destination
* address was not given. It will be initalized with just
* one address if a destination address was given. In both
* the cases, we initialize here with ipsc_dst_addr and enter
* the loop below.
*/
if (dhp == NULL) {
assert(shp != NULL);
hent.h_addr_list = ptr;
ptr[0] = (char *)&zeroes.s6_addr;
ptr[1] = NULL;
dhp = &hent;
} else if (shp == NULL) {
assert(dhp != NULL);
hent.h_addr_list = ptr;
ptr[0] = (char *)&zeroes.s6_addr;
ptr[1] = NULL;
shp = &hent;
}
/*
* Set mask info here. Bail if multihomed and there's a prefix len.
*/
if (has_saprefix) {
rc = set_mask_info(shp, splen, &cptr->ips_src_mask_v6);
if (rc != 0)
goto bail;
cptr->ips_src_mask_len = splen;
}
if (has_daprefix) {
rc = set_mask_info(dhp, dplen, &cptr->ips_dst_mask_v6);
if (rc != 0)
goto bail;
cptr->ips_dst_mask_len = dplen;
}
for (i = 0; shp->h_addr_list[i] != NULL; i++) {
bcopy(shp->h_addr_list[i], &cptr->ips_src_addr_v6,
sizeof (struct in6_addr));
isv4 = cptr->ips_isv4 =
IN6_IS_ADDR_V4MAPPED(&cptr->ips_src_addr_v6);
if (IN6_IS_ADDR_UNSPECIFIED(&cptr->ips_src_mask_v6) &&
shp != &hent) {
init_addr_wildcard(&cptr->ips_src_mask_v6, isv4);
}
for (j = 0; dhp->h_addr_list[j] != NULL; j++) {
bcopy(dhp->h_addr_list[j], &cptr->ips_dst_addr_v6,
sizeof (struct in6_addr));
if (IN6_IS_ADDR_UNSPECIFIED(&cptr->ips_src_addr_v6)) {
/*
* Src was not specified, so update isv4 flag
* for this policy according to the family
* of the destination address.
*/
isv4 = cptr->ips_isv4 =
IN6_IS_ADDR_V4MAPPED(
&cptr->ips_dst_addr_v6);
} else if ((dhp != &hent) && (isv4 !=
IN6_IS_ADDR_V4MAPPED(&cptr->ips_dst_addr_v6))) {
/* v6/v4 mismatch. */
continue;
}
if (IN6_IS_ADDR_UNSPECIFIED(&cptr->ips_dst_mask_v6) &&
dhp != &hent) {
init_addr_wildcard(&cptr->ips_dst_mask_v6,
isv4);
}
ret = send_pf_pol_message(SPD_ADDRULE, cptr);
if (ret == 0) {
add_count++;
} else {
/* For now, allow duplicate/overlap policies. */
if (ret != EEXIST) {
/*
* We have an error where we added
* some, but had errors with others.
* Undo the previous adds, and
* bail.
*/
rc = ret;
goto bail;
}
}
bzero(&cptr->ips_dst_mask_v6,
sizeof (struct in6_addr));
}
bzero(&cptr->ips_src_mask_v6, sizeof (struct in6_addr));
}
bail:
if (shp != &hent)
freehostent(shp);
shp = NULL;
if (dhp != &hent)
freehostent(dhp);
dhp = NULL;
splen = 0;
dplen = 0;
if ((add_count == 0) && (rc == 0)) {
/*
* No entries were added. We failed all adds
* because the entries already existed, or because
* no v4 or v6 src/dst pairs were found. Either way,
* we must fail here with an appropriate error
* to avoid a corresponding entry from being added
* to ipsecpolicy.conf.
*/
if ((ret == EEXIST)) {
/* All adds failed with EEXIST */
rc = EEXIST;
} else {
/* No matching v4 or v6 src/dst pairs */
rc = ESRCH;
}
}
return (rc);
}
static int
parse_mask(int type, char *mask_str, ips_conf_t *cptr)
{
struct in_addr mask;
struct in6_addr *mask6;
if (type == IPSEC_CONF_SRC_MASK) {
mask6 = &cptr->ips_src_mask_v6;
} else {
mask6 = &cptr->ips_dst_mask_v6;
}
if ((strncasecmp(mask_str, "0x", 2) == 0) &&
(strchr(mask_str, '.') == NULL)) {
/* Is it in the form 0xff000000 ? */
char *end;
mask.s_addr = strtoul(mask_str, &end, 0);
if (end == mask_str) {
return (-1);
}
if (*end != '\0') {
return (-1);
}
mask.s_addr = htonl(mask.s_addr);
} else {
/*
* Since inet_addr() returns -1 on error, we have
* to convert a broadcast address ourselves.
*/
if (strcmp(mask_str, "255.255.255.255") == 0) {
mask.s_addr = 0xffffffff;
} else {
mask.s_addr = inet_addr(mask_str);
if (mask.s_addr == (unsigned int)-1)
return (-1);
}
}
/* Should we check for non-contiguous masks ? */
if (mask.s_addr == 0)
return (-1);
IN6_INADDR_TO_V4MAPPED(&mask, mask6);
if (type == IPSEC_CONF_SRC_MASK) {
cptr->ips_src_mask_len = in_masktoprefix(mask6->s6_addr,
B_TRUE);
} else {
cptr->ips_dst_mask_len = in_masktoprefix(mask6->s6_addr,
B_TRUE);
}
return (0);
}
static int
parse_port(int type, char *port_str, ips_conf_t *conf)
{
struct servent *sent;
in_port_t port;
int ret;
sent = getservbyname(port_str, NULL);
if (sent == NULL) {
ret = parse_int(port_str);
if (ret < 0 || ret >= 65536) {
return (-1);
}
port = htons((in_port_t)ret);
} else {
port = sent->s_port;
}
if (type == IPSEC_CONF_SRC_PORT) {
conf->ips_src_port_min = conf->ips_src_port_max = port;
} else {
conf->ips_dst_port_min = conf->ips_dst_port_max = port;
}
return (0);
}
static int
valid_algorithm(int proto_num, const char *str)
{
const char *tmp;
int ret;
struct ipsecalgent *alg;
/* Short-circuit "none" */
if (strncasecmp("none", str, 5) == 0)
return (-2);
alg = getipsecalgbyname(str, proto_num, NULL);
if (alg != NULL) {
ret = alg->a_alg_num;
freeipsecalgent(alg);
return (ret);
}
/*
* Look whether it could be a valid number.
* We support numbers also so that users can
* load algorithms as they need it. We can't
* check for validity of numbers here. It will
* be checked when the SA is negotiated/looked up.
* parse_int uses strtol(str), which converts 3DES
* to a valid number i.e looks only at initial
* number part. If we come here we should expect
* only a decimal number.
*/
tmp = str;
while (*tmp) {
if (!isdigit(*tmp))
return (-1);
tmp++;
}
ret = parse_int(str);
if (ret > 0 && ret <= 255)
return (ret);
else
return (-1);
}
static int
parse_ipsec_alg(char *str, ips_act_props_t *iap, int alg_type)
{
int alg_value;
char tstr[VALID_ALG_LEN];
char *lens = NULL;
char *l1_str;
int l1 = 0;
char *l2_str;
int l2 = SPD_MAX_MAXBITS;
algreq_t *ap;
uint_t a_type;
fetch_algorithms();
/*
* Make sure that we get a null terminated string.
* For a bad input, we truncate at VALID_ALG_LEN.
*/
(void) strncpy(tstr, str, VALID_ALG_LEN - 1);
tstr[VALID_ALG_LEN - 1] = '\0';
lens = strtok(tstr, "()");
lens = strtok(NULL, "()");
if (lens != NULL) {
int len1 = 0;
int len2 = SPD_MAX_MAXBITS;
int len_all = strlen(lens);
int dot_start = (lens[0] == '.');
l1_str = strtok(lens, ".");
l2_str = strtok(NULL, ".");
if (l1_str != NULL) {
l1 = parse_int(l1_str);
len1 = strlen(l1_str);
if (len1 < 0)
return (1);
}
if (l2_str != NULL) {
l2 = parse_int(l2_str);
len2 = strlen(l2_str);
if (len2 < 0)
return (1);
}
if (len_all == len1) {
/* alg(n) */
l2 = l1;
} else if (dot_start) {
/* alg(..n) */
l2 = l1;
l1 = 0;
} else if ((len_all - 2) == len1) {
/* alg(n..) */
l2 = SPD_MAX_MAXBITS;
} /* else alg(n..m) */
}
if (alg_type == SPD_ATTR_AH_AUTH ||
alg_type == SPD_ATTR_ESP_AUTH) {
alg_value = valid_algorithm(IPSEC_PROTO_AH, tstr);
} else {
alg_value = valid_algorithm(IPSEC_PROTO_ESP, tstr);
}
if (alg_value < 0) {
/* Invalid algorithm or "none" */
return (alg_value);
}
if (alg_type == SPD_ATTR_AH_AUTH) {
a_type = AH_AUTH;
iap->iap_attr |= SPD_APPLY_AH;
ap = &(iap->iap_aauth);
} else if (alg_type == SPD_ATTR_ESP_AUTH) {
a_type = ESP_AUTH;
iap->iap_attr |= SPD_APPLY_ESP|SPD_APPLY_ESPA;
ap = &(iap->iap_eauth);
} else {
a_type = ESP_ENCR;
iap->iap_attr |= SPD_APPLY_ESP;
ap = &(iap->iap_eencr);
}
ap->alg_id = alg_value;
ap->alg_minbits = l1;
ap->alg_maxbits = l2;
if (!alg_rangecheck(a_type, alg_value, ap))
return (1);
return (0);
}
static void
error_message(error_type_t error, int type, int line)
{
char *mesg;
switch (type) {
case IPSEC_CONF_SRC_ADDRESS:
mesg = gettext("Source Address");
break;
case IPSEC_CONF_DST_ADDRESS:
mesg = gettext("Destination Address");
break;
case IPSEC_CONF_SRC_PORT:
mesg = gettext("Source Port");
break;
case IPSEC_CONF_DST_PORT:
mesg = gettext("Destination Port");
break;
case IPSEC_CONF_SRC_MASK:
mesg = gettext("Source Mask");
break;
case IPSEC_CONF_DST_MASK:
mesg = gettext("Destination Mask");
break;
case IPSEC_CONF_ULP:
mesg = gettext("Upper Layer Protocol");
break;
case IPSEC_CONF_IPSEC_AALGS:
mesg = gettext("Authentication Algorithm");
break;
case IPSEC_CONF_IPSEC_EALGS:
mesg = gettext("Encryption Algorithm");
break;
case IPSEC_CONF_IPSEC_EAALGS:
mesg = gettext("ESP Authentication Algorithm");
break;
case IPSEC_CONF_IPSEC_SA:
mesg = gettext("SA");
break;
case IPSEC_CONF_IPSEC_DIR:
mesg = gettext("Direction");
break;
case IPSEC_CONF_ICMP_TYPE:
mesg = gettext("ICMP type");
break;
case IPSEC_CONF_ICMP_CODE:
mesg = gettext("ICMP code");
break;
default :
return;
}
/*
* If we never read a newline character, we don't want
* to print 0.
*/
warnx(gettext("%s%s%s %s on line: %d"),
(error == BAD_ERROR) ? gettext("Bad") : "",
(error == DUP_ERROR) ? gettext("Duplicate") : "",
(error == REQ_ERROR) ? gettext("Requires") : "",
mesg,
(arg_indices[line] == 0) ? 1 : arg_indices[line]);
}
static int
validate_properties(ips_act_props_t *cptr, boolean_t dir, boolean_t is_alg)
{
if (cptr->iap_action == SPD_ACTTYPE_PASS ||
cptr->iap_action == SPD_ACTTYPE_DROP) {
if (!dir) {
warnx(gettext("dir string "
"not found for bypass policy"));
}
if (is_alg) {
warnx(gettext("Algorithms found for bypass policy"));
return (-1);
}
return (0);
}
if (!is_alg) {
warnx(gettext("No IPSEC algorithms given"));
return (-1);
}
if (cptr->iap_attr == 0) {
warnx(gettext("No SA attribute"));
return (-1);
}
return (0);
}
/*
* This function is called only to parse a single rule's worth of
* action strings. This is called after parsing pattern and before
* parsing properties. Thus we may have something in the leftover
* buffer while parsing the pattern, which we need to handle here.
*/
static int
parse_action(FILE *fp, char **action, char **leftover)
{
char *cp;
char ibuf[MAXLEN];
char *tmp_buf;
char *buf;
boolean_t new_stuff;
if (*leftover != NULL) {
buf = *leftover;
new_stuff = B_FALSE;
goto scan;
}
while (fgets(ibuf, MAXLEN, fp) != NULL) {
new_stuff = B_TRUE;
if (ibuf[strlen(ibuf) - 1] == '\n')
linecount++;
buf = ibuf;
scan:
/* Truncate at the beginning of a comment */
cp = strchr(buf, '#');
if (cp != NULL)
*cp = NULL;
/* Skip any whitespace */
while (*buf != NULL && isspace(*buf))
buf++;
/* Empty line */
if (*buf == NULL)
continue;
/*
* Store the command for error reporting
* and ipsec_conf_add().
*/
if (new_stuff) {
/*
* Check for buffer overflow including the null
* terminating character.
*/
int len = strlen(ibuf);
if ((cbuf_offset + len + 1) >= CBUF_LEN)
return (-1);
(void) strcpy(cbuf + cbuf_offset, ibuf);
cbuf_offset += len;
}
/*
* Start of the non-empty non-space character.
*/
tmp_buf = buf++;
/* Skip until next whitespace or CURL_BEGIN */
while (*buf != NULL && !isspace(*buf) &&
*buf != CURL_BEGIN)
buf++;
if (*buf != NULL) {
if (*buf == CURL_BEGIN) {
*buf = NULL;
/* Allocate an extra byte for the null also */
if ((*action = malloc(strlen(tmp_buf) + 1)) ==
NULL) {
warn("malloc");
return (ENOMEM);
}
(void) strcpy(*action, tmp_buf);
*buf = CURL_BEGIN;
} else {
/* We have hit a space */
*buf++ = NULL;
/* Allocate an extra byte for the null also */
if ((*action = malloc(strlen(tmp_buf) + 1)) ==
NULL) {
warn("malloc");
return (ENOMEM);
}
(void) strcpy(*action, tmp_buf);
}
/*
* Copy the rest of the line into the
* leftover buffer.
*/
if (*buf != NULL) {
(void) strcpy(lo_buf, buf);
*leftover = lo_buf;
} else {
*leftover = NULL;
}
} else {
/* Allocate an extra byte for the null also */
if ((*action = malloc(strlen(tmp_buf) + 1)) ==
NULL) {
warn("malloc");
return (ENOMEM);
}
(void) strcpy(*action, tmp_buf);
*leftover = NULL;
}
if (argindex >= ARG_BUF_LEN)
return (-1);
arg_indices[argindex++] = linecount;
return (PARSE_SUCCESS);
}
/*
* Return error, on an empty action field.
*/
return (-1);
}
/*
* This is called to parse pattern or properties that is enclosed
* between CURL_BEGIN and CURL_END.
*/
static int
parse_pattern_or_prop(FILE *fp, char *argvec[], char **leftover)
{
char *cp;
int i = 0;
boolean_t curl_begin_seen = B_FALSE;
char ibuf[MAXLEN];
char *tmp_buf;
char *buf;
boolean_t new_stuff;
/*
* When parsing properties, leftover buffer could have the
* leftovers of the previous fgets().
*/
if (*leftover != NULL) {
buf = *leftover;
new_stuff = B_FALSE;
goto scan;
}
while (fgets(ibuf, MAXLEN, fp) != NULL) {
new_stuff = B_TRUE;
#ifdef DEBUG_HEAVY
(void) printf("%s\n", ibuf);
#endif
if (ibuf[strlen(ibuf) - 1] == '\n')
linecount++;
buf = ibuf;
scan:
/* Truncate at the beginning of a comment */
cp = strchr(buf, '#');
if (cp != NULL)
*cp = NULL;
/* Skip any whitespace */
while (*buf != NULL && isspace(*buf))
buf++;
/* Empty line */
if (*buf == NULL)
continue;
/*
* Store the command for error reporting
* and ipsec_conf_add().
*/
if (new_stuff) {
/*
* Check for buffer overflow including the null
* terminating character.
*/
int len = strlen(ibuf);
if ((cbuf_offset + len + 1) >= CBUF_LEN)
return (-1);
(void) strcpy(cbuf + cbuf_offset, ibuf);
cbuf_offset += len;
}
/*
* First non-space character should be
* a curly bracket.
*/
if (!curl_begin_seen) {
if (*buf != CURL_BEGIN) {
/*
* If we never read a newline character,
* we don't want to print 0.
*/
warnx(gettext("Bad start on line %d :"),
(linecount == 0) ? 1 : linecount);
return (-1);
}
buf++;
curl_begin_seen = B_TRUE;
}
/*
* Arguments are separated by white spaces or
* newlines. Scan till you see a CURL_END.
*/
while (*buf != NULL) {
if (*buf == CURL_END) {
ret:
*buf++ = NULL;
/*
* Copy the rest of the line into the
* leftover buffer if any.
*/
if (*buf != NULL) {
(void) strcpy(lo_buf, buf);
*leftover = lo_buf;
} else {
*leftover = NULL;
}
return (PARSE_SUCCESS);
}
/*
* Skip any trailing whitespace until we see a
* non white-space character.
*/
while (*buf != NULL && isspace(*buf))
buf++;
if (*buf == CURL_END)
goto ret;
/* Scan the next line as this buffer is empty */
if (*buf == NULL)
break;
if (i >= MAXARGS) {
warnx(
gettext("Number of Arguments exceeded %d"),
i);
return (-1);
}
/*
* Non-empty, Non-space buffer.
*/
tmp_buf = buf++;
/*
* Real scan of the argument takes place here.
* Skip past till space or CURL_END.
*/
while (*buf != NULL && !isspace(*buf) &&
*buf != CURL_END) {
buf++;
}
/*
* Either a space or we have hit the CURL_END or
* the real end.
*/
if (*buf != NULL) {
if (*buf == CURL_END) {
*buf++ = NULL;
if ((argvec[i] = malloc(strlen(tmp_buf)
+ 1)) == NULL) {
warn("malloc");
return (ENOMEM);
}
/*
* Copy the rest of the line into the
* leftover buffer.
*/
if (*buf != NULL) {
(void) strcpy(lo_buf, buf);
*leftover = lo_buf;
} else {
*leftover = NULL;
}
if (strlen(tmp_buf) != 0) {
(void) strcpy(argvec[i],
tmp_buf);
if (argindex >= ARG_BUF_LEN)
return (-1);
arg_indices[argindex++] =
linecount;
}
return (PARSE_SUCCESS);
} else {
*buf++ = NULL;
}
}
/*
* Copy this argument and scan for the buffer more
* if it is non-empty. If it is empty scan for
* the next line.
*/
if ((argvec[i] = malloc(strlen(tmp_buf) + 1)) ==
NULL) {
warn("malloc");
return (ENOMEM);
}
(void) strcpy(argvec[i++], tmp_buf);
if (argindex >= ARG_BUF_LEN)
return (-1);
arg_indices[argindex++] = linecount;
}
}
/*
* If nothing is given in the file, it is okay.
* If something is given in the file and it is
* not CURL_BEGIN, we would have returned error
* above. If curl_begin_seen and we are here,
* something is wrong.
*/
if (curl_begin_seen)
return (-1);
return (PARSE_EOF); /* Nothing more in the file */
}
/*
* Parse one command i.e {pattern} action {properties}.
*
* {pattern} ( action {prop} | pass | drop ) (or ...)*
*/
static int
parse_one(FILE *fp, act_prop_t *act_props)
{
char *leftover;
int ret;
int i;
int ap_num = 0;
enum parse_state {pattern, action, prop } pstate;
has_daprefix = has_saprefix = B_FALSE;
(void) memset(act_props, 0, sizeof (act_prop_t));
pstate = pattern;
ret = 0;
leftover = NULL;
argindex = 0;
cbuf_offset = 0;
assert(shp == NULL && dhp == NULL);
for (;;) {
switch (pstate) {
case pattern:
{
#ifdef DEBUG_HEAVY
(void) printf("pattern\n");
#endif
ret = parse_pattern_or_prop(fp,
act_props->pattern, &leftover);
if (ret == PARSE_EOF) {
/* EOF reached */
return (0);
}
if (ret != 0) {
goto err;
}
pstate = action;
break;
}
case action:
{
#ifdef DEBUG_HEAVY
(void) printf("action\n");
#endif
ret = parse_action(fp,
&act_props->ap[ap_num].act, &leftover);
if (ret != 0) {
goto err;
}
/*
* Validate action now itself so that we don't
* proceed too much into the bad world.
*/
for (i = 0; action_table[i].string; i++) {
if (strcmp(act_props->ap[ap_num].act,
action_table[i].string) == 0)
break;
}
if (action_table[i].tok_val == TOK_or) {
/* hit an or, go again */
break;
}
if (action_table[i].string == NULL) {
/*
* If we never read a newline
* character, we don't want
* to print 0.
*/
warnx(gettext("(parse one)"
"Invalid action on line %d: %s"),
(linecount == 0) ? 1 : linecount,
act_props->ap[ap_num].act);
return (-1);
}
pstate = prop;
break;
}
case prop:
{
#ifdef DEBUG_HEAVY
(void) printf("prop\n");
#endif
ret = parse_pattern_or_prop(fp,
act_props->ap[ap_num].prop, &leftover);
if (ret != 0) {
goto err;
}
if (leftover != NULL) {
/* Accomodate spaces at the end */
while (*leftover != NULL) {
if (*leftover == 'o') {
leftover++;
if (*leftover == 'r') {
leftover++;
ap_num++;
if (ap_num > MAXARGS)
return (1);
pstate = action;
goto again;
}
}
if (!isspace(*leftover)) {
ret = -1;
goto err;
}
leftover++;
}
return (0);
}
ap_num++;
if (ap_num > MAXARGS)
return (0);
pstate = action; /* or */
break;
} /* case prop: */
} /* switch(pstate) */
again:
if (ap_num > MAXARGS)
return (0);
} /* while(1) */
err:
if (ret != 0) {
/*
* If we never read a newline character, we don't want
* to print 0.
*/
warnx(gettext("Error before or at line %d"),
(linecount == 0) ? 1 : linecount);
}
return (ret);
}
/*
* convert an act_propts_t to an ips_conf_t
*/
static int
form_ipsec_conf(act_prop_t *act_props, ips_conf_t *cptr)
{
int i, j, k;
int tok_count = 0;
struct protoent *pent;
boolean_t saddr, daddr, ipsec_aalg, ipsec_ealg, ipsec_eaalg, dir;
boolean_t old_style, new_style;
struct in_addr mask;
int line_no;
int ret;
int ap_num = 0;
int type, code, type_end, code_end;
#ifdef DEBUG_HEAVY
/*
* pattern => act_props->pattern
* action => act_props->ap[].act
* properties => act_props->ap[].prop
*/
(void) printf("\npattern\n------------\n");
for (i = 0; act_props->pattern[i] != NULL; i++)
(void) printf("%s\n", act_props->pattern[i]);
(void) printf("apz\n----------\n");
for (j = 0; act_props->ap[j].act != NULL; j++) {
(void) printf("act%d->%s\n", j, act_props->ap[j].act);
for (i = 0; act_props->ap[j].prop[i] != NULL; i++)
(void) printf("%dprop%d->%s\n",
j, i, act_props->ap[j].prop[i]);
}
(void) printf("------------\n\n");
#endif
(void) memset(cptr, 0, sizeof (ips_conf_t));
saddr = daddr = ipsec_aalg = ipsec_ealg = ipsec_eaalg = dir = B_FALSE;
old_style = new_style = B_FALSE;
/*
* Get the Pattern. NULL pattern is valid.
*/
for (i = 0, line_no = 0; act_props->pattern[i]; i++, line_no++) {
for (j = 0; pattern_table[j].string; j++) {
if (strcmp(act_props->pattern[i],
pattern_table[j].string) == 0)
break;
}
if (pattern_table[j].string == NULL) {
/*
* If we never read a newline character, we don't want
* to print 0.
*/
warnx(gettext("Invalid pattern on line %d: %s"),
(arg_indices[line_no] == 0) ? 1 :
arg_indices[line_no], act_props->pattern[i]);
return (-1);
}
cptr->patt_tok[tok_count++] = pattern_table[j].tok_val;
switch (pattern_table[j].tok_val) {
case TOK_dir:
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_DIR, line_no);
return (-1);
}
if (strncmp(act_props->pattern[i], "in", 2) == 0) {
cptr->ips_dir = SPD_RULE_FLAG_INBOUND;
} else if (strncmp(
act_props->pattern[i], "out", 3) == 0) {
cptr->ips_dir = SPD_RULE_FLAG_OUTBOUND;
} else if (strncmp(
act_props->pattern[i], "both", 4) == 0) {
if (old_style) {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_DIR, line_no);
return (-1);
}
new_style = B_TRUE;
cptr->ips_dir =
SPD_RULE_FLAG_OUTBOUND |
SPD_RULE_FLAG_INBOUND;
} else {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_DIR, line_no);
return (-1);
}
dir = B_TRUE;
break;
case TOK_local:
if (old_style) {
error_message(BAD_ERROR,
IPSEC_CONF_SRC_ADDRESS, line_no);
return (-1);
}
new_style = B_TRUE;
if (saddr) {
error_message(DUP_ERROR,
IPSEC_CONF_SRC_ADDRESS, line_no);
return (-1);
}
/*
* Use this to detect duplicates rather
* than 0 like other cases, because 0 for
* address means INADDR_ANY.
*/
saddr = B_TRUE;
cptr->has_saddr = 1;
/*
* Advance to the string containing
* the address.
*/
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR,
IPSEC_CONF_SRC_ADDRESS, line_no);
return (-1);
}
if (parse_address(IPSEC_CONF_SRC_ADDRESS,
act_props->pattern[i]) != 0) {
error_message(BAD_ERROR,
IPSEC_CONF_SRC_ADDRESS, line_no);
return (-1);
}
if (!cptr->has_smask)
cptr->has_smask = has_saprefix;
break;
case TOK_remote:
if (old_style) {
error_message(BAD_ERROR,
IPSEC_CONF_DST_ADDRESS, line_no);
return (-1);
}
new_style = B_TRUE;
if (daddr) {
error_message(DUP_ERROR,
IPSEC_CONF_DST_ADDRESS, line_no);
return (-1);
}
/*
* Use this to detect duplicates rather
* than 0 like other cases, because 0 for
* address means INADDR_ANY.
*/
daddr = B_TRUE;
cptr->has_daddr = 1;
/*
* Advance to the string containing
* the address.
*/
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR,
IPSEC_CONF_DST_ADDRESS, line_no);
return (-1);
}
if (parse_address(IPSEC_CONF_DST_ADDRESS,
act_props->pattern[i]) != 0) {
error_message(BAD_ERROR,
IPSEC_CONF_DST_ADDRESS, line_no);
return (-1);
}
if (!cptr->has_dmask)
cptr->has_dmask = has_daprefix;
break;
case TOK_saddr:
if (new_style) {
error_message(BAD_ERROR,
IPSEC_CONF_SRC_ADDRESS, line_no);
return (-1);
}
old_style = B_TRUE;
if (saddr) {
error_message(DUP_ERROR,
IPSEC_CONF_SRC_ADDRESS, line_no);
return (-1);
}
/*
* Use this to detect duplicates rather
* than 0 like other cases, because 0 for
* address means INADDR_ANY.
*/
saddr = B_TRUE;
cptr->has_saddr = 1;
/*
* Advance to the string containing
* the address.
*/
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR,
IPSEC_CONF_SRC_ADDRESS, line_no);
return (-1);
}
if (parse_address(IPSEC_CONF_SRC_ADDRESS,
act_props->pattern[i]) != 0) {
error_message(BAD_ERROR,
IPSEC_CONF_SRC_ADDRESS, line_no);
return (-1);
}
/* shp or bhp? */
if (!cptr->has_smask)
cptr->has_smask = has_saprefix;
break;
case TOK_daddr:
if (new_style) {
error_message(BAD_ERROR,
IPSEC_CONF_DST_ADDRESS, line_no);
return (-1);
}
old_style = B_TRUE;
if (daddr) {
error_message(DUP_ERROR,
IPSEC_CONF_DST_ADDRESS, line_no);
return (-1);
}
/*
* Use this to detect duplicates rather
* than 0 like other cases, because 0 for
* address means INADDR_ANY.
*/
daddr = B_TRUE;
cptr->has_daddr = 1;
/*
* Advance to the string containing
* the address.
*/
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR,
IPSEC_CONF_DST_ADDRESS, line_no);
return (-1);
}
if (parse_address(IPSEC_CONF_DST_ADDRESS,
act_props->pattern[i]) != 0) {
error_message(BAD_ERROR,
IPSEC_CONF_DST_ADDRESS, line_no);
return (-1);
}
if (!cptr->has_dmask)
cptr->has_dmask = has_daprefix;
break;
case TOK_sport:
if (new_style) {
error_message(BAD_ERROR,
IPSEC_CONF_SRC_PORT, line_no);
return (-1);
}
old_style = B_TRUE;
if (cptr->ips_src_port_min != 0) {
error_message(DUP_ERROR, IPSEC_CONF_SRC_PORT,
line_no);
return (-1);
}
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR, IPSEC_CONF_SRC_PORT,
line_no);
return (-1);
}
ret = parse_port(IPSEC_CONF_SRC_PORT,
act_props->pattern[i], cptr);
if (ret != 0) {
error_message(BAD_ERROR, IPSEC_CONF_SRC_PORT,
line_no);
return (-1);
}
break;
case TOK_dport:
if (new_style) {
error_message(BAD_ERROR,
IPSEC_CONF_DST_PORT, line_no);
return (-1);
}
old_style = B_TRUE;
if (cptr->ips_dst_port_min != 0) {
error_message(DUP_ERROR, IPSEC_CONF_DST_PORT,
line_no);
return (-1);
}
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR, IPSEC_CONF_DST_PORT,
line_no);
return (-1);
}
ret = parse_port(IPSEC_CONF_DST_PORT,
act_props->pattern[i],
cptr);
if (ret != 0) {
error_message(BAD_ERROR, IPSEC_CONF_DST_PORT,
line_no);
return (-1);
}
break;
case TOK_lport:
if (old_style) {
error_message(BAD_ERROR,
IPSEC_CONF_SRC_PORT, line_no);
return (-1);
}
new_style = B_TRUE;
if (cptr->ips_src_port_min != 0) {
error_message(DUP_ERROR, IPSEC_CONF_SRC_PORT,
line_no);
return (-1);
}
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR, IPSEC_CONF_SRC_PORT,
line_no);
return (-1);
}
ret = parse_port(IPSEC_CONF_SRC_PORT,
act_props->pattern[i],
cptr);
if (ret != 0) {
error_message(BAD_ERROR, IPSEC_CONF_SRC_PORT,
line_no);
return (-1);
}
break;
case TOK_rport:
if (old_style) {
error_message(BAD_ERROR,
IPSEC_CONF_DST_PORT, line_no);
return (-1);
}
new_style = B_TRUE;
if (cptr->ips_dst_port_min != 0) {
error_message(DUP_ERROR, IPSEC_CONF_DST_PORT,
line_no);
return (-1);
}
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR, IPSEC_CONF_DST_PORT,
line_no);
return (-1);
}
ret = parse_port(IPSEC_CONF_DST_PORT,
act_props->pattern[i],
cptr);
if (ret != 0) {
error_message(BAD_ERROR, IPSEC_CONF_DST_PORT,
line_no);
return (-1);
}
break;
case TOK_smask:
if (new_style) {
error_message(BAD_ERROR,
IPSEC_CONF_SRC_MASK, line_no);
return (-1);
}
old_style = B_TRUE;
cptr->has_smask = B_TRUE;
IN6_V4MAPPED_TO_INADDR(&cptr->ips_src_mask_v6, &mask);
if (mask.s_addr != 0) {
error_message(DUP_ERROR, IPSEC_CONF_SRC_MASK,
line_no);
return (-1);
}
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR, IPSEC_CONF_SRC_MASK,
line_no);
return (-1);
}
ret = parse_mask(IPSEC_CONF_SRC_MASK,
act_props->pattern[i],
cptr);
if (ret != 0) {
error_message(BAD_ERROR, IPSEC_CONF_SRC_MASK,
line_no);
return (-1);
}
break;
case TOK_dmask:
if (new_style) {
error_message(BAD_ERROR,
IPSEC_CONF_DST_MASK, line_no);
return (-1);
}
old_style = B_TRUE;
cptr->has_dmask = B_TRUE;
IN6_V4MAPPED_TO_INADDR(&cptr->ips_dst_mask_v6, &mask);
if (mask.s_addr != 0) {
error_message(DUP_ERROR, IPSEC_CONF_DST_MASK,
line_no);
return (-1);
}
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR, IPSEC_CONF_DST_MASK,
line_no);
return (-1);
}
ret = parse_mask(IPSEC_CONF_DST_MASK,
act_props->pattern[i],
cptr);
if (ret != 0) {
error_message(BAD_ERROR, IPSEC_CONF_DST_MASK,
line_no);
return (-1);
}
break;
case TOK_ulp:
if (cptr->ips_ulp_prot != 0) {
error_message(DUP_ERROR,
IPSEC_CONF_ULP, line_no);
return (-1);
}
i++, line_no++;
if (act_props->pattern[i] == NULL) {
error_message(BAD_ERROR,
IPSEC_CONF_ULP, line_no);
return (-1);
}
pent = getprotobyname(act_props->pattern[i]);
if (pent == NULL) {
int ulp;
ulp = parse_int(act_props->pattern[i]);
if (ulp == -1) {
error_message(BAD_ERROR,
IPSEC_CONF_ULP, line_no);
return (-1);
}
cptr->ips_ulp_prot = ulp;
} else {
cptr->ips_ulp_prot = pent->p_proto;
}
break;
case TOK_type:
if (cptr->has_type) {
error_message(DUP_ERROR,
IPSEC_CONF_ICMP_TYPE, line_no);
return (-1);
}
i++, line_no++;
type = parse_type_code(act_props->pattern[i],
icmp_type_table);
if (type > 65536 || type < 0) {
error_message(BAD_ERROR,
IPSEC_CONF_ICMP_TYPE, line_no);
return (-1);
}
type_end = type / 256;
type = type % 256;
if (type_end < type)
type_end = type;
cptr->has_type = 1;
cptr->ips_icmp_type = (uint8_t)type;
cptr->ips_icmp_type_end = (uint8_t)type_end;
break;
case TOK_code:
if (!cptr->has_type) {
error_message(BAD_ERROR,
IPSEC_CONF_ICMP_CODE, line_no);
return (-1);
}
if (cptr->has_code) {
error_message(DUP_ERROR,
IPSEC_CONF_ICMP_CODE, line_no);
return (-1);
}
i++, line_no++;
code = parse_type_code(act_props->pattern[i],
icmp_code_table);
if (type > 65536 || type < 0) {
error_message(BAD_ERROR,
IPSEC_CONF_ICMP_CODE, line_no);
return (-1);
}
code_end = code / 256;
code = code % 256;
if (code_end < code)
code_end = code;
cptr->has_code = 1;
cptr->ips_icmp_code = (uint8_t)code;
cptr->ips_icmp_code_end = (uint8_t)code_end;
break;
}
}
/*
* Get the actions.
*/
for (ap_num = 0; act_props->ap[ap_num].act != NULL; ap_num++) {
ips_act_props_t *iap;
if (ap_num > 0) {
/* or's only with new style */
if (old_style) {
(void) printf("%s\n", gettext(
"or's only with new style"));
return (-1);
}
new_style = B_TRUE;
}
ipsec_aalg = ipsec_ealg = ipsec_eaalg = B_FALSE;
tok_count = 0;
for (k = 0; action_table[k].string; k++) {
if (strcmp(act_props->ap[ap_num].act,
action_table[k].string) == 0)
break;
}
/*
* The following thing should never happen as
* we have already tested for its validity in parse.
*/
if (action_table[k].string == NULL) {
warnx(gettext("(form act)Invalid action on line "
"%d: %s"), (arg_indices[line_no] == 0) ? 1 :
arg_indices[line_no],
act_props->ap[ap_num].act);
warnx("%s", act_props->ap[ap_num].act);
return (-1);
}
/* we have a good action alloc an iap */
iap = alloc_iap(cptr);
iap->iap_action = action_table[k].value;
iap->iap_act_tok = action_table[k].tok_val;
switch (action_table[k].tok_val) {
case TOK_apply:
cptr->ips_dir = SPD_RULE_FLAG_OUTBOUND;
break;
case TOK_permit:
cptr->ips_dir = SPD_RULE_FLAG_INBOUND;
break;
case TOK_ipsec:
if (old_style) {
/* Using saddr/daddr with ipsec action. */
if (!dir) {
/* No direction specified */
error_message(REQ_ERROR,
IPSEC_CONF_IPSEC_DIR, line_no);
return (-1);
}
if (cptr->ips_dir == SPD_RULE_FLAG_INBOUND)
/*
* Need to swap addresses if
* 'dir in' or translation to
* laddr/raddr will be incorrect.
*/
cptr->swap = 1;
}
if (!dir)
cptr->ips_dir =
SPD_RULE_FLAG_INBOUND
|SPD_RULE_FLAG_OUTBOUND;
break;
case TOK_bypass:
/* do something? */
break;
}
line_no++;
/*
* Get the properties. NULL properties is not valid.
* Later checks will catch it.
*/
for (i = 0; act_props->ap[ap_num].prop[i]; i++, line_no++) {
for (j = 0; property_table[j].string; j++) {
if (strcmp(act_props->ap[ap_num].prop[i],
property_table[j].string) == 0) {
break;
}
}
if (property_table[j].string == NULL) {
warnx(gettext("Invalid properties on line "
"%d: %s"),
(arg_indices[line_no] == 0) ?
1 : arg_indices[line_no],
act_props->ap[ap_num].prop[i]);
return (-1);
}
iap->iap_attr_tok[tok_count++]
= property_table[j].value;
switch (property_table[j].value) {
case SPD_ATTR_AH_AUTH:
if (ipsec_aalg) {
error_message(DUP_ERROR,
IPSEC_CONF_IPSEC_AALGS, line_no);
return (-1);
}
i++, line_no++;
if (act_props->ap[ap_num].prop[i] == NULL) {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_AALGS, line_no);
return (-1);
}
ret = parse_ipsec_alg(
act_props->ap[ap_num].prop[i],
iap, SPD_ATTR_AH_AUTH);
if (ret == -2) {
/* "none" - ignore */
break;
}
if (ret != 0) {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_AALGS, line_no);
return (-1);
}
ipsec_aalg = B_TRUE;
break;
case SPD_ATTR_ESP_ENCR:
/*
* If this option was not given
* and encr_auth_algs was given,
* we provide null-encryption. We do the
* setting after we parse all the options.
*/
if (ipsec_ealg) {
error_message(DUP_ERROR,
IPSEC_CONF_IPSEC_EALGS, line_no);
return (-1);
}
i++, line_no++;
if (act_props->ap[ap_num].prop[i] == NULL) {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_EALGS, line_no);
return (-1);
}
ret = parse_ipsec_alg(
act_props->ap[ap_num].prop[i],
iap, SPD_ATTR_ESP_ENCR);
if (ret == -2) {
/* "none" - ignore */
break;
}
if (ret != 0) {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_EALGS, line_no);
return (-1);
}
ipsec_ealg = B_TRUE;
break;
case SPD_ATTR_ESP_AUTH:
/*
* If this option was not given and encr_algs
* option was given, we still pass a default
* value in ipsc_esp_auth_algs. This is to
* encourage the use of authentication with
* ESP.
*/
if (ipsec_eaalg) {
error_message(DUP_ERROR,
IPSEC_CONF_IPSEC_EAALGS, line_no);
return (-1);
}
i++, line_no++;
if (act_props->ap[ap_num].prop[i] == NULL) {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_EAALGS, line_no);
return (-1);
}
ret = parse_ipsec_alg(
act_props->ap[ap_num].prop[i],
iap, SPD_ATTR_ESP_AUTH);
if (ret == -2) {
/* "none" - ignore */
break;
}
if (ret != 0) {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_EAALGS, line_no);
return (-1);
}
ipsec_eaalg = B_TRUE;
break;
case IPS_SA:
i++, line_no++;
if (act_props->ap[ap_num].prop[i] == NULL) {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_SA, line_no);
return (-1);
}
if (strcmp(act_props->ap[ap_num].prop[i],
"unique") == 0) {
iap->iap_attr |= SPD_APPLY_UNIQUE;
} else if (strcmp(act_props->ap[ap_num].prop[i],
"shared") != 0) {
/* "shared" is default. */
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_SA, line_no);
return (-1);
}
break;
case IPS_DIR:
if (dir) {
error_message(DUP_ERROR,
IPSEC_CONF_IPSEC_DIR, line_no);
return (-1);
}
if (new_style) {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_DIR, line_no);
return (-1);
}
old_style = B_TRUE;
dir = B_TRUE;
i++, line_no++;
if (act_props->ap[ap_num].prop[i] == NULL) {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_DIR, line_no);
return (-1);
}
if (strcmp(act_props->ap[ap_num].prop[i],
"out") == 0) {
cptr->ips_dir = SPD_RULE_FLAG_OUTBOUND;
} else if (strcmp(act_props->ap[ap_num].prop[i],
"in") == 0) {
cptr->ips_dir = SPD_RULE_FLAG_INBOUND;
} else {
error_message(BAD_ERROR,
IPSEC_CONF_IPSEC_DIR, line_no);
return (-1);
}
if ((cptr->ips_dir & SPD_RULE_FLAG_INBOUND) &&
iap->iap_act_tok == TOK_apply) {
warnx(gettext("Direction"
" in conflict with action"));
return (-1);
}
if ((cptr->ips_dir & SPD_RULE_FLAG_OUTBOUND) &&
iap->iap_act_tok == TOK_permit) {
warnx(gettext("Direction"
"in conflict with action"));
return (-1);
}
break;
}
}
if (!ipsec_ealg && ipsec_eaalg) {
/*
* If the user has specified the auth alg to be used
* with encryption and did not provide a encryption
* algorithm, provide null encryption.
*/
iap->iap_eencr.alg_id = SADB_EALG_NULL;
ipsec_ealg = B_TRUE;
}
/* Set the level of IPSEC protection we want */
if (ipsec_aalg && (ipsec_ealg || ipsec_eaalg)) {
iap->iap_attr |= SPD_APPLY_AH|SPD_APPLY_ESP;
} else if (ipsec_aalg) {
iap->iap_attr |= SPD_APPLY_AH;
} else if (ipsec_ealg || ipsec_eaalg) {
iap->iap_attr |= SPD_APPLY_ESP;
}
/* convert src/dst to local/remote */
if (!new_style) {
switch (cptr->ips_acts->iap_act_tok) {
case TOK_apply:
/* outbound */
/* src=local, dst=remote */
/* this is ok. */
break;
case TOK_permit:
/* inbound */
/* src=remote, dst=local */
/* switch */
cptr->swap = 1;
break;
case TOK_bypass:
case TOK_drop:
/* check the direction for what to do */
if (cptr->ips_dir == SPD_RULE_FLAG_INBOUND)
cptr->swap = 1;
break;
default:
break;
}
}
/* Validate the properties */
if (ret = validate_properties(iap, dir,
(ipsec_aalg || ipsec_ealg || ipsec_eaalg))) {
return (ret);
}
}
return (0);
}
static int
print_cmd_buf(FILE *fp, int error)
{
*(cbuf + cbuf_offset) = '\0';
if (fp == stderr) {
if (ipsecconf_qflag) {
return (0);
}
if (error == EEXIST) {
warnx(gettext("Duplicate policy entry (ignored):\n%s"),
cbuf);
} else {
warnx(gettext("Malformed command (fatal):\n%s"),
cbuf);
}
} else {
if (fprintf(fp, "%s", cbuf) == -1) {
warn("fprintf");
return (-1);
}
}
return (0);
}
#ifdef DEBUG
static uchar_t *
addr_ptr(int isv4, struct in6_addr *addr6, struct in_addr *addr4)
{
if (isv4) {
IN6_V4MAPPED_TO_INADDR(addr6, addr4);
return ((uchar_t *)&addr4->s_addr);
} else {
return ((uchar_t *)&addr6->s6_addr);
}
}
static void
dump_algreq(const char *tag, algreq_t *alg)
{
(void) printf("%s algid %d, bits %d..%d\n",
tag, alg->alg_id, alg->alg_minbits, alg->alg_maxbits);
}
static void
dump_conf(ips_conf_t *conf)
{
boolean_t isv4 = conf->ips_isv4;
struct in_addr addr;
char buf[INET6_ADDRSTRLEN];
int af;
ips_act_props_t *iap = conf->ips_acts;
af = isv4 ? AF_INET : AF_INET6;
(void) printf("Source Addr is %s\n",
inet_ntop(af, addr_ptr(isv4, &conf->ips_src_addr_v6, &addr),
buf, INET6_ADDRSTRLEN));
(void) printf("Dest Addr is %s\n",
inet_ntop(af, addr_ptr(isv4, &conf->ips_dst_addr_v6, &addr),
buf, INET6_ADDRSTRLEN));
(void) printf("Source Mask is %s\n",
inet_ntop(af, addr_ptr(isv4, &conf->ips_src_mask_v6, &addr),
buf, INET6_ADDRSTRLEN));
(void) printf("Dest Mask is %s\n",
inet_ntop(af, addr_ptr(isv4, &conf->ips_dst_mask_v6, &addr),
buf, INET6_ADDRSTRLEN));
(void) printf("Source port %d\n", ntohs(conf->ips_src_port_min));
(void) printf("Dest port %d\n", ntohs(conf->ips_dst_port_min));
(void) printf("ULP %d\n", conf->ips_ulp_prot);
(void) printf("ICMP type %d-%d code %d-%d", conf->ips_icmp_type,
conf->ips_icmp_type_end,
conf->ips_icmp_code,
conf->ips_icmp_code_end);
while (iap != NULL) {
(void) printf("------------------------------------\n");
(void) printf("IPSec act is %d\n", iap->iap_action);
(void) printf("IPSec attr is %d\n", iap->iap_attr);
dump_algreq("AH authentication", &iap->iap_aauth);
dump_algreq("ESP authentication", &iap->iap_eauth);
dump_algreq("ESP encryption", &iap->iap_eencr);
(void) printf("------------------------------------\n");
iap = iap->iap_next;
}
fflush(stdout);
}
#endif /* DEBUG */
static int
ipsec_conf_add(void)
{
act_prop_t *act_props = malloc(sizeof (act_prop_t));
ips_conf_t conf;
FILE *fp, *policy_fp;
int ret, i, j;
char *warning = gettext(
"\tWARNING : New policy entries that are being added may\n "
"\taffect the existing connections. Existing connections\n"
"\tthat are not subjected to policy constraints, may be\n"
"\tsubjected to policy constraints because of the new\n"
"\tpolicy. This can disrupt the communication of the\n"
"\texisting connections.\n\n");
if (act_props == NULL) {
warn(gettext("memory"));
return (-1);
}
/* clone into standby DB */
ipsec_conf_admin(SPD_CLONE);
if (strcmp(filename, "-") == 0)
fp = stdin;
else
fp = fopen(filename, "r");
if (fp == NULL) {
warn(gettext("%s : Input file cannot be opened"), filename);
usage();
return (-1);
}
/*
* This will create the file if it does not exist.
* Make sure the umask is right.
*/
(void) umask(0022);
policy_fp = fopen(POLICY_CONF_FILE, "a");
if (policy_fp == NULL) {
warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
return (-1);
}
if (!ipsecconf_qflag) {
(void) printf("%s", warning);
}
/*
* Pattern, action, and properties are allocated in
* parse_pattern_or_prop and in parse_action (called by
* parse_one) as we parse arguments.
*/
while ((ret = parse_one(fp, act_props)) == 0) {
/*
* If there is no action and parse returned success,
* it means that there is nothing to add.
*/
if (act_props->pattern[0] == NULL &&
act_props->ap[0].act == NULL)
break;
ret = form_ipsec_conf(act_props, &conf);
if (ret != 0) {
warnx(gettext("form_ipsec_conf error"));
break;
}
/*
* shp, dhp, splen, and dplen are globals set by
* form_ipsec_conf() while parsing the addresses.
*/
if (shp == NULL && dhp == NULL) {
switch (do_port_adds(&conf)) {
case 0:
/* no error */
break;
case EEXIST:
/* duplicate entries, continue adds */
(void) print_cmd_buf(stderr, EEXIST);
goto next;
default:
/* other error, bail */
ret = -1;
goto bail;
}
} else {
ret = do_address_adds(&conf);
switch (ret) {
case 0:
/* no error. */
break;
case EEXIST:
(void) print_cmd_buf(stderr, EEXIST);
goto next;
case EBUSY:
warnx(gettext(
"Can't set mask and /NN prefix."));
ret = -1;
break;
case EINVAL:
warnx(gettext("Can't set /NN"
" prefix on multi-host name."));
ret = -1;
break;
case ERANGE:
warnx(gettext("/NN prefix is too big!"));
ret = -1;
break;
case ESRCH:
warnx(gettext("No matching IPv4 or "
"IPv6 saddr/daddr pairs"));
ret = -1;
break;
default:
/* Should never get here. */
errno = ret;
warn(gettext("Misc. error"));
ret = -1;
}
if (ret == -1)
goto bail;
}
/*
* Go ahead and add policy entries to config file.
* The # should help re-using the ipsecpolicy.conf
* for input again as # will be treated as comment.
*/
if (fprintf(policy_fp, "%s %lld \n", INDEX_TAG,
conf.ips_policy_index) == -1) {
warn("fprintf");
warnx(gettext("Addition incomplete, Please "
"flush all the entries and re-configure :"));
reconfigure();
ret = -1;
break;
}
if (print_cmd_buf(policy_fp, NOERROR) == -1) {
warnx(gettext("Addition incomplete. Please "
"flush all the entries and re-configure :"));
reconfigure();
ret = -1;
break;
}
/*
* We add one newline by default to separate out the
* entries. If the last character is not a newline, we
* insert a newline for free. This makes sure that all
* entries look consistent in the file.
*/
if (*(cbuf + cbuf_offset - 1) == '\n') {
if (fprintf(policy_fp, "\n") == -1) {
warn("fprintf");
warnx(gettext("Addition incomplete. "
"Please flush all the entries and "
"re-configure :"));
reconfigure();
ret = -1;
break;
}
} else {
if (fprintf(policy_fp, "\n\n") == -1) {
warn("fprintf");
warnx(gettext("Addition incomplete. "
"Please flush all the entries and "
"re-configure :"));
reconfigure();
ret = -1;
break;
}
}
/*
* Make sure this gets to the disk before
* we parse the next entry.
*/
(void) fflush(policy_fp);
next:
for (i = 0; act_props->pattern[i] != NULL; i++)
free(act_props->pattern[i]);
for (j = 0; act_props->ap[j].act != NULL; j++) {
free(act_props->ap[j].act);
for (i = 0; act_props->ap[j].prop[i] != NULL; i++)
free(act_props->ap[j].prop[i]);
}
}
bail:
if (ret == -1) {
(void) print_cmd_buf(stderr, EINVAL);
for (i = 0; act_props->pattern[i] != NULL; i++)
free(act_props->pattern[i]);
for (j = 0; act_props->ap[j].act != NULL; j++) {
free(act_props->ap[j].act);
for (i = 0; act_props->ap[j].prop[i] != NULL; i++)
free(act_props->ap[j].prop[i]);
}
}
#ifdef DEBUG_HEAVY
(void) printf("ipsec_conf_add: ret val = %d\n", ret);
fflush(stdout);
#endif
/* looks good, flip it in */
if (ret == 0)
ipsec_conf_admin(SPD_FLIP);
else
nuke_adds();
return (ret);
}
static int
ipsec_conf_sub()
{
act_prop_t *act_props = malloc(sizeof (act_prop_t));
FILE *remove_fp, *policy_fp;
char rbuf[MAXLEN], pbuf[MAXLEN], /* remove buffer, and policy buffer */
*warning = gettext(
"\tWARNING: Policy entries that are being removed may\n"
"\taffect the existing connections. Existing connections\n"
"\tthat are subjeced to policy constraints may no longer\n"
"\tbe subjected to policy contraints because of its\n"
"\tremoval. This can compromise security, and disrupt\n"
"\tthe communication of the existing connection.\n"
"\tConnections that are latched will remain unaffected\n"
"\tuntil they close.\n");
int ret = 0;
int index_len, pindex = 0; /* init value in case of pfile error */
if (act_props == NULL) {
warn(gettext("memory"));
return (-1);
}
/* clone into standby DB */
(void) ipsec_conf_admin(SPD_CLONE);
if (strcmp(filename, "-") == 0)
remove_fp = stdin;
else
remove_fp = fopen(filename, "r");
if (remove_fp == NULL) {
warn(gettext("%s : Input file cannot be opened"), filename);
usage();
free(act_props);
return (-1);
}
/* open policy file so we can locate the correct policy */
(void) umask(0022); /* in case it gets created! */
policy_fp = fopen(POLICY_CONF_FILE, "r+");
if (policy_fp == NULL) {
warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
(void) fclose(remove_fp);
free(act_props);
return (-1);
}
/* don't print the warning if we're in q[uiet] mode */
if (!ipsecconf_qflag)
(void) printf("%s", warning);
/* this bit is done primarily so we can read what we write */
index_len = strlen(INDEX_TAG);
/*
* We want to look for the policy in rbuf in the policy file.
* Go through the list of policies to remove, locating each one.
*/
while (fgets(rbuf, MAXLEN, remove_fp) != NULL) {
char *buf;
int offset, prev_offset, prev_prev_offset, nlines;
fpos_t ipos;
int pbuf_len = 0;
char *tmp;
/* skip blanks here (so we don't need to do it below)! */
for (tmp = rbuf; (*tmp != '\0') && isspace(*tmp); tmp++);
if (*tmp == '\0')
continue;
/* skip the INDEX_TAG lines in the remove buffer */
if (strncasecmp(rbuf, INDEX_TAG, index_len) == 0)
continue;
/* skip commented lines */
if (*tmp == '#')
continue;
/*
* We start by presuming only good policies are in the pfile,
* and so only good policies from the rfile will match them.
* ipsec_conf_del ensures this later by calling parse_one() on
* pfile before it deletes the entry.
*/
for (offset = prev_offset = prev_prev_offset = 0;
fgets(pbuf, MAXLEN, policy_fp) != NULL;
offset += pbuf_len) {
prev_offset = offset;
pbuf_len = strlen(pbuf);
/* skip blank lines which seperate policy entries */
if (pbuf[0] == '\n')
continue;
/* if we found an index, save it */
if (strncasecmp(pbuf, INDEX_TAG, index_len) == 0) {
buf = pbuf + index_len;
buf++;
if ((pindex = parse_int(buf)) == -1) {
/* bad index, we can't continue */
warnx(gettext(
"Invalid index in the file"));
(void) fclose(remove_fp);
(void) fclose(policy_fp);
free(act_props);
return (-1);
}
/* save this position in case it's the one */
if (fgetpos(policy_fp, &ipos) != 0) {
(void) fclose(remove_fp);
(void) fclose(policy_fp);
free(act_props);
return (-1);
}
}
/* Does pbuf contain the remove policy? */
if (strncasecmp(rbuf, pbuf, pbuf_len) == 0) {
/* we found the one to remove! */
if (pindex == 0) {
warnx(gettext("Didn't find a valid "
"index for policy"));
(void) fclose(remove_fp);
(void) fclose(policy_fp);
free(act_props);
return (-1);
}
/* off it - back up to the last INDEX! */
if (fsetpos(policy_fp, &ipos) != 0) {
(void) fclose(remove_fp);
(void) fclose(policy_fp);
free(act_props);
return (-1);
}
/* parse_one sets linecount = #lines to off */
if (parse_one(policy_fp, act_props) == -1) {
warnx(gettext("Invalid policy entry "
"in the file"));
(void) fclose(remove_fp);
(void) fclose(policy_fp);
free(act_props);
return (-1);
}
nlines = linecount + 2;
goto delete;
}
/*
* When we find a match, we want to pass the offset
* of the line that is before it - the INDEX_TAG line.
*/
prev_prev_offset = prev_offset;
}
/* Didn't find a match - look at the next remove policy */
continue;
delete:
(void) fclose(policy_fp);
if (delete_from_file(prev_prev_offset, nlines) != 0) {
warnx(gettext("delete_from_file failure. "
"Please flush all entries and re-configure :"));
reconfigure();
(void) fclose(remove_fp);
free(act_props);
return (-1);
}
if (pfp_delete_rule(pindex) != 0) {
warnx(gettext("Deletion incomplete. Please flush"
"all the entries and re-configure :"));
reconfigure();
(void) fclose(remove_fp);
free(act_props);
return (-1);
}
/* reset the globals */
linecount = 0;
pindex = 0;
/* reopen for next pass, automagically starting over. */
policy_fp = fopen(POLICY_CONF_FILE, "r");
if (policy_fp == NULL) {
warn(gettext("%s cannot be re-opened, can't continue"),
POLICY_CONF_FILE);
(void) fclose(remove_fp);
free(act_props);
return (-1);
}
} /* read next remove policy */
if ((ret = pfp_delete_rule(pindex)) != 0) {
warnx(gettext("Removal incomplete. Please flush "
"all the entries and re-configure :"));
reconfigure();
free(act_props);
return (ret);
}
/* nothing left to look for */
(void) fclose(remove_fp);
free(act_props);
return (0);
}