1 /* command interface to Pluto
2 * Copyright (C) 1997 Angelos D. Keromytis.
3 * Copyright (C) 1998-2001 D. Hugh Redelmeier.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * RCSID $Id: whack.c,v 1.91 2002/03/19 07:17:01 dhr Exp $
25 #include <sys/types.h>
26 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
35 #include "constants.h"
45 " [--optionsfrom <filename>]"
54 " --name <connection_name>"
57 " [--tunnelipv4 | --tunnelipv6 ]"
59 " (--host <ip-address> | --id <identity> | --cert <path>)"
60 " [--ikeport <port-number>]"
63 " [--nexthop <ip-address>]"
64 " [--client <subnet>] | [--clientwithin <address range>])"
65 " [--clientprotoport <protocol>/<port>]"
66 " [--updown <updown>]"
68 " (--host <ip-address> | --id <identity> | --cert <path>)"
69 " [--ikeport <port-number>]"
72 " [--nexthop <ip-address>]"
73 " [--client <subnet>] | [--clientwithin <address range>])"
74 " [--clientprotoport <protocol>/<port>]"
75 " [--updown <updown>]"
86 " [--ikelifetime <seconds>]"
87 " [--ipseclifetime <seconds>]"
89 " [--reykeymargin <seconds>]"
90 " [--reykeyfuzz <percentage>]"
92 " [--keyingtries <count>]"
94 " [--esp <esp-algos>]"
96 " [--dpddelay <seconds> --dpdtimeout <seconds>]"
97 " [--cipher_p1 <Phase 1 encryption algorithm> --dhg_p1 <Phase 1 Diffie Helman group>]"
98 " [--hash_p1 <Phase 1 hash algorithm>]"
102 " (--route | --unroute)"
103 " --name <connection_name>"
106 " (--initiate | --terminate)"
107 " --name <connection_name>"
110 "opportunistic initiation:"
113 " [--ipv4 | --ipv6 ]"
114 " [--tunnelipv4 | --tunnelipv6 ]"
116 " --oppohere <ip-address>"
117 " --oppothere <ip-address>"
121 " --name <connection_name>"
124 " --deletestate <state_object_number>"
129 " [--pubkeyrsa <key>]"
132 "debug: whack [--name <connection_name>]"
140 " [--debug-emitting]"
149 " (--listen | --unlisten)"
151 "list: whack [--utc]"
172 ipsec_version_code());
175 static const char *label = NULL; /* --label operand, saved for diagnostics */
177 static const char *name = NULL; /* --name operand, saved for diagnostics */
179 /* print a string as a diagnostic, then exit whack unhappily */
181 diag(const char *mess)
185 fprintf(stderr, "whack error: ");
187 fprintf(stderr, "%s ", label);
189 fprintf(stderr, "\"%s\" ", name);
190 fprintf(stderr, "%s\n", mess);
193 exit(RC_WHACK_PROBLEM);
196 /* conditially calls diag; prints second arg, if non-NULL, as quoted string */
198 diagq(err_t ugh, const char *this)
208 char buf[120]; /* arbitrary limit */
210 snprintf(buf, sizeof(buf), "%s \"%s\"", ugh, this);
216 /* complex combined operands return one of these enumerated values
217 * Note: these become flags in an lset_t. Since there are more than
218 * 32, we partition them into:
219 * - OPT_* options (most random options)
220 * - DBGOPT_* option (DEBUG options)
221 * - CD_* options (Connection Description options)
236 /* put all options that don't require opts_seen service at end */
265 # define OPT_LAST OPT_ASYNC /* last "normal" option */
267 /* Connection Description options -- segregated */
269 # define CD_FIRST CD_TO /* first connection description */
271 CD_HOST, /* first per-end */
279 CD_UPDOWN, /* last per-end */
281 # define CD_POLICY_FIRST CD_AGGRESSIVE
282 CD_AGGRESSIVE, /* same order as POLICY_* */
283 CD_PSK, /* same order as POLICY_* */
284 CD_RSASIG, /* same order as POLICY_* */
285 CD_ENCRYPT, /* same order as POLICY_* */
286 CD_AUTHENTICATE, /* same order as POLICY_* */
287 CD_COMPRESS, /* same order as POLICY_* */
288 CD_TUNNEL, /* same order as POLICY_* */
289 CD_PFS, /* same order as POLICY_* */
290 CD_DISABLEARRIVALCHECK, /* same order as POLICY_* */
291 CD_FAIL_PASS, /* same order as POLICY_* */
292 CD_FAIL_DROP, /* same order as POLICY_* */
293 CD_DONT_REKEY, /* same order as POLICY_* */
313 # define CD_LAST CD_RETRANSMIT /* last connection description */
315 #ifdef DEBUG /* must be last so others are less than 32 to fit in lset_t */
316 # define DBGOPT_FIRST DBGOPT_NONE
321 DBGOPT_RAW, /* same order as DBG_* */
322 DBGOPT_CRYPT, /* same order as DBG_* */
323 DBGOPT_PARSING, /* same order as DBG_* */
324 DBGOPT_EMITTING, /* same order as DBG_* */
325 DBGOPT_CONTROL, /* same order as DBG_* */
326 DBGOPT_LIFECYCLE, /* same order as DBG_* */
327 DBGOPT_KLIPS, /* same order as DBG_* */
328 DBGOPT_DNS, /* same order as DBG_* */
329 DBGOPT_PRIVATE /* same order as DBG_* */
330 # define DBGOPT_LAST DBGOPT_PRIVATE
335 #define OPTION_OFFSET 256 /* to get out of the way of letter options */
336 #define NUMERIC_ARG (1 << 7) /* expect a numeric argument */
338 static const struct option long_opts[] = {
339 # define OO OPTION_OFFSET
340 /* name, has_arg, flag, val */
342 { "help", no_argument, NULL, 'h' },
343 { "version", no_argument, NULL, 'v' },
344 { "optionsfrom", required_argument, NULL, '+' },
345 { "label", required_argument, NULL, 'l' },
347 { "ctlbase", required_argument, NULL, OPT_CTLBASE + OO },
348 { "name", required_argument, NULL, OPT_NAME + OO },
350 { "keyid", required_argument, NULL, OPT_KEYID + OO },
351 { "addkey", no_argument, NULL, OPT_ADDKEY + OO },
352 { "pubkeyrsa", required_argument, NULL, OPT_PUBKEYRSA + OO },
354 { "route", no_argument, NULL, OPT_ROUTE + OO },
355 { "unroute", no_argument, NULL, OPT_UNROUTE + OO },
357 { "initiate", no_argument, NULL, OPT_INITIATE + OO },
358 { "terminate", no_argument, NULL, OPT_TERMINATE + OO },
359 { "delete", no_argument, NULL, OPT_DELETE + OO },
360 { "deletestate", required_argument, NULL, OPT_DELETESTATE + OO + NUMERIC_ARG },
361 { "listen", no_argument, NULL, OPT_LISTEN + OO },
362 { "unlisten", no_argument, NULL, OPT_UNLISTEN + OO },
363 { "utc", no_argument, NULL, OPT_UTC + OO },
364 { "listpubkeys", no_argument, NULL, OPT_LISTPUBKEYS + OO },
365 { "listcerts", no_argument, NULL, OPT_LISTCERTS + OO },
366 { "listcacerts", no_argument, NULL, OPT_LISTCACERTS + OO },
367 { "listcrls", no_argument, NULL, OPT_LISTCRLS + OO },
368 { "listall", no_argument, NULL, OPT_LISTALL + OO },
369 { "rereadsecrets", no_argument, NULL, OPT_REREADSECRETS + OO },
370 { "rereadmycert", no_argument, NULL, OPT_REREADMYCERT + OO },
371 { "rereadcacerts", no_argument, NULL, OPT_REREADCACERTS + OO },
372 { "rereadcrls", no_argument, NULL, OPT_REREADCRLS + OO },
373 { "rereadall", no_argument, NULL, OPT_REREADALL + OO },
374 { "status", no_argument, NULL, OPT_STATUS + OO },
375 { "shutdown", no_argument, NULL, OPT_SHUTDOWN + OO },
377 { "oppohere", required_argument, NULL, OPT_OPPO_HERE + OO },
378 { "oppothere", required_argument, NULL, OPT_OPPO_THERE + OO },
380 { "asynchronous", no_argument, NULL, OPT_ASYNC + OO },
383 /* options for a connection description */
385 { "to", no_argument, NULL, CD_TO + OO },
387 { "host", required_argument, NULL, CD_HOST + OO },
388 { "id", required_argument, NULL, CD_ID + OO },
389 { "cert", required_argument, NULL, CD_CERT + OO },
390 { "ikeport", required_argument, NULL, CD_IKEPORT + OO + NUMERIC_ARG },
391 { "nexthop", required_argument, NULL, CD_NEXTHOP + OO },
392 { "client", required_argument, NULL, CD_CLIENT + OO },
393 { "clientwithin", required_argument, NULL, CD_CLIENTWITHIN + OO },
394 { "clientprotoport", required_argument, NULL, CD_CLIENTPROTOPORT + OO },
395 { "updown", required_argument, NULL, CD_UPDOWN + OO },
397 { "aggrmode", no_argument, NULL, CD_AGGRESSIVE + OO },
398 { "psk", no_argument, NULL, CD_PSK + OO },
399 { "rsasig", no_argument, NULL, CD_RSASIG + OO },
401 { "encrypt", no_argument, NULL, CD_ENCRYPT + OO },
402 { "authenticate", no_argument, NULL, CD_AUTHENTICATE + OO },
403 { "compress", no_argument, NULL, CD_COMPRESS + OO },
404 { "tunnel", no_argument, NULL, CD_TUNNEL + OO },
405 { "tunnelipv4", no_argument, NULL, CD_TUNNELIPV4 + OO },
406 { "tunnelipv6", no_argument, NULL, CD_TUNNELIPV6 + OO },
407 { "pfs", no_argument, NULL, CD_PFS + OO },
408 { "disablearrivalcheck", no_argument, NULL, CD_DISABLEARRIVALCHECK + OO },
409 { "pass", no_argument, NULL, CD_FAIL_PASS + OO },
410 { "drop", no_argument, NULL, CD_FAIL_DROP + OO },
411 { "dontrekey", no_argument, NULL, CD_DONT_REKEY + OO },
412 { "ipv4", no_argument, NULL, CD_CONNIPV4 + OO },
413 { "ipv6", no_argument, NULL, CD_CONNIPV6 + OO },
415 { "ikelifetime", required_argument, NULL, CD_IKELIFETIME + OO + NUMERIC_ARG },
416 { "ipseclifetime", required_argument, NULL, CD_IPSECLIFETIME + OO + NUMERIC_ARG },
417 { "rekeymargin", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG },
418 { "rekeywindow", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG }, /* OBSOLETE */
419 { "rekeyfuzz", required_argument, NULL, CD_RKFUZZ + OO + NUMERIC_ARG },
420 { "keyingtries", required_argument, NULL, CD_KTRIES + OO + NUMERIC_ARG },
421 { "dpddelay", required_argument, NULL, CD_DPDDELAY + OO + NUMERIC_ARG },
422 { "dpdtimeout", required_argument, NULL, CD_DPDTIMEOUT + OO + NUMERIC_ARG },
423 { "cipher_p1", required_argument, NULL, CD_CIPHER_P1 + OO },
424 { "dhg_p1", required_argument, NULL, CD_DHG_P1 + OO + NUMERIC_ARG },
425 { "hash_p1", required_argument, NULL, CD_HASH_P1 + OO },
426 { "ike", required_argument, NULL, CD_IKE + OO },
427 { "pfsgroup", required_argument, NULL, CD_PFSGROUP + OO },
428 { "esp", required_argument, NULL, CD_ESP + OO },
429 { "retransmit_trigger", required_argument, NULL, CD_RETRANSMIT + OO + NUMERIC_ARG},
431 { "debug-none", no_argument, NULL, DBGOPT_NONE + OO },
432 { "debug-all]", no_argument, NULL, DBGOPT_ALL + OO },
433 { "debug-raw", no_argument, NULL, DBGOPT_RAW + OO },
434 { "debug-crypt", no_argument, NULL, DBGOPT_CRYPT + OO },
435 { "debug-parsing", no_argument, NULL, DBGOPT_PARSING + OO },
436 { "debug-emitting", no_argument, NULL, DBGOPT_EMITTING + OO },
437 { "debug-control", no_argument, NULL, DBGOPT_CONTROL + OO },
438 { "debug-lifecycle", no_argument, NULL, DBGOPT_LIFECYCLE + OO },
439 { "debug-klips", no_argument, NULL, DBGOPT_KLIPS + OO },
440 { "debug-dns", no_argument, NULL, DBGOPT_DNS + OO },
441 { "debug-private", no_argument, NULL, DBGOPT_PRIVATE + OO },
447 struct sockaddr_un ctl_addr = { AF_UNIX, DEFAULT_CTLBASE CTL_SUFFIX };
449 /* helper variables and function to encode strings from whack message */
458 const char *s = *p == NULL? "" : *p; /* note: NULL becomes ""! */
459 size_t len = strlen(s) + 1;
461 if (str_roof - next_str < (ptrdiff_t)len)
463 return FALSE; /* fishy: no end found */
469 *p = NULL; /* don't send pointers on the wire! */
475 check_life_time(time_t life, time_t limit, const char *which
476 , const struct whack_message *msg)
478 time_t mint = msg->sa_rekey_margin * (100 + msg->sa_rekey_fuzz) / 100;
482 char buf[200]; /* arbitrary limit */
484 snprintf(buf, sizeof(buf)
485 , "%s [%lu seconds] must be less than %lu seconds"
486 , which, (unsigned long)life, (unsigned long)limit);
489 if ((msg->policy & POLICY_DONT_REKEY) == LEMPTY && life <= mint)
491 char buf[200]; /* arbitrary limit */
493 snprintf(buf, sizeof(buf)
494 , "%s [%lu] must be greater than"
495 " rekeymargin*(100+rekeyfuzz)/100 [%lu*(100+%lu)/100 = %lu]"
497 , (unsigned long)life
498 , (unsigned long)msg->sa_rekey_margin
499 , (unsigned long)msg->sa_rekey_fuzz
500 , (unsigned long)mint);
506 check_end(struct whack_end *this, struct whack_end *that
507 , lset_t policy, bool default_nexthop, sa_family_t caf, sa_family_t taf)
509 if (caf != addrtypeof(&this->host_addr))
510 diag("address family of host inconsistent");
514 if (isanyaddr(&that->host_addr))
515 diag("our nexthop must be specified when other host is a %any or %opportunistic");
516 this->host_nexthop = that->host_addr;
519 if (caf != addrtypeof(&this->host_nexthop))
520 diag("address family of nexthop inconsistent");
522 if (this->has_client)
526 networkof(&this->client, &cn);
528 if (taf != addrtypeof(&cn))
529 diag("address family of client subnet inconsistent");
531 if (isanyaddr(&cn) && subnetishost(&this->client))
533 /* client is 0.0.0.0/32: Opportunism connection */
534 if (!isanyaddr(&this->host_addr))
535 diag("normal client network must not be 0.0.0.0/32 or 0::0/128");
536 if ((policy & (POLICY_PSK | POLICY_RSASIG)) != POLICY_RSASIG)
537 diag("only RSASIG is supported for opportunism");
538 if ((policy & POLICY_PFS) == 0)
539 diag("PFS required for opportunism");
540 if ((policy & POLICY_ENCRYPT) == 0)
541 diag("encryption required for opportunism");
546 /* fill in anyaddr-anyaddr as (missing) client subnet */
549 diagq(anyaddr(caf, &cn), NULL);
550 diagq(rangetosubnet(&cn, &cn, &this->client), NULL);
554 if (this->protocol != that->protocol)
555 diagq("the protocol for leftprotoport and rightprotoport must be the same", NULL);
558 /* This is a hack for initiating ISAKMP exchanges. */
561 main(int argc, char **argv)
563 struct whack_message msg;
564 char esp_buf[256]; /* uses snprintf */
571 *tunnel_af_used_by = NULL;
574 msg.magic = WHACK_MAGIC;
575 msg.right.host_port = IKE_UDP_PORT;
578 msg.dnshostname = NULL;
580 msg.left.cert = NULL;
581 msg.left.updown = NULL;
583 msg.right.cert = NULL;
584 msg.right.updown = NULL;
586 msg.keyval.ptr = NULL;
587 msg.cipher_p1 = NULL;
593 msg.retransmit_trigger = 2;
595 msg.sa_ike_life_seconds = OAKLEY_ISAKMP_SA_LIFETIME_DEFAULT;
596 msg.sa_ipsec_life_seconds = PLUTO_SA_LIFE_DURATION_DEFAULT;
597 msg.sa_rekey_margin = SA_REPLACEMENT_MARGIN_DEFAULT;
598 msg.sa_rekey_fuzz = SA_REPLACEMENT_FUZZ_DEFAULT;
599 msg.sa_keying_tries = SA_REPLACEMENT_RETRIES_DEFAULT;
601 msg.addr_family = AF_INET;
602 msg.tunnel_addr_family = AF_INET;
607 unsigned long opt_whole; /* numeric argument for some flags */
609 /* Note: we don't like the way short options get parsed
610 * by getopt_long, so we simply pass an empty string as
611 * the list. It could be "hp:d:c:o:eatfs" "NARXPECK".
613 int c = getopt_long(argc, argv, "", long_opts, &long_index) - OPTION_OFFSET;
615 /* decode a numeric argument, if expected */
616 if (0 <= c && (c & NUMERIC_ARG))
621 opt_whole = strtoul(optarg, &endptr, 0);
623 if (*endptr != '\0' || endptr == optarg)
624 diagq("badly formed numeric argument", optarg);
627 /* per-class option processing */
628 if (0 <= c && c < OPT_LAST)
630 /* OPT_* options get added opts_seen.
631 * Reject repeated options (unless later code intervenes).
636 diagq("duplicated flag", long_opts[long_index].name);
640 else if (DBGOPT_FIRST <= c && c <= DBGOPT_LAST)
642 /* DBGOPT_* options are treated separately to reduce
643 * potential members of opts_seen.
645 msg.whack_options = TRUE;
648 else if (CD_FIRST <= c && c <= CD_LAST)
650 /* CD_* options are added to cd_seen.
651 * Reject repeated options (unless later code intervenes).
653 lset_t f = LELEM(c - CD_FIRST);
655 if ((CD_FIRST+32) <= c)
658 diagq("duplicated flag", long_opts[long_index].name);
660 opts_seen |= LELEM(OPT_CD);
663 /* Note: "break"ing from switch terminates loop.
664 * most cases should end with "continue".
668 case EOF - OPTION_OFFSET: /* end of flags */
671 case 0 - OPTION_OFFSET: /* long option already handled */
674 case ':' - OPTION_OFFSET: /* diagnostic already printed by getopt_long */
675 case '?' - OPTION_OFFSET: /* diagnostic already printed by getopt_long */
676 diag(NULL); /* print no additional diagnostic, but exit sadly */
677 break; /* not actually reached */
679 case 'h' - OPTION_OFFSET: /* --help */
681 return 0; /* GNU coding standards say to stop here */
683 case 'v' - OPTION_OFFSET: /* --version */
685 const char **sp = ipsec_copyright_notice();
687 printf("%s\n", ipsec_version_string());
688 for (; *sp != NULL; sp++)
691 return 0; /* GNU coding standards say to stop here */
693 case 'l' - OPTION_OFFSET: /* --label <string> */
694 label = optarg; /* remember for diagnostics */
697 case '+' - OPTION_OFFSET: /* --optionsfrom <filename> */
698 optionsfrom(optarg, &argc, &argv, optind, stderr);
699 fprintf(stderr, "DAN %s", optarg);
700 /* does not return on error */
703 /* the rest of the options combine in complex ways */
705 case OPT_CTLBASE: /* --port <ctlbase> */
706 if (snprintf(ctl_addr.sun_path, sizeof(ctl_addr.sun_path)
707 , "%s%s", optarg, CTL_SUFFIX) == -1)
708 diag("<ctlbase>" CTL_SUFFIX " must be fit in a sun_addr");
711 case OPT_NAME: /* --name <connection-name> */
716 case OPT_KEYID: /* --keyid <identity> */
717 msg.whack_key = TRUE;
718 msg.keyid = optarg; /* decoded by Pluto */
721 case OPT_ADDKEY: /* --addkey */
722 msg.whack_addkey = TRUE;
725 case OPT_PUBKEYRSA: /* --pubkeyrsa <key> */
727 static char keyspace[1024 + 4]; /* room for 8K bit key */
728 char diag_space[TTODATAV_BUF];
729 const char *ugh = ttodatav(optarg, 0, 0
730 , keyspace, sizeof(keyspace)
731 , &msg.keyval.len, diag_space, sizeof(diag_space));
735 char ugh_space[80]; /* perhaps enough space */
737 snprintf(ugh_space, sizeof(ugh_space)
738 , "RSA public-key data malformed (%s)", ugh);
739 diagq(ugh_space, optarg);
741 msg.pubkey_alg = PUBKEY_ALG_RSA;
742 msg.keyval.ptr = keyspace;
746 case OPT_ROUTE: /* --route */
747 msg.whack_route = TRUE;
750 case OPT_UNROUTE: /* --unroute */
751 msg.whack_unroute = TRUE;
754 case OPT_INITIATE: /* --initiate */
755 msg.whack_initiate = TRUE;
758 case OPT_TERMINATE: /* --terminate */
759 msg.whack_terminate = TRUE;
762 case OPT_DELETE: /* --delete */
763 msg.whack_delete = TRUE;
766 case OPT_DELETESTATE: /* --deletestate <state_object_number> */
767 msg.whack_deletestate = TRUE;
768 msg.whack_deletestateno = opt_whole;
771 case OPT_LISTEN: /* --listen */
772 msg.whack_listen = TRUE;
775 case OPT_UNLISTEN: /* --unlisten */
776 msg.whack_unlisten = TRUE;
779 case OPT_UTC: /* --utc */
780 msg.whack_utc = TRUE;
783 case OPT_LISTPUBKEYS: /* --listpubkeys */
784 case OPT_LISTCERTS: /* --listcerts */
785 case OPT_LISTCACERTS: /* --listcacerts */
786 case OPT_LISTCRLS: /* --listcrls */
787 msg.whack_list |= LELEM(c-OPT_LISTPUBKEYS);
790 case OPT_LISTALL: /* --listall */
791 msg.whack_list = LIST_ALL;
794 case OPT_REREADSECRETS: /* --rereadsecrets */
795 case OPT_REREADMYCERT: /* --rereadmycert */
796 case OPT_REREADCACERTS: /* --rereadcacerts */
797 case OPT_REREADCRLS: /* --rereadcrls */
798 msg.whack_reread |= LELEM(c-OPT_REREADSECRETS);
801 case OPT_REREADALL: /* --rereadall */
802 msg.whack_reread = REREAD_ALL;
805 case OPT_STATUS: /* --status */
806 msg.whack_status = TRUE;
809 case OPT_SHUTDOWN: /* --shutdown */
810 msg.whack_shutdown = TRUE;
813 case OPT_OPPO_HERE: /* --oppohere <ip-address> */
814 af_used_by = long_opts[long_index].name;
815 diagq(ttoaddr(optarg, 0, msg.addr_family, &msg.oppo_my_client), optarg);
816 if (isanyaddr(&msg.oppo_my_client))
817 diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
820 case OPT_OPPO_THERE: /* --oppohere <ip-address> */
821 af_used_by = long_opts[long_index].name;
822 diagq(ttoaddr(optarg, 0, msg.addr_family, &msg.oppo_peer_client), optarg);
823 if (isanyaddr(&msg.oppo_peer_client))
824 diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
828 msg.whack_async = TRUE;
832 /* Connection Description options */
834 case CD_HOST: /* --host <ip-address> */
835 af_used_by = long_opts[long_index].name;
836 if (streq(optarg, "%any") || streq(optarg, "0.0.0.0"))
838 diagq(anyaddr(msg.addr_family, &msg.right.host_addr), optarg);
840 else if (streq(optarg, "%opportunistic"))
842 /* we also fill in --client to 0.0.0.0/32 or IPV6 equivalent */
845 if (cd_seen & LELEM(CD_CLIENT - CD_FIRST))
846 diag("%opportunistic clashes with --client");
848 cd_seen |= LELEM(CD_CLIENT - CD_FIRST);
849 tunnel_af_used_by = optarg;
850 diagq(anyaddr(msg.addr_family, &msg.right.host_addr), optarg);
851 diagq(anyaddr(msg.tunnel_addr_family, &any), optarg);
852 diagq(rangetosubnet(&any, &any, &msg.right.client), optarg);
853 msg.right.has_client = TRUE;
854 /* always use tunnel mode; mark as opportunistic */
855 msg.policy |= POLICY_TUNNEL | POLICY_OPPO;
859 if (msg.left.id != NULL) {
860 msg.dnshostname = optarg;
862 diagq(ttoaddr(optarg, 0, msg.addr_family
863 , &msg.right.host_addr), optarg);
867 case CD_ID: /* --id <identity> */
868 msg.right.id = optarg; /* decoded by Pluto */
871 case CD_CERT: /* --cert <path> */
872 msg.right.cert = optarg; /* decoded by Pluto */
875 case CD_IKEPORT: /* --ikeport <port-number> */
876 if (opt_whole<=0 || opt_whole >= 0x10000)
877 diagq("<port-number> must be a number between 1 and 65535", optarg);
878 msg.right.host_port = opt_whole;
881 case CD_NEXTHOP: /* --nexthop <ip-address> */
882 af_used_by = long_opts[long_index].name;
883 if (streq(optarg, "%direct"))
884 diagq(anyaddr(msg.addr_family
885 , &msg.right.host_nexthop), optarg);
887 diagq(ttoaddr(optarg, 0, msg.addr_family
888 , &msg.right.host_nexthop), optarg);
891 case CD_CLIENT: /* --client <subnet> */
892 if (cd_seen & LELEM(CD_CLIENTWITHIN - CD_FIRST))
893 diag("--client conflicts with --clientwithin");
894 tunnel_af_used_by = long_opts[long_index].name;
896 if ( ((strlen(optarg)>=6) && (strncmp(optarg,"vhost:",6)==0)) ||
897 ((strlen(optarg)>=5) && (strncmp(optarg,"vnet:",5)==0)) ) {
898 msg.right.virt = optarg;
901 diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
902 msg.right.has_client = TRUE;
905 diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
906 msg.right.has_client = TRUE;
908 msg.policy |= POLICY_TUNNEL; /* client => tunnel */
911 case CD_CLIENTWITHIN: /* --clienwithin <address range> */
912 if (cd_seen & LELEM(CD_CLIENT - CD_FIRST))
913 diag("--clientwithin conflicts with --client");
914 tunnel_af_used_by = long_opts[long_index].name;
915 diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
916 msg.right.has_client = TRUE;
917 msg.policy |= POLICY_TUNNEL; /* client => tunnel */
918 msg.right.has_client_wildcard = TRUE;
921 case CD_CLIENTPROTOPORT: /* --clientprotoport <protocol>/<port> */
922 diagq(ttoprotoport(optarg, 0, &msg.right.protocol,
923 &msg.right.port), optarg);
926 case CD_UPDOWN: /* --updown <updown> */
927 msg.right.updown = optarg;
930 case CD_TO: /* --to */
931 /* process right end, move it to left, reset it */
932 if ((cd_seen & LELEM(CD_HOST-CD_FIRST)) == 0)
933 diag("connection missing --host before --to");
934 msg.left = msg.right;
937 msg.right.cert = NULL;
938 msg.right.updown = NULL;
939 msg.right.host_port = IKE_UDP_PORT;
940 cd_seen_before_to = cd_seen;
941 cd_seen &= ~LRANGE(CD_HOST-CD_FIRST, CD_UPDOWN-CD_FIRST);
944 case CD_AGGRESSIVE: /* --aggrmode */
945 case CD_PSK: /* --psk */
946 case CD_RSASIG: /* --rsasig */
947 case CD_ENCRYPT: /* --encrypt */
948 case CD_AUTHENTICATE: /* --authenticate */
949 case CD_COMPRESS: /* --compress */
950 case CD_TUNNEL: /* --tunnel */
951 case CD_PFS: /* --pfs */
952 case CD_DISABLEARRIVALCHECK: /* --disablearrivalcheck */
953 case CD_FAIL_PASS: /* --pass */
954 case CD_FAIL_DROP: /* --drop */
955 case CD_DONT_REKEY: /* --donotrekey */
956 msg.policy |= LELEM(c - CD_POLICY_FIRST);
959 case CD_IKELIFETIME: /* --ikelifetime <seconds> */
960 msg.sa_ike_life_seconds = opt_whole;
963 case CD_IPSECLIFETIME: /* --ipseclifetime <seconds> */
964 msg.sa_ipsec_life_seconds = opt_whole;
967 case CD_RKMARGIN: /* --rekeymargin <seconds> */
968 msg.sa_rekey_margin = opt_whole;
971 case CD_RKFUZZ: /* --rekeyfuzz <percentage> */
972 msg.sa_rekey_fuzz = opt_whole;
975 case CD_KTRIES: /* --keyingtries <count> */
976 msg.sa_keying_tries = opt_whole;
979 case CD_DPDDELAY: /* --dpddelay */
980 msg.dpd_delay = opt_whole;
983 case CD_DPDTIMEOUT: /* --dpdtimeout */
984 msg.dpd_timeout = opt_whole;
987 case CD_CIPHER_P1: /* --cipher */
988 if (streq(optarg, "des"))
989 msg.cipher_p1 = OAKLEY_DES_CBC;
990 else if (streq(optarg, "3des"))
991 msg.cipher_p1 = OAKLEY_3DES_CBC;
994 case CD_DHG_P1: /* --dhg */
995 msg.dhg_p1 = opt_whole;
998 case CD_HASH_P1: /* --hash */
999 if (streq(optarg, "md5"))
1000 msg.hash_p1 = OAKLEY_MD5;
1001 else if (streq(optarg, "sha"))
1002 msg.hash_p1 = OAKLEY_SHA;
1005 case CD_IKE: /* --ike <ike_alg1,ike_alg2,...> */
1009 case CD_PFSGROUP: /* --pfsgroup modpXXXX */
1010 msg.pfsgroup = optarg;
1013 case CD_ESP: /* --esp <esp_alg1,esp_alg2,...> */
1018 msg.retransmit_trigger = opt_whole;
1022 if (cd_seen & LELEM(CD_CONNIPV6 - CD_FIRST))
1023 diag("--ipv4 conflicts with --ipv6");
1025 /* Since this is the default, the flag is redundant.
1026 * So we don't need to set msg.addr_family
1027 * and we don't need to check af_used_by
1028 * and we don't have to consider defaulting tunnel_addr_family.
1033 if (cd_seen & LELEM(CD_CONNIPV4 - CD_FIRST))
1034 diag("--ipv6 conflicts with --ipv4");
1036 if (af_used_by != NULL)
1037 diagq("--ipv6 must precede", af_used_by);
1039 af_used_by = long_opts[long_index].name;
1040 msg.addr_family = AF_INET6;
1042 /* Consider defaulting tunnel_addr_family to AF_INET6.
1043 * Do so only if it hasn't yet been specified or used.
1045 if ((cd_seen & (LELEM(CD_TUNNELIPV4 - CD_FIRST) | LELEM(CD_TUNNELIPV6 - CD_FIRST))) == 0
1046 && tunnel_af_used_by == NULL)
1047 msg.tunnel_addr_family = AF_INET6;
1051 if (cd_seen & LELEM(CD_TUNNELIPV6 - CD_FIRST))
1052 diag("--tunnelipv4 conflicts with --tunnelipv6");
1054 if (tunnel_af_used_by != NULL)
1055 diagq("--tunnelipv4 must precede", af_used_by);
1057 msg.tunnel_addr_family = AF_INET;
1061 if (cd_seen & LELEM(CD_TUNNELIPV4 - CD_FIRST))
1062 diag("--tunnelipv6 conflicts with --tunnelipv4");
1064 if (tunnel_af_used_by != NULL)
1065 diagq("--tunnelipv6 must precede", af_used_by);
1067 msg.tunnel_addr_family = AF_INET6;
1071 case DBGOPT_NONE: /* --debug-none */
1072 msg.debugging = DBG_NONE;
1075 case DBGOPT_ALL: /* --debug-all */
1076 msg.debugging |= DBG_ALL; /* note: does not include PRIVATE */
1079 case DBGOPT_RAW: /* --debug-raw */
1080 case DBGOPT_CRYPT: /* --debug-crypt */
1081 case DBGOPT_PARSING: /* --debug-parsing */
1082 case DBGOPT_EMITTING: /* --debug-emitting */
1083 case DBGOPT_CONTROL: /* --debug-control */
1084 case DBGOPT_LIFECYCLE: /* --debug-lifecycle */
1085 case DBGOPT_KLIPS: /* --debug-klips */
1086 case DBGOPT_DNS: /* --debug-dns */
1087 case DBGOPT_PRIVATE: /* --debug-private */
1088 msg.debugging |= LELEM(c-DBGOPT_RAW);
1092 assert(FALSE); /* unknown return value */
1097 if (optind != argc) {
1098 fprintf(stderr, "optind %d ", optind);
1099 fprintf(stderr, "argc %d ", argc);
1100 fprintf(stderr, "optarg %s ", optarg);
1101 diagq("unexpected argument", argv[optind]);
1103 /* For each possible form of the command, figure out if an argument
1104 * suggests whether that form was intended, and if so, whether all
1105 * required information was supplied.
1108 /* check opportunistic initiation simulation request */
1109 switch (opts_seen & (LELEM(OPT_OPPO_HERE) | LELEM(OPT_OPPO_THERE)))
1111 case LELEM(OPT_OPPO_HERE):
1112 case LELEM(OPT_OPPO_THERE):
1113 diag("--oppohere and --oppothere must be used together");
1115 case LELEM(OPT_OPPO_HERE) | LELEM(OPT_OPPO_THERE):
1116 msg.whack_oppo_initiate = TRUE;
1120 /* check connection description */
1121 if (opts_seen & LELEM(OPT_CD))
1123 if (!LALLIN(cd_seen, LELEM(CD_TO-CD_FIRST)))
1124 diag("connection description option, but no --to");
1126 if ((cd_seen & LELEM(CD_HOST-CD_FIRST)) == 0)
1127 diag("connection missing --host after --to");
1129 if ((cd_seen & (CD_PSK | CD_RSASIG)) == 0)
1130 diag("connection must have --psk or --rsasig or both");
1132 if (isanyaddr(&msg.left.host_addr)
1133 && isanyaddr(&msg.right.host_addr))
1134 diag("hosts cannot both be 0.0.0.0 or 0::0");
1136 check_end(&msg.left, &msg.right, msg.policy, (cd_seen_before_to & LELEM(CD_NEXTHOP-CD_FIRST)) == 0
1137 , msg.addr_family, msg.tunnel_addr_family);
1139 check_end(&msg.right, &msg.left, msg.policy, (cd_seen & LELEM(CD_NEXTHOP-CD_FIRST)) == 0
1140 , msg.addr_family, msg.tunnel_addr_family);
1142 if (subnettypeof(&msg.left.client) != subnettypeof(&msg.right.client))
1143 diag("endpoints clash: one is IPv4 and the other is IPv6");
1145 if ((msg.policy & POLICY_ID_AUTH_MASK) == LEMPTY)
1146 diag("must specify --rsasig or --psk for a connection");
1148 if (!HAS_IPSEC_POLICY(msg.policy)
1149 && (msg.left.has_client || msg.right.has_client))
1150 diag("must not specify clients for ISAKMP-only connection");
1152 msg.whack_connection = TRUE;
1155 /* decide whether --name is mandatory or forbidden */
1156 if (opts_seen & (LELEM(OPT_ROUTE) | LELEM(OPT_UNROUTE)
1157 | LELEM(OPT_INITIATE) | LELEM(OPT_TERMINATE)
1158 | LELEM(OPT_DELETE) | LELEM(OPT_CD)))
1160 if ((opts_seen & LELEM(OPT_NAME)) == 0)
1161 diag("missing --name <connection_name>");
1163 else if (!msg.whack_options)
1165 if ((opts_seen & LELEM(OPT_NAME)) != 0)
1166 diag("no reason for --name");
1169 if (opts_seen & (LELEM(OPT_PUBKEYRSA) | LELEM(OPT_ADDKEY)))
1171 if ((opts_seen & LELEM(OPT_KEYID)) == 0)
1172 diag("--addkey and --pubkeyrsa require --keyid");
1175 if (!(msg.whack_connection || msg.whack_key
1176 || msg.whack_delete || msg.whack_deletestate
1177 || msg.whack_initiate || msg.whack_oppo_initiate || msg.whack_terminate
1178 || msg.whack_route || msg.whack_unroute || msg.whack_listen
1179 || msg.whack_unlisten || msg.whack_list || msg.whack_reread
1180 || msg.whack_status || msg.whack_options || msg.whack_shutdown))
1182 diag("no action specified; try --help for hints");
1185 /* tricky quick and dirty check for wild values */
1186 if (msg.sa_rekey_margin != 0
1187 && msg.sa_rekey_fuzz * msg.sa_rekey_margin * 4 / msg.sa_rekey_margin / 4
1188 != msg.sa_rekey_fuzz)
1189 diag("rekeymargin or rekeyfuzz values are so large that they cause oveflow");
1191 check_life_time (msg.sa_ike_life_seconds, OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM
1192 , "ikelifetime", &msg);
1194 check_life_time(msg.sa_ipsec_life_seconds, SA_LIFE_DURATION_MAXIMUM
1195 , "ipseclifetime", &msg);
1197 if (msg.dpd_delay && !msg.dpd_timeout)
1198 diag("dpddelay specified, but dpdtimeout is zero");
1200 /* build esp message as esp="<esp>;<pfsgroup>" */
1202 snprintf(esp_buf, sizeof (esp_buf), "%s;%s",
1203 msg.esp ? msg.esp : "",
1204 msg.pfsgroup ? msg.pfsgroup : "");
1207 /* pack strings for inclusion in message */
1208 next_str = msg.string;
1209 str_roof = &msg.string[sizeof(msg.string)];
1211 if (!pack_str(&msg.name) /* string 1 */
1212 || !pack_str(&msg.left.id) /* string 2 */
1213 || !pack_str(&msg.left.cert) /* string 3 */
1214 || !pack_str(&msg.left.updown) /* string 4 */
1216 || !pack_str(&msg.left.virt)
1218 || !pack_str(&msg.right.id) /* string 5 */
1219 || !pack_str(&msg.right.cert) /* string 6 */
1220 || !pack_str(&msg.right.updown) /* string 7 */
1222 || !pack_str(&msg.right.virt)
1224 || !pack_str(&msg.keyid) /* string 8 */
1225 || !pack_str(&msg.ike) /* string 9 */
1226 || !pack_str(&msg.esp) /* string 10 */
1227 || !pack_str(&msg.dnshostname) /* string 11 */
1228 || str_roof - next_str < (ptrdiff_t)msg.keyval.len) /* chunk (sort of string 5) */
1229 diag("too many bytes of strings to fit in message to pluto");
1231 memcpy(next_str, msg.keyval.ptr, msg.keyval.len);
1232 msg.keyval.ptr = NULL;
1233 next_str += msg.keyval.len;
1235 /* send message to Pluto */
1236 if (access(ctl_addr.sun_path, R_OK | W_OK) < 0)
1243 fprintf(stderr, "whack: no right to communicate with pluto (access(\"%s\"))\n"
1244 , ctl_addr.sun_path);
1247 fprintf(stderr, "whack: Pluto is not running (no \"%s\")\n"
1248 , ctl_addr.sun_path);
1251 fprintf(stderr, "whack: access(\"%s\") failed with %d %s\n"
1252 , ctl_addr.sun_path, errno, strerror(e));
1255 exit(RC_WHACK_PROBLEM);
1259 int sock = socket(AF_UNIX, SOCK_STREAM, 0);
1260 int exit_status = 0;
1261 ssize_t len = next_str - (char *)&msg;
1267 fprintf(stderr, "whack: socket() failed (%d %s)\n", e, strerror(e));
1268 exit(RC_WHACK_PROBLEM);
1271 if (connect(sock, (struct sockaddr *)&ctl_addr
1272 , offsetof(struct sockaddr_un, sun_path) + strlen(ctl_addr.sun_path)) < 0)
1276 fprintf(stderr, "whack:%s connect() for \"%s\" failed (%d %s)\n"
1277 , e == ECONNREFUSED? " is Pluto running? " : ""
1278 , ctl_addr.sun_path, e, strerror(e));
1279 exit(RC_WHACK_PROBLEM);
1282 if (write(sock, &msg, len) != len)
1286 fprintf(stderr, "whack: write() failed (%d %s)\n", e, strerror(e));
1287 exit(RC_WHACK_PROBLEM);
1290 /* for now, just copy reply back to stdout */
1293 char buf[4097]; /* arbitrary limit on log line length */
1299 ssize_t rl = read(sock, be, (buf + sizeof(buf)-1) - be);
1305 fprintf(stderr, "whack: read() failed (%d %s)\n", e, strerror(e));
1306 exit(RC_WHACK_PROBLEM);
1311 fprintf(stderr, "whack: last line from pluto too long or unterminated\n");
1320 char *le = strchr(ls, '\n');
1324 /* move last, partial line to start of buffer */
1325 memmove(buf, ls, be-ls);
1330 le++; /* include NL in line */
1332 /* figure out prefix number
1333 * and how it should affect our exit status
1336 unsigned long s = strtoul(ls, NULL, 10);
1348 /* case RC_LOG_SERIOUS: */
1355 write(1, ls, le - ls);