#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/sysmacros.h>
#include <sys/fcntl.h>
#include <net/pfkeyv2.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/uio.h>
#include <syslog.h>
#include <signal.h>
#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <netdb.h>
#include <pwd.h>
#include <errno.h>
#include <libintl.h>
#include <locale.h>
#include <fcntl.h>
#include <strings.h>
#include <ctype.h>
#include <sys/cladm.h>
#include <ipsec_util.h>
static int keysock;
static int cluster_socket;
static uint32_t seq;
static pid_t mypid;
static boolean_t vflag = B_FALSE;
static boolean_t cflag = B_FALSE;
static boolean_t tcpkey = B_FALSE;
const char *progname;
char *my_fmri = NULL;
FILE *debugfile = stdout;
static struct sockaddr_in cli_addr;
static boolean_t in_cluster_mode = B_FALSE;
#define MAX_GET_SIZE 1024
#define ERROR(x, y, z) x = record_error(x, y, z)
#define ERROR1(w, x, y, z) w = record_error(w, x, y, z)
#define ERROR2(v, w, x, y, z) v = record_error(v, w, x, y, z)
#define WARN(x, y, z) ERROR(x, y, z);\
handle_errors(x, NULL, B_FALSE, B_FALSE); x = NULL
#define WARN1(w, x, y, z) ERROR1(w, x, y, z);\
handle_errors(w, NULL, B_FALSE, B_FALSE); w = NULL
#define WARN2(v, w, x, y, z) ERROR2(v, w, x, y, z);\
handle_errors(v, NULL, B_FALSE, B_FALSE); v = NULL
#define FATAL(x, y, z) ERROR(x, y, z);\
handle_errors(x, y, B_TRUE, B_TRUE)
#define FATAL1(w, x, y, z) ERROR1(w, x, y, z);\
handle_errors(w, x, B_TRUE, B_TRUE)
#define QUOTE(x) #x
static uint64_t get_buffer[MAX_GET_SIZE];
static
CPL_MATCH_FN(no_match)
{
return (0);
}
char *
record_error(char *ep, char *ebuf, char *fmt, ...)
{
char *err_ptr;
char tmp_buff[1024];
va_list ap;
int length = 0;
err_ptr = ep;
va_start(ap, fmt);
length = vsnprintf(tmp_buff, sizeof (tmp_buff), fmt, ap);
va_end(ap);
length++;
if (ep == NULL) {
if (ebuf != NULL)
length += strlen(ebuf);
} else {
length += strlen(ep);
}
if (err_ptr == NULL)
err_ptr = calloc(length, sizeof (char));
else
err_ptr = realloc(err_ptr, length);
if (err_ptr == NULL)
Bail("realloc() failure");
if (ep == NULL && ebuf != NULL)
(void) strlcpy(err_ptr, ebuf, length);
(void) strlcat(err_ptr, tmp_buff, length);
return (err_ptr);
}
static void
usage(void)
{
if (!interactive) {
(void) fprintf(stderr, gettext("Usage:\t"
"%s [ -nvp ] | cmd [sa_type] [extfield value]*\n"),
progname);
(void) fprintf(stderr,
gettext("\t%s [ -nvp ] -f infile\n"), progname);
(void) fprintf(stderr,
gettext("\t%s [ -nvp ] -s outfile\n"), progname);
EXIT_FATAL(NULL);
} else {
(void) fprintf(stderr,
gettext("Type help or ? for usage info\n"));
}
}
void
handle_errors(char *ep, char *ebuf, boolean_t fatal, boolean_t done)
{
if (ep != NULL) {
if (my_fmri == NULL) {
(void) fprintf(stdout, "%s\n", ep);
(void) fflush(stdout);
}
free(ep);
if (fatal) {
if (ebuf != NULL) {
free(ebuf);
}
if (interactive)
longjmp(env, 1);
} else {
return;
}
} else {
if (done) {
if (ebuf != NULL) {
free(ebuf);
}
if (interactive)
longjmp(env, 1);
}
return;
}
EXIT_FATAL(NULL);
}
static void
msg_init(struct sadb_msg *msg, uint8_t type, uint8_t satype)
{
msg->sadb_msg_version = PF_KEY_V2;
msg->sadb_msg_type = type;
msg->sadb_msg_errno = 0;
msg->sadb_msg_satype = satype;
msg->sadb_msg_len = SADB_8TO64(sizeof (*msg));
msg->sadb_msg_reserved = 0;
msg->sadb_msg_seq = ++seq;
msg->sadb_msg_pid = mypid;
}
#define CMD_NONE 0
#define CMD_UPDATE 2
#define CMD_UPDATE_PAIR 3
#define CMD_ADD 4
#define CMD_DELETE 5
#define CMD_DELETE_PAIR 6
#define CMD_GET 7
#define CMD_FLUSH 9
#define CMD_DUMP 10
#define CMD_MONITOR 11
#define CMD_PMONITOR 12
#define CMD_QUIT 13
#define CMD_SAVE 14
#define CMD_HELP 15
struct cmdtable {
char *cmd;
int token;
};
static struct cmdtable default_cmdtable[] = {
{ "update", CMD_UPDATE },
{ "update-pair", CMD_UPDATE_PAIR },
{ "add", CMD_ADD },
{ "delete", CMD_DELETE },
{ "delete-pair", CMD_DELETE_PAIR },
{ "get", CMD_GET },
{ "flush", CMD_FLUSH },
{ "dump", CMD_DUMP },
{ "monitor", CMD_MONITOR },
{ "passive_monitor", CMD_PMONITOR },
{ "pmonitor", CMD_PMONITOR },
{ "quit", CMD_QUIT },
{ "exit", CMD_QUIT },
{ "save", CMD_SAVE },
{ "help", CMD_HELP },
{ "?", CMD_HELP },
{ NULL, CMD_NONE }
};
static struct cmdtable tcpkey_cmdtable[] = {
{ "update", CMD_UPDATE },
{ "add", CMD_ADD },
{ "delete", CMD_DELETE },
{ "get", CMD_GET },
{ "flush", CMD_FLUSH },
{ "dump", CMD_DUMP },
{ "quit", CMD_QUIT },
{ "exit", CMD_QUIT },
{ "save", CMD_SAVE },
{ "help", CMD_HELP },
{ "?", CMD_HELP },
{ NULL, CMD_NONE }
};
static int
parsecmd(char *cmdstr)
{
struct cmdtable *ct;
ct = tcpkey ? tcpkey_cmdtable : default_cmdtable;
while (ct->cmd != NULL && strcmp(ct->cmd, cmdstr) != 0)
ct++;
return (ct->token);
}
static u_longlong_t
parsenum(char *num, boolean_t bail, char *ebuf)
{
u_longlong_t rc = 0;
char *end = NULL;
char *ep = NULL;
if (num == NULL) {
FATAL(ep, ebuf, gettext("Unexpected end of command line,"
" was expecting a number.\n"));
}
errno = 0;
rc = strtoull(num, &end, 0);
if (errno != 0 || end == num || *end != '\0') {
if (bail) {
FATAL1(ep, ebuf, gettext(
"Expecting a number, not \"%s\"!\n"), num);
} else {
return ((u_longlong_t)-1);
}
}
handle_errors(ep, NULL, B_FALSE, B_FALSE);
return (rc);
}
static struct typetable {
char *type;
int token;
} type_table[] = {
{"all", SADB_SATYPE_UNSPEC},
{"ah", SADB_SATYPE_AH},
{"esp", SADB_SATYPE_ESP},
{"tcpsig", SADB_X_SATYPE_TCPSIG},
{NULL, 0}
};
static int
parsesatype(char *type, char *ebuf)
{
struct typetable *tt = type_table;
char *ep = NULL;
if (type == NULL)
return (SADB_SATYPE_UNSPEC);
while (tt->type != NULL && strcasecmp(tt->type, type) != 0)
tt++;
if (tt->type == NULL) {
tt->token = (int)parsenum(type, B_FALSE, ebuf);
if (tt->token == -1) {
ERROR1(ep, ebuf, gettext(
"Unknown SA type (%s).\n"), type);
tt->token = SADB_SATYPE_UNSPEC;
}
}
handle_errors(ep, NULL, interactive ? B_TRUE : B_FALSE, B_FALSE);
return (tt->token);
}
#define NEXTEOF 0
#define NEXTNONE 1
#define NEXTNUM 2
#define NEXTSTR 3
#define NEXTNUMSTR 4
#define NEXTADDR 5
#define NEXTHEX 6
#define NEXTIDENT 7
#define NEXTADDR4 8
#define NEXTADDR6 9
#define NEXTLABEL 10
#define TOK_EOF 0
#define TOK_UNKNOWN 1
#define TOK_SPI 2
#define TOK_REPLAY 3
#define TOK_STATE 4
#define TOK_AUTHALG 5
#define TOK_ENCRALG 6
#define TOK_FLAGS 7
#define TOK_SOFT_ALLOC 8
#define TOK_SOFT_BYTES 9
#define TOK_SOFT_ADDTIME 10
#define TOK_SOFT_USETIME 11
#define TOK_HARD_ALLOC 12
#define TOK_HARD_BYTES 13
#define TOK_HARD_ADDTIME 14
#define TOK_HARD_USETIME 15
#define TOK_CURRENT_ALLOC 16
#define TOK_CURRENT_BYTES 17
#define TOK_CURRENT_ADDTIME 18
#define TOK_CURRENT_USETIME 19
#define TOK_SRCADDR 20
#define TOK_DSTADDR 21
#define TOK_PROXYADDR 22
#define TOK_AUTHKEY 23
#define TOK_ENCRKEY 24
#define TOK_SRCIDTYPE 25
#define TOK_DSTIDTYPE 26
#define TOK_DPD 27
#define TOK_SENS_LEVEL 28
#define TOK_SENS_MAP 29
#define TOK_INTEG_LEVEL 30
#define TOK_INTEG_MAP 31
#define TOK_SRCADDR6 32
#define TOK_DSTADDR6 33
#define TOK_PROXYADDR6 34
#define TOK_SRCPORT 35
#define TOK_DSTPORT 36
#define TOK_PROTO 37
#define TOK_ENCAP 38
#define TOK_NATLOC 39
#define TOK_NATREM 40
#define TOK_NATLPORT 41
#define TOK_NATRPORT 42
#define TOK_IPROTO 43
#define TOK_IDSTADDR 44
#define TOK_IDSTADDR6 45
#define TOK_ISRCPORT 46
#define TOK_IDSTPORT 47
#define TOK_PAIR_SPI 48
#define TOK_FLAG_INBOUND 49
#define TOK_FLAG_OUTBOUND 50
#define TOK_REPLAY_VALUE 51
#define TOK_IDLE_ADDTIME 52
#define TOK_IDLE_USETIME 53
#define TOK_RESERVED 54
#define TOK_LABEL 55
#define TOK_OLABEL 56
#define TOK_IMPLABEL 57
#define TOK_AUTHSTR 58
struct toktable {
char *string;
int token;
int next;
};
static struct toktable tokens[] = {
{"spi", TOK_SPI, NEXTNUM},
{"pair-spi", TOK_PAIR_SPI, NEXTNUM},
{"replay", TOK_REPLAY, NEXTNUM},
{"state", TOK_STATE, NEXTNUMSTR},
{"auth_alg", TOK_AUTHALG, NEXTNUMSTR},
{"authalg", TOK_AUTHALG, NEXTNUMSTR},
{"encr_alg", TOK_ENCRALG, NEXTNUMSTR},
{"encralg", TOK_ENCRALG, NEXTNUMSTR},
{"flags", TOK_FLAGS, NEXTNUM},
{"soft_alloc", TOK_SOFT_ALLOC, NEXTNUM},
{"soft_bytes", TOK_SOFT_BYTES, NEXTNUM},
{"soft_addtime", TOK_SOFT_ADDTIME, NEXTNUM},
{"soft_usetime", TOK_SOFT_USETIME, NEXTNUM},
{"hard_alloc", TOK_HARD_ALLOC, NEXTNUM},
{"hard_bytes", TOK_HARD_BYTES, NEXTNUM},
{"hard_addtime", TOK_HARD_ADDTIME, NEXTNUM},
{"hard_usetime", TOK_HARD_USETIME, NEXTNUM},
{"current_alloc", TOK_CURRENT_ALLOC, NEXTNUM},
{"current_bytes", TOK_CURRENT_BYTES, NEXTNUM},
{"current_addtime", TOK_CURRENT_ADDTIME, NEXTNUM},
{"current_usetime", TOK_CURRENT_USETIME, NEXTNUM},
{"saddr", TOK_SRCADDR, NEXTADDR},
{"srcaddr", TOK_SRCADDR, NEXTADDR},
{"src", TOK_SRCADDR, NEXTADDR},
{"daddr", TOK_DSTADDR, NEXTADDR},
{"dstaddr", TOK_DSTADDR, NEXTADDR},
{"dst", TOK_DSTADDR, NEXTADDR},
{"proxyaddr", TOK_PROXYADDR, NEXTADDR},
{"proxy", TOK_PROXYADDR, NEXTADDR},
{"innersrc", TOK_PROXYADDR, NEXTADDR},
{"isrc", TOK_PROXYADDR, NEXTADDR},
{"innerdst", TOK_IDSTADDR, NEXTADDR},
{"idst", TOK_IDSTADDR, NEXTADDR},
{"sport", TOK_SRCPORT, NEXTNUM},
{"dport", TOK_DSTPORT, NEXTNUM},
{"innersport", TOK_ISRCPORT, NEXTNUM},
{"isport", TOK_ISRCPORT, NEXTNUM},
{"innerdport", TOK_IDSTPORT, NEXTNUM},
{"idport", TOK_IDSTPORT, NEXTNUM},
{"proto", TOK_PROTO, NEXTNUM},
{"ulp", TOK_PROTO, NEXTNUM},
{"iproto", TOK_IPROTO, NEXTNUM},
{"iulp", TOK_IPROTO, NEXTNUM},
{"saddr6", TOK_SRCADDR6, NEXTADDR},
{"srcaddr6", TOK_SRCADDR6, NEXTADDR},
{"src6", TOK_SRCADDR6, NEXTADDR},
{"daddr6", TOK_DSTADDR6, NEXTADDR},
{"dstaddr6", TOK_DSTADDR6, NEXTADDR},
{"dst6", TOK_DSTADDR6, NEXTADDR},
{"proxyaddr6", TOK_PROXYADDR6, NEXTADDR},
{"proxy6", TOK_PROXYADDR6, NEXTADDR},
{"innersrc6", TOK_PROXYADDR6, NEXTADDR},
{"isrc6", TOK_PROXYADDR6, NEXTADDR},
{"innerdst6", TOK_IDSTADDR6, NEXTADDR},
{"idst6", TOK_IDSTADDR6, NEXTADDR},
{"authkey", TOK_AUTHKEY, NEXTHEX},
{"authstring", TOK_AUTHSTR, NEXTNUMSTR},
{"encrkey", TOK_ENCRKEY, NEXTHEX},
{"srcidtype", TOK_SRCIDTYPE, NEXTIDENT},
{"dstidtype", TOK_DSTIDTYPE, NEXTIDENT},
{"dpd", TOK_DPD, NEXTNUM},
{"sens_level", TOK_SENS_LEVEL, NEXTNUM},
{"sens_map", TOK_SENS_MAP, NEXTHEX},
{"integ_level", TOK_INTEG_LEVEL, NEXTNUM},
{"integ_map", TOK_INTEG_MAP, NEXTHEX},
{"nat_loc", TOK_NATLOC, NEXTADDR},
{"nat_rem", TOK_NATREM, NEXTADDR},
{"nat_lport", TOK_NATLPORT, NEXTNUM},
{"nat_rport", TOK_NATRPORT, NEXTNUM},
{"encap", TOK_ENCAP, NEXTNUMSTR},
{"outbound", TOK_FLAG_OUTBOUND, 0},
{"inbound", TOK_FLAG_INBOUND, 0},
{"reserved_bits", TOK_RESERVED, NEXTNUM},
{"replay_value", TOK_REPLAY_VALUE, NEXTNUM},
{"idle_addtime", TOK_IDLE_ADDTIME, NEXTNUM},
{"idle_usetime", TOK_IDLE_USETIME, NEXTNUM},
{"label", TOK_LABEL, NEXTLABEL},
{"outer-label", TOK_OLABEL, NEXTLABEL},
{"implicit-label", TOK_IMPLABEL, NEXTLABEL},
{NULL, TOK_UNKNOWN, NEXTEOF}
};
static struct toktable tcpkey_tokens[] = {
{"src", TOK_SRCADDR, NEXTADDR},
{"src6", TOK_SRCADDR6, NEXTADDR},
{"dst", TOK_DSTADDR, NEXTADDR},
{"dst6", TOK_DSTADDR6, NEXTADDR},
{"sport", TOK_SRCPORT, NEXTNUM},
{"dport", TOK_DSTPORT, NEXTNUM},
{"authalg", TOK_AUTHALG, NEXTNUMSTR},
{"authstring", TOK_AUTHSTR, NEXTNUMSTR},
{"soft_addtime", TOK_SOFT_ADDTIME, NEXTNUM},
{"soft_usetime", TOK_SOFT_USETIME, NEXTNUM},
{"hard_addtime", TOK_HARD_ADDTIME, NEXTNUM},
{"hard_usetime", TOK_HARD_USETIME, NEXTNUM},
{NULL, TOK_UNKNOWN, NEXTEOF}
};
static int
parseextval(char *value, int *next)
{
struct toktable *tp;
if (value == NULL)
return (TOK_EOF);
tp = tcpkey ? tcpkey_tokens : tokens;
for (; tp->string != NULL; tp++)
if (strcmp(value, tp->string) == 0)
break;
*next = tp->next;
return (tp->token);
}
static uint8_t
parsestate(char *state, char *ebuf)
{
struct states {
char *state;
uint8_t retval;
} states[] = {
{"larval", SADB_SASTATE_LARVAL},
{"mature", SADB_SASTATE_MATURE},
{"dying", SADB_SASTATE_DYING},
{"dead", SADB_SASTATE_DEAD},
{NULL, 0}
};
struct states *sp;
char *ep = NULL;
if (state == NULL) {
FATAL(ep, ebuf, "Unexpected end of command line "
"was expecting a state.\n");
}
for (sp = states; sp->state != NULL; sp++) {
if (strcmp(sp->state, state) == 0)
return (sp->retval);
}
ERROR1(ep, ebuf, gettext("Unknown state type \"%s\"\n"), state);
handle_errors(ep, NULL, B_FALSE, B_FALSE);
return (0);
}
static uint8_t
parsealg(char *alg, int proto_num, char *ebuf)
{
u_longlong_t invalue;
struct ipsecalgent *algent;
char *ep = NULL;
if (alg == NULL) {
FATAL(ep, ebuf, gettext("Unexpected end of command line, "
"was expecting an algorithm name.\n"));
}
algent = getipsecalgbyname(alg, proto_num, NULL);
if (algent != NULL) {
uint8_t alg_num;
alg_num = algent->a_alg_num;
if (ALG_FLAG_COUNTERMODE & algent->a_alg_flags)
WARN1(ep, ebuf, gettext(
"Using manual keying with a Counter mode algorithm "
"such as \"%s\" may be insecure!\n"),
algent->a_names[0]);
freeipsecalgent(algent);
return (alg_num);
}
invalue = parsenum(alg, B_FALSE, ebuf);
if (invalue != (u_longlong_t)-1 &&
(u_longlong_t)(invalue & (u_longlong_t)0xff) == invalue)
return ((uint8_t)invalue);
if (proto_num == IPSEC_PROTO_ESP) {
ERROR1(ep, ebuf, gettext(
"Unknown encryption algorithm type \"%s\"\n"), alg);
} else {
ERROR1(ep, ebuf, gettext(
"Unknown authentication algorithm type \"%s\"\n"), alg);
}
handle_errors(ep, NULL, B_FALSE, B_FALSE);
return (0);
}
static uint8_t
parsetcpalg(char *alg, char *ebuf)
{
char *ep = NULL;
uint8_t algnum;
if (alg == NULL) {
FATAL(ep, ebuf, gettext("Unexpected end of command line, "
"was expecting an algorithm name.\n"));
}
algnum = gettcpsigalgbyname(alg);
if (algnum > 0)
return (algnum);
ERROR1(ep, ebuf,
gettext("Unknown tcpsig authentication algorithm type \"%s\"\n"),
alg);
handle_errors(ep, NULL, B_FALSE, B_FALSE);
return (0);
}
static struct idtypes {
char *idtype;
uint8_t retval;
} idtypes[] = {
{"prefix", SADB_IDENTTYPE_PREFIX},
{"fqdn", SADB_IDENTTYPE_FQDN},
{"domain", SADB_IDENTTYPE_FQDN},
{"domainname", SADB_IDENTTYPE_FQDN},
{"user_fqdn", SADB_IDENTTYPE_USER_FQDN},
{"mailbox", SADB_IDENTTYPE_USER_FQDN},
{"der_dn", SADB_X_IDENTTYPE_DN},
{"der_gn", SADB_X_IDENTTYPE_GN},
{NULL, 0}
};
static uint16_t
parseidtype(char *type, char *ebuf)
{
struct idtypes *idp;
u_longlong_t invalue;
char *ep = NULL;
if (type == NULL) {
FATAL(ep, ebuf, gettext("Unexpected end of command line, "
"was expecting a type.\n"));
}
for (idp = idtypes; idp->idtype != NULL; idp++) {
if (strcasecmp(idp->idtype, type) == 0)
return (idp->retval);
}
invalue = parsenum(type, B_FALSE, ebuf);
if (invalue != (u_longlong_t)-1 &&
(u_longlong_t)(invalue & (u_longlong_t)0xffff) == invalue)
return ((uint16_t)invalue);
ERROR1(ep, ebuf, gettext("Unknown identity type \"%s\"\n"), type);
handle_errors(ep, NULL, B_FALSE, B_FALSE);
return (0);
}
static struct {
struct hostent he;
char *addtl[2];
} dummy;
static union {
struct in6_addr ipv6;
struct in_addr ipv4;
uint64_t aligner;
} addr1;
static int
parseaddr(char *addr, struct hostent **hpp, boolean_t v6only, char *ebuf)
{
int hp_errno;
struct hostent *hp = NULL;
char *ep = NULL;
if (addr == NULL) {
FATAL(ep, ebuf, gettext("Unexpected end of command line, "
"was expecting an address.\n"));
}
if (!nflag) {
hp = getipnodebyname(addr, AF_INET6,
(v6only ? AI_ADDRCONFIG : (AI_DEFAULT | AI_ALL)),
&hp_errno);
} else {
if (inet_pton(AF_INET6, addr, &addr1) == 1) {
dummy.he.h_addr_list = dummy.addtl;
dummy.addtl[0] = (char *)&addr1;
dummy.addtl[1] = NULL;
hp = &dummy.he;
dummy.he.h_addrtype = AF_INET6;
dummy.he.h_length = sizeof (struct in6_addr);
} else if (inet_pton(AF_INET, addr, &addr1) == 1) {
dummy.he.h_addr_list = dummy.addtl;
dummy.addtl[0] = (char *)&addr1;
dummy.addtl[1] = NULL;
hp = &dummy.he;
dummy.he.h_addrtype = AF_INET6;
dummy.he.h_length = sizeof (struct in6_addr);
IN6_INADDR_TO_V4MAPPED(&addr1.ipv4, &addr1.ipv6);
} else {
hp = NULL;
}
}
if (hp == NULL)
WARN1(ep, ebuf, gettext("Unknown address %s."), addr);
*hpp = hp;
handle_errors(ep, NULL, B_FALSE, B_FALSE);
return (sizeof (struct sockaddr_in6));
}
#define hd2num(hd) (((hd) >= '0' && (hd) <= '9') ? ((hd) - '0') : \
(((hd) >= 'a' && (hd) <= 'f') ? ((hd) - 'a' + 10) : ((hd) - 'A' + 10)))
static struct sadb_key *
parsekey(char *input, char *ebuf, uint_t reserved_bits)
{
struct sadb_key *retval;
uint_t i, hexlen = 0, bits, alloclen;
uint8_t *key;
char *ep = NULL;
if (input == NULL) {
FATAL(ep, ebuf, gettext("Unexpected end of command line, "
"was expecting a key.\n"));
}
if ((strnlen(input, sizeof (hexlen)) > 2) &&
(strncasecmp(input, "0x", 2) == 0))
input += 2;
for (i = 0; input[i] != '\0' && input[i] != '/'; i++)
hexlen++;
if (input[i] == '\0') {
bits = 0;
} else {
input[i] = '\0';
if (sscanf((input + i + 1), "%u", &bits) != 1) {
FATAL1(ep, ebuf, gettext(
"\"%s\" is not a bit specifier.\n"),
(input + i + 1));
}
if (((bits + 3) >> 2) > hexlen) {
ERROR2(ep, ebuf, gettext(
"bit length %d is too big for %s.\n"), bits, input);
}
if ((hexlen << 2) > bits + 3) {
WARN2(ep, ebuf, gettext(
"WARNING: Lower bits will be truncated "
"for:\n\t%s/%d.\n"), input, bits);
hexlen = (bits + 3) >> 2;
input[hexlen] = '\0';
}
}
alloclen = sizeof (*retval) + roundup((hexlen/2 + (hexlen & 0x1)), 8);
retval = malloc(alloclen);
if (retval == NULL)
Bail("malloc(parsekey)");
retval->sadb_key_len = SADB_8TO64(alloclen);
retval->sadb_key_reserved = reserved_bits;
if (bits == 0)
retval->sadb_key_bits = (hexlen + (hexlen & 0x1)) << 2;
else
retval->sadb_key_bits = bits;
key = (uint8_t *)(retval + 1);
for (i = 0; input[i] != '\0'; i += 2) {
boolean_t second = (input[i + 1] != '\0');
if (!isxdigit(input[i]) ||
(!isxdigit(input[i + 1]) && second)) {
ERROR1(ep, ebuf, gettext(
"string '%s' not a hex value.\n"), input);
free(retval);
retval = NULL;
break;
}
*key = (hd2num(input[i]) << 4);
if (second)
*key |= hd2num(input[i + 1]);
else
break;
key++;
}
if (bits & 0x7)
*((input[i] == '\0') ? key - 1 : key) &=
0xff << (8 - (bits & 0x7));
handle_errors(ep, NULL, B_FALSE, B_FALSE);
return (retval);
}
static struct sadb_key *
parseauthstr(char *input, char *ebuf)
{
struct sadb_key *retval;
size_t alloclen, keylen;
char *ep = NULL;
if (input == NULL) {
FATAL(ep, ebuf, gettext("Unexpected end of command line, "
"was expecting an authentication string.\n"));
}
keylen = strlen(input);
if (keylen > TCPSIG_MD5_KEY_LEN) {
FATAL(ep, ebuf, gettext("Authentication string is too long, "
"must be < " QUOTE(TCPSIG_MD5_KEY_LEN) " characters.\n"));
}
alloclen = sizeof (*retval) + roundup(keylen, sizeof (uint64_t));
retval = malloc(alloclen);
if (retval == NULL)
Bail("malloc(parseauthstr)");
retval->sadb_key_bits = SADB_8TO1(keylen);
retval->sadb_key_len = SADB_8TO64(alloclen);
retval->sadb_key_reserved = 0;
bcopy(input, (void *)(retval + 1), keylen);
handle_errors(ep, NULL, B_FALSE, B_FALSE);
return (retval);
}
#include <tsol/label.h>
#define PARSELABEL_BAD_TOKEN ((struct sadb_sens *)-1)
static struct sadb_sens *
parselabel(int token, char *label)
{
bslabel_t *sl = NULL;
int err, len;
sadb_sens_t *sens;
int doi = 1;
err = str_to_label(label, &sl, MAC_LABEL, L_DEFAULT, NULL);
if (err < 0)
return (NULL);
len = ipsec_convert_sl_to_sens(doi, sl, NULL);
sens = malloc(len);
if (sens == NULL) {
Bail("malloc parsed label");
return (NULL);
}
(void) ipsec_convert_sl_to_sens(doi, sl, sens);
switch (token) {
case TOK_LABEL:
break;
case TOK_OLABEL:
sens->sadb_sens_exttype = SADB_X_EXT_OUTER_SENS;
break;
case TOK_IMPLABEL:
sens->sadb_sens_exttype = SADB_X_EXT_OUTER_SENS;
sens->sadb_x_sens_flags = SADB_X_SENS_IMPLICIT;
break;
default:
free(sens);
return (PARSELABEL_BAD_TOKEN);
}
return (sens);
}
static int
key_write(int fd, void *msg, size_t len)
{
if (vflag) {
(void) printf(
gettext("VERBOSE ON: Message to kernel looks like:\n"));
(void) printf("==========================================\n");
print_samsg(stdout, msg, B_FALSE, vflag, nflag);
(void) printf("==========================================\n");
}
return (write(fd, msg, len));
}
static void
time_critical_catch(int signal)
{
if (signal == SIGALRM) {
errx(1, gettext("Reply message from PF_KEY timed out."));
} else {
errx(1, gettext("Caught signal %d while trying to receive"
"PF_KEY reply message"), signal);
}
}
#define TIME_CRITICAL_TIME 10
static void
time_critical_enter(void)
{
(void) signal(SIGALRM, time_critical_catch);
(void) alarm(TIME_CRITICAL_TIME);
}
static void
time_critical_exit(void)
{
(void) alarm(0);
(void) signal(SIGALRM, SIG_DFL);
}
static void
doflush(int satype)
{
struct sadb_msg msg;
int rc;
msg_init(&msg, SADB_FLUSH, (uint8_t)satype);
rc = key_write(keysock, &msg, sizeof (msg));
if (rc == -1)
Bail("write() to PF_KEY socket failed (in doflush)");
time_critical_enter();
do {
rc = read(keysock, &msg, sizeof (msg));
if (rc == -1)
Bail("read (in doflush)");
} while (msg.sadb_msg_seq != seq || msg.sadb_msg_pid != mypid);
time_critical_exit();
if (msg.sadb_msg_type != SADB_FLUSH ||
msg.sadb_msg_satype != (uint8_t)satype) {
syslog((LOG_NOTICE|LOG_AUTH),
gettext("doflush: Return message not of type SADB_FLUSH!"));
Bail("doflush: Return message not of type SADB_FLUSH!");
}
if (msg.sadb_msg_errno != 0) {
errno = msg.sadb_msg_errno;
if (errno == EINVAL) {
print_diagnostic(stderr, msg.sadb_x_msg_diagnostic);
warnx(gettext("Cannot flush SA type %d."), satype);
}
Bail("return message (in doflush)");
}
}
static void
dodump(int satype, FILE *ofile)
{
struct sadb_msg *msg = (struct sadb_msg *)get_buffer;
int rc;
if (ofile != NULL) {
(void) fprintf(ofile,
gettext("# This key file was generated by the"));
(void) fprintf(ofile,
gettext(" %s(8) command's 'save' feature.\n\n"),
progname);
}
msg_init(msg, SADB_DUMP, (uint8_t)satype);
rc = key_write(keysock, msg, sizeof (*msg));
if (rc == -1)
Bail("write to PF_KEY socket failed (in dodump)");
do {
time_critical_enter();
rc = read(keysock, get_buffer, sizeof (get_buffer));
time_critical_exit();
if (rc == -1)
Bail("read (in dodump)");
if (msg->sadb_msg_pid == mypid &&
msg->sadb_msg_type == SADB_DUMP &&
msg->sadb_msg_seq != 0 &&
msg->sadb_msg_errno == 0) {
if (ofile == NULL) {
print_samsg(stdout, get_buffer, B_FALSE, vflag,
nflag);
(void) putchar('\n');
} else {
save_assoc(get_buffer, ofile);
}
}
} while (msg->sadb_msg_pid != mypid ||
(msg->sadb_msg_errno == 0 && msg->sadb_msg_seq != 0));
if (ofile != NULL && ofile != stdout)
(void) fclose(ofile);
if (msg->sadb_msg_errno == 0) {
if (ofile == NULL)
(void) printf(
gettext("Dump succeeded for SA type %d.\n"),
satype);
} else {
print_diagnostic(stderr, msg->sadb_x_msg_diagnostic);
errno = msg->sadb_msg_errno;
Bail("Dump failed");
}
}
#define SCOPE_UNSPEC 0
#define SCOPE_LINKLOCAL 1
#define SCOPE_SITELOCAL 2
#define SCOPE_GLOBAL 3
#define SCOPE_V4COMPAT 4
#define SCOPE_LOOPBACK 5
static int
ipv6_addr_scope(struct in6_addr *addr)
{
if (IN6_IS_ADDR_UNSPECIFIED(addr))
return (SCOPE_UNSPEC);
if (IN6_IS_ADDR_LINKLOCAL(addr))
return (SCOPE_LINKLOCAL);
if (IN6_IS_ADDR_SITELOCAL(addr))
return (SCOPE_SITELOCAL);
if (IN6_IS_ADDR_V4COMPAT(addr))
return (SCOPE_V4COMPAT);
if (IN6_IS_ADDR_LOOPBACK(addr))
return (SCOPE_LOOPBACK);
return (SCOPE_GLOBAL);
}
static void
doaddresses(uint8_t sadb_msg_type, uint8_t sadb_msg_satype, int cmd,
struct hostent *srchp, struct hostent *dsthp,
struct sadb_address *src, struct sadb_address *dst,
boolean_t unspec_src, uint64_t *buffer, int buffer_size, uint32_t spi,
char *ebuf)
{
boolean_t last_dst;
struct sockaddr_in6 *sin6;
struct sadb_msg *msgp;
int i, rc;
char **walker;
char *first_match;
uint64_t savebuf[MAX_GET_SIZE];
uint16_t srcport = 0, dstport = 0;
char *ep = NULL;
if (src != NULL) {
sin6 = (struct sockaddr_in6 *)(src + 1);
srcport = ntohs(sin6->sin6_port);
}
if (dst != NULL) {
sin6 = (struct sockaddr_in6 *)(dst + 1);
dstport = ntohs(sin6->sin6_port);
}
if (dsthp == NULL) {
i = 0;
do {
if (srchp == &dummy.he) {
srchp->h_addr_list[1] = NULL;
} else if (srchp != NULL) {
if (srchp->h_addr_list[i] == NULL)
Bail("Empty source address list");
sin6 = (struct sockaddr_in6 *)(src + 1);
bzero(sin6, sizeof (*sin6));
bcopy(srchp->h_addr_list[i], &sin6->sin6_addr,
sizeof (struct in6_addr));
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(srcport);
}
msgp = (struct sadb_msg *)buffer;
bcopy(buffer, savebuf, SADB_64TO8(msgp->sadb_msg_len));
rc = key_write(keysock, buffer,
SADB_64TO8(msgp->sadb_msg_len));
if (rc == -1)
Bail("write() to PF_KEY socket "
"(in doaddresses)");
if (in_cluster_mode) {
(void) sendto(cluster_socket, buffer,
SADB_64TO8(msgp->sadb_msg_len), 0,
(struct sockaddr *)&cli_addr,
sizeof (cli_addr));
}
time_critical_enter();
do {
rc = read(keysock, buffer, buffer_size);
if (rc == -1)
Bail("read (in doaddresses)");
} while (msgp->sadb_msg_seq != seq ||
msgp->sadb_msg_pid != mypid);
time_critical_exit();
if (msgp->sadb_msg_type != sadb_msg_type ||
msgp->sadb_msg_satype != sadb_msg_satype) {
syslog((LOG_NOTICE|LOG_AUTH), gettext(
"doaddresses: Unexpected returned message "
"(%d exp %d)\n"), msgp->sadb_msg_type,
sadb_msg_type);
Bail("doaddresses: Unexpected returned "
"message");
}
errno = msgp->sadb_msg_errno;
if (errno != 0) {
if (errno == EINVAL) {
WARN(ep, ebuf, gettext(
"One of the entered "
"values is incorrect."));
print_diagnostic(stderr,
msgp->sadb_x_msg_diagnostic);
} else {
Bail("return message (in doaddresses)");
}
}
msgp = (struct sadb_msg *)savebuf;
bcopy(savebuf, buffer, SADB_64TO8(msgp->sadb_msg_len));
} while (srchp != NULL && srchp->h_addr_list[++i] != NULL);
return;
}
for (i = 0; dsthp->h_addr_list[i] != NULL; i++) {
if (dsthp == &dummy.he) {
dsthp->h_addr_list[1] = NULL;
} else {
sin6 = (struct sockaddr_in6 *)(dst + 1);
bzero(sin6, sizeof (*sin6));
bcopy(dsthp->h_addr_list[i], &sin6->sin6_addr,
sizeof (struct in6_addr));
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(dstport);
}
last_dst = (dsthp->h_addr_list[i + 1] == NULL);
if (!unspec_src && srchp != &dummy.he) {
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
first_match = NULL;
for (walker = srchp->h_addr_list;
*walker != NULL; walker++) {
if (IN6_IS_ADDR_V4MAPPED(
(struct in6_addr *)*walker)) {
if (first_match != NULL)
break;
else
first_match = *walker;
}
}
sin6 = (struct sockaddr_in6 *)(src + 1);
bzero(sin6, sizeof (*sin6));
if (first_match == NULL) {
ERROR1(ep, ebuf, gettext(
"No IPv4 source address "
"for name %s.\n"), srchp->h_name);
if (last_dst) {
FATAL(ep, ebuf, gettext(
"No match for destination "
"IP address.\n"));
} else {
continue;
}
}
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(srcport);
if (*walker != NULL) {
WARN1(ep, ebuf, gettext(
"Multiple IPv4 source addresses "
"for %s, using unspecified source "
"instead."), srchp->h_name);
} else {
bcopy(first_match, &sin6->sin6_addr,
sizeof (struct in6_addr));
}
} else {
int dst_scope, src_scope;
dst_scope = ipv6_addr_scope(&sin6->sin6_addr);
first_match = NULL;
for (walker = srchp->h_addr_list;
*walker != NULL; walker++) {
if (!IN6_IS_ADDR_V4MAPPED(
(struct in6_addr *)*walker)) {
src_scope = ipv6_addr_scope(
(struct in6_addr *)*walker);
if (src_scope == SCOPE_UNSPEC ||
src_scope == dst_scope) {
if (first_match !=
NULL)
break;
else
first_match =
*walker;
}
}
}
sin6 = (struct sockaddr_in6 *)(src + 1);
bzero(sin6, sizeof (*sin6));
sin6->sin6_port = htons(srcport);
if (first_match == NULL) {
ERROR1(ep, ebuf, gettext(
"No IPv6 source address of "
"matching scope for name %s.\n"),
srchp->h_name);
if (last_dst) {
FATAL(ep, ebuf, gettext(
"No match for IPV6 "
"destination "
"address.\n"));
} else {
continue;
}
}
sin6->sin6_family = AF_INET6;
if (*walker != NULL) {
WARN1(ep, ebuf, gettext(
"Multiple IPv6 source addresses "
"for %s of the same scope, using "
"unspecified source instead.\n"),
srchp->h_name);
} else {
bcopy(first_match, &sin6->sin6_addr,
sizeof (struct in6_addr));
}
}
}
handle_errors(ep, ebuf, B_TRUE, B_FALSE);
msgp = (struct sadb_msg *)buffer;
bcopy(buffer, savebuf, SADB_64TO8(msgp->sadb_msg_len));
rc = key_write(keysock, buffer, SADB_64TO8(msgp->sadb_msg_len));
if (rc == -1)
Bail("write() to PF_KEY socket (in doaddresses)");
if (in_cluster_mode) {
(void) sendto(cluster_socket, buffer,
SADB_64TO8(msgp->sadb_msg_len), 0,
(struct sockaddr *)&cli_addr,
sizeof (cli_addr));
}
explicit_bzero(buffer, buffer_size);
time_critical_enter();
do {
rc = read(keysock, buffer, buffer_size);
if (rc == -1)
Bail("read (in doaddresses)");
} while (msgp->sadb_msg_seq != seq ||
msgp->sadb_msg_pid != mypid);
time_critical_exit();
if (msgp->sadb_msg_type != sadb_msg_type ||
msgp->sadb_msg_satype != sadb_msg_satype) {
syslog((LOG_NOTICE|LOG_AUTH), gettext(
"doaddresses: Unexpected returned message "
"(%d exp %d)\n"), msgp->sadb_msg_type,
sadb_msg_type);
Bail("doaddresses: Unexpected returned message");
}
if (msgp->sadb_msg_errno != 0) {
char addrprint[INET6_ADDRSTRLEN];
int on_errno = 0;
char *on_errno_msg;
if (sadb_msg_type == SADB_GET ||
sadb_msg_type == SADB_DELETE) {
on_errno = ESRCH;
on_errno_msg = "does not exist";
} else if (sadb_msg_type == SADB_ADD ||
sadb_msg_type == SADB_UPDATE) {
on_errno = EEXIST;
on_errno_msg = "already exists";
}
errno = msgp->sadb_msg_errno;
if (errno == on_errno) {
ERROR2(ep, ebuf, gettext(
"Association (type = %s) "
"with spi 0x%x and addr\n"),
rparsesatype(msgp->sadb_msg_satype),
ntohl(spi));
ERROR2(ep, ebuf, "%s %s.\n",
do_inet_ntop(dsthp->h_addr_list[i],
addrprint, sizeof (addrprint)),
on_errno_msg);
msgp = (struct sadb_msg *)savebuf;
bcopy(savebuf, buffer,
SADB_64TO8(msgp->sadb_msg_len));
continue;
} else {
if (errno == EINVAL || errno == ESRCH) {
ERROR2(ep, ebuf, gettext(
"PF_KEY Diagnostic code %u: %s.\n"),
msgp->sadb_x_msg_diagnostic,
keysock_diag(
msgp->sadb_x_msg_diagnostic));
} else {
Bail("return message (in doaddresses)");
}
}
}
if (cmd == CMD_GET) {
if (msgp->sadb_msg_len > MAX_GET_SIZE) {
WARN1(ep, ebuf, gettext("WARNING: "
"SA information bigger than %d bytes.\n"),
SADB_64TO8(MAX_GET_SIZE));
}
print_samsg(stdout, buffer, B_FALSE, vflag, nflag);
}
handle_errors(ep, ebuf, B_TRUE, B_FALSE);
msgp = (struct sadb_msg *)savebuf;
bcopy(savebuf, buffer, SADB_64TO8(msgp->sadb_msg_len));
lines_added++;
}
if (i == 0)
Bail("Empty destination address list");
handle_errors(ep, ebuf, B_TRUE, B_TRUE);
}
static void
doaddup(int cmd, int satype, char *argv[], char *ebuf)
{
uint64_t *buffer, *nexthdr;
struct sadb_msg msg;
struct sadb_sa *assoc = NULL;
struct sadb_x_pair *sadb_pair = NULL;
struct sadb_address *src = NULL, *dst = NULL;
struct sadb_address *isrc = NULL, *idst = NULL;
struct sadb_address *natt_local = NULL, *natt_remote = NULL;
struct sadb_key *encrypt = NULL, *auth = NULL;
struct sadb_ident *srcid = NULL, *dstid = NULL;
struct sadb_lifetime *hard = NULL, *soft = NULL;
struct sadb_lifetime *idle = NULL;
struct sadb_x_replay_ctr *replay_ctr = NULL;
struct sadb_sens *label = NULL, *olabel = NULL;
struct sockaddr_in6 *sin6;
int next, token, sa_len, alloclen, totallen = sizeof (msg), prefix;
uint32_t spi = 0;
uint_t reserved_bits = 0;
uint8_t sadb_msg_type;
char *thiscmd, *pstr;
boolean_t readstate = B_FALSE, unspec_src = B_FALSE;
boolean_t alloc_inner = B_FALSE, use_natt = B_FALSE;
struct hostent *srchp = NULL, *dsthp = NULL, *isrchp = NULL,
*idsthp = NULL;
struct hostent *natt_lhp = NULL, *natt_rhp = NULL;
uint16_t srcport = 0, dstport = 0, natt_lport = 0, natt_rport = 0,
isrcport = 0, idstport = 0;
uint8_t proto = 0, iproto = 0;
char *ep = NULL;
switch (cmd) {
case CMD_ADD:
thiscmd = "add";
sadb_msg_type = SADB_ADD;
break;
case CMD_UPDATE:
thiscmd = "update";
sadb_msg_type = SADB_UPDATE;
break;
case CMD_UPDATE_PAIR:
thiscmd = "update-pair";
sadb_msg_type = SADB_X_UPDATEPAIR;
break;
}
msg_init(&msg, sadb_msg_type, (uint8_t)satype);
do {
token = parseextval(*argv, &next);
argv++;
switch (token) {
case TOK_EOF:
break;
case TOK_UNKNOWN:
ERROR1(ep, ebuf, gettext(
"Unknown extension field \"%s\" \n"), *(argv - 1));
break;
case TOK_SPI:
case TOK_PAIR_SPI:
case TOK_REPLAY:
case TOK_STATE:
case TOK_AUTHALG:
case TOK_ENCRALG:
case TOK_ENCAP:
if (assoc == NULL) {
assoc = malloc(sizeof (*assoc));
if (assoc == NULL)
Bail("malloc(assoc)");
bzero(assoc, sizeof (*assoc));
assoc->sadb_sa_exttype = SADB_EXT_SA;
assoc->sadb_sa_len =
SADB_8TO64(sizeof (*assoc));
totallen += sizeof (*assoc);
}
switch (token) {
case TOK_SPI:
if (assoc->sadb_sa_spi != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single SPI value.\n"));
break;
}
assoc->sadb_sa_spi =
htonl((uint32_t)parsenum(*argv, B_TRUE,
ebuf));
if (assoc->sadb_sa_spi == 0) {
ERROR(ep, ebuf, gettext(
"Invalid SPI value \"0\" .\n"));
}
break;
case TOK_PAIR_SPI:
if (cmd == CMD_UPDATE_PAIR) {
ERROR(ep, ebuf, gettext(
"pair-spi can not be used with the "
"\"update-pair\" command.\n"));
}
if (sadb_pair == NULL) {
sadb_pair = malloc(sizeof (*sadb_pair));
if (assoc == NULL)
Bail("malloc(assoc)");
bzero(sadb_pair, sizeof (*sadb_pair));
totallen += sizeof (*sadb_pair);
}
if (sadb_pair->sadb_x_pair_spi != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single pair SPI value.\n"));
break;
}
sadb_pair->sadb_x_pair_len =
SADB_8TO64(sizeof (*sadb_pair));
sadb_pair->sadb_x_pair_exttype =
SADB_X_EXT_PAIR;
sadb_pair->sadb_x_pair_spi =
htonl((uint32_t)parsenum(*argv, B_TRUE,
ebuf));
if (sadb_pair->sadb_x_pair_spi == 0) {
ERROR(ep, ebuf, gettext(
"Invalid SPI value \"0\" .\n"));
}
assoc->sadb_sa_flags |=
SADB_X_SAFLAGS_PAIRED;
break;
case TOK_REPLAY:
if (assoc->sadb_sa_replay != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single replay window size.\n"));
break;
}
assoc->sadb_sa_replay =
(uint8_t)parsenum(*argv, B_TRUE, ebuf);
if (assoc->sadb_sa_replay != 0) {
WARN(ep, ebuf, gettext(
"WARNING: Replay with manual"
" keying considered harmful.\n"));
}
break;
case TOK_STATE:
if (assoc->sadb_sa_state != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single SA state.\n"));
break;
}
assoc->sadb_sa_state = parsestate(*argv,
ebuf);
readstate = B_TRUE;
break;
case TOK_AUTHALG:
if (assoc->sadb_sa_auth != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single auth algorithm.\n"));
break;
}
if (satype == SADB_X_SATYPE_TCPSIG) {
assoc->sadb_sa_auth =
parsetcpalg(*argv, ebuf);
assoc->sadb_sa_flags =
SADB_X_SAFLAGS_TCPSIG;
} else {
assoc->sadb_sa_auth = parsealg(*argv,
IPSEC_PROTO_AH, ebuf);
}
break;
case TOK_ENCRALG:
if (satype == SADB_SATYPE_AH) {
ERROR(ep, ebuf, gettext("Cannot specify"
" encryption with SA type ah.\n"));
break;
}
if (satype == SADB_X_SATYPE_TCPSIG) {
ERROR(ep, ebuf, gettext("Cannot specify"
" encryption with SA type"
" tcpsig.\n"));
break;
}
if (assoc->sadb_sa_encrypt != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single encryption algorithm.\n"));
break;
}
assoc->sadb_sa_encrypt = parsealg(*argv,
IPSEC_PROTO_ESP, ebuf);
break;
case TOK_ENCAP:
if (use_natt) {
ERROR(ep, ebuf, gettext(
"Can only specify single"
" encapsulation.\n"));
break;
}
if (strncmp(*argv, "udp", 3)) {
ERROR(ep, ebuf, gettext(
"Can only specify udp"
" encapsulation.\n"));
break;
}
use_natt = B_TRUE;
break;
}
argv++;
break;
case TOK_SRCPORT:
if (srcport != 0) {
ERROR(ep, ebuf, gettext("Can only specify "
"single source port.\n"));
break;
}
srcport = parsenum(*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_DSTPORT:
if (dstport != 0) {
ERROR(ep, ebuf, gettext("Can only specify "
"single destination port.\n"));
break;
}
dstport = parsenum(*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_ISRCPORT:
alloc_inner = B_TRUE;
if (isrcport != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single inner-source port.\n"));
break;
}
isrcport = parsenum(*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_IDSTPORT:
alloc_inner = B_TRUE;
if (idstport != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single inner-destination port.\n"));
break;
}
idstport = parsenum(*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_NATLPORT:
if (natt_lport != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single NAT-T local port.\n"));
break;
}
natt_lport = parsenum(*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_NATRPORT:
if (natt_rport != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single NAT-T remote port.\n"));
break;
}
natt_rport = parsenum(*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_PROTO:
if (proto != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single protocol.\n"));
break;
}
proto = parsenum(*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_IPROTO:
alloc_inner = B_TRUE;
if (iproto != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single inner protocol.\n"));
break;
}
iproto = parsenum(*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_SRCADDR:
case TOK_SRCADDR6:
if (src != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single source address.\n"));
break;
}
sa_len = parseaddr(*argv, &srchp,
(token == TOK_SRCADDR6), ebuf);
if (srchp == NULL) {
ERROR1(ep, ebuf, gettext(
"Unknown src address \"%s\"\n"), *argv);
break;
}
argv++;
alloclen = sizeof (*src) + roundup(sa_len, 8);
src = malloc(alloclen);
if (src == NULL)
Bail("malloc(src)");
totallen += alloclen;
src->sadb_address_len = SADB_8TO64(alloclen);
src->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
src->sadb_address_reserved = 0;
src->sadb_address_prefixlen = 0;
src->sadb_address_proto = 0;
if (srchp == &dummy.he) {
sin6 = (struct sockaddr_in6 *)(src + 1);
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = AF_INET6;
bcopy(srchp->h_addr_list[0], &sin6->sin6_addr,
sizeof (struct in6_addr));
}
break;
case TOK_DSTADDR:
case TOK_DSTADDR6:
if (dst != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify single "
"destination address.\n"));
break;
}
sa_len = parseaddr(*argv, &dsthp,
(token == TOK_DSTADDR6), ebuf);
if (dsthp == NULL) {
ERROR1(ep, ebuf, gettext(
"Unknown dst address \"%s\"\n"), *argv);
break;
}
argv++;
alloclen = sizeof (*dst) + roundup(sa_len, 8);
dst = malloc(alloclen);
if (dst == NULL)
Bail("malloc(dst)");
totallen += alloclen;
dst->sadb_address_len = SADB_8TO64(alloclen);
dst->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
dst->sadb_address_reserved = 0;
dst->sadb_address_prefixlen = 0;
dst->sadb_address_proto = 0;
if (dsthp == &dummy.he) {
sin6 = (struct sockaddr_in6 *)(dst + 1);
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = AF_INET6;
bcopy(dsthp->h_addr_list[0], &sin6->sin6_addr,
sizeof (struct in6_addr));
}
break;
case TOK_PROXYADDR:
case TOK_PROXYADDR6:
if (isrc != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify single "
"proxy/inner-source address.\n"));
break;
}
if ((pstr = strchr(*argv, '/')) != NULL) {
errno = 0;
prefix = strtol(pstr + 1, NULL, 10);
if (errno != 0) {
ERROR1(ep, ebuf, gettext(
"Invalid prefix %s."), pstr);
break;
}
alloclen = (int)(pstr - *argv);
pstr = malloc(alloclen + 1);
if (pstr == NULL) {
Bail("malloc(pstr)");
}
(void) strlcpy(pstr, *argv, alloclen + 1);
} else {
pstr = *argv;
prefix = 128;
}
sa_len = parseaddr(pstr, &isrchp,
(token == TOK_PROXYADDR6), ebuf);
if (isrchp == NULL) {
ERROR1(ep, ebuf, gettext(
"Unknown proxy/inner-source address "
"\"%s\"\n"), *argv);
break;
}
if (pstr != *argv)
free(pstr);
argv++;
alloclen = sizeof (*isrc) + roundup(sa_len, 8);
isrc = malloc(alloclen);
if (isrc == NULL)
Bail("malloc(isrc)");
totallen += alloclen;
isrc->sadb_address_len = SADB_8TO64(alloclen);
isrc->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
isrc->sadb_address_reserved = 0;
isrc->sadb_address_prefixlen = prefix;
isrc->sadb_address_proto = 0;
if (isrchp == &dummy.he ||
isrchp->h_addr_list[1] == NULL) {
sin6 = (struct sockaddr_in6 *)(isrc + 1);
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = AF_INET6;
bcopy(isrchp->h_addr_list[0], &sin6->sin6_addr,
sizeof (struct in6_addr));
if (prefix <= 32 &&
IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
isrc->sadb_address_prefixlen += 96;
alloc_inner = B_TRUE;
} else {
totallen -= alloclen;
free(isrc);
isrc = NULL;
WARN1(ep, ebuf, gettext(
"Proxy/inner-source address %s "
"is vague, not using.\n"), isrchp->h_name);
freehostent(isrchp);
isrchp = NULL;
break;
}
break;
case TOK_IDSTADDR:
case TOK_IDSTADDR6:
if (idst != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify single "
"inner-destination address.\n"));
break;
}
if ((pstr = strchr(*argv, '/')) != NULL) {
errno = 0;
prefix = strtol(pstr + 1, NULL, 10);
if (errno != 0) {
ERROR1(ep, ebuf, gettext(
"Invalid prefix %s.\n"), pstr);
break;
}
alloclen = (int)(pstr - *argv);
pstr = malloc(alloclen + 1);
if (pstr == NULL) {
Bail("malloc(pstr)");
}
(void) strlcpy(pstr, *argv, alloclen + 1);
} else {
pstr = *argv;
prefix = 128;
}
sa_len = parseaddr(pstr, &idsthp,
(token == TOK_IDSTADDR6), ebuf);
if (idsthp == NULL) {
ERROR1(ep, ebuf, gettext(
"Unknown Inner Src address "
" \"%s\"\n"), *argv);
break;
}
if (pstr != *argv)
free(pstr);
argv++;
alloclen = sizeof (*idst) + roundup(sa_len, 8);
idst = malloc(alloclen);
if (idst == NULL)
Bail("malloc(idst)");
totallen += alloclen;
idst->sadb_address_len = SADB_8TO64(alloclen);
idst->sadb_address_exttype =
SADB_X_EXT_ADDRESS_INNER_DST;
idst->sadb_address_reserved = 0;
idst->sadb_address_prefixlen = prefix;
idst->sadb_address_proto = 0;
if (idsthp == &dummy.he ||
idsthp->h_addr_list[1] == NULL) {
sin6 = (struct sockaddr_in6 *)(idst + 1);
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = AF_INET6;
bcopy(idsthp->h_addr_list[0], &sin6->sin6_addr,
sizeof (struct in6_addr));
if (prefix <= 32 &&
IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
idst->sadb_address_prefixlen += 96;
alloc_inner = B_TRUE;
} else {
totallen -= alloclen;
free(idst);
idst = NULL;
WARN1(ep, ebuf, gettext(
"Inner destination address %s "
"is vague, not using.\n"), idsthp->h_name);
freehostent(idsthp);
idsthp = NULL;
break;
}
break;
case TOK_NATLOC:
if (natt_local != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single NAT-T local address.\n"));
break;
}
sa_len = parseaddr(*argv, &natt_lhp, 0, ebuf);
if (natt_lhp == NULL) {
ERROR1(ep, ebuf, gettext(
"Unknown NAT-T local address \"%s\"\n"),
*argv);
break;
}
argv++;
alloclen = sizeof (*natt_local) + roundup(sa_len, 8);
natt_local = malloc(alloclen);
if (natt_local == NULL)
Bail("malloc(natt_local)");
totallen += alloclen;
natt_local->sadb_address_len = SADB_8TO64(alloclen);
natt_local->sadb_address_exttype =
SADB_X_EXT_ADDRESS_NATT_LOC;
natt_local->sadb_address_reserved = 0;
natt_local->sadb_address_prefixlen = 0;
natt_local->sadb_address_proto = 0;
if (natt_lhp == &dummy.he ||
natt_lhp->h_addr_list[1] == NULL) {
sin6 = (struct sockaddr_in6 *)(natt_local + 1);
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = AF_INET6;
bcopy(natt_lhp->h_addr_list[0],
&sin6->sin6_addr, sizeof (struct in6_addr));
} else {
totallen -= alloclen;
free(natt_local);
natt_local = NULL;
WARN1(ep, ebuf, gettext(
"NAT-T local address %s "
"is vague, not using.\n"),
natt_lhp->h_name);
freehostent(natt_lhp);
natt_lhp = NULL;
break;
}
break;
case TOK_NATREM:
if (natt_remote != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single NAT-T remote address.\n"));
break;
}
sa_len = parseaddr(*argv, &natt_rhp, 0, ebuf);
if (natt_rhp == NULL) {
ERROR1(ep, ebuf, gettext(
"Unknown NAT-T remote address \"%s\"\n"),
*argv);
break;
}
argv++;
alloclen = sizeof (*natt_remote) + roundup(sa_len, 8);
natt_remote = malloc(alloclen);
if (natt_remote == NULL)
Bail("malloc(natt_remote)");
totallen += alloclen;
natt_remote->sadb_address_len = SADB_8TO64(alloclen);
natt_remote->sadb_address_exttype =
SADB_X_EXT_ADDRESS_NATT_REM;
natt_remote->sadb_address_reserved = 0;
natt_remote->sadb_address_prefixlen = 0;
natt_remote->sadb_address_proto = 0;
if (natt_rhp == &dummy.he ||
natt_rhp->h_addr_list[1] == NULL) {
sin6 = (struct sockaddr_in6 *)(natt_remote + 1);
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = AF_INET6;
bcopy(natt_rhp->h_addr_list[0],
&sin6->sin6_addr, sizeof (struct in6_addr));
} else {
totallen -= alloclen;
free(natt_remote);
natt_remote = NULL;
WARN1(ep, ebuf, gettext(
"NAT-T remote address %s "
"is vague, not using.\n"),
natt_rhp->h_name);
freehostent(natt_rhp);
natt_rhp = NULL;
break;
}
break;
case TOK_ENCRKEY:
if (encrypt != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify a single"
" encryption key.\n"));
break;
}
if (assoc != NULL &&
assoc->sadb_sa_encrypt == SADB_EALG_NULL) {
FATAL(ep, ebuf, gettext(
"Cannot specify a key with NULL "
"encryption algorithm.\n"));
break;
}
encrypt = parsekey(*argv, ebuf, reserved_bits);
argv++;
if (encrypt == NULL) {
ERROR(ep, ebuf, gettext(
"Invalid encryption key.\n"));
break;
}
totallen += SADB_64TO8(encrypt->sadb_key_len);
encrypt->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
break;
case TOK_AUTHKEY:
if (auth != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify a single"
" authentication key.\n"));
break;
}
auth = parsekey(*argv, ebuf, 0);
argv++;
if (auth == NULL) {
ERROR(ep, ebuf, gettext(
"Invalid authentication key.\n"));
break;
}
totallen += SADB_64TO8(auth->sadb_key_len);
auth->sadb_key_exttype = SADB_EXT_KEY_AUTH;
break;
case TOK_AUTHSTR:
if (satype != SADB_X_SATYPE_TCPSIG) {
ERROR(ep, ebuf, gettext(
"An authentication string can only be "
"specified for SA type tcpsig.\n"));
break;
}
if (auth != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify a single"
" authentication key or string.\n"));
break;
}
auth = parseauthstr(*argv, ebuf);
argv++;
if (auth == NULL) {
ERROR(ep, ebuf, gettext(
"Invalid authentication string.\n"));
break;
}
totallen += SADB_64TO8(auth->sadb_key_len);
auth->sadb_key_exttype = SADB_X_EXT_STR_AUTH;
break;
case TOK_SRCIDTYPE:
if (*argv == NULL || *(argv + 1) == NULL) {
FATAL(ep, ebuf, gettext(
"Unexpected end of command "
"line - Expecting Src Type.\n"));
break;
}
if (srcid != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify single"
" source certificate identity.\n"));
break;
}
alloclen = sizeof (*srcid) +
roundup(strlen(*(argv + 1)) + 1, 8);
srcid = malloc(alloclen);
if (srcid == NULL)
Bail("malloc(srcid)");
totallen += alloclen;
srcid->sadb_ident_type = parseidtype(*argv, ebuf);
argv++;
srcid->sadb_ident_len = SADB_8TO64(alloclen);
srcid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
srcid->sadb_ident_reserved = 0;
srcid->sadb_ident_id = 0;
(void) strlcpy((char *)(srcid + 1), *argv, alloclen);
argv++;
break;
case TOK_DSTIDTYPE:
if (*argv == NULL || *(argv + 1) == NULL) {
ERROR(ep, ebuf, gettext(
"Unexpected end of command"
" line - expecting dst type.\n"));
break;
}
if (dstid != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify single destination "
"certificate identity.\n"));
break;
}
alloclen = sizeof (*dstid) +
roundup(strlen(*(argv + 1)) + 1, 8);
dstid = malloc(alloclen);
if (dstid == NULL)
Bail("malloc(dstid)");
totallen += alloclen;
dstid->sadb_ident_type = parseidtype(*argv, ebuf);
argv++;
dstid->sadb_ident_len = SADB_8TO64(alloclen);
dstid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
dstid->sadb_ident_reserved = 0;
dstid->sadb_ident_id = 0;
(void) strlcpy((char *)(dstid + 1), *argv, alloclen);
argv++;
break;
case TOK_HARD_ALLOC:
case TOK_HARD_BYTES:
case TOK_HARD_ADDTIME:
case TOK_HARD_USETIME:
if (hard == NULL) {
hard = malloc(sizeof (*hard));
if (hard == NULL)
Bail("malloc(hard_lifetime)");
bzero(hard, sizeof (*hard));
hard->sadb_lifetime_exttype =
SADB_EXT_LIFETIME_HARD;
hard->sadb_lifetime_len =
SADB_8TO64(sizeof (*hard));
totallen += sizeof (*hard);
}
switch (token) {
case TOK_HARD_ALLOC:
if (hard->sadb_lifetime_allocations != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify single"
" hard allocation limit.\n"));
break;
}
hard->sadb_lifetime_allocations =
(uint32_t)parsenum(*argv, B_TRUE, ebuf);
break;
case TOK_HARD_BYTES:
if (hard->sadb_lifetime_bytes != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single hard byte limit.\n"));
break;
}
hard->sadb_lifetime_bytes = parsenum(*argv,
B_TRUE, ebuf);
break;
case TOK_HARD_ADDTIME:
if (hard->sadb_lifetime_addtime != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single past-add lifetime.\n"));
break;
}
hard->sadb_lifetime_addtime = parsenum(*argv,
B_TRUE, ebuf);
break;
case TOK_HARD_USETIME:
if (hard->sadb_lifetime_usetime != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify "
"single past-use lifetime.\n"));
break;
}
hard->sadb_lifetime_usetime = parsenum(*argv,
B_TRUE, ebuf);
break;
}
argv++;
break;
case TOK_SOFT_ALLOC:
case TOK_SOFT_BYTES:
case TOK_SOFT_ADDTIME:
case TOK_SOFT_USETIME:
if (soft == NULL) {
soft = malloc(sizeof (*soft));
if (soft == NULL)
Bail("malloc(soft_lifetime)");
bzero(soft, sizeof (*soft));
soft->sadb_lifetime_exttype =
SADB_EXT_LIFETIME_SOFT;
soft->sadb_lifetime_len =
SADB_8TO64(sizeof (*soft));
totallen += sizeof (*soft);
}
switch (token) {
case TOK_SOFT_ALLOC:
if (soft->sadb_lifetime_allocations != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify single"
" soft allocation limit.\n"));
break;
}
soft->sadb_lifetime_allocations =
(uint32_t)parsenum(*argv, B_TRUE, ebuf);
break;
case TOK_SOFT_BYTES:
if (soft->sadb_lifetime_bytes != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify single"
" soft byte limit.\n"));
break;
}
soft->sadb_lifetime_bytes = parsenum(*argv,
B_TRUE, ebuf);
break;
case TOK_SOFT_ADDTIME:
if (soft->sadb_lifetime_addtime != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify single"
" past-add lifetime.\n"));
break;
}
soft->sadb_lifetime_addtime = parsenum(*argv,
B_TRUE, ebuf);
break;
case TOK_SOFT_USETIME:
if (soft->sadb_lifetime_usetime != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify single"
" past-use lifetime.\n"));
break;
}
soft->sadb_lifetime_usetime = parsenum(*argv,
B_TRUE, ebuf);
break;
}
argv++;
break;
case TOK_FLAG_INBOUND:
assoc->sadb_sa_flags |= SADB_X_SAFLAGS_INBOUND;
break;
case TOK_FLAG_OUTBOUND:
assoc->sadb_sa_flags |= SADB_X_SAFLAGS_OUTBOUND;
break;
case TOK_REPLAY_VALUE:
if (replay_ctr != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify single "
"replay value."));
break;
}
replay_ctr = calloc(1, sizeof (*replay_ctr));
if (replay_ctr == NULL) {
Bail("malloc(replay value)");
}
replay_ctr->sadb_x_rc_exttype = SADB_X_EXT_REPLAY_VALUE;
replay_ctr->sadb_x_rc_len =
SADB_8TO64(sizeof (*replay_ctr));
totallen += sizeof (*replay_ctr);
replay_ctr->sadb_x_rc_replay32 = (uint32_t)parsenum(
*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_IDLE_ADDTIME:
case TOK_IDLE_USETIME:
if (idle == NULL) {
idle = calloc(1, sizeof (*idle));
if (idle == NULL) {
Bail("malloc idle lifetime");
}
idle->sadb_lifetime_exttype =
SADB_X_EXT_LIFETIME_IDLE;
idle->sadb_lifetime_len =
SADB_8TO64(sizeof (*idle));
totallen += sizeof (*idle);
}
switch (token) {
case TOK_IDLE_ADDTIME:
idle->sadb_lifetime_addtime =
(uint32_t)parsenum(*argv,
B_TRUE, ebuf);
break;
case TOK_IDLE_USETIME:
idle->sadb_lifetime_usetime =
(uint32_t)parsenum(*argv,
B_TRUE, ebuf);
break;
}
argv++;
break;
case TOK_RESERVED:
if (encrypt != NULL)
ERROR(ep, ebuf, gettext(
"Reserved bits need to be "
"specified before key.\n"));
reserved_bits = (uint_t)parsenum(*argv,
B_TRUE, ebuf);
argv++;
break;
case TOK_LABEL:
label = parselabel(token, *argv);
argv++;
if (label == NULL) {
ERROR(ep, ebuf,
gettext("Malformed security label\n"));
break;
} else if (label == PARSELABEL_BAD_TOKEN) {
Bail("Internal token value error");
}
totallen += SADB_64TO8(label->sadb_sens_len);
break;
case TOK_OLABEL:
case TOK_IMPLABEL:
olabel = parselabel(token, *argv);
argv++;
if (label == NULL) {
ERROR(ep, ebuf,
gettext("Malformed security label\n"));
break;
} else if (label == PARSELABEL_BAD_TOKEN) {
Bail("Internal token value error");
}
totallen += SADB_64TO8(olabel->sadb_sens_len);
break;
default:
ERROR1(ep, ebuf, gettext(
"Don't use extension %s for add/update.\n"),
*(argv - 1));
break;
}
} while (token != TOK_EOF);
handle_errors(ep, ebuf, B_TRUE, B_FALSE);
#define PORT_ONLY_ALLOCATE(af, socktype, exttype, extvar, port) { \
alloclen = sizeof (sadb_address_t) + roundup(sizeof (socktype), 8); \
(extvar) = calloc(1, alloclen); \
if ((extvar) == NULL) { \
Bail("malloc(implicit port)"); \
} \
totallen += alloclen; \
(extvar)->sadb_address_len = SADB_8TO64(alloclen); \
(extvar)->sadb_address_exttype = (exttype); \
\
sin6 = (struct sockaddr_in6 *)((extvar) + 1); \
sin6->sin6_family = (af); \
sin6->sin6_port = (port); \
}
if (use_natt) {
if (natt_lport != 0 && natt_local == NULL) {
PORT_ONLY_ALLOCATE(AF_INET, struct sockaddr_in,
SADB_X_EXT_ADDRESS_NATT_LOC, natt_local,
natt_lport);
}
if (natt_rport != 0 && natt_remote == NULL) {
PORT_ONLY_ALLOCATE(AF_INET, struct sockaddr_in,
SADB_X_EXT_ADDRESS_NATT_REM, natt_remote,
natt_rport);
}
} else {
if (natt_lport != 0 || natt_rport != 0) {
ERROR(ep, ebuf, gettext("Must specify 'encap udp' "
"with any NAT-T port.\n"));
} else if (natt_local != NULL || natt_remote != NULL) {
ERROR(ep, ebuf, gettext("Must specify 'encap udp' "
"with any NAT-T address.\n"));
}
}
if (alloc_inner && idst == NULL) {
PORT_ONLY_ALLOCATE(AF_INET6, struct sockaddr_in6,
SADB_X_EXT_ADDRESS_INNER_DST, idst, 0);
}
if (alloc_inner && isrc == NULL) {
PORT_ONLY_ALLOCATE(AF_INET6, struct sockaddr_in6,
SADB_X_EXT_ADDRESS_INNER_SRC, isrc, 0);
}
#undef PORT_ONLY_ALLOCATE
if (src == NULL && dst != NULL) {
size_t lenbytes = SADB_64TO8(dst->sadb_address_len);
unspec_src = B_TRUE;
totallen += lenbytes;
src = malloc(lenbytes);
if (src == NULL)
Bail("malloc(implicit src)");
bcopy(dst, src, lenbytes);
src->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
sin6 = (struct sockaddr_in6 *)(src + 1);
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = AF_INET6;
}
msg.sadb_msg_len = SADB_8TO64(totallen);
buffer = malloc(totallen);
nexthdr = buffer;
bcopy(&msg, nexthdr, sizeof (msg));
nexthdr += SADB_8TO64(sizeof (msg));
if (assoc != NULL) {
if (satype != SADB_X_SATYPE_TCPSIG && assoc->sadb_sa_spi == 0) {
ERROR1(ep, ebuf, gettext(
"The SPI value is missing for "
"the association you wish to %s.\n"), thiscmd);
}
if (assoc->sadb_sa_auth == 0 && assoc->sadb_sa_encrypt == 0 &&
cmd == CMD_ADD) {
free(assoc);
FATAL(ep, ebuf, gettext(
"Select at least one algorithm "
"for this add.\n"));
}
if (msg.sadb_msg_satype == SADB_SATYPE_ESP &&
assoc->sadb_sa_encrypt == 0)
assoc->sadb_sa_encrypt = SADB_EALG_NULL;
if (assoc->sadb_sa_state == 0) {
if (readstate) {
ERROR(ep, ebuf, gettext(
"WARNING: Cannot set LARVAL SA state.\n"));
}
assoc->sadb_sa_state = SADB_SASTATE_MATURE;
}
if (use_natt) {
if (natt_remote != NULL)
assoc->sadb_sa_flags |= SADB_X_SAFLAGS_NATT_REM;
if (natt_local != NULL)
assoc->sadb_sa_flags |= SADB_X_SAFLAGS_NATT_LOC;
}
if (alloc_inner) {
assoc->sadb_sa_flags |= SADB_X_SAFLAGS_TUNNEL;
if (proto != 0 && proto != IPPROTO_ENCAP &&
proto != IPPROTO_IPV6) {
ERROR1(ep, ebuf, gettext(
"WARNING: Protocol type %d not "
"for use with Tunnel-Mode SA.\n"), proto);
}
}
bcopy(assoc, nexthdr, SADB_64TO8(assoc->sadb_sa_len));
nexthdr += assoc->sadb_sa_len;
spi = assoc->sadb_sa_spi;
free(assoc);
} else {
if (satype != SADB_X_SATYPE_TCPSIG) {
if (spi == 0) {
ERROR1(ep, ebuf, gettext(
"Need to define SPI for %s.\n"), thiscmd);
}
ERROR1(ep, ebuf, gettext(
"Need SA parameters for %s.\n"), thiscmd);
}
}
if (sadb_pair != NULL) {
if (sadb_pair->sadb_x_pair_spi == 0) {
ERROR1(ep, ebuf, gettext(
"The SPI value is missing for the "
"association you wish to %s.\n"), thiscmd);
}
bcopy(sadb_pair, nexthdr,
SADB_64TO8(sadb_pair->sadb_x_pair_len));
nexthdr += sadb_pair->sadb_x_pair_len;
free(sadb_pair);
}
if (hard != NULL) {
bcopy(hard, nexthdr, SADB_64TO8(hard->sadb_lifetime_len));
nexthdr += hard->sadb_lifetime_len;
free(hard);
}
if (soft != NULL) {
bcopy(soft, nexthdr, SADB_64TO8(soft->sadb_lifetime_len));
nexthdr += soft->sadb_lifetime_len;
free(soft);
}
if (idle != NULL) {
bcopy(idle, nexthdr, SADB_64TO8(idle->sadb_lifetime_len));
nexthdr += idle->sadb_lifetime_len;
free(idle);
}
if (encrypt == NULL && auth == NULL && cmd == CMD_ADD) {
ERROR(ep, ebuf, gettext(
"Must have at least one key for an add.\n"));
}
if (encrypt != NULL) {
bcopy(encrypt, nexthdr, SADB_64TO8(encrypt->sadb_key_len));
nexthdr += encrypt->sadb_key_len;
explicit_bzero(encrypt, SADB_64TO8(encrypt->sadb_key_len));
free(encrypt);
}
if (auth != NULL) {
bcopy(auth, nexthdr, SADB_64TO8(auth->sadb_key_len));
nexthdr += auth->sadb_key_len;
explicit_bzero(auth, SADB_64TO8(auth->sadb_key_len));
free(auth);
}
if (srcid != NULL) {
bcopy(srcid, nexthdr, SADB_64TO8(srcid->sadb_ident_len));
nexthdr += srcid->sadb_ident_len;
free(srcid);
}
if (dstid != NULL) {
bcopy(dstid, nexthdr, SADB_64TO8(dstid->sadb_ident_len));
nexthdr += dstid->sadb_ident_len;
free(dstid);
}
if (dst != NULL) {
bcopy(dst, nexthdr, SADB_64TO8(dst->sadb_address_len));
free(dst);
dst = (struct sadb_address *)nexthdr;
dst->sadb_address_proto = proto;
((struct sockaddr_in6 *)(dst + 1))->sin6_port = htons(dstport);
nexthdr += dst->sadb_address_len;
} else {
FATAL1(ep, ebuf, gettext(
"Need destination address for %s.\n"), thiscmd);
}
if (use_natt) {
if (natt_remote == NULL && natt_local == NULL) {
ERROR(ep, ebuf, gettext(
"Must specify NAT-T remote or local address "
"for UDP encapsulation.\n"));
}
if (natt_remote != NULL) {
bcopy(natt_remote, nexthdr,
SADB_64TO8(natt_remote->sadb_address_len));
free(natt_remote);
natt_remote = (struct sadb_address *)nexthdr;
nexthdr += natt_remote->sadb_address_len;
((struct sockaddr_in6 *)(natt_remote + 1))->sin6_port =
htons(natt_rport);
}
if (natt_local != NULL) {
bcopy(natt_local, nexthdr,
SADB_64TO8(natt_local->sadb_address_len));
free(natt_local);
natt_local = (struct sadb_address *)nexthdr;
nexthdr += natt_local->sadb_address_len;
((struct sockaddr_in6 *)(natt_local + 1))->sin6_port =
htons(natt_lport);
}
}
handle_errors(ep, ebuf, B_TRUE, B_FALSE);
bcopy(src, nexthdr, SADB_64TO8(src->sadb_address_len));
free(src);
src = (struct sadb_address *)nexthdr;
src->sadb_address_proto = proto;
((struct sockaddr_in6 *)(src + 1))->sin6_port = htons(srcport);
nexthdr += src->sadb_address_len;
if (isrc != NULL) {
bcopy(isrc, nexthdr, SADB_64TO8(isrc->sadb_address_len));
free(isrc);
isrc = (struct sadb_address *)nexthdr;
isrc->sadb_address_proto = iproto;
((struct sockaddr_in6 *)(isrc + 1))->sin6_port =
htons(isrcport);
nexthdr += isrc->sadb_address_len;
}
if (idst != NULL) {
bcopy(idst, nexthdr, SADB_64TO8(idst->sadb_address_len));
free(idst);
idst = (struct sadb_address *)nexthdr;
idst->sadb_address_proto = iproto;
((struct sockaddr_in6 *)(idst + 1))->sin6_port =
htons(idstport);
nexthdr += idst->sadb_address_len;
}
if (replay_ctr != NULL) {
bcopy(replay_ctr, nexthdr,
SADB_64TO8(replay_ctr->sadb_x_rc_len));
nexthdr += replay_ctr->sadb_x_rc_len;
free(replay_ctr);
}
if (label != NULL) {
bcopy(label, nexthdr, SADB_64TO8(label->sadb_sens_len));
nexthdr += label->sadb_sens_len;
free(label);
label = NULL;
}
if (olabel != NULL) {
bcopy(olabel, nexthdr, SADB_64TO8(olabel->sadb_sens_len));
nexthdr += olabel->sadb_sens_len;
free(olabel);
olabel = NULL;
}
if (cflag) {
lines_added++;
} else {
doaddresses(sadb_msg_type, satype,
cmd, srchp, dsthp, src, dst, unspec_src, buffer, totallen,
spi, ebuf);
}
if (isrchp != NULL && isrchp != &dummy.he)
freehostent(isrchp);
if (idsthp != NULL && idsthp != &dummy.he)
freehostent(idsthp);
if (srchp != NULL && srchp != &dummy.he)
freehostent(srchp);
if (dsthp != NULL && dsthp != &dummy.he)
freehostent(dsthp);
if (natt_lhp != NULL && natt_lhp != &dummy.he)
freehostent(natt_lhp);
if (natt_rhp != NULL && natt_rhp != &dummy.he)
freehostent(natt_rhp);
free(ebuf);
free(buffer);
}
static void
dodelget(int cmd, int satype, char *argv[], char *ebuf)
{
struct sadb_msg *msg = (struct sadb_msg *)get_buffer;
uint64_t *nextext;
struct sadb_sa *assoc = NULL;
struct sadb_address *src = NULL, *dst = NULL;
int next, token, sa_len;
char *thiscmd;
uint32_t spi = 0;
uint8_t sadb_msg_type;
struct hostent *srchp = NULL, *dsthp = NULL;
struct sockaddr_in6 *sin6;
boolean_t unspec_src = B_TRUE;
uint16_t srcport = 0, dstport = 0;
uint8_t proto = 0;
char *ep = NULL;
uint32_t sa_flags = 0;
nextext = (uint64_t *)(msg + 1);
bzero(nextext, sizeof (get_buffer) - sizeof (*msg));
switch (cmd) {
case CMD_GET:
thiscmd = "get";
sadb_msg_type = SADB_GET;
break;
case CMD_DELETE:
thiscmd = "delete";
sadb_msg_type = SADB_DELETE;
break;
case CMD_DELETE_PAIR:
thiscmd = "delete-pair";
sadb_msg_type = SADB_X_DELPAIR;
break;
}
msg_init(msg, sadb_msg_type, (uint8_t)satype);
#define ALLOC_ADDR_EXT(ext, exttype) \
(ext) = (struct sadb_address *)nextext; \
nextext = (uint64_t *)((ext) + 1); \
nextext += SADB_8TO64(roundup(sa_len, 8)); \
(ext)->sadb_address_exttype = exttype; \
(ext)->sadb_address_len = nextext - ((uint64_t *)ext);
do {
token = parseextval(*argv, &next);
argv++;
switch (token) {
case TOK_EOF:
break;
case TOK_UNKNOWN:
ERROR1(ep, ebuf, gettext(
"Unknown extension field \"%s\"\n"), *(argv - 1));
break;
case TOK_SPI:
if (assoc != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify single SPI value.\n"));
break;
}
assoc = (struct sadb_sa *)nextext;
nextext = (uint64_t *)(assoc + 1);
assoc->sadb_sa_len = SADB_8TO64(sizeof (*assoc));
assoc->sadb_sa_exttype = SADB_EXT_SA;
assoc->sadb_sa_spi = htonl((uint32_t)parsenum(*argv,
B_TRUE, ebuf));
spi = assoc->sadb_sa_spi;
argv++;
break;
case TOK_SRCPORT:
if (srcport != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify single source port.\n"));
break;
}
srcport = parsenum(*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_DSTPORT:
if (dstport != 0) {
ERROR(ep, ebuf, gettext(
"Can only "
"specify single destination port.\n"));
break;
}
dstport = parsenum(*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_PROTO:
if (proto != 0) {
ERROR(ep, ebuf, gettext(
"Can only specify single protocol.\n"));
break;
}
proto = parsenum(*argv, B_TRUE, ebuf);
argv++;
break;
case TOK_SRCADDR:
case TOK_SRCADDR6:
if (src != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify single source addr.\n"));
break;
}
sa_len = parseaddr(*argv, &srchp,
(token == TOK_SRCADDR6), ebuf);
if (srchp == NULL) {
ERROR1(ep, ebuf, gettext(
"Unknown source address \"%s\"\n"), *argv);
break;
}
argv++;
unspec_src = B_FALSE;
ALLOC_ADDR_EXT(src, SADB_EXT_ADDRESS_SRC);
if (srchp == &dummy.he) {
sin6 = (struct sockaddr_in6 *)(src + 1);
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = AF_INET6;
bcopy(srchp->h_addr_list[0], &sin6->sin6_addr,
sizeof (struct in6_addr));
}
break;
case TOK_DSTADDR:
case TOK_DSTADDR6:
if (dst != NULL) {
ERROR(ep, ebuf, gettext(
"Can only specify single destination "
"address.\n"));
break;
}
sa_len = parseaddr(*argv, &dsthp,
(token == TOK_SRCADDR6), ebuf);
if (dsthp == NULL) {
ERROR1(ep, ebuf, gettext(
"Unknown destination address \"%s\"\n"),
*argv);
break;
}
argv++;
ALLOC_ADDR_EXT(dst, SADB_EXT_ADDRESS_DST);
if (dsthp == &dummy.he) {
sin6 = (struct sockaddr_in6 *)(dst + 1);
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = AF_INET6;
bcopy(dsthp->h_addr_list[0], &sin6->sin6_addr,
sizeof (struct in6_addr));
}
break;
case TOK_FLAG_INBOUND:
sa_flags |= SADB_X_SAFLAGS_INBOUND;
break;
case TOK_FLAG_OUTBOUND:
sa_flags |= SADB_X_SAFLAGS_OUTBOUND;
break;
default:
ERROR2(ep, ebuf, gettext(
"Don't use extension %s for '%s' command.\n"),
*(argv - 1), thiscmd);
break;
}
} while (token != TOK_EOF);
handle_errors(ep, ebuf, B_TRUE, B_FALSE);
if (assoc == NULL && satype != SADB_X_SATYPE_TCPSIG) {
FATAL1(ep, ebuf, gettext(
"Need SA parameters for %s.\n"), thiscmd);
}
if (assoc != NULL) {
assoc->sadb_sa_flags |= sa_flags;
}
if (srcport != 0) {
if (src == NULL) {
ALLOC_ADDR_EXT(src, SADB_EXT_ADDRESS_SRC);
sin6 = (struct sockaddr_in6 *)(src + 1);
src->sadb_address_proto = proto;
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = AF_INET6;
} else {
sin6 = (struct sockaddr_in6 *)(src + 1);
}
sin6->sin6_port = htons(srcport);
}
if (dstport != 0) {
if (dst == NULL) {
ALLOC_ADDR_EXT(dst, SADB_EXT_ADDRESS_DST);
sin6 = (struct sockaddr_in6 *)(dst + 1);
src->sadb_address_proto = proto;
bzero(sin6, sizeof (*sin6));
sin6->sin6_family = AF_INET6;
} else {
sin6 = (struct sockaddr_in6 *)(dst + 1);
}
sin6->sin6_port = htons(dstport);
}
msg->sadb_msg_len = nextext - get_buffer;
if (cflag) {
lines_added++;
} else {
doaddresses(sadb_msg_type, satype,
cmd, srchp, dsthp, src, dst, unspec_src, get_buffer,
sizeof (get_buffer), spi, NULL);
}
if (srchp != NULL && srchp != &dummy.he)
freehostent(srchp);
if (dsthp != NULL && dsthp != &dummy.he)
freehostent(dsthp);
}
static void
monitor_catch(int signal)
{
if (!interactive)
errx(signal, gettext("Bailing on signal %d."), signal);
}
static void
domonitor(boolean_t passive)
{
struct sadb_msg *samsg;
struct sigaction newsig, oldsig;
int rc;
newsig.sa_handler = monitor_catch;
newsig.sa_flags = 0;
(void) sigemptyset(&newsig.sa_mask);
(void) sigaddset(&newsig.sa_mask, SIGINT);
(void) sigaction(SIGINT, &newsig, &oldsig);
samsg = (struct sadb_msg *)get_buffer;
if (!passive) {
(void) printf(gettext("Actively"));
msg_init(samsg, SADB_X_PROMISC, 1);
rc = key_write(keysock, samsg, sizeof (*samsg));
if (rc == -1)
Bail("write (SADB_X_PROMISC)");
} else {
(void) printf(gettext("Passively"));
}
(void) printf(gettext(" monitoring the PF_KEY socket.\n"));
for (; ; ) {
rc = read(keysock, samsg, sizeof (get_buffer));
if (rc == -1) {
if (errno == EINTR && interactive)
goto out;
else
Bail("read (in domonitor)");
}
(void) printf(gettext("Read %d bytes.\n"), rc);
print_samsg(stdout, get_buffer, B_TRUE, vflag, nflag);
(void) putchar('\n');
}
out:
if (interactive)
(void) sigaction(SIGINT, &oldsig, NULL);
}
static void
mask_signals(boolean_t unmask)
{
sigset_t set;
static sigset_t oset;
if (unmask) {
(void) sigprocmask(SIG_SETMASK, &oset, NULL);
} else {
(void) sigfillset(&set);
(void) sigprocmask(SIG_SETMASK, &set, &oset);
}
}
#define puts_tr(s) (void) puts(gettext(s))
static void
doattrhelp()
{
struct toktable *tp;
int i;
puts_tr("\nSA attributes:");
tp = tcpkey ? tcpkey_tokens : tokens;
for (i = 0; tp->string != NULL; tp++, i++) {
if (i % 3 == 0)
(void) printf("\n");
(void) printf(" %-15.15s", tp->string);
}
(void) printf("\n");
}
static void
dohelpcmd(char *cmds)
{
int cmd;
if (strcmp(cmds, "attr") == 0) {
doattrhelp();
return;
}
cmd = parsecmd(cmds);
switch (cmd) {
case CMD_UPDATE:
puts_tr("update - Update an existing SA");
break;
case CMD_UPDATE_PAIR:
puts_tr("update-pair - Update an existing pair of SAs");
break;
case CMD_ADD:
puts_tr("add - Add a new security association (SA)");
break;
case CMD_DELETE:
puts_tr("delete - Delete an SA");
break;
case CMD_DELETE_PAIR:
puts_tr("delete-pair - Delete a pair of SAs");
break;
case CMD_GET:
puts_tr("get - Display an SA");
break;
case CMD_FLUSH:
puts_tr("flush - Delete all SAs");
if (!tcpkey) {
puts_tr("");
puts_tr("Optional arguments:");
puts_tr("all delete all SAs");
puts_tr("esp delete just ESP SAs");
puts_tr("ah delete just AH SAs");
puts_tr("<number> delete just SAs with type "
"given by number");
puts_tr("");
}
break;
case CMD_DUMP:
puts_tr("dump - Display all SAs");
if (!tcpkey) {
puts_tr("");
puts_tr("Optional arguments:");
puts_tr("all display all SAs");
puts_tr("esp display just ESP SAs");
puts_tr("ah display just AH SAs");
puts_tr("<number> display just SAs with type "
"given by number");
puts_tr("");
}
break;
case CMD_MONITOR:
puts_tr("monitor - Monitor all PF_KEY reply messages.");
break;
case CMD_PMONITOR:
puts_tr(
"pmonitor, passive_monitor - Monitor PF_KEY messages that");
puts_tr(
" reply to all PF_KEY sockets.");
break;
case CMD_QUIT:
puts_tr("quit, exit - Exit the program");
break;
case CMD_SAVE:
puts_tr("save - Saves all SAs to a file");
break;
case CMD_HELP:
puts_tr("help - Display list of commands");
puts_tr("help <cmd> - Display help for command");
puts_tr("help attr - Display possible SA attributes");
break;
default:
(void) printf(gettext("%s: Unknown command\n"), cmds);
break;
}
}
static void
dohelp_tcpkey(void)
{
puts_tr("");
puts_tr("The following commands are of the form:");
puts_tr(" <command> {SA type} {attribute value}*");
puts_tr("");
puts_tr("add (interactive only) - Add a new security association (SA)");
puts_tr("delete - Delete an SA");
puts_tr("get - Display an SA");
puts_tr("flush - Delete all SAs");
puts_tr("dump - Display all SAs");
puts_tr("save - Saves all SAs to a file");
}
static void
dohelp(char *cmds)
{
if (cmds != NULL) {
dohelpcmd(cmds);
return;
}
puts_tr("Commands");
puts_tr("--------");
puts_tr("?, help - Display this list");
puts_tr("help <cmd> - Display help for command");
puts_tr("help attr - Display possible SA attributes");
puts_tr("quit, exit - Exit the program");
if (tcpkey) {
dohelp_tcpkey();
return;
}
puts_tr("monitor - Monitor all PF_KEY reply messages.");
puts_tr("pmonitor, passive_monitor - Monitor PF_KEY messages that");
puts_tr(" reply to all PF_KEY sockets.");
puts_tr("");
puts_tr("The following commands are of the form:");
puts_tr(" <command> {SA type} {attribute value}*");
puts_tr("");
puts_tr("add (interactive only) - Add a new security association (SA)");
puts_tr("update (interactive only) - Update an existing SA");
puts_tr("update-pair (interactive only) - Update an existing SA pair");
puts_tr("delete - Delete an SA");
puts_tr("delete-pair - Delete an SA pair");
puts_tr("get - Display an SA");
puts_tr("flush - Delete all SAs");
puts_tr("dump - Display all SAs");
puts_tr("save - Saves all SAs to a file");
}
static void
parseit(int argc, char *argv[], char *ebuf, boolean_t read_cmdfile)
{
int cmd, satype;
char *cmdstr;
char *ep = NULL;
if (argc == 0)
return;
cmdstr = argv[0];
cmd = parsecmd(*argv++);
switch (cmd) {
case CMD_HELP:
if (read_cmdfile)
ERROR(ep, ebuf, gettext("Help not appropriate in "
"config file."));
else
dohelp(*argv);
return;
case CMD_MONITOR:
if (read_cmdfile)
ERROR(ep, ebuf, gettext("Monitor not appropriate in "
"config file."));
else {
domonitor(B_FALSE);
if (interactive) {
(void) printf("\n");
return;
}
}
break;
case CMD_PMONITOR:
if (read_cmdfile)
ERROR(ep, ebuf, gettext("Monitor not appropriate in "
"config file."));
else {
domonitor(B_TRUE);
if (interactive) {
(void) printf("\n");
return;
}
}
break;
case CMD_QUIT:
EXIT_OK(NULL);
}
handle_errors(ep, ebuf, B_FALSE, B_FALSE);
if (tcpkey) {
satype = SADB_X_SATYPE_TCPSIG;
} else {
satype = parsesatype(*argv, ebuf);
if (satype != SADB_SATYPE_UNSPEC) {
argv++;
} else {
if (cmd == CMD_SAVE) {
if (*argv == NULL) {
FATAL(ep, ebuf, gettext(
"Must specify a specific "
"SA type for save.\n"));
} else {
argv++;
}
}
}
}
switch (cmd) {
case CMD_FLUSH:
if (argc > 2) {
ERROR(ep, ebuf, gettext("Too many arguments for "
"flush command"));
handle_errors(ep, ebuf,
interactive ? B_TRUE : B_FALSE, B_FALSE);
}
if (!cflag)
doflush(satype);
lines_added++;
break;
case CMD_ADD:
case CMD_UPDATE:
case CMD_UPDATE_PAIR:
if (!interactive) {
errx(1, gettext(
"can't do ADD or UPDATE from the command line.\n"));
}
if (satype == SADB_SATYPE_UNSPEC) {
FATAL(ep, ebuf, gettext(
"Must specify a specific SA type."));
}
doaddup(cmd, satype, argv, ebuf);
break;
case CMD_DELETE:
case CMD_DELETE_PAIR:
case CMD_GET:
if (satype == SADB_SATYPE_UNSPEC) {
FATAL(ep, ebuf, gettext(
"Must specify a single SA type."));
}
dodelget(cmd, satype, argv, ebuf);
break;
case CMD_DUMP:
if (read_cmdfile)
ERROR(ep, ebuf, gettext("Dump not appropriate in "
"config file."));
else {
if (argc > 2) {
ERROR(ep, ebuf, gettext("Too many arguments "
"for dump command"));
handle_errors(ep, ebuf,
interactive ? B_TRUE : B_FALSE, B_FALSE);
}
dodump(satype, NULL);
}
break;
case CMD_SAVE:
if (read_cmdfile) {
ERROR(ep, ebuf, gettext("Save not appropriate in "
"config file."));
} else {
mask_signals(B_FALSE);
dodump(satype, opensavefile(argv[0]));
mask_signals(B_TRUE);
}
break;
default:
warnx(gettext("Unknown command (%s).\n"), cmdstr);
usage();
}
handle_errors(ep, ebuf, B_FALSE, B_FALSE);
}
int
main(int argc, char *argv[])
{
int ch;
FILE *infile = stdin, *savefile;
boolean_t dosave = B_FALSE, readfile = B_FALSE;
char *configfile = NULL;
struct stat sbuf;
int bootflags;
int satype = SADB_SATYPE_UNSPEC;
char *prompt = "ipseckey> ";
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
my_fmri = getenv("SMF_FMRI");
progname = getprogname();
tcpkey = strcmp(progname, "tcpkey") == 0;
if (tcpkey) {
satype = SADB_X_SATYPE_TCPSIG;
prompt = "tcpkey> ";
}
openlog(progname, LOG_CONS, LOG_AUTH);
if (!priv_ineffect(PRIV_SYS_IP_CONFIG))
errx(1, "Insufficient privileges to run %s.", progname);
(void) umask((mode_t)00377);
while ((ch = getopt(argc, argv, "pnvf:s:c:")) != EOF)
switch (ch) {
case 'p':
pflag = B_TRUE;
break;
case 'n':
nflag = B_TRUE;
break;
case 'v':
vflag = B_TRUE;
break;
case 'c':
cflag = B_TRUE;
case 'f':
if (dosave)
usage();
if (stat(optarg, &sbuf) == -1) {
EXIT_BADCONFIG2("Invalid pathname: %s\n",
optarg);
}
if (!(sbuf.st_mode & S_IFREG)) {
EXIT_BADCONFIG2("%s - Not a regular file\n",
optarg);
}
infile = fopen(optarg, "r");
if (infile == NULL) {
EXIT_BADCONFIG2("Unable to open configuration "
"file: %s\n", optarg);
}
if (fstat(fileno(infile), &sbuf) == -1) {
(void) fclose(infile);
EXIT_BADCONFIG2("Unable to stat configuration "
"file: %s\n", optarg);
}
if (INSECURE_PERMS(sbuf)) {
if (my_fmri != NULL) {
(void) fclose(infile);
EXIT_BADCONFIG2("Config file "
"%s has insecure permissions.",
optarg);
} else {
(void) fprintf(stderr, gettext(
"Config file %s has insecure "
"permissions, will be rejected in "
"permanent config.\n"), optarg);
}
}
configfile = strdup(optarg);
readfile = B_TRUE;
break;
case 's':
if (readfile)
usage();
dosave = B_TRUE;
savefile = opensavefile(optarg);
break;
default:
usage();
}
argc -= optind;
argv += optind;
mypid = getpid();
keysock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
if (keysock == -1) {
if (errno == EPERM) {
EXIT_BADPERM("Insufficient privileges to open "
"PF_KEY socket.\n");
} else {
EXIT_FATAL("Opening PF_KEY socket");
}
}
if ((_cladm(CL_INITIALIZE, CL_GET_BOOTFLAG, &bootflags) != 0) ||
(bootflags & CLUSTER_BOOTED)) {
in_cluster_mode = B_TRUE;
cluster_socket = socket(AF_INET, SOCK_DGRAM, 0);
cli_addr.sin_family = AF_INET;
cli_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
cli_addr.sin_port = htons(CLUSTER_UDP_PORT);
}
if (dosave) {
mask_signals(B_FALSE);
dodump(satype, savefile);
mask_signals(B_TRUE);
EXIT_OK(NULL);
}
if (my_fmri != NULL && readfile) {
(void) fprintf(stdout, gettext(
"Flushing existing SAs before adding new SAs\n"));
(void) fflush(stdout);
doflush(satype);
}
if (infile != stdin || argc == 0) {
do_interactive(infile, configfile, prompt, my_fmri,
parseit, no_match);
}
parseit(argc, argv, NULL, B_FALSE);
return (0);
}