aboutsummaryrefslogtreecommitdiffstats
path: root/src/whack
diff options
context:
space:
mode:
authorMartin Willi <martin@strongswan.org>2006-05-10 13:16:27 +0000
committerMartin Willi <martin@strongswan.org>2006-05-10 13:16:27 +0000
commit9cf5f29027969d5477fe190a8bf85f1aef457179 (patch)
tree120bd8cc9fc8dc1fdc3b2378c94b1df79ada0d9b /src/whack
parent4e98759d32841d3f4f67feba9f7aaa842fdf3acf (diff)
downloadstrongswan-9cf5f29027969d5477fe190a8bf85f1aef457179.tar.bz2
strongswan-9cf5f29027969d5477fe190a8bf85f1aef457179.tar.xz
Diffstat (limited to 'src/whack')
-rw-r--r--src/whack/whack.c1911
-rw-r--r--src/whack/whack.h316
2 files changed, 2227 insertions, 0 deletions
diff --git a/src/whack/whack.c b/src/whack/whack.c
new file mode 100644
index 000000000..a3b983771
--- /dev/null
+++ b/src/whack/whack.c
@@ -0,0 +1,1911 @@
+/* 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;
+ }
+}
diff --git a/src/whack/whack.h b/src/whack/whack.h
new file mode 100644
index 000000000..a62853f61
--- /dev/null
+++ b/src/whack/whack.h
@@ -0,0 +1,316 @@
+/* Structure of messages from whack to Pluto proper.
+ * 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.h,v 1.16 2006/04/17 10:39:14 as Exp $
+ */
+
+#ifndef _WHACK_H
+#define _WHACK_H
+
+#include <freeswan.h>
+
+/* Since the message remains on one host, native representation is used.
+ * Think of this as horizontal microcode: all selected operations are
+ * to be done (in the order declared here).
+ *
+ * MAGIC is used to help detect version mismatches between whack and Pluto.
+ * Whenever the interface (i.e. this struct) changes in form or
+ * meaning, change this value (probably by changing the last number).
+ *
+ * If the command only requires basic actions (status or shutdown),
+ * it is likely that the relevant part of the message changes less frequently.
+ * Whack uses WHACK_BASIC_MAGIC in those cases.
+ *
+ * NOTE: no value of WHACK_BASIC_MAGIC may equal any value of WHACK_MAGIC.
+ * Otherwise certain version mismatches will not be detected.
+ */
+
+#define WHACK_BASIC_MAGIC (((((('w' << 8) + 'h') << 8) + 'k') << 8) + 24)
+#define WHACK_MAGIC (((((('w' << 8) + 'h') << 8) + 'k') << 8) + 26)
+
+typedef struct whack_end whack_end_t;
+
+/* struct whack_end is a lot like connection.h's struct end
+ * It differs because it is going to be shipped down a socket
+ * and because whack is a separate program from pluto.
+ */
+struct whack_end {
+ char *id; /* id string (if any) -- decoded by pluto */
+ char *cert; /* path string (if any) -- loaded by pluto */
+ char *ca; /* distinguished name string (if any) -- parsed by pluto */
+ char *groups; /* access control groups (if any) -- parsed by pluto */
+ ip_address
+ host_addr,
+ host_nexthop,
+ host_srcip;
+ ip_subnet client;
+
+ bool key_from_DNS_on_demand;
+ bool has_client;
+ bool has_client_wildcard;
+ bool has_port_wildcard;
+ bool has_srcip;
+ bool modecfg;
+ bool hostaccess;
+ certpolicy_t sendcert;
+ char *updown; /* string */
+ u_int16_t host_port; /* host order */
+ u_int16_t port; /* host order */
+ u_int8_t protocol;
+#ifdef VIRTUAL_IP
+ char *virt;
+#endif
+ };
+
+typedef struct whack_message whack_message_t;
+
+struct whack_message {
+ unsigned int magic;
+
+ /* for WHACK_STATUS: */
+ bool whack_status;
+ bool whack_statusall;
+
+
+ /* for WHACK_SHUTDOWN */
+ bool whack_shutdown;
+
+ /* END OF BASIC COMMANDS
+ * If you change anything earlier in this struct, update WHACK_BASIC_MAGIC.
+ */
+
+ /* name is used in connection, ca and initiate */
+ size_t name_len; /* string 1 */
+ char *name;
+
+ /* for WHACK_OPTIONS: */
+
+ bool whack_options;
+
+ lset_t debugging; /* only used #ifdef DEBUG, but don't want layout to change */
+
+ /* for WHACK_CONNECTION */
+
+ bool whack_connection;
+ bool whack_async;
+
+ lset_t policy;
+ time_t sa_ike_life_seconds;
+ time_t sa_ipsec_life_seconds;
+ time_t sa_rekey_margin;
+ unsigned long sa_rekey_fuzz;
+ unsigned long sa_keying_tries;
+
+ /* For DPD 3706 - Dead Peer Detection */
+ time_t dpd_delay;
+ time_t dpd_timeout;
+ dpd_action_t dpd_action;
+
+ /* note that each end contains string 2/5.id, string 3/6 cert,
+ * and string 4/7 updown
+ */
+ whack_end_t left;
+ whack_end_t right;
+
+ /* note: if the client is the gateway, the following must be equal */
+ sa_family_t addr_family; /* between gateways */
+ sa_family_t tunnel_addr_family; /* between clients */
+
+ char *ike; /* ike algo string (separated by commas) */
+ char *pfsgroup; /* pfsgroup will be "encapsulated" in esp string for pluto */
+ char *esp; /* esp algo string (separated by commas) */
+
+ /* for WHACK_KEY: */
+ bool whack_key;
+ bool whack_addkey;
+ char *keyid; /* string 8 */
+ enum pubkey_alg pubkey_alg;
+ chunk_t keyval; /* chunk */
+
+ /* for WHACK_MYID: */
+ bool whack_myid;
+ char *myid; /* string 7 */
+
+ /* for WHACK_ROUTE: */
+ bool whack_route;
+
+ /* for WHACK_UNROUTE: */
+ bool whack_unroute;
+
+ /* for WHACK_INITIATE: */
+ bool whack_initiate;
+
+ /* for WHACK_OPINITIATE */
+ bool whack_oppo_initiate;
+ ip_address oppo_my_client, oppo_peer_client;
+
+ /* for WHACK_TERMINATE: */
+ bool whack_terminate;
+
+ /* for WHACK_DELETE: */
+ bool whack_delete;
+
+ /* for WHACK_DELETESTATE: */
+ bool whack_deletestate;
+ so_serial_t whack_deletestateno;
+
+ /* for WHACK_LISTEN: */
+ bool whack_listen, whack_unlisten;
+
+ /* for WHACK_CRASH - note if a remote peer is known to have rebooted */
+ bool whack_crash;
+ ip_address whack_crash_peer;
+
+ /* for WHACK_LIST */
+ bool whack_utc;
+ lset_t whack_list;
+
+ /* for WHACK_PURGEOCSP */
+ bool whack_purgeocsp;
+
+ /* for WHACK_REREAD */
+ u_char whack_reread;
+
+ /* for WHACK_CA */
+ bool whack_ca;
+ bool whack_strict;
+
+ char *cacert;
+ char *ldaphost;
+ char *ldapbase;
+ char *crluri;
+ char *crluri2;
+ char *ocspuri;
+
+ /* for WHACK_SC_OP */
+ sc_op_t whack_sc_op;
+ int inbase, outbase;
+ char *sc_data;
+
+ /* space for strings (hope there is enough room):
+ * Note that pointers don't travel on wire.
+ * 1 connection name [name_len]
+ * 2 left's name [left.host.name.len]
+ * 3 left's cert
+ * 4 left's ca
+ * 5 left's groups
+ * 6 left's updown
+ * 7 right's name [left.host.name.len]
+ * 8 right's cert
+ * 9 right's ca
+ * 10 right's groups
+ * 11 right's updown
+ * 12 keyid
+ * 13 myid
+ * 14 cacert
+ * 15 ldaphost
+ * 16 ldapbase
+ * 17 crluri
+ * 18 crluri2
+ * 19 ocspuri
+ * 20 ike
+ " 21 esp
+ * 22 rsa_data
+ * plus keyval (limit: 8K bits + overhead), a chunk.
+ */
+ size_t str_size;
+ char string[2048];
+};
+
+/* Codes for status messages returned to whack.
+ * These are 3 digit decimal numerals. The structure
+ * is inspired by section 4.2 of RFC959 (FTP).
+ * Since these will end up as the exit status of whack, they
+ * must be less than 256.
+ * NOTE: ipsec_auto(8) knows about some of these numbers -- change carefully.
+ */
+enum rc_type {
+ RC_COMMENT, /* non-commital utterance (does not affect exit status) */
+ RC_WHACK_PROBLEM, /* whack-detected problem */
+ RC_LOG, /* message aimed at log (does not affect exit status) */
+ RC_LOG_SERIOUS, /* serious message aimed at log (does not affect exit status) */
+ RC_SUCCESS, /* success (exit status 0) */
+
+ /* failure, but not definitive */
+
+ RC_RETRANSMISSION = 10,
+
+ /* improper request */
+
+ RC_DUPNAME = 20, /* attempt to reuse a connection name */
+ RC_UNKNOWN_NAME, /* connection name unknown or state number */
+ RC_ORIENT, /* cannot orient connection: neither end is us */
+ RC_CLASH, /* clash between two Road Warrior connections OVERLOADED */
+ RC_DEAF, /* need --listen before --initiate */
+ RC_ROUTE, /* cannot route */
+ RC_RTBUSY, /* cannot unroute: route busy */
+ RC_BADID, /* malformed --id */
+ RC_NOKEY, /* no key found through DNS */
+ RC_NOPEERIP, /* cannot initiate when peer IP is unknown */
+ RC_INITSHUNT, /* cannot initiate a shunt-oly connection */
+ RC_WILDCARD, /* cannot initiate when ID has wildcards */
+ RC_NOVALIDPIN, /* cannot initiate without valid PIN */
+
+ /* permanent failure */
+
+ RC_BADWHACKMESSAGE = 30,
+ RC_NORETRANSMISSION,
+ RC_INTERNALERR,
+ RC_OPPOFAILURE, /* Opportunism failed */
+
+ /* entry of secrets */
+ RC_ENTERSECRET = 40,
+
+ /* progress: start of range for successful state transition.
+ * Actual value is RC_NEW_STATE plus the new state code.
+ */
+ RC_NEW_STATE = 100,
+
+ /* start of range for notification.
+ * Actual value is RC_NOTIFICATION plus code for notification
+ * that should be generated by this Pluto.
+ */
+ RC_NOTIFICATION = 200 /* as per IKE notification messages */
+};
+
+/* options of whack --list*** command */
+
+#define LIST_NONE 0x0000 /* don't list anything */
+#define LIST_ALGS 0x0001 /* list all registered IKE algorithms */
+#define LIST_PUBKEYS 0x0002 /* list all public keys */
+#define LIST_CERTS 0x0004 /* list all host/user certs */
+#define LIST_CACERTS 0x0008 /* list all ca certs */
+#define LIST_ACERTS 0x0010 /* list all attribute certs */
+#define LIST_AACERTS 0x0020 /* list all aa certs */
+#define LIST_OCSPCERTS 0x0040 /* list all ocsp certs */
+#define LIST_GROUPS 0x0080 /* list all access control groups */
+#define LIST_CAINFOS 0x0100 /* list all ca information records */
+#define LIST_CRLS 0x0200 /* list all crls */
+#define LIST_OCSP 0x0400 /* list all ocsp cache entries */
+#define LIST_CARDS 0x0800 /* list all smartcard records */
+
+#define LIST_ALL LRANGES(LIST_ALGS, LIST_CARDS) /* all list options */
+
+/* options of whack --reread*** command */
+
+#define REREAD_NONE 0x00 /* don't reread anything */
+#define REREAD_SECRETS 0x01 /* reread /etc/ipsec.secrets */
+#define REREAD_CACERTS 0x02 /* reread certs in /etc/ipsec.d/cacerts */
+#define REREAD_AACERTS 0x04 /* reread certs in /etc/ipsec.d/aacerts */
+#define REREAD_OCSPCERTS 0x08 /* reread certs in /etc/ipsec.d/ocspcerts */
+#define REREAD_ACERTS 0x10 /* reread certs in /etc/ipsec.d/acerts */
+#define REREAD_CRLS 0x20 /* reread crls in /etc/ipsec.d/crls */
+
+#define REREAD_ALL LRANGES(REREAD_SECRETS, REREAD_CRLS) /* all reread options */
+
+#endif /* _WHACK_H */