1912 lines
54 KiB
C
1912 lines
54 KiB
C
/* command interface to Pluto
|
|
* Copyright (C) 1997 Angelos D. Keromytis.
|
|
* Copyright (C) 1998-2001 D. Hugh Redelmeier.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* RCSID $Id: whack.c,v 1.21 2006/04/20 04:42:12 as Exp $
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <getopt.h>
|
|
#include <assert.h>
|
|
|
|
#include <freeswan.h>
|
|
|
|
#include "constants.h"
|
|
#include "defs.h"
|
|
#include "whack.h"
|
|
|
|
static void
|
|
help(void)
|
|
{
|
|
fprintf(stderr
|
|
, "Usage:\n\n"
|
|
"all forms:"
|
|
" [--optionsfrom <filename>]"
|
|
" [--ctlbase <path>]"
|
|
" [--label <string>]"
|
|
"\n\n"
|
|
"help: whack"
|
|
" [--help]"
|
|
" [--version]"
|
|
"\n\n"
|
|
"connection: whack"
|
|
" --name <connection_name>"
|
|
" \\\n "
|
|
" [--ipv4 | --ipv6]"
|
|
" [--tunnelipv4 | --tunnelipv6]"
|
|
" \\\n "
|
|
" (--host <ip-address> | --id <identity>)"
|
|
" \\\n "
|
|
" [--cert <path>]"
|
|
" [--ca <distinguished name>]"
|
|
" [--sendcert <policy>]"
|
|
" \\\n "
|
|
" [--groups <access control groups>]"
|
|
" \\\n "
|
|
" [--ikeport <port-number>]"
|
|
" [--nexthop <ip-address>]"
|
|
" [--srcip <ip-address>]"
|
|
" \\\n "
|
|
" [--client <subnet> | --clientwithin <address range>]"
|
|
" [--clientprotoport <protocol>/<port>]"
|
|
" \\\n "
|
|
" [--dnskeyondemand]"
|
|
" [--updown <updown>]"
|
|
" \\\n "
|
|
" --to"
|
|
" (--host <ip-address> | --id <identity>)"
|
|
" \\\n "
|
|
" [--cert <path>]"
|
|
" [--ca <distinguished name>]"
|
|
" [--sendcert <policy>]"
|
|
" \\\n "
|
|
" [--ikeport <port-number>]"
|
|
" [--nexthop <ip-address>]"
|
|
" [--srcip <ip-address>]"
|
|
" \\\n "
|
|
" [--client <subnet> | --clientwithin <address range>]"
|
|
" [--clientprotoport <protocol>/<port>]"
|
|
" \\\n "
|
|
" [--dnskeyondemand]"
|
|
" [--updown <updown>]"
|
|
" [--psk]"
|
|
" [--rsasig]"
|
|
" \\\n "
|
|
" [--encrypt]"
|
|
" [--authenticate]"
|
|
" [--compress]"
|
|
" [--tunnel]"
|
|
" [--pfs]"
|
|
" \\\n "
|
|
" [--ikelifetime <seconds>]"
|
|
" [--ipseclifetime <seconds>]"
|
|
" \\\n "
|
|
" [--reykeymargin <seconds>]"
|
|
" [--reykeyfuzz <percentage>]"
|
|
" \\\n "
|
|
" [--keyingtries <count>]"
|
|
" \\\n "
|
|
" [--esp <esp-algos>]"
|
|
" \\\n "
|
|
" [--dontrekey]"
|
|
|
|
" [--dpdaction (none|clear|hold|restart)]"
|
|
" \\\n "
|
|
" [--dpddelay <seconds> --dpdtimeout <seconds>]"
|
|
" \\\n "
|
|
" [--initiateontraffic|--pass|--drop|--reject]"
|
|
" \\\n "
|
|
" [--failnone|--failpass|--faildrop|--failreject]"
|
|
"\n\n"
|
|
"routing: whack"
|
|
" (--route | --unroute)"
|
|
" --name <connection_name>"
|
|
"\n\n"
|
|
"initiation:"
|
|
"\n "
|
|
" whack"
|
|
" (--initiate | --terminate)"
|
|
" --name <connection_name>"
|
|
" [--asynchronous]"
|
|
"\n\n"
|
|
"opportunistic initiation: whack"
|
|
" [--tunnelipv4 | --tunnelipv6]"
|
|
" \\\n "
|
|
" --oppohere <ip-address>"
|
|
" --oppothere <ip-address>"
|
|
"\n\n"
|
|
"delete: whack"
|
|
" --delete"
|
|
" (--name <connection_name> | --caname <ca name>)"
|
|
"\n\n"
|
|
"deletestate: whack"
|
|
" --deletestate <state_object_number>"
|
|
" --crash <ip-address>"
|
|
"\n\n"
|
|
"pubkey: whack"
|
|
" --keyid <id>"
|
|
" [--addkey]"
|
|
" [--pubkeyrsa <key>]"
|
|
"\n\n"
|
|
"myid: whack"
|
|
" --myid <id>"
|
|
"\n\n"
|
|
"ca: whack"
|
|
" --caname <name>"
|
|
" --cacert <path>"
|
|
" \\\n "
|
|
" [--ldaphost <hostname>]"
|
|
" [--ldapbase <base>]"
|
|
" \\\n "
|
|
" [--crluri <uri>]"
|
|
" [--crluri2 <uri>]"
|
|
" [--ocspuri <uri>]"
|
|
" [--strictcrlpolicy]"
|
|
"\n\n"
|
|
#ifdef DEBUG
|
|
"debug: whack [--name <connection_name>]"
|
|
" \\\n "
|
|
" [--debug-none]"
|
|
" [--debug-all]"
|
|
" \\\n "
|
|
" [--debug-raw]"
|
|
" [--debug-crypt]"
|
|
" [--debug-parsing]"
|
|
" [--debug-emitting]"
|
|
" \\\n "
|
|
" [--debug-control]"
|
|
" [--debug-lifecycle]"
|
|
" [--debug-klips]"
|
|
" [--debug-dns]"
|
|
" \\\n "
|
|
" [--debug-natt]"
|
|
" [--debug-oppo]"
|
|
" [--debug-controlmore]"
|
|
" [--debug-private]"
|
|
"\n\n"
|
|
#endif
|
|
"listen: whack"
|
|
" (--listen | --unlisten)"
|
|
"\n\n"
|
|
"list: whack [--utc]"
|
|
" [--listalgs]"
|
|
" [--listpubkeys]"
|
|
" [--listcerts]"
|
|
" [--listcacerts]"
|
|
" \\\n "
|
|
" [--listacerts]"
|
|
" [--listaacerts]"
|
|
" [--listocspcerts]"
|
|
" [--listgroups]"
|
|
" \\\n "
|
|
" [--listcainfos]"
|
|
" [--listcrls]"
|
|
" [--listocsp]"
|
|
" [--listcards]"
|
|
" [--listall]"
|
|
"\n\n"
|
|
"purge: whack"
|
|
" [--purgeocsp]"
|
|
"\n\n"
|
|
"reread: whack"
|
|
" [--rereadsecrets]"
|
|
" [--rereadcacerts]"
|
|
" [--rereadaacerts]"
|
|
" \\\n "
|
|
" [--rereadocspcerts]"
|
|
" [--rereadacerts]"
|
|
" [--rereadcrls]"
|
|
" [--rereadall]"
|
|
"\n\n"
|
|
"status: whack"
|
|
" [--name <connection_name>] --status|--statusall"
|
|
"\n\n"
|
|
"scdecrypt: whack"
|
|
" --scencrypt|scdecrypt <value>"
|
|
" [--inbase <base>]"
|
|
" [--outbase <base>]"
|
|
" [--keyid <id>]"
|
|
"\n\n"
|
|
"shutdown: whack"
|
|
" --shutdown"
|
|
"\n\n"
|
|
"strongSwan %s\n"
|
|
, ipsec_version_code());
|
|
}
|
|
|
|
static const char *label = NULL; /* --label operand, saved for diagnostics */
|
|
|
|
static const char *name = NULL; /* --name operand, saved for diagnostics */
|
|
|
|
/* print a string as a diagnostic, then exit whack unhappily */
|
|
static void
|
|
diag(const char *mess)
|
|
{
|
|
if (mess != NULL)
|
|
{
|
|
fprintf(stderr, "whack error: ");
|
|
if (label != NULL)
|
|
fprintf(stderr, "%s ", label);
|
|
if (name != NULL)
|
|
fprintf(stderr, "\"%s\" ", name);
|
|
fprintf(stderr, "%s\n", mess);
|
|
}
|
|
|
|
exit(RC_WHACK_PROBLEM);
|
|
}
|
|
|
|
/* conditially calls diag; prints second arg, if non-NULL, as quoted string */
|
|
static void
|
|
diagq(err_t ugh, const char *this)
|
|
{
|
|
if (ugh != NULL)
|
|
{
|
|
if (this == NULL)
|
|
{
|
|
diag(ugh);
|
|
}
|
|
else
|
|
{
|
|
char buf[120]; /* arbitrary limit */
|
|
|
|
snprintf(buf, sizeof(buf), "%s \"%s\"", ugh, this);
|
|
diag(buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* complex combined operands return one of these enumerated values
|
|
* Note: these become flags in an lset_t. Since there are more than
|
|
* 32, we partition them into:
|
|
* - OPT_* options (most random options)
|
|
* - LST_* options (list various internal data)
|
|
* - DBGOPT_* option (DEBUG options)
|
|
* - END_* options (End description options)
|
|
* - CD_* options (Connection Description options)
|
|
* - CA_* options (CA description options)
|
|
*/
|
|
enum {
|
|
# define OPT_FIRST OPT_CTLBASE
|
|
OPT_CTLBASE,
|
|
OPT_NAME,
|
|
|
|
OPT_CD,
|
|
|
|
OPT_KEYID,
|
|
OPT_ADDKEY,
|
|
OPT_PUBKEYRSA,
|
|
|
|
OPT_MYID,
|
|
|
|
OPT_ROUTE,
|
|
OPT_UNROUTE,
|
|
|
|
OPT_INITIATE,
|
|
OPT_TERMINATE,
|
|
OPT_DELETE,
|
|
OPT_DELETESTATE,
|
|
OPT_LISTEN,
|
|
OPT_UNLISTEN,
|
|
|
|
OPT_PURGEOCSP,
|
|
|
|
OPT_REREADSECRETS,
|
|
OPT_REREADCACERTS,
|
|
OPT_REREADAACERTS,
|
|
OPT_REREADOCSPCERTS,
|
|
OPT_REREADACERTS,
|
|
OPT_REREADCRLS,
|
|
OPT_REREADALL,
|
|
|
|
OPT_STATUS,
|
|
OPT_STATUSALL,
|
|
OPT_SHUTDOWN,
|
|
|
|
OPT_OPPO_HERE,
|
|
OPT_OPPO_THERE,
|
|
|
|
OPT_ASYNC,
|
|
OPT_DELETECRASH,
|
|
|
|
# define OPT_LAST OPT_ASYNC /* last "normal" option */
|
|
|
|
/* Smartcard options */
|
|
|
|
# define SC_FIRST SC_ENCRYPT /* first smartcard option */
|
|
|
|
SC_ENCRYPT,
|
|
SC_DECRYPT,
|
|
SC_INBASE,
|
|
SC_OUTBASE,
|
|
|
|
# define SC_LAST SC_OUTBASE /* last "smartcard" option */
|
|
|
|
/* List options */
|
|
|
|
# define LST_FIRST LST_UTC /* first list option */
|
|
LST_UTC,
|
|
LST_ALGS,
|
|
LST_PUBKEYS,
|
|
LST_CERTS,
|
|
LST_CACERTS,
|
|
LST_ACERTS,
|
|
LST_AACERTS,
|
|
LST_OCSPCERTS,
|
|
LST_GROUPS,
|
|
LST_CAINFOS,
|
|
LST_CRLS,
|
|
LST_OCSP,
|
|
LST_CARDS,
|
|
LST_ALL,
|
|
|
|
# define LST_LAST LST_ALL /* last list option */
|
|
|
|
/* Connection End Description options */
|
|
|
|
# define END_FIRST END_HOST /* first end description */
|
|
END_HOST,
|
|
END_ID,
|
|
END_CERT,
|
|
END_CA,
|
|
END_SENDCERT,
|
|
END_GROUPS,
|
|
END_IKEPORT,
|
|
END_NEXTHOP,
|
|
END_CLIENT,
|
|
END_CLIENTWITHIN,
|
|
END_CLIENTPROTOPORT,
|
|
END_DNSKEYONDEMAND,
|
|
END_SRCIP,
|
|
END_HOSTACCESS,
|
|
END_UPDOWN,
|
|
|
|
#define END_LAST END_UPDOWN /* last end description*/
|
|
|
|
/* Connection Description options -- segregated */
|
|
|
|
# define CD_FIRST CD_TO /* first connection description */
|
|
CD_TO,
|
|
|
|
# define CD_POLICY_FIRST CD_PSK
|
|
CD_PSK, /* same order as POLICY_* */
|
|
CD_RSASIG, /* same order as POLICY_* */
|
|
CD_ENCRYPT, /* same order as POLICY_* */
|
|
CD_AUTHENTICATE, /* same order as POLICY_* */
|
|
CD_COMPRESS, /* same order as POLICY_* */
|
|
CD_TUNNEL, /* same order as POLICY_* */
|
|
CD_PFS, /* same order as POLICY_* */
|
|
CD_DISABLEARRIVALCHECK, /* same order as POLICY_* */
|
|
CD_SHUNT0, /* same order as POLICY_* */
|
|
CD_SHUNT1, /* same order as POLICY_* */
|
|
CD_FAIL0, /* same order as POLICY_* */
|
|
CD_FAIL1, /* same order as POLICY_* */
|
|
CD_DONT_REKEY, /* same order as POLICY_* */
|
|
|
|
CD_TUNNELIPV4,
|
|
CD_TUNNELIPV6,
|
|
CD_CONNIPV4,
|
|
CD_CONNIPV6,
|
|
|
|
CD_IKELIFETIME,
|
|
CD_IPSECLIFETIME,
|
|
CD_RKMARGIN,
|
|
CD_RKFUZZ,
|
|
CD_KTRIES,
|
|
CD_DPDACTION,
|
|
CD_DPDDELAY,
|
|
CD_DPDTIMEOUT,
|
|
CD_IKE,
|
|
CD_PFSGROUP,
|
|
CD_ESP,
|
|
|
|
# define CD_LAST CD_ESP /* last connection description */
|
|
|
|
/* Certificate Authority (CA) description options */
|
|
|
|
# define CA_FIRST CA_NAME /* first ca description */
|
|
|
|
CA_NAME,
|
|
CA_CERT,
|
|
CA_LDAPHOST,
|
|
CA_LDAPBASE,
|
|
CA_CRLURI,
|
|
CA_CRLURI2,
|
|
CA_OCSPURI,
|
|
CA_STRICT
|
|
|
|
# define CA_LAST CA_STRICT /* last ca description */
|
|
|
|
#ifdef DEBUG /* must be last so others are less than 32 to fit in lset_t */
|
|
# define DBGOPT_FIRST DBGOPT_NONE
|
|
,
|
|
/* NOTE: these definitions must match DBG_* and IMPAIR_* in constants.h */
|
|
DBGOPT_NONE,
|
|
DBGOPT_ALL,
|
|
|
|
DBGOPT_RAW, /* same order as DBG_* */
|
|
DBGOPT_CRYPT, /* same order as DBG_* */
|
|
DBGOPT_PARSING, /* same order as DBG_* */
|
|
DBGOPT_EMITTING, /* same order as DBG_* */
|
|
DBGOPT_CONTROL, /* same order as DBG_* */
|
|
DBGOPT_LIFECYCLE, /* same order as DBG_* */
|
|
DBGOPT_KLIPS, /* same order as DBG_* */
|
|
DBGOPT_DNS, /* same order as DBG_* */
|
|
DBGOPT_NATT, /* same order as DBG_* */
|
|
DBGOPT_OPPO, /* same order as DBG_* */
|
|
DBGOPT_CONTROLMORE, /* same order as DBG_* */
|
|
|
|
DBGOPT_PRIVATE, /* same order as DBG_* */
|
|
|
|
DBGOPT_IMPAIR_DELAY_ADNS_KEY_ANSWER, /* same order as IMPAIR_* */
|
|
DBGOPT_IMPAIR_DELAY_ADNS_TXT_ANSWER, /* same order as IMPAIR_* */
|
|
DBGOPT_IMPAIR_BUST_MI2, /* same order as IMPAIR_* */
|
|
DBGOPT_IMPAIR_BUST_MR2 /* same order as IMPAIR_* */
|
|
|
|
# define DBGOPT_LAST DBGOPT_IMPAIR_BUST_MR2
|
|
#endif
|
|
|
|
};
|
|
|
|
/* Carve up space for result from getop_long.
|
|
* Stupidly, the only result is an int.
|
|
* Numeric arg is bit immediately left of basic value.
|
|
*
|
|
*/
|
|
#define OPTION_OFFSET 256 /* to get out of the way of letter options */
|
|
#define NUMERIC_ARG (1 << 9) /* expect a numeric argument */
|
|
#define AUX_SHIFT 10 /* amount to shift for aux information */
|
|
|
|
static const struct option long_opts[] = {
|
|
# define OO OPTION_OFFSET
|
|
/* name, has_arg, flag, val */
|
|
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "version", no_argument, NULL, 'v' },
|
|
{ "optionsfrom", required_argument, NULL, '+' },
|
|
{ "label", required_argument, NULL, 'l' },
|
|
|
|
{ "ctlbase", required_argument, NULL, OPT_CTLBASE + OO },
|
|
{ "name", required_argument, NULL, OPT_NAME + OO },
|
|
|
|
{ "keyid", required_argument, NULL, OPT_KEYID + OO },
|
|
{ "addkey", no_argument, NULL, OPT_ADDKEY + OO },
|
|
{ "pubkeyrsa", required_argument, NULL, OPT_PUBKEYRSA + OO },
|
|
|
|
{ "myid", required_argument, NULL, OPT_MYID + OO },
|
|
|
|
{ "route", no_argument, NULL, OPT_ROUTE + OO },
|
|
{ "unroute", no_argument, NULL, OPT_UNROUTE + OO },
|
|
|
|
{ "initiate", no_argument, NULL, OPT_INITIATE + OO },
|
|
{ "terminate", no_argument, NULL, OPT_TERMINATE + OO },
|
|
{ "delete", no_argument, NULL, OPT_DELETE + OO },
|
|
{ "deletestate", required_argument, NULL, OPT_DELETESTATE + OO + NUMERIC_ARG },
|
|
{ "crash", required_argument, NULL, OPT_DELETECRASH + OO },
|
|
{ "listen", no_argument, NULL, OPT_LISTEN + OO },
|
|
{ "unlisten", no_argument, NULL, OPT_UNLISTEN + OO },
|
|
|
|
{ "purgeocsp", no_argument, NULL, OPT_PURGEOCSP + OO },
|
|
|
|
{ "rereadsecrets", no_argument, NULL, OPT_REREADSECRETS + OO },
|
|
{ "rereadcacerts", no_argument, NULL, OPT_REREADCACERTS + OO },
|
|
{ "rereadaacerts", no_argument, NULL, OPT_REREADAACERTS + OO },
|
|
{ "rereadocspcerts", no_argument, NULL, OPT_REREADOCSPCERTS + OO },
|
|
{ "rereadacerts", no_argument, NULL, OPT_REREADACERTS + OO },
|
|
{ "rereadcrls", no_argument, NULL, OPT_REREADCRLS + OO },
|
|
{ "rereadall", no_argument, NULL, OPT_REREADALL + OO },
|
|
{ "status", no_argument, NULL, OPT_STATUS + OO },
|
|
{ "statusall", no_argument, NULL, OPT_STATUSALL + OO },
|
|
{ "shutdown", no_argument, NULL, OPT_SHUTDOWN + OO },
|
|
|
|
{ "oppohere", required_argument, NULL, OPT_OPPO_HERE + OO },
|
|
{ "oppothere", required_argument, NULL, OPT_OPPO_THERE + OO },
|
|
|
|
{ "asynchronous", no_argument, NULL, OPT_ASYNC + OO },
|
|
|
|
/* smartcard options */
|
|
|
|
{ "scencrypt", required_argument, NULL, SC_ENCRYPT + OO },
|
|
{ "scdecrypt", required_argument, NULL, SC_DECRYPT + OO },
|
|
{ "inbase", required_argument, NULL, SC_INBASE + OO },
|
|
{ "outbase", required_argument, NULL, SC_OUTBASE + OO },
|
|
|
|
/* list options */
|
|
|
|
{ "utc", no_argument, NULL, LST_UTC + OO },
|
|
{ "listalgs", no_argument, NULL, LST_ALGS + OO },
|
|
{ "listpubkeys", no_argument, NULL, LST_PUBKEYS + OO },
|
|
{ "listcerts", no_argument, NULL, LST_CERTS + OO },
|
|
{ "listcacerts", no_argument, NULL, LST_CACERTS + OO },
|
|
{ "listacerts", no_argument, NULL, LST_ACERTS + OO },
|
|
{ "listaacerts", no_argument, NULL, LST_AACERTS + OO },
|
|
{ "listocspcerts", no_argument, NULL, LST_OCSPCERTS + OO },
|
|
{ "listgroups", no_argument, NULL, LST_GROUPS + OO },
|
|
{ "listcainfos", no_argument, NULL, LST_CAINFOS + OO },
|
|
{ "listcrls", no_argument, NULL, LST_CRLS + OO },
|
|
{ "listocsp", no_argument, NULL, LST_OCSP + OO },
|
|
{ "listcards", no_argument, NULL, LST_CARDS + OO },
|
|
{ "listall", no_argument, NULL, LST_ALL + OO },
|
|
|
|
/* options for an end description */
|
|
|
|
{ "host", required_argument, NULL, END_HOST + OO },
|
|
{ "id", required_argument, NULL, END_ID + OO },
|
|
{ "cert", required_argument, NULL, END_CERT + OO },
|
|
{ "ca", required_argument, NULL, END_CA + OO },
|
|
{ "sendcert", required_argument, NULL, END_SENDCERT + OO },
|
|
{ "groups", required_argument, NULL, END_GROUPS + OO },
|
|
{ "ikeport", required_argument, NULL, END_IKEPORT + OO + NUMERIC_ARG },
|
|
{ "nexthop", required_argument, NULL, END_NEXTHOP + OO },
|
|
{ "client", required_argument, NULL, END_CLIENT + OO },
|
|
{ "clientwithin", required_argument, NULL, END_CLIENTWITHIN + OO },
|
|
{ "clientprotoport", required_argument, NULL, END_CLIENTPROTOPORT + OO },
|
|
{ "dnskeyondemand", no_argument, NULL, END_DNSKEYONDEMAND + OO },
|
|
{ "srcip", required_argument, NULL, END_SRCIP + OO },
|
|
{ "hostaccess", no_argument, NULL, END_HOSTACCESS + OO },
|
|
{ "updown", required_argument, NULL, END_UPDOWN + OO },
|
|
|
|
/* options for a connection description */
|
|
|
|
{ "to", no_argument, NULL, CD_TO + OO },
|
|
|
|
{ "psk", no_argument, NULL, CD_PSK + OO },
|
|
{ "rsasig", no_argument, NULL, CD_RSASIG + OO },
|
|
|
|
{ "encrypt", no_argument, NULL, CD_ENCRYPT + OO },
|
|
{ "authenticate", no_argument, NULL, CD_AUTHENTICATE + OO },
|
|
{ "compress", no_argument, NULL, CD_COMPRESS + OO },
|
|
{ "tunnel", no_argument, NULL, CD_TUNNEL + OO },
|
|
{ "tunnelipv4", no_argument, NULL, CD_TUNNELIPV4 + OO },
|
|
{ "tunnelipv6", no_argument, NULL, CD_TUNNELIPV6 + OO },
|
|
{ "pfs", no_argument, NULL, CD_PFS + OO },
|
|
{ "disablearrivalcheck", no_argument, NULL, CD_DISABLEARRIVALCHECK + OO },
|
|
{ "initiateontraffic", no_argument, NULL
|
|
, CD_SHUNT0 + (POLICY_SHUNT_TRAP >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
|
|
{ "pass", no_argument, NULL
|
|
, CD_SHUNT0 + (POLICY_SHUNT_PASS >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
|
|
{ "drop", no_argument, NULL
|
|
, CD_SHUNT0 + (POLICY_SHUNT_DROP >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
|
|
{ "reject", no_argument, NULL
|
|
, CD_SHUNT0 + (POLICY_SHUNT_REJECT >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
|
|
{ "failnone", no_argument, NULL
|
|
, CD_FAIL0 + (POLICY_FAIL_NONE >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
|
|
{ "failpass", no_argument, NULL
|
|
, CD_FAIL0 + (POLICY_FAIL_PASS >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
|
|
{ "faildrop", no_argument, NULL
|
|
, CD_FAIL0 + (POLICY_FAIL_DROP >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
|
|
{ "failreject", no_argument, NULL
|
|
, CD_FAIL0 + (POLICY_FAIL_REJECT >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
|
|
{ "dontrekey", no_argument, NULL, CD_DONT_REKEY + OO },
|
|
{ "ipv4", no_argument, NULL, CD_CONNIPV4 + OO },
|
|
{ "ipv6", no_argument, NULL, CD_CONNIPV6 + OO },
|
|
|
|
{ "ikelifetime", required_argument, NULL, CD_IKELIFETIME + OO + NUMERIC_ARG },
|
|
{ "ipseclifetime", required_argument, NULL, CD_IPSECLIFETIME + OO + NUMERIC_ARG },
|
|
{ "rekeymargin", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG },
|
|
{ "rekeywindow", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG }, /* OBSOLETE */
|
|
{ "rekeyfuzz", required_argument, NULL, CD_RKFUZZ + OO + NUMERIC_ARG },
|
|
{ "keyingtries", required_argument, NULL, CD_KTRIES + OO + NUMERIC_ARG },
|
|
{ "dpdaction", required_argument, NULL, CD_DPDACTION + OO },
|
|
{ "dpddelay", required_argument, NULL, CD_DPDDELAY + OO + NUMERIC_ARG },
|
|
{ "dpdtimeout", required_argument, NULL, CD_DPDTIMEOUT + OO + NUMERIC_ARG },
|
|
{ "ike", required_argument, NULL, CD_IKE + OO },
|
|
{ "pfsgroup", required_argument, NULL, CD_PFSGROUP + OO },
|
|
{ "esp", required_argument, NULL, CD_ESP + OO },
|
|
|
|
/* options for a ca description */
|
|
|
|
{ "caname", required_argument, NULL, CA_NAME + OO },
|
|
{ "cacert", required_argument, NULL, CA_CERT + OO },
|
|
{ "ldaphost", required_argument, NULL, CA_LDAPHOST + OO },
|
|
{ "ldapbase", required_argument, NULL, CA_LDAPBASE + OO },
|
|
{ "crluri", required_argument, NULL, CA_CRLURI + OO },
|
|
{ "crluri2", required_argument, NULL, CA_CRLURI2 + OO },
|
|
{ "ocspuri", required_argument, NULL, CA_OCSPURI + OO },
|
|
{ "strictcrlpolicy", no_argument, NULL, CA_STRICT + OO },
|
|
|
|
#ifdef DEBUG
|
|
{ "debug-none", no_argument, NULL, DBGOPT_NONE + OO },
|
|
{ "debug-all]", no_argument, NULL, DBGOPT_ALL + OO },
|
|
{ "debug-raw", no_argument, NULL, DBGOPT_RAW + OO },
|
|
{ "debug-crypt", no_argument, NULL, DBGOPT_CRYPT + OO },
|
|
{ "debug-parsing", no_argument, NULL, DBGOPT_PARSING + OO },
|
|
{ "debug-emitting", no_argument, NULL, DBGOPT_EMITTING + OO },
|
|
{ "debug-control", no_argument, NULL, DBGOPT_CONTROL + OO },
|
|
{ "debug-lifecycle", no_argument, NULL, DBGOPT_LIFECYCLE + OO },
|
|
{ "debug-klips", no_argument, NULL, DBGOPT_KLIPS + OO },
|
|
{ "debug-dns", no_argument, NULL, DBGOPT_DNS + OO },
|
|
{ "debug-natt", no_argument, NULL, DBGOPT_NATT + OO },
|
|
{ "debug-oppo", no_argument, NULL, DBGOPT_OPPO + OO },
|
|
{ "debug-controlmore", no_argument, NULL, DBGOPT_CONTROLMORE + OO },
|
|
{ "debug-private", no_argument, NULL, DBGOPT_PRIVATE + OO },
|
|
|
|
{ "impair-delay-adns-key-answer", no_argument, NULL, DBGOPT_IMPAIR_DELAY_ADNS_KEY_ANSWER + OO },
|
|
{ "impair-delay-adns-txt-answer", no_argument, NULL, DBGOPT_IMPAIR_DELAY_ADNS_TXT_ANSWER + OO },
|
|
{ "impair-bust-mi2", no_argument, NULL, DBGOPT_IMPAIR_BUST_MI2 + OO },
|
|
{ "impair-bust-mr2", no_argument, NULL, DBGOPT_IMPAIR_BUST_MR2 + OO },
|
|
#endif
|
|
# undef OO
|
|
{ 0,0,0,0 }
|
|
};
|
|
|
|
struct sockaddr_un ctl_addr = { AF_UNIX, DEFAULT_CTLBASE CTL_SUFFIX };
|
|
|
|
/* helper variables and function to encode strings from whack message */
|
|
|
|
static char
|
|
*next_str,
|
|
*str_roof;
|
|
|
|
static bool
|
|
pack_str(char **p)
|
|
{
|
|
const char *s = *p == NULL? "" : *p; /* note: NULL becomes ""! */
|
|
size_t len = strlen(s) + 1;
|
|
|
|
if (str_roof - next_str < (ptrdiff_t)len)
|
|
{
|
|
return FALSE; /* fishy: no end found */
|
|
}
|
|
else
|
|
{
|
|
strcpy(next_str, s);
|
|
next_str += len;
|
|
*p = NULL; /* don't send pointers on the wire! */
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
check_life_time(time_t life, time_t limit, const char *which
|
|
, const whack_message_t *msg)
|
|
{
|
|
time_t mint = msg->sa_rekey_margin * (100 + msg->sa_rekey_fuzz) / 100;
|
|
|
|
if (life > limit)
|
|
{
|
|
char buf[200]; /* arbitrary limit */
|
|
|
|
snprintf(buf, sizeof(buf)
|
|
, "%s [%lu seconds] must be less than %lu seconds"
|
|
, which, (unsigned long)life, (unsigned long)limit);
|
|
diag(buf);
|
|
}
|
|
if ((msg->policy & POLICY_DONT_REKEY) == LEMPTY && life <= mint)
|
|
{
|
|
char buf[200]; /* arbitrary limit */
|
|
|
|
snprintf(buf, sizeof(buf)
|
|
, "%s [%lu] must be greater than"
|
|
" rekeymargin*(100+rekeyfuzz)/100 [%lu*(100+%lu)/100 = %lu]"
|
|
, which
|
|
, (unsigned long)life
|
|
, (unsigned long)msg->sa_rekey_margin
|
|
, (unsigned long)msg->sa_rekey_fuzz
|
|
, (unsigned long)mint);
|
|
diag(buf);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clear_end(whack_end_t *e)
|
|
{
|
|
zero(e);
|
|
e->id = NULL;
|
|
e->cert = NULL;
|
|
e->ca = NULL;
|
|
e->updown = NULL;
|
|
e->host_port = IKE_UDP_PORT;
|
|
}
|
|
|
|
static void
|
|
update_ports(whack_message_t *m)
|
|
{
|
|
int port;
|
|
|
|
if (m->left.port != 0) {
|
|
port = htons(m->left.port);
|
|
setportof(port, &m->left.host_addr);
|
|
setportof(port, &m->left.client.addr);
|
|
}
|
|
if (m->right.port != 0) {
|
|
port = htons(m->right.port);
|
|
setportof(port, &m->right.host_addr);
|
|
setportof(port, &m->right.client.addr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
check_end(whack_end_t *this, whack_end_t *that
|
|
, bool default_nexthop, sa_family_t caf, sa_family_t taf)
|
|
{
|
|
if (caf != addrtypeof(&this->host_addr))
|
|
diag("address family of host inconsistent");
|
|
|
|
if (default_nexthop)
|
|
{
|
|
if (isanyaddr(&that->host_addr))
|
|
diag("our nexthop must be specified when other host is a %any or %opportunistic");
|
|
this->host_nexthop = that->host_addr;
|
|
}
|
|
|
|
if (caf != addrtypeof(&this->host_nexthop))
|
|
diag("address family of nexthop inconsistent");
|
|
|
|
if (this->has_client)
|
|
{
|
|
if (taf != subnettypeof(&this->client))
|
|
diag("address family of client subnet inconsistent");
|
|
}
|
|
else
|
|
{
|
|
/* fill in anyaddr-anyaddr as (missing) client subnet */
|
|
ip_address cn;
|
|
|
|
diagq(anyaddr(caf, &cn), NULL);
|
|
diagq(rangetosubnet(&cn, &cn, &this->client), NULL);
|
|
}
|
|
|
|
/* fill in anyaddr if source IP is not defined */
|
|
if (!this->has_srcip)
|
|
diagq(anyaddr(caf, &this->host_srcip), optarg);
|
|
|
|
/* check protocol */
|
|
if (this->protocol != that->protocol)
|
|
diag("the protocol for leftprotoport and rightprotoport must be the same");
|
|
}
|
|
|
|
static void
|
|
get_secret(int sock)
|
|
{
|
|
const char *buf, *secret;
|
|
int len;
|
|
|
|
fflush(stdout);
|
|
usleep(20000); /* give fflush time for flushing */
|
|
buf = getpass("Enter: ");
|
|
secret = (buf == NULL)? "" : buf;
|
|
|
|
/* send the secret to pluto */
|
|
len = strlen(secret) + 1;
|
|
if (write(sock, secret, len) != len)
|
|
{
|
|
int e = errno;
|
|
|
|
fprintf(stderr, "whack: write() failed (%d %s)\n", e, strerror(e));
|
|
exit(RC_WHACK_PROBLEM);
|
|
}
|
|
}
|
|
|
|
/* This is a hack for initiating ISAKMP exchanges. */
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
whack_message_t msg;
|
|
char esp_buf[256]; /* uses snprintf */
|
|
lset_t
|
|
opts_seen = LEMPTY,
|
|
sc_seen = LEMPTY,
|
|
lst_seen = LEMPTY,
|
|
cd_seen = LEMPTY,
|
|
ca_seen = LEMPTY,
|
|
end_seen = LEMPTY,
|
|
end_seen_before_to = LEMPTY;
|
|
const char
|
|
*af_used_by = NULL,
|
|
*tunnel_af_used_by = NULL;
|
|
|
|
/* check division of numbering space */
|
|
#ifdef DEBUG
|
|
assert(OPTION_OFFSET + DBGOPT_LAST < NUMERIC_ARG);
|
|
#else
|
|
assert(OPTION_OFFSET + CA_LAST < NUMERIC_ARG);
|
|
#endif
|
|
assert(OPT_LAST - OPT_FIRST < (sizeof opts_seen * BITS_PER_BYTE));
|
|
assert(SC_LAST - SC_FIRST < (sizeof sc_seen * BITS_PER_BYTE));
|
|
assert(LST_LAST - LST_FIRST < (sizeof lst_seen * BITS_PER_BYTE));
|
|
assert(END_LAST - END_FIRST < (sizeof end_seen * BITS_PER_BYTE));
|
|
assert(CD_LAST - CD_FIRST < (sizeof cd_seen * BITS_PER_BYTE));
|
|
assert(CA_LAST - CA_FIRST < (sizeof ca_seen * BITS_PER_BYTE));
|
|
#ifdef DEBUG /* must be last so others are less than (sizeof cd_seen * BITS_PER_BYTE) to fit in lset_t */
|
|
assert(DBGOPT_LAST - DBGOPT_FIRST < (sizeof cd_seen * BITS_PER_BYTE));
|
|
#endif
|
|
/* check that POLICY bit assignment matches with CD_ */
|
|
assert(LELEM(CD_DONT_REKEY - CD_POLICY_FIRST) == POLICY_DONT_REKEY);
|
|
|
|
zero(&msg);
|
|
|
|
clear_end(&msg.right); /* left set from this after --to */
|
|
|
|
msg.name = NULL;
|
|
msg.keyid = NULL;
|
|
msg.keyval.ptr = NULL;
|
|
msg.esp = NULL;
|
|
msg.ike = NULL;
|
|
msg.pfsgroup = NULL;
|
|
|
|
msg.sa_ike_life_seconds = OAKLEY_ISAKMP_SA_LIFETIME_DEFAULT;
|
|
msg.sa_ipsec_life_seconds = PLUTO_SA_LIFE_DURATION_DEFAULT;
|
|
msg.sa_rekey_margin = SA_REPLACEMENT_MARGIN_DEFAULT;
|
|
msg.sa_rekey_fuzz = SA_REPLACEMENT_FUZZ_DEFAULT;
|
|
msg.sa_keying_tries = SA_REPLACEMENT_RETRIES_DEFAULT;
|
|
|
|
msg.addr_family = AF_INET;
|
|
msg.tunnel_addr_family = AF_INET;
|
|
|
|
msg.cacert = NULL;
|
|
msg.ldaphost = NULL;
|
|
msg.ldapbase = NULL;
|
|
msg.crluri = NULL;
|
|
msg.crluri2 = NULL;
|
|
msg.ocspuri = NULL;
|
|
|
|
for (;;)
|
|
{
|
|
int long_index;
|
|
unsigned long opt_whole = 0; /* numeric argument for some flags */
|
|
|
|
/* Note: we don't like the way short options get parsed
|
|
* by getopt_long, so we simply pass an empty string as
|
|
* the list. It could be "hp:d:c:o:eatfs" "NARXPECK".
|
|
*/
|
|
int c = getopt_long(argc, argv, "", long_opts, &long_index) - OPTION_OFFSET;
|
|
int aux = 0;
|
|
|
|
/* decode a numeric argument, if expected */
|
|
if (0 <= c)
|
|
{
|
|
if (c & NUMERIC_ARG)
|
|
{
|
|
char *endptr;
|
|
|
|
c -= NUMERIC_ARG;
|
|
opt_whole = strtoul(optarg, &endptr, 0);
|
|
|
|
if (*endptr != '\0' || endptr == optarg)
|
|
diagq("badly formed numeric argument", optarg);
|
|
}
|
|
if (c >= (1 << AUX_SHIFT))
|
|
{
|
|
aux = c >> AUX_SHIFT;
|
|
c -= aux << AUX_SHIFT;
|
|
}
|
|
}
|
|
|
|
/* per-class option processing */
|
|
if (0 <= c && c <= OPT_LAST)
|
|
{
|
|
/* OPT_* options get added to opts_seen.
|
|
* Reject repeated options (unless later code intervenes).
|
|
*/
|
|
lset_t f = LELEM(c);
|
|
|
|
if (opts_seen & f)
|
|
diagq("duplicated flag", long_opts[long_index].name);
|
|
opts_seen |= f;
|
|
}
|
|
else if (SC_FIRST <= c && c <= SC_LAST)
|
|
{
|
|
/* SC_* options get added to sc_seen.
|
|
* Reject repeated options (unless later code intervenes).
|
|
*/
|
|
lset_t f = LELEM(c - SC_FIRST);
|
|
|
|
if (sc_seen & f)
|
|
diagq("duplicated flag", long_opts[long_index].name);
|
|
sc_seen |= f;
|
|
}
|
|
else if (LST_FIRST <= c && c <= LST_LAST)
|
|
{
|
|
/* LST_* options get added to lst_seen.
|
|
* Reject repeated options (unless later code intervenes).
|
|
*/
|
|
lset_t f = LELEM(c - LST_FIRST);
|
|
|
|
if (lst_seen & f)
|
|
diagq("duplicated flag", long_opts[long_index].name);
|
|
lst_seen |= f;
|
|
}
|
|
#ifdef DEBUG
|
|
else if (DBGOPT_FIRST <= c && c <= DBGOPT_LAST)
|
|
{
|
|
msg.whack_options = TRUE;
|
|
}
|
|
#endif
|
|
else if (END_FIRST <= c && c <= END_LAST)
|
|
{
|
|
/* END_* options are added to end_seen.
|
|
* Reject repeated options (unless later code intervenes).
|
|
*/
|
|
lset_t f = LELEM(c - END_FIRST);
|
|
|
|
if (end_seen & f)
|
|
diagq("duplicated flag", long_opts[long_index].name);
|
|
end_seen |= f;
|
|
opts_seen |= LELEM(OPT_CD);
|
|
}
|
|
else if (CD_FIRST <= c && c <= CD_LAST)
|
|
{
|
|
/* CD_* options are added to cd_seen.
|
|
* Reject repeated options (unless later code intervenes).
|
|
*/
|
|
lset_t f = LELEM(c - CD_FIRST);
|
|
|
|
if (cd_seen & f)
|
|
diagq("duplicated flag", long_opts[long_index].name);
|
|
cd_seen |= f;
|
|
opts_seen |= LELEM(OPT_CD);
|
|
}
|
|
else if (CA_FIRST <= c && c <= CA_LAST)
|
|
{
|
|
/* CA_* options are added to ca_seen.
|
|
* Reject repeated options (unless later code intervenes).
|
|
*/
|
|
lset_t f = LELEM(c - CA_FIRST);
|
|
|
|
if (ca_seen & f)
|
|
diagq("duplicated flag", long_opts[long_index].name);
|
|
ca_seen |= f;
|
|
}
|
|
|
|
/* Note: "break"ing from switch terminates loop.
|
|
* most cases should end with "continue".
|
|
*/
|
|
switch (c)
|
|
{
|
|
case EOF - OPTION_OFFSET: /* end of flags */
|
|
break;
|
|
|
|
case 0 - OPTION_OFFSET: /* long option already handled */
|
|
continue;
|
|
|
|
case ':' - OPTION_OFFSET: /* diagnostic already printed by getopt_long */
|
|
case '?' - OPTION_OFFSET: /* diagnostic already printed by getopt_long */
|
|
diag(NULL); /* print no additional diagnostic, but exit sadly */
|
|
break; /* not actually reached */
|
|
|
|
case 'h' - OPTION_OFFSET: /* --help */
|
|
help();
|
|
return 0; /* GNU coding standards say to stop here */
|
|
|
|
case 'v' - OPTION_OFFSET: /* --version */
|
|
{
|
|
const char **sp = ipsec_copyright_notice();
|
|
|
|
printf("%s\n", ipsec_version_string());
|
|
for (; *sp != NULL; sp++)
|
|
puts(*sp);
|
|
}
|
|
return 0; /* GNU coding standards say to stop here */
|
|
|
|
case 'l' - OPTION_OFFSET: /* --label <string> */
|
|
label = optarg; /* remember for diagnostics */
|
|
continue;
|
|
|
|
case '+' - OPTION_OFFSET: /* --optionsfrom <filename> */
|
|
optionsfrom(optarg, &argc, &argv, optind, stderr);
|
|
/* does not return on error */
|
|
continue;
|
|
|
|
/* the rest of the options combine in complex ways */
|
|
|
|
case OPT_CTLBASE: /* --port <ctlbase> */
|
|
if (snprintf(ctl_addr.sun_path, sizeof(ctl_addr.sun_path)
|
|
, "%s%s", optarg, CTL_SUFFIX) == -1)
|
|
diag("<ctlbase>" CTL_SUFFIX " must be fit in a sun_addr");
|
|
continue;
|
|
|
|
case OPT_NAME: /* --name <connection-name> */
|
|
name = optarg;
|
|
msg.name = optarg;
|
|
continue;
|
|
|
|
case OPT_KEYID: /* --keyid <identity> */
|
|
msg.whack_key = !msg.whack_sc_op;
|
|
msg.keyid = optarg; /* decoded by Pluto */
|
|
continue;
|
|
|
|
case OPT_MYID: /* --myid <identity> */
|
|
msg.whack_myid = TRUE;
|
|
msg.myid = optarg; /* decoded by Pluto */
|
|
continue;
|
|
|
|
case OPT_ADDKEY: /* --addkey */
|
|
msg.whack_addkey = TRUE;
|
|
continue;
|
|
|
|
case OPT_PUBKEYRSA: /* --pubkeyrsa <key> */
|
|
{
|
|
static char keyspace[RSA_MAX_ENCODING_BYTES]; /* room for 8K bit key */
|
|
char diag_space[TTODATAV_BUF];
|
|
const char *ugh = ttodatav(optarg, 0, 0
|
|
, keyspace, sizeof(keyspace)
|
|
, &msg.keyval.len, diag_space, sizeof(diag_space)
|
|
, TTODATAV_SPACECOUNTS);
|
|
|
|
if (ugh != NULL)
|
|
{
|
|
char ugh_space[80]; /* perhaps enough space */
|
|
|
|
snprintf(ugh_space, sizeof(ugh_space)
|
|
, "RSA public-key data malformed (%s)", ugh);
|
|
diagq(ugh_space, optarg);
|
|
}
|
|
msg.pubkey_alg = PUBKEY_ALG_RSA;
|
|
msg.keyval.ptr = keyspace;
|
|
}
|
|
continue;
|
|
|
|
case OPT_ROUTE: /* --route */
|
|
msg.whack_route = TRUE;
|
|
continue;
|
|
|
|
case OPT_UNROUTE: /* --unroute */
|
|
msg.whack_unroute = TRUE;
|
|
continue;
|
|
|
|
case OPT_INITIATE: /* --initiate */
|
|
msg.whack_initiate = TRUE;
|
|
continue;
|
|
|
|
case OPT_TERMINATE: /* --terminate */
|
|
msg.whack_terminate = TRUE;
|
|
continue;
|
|
|
|
case OPT_DELETE: /* --delete */
|
|
msg.whack_delete = TRUE;
|
|
continue;
|
|
|
|
case OPT_DELETESTATE: /* --deletestate <state_object_number> */
|
|
msg.whack_deletestate = TRUE;
|
|
msg.whack_deletestateno = opt_whole;
|
|
continue;
|
|
|
|
case OPT_DELETECRASH: /* --crash <ip-address> */
|
|
msg.whack_crash = TRUE;
|
|
tunnel_af_used_by = long_opts[long_index].name;
|
|
diagq(ttoaddr(optarg, 0, msg.tunnel_addr_family, &msg.whack_crash_peer), optarg);
|
|
if (isanyaddr(&msg.whack_crash_peer))
|
|
diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
|
|
continue;
|
|
|
|
case OPT_LISTEN: /* --listen */
|
|
msg.whack_listen = TRUE;
|
|
continue;
|
|
|
|
case OPT_UNLISTEN: /* --unlisten */
|
|
msg.whack_unlisten = TRUE;
|
|
continue;
|
|
|
|
case OPT_PURGEOCSP: /* --purgeocsp */
|
|
msg.whack_purgeocsp = TRUE;
|
|
continue;
|
|
|
|
case OPT_REREADSECRETS: /* --rereadsecrets */
|
|
case OPT_REREADCACERTS: /* --rereadcacerts */
|
|
case OPT_REREADAACERTS: /* --rereadaacerts */
|
|
case OPT_REREADOCSPCERTS: /* --rereadocspcerts */
|
|
case OPT_REREADACERTS: /* --rereadacerts */
|
|
case OPT_REREADCRLS: /* --rereadcrls */
|
|
msg.whack_reread |= LELEM(c-OPT_REREADSECRETS);
|
|
continue;
|
|
|
|
case OPT_REREADALL: /* --rereadall */
|
|
msg.whack_reread = REREAD_ALL;
|
|
continue;
|
|
|
|
case OPT_STATUSALL: /* --statusall */
|
|
msg.whack_statusall = TRUE;
|
|
|
|
case OPT_STATUS: /* --status */
|
|
msg.whack_status = TRUE;
|
|
continue;
|
|
|
|
case OPT_SHUTDOWN: /* --shutdown */
|
|
msg.whack_shutdown = TRUE;
|
|
continue;
|
|
|
|
case OPT_OPPO_HERE: /* --oppohere <ip-address> */
|
|
tunnel_af_used_by = long_opts[long_index].name;
|
|
diagq(ttoaddr(optarg, 0, msg.tunnel_addr_family, &msg.oppo_my_client), optarg);
|
|
if (isanyaddr(&msg.oppo_my_client))
|
|
diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
|
|
continue;
|
|
|
|
case OPT_OPPO_THERE: /* --oppohere <ip-address> */
|
|
tunnel_af_used_by = long_opts[long_index].name;
|
|
diagq(ttoaddr(optarg, 0, msg.tunnel_addr_family, &msg.oppo_peer_client), optarg);
|
|
if (isanyaddr(&msg.oppo_peer_client))
|
|
diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
|
|
continue;
|
|
|
|
case OPT_ASYNC:
|
|
msg.whack_async = TRUE;
|
|
continue;
|
|
|
|
/* Smartcard options */
|
|
|
|
case SC_ENCRYPT: /* --scencrypt <plaintext data> */
|
|
case SC_DECRYPT: /* --scdecrypt <encrypted data> */
|
|
msg.whack_sc_op = 1 + c - SC_ENCRYPT;
|
|
msg.whack_key = FALSE;
|
|
msg.sc_data = optarg;
|
|
continue;
|
|
|
|
case SC_INBASE: /* --inform <format> */
|
|
case SC_OUTBASE: /* --outform <format> */
|
|
{
|
|
int base = 0;
|
|
|
|
if (streq(optarg, "16") || strcaseeq(optarg, "hex"))
|
|
base = 16;
|
|
else if (streq(optarg, "64") || strcaseeq(optarg, "base64"))
|
|
base = 64;
|
|
else if (streq(optarg, "256") || strcaseeq(optarg, "text")
|
|
|| strcaseeq(optarg, "ascii"))
|
|
base = 256;
|
|
else
|
|
diagq("not a valid base", optarg);
|
|
|
|
if (c == SC_INBASE)
|
|
msg.inbase = base;
|
|
else
|
|
msg.outbase = base;
|
|
}
|
|
continue;
|
|
|
|
/* List options */
|
|
|
|
case LST_UTC: /* --utc */
|
|
msg.whack_utc = TRUE;
|
|
continue;
|
|
|
|
case LST_ALGS: /* --listalgs */
|
|
case LST_PUBKEYS: /* --listpubkeys */
|
|
case LST_CERTS: /* --listcerts */
|
|
case LST_CACERTS: /* --listcacerts */
|
|
case LST_ACERTS: /* --listacerts */
|
|
case LST_AACERTS: /* --listaacerts */
|
|
case LST_OCSPCERTS: /* --listocspcerts */
|
|
case LST_GROUPS: /* --listgroups */
|
|
case LST_CAINFOS: /* --listcainfos */
|
|
case LST_CRLS: /* --listcrls */
|
|
case LST_OCSP: /* --listocsp */
|
|
case LST_CARDS: /* --listcards */
|
|
msg.whack_list |= LELEM(c - LST_ALGS);
|
|
continue;
|
|
|
|
case LST_ALL: /* --listall */
|
|
msg.whack_list = LIST_ALL;
|
|
continue;
|
|
|
|
/* Connection Description options */
|
|
|
|
case END_HOST: /* --host <ip-address> */
|
|
{
|
|
lset_t new_policy = LEMPTY;
|
|
|
|
af_used_by = long_opts[long_index].name;
|
|
diagq(anyaddr(msg.addr_family, &msg.right.host_addr), optarg);
|
|
if (streq(optarg, "%any"))
|
|
{
|
|
}
|
|
else if (streq(optarg, "%opportunistic"))
|
|
{
|
|
/* always use tunnel mode; mark as opportunistic */
|
|
new_policy |= POLICY_TUNNEL | POLICY_OPPO;
|
|
}
|
|
else if (streq(optarg, "%group"))
|
|
{
|
|
/* always use tunnel mode; mark as group */
|
|
new_policy |= POLICY_TUNNEL | POLICY_GROUP;
|
|
}
|
|
else if (streq(optarg, "%opportunisticgroup"))
|
|
{
|
|
/* always use tunnel mode; mark as opportunistic */
|
|
new_policy |= POLICY_TUNNEL | POLICY_OPPO | POLICY_GROUP;
|
|
}
|
|
else
|
|
{
|
|
diagq(ttoaddr(optarg, 0, msg.addr_family
|
|
, &msg.right.host_addr), optarg);
|
|
}
|
|
|
|
msg.policy |= new_policy;
|
|
|
|
if (new_policy & (POLICY_OPPO | POLICY_GROUP))
|
|
{
|
|
if (!LHAS(end_seen, END_CLIENT - END_FIRST))
|
|
{
|
|
/* set host to 0.0.0 and --client to 0.0.0.0/0
|
|
* or IPV6 equivalent
|
|
*/
|
|
ip_address any;
|
|
|
|
tunnel_af_used_by = optarg;
|
|
diagq(anyaddr(msg.tunnel_addr_family, &any), optarg);
|
|
diagq(initsubnet(&any, 0, '0', &msg.right.client), optarg);
|
|
}
|
|
msg.right.has_client = TRUE;
|
|
}
|
|
if (new_policy & POLICY_GROUP)
|
|
{
|
|
/* client subnet must not be specified by user:
|
|
* it will come from the group's file.
|
|
*/
|
|
if (LHAS(end_seen, END_CLIENT - END_FIRST))
|
|
diag("--host %group clashes with --client");
|
|
|
|
end_seen |= LELEM(END_CLIENT - END_FIRST);
|
|
}
|
|
if (new_policy & POLICY_OPPO)
|
|
msg.right.key_from_DNS_on_demand = TRUE;
|
|
continue;
|
|
}
|
|
case END_ID: /* --id <identity> */
|
|
msg.right.id = optarg; /* decoded by Pluto */
|
|
continue;
|
|
|
|
case END_CERT: /* --cert <path> */
|
|
msg.right.cert = optarg; /* decoded by Pluto */
|
|
continue;
|
|
|
|
case END_CA: /* --ca <distinguished name> */
|
|
msg.right.ca = optarg; /* decoded by Pluto */
|
|
continue;
|
|
|
|
case END_SENDCERT:
|
|
if (streq(optarg, "yes") || streq(optarg, "always"))
|
|
{
|
|
msg.right.sendcert = CERT_ALWAYS_SEND;
|
|
}
|
|
else if (streq(optarg, "no") || streq(optarg, "never"))
|
|
{
|
|
msg.right.sendcert = CERT_NEVER_SEND;
|
|
}
|
|
else if (streq(optarg, "ifasked"))
|
|
{
|
|
msg.right.sendcert = CERT_SEND_IF_ASKED;
|
|
}
|
|
else
|
|
{
|
|
diagq("whack sendcert value is not legal", optarg);
|
|
}
|
|
continue;
|
|
|
|
case END_GROUPS:/* --groups <access control groups> */
|
|
msg.right.groups = optarg; /* decoded by Pluto */
|
|
continue;
|
|
|
|
case END_IKEPORT: /* --ikeport <port-number> */
|
|
if (opt_whole<=0 || opt_whole >= 0x10000)
|
|
diagq("<port-number> must be a number between 1 and 65535", optarg);
|
|
msg.right.host_port = opt_whole;
|
|
continue;
|
|
|
|
case END_NEXTHOP: /* --nexthop <ip-address> */
|
|
af_used_by = long_opts[long_index].name;
|
|
if (streq(optarg, "%direct"))
|
|
diagq(anyaddr(msg.addr_family
|
|
, &msg.right.host_nexthop), optarg);
|
|
else
|
|
diagq(ttoaddr(optarg, 0, msg.addr_family
|
|
, &msg.right.host_nexthop), optarg);
|
|
continue;
|
|
|
|
case END_SRCIP: /* --srcip <ip-address> */
|
|
af_used_by = long_opts[long_index].name;
|
|
if (streq(optarg, "%modeconfig") || streq(optarg, "%modecfg"))
|
|
{
|
|
msg.right.modecfg = TRUE;
|
|
}
|
|
else
|
|
{
|
|
diagq(ttoaddr(optarg, 0, msg.addr_family
|
|
, &msg.right.host_srcip), optarg);
|
|
msg.right.has_srcip = TRUE;
|
|
}
|
|
msg.policy |= POLICY_TUNNEL; /* srcip => tunnel */
|
|
continue;
|
|
|
|
case END_CLIENT: /* --client <subnet> */
|
|
if (end_seen & LELEM(END_CLIENTWITHIN - END_FIRST))
|
|
diag("--client conflicts with --clientwithin");
|
|
tunnel_af_used_by = long_opts[long_index].name;
|
|
#ifdef VIRTUAL_IP
|
|
if ((strlen(optarg) >= 6 && strncmp(optarg,"vhost:",6) == 0)
|
|
|| (strlen(optarg) >= 5 && strncmp(optarg,"vnet:",5) == 0))
|
|
{
|
|
msg.right.virt = optarg;
|
|
}
|
|
else
|
|
{
|
|
diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
|
|
msg.right.has_client = TRUE;
|
|
}
|
|
#else
|
|
diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
|
|
msg.right.has_client = TRUE;
|
|
#endif
|
|
msg.policy |= POLICY_TUNNEL; /* client => tunnel */
|
|
continue;
|
|
|
|
case END_CLIENTWITHIN: /* --clienwithin <address range> */
|
|
if (end_seen & LELEM(END_CLIENT - END_FIRST))
|
|
diag("--clientwithin conflicts with --client");
|
|
tunnel_af_used_by = long_opts[long_index].name;
|
|
diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
|
|
msg.right.has_client = TRUE;
|
|
msg.policy |= POLICY_TUNNEL; /* client => tunnel */
|
|
msg.right.has_client_wildcard = TRUE;
|
|
continue;
|
|
|
|
case END_CLIENTPROTOPORT: /* --clientprotoport <protocol>/<port> */
|
|
diagq(ttoprotoport(optarg, 0, &msg.right.protocol, &msg.right.port
|
|
, &msg.right.has_port_wildcard), optarg);
|
|
continue;
|
|
|
|
case END_DNSKEYONDEMAND: /* --dnskeyondemand */
|
|
msg.right.key_from_DNS_on_demand = TRUE;
|
|
continue;
|
|
|
|
case END_HOSTACCESS: /* --hostaccess */
|
|
msg.right.hostaccess = TRUE;
|
|
continue;
|
|
|
|
case END_UPDOWN: /* --updown <updown> */
|
|
msg.right.updown = optarg;
|
|
continue;
|
|
|
|
case CD_TO: /* --to */
|
|
/* process right end, move it to left, reset it */
|
|
if (!LHAS(end_seen, END_HOST - END_FIRST))
|
|
diag("connection missing --host before --to");
|
|
msg.left = msg.right;
|
|
clear_end(&msg.right);
|
|
end_seen_before_to = end_seen;
|
|
end_seen = LEMPTY;
|
|
continue;
|
|
|
|
case CD_PSK: /* --psk */
|
|
case CD_RSASIG: /* --rsasig */
|
|
case CD_ENCRYPT: /* --encrypt */
|
|
case CD_AUTHENTICATE: /* --authenticate */
|
|
case CD_COMPRESS: /* --compress */
|
|
case CD_TUNNEL: /* --tunnel */
|
|
case CD_PFS: /* --pfs */
|
|
case CD_DISABLEARRIVALCHECK: /* --disablearrivalcheck */
|
|
case CD_DONT_REKEY: /* --donotrekey */
|
|
msg.policy |= LELEM(c - CD_POLICY_FIRST);
|
|
continue;
|
|
|
|
/* --initiateontraffic
|
|
* --pass
|
|
* --drop
|
|
* --reject
|
|
*/
|
|
case CD_SHUNT0:
|
|
msg.policy = (msg.policy & ~POLICY_SHUNT_MASK)
|
|
| ((lset_t)aux << POLICY_SHUNT_SHIFT);
|
|
continue;
|
|
|
|
/* --failnone
|
|
* --failpass
|
|
* --faildrop
|
|
* --failreject
|
|
*/
|
|
case CD_FAIL0:
|
|
msg.policy = (msg.policy & ~POLICY_FAIL_MASK)
|
|
| ((lset_t)aux << POLICY_FAIL_SHIFT);
|
|
continue;
|
|
|
|
case CD_IKELIFETIME: /* --ikelifetime <seconds> */
|
|
msg.sa_ike_life_seconds = opt_whole;
|
|
continue;
|
|
|
|
case CD_IPSECLIFETIME: /* --ipseclifetime <seconds> */
|
|
msg.sa_ipsec_life_seconds = opt_whole;
|
|
continue;
|
|
|
|
case CD_RKMARGIN: /* --rekeymargin <seconds> */
|
|
msg.sa_rekey_margin = opt_whole;
|
|
continue;
|
|
|
|
case CD_RKFUZZ: /* --rekeyfuzz <percentage> */
|
|
msg.sa_rekey_fuzz = opt_whole;
|
|
continue;
|
|
|
|
case CD_KTRIES: /* --keyingtries <count> */
|
|
msg.sa_keying_tries = opt_whole;
|
|
continue;
|
|
|
|
case CD_DPDACTION:
|
|
if (streq(optarg, "none"))
|
|
msg.dpd_action = DPD_ACTION_NONE;
|
|
else if (streq(optarg, "clear"))
|
|
msg.dpd_action = DPD_ACTION_CLEAR;
|
|
else if (streq(optarg, "hold"))
|
|
msg.dpd_action = DPD_ACTION_HOLD;
|
|
else if (streq(optarg, "restart"))
|
|
msg.dpd_action = DPD_ACTION_RESTART;
|
|
else
|
|
msg.dpd_action = DPD_ACTION_UNKNOWN;
|
|
continue;
|
|
|
|
case CD_DPDDELAY:
|
|
msg.dpd_delay = opt_whole;
|
|
continue;
|
|
|
|
case CD_DPDTIMEOUT:
|
|
msg.dpd_timeout = opt_whole;
|
|
continue;
|
|
|
|
case CD_IKE: /* --ike <ike_alg1,ike_alg2,...> */
|
|
msg.ike = optarg;
|
|
continue;
|
|
|
|
case CD_PFSGROUP: /* --pfsgroup modpXXXX */
|
|
msg.pfsgroup = optarg;
|
|
continue;
|
|
|
|
case CD_ESP: /* --esp <esp_alg1,esp_alg2,...> */
|
|
msg.esp = optarg;
|
|
continue;
|
|
|
|
case CD_CONNIPV4:
|
|
if (LHAS(cd_seen, CD_CONNIPV6 - CD_FIRST))
|
|
diag("--ipv4 conflicts with --ipv6");
|
|
|
|
/* Since this is the default, the flag is redundant.
|
|
* So we don't need to set msg.addr_family
|
|
* and we don't need to check af_used_by
|
|
* and we don't have to consider defaulting tunnel_addr_family.
|
|
*/
|
|
continue;
|
|
|
|
case CD_CONNIPV6:
|
|
if (LHAS(cd_seen, CD_CONNIPV4 - CD_FIRST))
|
|
diag("--ipv6 conflicts with --ipv4");
|
|
|
|
if (af_used_by != NULL)
|
|
diagq("--ipv6 must precede", af_used_by);
|
|
|
|
af_used_by = long_opts[long_index].name;
|
|
msg.addr_family = AF_INET6;
|
|
|
|
/* Consider defaulting tunnel_addr_family to AF_INET6.
|
|
* Do so only if it hasn't yet been specified or used.
|
|
*/
|
|
if (LDISJOINT(cd_seen, LELEM(CD_TUNNELIPV4 - CD_FIRST) | LELEM(CD_TUNNELIPV6 - CD_FIRST))
|
|
&& tunnel_af_used_by == NULL)
|
|
msg.tunnel_addr_family = AF_INET6;
|
|
continue;
|
|
|
|
case CD_TUNNELIPV4:
|
|
if (LHAS(cd_seen, CD_TUNNELIPV6 - CD_FIRST))
|
|
diag("--tunnelipv4 conflicts with --tunnelipv6");
|
|
|
|
if (tunnel_af_used_by != NULL)
|
|
diagq("--tunnelipv4 must precede", af_used_by);
|
|
|
|
msg.tunnel_addr_family = AF_INET;
|
|
continue;
|
|
|
|
case CD_TUNNELIPV6:
|
|
if (LHAS(cd_seen, CD_TUNNELIPV4 - CD_FIRST))
|
|
diag("--tunnelipv6 conflicts with --tunnelipv4");
|
|
|
|
if (tunnel_af_used_by != NULL)
|
|
diagq("--tunnelipv6 must precede", af_used_by);
|
|
|
|
msg.tunnel_addr_family = AF_INET6;
|
|
continue;
|
|
|
|
case CA_NAME: /* --caname <name> */
|
|
msg.name = optarg;
|
|
msg.whack_ca = TRUE;
|
|
continue;
|
|
case CA_CERT: /* --cacert <path> */
|
|
msg.cacert = optarg;
|
|
continue;
|
|
case CA_LDAPHOST: /* --ldaphost <hostname> */
|
|
msg.ldaphost = optarg;
|
|
continue;
|
|
case CA_LDAPBASE: /* --ldapbase <base> */
|
|
msg.ldapbase = optarg;
|
|
continue;
|
|
case CA_CRLURI: /* --crluri <uri> */
|
|
msg.crluri = optarg;
|
|
continue;
|
|
case CA_CRLURI2: /* --crluri2 <uri> */
|
|
msg.crluri2 = optarg;
|
|
continue;
|
|
case CA_OCSPURI: /* --ocspuri <uri> */
|
|
msg.ocspuri = optarg;
|
|
continue;
|
|
case CA_STRICT: /* --strictcrlpolicy */
|
|
msg.whack_strict = TRUE;
|
|
continue;
|
|
|
|
#ifdef DEBUG
|
|
case DBGOPT_NONE: /* --debug-none */
|
|
msg.debugging = DBG_NONE;
|
|
continue;
|
|
|
|
case DBGOPT_ALL: /* --debug-all */
|
|
msg.debugging |= DBG_ALL; /* note: does not include PRIVATE */
|
|
continue;
|
|
|
|
case DBGOPT_RAW: /* --debug-raw */
|
|
case DBGOPT_CRYPT: /* --debug-crypt */
|
|
case DBGOPT_PARSING: /* --debug-parsing */
|
|
case DBGOPT_EMITTING: /* --debug-emitting */
|
|
case DBGOPT_CONTROL: /* --debug-control */
|
|
case DBGOPT_LIFECYCLE: /* --debug-lifecycle */
|
|
case DBGOPT_KLIPS: /* --debug-klips */
|
|
case DBGOPT_DNS: /* --debug-dns */
|
|
case DBGOPT_NATT: /* --debug-natt */
|
|
case DBGOPT_OPPO: /* --debug-oppo */
|
|
case DBGOPT_CONTROLMORE: /* --debug-controlmore */
|
|
case DBGOPT_PRIVATE: /* --debug-private */
|
|
case DBGOPT_IMPAIR_DELAY_ADNS_KEY_ANSWER: /* --impair-delay-adns-key-answer */
|
|
case DBGOPT_IMPAIR_DELAY_ADNS_TXT_ANSWER: /* --impair-delay-adns-txt-answer */
|
|
case DBGOPT_IMPAIR_BUST_MI2: /* --impair_bust_mi2 */
|
|
case DBGOPT_IMPAIR_BUST_MR2: /* --impair_bust_mr2 */
|
|
msg.debugging |= LELEM(c-DBGOPT_RAW);
|
|
continue;
|
|
#endif
|
|
default:
|
|
assert(FALSE); /* unknown return value */
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (optind != argc)
|
|
{
|
|
/* If you see this message unexpectedly, perhaps the
|
|
* case for the previous option ended with "break"
|
|
* instead of "continue"
|
|
*/
|
|
diagq("unexpected argument", argv[optind]);
|
|
}
|
|
|
|
/* For each possible form of the command, figure out if an argument
|
|
* suggests whether that form was intended, and if so, whether all
|
|
* required information was supplied.
|
|
*/
|
|
|
|
/* check opportunistic initiation simulation request */
|
|
switch (opts_seen & (LELEM(OPT_OPPO_HERE) | LELEM(OPT_OPPO_THERE)))
|
|
{
|
|
case LELEM(OPT_OPPO_HERE):
|
|
case LELEM(OPT_OPPO_THERE):
|
|
diag("--oppohere and --oppothere must be used together");
|
|
/*NOTREACHED*/
|
|
case LELEM(OPT_OPPO_HERE) | LELEM(OPT_OPPO_THERE):
|
|
msg.whack_oppo_initiate = TRUE;
|
|
if (LIN(cd_seen, LELEM(CD_TUNNELIPV4 - CD_FIRST) | LELEM(CD_TUNNELIPV6 - CD_FIRST)))
|
|
opts_seen &= ~LELEM(OPT_CD);
|
|
break;
|
|
}
|
|
|
|
/* check connection description */
|
|
if (LHAS(opts_seen, OPT_CD))
|
|
{
|
|
if (!LHAS(cd_seen, CD_TO-CD_FIRST))
|
|
diag("connection description option, but no --to");
|
|
|
|
if (!LHAS(end_seen, END_HOST-END_FIRST))
|
|
diag("connection missing --host after --to");
|
|
|
|
if (isanyaddr(&msg.left.host_addr)
|
|
&& isanyaddr(&msg.right.host_addr))
|
|
diag("hosts cannot both be 0.0.0.0 or 0::0");
|
|
|
|
if (msg.policy & POLICY_OPPO)
|
|
{
|
|
if ((msg.policy & (POLICY_PSK | POLICY_RSASIG)) != POLICY_RSASIG)
|
|
diag("only RSASIG is supported for opportunism");
|
|
if ((msg.policy & POLICY_PFS) == 0)
|
|
diag("PFS required for opportunism");
|
|
if ((msg.policy & POLICY_ENCRYPT) == 0)
|
|
diag("encryption required for opportunism");
|
|
}
|
|
|
|
check_end(&msg.left, &msg.right, !LHAS(end_seen_before_to, END_NEXTHOP-END_FIRST)
|
|
, msg.addr_family, msg.tunnel_addr_family);
|
|
|
|
check_end(&msg.right, &msg.left, !LHAS(end_seen, END_NEXTHOP-END_FIRST)
|
|
, msg.addr_family, msg.tunnel_addr_family);
|
|
|
|
if (subnettypeof(&msg.left.client) != subnettypeof(&msg.right.client))
|
|
diag("endpoints clash: one is IPv4 and the other is IPv6");
|
|
|
|
if (NEVER_NEGOTIATE(msg.policy))
|
|
{
|
|
/* we think this is just a shunt (because he didn't specify
|
|
* a host authentication method). If he didn't specify a
|
|
* shunt type, he's probably gotten it wrong.
|
|
*/
|
|
if ((msg.policy & POLICY_SHUNT_MASK) == POLICY_SHUNT_TRAP)
|
|
diag("non-shunt connection must have --psk or --rsasig or both");
|
|
}
|
|
else
|
|
{
|
|
/* not just a shunt: a real ipsec connection */
|
|
if ((msg.policy & POLICY_ID_AUTH_MASK) == LEMPTY)
|
|
diag("must specify --rsasig or --psk for a connection");
|
|
|
|
if (!HAS_IPSEC_POLICY(msg.policy)
|
|
&& (msg.left.has_client || msg.right.has_client))
|
|
diag("must not specify clients for ISAKMP-only connection");
|
|
}
|
|
|
|
msg.whack_connection = TRUE;
|
|
}
|
|
|
|
/* decide whether --name is mandatory or forbidden */
|
|
if (!LDISJOINT(opts_seen
|
|
, LELEM(OPT_ROUTE) | LELEM(OPT_UNROUTE)
|
|
| LELEM(OPT_INITIATE) | LELEM(OPT_TERMINATE)
|
|
| LELEM(OPT_DELETE) | LELEM(OPT_CD)))
|
|
{
|
|
if (!LHAS(opts_seen, OPT_NAME) && !msg.whack_ca)
|
|
diag("missing --name <connection_name>");
|
|
}
|
|
else if (!msg.whack_options && !msg.whack_status)
|
|
{
|
|
if (LHAS(opts_seen, OPT_NAME))
|
|
diag("no reason for --name");
|
|
}
|
|
|
|
if (!LDISJOINT(opts_seen, LELEM(OPT_PUBKEYRSA) | LELEM(OPT_ADDKEY)))
|
|
{
|
|
if (!LHAS(opts_seen, OPT_KEYID))
|
|
diag("--addkey and --pubkeyrsa require --keyid");
|
|
}
|
|
|
|
if (!(msg.whack_connection || msg.whack_key || msg.whack_myid
|
|
|| msg.whack_delete || msg.whack_deletestate
|
|
|| msg.whack_initiate || msg.whack_oppo_initiate || msg.whack_terminate
|
|
|| msg.whack_route || msg.whack_unroute || msg.whack_listen
|
|
|| msg.whack_unlisten || msg.whack_list || msg.whack_purgeocsp || msg.whack_reread
|
|
|| msg.whack_ca || msg.whack_status || msg.whack_options || msg.whack_shutdown
|
|
|| msg.whack_sc_op))
|
|
{
|
|
diag("no action specified; try --help for hints");
|
|
}
|
|
|
|
update_ports(&msg);
|
|
|
|
/* tricky quick and dirty check for wild values */
|
|
if (msg.sa_rekey_margin != 0
|
|
&& msg.sa_rekey_fuzz * msg.sa_rekey_margin * 4 / msg.sa_rekey_margin / 4
|
|
!= msg.sa_rekey_fuzz)
|
|
diag("rekeymargin or rekeyfuzz values are so large that they cause oveflow");
|
|
|
|
check_life_time (msg.sa_ike_life_seconds, OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM
|
|
, "ikelifetime", &msg);
|
|
|
|
check_life_time(msg.sa_ipsec_life_seconds, SA_LIFE_DURATION_MAXIMUM
|
|
, "ipseclifetime", &msg);
|
|
|
|
if (msg.dpd_action == DPD_ACTION_UNKNOWN)
|
|
diag("dpdaction must be \"none\", \"clear\", \"hold\" or \"restart\"");
|
|
|
|
if (msg.dpd_action != DPD_ACTION_NONE)
|
|
{
|
|
if (msg.dpd_delay <= 0)
|
|
diag("dpddelay must be larger than zero");
|
|
|
|
if (msg.dpd_timeout <= 0)
|
|
diag("dpdtimeout must be larger than zero");
|
|
|
|
if (msg.dpd_timeout <= msg.dpd_delay)
|
|
diag("dpdtimeout must be larger than dpddelay");
|
|
}
|
|
|
|
/* pack strings for inclusion in message */
|
|
next_str = msg.string;
|
|
str_roof = &msg.string[sizeof(msg.string)];
|
|
|
|
/* build esp message as esp="<esp>;<pfsgroup>" */
|
|
if (msg.pfsgroup) {
|
|
snprintf(esp_buf, sizeof (esp_buf), "%s;%s",
|
|
msg.esp ? msg.esp : "",
|
|
msg.pfsgroup ? msg.pfsgroup : "");
|
|
msg.esp=esp_buf;
|
|
}
|
|
if (!pack_str(&msg.name) /* string 1 */
|
|
|| !pack_str(&msg.left.id) /* string 2 */
|
|
|| !pack_str(&msg.left.cert) /* string 3 */
|
|
|| !pack_str(&msg.left.ca) /* string 4 */
|
|
|| !pack_str(&msg.left.groups) /* string 5 */
|
|
|| !pack_str(&msg.left.updown) /* string 6 */
|
|
#ifdef VIRTUAL_IP
|
|
|| !pack_str(&msg.left.virt)
|
|
#endif
|
|
|| !pack_str(&msg.right.id) /* string 7 */
|
|
|| !pack_str(&msg.right.cert) /* string 8 */
|
|
|| !pack_str(&msg.right.ca) /* string 9 */
|
|
|| !pack_str(&msg.right.groups) /* string 10 */
|
|
|| !pack_str(&msg.right.updown) /* string 11 */
|
|
#ifdef VIRTUAL_IP
|
|
|| !pack_str(&msg.right.virt)
|
|
#endif
|
|
|| !pack_str(&msg.keyid) /* string 12 */
|
|
|| !pack_str(&msg.myid) /* string 13 */
|
|
|| !pack_str(&msg.cacert) /* string 14 */
|
|
|| !pack_str(&msg.ldaphost) /* string 15 */
|
|
|| !pack_str(&msg.ldapbase) /* string 16 */
|
|
|| !pack_str(&msg.crluri) /* string 17 */
|
|
|| !pack_str(&msg.crluri2) /* string 18 */
|
|
|| !pack_str(&msg.ocspuri) /* string 19 */
|
|
|| !pack_str(&msg.ike) /* string 20 */
|
|
|| !pack_str(&msg.esp) /* string 21 */
|
|
|| !pack_str(&msg.sc_data) /* string 22 */
|
|
|| str_roof - next_str < (ptrdiff_t)msg.keyval.len) /* chunk (sort of string 5) */
|
|
diag("too many bytes of strings to fit in message to pluto");
|
|
|
|
memcpy(next_str, msg.keyval.ptr, msg.keyval.len);
|
|
msg.keyval.ptr = NULL;
|
|
next_str += msg.keyval.len;
|
|
|
|
msg.magic = ((opts_seen & ~LELEM(OPT_SHUTDOWN))
|
|
| sc_seen | lst_seen | cd_seen | ca_seen) != LEMPTY
|
|
|| msg.whack_options
|
|
? WHACK_MAGIC : WHACK_BASIC_MAGIC;
|
|
|
|
/* send message to Pluto */
|
|
if (access(ctl_addr.sun_path, R_OK | W_OK) < 0)
|
|
{
|
|
int e = errno;
|
|
|
|
switch (e)
|
|
{
|
|
case EACCES:
|
|
fprintf(stderr, "whack: no right to communicate with pluto (access(\"%s\"))\n"
|
|
, ctl_addr.sun_path);
|
|
break;
|
|
case ENOENT:
|
|
fprintf(stderr, "whack: Pluto is not running (no \"%s\")\n"
|
|
, ctl_addr.sun_path);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "whack: access(\"%s\") failed with %d %s\n"
|
|
, ctl_addr.sun_path, errno, strerror(e));
|
|
break;
|
|
}
|
|
exit(RC_WHACK_PROBLEM);
|
|
}
|
|
else
|
|
{
|
|
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
int exit_status = 0;
|
|
ssize_t len = next_str - (char *)&msg;
|
|
|
|
if (sock == -1)
|
|
{
|
|
int e = errno;
|
|
|
|
fprintf(stderr, "whack: socket() failed (%d %s)\n", e, strerror(e));
|
|
exit(RC_WHACK_PROBLEM);
|
|
}
|
|
|
|
if (connect(sock, (struct sockaddr *)&ctl_addr
|
|
, offsetof(struct sockaddr_un, sun_path) + strlen(ctl_addr.sun_path)) < 0)
|
|
{
|
|
int e = errno;
|
|
|
|
fprintf(stderr, "whack:%s connect() for \"%s\" failed (%d %s)\n"
|
|
, e == ECONNREFUSED? " is Pluto running? " : ""
|
|
, ctl_addr.sun_path, e, strerror(e));
|
|
exit(RC_WHACK_PROBLEM);
|
|
}
|
|
|
|
if (write(sock, &msg, len) != len)
|
|
{
|
|
int e = errno;
|
|
|
|
fprintf(stderr, "whack: write() failed (%d %s)\n", e, strerror(e));
|
|
exit(RC_WHACK_PROBLEM);
|
|
}
|
|
|
|
/* for now, just copy reply back to stdout */
|
|
|
|
{
|
|
char buf[4097]; /* arbitrary limit on log line length */
|
|
char *be = buf;
|
|
|
|
for (;;)
|
|
{
|
|
char *ls = buf;
|
|
ssize_t rl = read(sock, be, (buf + sizeof(buf)-1) - be);
|
|
|
|
if (rl < 0)
|
|
{
|
|
int e = errno;
|
|
|
|
fprintf(stderr, "whack: read() failed (%d %s)\n", e, strerror(e));
|
|
exit(RC_WHACK_PROBLEM);
|
|
}
|
|
if (rl == 0)
|
|
{
|
|
if (be != buf)
|
|
fprintf(stderr, "whack: last line from pluto too long or unterminated\n");
|
|
break;
|
|
}
|
|
|
|
be += rl;
|
|
*be = '\0';
|
|
|
|
for (;;)
|
|
{
|
|
char *le = strchr(ls, '\n');
|
|
|
|
if (le == NULL)
|
|
{
|
|
/* move last, partial line to start of buffer */
|
|
memmove(buf, ls, be-ls);
|
|
be -= ls - buf;
|
|
break;
|
|
}
|
|
|
|
le++; /* include NL in line */
|
|
write(1, ls, le - ls);
|
|
|
|
/* figure out prefix number
|
|
* and how it should affect our exit status
|
|
*/
|
|
{
|
|
unsigned long s = strtoul(ls, NULL, 10);
|
|
|
|
switch (s)
|
|
{
|
|
case RC_COMMENT:
|
|
case RC_LOG:
|
|
/* ignore */
|
|
break;
|
|
case RC_SUCCESS:
|
|
/* be happy */
|
|
exit_status = 0;
|
|
break;
|
|
case RC_ENTERSECRET:
|
|
get_secret(sock);
|
|
break;
|
|
/* case RC_LOG_SERIOUS: */
|
|
default:
|
|
/* pass through */
|
|
exit_status = s;
|
|
break;
|
|
}
|
|
}
|
|
ls = le;
|
|
}
|
|
}
|
|
}
|
|
return exit_status;
|
|
}
|
|
}
|