/* 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 . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "constants.h" #include "defs.h" #include "whack.h" static void help(void) { fprintf(stderr , "Usage:\n\n" "all forms:" " [--optionsfrom ]" " [--ctlbase ]" " [--label ]" "\n\n" "help: whack" " [--help]" " [--version]" "\n\n" "connection: whack" " --name " " \\\n " " [--ipv4 | --ipv6]" " [--tunnelipv4 | --tunnelipv6]" " \\\n " " (--host | --id )" " \\\n " " [--cert ]" " [--ca ]" " [--sendcert ]" " \\\n " " [--groups ]" " \\\n " " [--ikeport ]" " [--nexthop ]" " [--srcip ]" " \\\n " " [--client | --clientwithin
]" " [--clientprotoport /]" " \\\n " " [--dnskeyondemand]" " [--updown ]" " \\\n " " --to" " (--host | --id )" " \\\n " " [--cert ]" " [--ca ]" " [--sendcert ]" " \\\n " " [--ikeport ]" " [--nexthop ]" " [--srcip ]" " \\\n " " [--client | --clientwithin
]" " [--clientprotoport /]" " \\\n " " [--dnskeyondemand]" " [--updown ]" " [--psk]" " [--rsasig]" " \\\n " " [--encrypt]" " [--authenticate]" " [--compress]" " [--tunnel]" " [--pfs]" " \\\n " " [--ikelifetime ]" " [--ipseclifetime ]" " \\\n " " [--reykeymargin ]" " [--reykeyfuzz ]" " \\\n " " [--keyingtries ]" " \\\n " " [--esp ]" " \\\n " " [--dontrekey]" " [--dpdaction (none|clear|hold|restart)]" " \\\n " " [--dpddelay --dpdtimeout ]" " \\\n " " [--initiateontraffic|--pass|--drop|--reject]" " \\\n " " [--failnone|--failpass|--faildrop|--failreject]" "\n\n" "routing: whack" " (--route | --unroute)" " --name " "\n\n" "initiation:" "\n " " whack" " (--initiate | --terminate)" " --name " " [--asynchronous]" "\n\n" "opportunistic initiation: whack" " [--tunnelipv4 | --tunnelipv6]" " \\\n " " --oppohere " " --oppothere " "\n\n" "delete: whack" " --delete" " (--name | --caname )" "\n\n" "deletestate: whack" " --deletestate " " --crash " "\n\n" "pubkey: whack" " --keyid " " [--addkey]" " [--pubkeyrsa ]" "\n\n" "myid: whack" " --myid " "\n\n" "ca: whack" " --caname " " --cacert " " \\\n " " [--ldaphost ]" " [--ldapbase ]" " \\\n " " [--crluri ]" " [--crluri2 ]" " [--ocspuri ]" " [--strictcrlpolicy]" "\n\n" #ifdef DEBUG "debug: whack [--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 ] --status|--statusall" "\n\n" "scdecrypt: whack" " --scencrypt|scdecrypt " " [--inbase ]" " [--outbase ]" " [--keyid ]" "\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 */ label = optarg; /* remember for diagnostics */ continue; case '+' - OPTION_OFFSET: /* --optionsfrom */ 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 */ if (snprintf(ctl_addr.sun_path, sizeof(ctl_addr.sun_path) , "%s%s", optarg, CTL_SUFFIX) == -1) diag("" CTL_SUFFIX " must be fit in a sun_addr"); continue; case OPT_NAME: /* --name */ name = optarg; msg.name = optarg; continue; case OPT_KEYID: /* --keyid */ msg.whack_key = !msg.whack_sc_op; msg.keyid = optarg; /* decoded by Pluto */ continue; case OPT_MYID: /* --myid */ msg.whack_myid = TRUE; msg.myid = optarg; /* decoded by Pluto */ continue; case OPT_ADDKEY: /* --addkey */ msg.whack_addkey = TRUE; continue; case OPT_PUBKEYRSA: /* --pubkeyrsa */ { 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 */ msg.whack_deletestate = TRUE; msg.whack_deletestateno = opt_whole; continue; case OPT_DELETECRASH: /* --crash */ 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 */ 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 */ 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 */ 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; } }