/*
*
* 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
*/
/*
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <strings.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <net/pfpolicy.h>
#include <libintl.h>
#include <setjmp.h>
#include <libgen.h>
#include <libscf.h>
#include "ipsec_util.h"
#include "ikedoor.h"
/*
* This file contains support functions that are shared by the ipsec
* utilities and daemons including ipseckey(1m), ikeadm(1m) and in.iked(1m).
*/
/* Limits for interactive mode. */
/*
* Print errno and exit if cmdline or readfile, reset state if interactive
* The error string *what should be dgettext()'d before calling bail().
*/
void
{
if (errno != 0)
else
if (readfile) {
return;
}
if (interactive && !readfile)
}
/*
* Print caller-supplied variable-arg error msg, then exit if cmdline or
* readfile, or reset state if interactive.
*/
/*PRINTFLIKE1*/
void
{
if (readfile)
else
if (interactive && !readfile)
}
/*
* bytecnt2str() wrapper. Zeroes out the input buffer and if the number
* of bytes to be converted is more than 1K, it will produce readable string
* in parentheses, store it in the original buffer and return the pointer to it.
* Maximum length of the returned string is 14 characters (not including
* the terminating zero).
*/
char *
{
char *str;
if (num > 1024) {
/* Return empty string in case of out-of-memory. */
return (buf);
/* Detect overflow. */
return (buf);
}
/* Emit nothing in case of overflow. */
}
return (buf);
}
/*
* Convert 64-bit number to human readable string. Useful mainly for the
* byte lifetime counters. Returns pointer to the user supplied buffer.
* Able to convert up to Exabytes. Maximum length of the string produced
* is 9 characters (not counting the terminating zero).
*/
char *
{
char u;
int index = 0;
while (n >= 1024) {
n /= 1024;
index++;
}
/* The field has all units this function can represent. */
u = " KMGTPE"[index];
if (index == 0) {
/* Less than 1K */
} else {
/* Otherwise display 2 precision digits. */
}
return (buf);
}
/*
* secs2str() wrapper. Zeroes out the input buffer and if the number of
* seconds to be converted is more than minute, it will produce readable
* string in parentheses, store it in the original buffer and return the
* pointer to it.
*/
char *
{
char *str;
if (secs > 60) {
/* Return empty string in case of out-of-memory. */
return (buf);
/* Detect overflow. */
return (buf);
}
/* Emit nothing in case of overflow. */
}
return (buf);
}
/*
* Convert number of seconds to human readable string. Useful mainly for
* the lifetime counters. Returns pointer to the user supplied buffer.
* Able to convert up to days.
*/
char *
{
val /= 86400;
unit = "day";
unit = "hour";
} else if (val >= 60) {
val /= 60;
unit = "minute";
}
/* Emit nothing in case of overflow. */
return (buf);
}
/*
* dump_XXX functions produce ASCII output from various structures.
*
* Because certain errors need to do this to stderr, dump_XXX functions
* take a FILE pointer.
*
* If an error occured while writing to the specified file, these
* functions return -1, zero otherwise.
*/
int
{
/* Add 4 chars to hold '/nnn' for prefixes. */
case AF_INET:
/* LINTED E_BAD_PTR_CAST_ALIGN */
protocol = "AF_INET";
break;
case AF_INET6:
/* LINTED E_BAD_PTR_CAST_ALIGN */
protocol = "AF_INET6";
break;
default:
return (0);
}
NULL) {
} else {
if (prefixlen != 0) {
sizeof (storage));
}
}
if (addr_only) {
return (-1);
} else {
"%s: port %d, %s"), protocol,
return (-1);
if (ignore_nss == B_FALSE) {
/*
* Do AF_independent reverse hostname lookup here.
*/
if (unspec) {
" <unspecified>")) < 0)
return (-1);
} else {
return (-1);
} else {
" <unknown>")) < 0)
return (-1);
}
}
}
return (-1);
}
return (0);
}
/*
* Dump a key, any salt and bitlen.
* The key is made up of a stream of bits. If the algorithm requires a salt
* value, this will also be part of the dumped key. The last "saltbits" of the
* key string, reading left to right will be the salt value. To make it easier
* to see which bits make up the key, the salt value is enclosed in []'s.
* This function can also be called when ipseckey(1m) -s is run, this "saves"
* the SAs, including the key to a file. When this is the case, the []'s are
* not printed.
*
* The implementation allows the kernel to be told about the length of the salt
* in whole bytes only. If this changes, this function will need to be updated.
*/
int
{
/* The & 0x7 is to check for leftover bits. */
if ((bitlen & 0x7) != 0)
numbytes++;
while (numbytes-- != 0) {
if (pflag) {
/* Print no keys if paranoid */
return (-1);
} else {
return (-1);
}
if (separate_salt && saltbytes != 0 &&
return (-1);
}
}
if (separate_salt && saltbits != 0) {
return (-1);
} else {
return (-1);
}
return (0);
}
/*
* Print an authentication or encryption algorithm
*/
static int
{
"<unknown %u>"), alg_num) < 0)
return (-1);
return (0);
}
/*
* Special-case <none> for backward output compat.
* Assume that SADB_AALG_NONE == SADB_EALG_NONE.
*/
if (alg_num == SADB_AALG_NONE) {
return (-1);
} else {
return (-1);
}
return (0);
}
int
{
}
int
{
}
/*
* Print an SADB_IDENTTYPE string
*
* Also return TRUE if the actual ident may be printed, FALSE if not.
*
* If rc is not NULL, set its value to -1 if an error occured while writing
* to the specified file, zero otherwise.
*/
{
int rc_val = 0;
switch (idtype) {
case SADB_IDENTTYPE_PREFIX:
rc_val = -1;
break;
case SADB_IDENTTYPE_FQDN:
rc_val = -1;
break;
case SADB_IDENTTYPE_USER_FQDN:
rc_val = -1;
break;
case SADB_X_IDENTTYPE_DN:
rc_val = -1;
break;
case SADB_X_IDENTTYPE_GN:
rc_val = -1;
break;
case SADB_X_IDENTTYPE_KEY_ID:
rc_val = -1;
break;
rc_val = -1;
break;
default:
"<unknown %u>"), idtype) < 0)
rc_val = -1;
break;
}
return (canprint);
}
/*
*/
static int
{
char **current;
return (MEMORY_ALLOCATION);
if (inquotes) {
continue;
}
*ibuf = '\0';
current++;
/* Regrow ***thisargv. */
if (argvlen == TOO_MANY_ARGS) {
return (TOO_MANY_TOKENS);
}
/* Double the allocation. */
sizeof (char *) * (argvlen << 1));
return (MEMORY_ALLOCATION);
}
}
}
} else {
if (firstchar) {
return (COMMENT_LINE);
}
}
if (*ibuf == QUOTE_CHAR) {
if (inquotes) {
*ibuf = '\0';
} else {
}
continue;
}
(*newargc)++;
}
}
}
/*
* Tricky corner case...
* I've parsed _exactly_ the amount of args as I have space. It
* won't return NULL-terminated, and bad things will happen to
* the caller.
*/
return (MEMORY_ALLOCATION);
}
}
return (SUCCESS);
}
/*
* init interactive mode if needed and not yet initialized
*/
static void
{
MAX_CMD_HIST)) == NULL)
"tecla initialization failed"));
match_fn) != 0) {
(void) del_GetLine(gl);
"tab completion failed to initialize"));
}
/*
* In interactive mode we only want to terminate
* when explicitly requested (e.g. by a command).
*/
}
} else {
}
}
/*
* free tecla data structure
*/
static void
fini_interactive(void)
{
(void) del_GetLine(gl);
}
/*
* Get single input line, wrapping around interactive and non-interactive
* mode.
*/
static char *
{
char *line;
/*
* If the user hits ^C then we want to catch it and
* start over. If the user hits EOF then we want to
* bail out.
*/
goto once_again;
} else {
"Line too long (max=%d chars)"),
}
}
return (line);
}
/*
* Enter a mode where commands are read from a file. Treat stdin special.
*/
void
{
int thisargc;
char *s;
/* panics for us */
if (readfile)
lineno++;
thisargc = 0;
/*
* Check byte IBUF_SIZE - 2, because byte IBUF_SIZE - 1 will
* be null-terminated because of fgets().
*/
/* do_getstr() issued a warning already */
continue;
} else {
"Line %d too big."), lineno);
}
}
if (!continue_in_progress) {
/* Use -2 because of \n from fgets. */
/*
* Can use strcpy here, I've checked the
* length already.
*/
/* Remove the CONT_CHAR from the string. */
continue;
}
} else {
/* Handle continuations... */
"Command buffer overrun."));
}
/* Use - 2 because of \n from fgets. */
/* Remove the CONT_CHAR from the string. */
continue;
} else {
/*
* I've already checked the length...
*/
}
}
/*
* Just in case the command fails keep a copy of the
* command buffer for diagnostic output.
*/
if (readfile) {
/*
* The error buffer needs to be big enough to
* hold the longest command string, plus
* some extra text, see below.
*/
"Memory allocation error."));
} else {
"Config file entry near line %u "
"caused error(s) or warnings:\n\n%s\n\n"),
}
}
case TOO_MANY_TOKENS:
break;
case MEMORY_ALLOCATION:
break;
case COMMENT_LINE:
/* Comment line. */
break;
default:
if (thisargc != 0) {
lines_parsed++;
/* ebuf consumed */
} else {
}
}
break;
}
}
/*
* The following code is ipseckey specific. This should never be
* used by ikeadm which also calls this function because ikeadm
* only runs interactively. If this ever changes this code block
* sould be revisited.
*/
if (readfile) {
if (lines_parsed != 0 && lines_added == 0) {
"contain any valid SAs"));
}
/*
* There were errors. Putting the service in maintenance mode.
* When svc.startd(1M) allows services to degrade themselves,
* this should be revisited.
*
* If this function was called from a program running as a
* smf_method(5), print a warning message. Don't spew out the
* errors as these will end up in the smf(5) log file which is
* publically readable, the errors may contain sensitive
* information.
*/
"The configuration file contained %d "
"errors.\n"
"Manually check the configuration with:\n"
"ipseckey -c %s\n"
"Use svcadm(1M) to clear maintenance "
"condition when errors are resolved.\n"),
} else {
}
} else {
"%d actions successfully processed."),
}
} else {
/* no newline upon Ctrl-D */
if (s != NULL)
(void) putchar('\n');
}
}
/*
* Functions to parse strings that represent a debug or privilege level.
* If this file evolves into a common library that may be used by in.iked
* as well as the usr.sbin utilities, those duplicate functions should be
* deleted.
*
* A privilege level may be represented by a simple keyword, corresponding
* to one of the possible levels. A debug level may be represented by a
* series of keywords, separated by '+' or '-', indicating categories to
* be added or removed from the set of categories in the debug level.
* For example, +all-op corresponds to level 0xfffffffb (all flags except
* for D_OP set); while p1+p2+pfkey corresponds to level 0x38. Note that
* the leading '+' is implicit; the first keyword in the list must be for
* a category that is to be added.
*
* These parsing functions make use of a local version of strtok, strtok_d,
* which includes an additional parameter, char *delim. This param is filled
* in with the character which ends the returned token. In other words,
* this version of strtok, in addition to returning the token, also returns
* the single character delimiter from the original string which marked the
* end of the token.
*/
static char *
{
static char *lasts;
char *q, *r;
/* first or subsequent call */
if (string == 0) /* return if no tokens remaining */
return (NULL);
if (*q == '\0') /* return if no tokens remaining */
return (NULL);
lasts = 0; /* indicate that this is last token */
} else {
*delim = *r; /* save delimitor */
*r = '\0';
lasts = r + 1;
}
return (q);
}
{ IKE_PRIV_MINIMUM, "base" },
{ IKE_PRIV_MODKEYS, "modkeys" },
{ IKE_PRIV_KEYMAT, "keymat" },
{ IKE_PRIV_MINIMUM, "0" },
};
int
{
char *endp;
int priv;
}
if (*endp == '\0')
return (priv);
return (-1);
}
{ D_CERT, "cert" },
{ D_KEY, "key" },
{ D_OP, "op" },
{ D_P1, "p1" },
{ D_P1, "phase1" },
{ D_P2, "p2" },
{ D_P2, "phase2" },
{ D_PFKEY, "pfkey" },
{ D_POL, "pol" },
{ D_POL, "policy" },
{ D_PROP, "prop" },
{ D_DOOR, "door" },
{ D_CONFIG, "config" },
{ D_LABEL, "label" },
{ D_ALL, "all" },
{ 0, "0" },
};
int
{
}
return (D_INVALID);
}
int
{
if (*endp == '\0')
return (mask);
if (op != '-')
op = '+';
do {
/* we encountered an invalid keywd */
return (new);
}
if (op == '+') {
} else {
}
return (mask);
}
/*
* functions to manipulate the kmcookie-label mapping file
*/
/*
* Open, lockf, fdopen the given file, returning a FILE * on success,
* or NULL on failure.
*/
FILE *
{
return (NULL);
}
return (NULL);
}
return (NULL);
}
/* save errno in case fclose changes it */
return (NULL);
}
return (fp);
}
/*
* Extract an integer cookie and string label from a line from the
* kmcookie-label file. Return -1 on failure, 0 on success.
*/
int
{
char *cookiestr;
*cookie = 0;
return (-1);
}
/* Everything that follows, up to the newline, is the label. */
return (-1);
}
return (0);
}
/*
* Insert a mapping into the file (if it's not already there), given the
* new label. Return the assigned cookie, or -1 on error.
*/
int
{
char *cur_label;
int rtnerr = 0;
/* open and lock the file; will sleep until lock is available */
/* kmc_open_and_lock() sets errno appropriately */
return (-1);
}
/* Skip blank lines, which often come near EOF. */
continue;
goto error;
}
if (cur_cookie > max_cookie)
}
}
if (!found) {
rtn_cookie = ++max_cookie;
goto error;
}
}
return (rtn_cookie);
return (-1);
}
/*
* Lookup the given cookie and return its corresponding label. Return
* a pointer to the label on success, NULL on error (or if the label is
* not found). Note that the returned label pointer points to a static
* string, so the label will be overwritten by a subsequent call to the
* function; the function is also not thread-safe as a result.
*/
char *
{
char *cur_label;
int cur_cookie;
return (NULL);
}
return (NULL);
}
if (cookie == cur_cookie) {
return (cur_label);
}
}
return (NULL);
}
/*
* Parse basic extension headers and return in the passed-in pointer vector.
* Return values include:
*
* KGE_OK Everything's nice and parsed out.
* If there are no extensions, place NULL in extv[0].
* KGE_DUP There is a duplicate extension.
* First instance in appropriate bin. First duplicate in
* extv[0].
* KGE_UNK Unknown extension type encountered. extv[0] contains
* unknown header.
* KGE_LEN Extension length error.
* KGE_CHK High-level reality check failed on specific extension.
*
* My apologies for some of the pointer arithmetic in here. I'm thinking
* like an assembly programmer, yet trying to make the compiler happy.
*/
int
{
int i;
diag_buf[0] = '\0';
for (i = 1; i <= SPD_EXT_MAX; i++)
i = 0;
/* Use extv[0] as the "current working pointer". */
/* Check for unknown headers. */
i++;
if (extv[0]->spd_ext_type == 0 ||
"spdsock ext 0x%X unknown: 0x%X",
i, extv[0]->spd_ext_type);
}
return (KGE_UNK);
}
/*
* Check length. Use uint64_t because extlen is in units
* of 64-bit words. If length goes beyond the msgsize,
* return an error. (Zero length also qualifies here.)
*/
if (extv[0]->spd_ext_len == 0 ||
return (KGE_LEN);
/* Check for redundant headers. */
return (KGE_DUP);
/* If I make it here, assign the appropriate bin. */
/* Advance pointer (See above for uint64_t ptr reasoning.) */
}
/* Everything's cool. */
/*
* If extv[0] == NULL, then there are no extension headers in this
* message. Ensure that this is the case.
*/
return (KGE_OK);
}
const char *
{
switch (diagnostic) {
case SPD_DIAGNOSTIC_NONE:
case SPD_DIAGNOSTIC_MIXED_AF:
case SPD_DIAGNOSTIC_BAD_SPDID:
return (dgettext(TEXT_DOMAIN,
"unsupported ESP encryption algorithm"));
return (dgettext(TEXT_DOMAIN,
"unsupported ESP authentication algorithm"));
return (dgettext(TEXT_DOMAIN,
"unsupported ESP encryption key size"));
return (dgettext(TEXT_DOMAIN,
"unsupported ESP authentication key size"));
return (dgettext(TEXT_DOMAIN,
"number of key sizes inconsistent"));
return (dgettext(TEXT_DOMAIN,
"number of block sizes inconsistent"));
return (dgettext(TEXT_DOMAIN,
"operation not applicable to all policies"));
return (dgettext(TEXT_DOMAIN,
"using selectors on a transport-mode tunnel"));
default:
}
}
/*
* PF_KEY Diagnostic table.
*
* PF_KEY NOTE: If you change pfkeyv2.h's SADB_X_DIAGNOSTIC_* space, this is
* where you need to add new messages.
*/
const char *
{
switch (diagnostic) {
case SADB_X_DIAGNOSTIC_NONE:
return (dgettext(TEXT_DOMAIN,
"Unknown Security Association type"));
return (dgettext(TEXT_DOMAIN,
"Specific Security Association type needed"));
return (dgettext(TEXT_DOMAIN,
"No Security Association Databases present"));
case SADB_X_DIAGNOSTIC_NO_EXT:
return (dgettext(TEXT_DOMAIN,
"No extensions needed for message"));
return (dgettext(TEXT_DOMAIN,
"Bad destination address family"));
return (dgettext(TEXT_DOMAIN,
"Bad inner-source address family"));
return (dgettext(TEXT_DOMAIN,
"Source/destination address family mismatch"));
return (dgettext(TEXT_DOMAIN,
"Soft allocations limit more than hard limit"));
return (dgettext(TEXT_DOMAIN,
"Soft bytes limit more than hard limit"));
"than hard expiration time"));
"than hard expiration time"));
return (dgettext(TEXT_DOMAIN,
"Supported algorithms extension not needed"));
return (dgettext(TEXT_DOMAIN,
"Unsupported authentication algorithm"));
return (dgettext(TEXT_DOMAIN,
"Unsupported encryption algorithm"));
return (dgettext(TEXT_DOMAIN,
"Bad number of authentication bits"));
return (dgettext(TEXT_DOMAIN,
"Bad number of encryption bits"));
return (dgettext(TEXT_DOMAIN,
"Encryption not supported for this SA type"));
return (dgettext(TEXT_DOMAIN,
"Duplicate key management protocol"));
return (dgettext(TEXT_DOMAIN,
"Duplicate key management cookie"));
return (dgettext(TEXT_DOMAIN,
"Duplicate NAT-T remote address"));
return (dgettext(TEXT_DOMAIN,
"Malformed NAT-T remote address"));
return (dgettext(TEXT_DOMAIN,
"Missing inner destination address"));
return (dgettext(TEXT_DOMAIN,
"Duplicate inner source address"));
return (dgettext(TEXT_DOMAIN,
"Duplicate inner destination address"));
return (dgettext(TEXT_DOMAIN,
"Malformed inner source address"));
return (dgettext(TEXT_DOMAIN,
"Malformed inner destination address"));
return (dgettext(TEXT_DOMAIN,
"Invalid inner-source prefix length "));
return (dgettext(TEXT_DOMAIN,
"Invalid inner-destination prefix length"));
return (dgettext(TEXT_DOMAIN,
"Bad inner-destination address family"));
return (dgettext(TEXT_DOMAIN,
"Inner source/destination address family mismatch"));
return (dgettext(TEXT_DOMAIN,
"Bad NAT-T remote address family"));
return (dgettext(TEXT_DOMAIN,
"Bad NAT-T local address family"));
return (dgettext(TEXT_DOMAIN,
"Source/desination protocol mismatch"));
return (dgettext(TEXT_DOMAIN,
"Inner source/desination protocol mismatch"));
return (dgettext(TEXT_DOMAIN,
"Both inner ports and outer ports are set"));
return (dgettext(TEXT_DOMAIN,
"Pairing failed, target SA unsuitable for pairing"));
return (dgettext(TEXT_DOMAIN,
"Source/destination address differs from pair SA"));
return (dgettext(TEXT_DOMAIN,
"Already paired with another security association"));
return (dgettext(TEXT_DOMAIN,
"Command failed, pair security association not found"));
return (dgettext(TEXT_DOMAIN,
"Inappropriate SA direction"));
return (dgettext(TEXT_DOMAIN,
"Security association not found"));
return (dgettext(TEXT_DOMAIN,
"Security association is not valid"));
return (dgettext(TEXT_DOMAIN,
"Algorithm invalid or not supported by Crypto Framework"));
return (dgettext(TEXT_DOMAIN,
"Invalid Replay counter"));
return (dgettext(TEXT_DOMAIN,
"Inappropriate lifetimes"));
default:
}
}
/*
* Convert an IPv6 mask to a prefix len. I assume all IPv6 masks are
* contiguous, so I stop at the first zero bit!
*/
int
{
int rc = 0;
if (is_v4mapped) {
}
while (*mask == 0xff) {
rc += 8;
return (limit);
mask++;
}
while (last != 0) {
rc++;
}
return (rc);
}
/*
* Expand the diagnostic code into a message.
*/
void
{
/* Use two spaces so above strings can fit on the line. */
" Diagnostic code %u: %s.\n"),
}
/*
* Prints the base PF_KEY message.
*/
void
{
if (wallclock != 0)
vflag);
"Base message (version %u) type "),
switch (samsg->sadb_msg_type) {
case SADB_RESERVED:
"RESERVED (warning: set to 0)"));
break;
case SADB_GETSPI:
break;
case SADB_UPDATE:
break;
case SADB_X_UPDATEPAIR:
break;
case SADB_ADD:
break;
case SADB_DELETE:
break;
case SADB_X_DELPAIR:
break;
case SADB_GET:
break;
case SADB_ACQUIRE:
break;
case SADB_REGISTER:
break;
case SADB_EXPIRE:
break;
case SADB_FLUSH:
break;
case SADB_DUMP:
break;
case SADB_X_PROMISC:
break;
case SADB_X_INVERSE_ACQUIRE:
break;
default:
break;
}
switch (samsg->sadb_msg_satype) {
case SADB_SATYPE_UNSPEC:
"<unspecified/all>"));
break;
case SADB_SATYPE_AH:
break;
case SADB_SATYPE_ESP:
break;
case SADB_SATYPE_RSVP:
break;
case SADB_SATYPE_OSPFV2:
break;
case SADB_SATYPE_RIPV2:
break;
case SADB_SATYPE_MIP:
break;
default:
break;
}
if (samsg->sadb_msg_errno != 0) {
"Error %s from PF_KEY.\n"),
}
"Message length %u bytes, seq=%u, pid=%u.\n"),
}
/*
* Print the SA extension for PF_KEY.
*/
void
{
"WARNING: SA info extension length (%u) is bad."),
}
"%sSADB_ASSOC spi=0x%x, replay window size=%u, state="),
switch (assoc->sadb_sa_state) {
case SADB_SASTATE_LARVAL:
break;
case SADB_SASTATE_MATURE:
break;
case SADB_SASTATE_DYING:
break;
case SADB_SASTATE_DEAD:
break;
"ACTIVE_ELSEWHERE"));
break;
case SADB_X_SASTATE_IDLE:
break;
default:
}
"\n%sAuthentication algorithm = "),
prefix);
}
"\n%sEncryption algorithm = "), prefix);
}
/* BEGIN Solaris-specific flags. */
/* END Solaris-specific flags. */
}
void
{
if (t != lt) {
if (lt > 0)
t = LONG_MAX;
else
t = LONG_MIN;
}
}
/*
* Print the SA lifetime information. (An SADB_EXT_LIFETIME_* extension.)
*/
void
{
"WARNING: CURRENT lifetime extension length (%u) is bad."),
}
"WARNING: HARD lifetime extension length (%u) is bad."),
}
"WARNING: SOFT lifetime extension length (%u) is bad."),
}
"WARNING: IDLE lifetime extension length (%u) is bad."),
}
/* Express values as current values. */
"%sCurrent lifetime information:\n"),
"used.\n"), current_prefix,
if (current->sadb_lifetime_usetime != 0) {
"%sSA first used at time %s\n"),
}
vflag);
}
"%sSoft lifetime information:\n"),
/* If possible, express values as time remaining. */
if (soft->sadb_lifetime_bytes != 0)
"protected.\n"), soft_prefix,
current->sadb_lifetime_bytes : 0,
if (soft->sadb_lifetime_addtime != 0 ||
(soft->sadb_lifetime_usetime != 0 &&
current->sadb_lifetime_usetime != 0)) {
if (soft->sadb_lifetime_addtime != 0) {
adddelta =
} else {
}
if (soft->sadb_lifetime_usetime != 0 &&
current->sadb_lifetime_usetime != 0) {
usedelta =
} else {
}
if (scratch >= 0) {
"Soft expiration occurs in %"
} else {
"Soft expiration occurred\n"));
}
"%sTime of expiration: %s.\n"),
}
}
}
"%sHard lifetime information:\n"), hard_prefix);
/* If possible, express values as time remaining. */
if (hard->sadb_lifetime_bytes != 0)
"protected.\n"), hard_prefix,
current->sadb_lifetime_bytes : 0,
if (hard->sadb_lifetime_addtime != 0 ||
(hard->sadb_lifetime_usetime != 0 &&
current->sadb_lifetime_usetime != 0)) {
if (hard->sadb_lifetime_addtime != 0) {
adddelta =
} else {
}
if (hard->sadb_lifetime_usetime != 0 &&
current->sadb_lifetime_usetime != 0) {
usedelta =
} else {
}
if (scratch >= 0) {
"Hard expiration occurs in %"
} else {
"Hard expiration occurred\n"));
}
"%sTime of expiration: %s.\n"),
}
}
}
"%sIdle lifetime information:\n"), idle_prefix);
}
}
/*
* Print an SADB_EXT_ADDRESS_* extension.
*/
void
{
switch (addr->sadb_address_exttype) {
case SADB_EXT_ADDRESS_SRC:
break;
"Inner source address "));
break;
case SADB_EXT_ADDRESS_DST:
"Destination address "));
break;
"Inner destination address "));
break;
"NAT-T local address "));
break;
"NAT-T remote address "));
break;
}
if (ignore_nss == B_FALSE) {
if (addr->sadb_address_proto == 0) {
"/<unspecified>"));
!= NULL) {
} else {
"/<unknown>"));
}
}
}
/*
* Print an SADB_EXT_KEY extension.
*/
void
{
switch (key->sadb_key_exttype) {
case SADB_EXT_KEY_AUTH:
break;
case SADB_EXT_KEY_ENCRYPT:
break;
}
}
/*
* Print an SADB_EXT_IDENTITY_* extension.
*/
void
{
switch (id->sadb_ident_exttype) {
case SADB_EXT_IDENTITY_SRC:
break;
case SADB_EXT_IDENTITY_DST:
break;
}
if (canprint) {
} else {
}
}
/*
* Convert sadb_sens extension into binary security label.
*/
void
{
}
void
{
"** Label conversion failed **"));
}
}
void
{
"** Label conversion failed **"));
}
}
int
{
return (sens_len);
sens->sadb_sens_integ_level = 0;
sens->sadb_sens_integ_len = 0;
sens->sadb_x_sens_flags = 0;
return (sens_len);
}
/*
* Print an SADB_SENSITIVITY extension.
*/
void
{
char *plabel;
char *hlabel;
int i;
"%s%s DPD %d, sens level=%d, integ level=%d, flags=%x\n"),
if (ignore_nss) {
} else {
"%s %s Label: %s (%s)\n"),
}
}
/*
* Print an SADB_EXT_PROPOSAL extension.
*/
void
{
int i, numcombs;
"%sProposal, replay counter = %u.\n"), prefix,
for (i = 0; i < numcombs; i++) {
"Authentication = "));
" minbits=%u, maxbits=%u.\n%s "),
}
"Encryption = "));
" minbits=%u, maxbits=%u.\n%s "),
}
if (combs[i].sadb_comb_hard_allocations)
if (combs[i].sadb_comb_hard_bytes)
if (combs[i].sadb_comb_hard_addtime)
if (combs[i].sadb_comb_hard_usetime)
prefix);
if (combs[i].sadb_comb_soft_allocations)
if (combs[i].sadb_comb_soft_bytes)
if (combs[i].sadb_comb_soft_addtime)
if (combs[i].sadb_comb_soft_usetime)
}
}
/*
* Print an extended proposal (SADB_X_EXT_EPROP).
*/
void
{
int i, j;
"%sExtended Proposal, replay counter = %u, "), prefix,
for (i = 0; i < eprop->sadb_x_prop_numecombs; ) {
"%s Extended combination #%u:\n"), prefix, ++i);
prefix);
prefix);
for (j = 0; j < ecomb->sadb_x_ecomb_numalgs; ) {
"%s Alg #%u "), prefix, ++j);
switch (algdesc->sadb_x_algdesc_satype) {
case SADB_SATYPE_ESP:
"for ESP "));
break;
case SADB_SATYPE_AH:
"for AH "));
break;
default:
"for satype=%d "),
}
switch (algdesc->sadb_x_algdesc_algtype) {
case SADB_X_ALGTYPE_CRYPT:
"Encryption = "));
file);
break;
case SADB_X_ALGTYPE_AUTH:
"Authentication = "));
file);
break;
default:
"algtype(%d) = alg(%d)"),
break;
}
" minbits=%u, maxbits=%u, saltbits=%u\n"),
}
}
}
/*
* Print an SADB_EXT_SUPPORTED extension.
*/
void
{
int i, numalgs;
switch (supp->sadb_supported_exttype) {
case SADB_EXT_SUPPORTED_AUTH:
break;
break;
}
for (i = 0; i < numalgs; i++) {
switch (exttype) {
case SADB_EXT_SUPPORTED_AUTH:
break;
break;
}
" minbits=%u, maxbits=%u, ivlen=%u, saltbits=%u"),
if (exttype == SADB_EXT_SUPPORTED_ENCRYPT)
}
}
/*
* Print an SADB_EXT_SPIRANGE extension.
*/
void
{
"%sSPI Range, min=0x%x, max=0x%x\n"), prefix,
}
/*
* Print an SADB_X_EXT_KM_COOKIE extension.
*/
void
{
char *cookie_label;
NULL)
"%sProtocol %u, cookie=\"%s\" (%u)\n"), prefix,
}
/*
* Print an SADB_X_EXT_REPLAY_CTR extension.
*/
void
{
"%sReplay Value "), prefix);
if ((repl->sadb_x_rc_replay32 == 0) &&
(repl->sadb_x_rc_replay64 == 0)) {
"<Value not found.>"));
}
/*
* We currently do not support a 64-bit replay value.
* RFC 4301 will require one, however, and we have a field
* in place when 4301 is built.
*/
((repl->sadb_x_rc_replay32 == 0) ?
}
/*
* Print an SADB_X_EXT_PAIR extension.
*/
static void
{
}
/*
* Take a PF_KEY message pointed to buffer and print it. Useful for DUMP
* and GET.
*/
void
{
int i;
int lenbytes;
switch (ext->sadb_ext_type) {
case SADB_EXT_SA:
break;
/*
* Pluck out lifetimes and print them at the end. This is
* to show relative lifetimes.
*/
break;
case SADB_EXT_LIFETIME_HARD:
break;
case SADB_EXT_LIFETIME_SOFT:
break;
case SADB_X_EXT_LIFETIME_IDLE:
break;
case SADB_EXT_ADDRESS_SRC:
break;
break;
case SADB_EXT_ADDRESS_DST:
break;
break;
case SADB_EXT_KEY_AUTH:
break;
case SADB_EXT_KEY_ENCRYPT:
break;
case SADB_EXT_IDENTITY_SRC:
(struct sadb_ident *)current);
break;
case SADB_EXT_IDENTITY_DST:
(struct sadb_ident *)current);
break;
case SADB_EXT_SENSITIVITY:
break;
case SADB_EXT_PROPOSAL:
break;
case SADB_EXT_SUPPORTED_AUTH:
(struct sadb_supported *)current);
break;
(struct sadb_supported *)current);
break;
case SADB_EXT_SPIRANGE:
(struct sadb_spirange *)current);
break;
case SADB_X_EXT_EPROP:
break;
case SADB_X_EXT_KM_COOKIE:
(struct sadb_x_kmc *)current);
break;
break;
break;
case SADB_X_EXT_PAIR:
(struct sadb_x_pair *)current);
break;
case SADB_X_EXT_OUTER_SENS:
break;
case SADB_X_EXT_REPLAY_VALUE:
break;
default:
"UNK: Unknown ext. %d, len %d.\n"),
for (i = 0; i < ext->sadb_ext_len; i++)
break;
}
}
/*
* Print lifetimes NOW.
*/
"WARNING: insufficient buffer space or corrupt message."));
}
}
/*
* 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.
*/
/*
* Print save information for a lifetime extension.
*
* NOTE : It saves the lifetime in absolute terms. For example, if you
* had a hard_usetime of 60 seconds, you'll save it as 60 seconds, even though
* there may have been 59 seconds burned off the clock.
*/
{
char *prefix;
switch (lifetime->sadb_lifetime_exttype) {
case SADB_EXT_LIFETIME_HARD:
prefix = "hard";
break;
case SADB_EXT_LIFETIME_SOFT:
prefix = "soft";
break;
case SADB_X_EXT_LIFETIME_IDLE:
prefix = "idle";
break;
}
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
lifetime->sadb_lifetime_addtime) < 0)
return (B_FALSE);
lifetime->sadb_lifetime_usetime) < 0)
return (B_FALSE);
return (B_TRUE);
}
/*
* Print save information for an address extension.
*/
{
/*
* Address-family reality check.
*/
return (B_FALSE);
switch (addr->sadb_address_exttype) {
case SADB_EXT_ADDRESS_SRC:
prefix = "src";
pprefix = "sport";
break;
prefix = "isrc";
pprefix = "isport";
break;
case SADB_EXT_ADDRESS_DST:
prefix = "dst";
pprefix = "dport";
break;
prefix = "idst";
pprefix = "idport";
break;
prefix = "nat_loc ";
pprefix = "nat_lport";
break;
prefix = "nat_rem ";
pprefix = "nat_rport";
break;
}
return (B_FALSE);
/*
* Do not do address-to-name translation, given that we live in
* an age of names that explode into many addresses.
*/
if (printable_addr == NULL)
printable_addr = "Invalid IP address.";
return (B_FALSE);
if (addr->sadb_address_prefixlen != 0 &&
return (B_FALSE);
}
/*
* The port is in the same position for struct sockaddr_in and
* struct sockaddr_in6. We exploit that property here.
*/
return (B_TRUE);
}
/*
* Print save information for a key extension. Returns whether writing
* to the specified output file was successful or not.
*/
{
char *prefix;
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
/*
* Print save information for an identity extension.
*/
{
char *prefix;
return (B_FALSE);
"dst";
rparseidtype(ident->sadb_ident_type)) < 0)
return (B_FALSE);
if (ident->sadb_ident_type == SADB_X_IDENTTYPE_DN ||
ident->sadb_ident_type == SADB_X_IDENTTYPE_GN) {
"<can-not-print>")) < 0)
return (B_FALSE);
} else {
return (B_FALSE);
}
return (B_TRUE);
}
{
char *prefix;
char *hlabel;
return (B_FALSE);
prefix = "label";
prefix = "outer-label";
else
prefix = "implicit-label";
return (B_FALSE);
}
return (B_TRUE);
}
/*
* "Save" a security association to an output file.
*
* NOTE the lack of calls to dgettext() because I'm outputting parseable stuff.
* ALSO NOTE that if you change keywords (see parsecmd()), you'll have to
* change them here as well.
*/
void
{
int terrno;
#define tidyup() \
"save_assoc: Opening comment of SA"));
savenl();
switch (ext->sadb_ext_type) {
case SADB_EXT_SA:
"or dead.\n") < 0) {
tidyup();
"save_assoc: fprintf not mature"));
}
}
tidyup();
"save_assoc: fprintf spi"));
}
IPSEC_PROTO_ESP)) < 0) {
tidyup();
"save_assoc: fprintf encrypt"));
}
}
IPSEC_PROTO_AH)) < 0) {
tidyup();
"save_assoc: fprintf auth"));
}
}
assoc->sadb_sa_replay) < 0) {
tidyup();
"save_assoc: fprintf replay"));
}
tidyup();
"save_assoc: fprintf encap"));
}
}
savenl();
break;
case SADB_EXT_LIFETIME_HARD:
case SADB_EXT_LIFETIME_SOFT:
case SADB_X_EXT_LIFETIME_IDLE:
ofile)) {
tidyup();
}
savenl();
break;
savenl();
}
goto skip_srcdst; /* Hack to avoid cases below... */
/* FALLTHRU */
case SADB_EXT_ADDRESS_SRC:
case SADB_EXT_ADDRESS_DST:
savenl();
seen_proto = B_TRUE;
}
/* FALLTHRU */
tidyup();
}
savenl();
break;
case SADB_EXT_KEY_AUTH:
case SADB_EXT_KEY_ENCRYPT:
tidyup();
}
savenl();
break;
case SADB_EXT_IDENTITY_SRC:
case SADB_EXT_IDENTITY_DST:
tidyup();
}
savenl();
break;
case SADB_X_EXT_REPLAY_VALUE:
if ((repl->sadb_x_rc_replay32 == 0) &&
(repl->sadb_x_rc_replay64 == 0)) {
tidyup();
}
(repl->sadb_x_rc_replay32 == 0 ?
repl->sadb_x_rc_replay32)) < 0) {
tidyup();
"save_assoc: fprintf replay value"));
}
savenl();
break;
case SADB_EXT_SENSITIVITY:
case SADB_X_EXT_OUTER_SENS:
tidyup();
}
savenl();
break;
default:
/* Skip over irrelevant extensions. */
break;
}
}
tidyup();
}
}
/*
* Open the output file for the "save" command.
*/
FILE *
{
int fd;
/*
* If the user specifies "-" or doesn't give a filename, then
* dump to stdout. Make sure to document the dangers of files
* that are NFS, directing your output to strange places, etc.
*/
return (stdout);
/*
* open the file with the create bits set. Since I check for
* real UID == root in main(), I won't worry about the ownership
* problem.
*/
if (fd == -1) {
"open error"),
if (fd == -1)
}
"WARNING: Save file already exists with "
"Normal users may be able to read IPsec "
"keying material."));
}
}
/* Okay, we have an FD. Assign it to a stdio FILE pointer. */
}
return (retval);
}
const char *
{
}
}
/*
* 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. */
};
char *
{
tt++;
} else {
}
return (numprint);
}
/*
* Return a string containing the name of the specified numerical algorithm
* identifier.
*/
char *
{
return (numprint);
}
}
/*
* 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}
};
char *
{
}
return (numprint);
}
/*
* This is a general purpose exit function, calling functions can specify an
* error type. If the command calling this function was started by smf(5) the
* error type could be used as a hint to the restarter. In the future this
* function could be used to do something more intelligent with a process that
* encounters an error. If exit() is called with an error code other than those
* defined by smf(5), the program will just get restarted. Unless restarting
* is likely to resolve the error condition, its probably sensible to just
* log the error and keep running.
*
* The SERVICE_* exit_types mean nothing if the command was run from the
* command line, just exit(). There are two special cases:
*
* SERVICE_DEGRADE - Not implemented in smf(5), one day it could hint that
* the service is not running as well is it could. For
* now, don't do anything, just record the error.
* DEBUG_FATAL - Something happened, if the command was being run in debug
* mode, exit() as you really want to know something happened,
* otherwise just keep running. This is ignored when running
* under smf(5).
*
* The function will handle an optional variable args error message, this
* will be written to the error stream, typically a log file or stderr.
*/
void
{
int exit_status;
}
/* Command being run directly from a shell. */
switch (type) {
case SERVICE_EXIT_OK:
exit_status = 0;
break;
case SERVICE_DEGRADE:
return;
/* NOTREACHED */
break;
case SERVICE_BADPERM:
case SERVICE_BADCONF:
case SERVICE_MAINTAIN:
case SERVICE_DISABLE:
case SERVICE_FATAL:
case SERVICE_RESTART:
case DEBUG_FATAL:
exit_status = 1;
break;
}
} else {
/* Command being run as a smf(5) method. */
switch (type) {
case SERVICE_EXIT_OK:
break;
case SERVICE_DEGRADE: /* Not implemented yet. */
case DEBUG_FATAL:
/* Keep running, don't exit(). */
return;
/* NOTREACHED */
break;
case SERVICE_BADPERM:
"Permission error with %s."), fmri);
break;
case SERVICE_BADCONF:
"Bad configuration of service %s."), fmri);
break;
case SERVICE_MAINTAIN:
"Service %s needs maintenance."), fmri);
break;
case SERVICE_DISABLE:
break;
case SERVICE_FATAL:
"Service %s fatal error."), fmri);
break;
case SERVICE_RESTART:
exit_status = 1;
break;
}
}
}