ipseckey.c revision bb3ed8dfcb84e1d06fdc5da3b0ca7758e737644b
/*
* 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
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* NOTE:I'm trying to use "struct sadb_foo" instead of "sadb_foo_t"
* as a maximal PF_KEY portability test.
*
* Also, this is a deliberately single-threaded app, also for portability
* to systems without POSIX threads.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.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 <ipsec_util.h>
static int keysock;
#define MAX_GET_SIZE 1024
/*
* WARN() and ERROR() do the same thing really, with ERROR() the function
* that prints the error buffer needs to be called at the end of a code block
* This will print out all accumulated errors before bailing. The WARN()
* macro calls handle_errors() in such a way that it prints the message
* then continues.
* If the FATAL() macro used call handle_errors() immediately.
*/
#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)
/* Defined as a uint64_t array for alignment purposes. */
/*
* is not NULL then it will contain a copy of the command line that
* append new messages to the existing buffer.
*/
/*PRINTFLIKE1*/
char *
{
char *err_ptr;
char tmp_buff[1024];
int length = 0;
/*
* This is the first error to record, get a
* new buffer, copy in the command line that
*/
Bail("calloc() failed");
}
} else {
}
/* There is a new line character */
length++;
Bail("realloc() failure");
return (err_ptr);
}
/*
* Print usage message.
*/
static void
usage(void)
{
if (!interactive) {
"ipseckey [ -nvp ] | cmd [sa_type] [extfield value]*\n"));
gettext("\tipseckey [ -nvp ] -f infile\n"));
gettext("\tipseckey [ -nvp ] -s outfile\n"));
}
}
/*
* Print out any errors, tidy up as required.
* error pointer ep will be free()'d
*/
void
{
/*
* For now suppress the errors when run from smf(5)
* because potentially sensitive information could
* end up in a publicly readable logfile.
*/
}
if (fatal) {
}
/* reset command buffer */
if (interactive)
} else {
return;
}
} else {
/*
* No errors, if this is the last time that this function
* is called, free(ebuf) and reset command buffer.
*/
if (done) {
}
/* reset command buffer */
if (interactive)
}
return;
}
}
/*
* Initialize a PF_KEY base message.
*/
static void
{
msg->sadb_msg_errno = 0;
/* For starters... */
msg->sadb_msg_reserved = 0;
}
/*
* parseXXX and rparseXXX commands parse input and convert them to PF_KEY
* field values, or do the reverse for the purposes of saving the SA tables.
* (See the save_XXX functions.)
*/
#define CMD_NONE 0
#define CMD_UPDATE 2
#define CMD_ADD 3
#define CMD_DELETE 4
#define CMD_GET 5
#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
/*
* Parse the command.
*/
static int
{
static struct cmdtable {
char *cmd;
int token;
} table[] = {
/*
* Q: Do we want to do GETSPI?
* A: No, it's for automated key mgmt. only. Either that,
* or it isn't relevant until we support non IPsec SA types.
*/
{"update", CMD_UPDATE},
{"add", CMD_ADD},
{"delete", CMD_DELETE},
{"get", CMD_GET},
/*
* Q: And ACQUIRE and REGISTER and EXPIRE?
* A: not until we support non IPsec SA types.
*/
{"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},
};
ct++;
}
/*
* Convert a number from a command line. I picked "u_longlong_t" for the
* number because we need the largest number available. Also, the strto<num>
* calls don't deal in units of uintNN_t.
*/
static u_longlong_t
{
u_longlong_t rc = 0;
" was expecting a number.\n"));
/* NOTREACHED */
}
errno = 0;
if (bail) {
"Expecting a number, not \"%s\"!\n"), num);
} else {
/*
* -1, while not optimal, is sufficiently out of range
* for most of this function's applications when
* we don't just bail.
*/
return ((u_longlong_t)-1);
}
}
return (rc);
}
/*
* Parse and reverse parse a specific SA type (AH, ESP, etc.).
*/
static struct typetable {
char *type;
int token;
} type_table[] = {
{"all", SADB_SATYPE_UNSPEC},
{"ah", SADB_SATYPE_AH},
{"esp", SADB_SATYPE_ESP},
{NULL, 0} /* Token value is irrelevant for this entry. */
};
static int
{
return (SADB_SATYPE_UNSPEC);
tt++;
/*
* New SA types (including ones keysock maintains for user-land
* protocols) may be added, so parse a numeric value if possible.
*/
"Unknown SA type (%s).\n"), type);
}
}
}
#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 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
static struct toktable {
char *string;
int token;
int next;
} tokens[] = {
/* "String", token value, next arg is */
};
/*
* Q: Do I need stuff for proposals, combinations, supported algorithms,
* or SPI ranges?
*
* A: Probably not, but you never know.
*
* Parse out extension header type values.
*/
static int
{
return (TOK_EOF);
break;
/*
* Since the OS controls what extensions are available, we don't have
* to parse numeric values here.
*/
}
/*
* Parse possible state values.
*/
static uint8_t
{
struct states {
char *state;
} states[] = {
{"larval", SADB_SASTATE_LARVAL},
{"mature", SADB_SASTATE_MATURE},
{"dying", SADB_SASTATE_DYING},
{"dead", SADB_SASTATE_DEAD},
{NULL, 0}
};
"was expecting a state.\n");
}
}
return (0);
}
/*
* Return the numerical algorithm identifier corresponding to the specified
* algorithm name.
*/
static uint8_t
{
struct ipsecalgent *algent;
"was expecting an algorithm name.\n"));
}
return (alg_num);
}
/*
* Since algorithms can be loaded during kernel run-time, check for
* numeric algorithm values too. PF_KEY can catch bad ones with EINVAL.
*/
if (proto_num == IPSEC_PROTO_ESP) {
"Unknown encryption algorithm type \"%s\"\n"), alg);
} else {
"Unknown authentication algorithm type \"%s\"\n"), alg);
}
return (0);
}
/*
* Parse and reverse parse out a source/destination ID type.
*/
static struct idtypes {
char *idtype;
} 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
{
/* Shouldn't reach here, see callers for why. */
"was expecting a type.\n"));
}
}
/*
* Since identity types are almost arbitrary, check for numeric
* algorithm values too. PF_KEY can catch bad ones with EINVAL.
*/
return (0);
}
/*
* Parse an address off the command line. Return length of sockaddr,
* and either return a hostent pointer (caller frees). The new
* getipnodebyname() call does the Right Thing (TM), even with
* raw addresses (colon-separated IPv6 or dotted decimal IPv4).
*/
static struct {
char *addtl[2];
} dummy;
static union {
} addr1;
static int
{
int hp_errno;
"was expecting an address.\n"));
}
if (!nflag) {
/*
* Try name->address first. Assume AF_INET6, and
* get IPv4's, plus IPv6's if and only if IPv6 is configured.
* This means to add IPv6 SAs, you must have IPv6
* up-and-running. (AI_DEFAULT works here.)
*/
&hp_errno);
} else {
/*
* Try a normal address conversion only. Use "dummy"
* to construct a fake hostent. Caller will know not
* to free this one.
*/
/*
* Remap to AF_INET6 anyway.
*/
/*
* NOTE: If macro changes to disallow in-place
* conversion, rewhack this.
*/
} else {
}
}
/* Always return sockaddr_in6 for now. */
return (sizeof (struct sockaddr_in6));
}
/*
* Parse a hex character for a key. A string will take the form:
* where
* xxxxxxxxx == a string of hex characters ([0-9][a-f][A-F])
* nn == an optional decimal "mask". If it is not present, it
* is assumed that the hex string will be rounded to the nearest
* byte, where odd nibbles, like 123 will become 0x0123.
*
* NOTE:Unlike the expression of IP addresses, I will not allow an
* excessive "mask". For example 2112/50 is very illegal.
* NOTE2: This key should be in canonical order. Consult your man
* pages per algorithm about said order.
*/
static struct sadb_key *
{
"was expecting a key.\n"));
}
/* Allow hex values prepended with 0x convention */
input += 2;
hexlen++;
if (input[i] == '\0') {
bits = 0;
} else {
/* Have /nn. */
input[i] = '\0';
"\"%s\" is not a bit specifier.\n"),
(input + i + 1));
}
/* hexlen in nibbles */
}
/*
* Adjust hexlen down if user gave us too small of a bit
* count.
*/
"WARNING: Lower bits will be truncated "
}
}
/*
* Allocate. Remember, hexlen is in nibbles.
*/
Bail("malloc(parsekey)");
retval->sadb_key_reserved = 0;
if (bits == 0)
else
/*
* Read in nibbles. Read in odd-numbered as shifted high.
* (e.g. 123 becomes 0x1230).
*/
"string '%s' not a hex value.\n"), input);
break;
}
if (second)
else
break; /* out of for loop. */
key++;
}
/* bzero the remaining bits if we're a non-octet amount. */
if (bits & 0x7)
return (retval);
}
/*
* Write a message to the PF_KEY socket. If verbose, print the message
* heading into the kernel.
*/
static int
{
if (vflag) {
(void) printf(
gettext("VERBOSE ON: Message to kernel looks like:\n"));
(void) printf("==========================================\n");
(void) printf("==========================================\n");
}
}
/*
* SIGALRM handler for time_critical_enter.
*/
static void
{
} else {
"PF_KEY reply message"), signal);
}
/* errx() calls exit. */
}
/*
* Enter a "time critical" section where key is waiting for a return message.
*/
static void
time_critical_enter(void)
{
(void) alarm(TIME_CRITICAL_TIME);
}
/*
* Exit the "time critical" section after getting an appropriate return
* message.
*/
static void
time_critical_exit(void)
{
(void) alarm(0);
}
/*
* Construct a PF_KEY FLUSH message for the SA type specified.
*/
static void
{
int rc;
if (rc == -1)
Bail("write() to PF_KEY socket failed (in doflush)");
do {
if (rc == -1)
Bail("read (in doflush)");
/*
* I should _never_ hit the following unless:
*
* 1. There is a kernel bug.
* 2. There is another process filling in its pid with mine, and
* issuing a different message that would cause a different result.
*/
gettext("doflush: Return message not of type SADB_FLUSH!"));
Bail("doflush: Return message not of type SADB_FLUSH!");
}
if (msg.sadb_msg_errno != 0) {
}
Bail("return message (in doflush)");
}
}
/*
* save_XXX functions are used when "saving" the SA tables to either a
* file or standard output. They use the dump_XXX functions where needed,
* but mostly they use the rparseXXX functions.
*/
/*
* Because "save" and "dump" both use the SADB_DUMP message, fold both
* into the same function.
*/
static void
{
int rc;
gettext("# This key file was generated by the"));
gettext(" ipseckey(1m) command's 'save' feature.\n\n"));
}
if (rc == -1)
Bail("write to PF_KEY socket failed (in dodump)");
do {
/*
* For DUMP, do only the read as a time critical section.
*/
if (rc == -1)
Bail("read (in dodump)");
msg->sadb_msg_seq != 0 &&
msg->sadb_msg_errno == 0) {
nflag);
(void) putchar('\n');
} else {
}
}
if (msg->sadb_msg_errno == 0) {
(void) printf(
gettext("Dump succeeded for SA type %d.\n"),
satype);
} else {
Bail("Dump failed");
}
}
#define SCOPE_UNSPEC 0
#define SCOPE_LINKLOCAL 1
#define SCOPE_SITELOCAL 2
#define SCOPE_GLOBAL 3
#define SCOPE_V4COMPAT 4
static int
{
/* Don't return anything regarding multicast for now... */
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);
/* For now, return global by default. */
return (SCOPE_GLOBAL);
}
/*
* doaddresses():
*
* Used by doaddup() and dodelget() to create new SA's based on the
* provided source and destination addresses hostent.
*
* sadb_msg_type: expected PF_KEY reply message type
* sadb_msg_satype: expected PF_KEY reply satype
* cmd: user command
* srchp: hostent for the source address(es)
* dsthp: hostent for the destination address(es)
* src: points to the SADB source address extension
* dst: points to the SADB destination address extension
* unspec_src: indicates an unspecified source address.
* buffer: pointer to the SADB buffer to use with PF_KEY
* buffer_size: size of buffer
* spi: spi for this message (set by caller)
* srcport: source port if specified
* dstport: destination port if specified
* proto: IP protocol number if specified
* iproto: Inner (tunnel mode) IP protocol number if specified
* NATT note: we are going to assume a semi-sane world where NAT
* boxen don't explode to multiple addresses.
*/
static void
char *ebuf)
{
struct sockaddr_in6 *sin6;
int i, rc;
char **walker; /* For the SRC and PROXY walking functions. */
char *first_match;
/*
* Okay, now we have "src", "dst", and maybe "proxy" reassigned
* to point into the buffer to be written to PF_KEY, we can do
* potentially several writes based on destination address.
*
* First, obtain port numbers from passed-in extensions.
*/
}
}
/*
* The rules for ADD, GET, and UPDATE: (NOTE: This assumes IPsec.
* If other consumers of PF_KEY happen, this will have to be
* rewhacked.):
*
* Do a message for every possible DST address.
*
* If a source or proxy address explodes, keep unspecified
* (and mention unspecified).
*
* If dsthp is == dummy.he, then go through the loop once.
* If any other hp is == dummy.he, then you don't have to apply any
* silly rules.
*
* DELETE is different, because you can leave either "src" or "dst"
* blank! You need to explode if one of them is full, and not assume
* that the other is set.
*/
/*
* No destination address specified.
* With extended diagnostics, we don't have to bail the
* non-DELETE cases here. The EINVAL diagnostics will be
* enough to inform the user(s) what happened.
*/
i = 0;
do {
/* Just to be sure... */
/* Degenerate case, h_addr_list[0] == NULL. */
Bail("Empty source address list");
/*
* Fill in the src sockaddr.
*/
sizeof (struct in6_addr));
}
/* Save off a copy for later writing... */
if (rc == -1)
Bail("write() to PF_KEY socket "
"(in doaddresses)");
do {
if (rc == -1)
Bail("read (in doaddresses)");
"doaddresses: Unexpected returned message "
Bail("doaddresses: Unexpected returned "
"message");
}
if (errno != 0) {
"One of the entered "
"values is incorrect."));
} else {
Bail("return message (in doaddresses)");
}
}
/* ...and then restore the saved buffer. */
return;
}
/* Just to be sure... */
} else {
/*
* Fill in the dst sockaddr.
*/
sizeof (struct in6_addr));
}
/*
* Try and assign src, if there's any ambiguity.
*/
/*
* IPv4 address. Find an IPv4 address, then
* keep looking for a second one. If a second
* exists, print a message, and fill in the
* unspecified address.
*/
first_match = NULL;
/* LINTED E_BAD_PTR_CAST_ALIGN */
if (IN6_IS_ADDR_V4MAPPED(
if (first_match != NULL)
break;
else
first_match = *walker;
}
}
if (first_match == NULL) {
/*
* No IPv4 hits. Is this a single
* dest?
*/
"No IPv4 source address "
if (single_dst) {
"Only single destination "
"IP address.\n"));
} else {
/* Continue, but do I print? */
continue; /* for loop */
}
/* I should never reach here. */
}
/*
* Early loop exit. It must've been
* multiple hits...
*
* Issue a null-source warning?
*/
"Multiple IPv4 source addresses "
"for %s, using unspecified source "
} else {
/*
* If I reach here w/o hitting the
* previous if statements, I have a
* single source address for this
* destination.
*/
sizeof (struct in6_addr));
}
} else {
/*
* IPv6 address. Find an IPv6 address.
* Unlike IPv4 addresses, things can get a
* little more sticky with scopes, etc.
*/
first_match = NULL;
/* LINTED E_BAD_PTR_CAST_ALIGN */
if (!IN6_IS_ADDR_V4MAPPED(
/*
* Set first-match, etc.
* Take into account scopes,
* and other IPv6 thingies.
*/
/* LINTED E_BAD_PTR_CAST */
if (src_scope == SCOPE_UNSPEC ||
if (first_match !=
NULL)
break;
else
*walker;
}
}
}
if (first_match == NULL) {
/*
* No IPv6 hits. Is this a single
* dest?
*/
"No IPv6 source address of "
"matching scope for name %s.\n"),
if (single_dst) {
"Only a single IPV6 "
"destination "
"address.\n"));
} else {
/* Continue, but do I print? */
continue; /* for loop */
}
/* I should never reach here. */
}
/*
* Early loop exit. Issue a
* null-source warning?
*/
"Multiple IPv6 source addresses "
"for %s of the same scope, using "
"unspecified source instead.\n"),
} else {
/*
* If I reach here w/o hitting the
* previous if statements, I have a
* single source address for this
* destination.
*/
sizeof (struct in6_addr));
}
}
}
/*
* If there are errors at this point there is no
* point sending anything to PF_KEY.
*/
/* Save off a copy for later writing... */
if (rc == -1)
Bail("write() to PF_KEY socket (in doaddresses)");
/* Blank the key for paranoia's sake. */
do {
if (rc == -1)
Bail("read (in doaddresses)");
/*
* I should _never_ hit the following unless:
*
* 1. There is a kernel bug.
* 2. Another process is mistakenly using my pid in a PF_KEY
* message.
*/
"doaddresses: Unexpected returned message "
Bail("doaddresses: Unexpected returned message");
}
if (msgp->sadb_msg_errno != 0) {
char addrprint[INET6_ADDRSTRLEN];
int on_errno = 0;
char *on_errno_msg;
/*
* Print different error messages depending
* on the SADB message type being processed.
* messages, we report that the SA does not
* exist. If we get a EEXIST error for a
* SA already exists.
*/
if (sadb_msg_type == SADB_GET ||
sadb_msg_type == SADB_DELETE) {
on_errno_msg = "does not exist";
} else if (sadb_msg_type == SADB_ADD ||
sadb_msg_type == SADB_UPDATE) {
on_errno_msg = "already exists";
}
"Association (type = %s) "
"with spi 0x%x and addr\n"),
continue;
} else {
"PF_KEY Diagnostic code %u: %s.\n"),
} else {
Bail("return message (in doaddresses)");
}
}
}
"SA information bigger than %d bytes.\n"),
}
}
/* ...and then restore the saved buffer. */
lines_added++;
}
/* Degenerate case, h_addr_list[0] == NULL. */
if (i == 0)
Bail("Empty destination address list");
/*
* free(ebuf) even if there are no errors.
* handle_errors() won't return here.
*/
}
/*
* Perform an add or an update. ADD and UPDATE are similar in the extensions
* they need.
*/
static void
{
struct sockaddr_in6 *sin6;
/* MLS TODO: Need sensitivity eventually. */
/* Assume last element in argv is set to NULL. */
do {
argv++;
switch (token) {
case TOK_EOF:
/* Do nothing, I'm done. */
break;
case TOK_UNKNOWN:
break;
case TOK_SPI:
case TOK_REPLAY:
case TOK_STATE:
case TOK_AUTHALG:
case TOK_ENCRALG:
case TOK_ENCAP:
/*
* May want to place this chunk of code in a function.
*
* This code checks for duplicate entries on a command
* line.
*/
/* Allocate the SADB_EXT_SA extension. */
Bail("malloc(assoc)");
assoc->sadb_sa_len =
SADB_8TO64(sizeof (*assoc));
}
switch (token) {
case TOK_SPI:
/*
* can type in another SPI.
*/
if (assoc->sadb_sa_spi != 0) {
"Can only specify "
"single SPI value.\n"));
break;
}
/* Must convert SPI to network order! */
assoc->sadb_sa_spi =
ebuf));
if (assoc->sadb_sa_spi == 0) {
"Invalid SPI value \"0\" .\n"));
}
break;
case TOK_REPLAY:
/*
* That same cretin can do the same with
* replay.
*/
if (assoc->sadb_sa_replay != 0) {
"Can only specify "
"single replay window size.\n"));
break;
}
if (assoc->sadb_sa_replay != 0) {
"WARNING: Replay with manual"
" keying considered harmful.\n"));
}
break;
case TOK_STATE:
/*
* 0 is an actual state value, LARVAL. This
* means that one can type in the larval state
* and then type in another state on the same
* command line.
*/
if (assoc->sadb_sa_state != 0) {
"Can only specify "
"single SA state.\n"));
break;
}
ebuf);
break;
case TOK_AUTHALG:
if (assoc->sadb_sa_auth != 0) {
"Can only specify "
"single auth algorithm.\n"));
break;
}
break;
case TOK_ENCRALG:
if (satype == SADB_SATYPE_AH) {
" encryption with SA type ah.\n"));
break;
}
if (assoc->sadb_sa_encrypt != 0) {
"Can only specify "
"single encryption algorithm.\n"));
break;
}
break;
case TOK_ENCAP:
if (use_natt) {
"Can only specify single"
" encapsulation.\n"));
break;
}
"Can only specify udp"
" encapsulation.\n"));
break;
}
/* set assoc flags later */
break;
}
argv++;
break;
case TOK_SRCPORT:
if (srcport != 0) {
"single source port.\n"));
break;
}
argv++;
break;
case TOK_DSTPORT:
if (dstport != 0) {
"single destination port.\n"));
break;
}
argv++;
break;
case TOK_ISRCPORT:
if (isrcport != 0) {
"Can only specify "
"single inner-source port.\n"));
break;
}
argv++;
break;
case TOK_IDSTPORT:
if (idstport != 0) {
"Can only specify "
"single inner-destination port.\n"));
break;
}
argv++;
break;
case TOK_NATLPORT:
if (natt_lport != 0) {
"Can only specify "
"single NAT-T local port.\n"));
break;
}
if (natt_rport != 0) {
"Can only specify "
"one of NAT-T remote and local port.\n"));
break;
}
argv++;
break;
case TOK_NATRPORT:
if (natt_rport != 0) {
"Can only specify "
"single NAT-T remote port.\n"));
break;
}
if (natt_lport != 0) {
"Can only specify "
"one of NAT-T remote and local port.\n"));
break;
}
argv++;
break;
case TOK_PROTO:
if (proto != 0) {
"Can only specify "
"single protocol.\n"));
break;
}
argv++;
break;
case TOK_IPROTO:
if (iproto != 0) {
"Can only specify "
"single inner protocol.\n"));
break;
}
argv++;
break;
case TOK_SRCADDR:
case TOK_SRCADDR6:
"Can only specify "
"single source address.\n"));
break;
}
"Unknown src address \"%s\"\n"), *argv);
break;
}
argv++;
/*
* Round of the sockaddr length to an 8 byte
* boundary to make PF_KEY happy.
*/
Bail("malloc(src)");
src->sadb_address_reserved = 0;
src->sadb_address_prefixlen = 0;
src->sadb_address_proto = 0;
/*
* Single address with -n flag.
*/
sizeof (struct in6_addr));
}
break;
case TOK_DSTADDR:
case TOK_DSTADDR6:
"Can only specify single "
"destination address.\n"));
break;
}
"Unknown dst address \"%s\"\n"), *argv);
break;
}
argv++;
Bail("malloc(dst)");
dst->sadb_address_reserved = 0;
dst->sadb_address_prefixlen = 0;
dst->sadb_address_proto = 0;
/*
* Single address with -n flag.
*/
sizeof (struct in6_addr));
}
break;
case TOK_PROXYADDR:
case TOK_PROXYADDR6:
"Can only specify single "
"proxy/inner-source address.\n"));
break;
}
/* Parse out the prefix. */
errno = 0;
if (errno != 0) {
"Invalid prefix %s."), pstr);
break;
}
/* Recycle pstr */
Bail("malloc(pstr)");
}
} else {
/*
* Assume mapping to AF_INET6, and we're a host.
* XXX some miscreants may still make classful
* assumptions. If this is a problem, fix it
* here.
*/
prefix = 128;
}
"Unknown proxy/inner-source address "
"\"%s\"\n"), *argv);
break;
}
argv++;
Bail("malloc(isrc)");
isrc->sadb_address_reserved = 0;
isrc->sadb_address_proto = 0;
/*
* Single address with -n flag or single name.
*/
sizeof (struct in6_addr));
/*
* normalize prefixlen for IPv4-mapped
* addresses.
*/
if (prefix <= 32 &&
} else {
/*
* bother.
*/
"Proxy/inner-source address %s "
break;
}
break;
case TOK_IDSTADDR:
case TOK_IDSTADDR6:
"Can only specify single "
"inner-destination address.\n"));
break;
}
/* Parse out the prefix. */
errno = 0;
if (errno != 0) {
"Invalid prefix %s.\n"), pstr);
break;
}
/* Recycle pstr */
Bail("malloc(pstr)");
}
} else {
/*
* Assume mapping to AF_INET6, and we're a host.
* XXX some miscreants may still make classful
* assumptions. If this is a problem, fix it
* here.
*/
prefix = 128;
}
"Unknown Inner Src address "
" \"%s\"\n"), *argv);
break;
}
argv++;
Bail("malloc(idst)");
idst->sadb_address_reserved = 0;
idst->sadb_address_proto = 0;
/*
* Single address with -n flag or single name.
*/
sizeof (struct in6_addr));
/*
* normalize prefixlen for IPv4-mapped
* addresses.
*/
if (prefix <= 32 &&
} else {
/*
* If the idst address is vague, don't bother.
*/
"Inner destination address %s "
break;
}
break;
case TOK_NATLOC:
if (natt_local != NULL) {
"Can only specify "
"single NAT-T local address.\n"));
break;
}
"Unknown NAT-T local address \"%s\"\n"),
*argv);
break;
}
argv++;
/*
* Round of the sockaddr length to an 8 byte
* boundary to make PF_KEY happy.
*/
if (natt_local == NULL)
Bail("malloc(natt_local)");
natt_local->sadb_address_proto = 0;
/*
* Single address with -n flag or single name.
*/
} else {
/*
* If the nat-local address is vague, don't
* bother.
*/
natt_local = NULL;
"NAT-T local address %s "
"is vague, not using.\n"),
break;
}
break;
case TOK_NATREM:
if (natt_remote != NULL) {
"Can only specify "
"single NAT-T remote address.\n"));
break;
}
"Unknown NAT-T remote address \"%s\"\n"),
*argv);
break;
}
argv++;
/*
* Round of the sockaddr length to an 8 byte
* boundary to make PF_KEY happy.
*/
if (natt_remote == NULL)
Bail("malloc(natt_remote)");
/*
* Single address with -n flag or single name.
*/
} else {
/*
* If the nat-renote address is vague, don't
* bother.
*/
natt_remote = NULL;
"NAT-T remote address %s "
"is vague, not using.\n"),
break;
}
break;
case TOK_ENCRKEY:
"Can only specify "
"single encryption key.\n"));
break;
}
"Cannot specify a key with NULL "
"encryption algorithm.\n"));
break;
}
argv++;
"Invalid encryption key.\n"));
break;
}
break;
case TOK_AUTHKEY:
"Can only specify single"
" authentication key.\n"));
break;
}
argv++;
"Invalid authentication key.\n"));
break;
}
break;
case TOK_SRCIDTYPE:
"Unexpected end of command "
"line - Expecting Src Type.\n"));
/* NOTREACHED */
break;
}
"Can only specify single"
" source certificate identity.\n"));
break;
}
Bail("malloc(srcid)");
argv++;
srcid->sadb_ident_reserved = 0;
argv++;
break;
case TOK_DSTIDTYPE:
"Unexpected end of command"
" line - expecting dst type.\n"));
break;
}
"Can only specify single destination "
"certificate identity.\n"));
break;
}
Bail("malloc(dstid)");
argv++;
dstid->sadb_ident_reserved = 0;
argv++;
break;
case TOK_HARD_ALLOC:
case TOK_HARD_BYTES:
case TOK_HARD_ADDTIME:
case TOK_HARD_USETIME:
Bail("malloc(hard_lifetime)");
SADB_8TO64(sizeof (*hard));
}
switch (token) {
case TOK_HARD_ALLOC:
if (hard->sadb_lifetime_allocations != 0) {
"Can only specify single"
" hard allocation limit.\n"));
break;
}
break;
case TOK_HARD_BYTES:
if (hard->sadb_lifetime_bytes != 0) {
"Can only specify "
"single hard byte limit.\n"));
break;
}
break;
case TOK_HARD_ADDTIME:
if (hard->sadb_lifetime_addtime != 0) {
"Can only specify "
"single past-add lifetime.\n"));
break;
}
break;
case TOK_HARD_USETIME:
if (hard->sadb_lifetime_usetime != 0) {
"Can only specify "
"single past-use lifetime.\n"));
break;
}
break;
}
argv++;
break;
case TOK_SOFT_ALLOC:
case TOK_SOFT_BYTES:
case TOK_SOFT_ADDTIME:
case TOK_SOFT_USETIME:
Bail("malloc(soft_lifetime)");
SADB_8TO64(sizeof (*soft));
}
switch (token) {
case TOK_SOFT_ALLOC:
if (soft->sadb_lifetime_allocations != 0) {
"Can only specify single"
" soft allocation limit.\n"));
break;
}
break;
case TOK_SOFT_BYTES:
if (soft->sadb_lifetime_bytes != 0) {
"Can only specify single"
" soft byte limit.\n"));
break;
}
break;
case TOK_SOFT_ADDTIME:
if (soft->sadb_lifetime_addtime != 0) {
"Can only specify single"
" past-add lifetime.\n"));
break;
}
break;
case TOK_SOFT_USETIME:
if (soft->sadb_lifetime_usetime != 0) {
"Can only specify single"
" past-use lifetime.\n"));
break;
}
break;
}
argv++;
break;
default:
*(argv - 1));
break;
}
/*
* If we specify inner ports w/o addresses, we still need to
* allocate. Also, if we have one inner address, we need the
* other, even if we don't specify anything.
*/
/* Allocate zeroed-out. */
Bail("malloc(implicit idst)");
}
}
/* Allocate zeroed-out. */
Bail("malloc(implicit isrc)");
}
}
/*
* Okay, so now I have all of the potential extensions!
* Allocate a single contiguous buffer. Keep in mind that it'll
* be enough because the key itself will be yanked.
*/
/*
* Set explicit unspecified source address.
*/
unspec_src = B_TRUE;
Bail("malloc(implicit src)");
/* Confusing, but we're copying from DST to SRC. :) */
}
if (assoc->sadb_sa_spi == 0) {
"The SPI value is missing for "
"the association you wish to %s.\n"), thiscmd);
}
"Select at least one algorithm "
"for this add.\n"));
}
/* Hack to let user specify NULL ESP implicitly. */
assoc->sadb_sa_encrypt == 0)
/* 0 is an actual value. Print a warning if it was entered. */
if (assoc->sadb_sa_state == 0) {
if (readstate) {
"WARNING: Cannot set LARVAL SA state.\n"));
}
}
if (use_natt) {
if (natt_remote != NULL)
if (natt_local != NULL)
}
if (alloc_inner) {
/*
* For now, assume RFC 3884's dream of transport-mode
* SAs with inner IP address selectors will not
* happen.
*/
proto != IPPROTO_IPV6) {
"WARNING: Protocol type %d not "
"for use with Tunnel-Mode SA.\n"), proto);
/* Continue and let PF_KEY scream... */
}
}
/* Save the SPI for the case of an error. */
} else {
"Need SA parameters for %s.\n"), thiscmd);
}
}
}
"Must have at least one key for an add.\n"));
}
}
}
}
}
} else {
"Need destination address for %s.\n"), thiscmd);
}
if (use_natt) {
"Must specify NAT-T remote or local address "
"for UDP encapsulation.\n"));
}
"If NAT-T local port is specified, NAT-T "
"local address must also be specified.\n"));
}
"If NAT-T remote port is specified, NAT-T "
"remote address must also be specified.\n"));
}
if (natt_remote != NULL) {
}
if (natt_local != NULL) {
}
}
/*
* PF_KEY requires a source address extension, even if the source
* address itself is unspecified. (See "Set explicit unspecified..."
* code fragment above. Destination reality check was above.)
*/
}
}
if (cflag) {
/*
* Assume the checked cmd would have worked if it was actually
* used. doaddresses() will increment lines_added if it
* succeeds.
*/
lines_added++;
} else {
}
}
/*
* DELETE and GET are similar, in that they only need the extensions
* required to _find_ an SA, and then either delete it or obtain its
* information.
*/
static void
{
char *thiscmd;
struct sockaddr_in6 *sin6;
/* Set the first extension header to right past the base message. */
/* Assume last element in argv is set to NULL. */
do {
argv++;
switch (token) {
case TOK_EOF:
/* Do nothing, I'm done. */
break;
case TOK_UNKNOWN:
break;
case TOK_SPI:
"Can only specify single SPI value.\n"));
break;
}
argv++;
break;
case TOK_SRCPORT:
if (srcport != 0) {
"Can only specify single source port.\n"));
break;
}
argv++;
break;
case TOK_DSTPORT:
if (dstport != 0) {
"Can only "
"specify single destination port.\n"));
break;
}
argv++;
break;
case TOK_PROTO:
if (proto != 0) {
"Can only specify single protocol.\n"));
break;
}
argv++;
break;
case TOK_SRCADDR:
case TOK_SRCADDR6:
"Can only specify single source addr.\n"));
break;
}
"Unknown source address \"%s\"\n"), *argv);
break;
}
argv++;
/*
* Single address with -n flag.
*/
sizeof (struct in6_addr));
}
/* The rest is pre-bzeroed for us. */
break;
case TOK_DSTADDR:
case TOK_DSTADDR6:
"Can only specify single destination "
"address.\n"));
break;
}
"Unknown destination address \"%s\"\n"),
*argv);
break;
}
argv++;
/*
* Single address with -n flag.
*/
sizeof (struct in6_addr));
}
/* The rest is pre-bzeroed for us. */
break;
default:
"Don't use extension %s for '%s' command.\n"),
break;
}
}
}
/* So I have enough of the message to send it down! */
"Need SA parameters for %s.\n"), thiscmd);
}
if (cflag) {
/*
* Assume the checked cmd would have worked if it was actually
* used. doaddresses() will increment lines_added if it
* succeeds.
*/
lines_added++;
} else {
}
}
/*
* "ipseckey monitor" should exit very gracefully if ^C is tapped.
*/
static void
monitor_catch(int signal)
{
}
/*
* Loop forever, listening on PF_KEY messages.
*/
static void
{
int rc;
/* Catch ^C. */
if (!passive) {
if (rc == -1)
Bail("write (SADB_X_PROMISC)");
} else {
}
for (; ; ) {
/*
* I assume that read() is non-blocking, and will never
* return 0.
*/
if (rc == -1)
Bail("read (in domonitor)");
/*
* Q: Should I use the same method of printing as GET does?
* A: For now, yes.
*/
(void) putchar('\n');
}
}
/*
* Either mask or unmask all relevant signals.
*/
static void
{
if (unmask) {
} else {
(void) sigfillset(&set);
}
}
/*
* Assorted functions to print help text.
*/
static void
{
int i;
puts_tr("\nSA attributes:");
if (i%3 == 0)
(void) printf("\n");
}
(void) printf("\n");
}
static void
{
int cmd;
doattrhelp();
return;
}
switch (cmd) {
case CMD_UPDATE:
puts_tr("update - Update an existing SA");
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_GET:
puts_tr("get - Display an SA");
break;
case CMD_FLUSH:
puts_tr("flush - Delete all SAs");
break;
case CMD_DUMP:
puts_tr("dump - Display all SAs");
break;
case CMD_MONITOR:
puts_tr("monitor - Monitor all PF_KEY reply messages.");
break;
case CMD_PMONITOR:
"pmonitor, passive_monitor - Monitor PF_KEY messages that");
" 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:
break;
}
}
static void
{
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");
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("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");
}
/*
* "Parse" a command line from argv.
*/
static void
{
if (argc == 0)
return;
/*
* Some commands loop forever and should only be run from the command
* line, they should never be run from a command file as this may
* be used at boot time.
*/
switch (cmd) {
case CMD_HELP:
if (read_cmdfile)
"config file."));
else
return;
case CMD_MONITOR:
if (read_cmdfile)
"config file."));
else
break;
case CMD_PMONITOR:
if (read_cmdfile)
"config file."));
else
break;
case CMD_QUIT:
}
if (satype != SADB_SATYPE_UNSPEC) {
argv++;
} else {
/*
* You must specify either "all" or a specific SA type
* for the "save" command.
*/
"Must specify a specific "
"SA type for save.\n"));
} else {
argv++;
}
}
switch (cmd) {
case CMD_FLUSH:
if (!cflag)
/*
* If this was called because of an entry in a cmd file
* then this action needs to be counted to prevent
* do_interactive() treating this as an error.
*/
lines_added++;
break;
case CMD_ADD:
case CMD_UPDATE:
/*
* NOTE: Shouldn't allow ADDs or UPDATEs with keying material
* from the command line.
*/
if (!interactive) {
"can't do ADD or UPDATE from the command line.\n"));
}
if (satype == SADB_SATYPE_UNSPEC) {
"Must specify a specific SA type."));
/* NOTREACHED */
}
/* Parse for extensions, including keying material. */
break;
case CMD_DELETE:
case CMD_GET:
if (satype == SADB_SATYPE_UNSPEC) {
"Must specify a single SA type."));
/* NOTREACHED */
}
/* Parse for bare minimum to locate an SA. */
break;
case CMD_DUMP:
if (read_cmdfile)
"config file."));
else
break;
case CMD_SAVE:
if (read_cmdfile) {
"config file."));
} else {
}
break;
default:
usage();
}
}
int
{
int ch;
char *configfile = NULL;
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
/*
* Check to see if the command is being run from smf(5).
*/
if (getuid() != 0) {
}
/* umask me to paranoid, I only want to create files read-only */
switch (ch) {
case 'p':
break;
case 'n':
break;
case 'v':
break;
case 'c':
/* FALLTHRU */
case 'f':
if (dosave)
usage();
EXIT_BADCONFIG2("Unable to open configuration "
"file: %s\n", optarg);
}
break;
case 's':
if (readfile)
usage();
break;
default:
usage();
}
if (keysock == -1) {
EXIT_BADPERM("Insufficient privileges to open "
"PF_KEY socket.\n");
} else {
/* some other reason */
EXIT_FATAL("Opening PF_KEY socket");
}
}
if (dosave) {
}
/*
* When run from smf(5) flush any existing SA's first
* otherwise you will end up in maintenance mode.
*/
"Flushing existing SA's before adding new SA's\n"));
}
/* Go into interactive mode here. */
parseit);
}
return (0);
}