(no commit message)
[strongswan.git] / src / whack / whack.c
1 /* command interface to Pluto
2 * Copyright (C) 1997 Angelos D. Keromytis.
3 * Copyright (C) 1998-2001 D. Hugh Redelmeier.
4 *
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>.
9 *
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
13 * for more details.
14 *
15 * RCSID $Id: whack.c,v 1.21 2006/04/20 04:42:12 as Exp $
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stddef.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <getopt.h>
31 #include <assert.h>
32
33 #include <freeswan.h>
34
35 #include "constants.h"
36 #include "defs.h"
37 #include "whack.h"
38
39 static void
40 help(void)
41 {
42 fprintf(stderr
43 , "Usage:\n\n"
44 "all forms:"
45 " [--optionsfrom <filename>]"
46 " [--ctlbase <path>]"
47 " [--label <string>]"
48 "\n\n"
49 "help: whack"
50 " [--help]"
51 " [--version]"
52 "\n\n"
53 "connection: whack"
54 " --name <connection_name>"
55 " \\\n "
56 " [--ipv4 | --ipv6]"
57 " [--tunnelipv4 | --tunnelipv6]"
58 " \\\n "
59 " (--host <ip-address> | --id <identity>)"
60 " \\\n "
61 " [--cert <path>]"
62 " [--ca <distinguished name>]"
63 " [--sendcert <policy>]"
64 " \\\n "
65 " [--groups <access control groups>]"
66 " \\\n "
67 " [--ikeport <port-number>]"
68 " [--nexthop <ip-address>]"
69 " [--srcip <ip-address>]"
70 " \\\n "
71 " [--client <subnet> | --clientwithin <address range>]"
72 " [--clientprotoport <protocol>/<port>]"
73 " \\\n "
74 " [--dnskeyondemand]"
75 " [--updown <updown>]"
76 " \\\n "
77 " --to"
78 " (--host <ip-address> | --id <identity>)"
79 " \\\n "
80 " [--cert <path>]"
81 " [--ca <distinguished name>]"
82 " [--sendcert <policy>]"
83 " \\\n "
84 " [--ikeport <port-number>]"
85 " [--nexthop <ip-address>]"
86 " [--srcip <ip-address>]"
87 " \\\n "
88 " [--client <subnet> | --clientwithin <address range>]"
89 " [--clientprotoport <protocol>/<port>]"
90 " \\\n "
91 " [--dnskeyondemand]"
92 " [--updown <updown>]"
93 " [--psk]"
94 " [--rsasig]"
95 " \\\n "
96 " [--encrypt]"
97 " [--authenticate]"
98 " [--compress]"
99 " [--tunnel]"
100 " [--pfs]"
101 " \\\n "
102 " [--ikelifetime <seconds>]"
103 " [--ipseclifetime <seconds>]"
104 " \\\n "
105 " [--reykeymargin <seconds>]"
106 " [--reykeyfuzz <percentage>]"
107 " \\\n "
108 " [--keyingtries <count>]"
109 " \\\n "
110 " [--esp <esp-algos>]"
111 " \\\n "
112 " [--dontrekey]"
113
114 " [--dpdaction (none|clear|hold|restart)]"
115 " \\\n "
116 " [--dpddelay <seconds> --dpdtimeout <seconds>]"
117 " \\\n "
118 " [--initiateontraffic|--pass|--drop|--reject]"
119 " \\\n "
120 " [--failnone|--failpass|--faildrop|--failreject]"
121 "\n\n"
122 "routing: whack"
123 " (--route | --unroute)"
124 " --name <connection_name>"
125 "\n\n"
126 "initiation:"
127 "\n "
128 " whack"
129 " (--initiate | --terminate)"
130 " --name <connection_name>"
131 " [--asynchronous]"
132 "\n\n"
133 "opportunistic initiation: whack"
134 " [--tunnelipv4 | --tunnelipv6]"
135 " \\\n "
136 " --oppohere <ip-address>"
137 " --oppothere <ip-address>"
138 "\n\n"
139 "delete: whack"
140 " --delete"
141 " (--name <connection_name> | --caname <ca name>)"
142 "\n\n"
143 "deletestate: whack"
144 " --deletestate <state_object_number>"
145 " --crash <ip-address>"
146 "\n\n"
147 "pubkey: whack"
148 " --keyid <id>"
149 " [--addkey]"
150 " [--pubkeyrsa <key>]"
151 "\n\n"
152 "myid: whack"
153 " --myid <id>"
154 "\n\n"
155 "ca: whack"
156 " --caname <name>"
157 " --cacert <path>"
158 " \\\n "
159 " [--ldaphost <hostname>]"
160 " [--ldapbase <base>]"
161 " \\\n "
162 " [--crluri <uri>]"
163 " [--crluri2 <uri>]"
164 " [--ocspuri <uri>]"
165 " [--strictcrlpolicy]"
166 "\n\n"
167 #ifdef DEBUG
168 "debug: whack [--name <connection_name>]"
169 " \\\n "
170 " [--debug-none]"
171 " [--debug-all]"
172 " \\\n "
173 " [--debug-raw]"
174 " [--debug-crypt]"
175 " [--debug-parsing]"
176 " [--debug-emitting]"
177 " \\\n "
178 " [--debug-control]"
179 " [--debug-lifecycle]"
180 " [--debug-klips]"
181 " [--debug-dns]"
182 " \\\n "
183 " [--debug-natt]"
184 " [--debug-oppo]"
185 " [--debug-controlmore]"
186 " [--debug-private]"
187 "\n\n"
188 #endif
189 "listen: whack"
190 " (--listen | --unlisten)"
191 "\n\n"
192 "list: whack [--utc]"
193 " [--listalgs]"
194 " [--listpubkeys]"
195 " [--listcerts]"
196 " [--listcacerts]"
197 " \\\n "
198 " [--listacerts]"
199 " [--listaacerts]"
200 " [--listocspcerts]"
201 " [--listgroups]"
202 " \\\n "
203 " [--listcainfos]"
204 " [--listcrls]"
205 " [--listocsp]"
206 " [--listcards]"
207 " [--listall]"
208 "\n\n"
209 "purge: whack"
210 " [--purgeocsp]"
211 "\n\n"
212 "reread: whack"
213 " [--rereadsecrets]"
214 " [--rereadcacerts]"
215 " [--rereadaacerts]"
216 " \\\n "
217 " [--rereadocspcerts]"
218 " [--rereadacerts]"
219 " [--rereadcrls]"
220 " [--rereadall]"
221 "\n\n"
222 "status: whack"
223 " [--name <connection_name>] --status|--statusall"
224 "\n\n"
225 "scdecrypt: whack"
226 " --scencrypt|scdecrypt <value>"
227 " [--inbase <base>]"
228 " [--outbase <base>]"
229 " [--keyid <id>]"
230 "\n\n"
231 "shutdown: whack"
232 " --shutdown"
233 "\n\n"
234 "strongSwan %s\n"
235 , ipsec_version_code());
236 }
237
238 static const char *label = NULL; /* --label operand, saved for diagnostics */
239
240 static const char *name = NULL; /* --name operand, saved for diagnostics */
241
242 /* print a string as a diagnostic, then exit whack unhappily */
243 static void
244 diag(const char *mess)
245 {
246 if (mess != NULL)
247 {
248 fprintf(stderr, "whack error: ");
249 if (label != NULL)
250 fprintf(stderr, "%s ", label);
251 if (name != NULL)
252 fprintf(stderr, "\"%s\" ", name);
253 fprintf(stderr, "%s\n", mess);
254 }
255
256 exit(RC_WHACK_PROBLEM);
257 }
258
259 /* conditially calls diag; prints second arg, if non-NULL, as quoted string */
260 static void
261 diagq(err_t ugh, const char *this)
262 {
263 if (ugh != NULL)
264 {
265 if (this == NULL)
266 {
267 diag(ugh);
268 }
269 else
270 {
271 char buf[120]; /* arbitrary limit */
272
273 snprintf(buf, sizeof(buf), "%s \"%s\"", ugh, this);
274 diag(buf);
275 }
276 }
277 }
278
279 /* complex combined operands return one of these enumerated values
280 * Note: these become flags in an lset_t. Since there are more than
281 * 32, we partition them into:
282 * - OPT_* options (most random options)
283 * - LST_* options (list various internal data)
284 * - DBGOPT_* option (DEBUG options)
285 * - END_* options (End description options)
286 * - CD_* options (Connection Description options)
287 * - CA_* options (CA description options)
288 */
289 enum {
290 # define OPT_FIRST OPT_CTLBASE
291 OPT_CTLBASE,
292 OPT_NAME,
293
294 OPT_CD,
295
296 OPT_KEYID,
297 OPT_ADDKEY,
298 OPT_PUBKEYRSA,
299
300 OPT_MYID,
301
302 OPT_ROUTE,
303 OPT_UNROUTE,
304
305 OPT_INITIATE,
306 OPT_TERMINATE,
307 OPT_DELETE,
308 OPT_DELETESTATE,
309 OPT_LISTEN,
310 OPT_UNLISTEN,
311
312 OPT_PURGEOCSP,
313
314 OPT_REREADSECRETS,
315 OPT_REREADCACERTS,
316 OPT_REREADAACERTS,
317 OPT_REREADOCSPCERTS,
318 OPT_REREADACERTS,
319 OPT_REREADCRLS,
320 OPT_REREADALL,
321
322 OPT_STATUS,
323 OPT_STATUSALL,
324 OPT_SHUTDOWN,
325
326 OPT_OPPO_HERE,
327 OPT_OPPO_THERE,
328
329 OPT_ASYNC,
330 OPT_DELETECRASH,
331
332 # define OPT_LAST OPT_ASYNC /* last "normal" option */
333
334 /* Smartcard options */
335
336 # define SC_FIRST SC_ENCRYPT /* first smartcard option */
337
338 SC_ENCRYPT,
339 SC_DECRYPT,
340 SC_INBASE,
341 SC_OUTBASE,
342
343 # define SC_LAST SC_OUTBASE /* last "smartcard" option */
344
345 /* List options */
346
347 # define LST_FIRST LST_UTC /* first list option */
348 LST_UTC,
349 LST_ALGS,
350 LST_PUBKEYS,
351 LST_CERTS,
352 LST_CACERTS,
353 LST_ACERTS,
354 LST_AACERTS,
355 LST_OCSPCERTS,
356 LST_GROUPS,
357 LST_CAINFOS,
358 LST_CRLS,
359 LST_OCSP,
360 LST_CARDS,
361 LST_ALL,
362
363 # define LST_LAST LST_ALL /* last list option */
364
365 /* Connection End Description options */
366
367 # define END_FIRST END_HOST /* first end description */
368 END_HOST,
369 END_ID,
370 END_CERT,
371 END_CA,
372 END_SENDCERT,
373 END_GROUPS,
374 END_IKEPORT,
375 END_NEXTHOP,
376 END_CLIENT,
377 END_CLIENTWITHIN,
378 END_CLIENTPROTOPORT,
379 END_DNSKEYONDEMAND,
380 END_SRCIP,
381 END_HOSTACCESS,
382 END_UPDOWN,
383
384 #define END_LAST END_UPDOWN /* last end description*/
385
386 /* Connection Description options -- segregated */
387
388 # define CD_FIRST CD_TO /* first connection description */
389 CD_TO,
390
391 # define CD_POLICY_FIRST CD_PSK
392 CD_PSK, /* same order as POLICY_* */
393 CD_RSASIG, /* same order as POLICY_* */
394 CD_ENCRYPT, /* same order as POLICY_* */
395 CD_AUTHENTICATE, /* same order as POLICY_* */
396 CD_COMPRESS, /* same order as POLICY_* */
397 CD_TUNNEL, /* same order as POLICY_* */
398 CD_PFS, /* same order as POLICY_* */
399 CD_DISABLEARRIVALCHECK, /* same order as POLICY_* */
400 CD_SHUNT0, /* same order as POLICY_* */
401 CD_SHUNT1, /* same order as POLICY_* */
402 CD_FAIL0, /* same order as POLICY_* */
403 CD_FAIL1, /* same order as POLICY_* */
404 CD_DONT_REKEY, /* same order as POLICY_* */
405
406 CD_TUNNELIPV4,
407 CD_TUNNELIPV6,
408 CD_CONNIPV4,
409 CD_CONNIPV6,
410
411 CD_IKELIFETIME,
412 CD_IPSECLIFETIME,
413 CD_RKMARGIN,
414 CD_RKFUZZ,
415 CD_KTRIES,
416 CD_DPDACTION,
417 CD_DPDDELAY,
418 CD_DPDTIMEOUT,
419 CD_IKE,
420 CD_PFSGROUP,
421 CD_ESP,
422
423 # define CD_LAST CD_ESP /* last connection description */
424
425 /* Certificate Authority (CA) description options */
426
427 # define CA_FIRST CA_NAME /* first ca description */
428
429 CA_NAME,
430 CA_CERT,
431 CA_LDAPHOST,
432 CA_LDAPBASE,
433 CA_CRLURI,
434 CA_CRLURI2,
435 CA_OCSPURI,
436 CA_STRICT
437
438 # define CA_LAST CA_STRICT /* last ca description */
439
440 #ifdef DEBUG /* must be last so others are less than 32 to fit in lset_t */
441 # define DBGOPT_FIRST DBGOPT_NONE
442 ,
443 /* NOTE: these definitions must match DBG_* and IMPAIR_* in constants.h */
444 DBGOPT_NONE,
445 DBGOPT_ALL,
446
447 DBGOPT_RAW, /* same order as DBG_* */
448 DBGOPT_CRYPT, /* same order as DBG_* */
449 DBGOPT_PARSING, /* same order as DBG_* */
450 DBGOPT_EMITTING, /* same order as DBG_* */
451 DBGOPT_CONTROL, /* same order as DBG_* */
452 DBGOPT_LIFECYCLE, /* same order as DBG_* */
453 DBGOPT_KLIPS, /* same order as DBG_* */
454 DBGOPT_DNS, /* same order as DBG_* */
455 DBGOPT_NATT, /* same order as DBG_* */
456 DBGOPT_OPPO, /* same order as DBG_* */
457 DBGOPT_CONTROLMORE, /* same order as DBG_* */
458
459 DBGOPT_PRIVATE, /* same order as DBG_* */
460
461 DBGOPT_IMPAIR_DELAY_ADNS_KEY_ANSWER, /* same order as IMPAIR_* */
462 DBGOPT_IMPAIR_DELAY_ADNS_TXT_ANSWER, /* same order as IMPAIR_* */
463 DBGOPT_IMPAIR_BUST_MI2, /* same order as IMPAIR_* */
464 DBGOPT_IMPAIR_BUST_MR2 /* same order as IMPAIR_* */
465
466 # define DBGOPT_LAST DBGOPT_IMPAIR_BUST_MR2
467 #endif
468
469 };
470
471 /* Carve up space for result from getop_long.
472 * Stupidly, the only result is an int.
473 * Numeric arg is bit immediately left of basic value.
474 *
475 */
476 #define OPTION_OFFSET 256 /* to get out of the way of letter options */
477 #define NUMERIC_ARG (1 << 9) /* expect a numeric argument */
478 #define AUX_SHIFT 10 /* amount to shift for aux information */
479
480 static const struct option long_opts[] = {
481 # define OO OPTION_OFFSET
482 /* name, has_arg, flag, val */
483
484 { "help", no_argument, NULL, 'h' },
485 { "version", no_argument, NULL, 'v' },
486 { "optionsfrom", required_argument, NULL, '+' },
487 { "label", required_argument, NULL, 'l' },
488
489 { "ctlbase", required_argument, NULL, OPT_CTLBASE + OO },
490 { "name", required_argument, NULL, OPT_NAME + OO },
491
492 { "keyid", required_argument, NULL, OPT_KEYID + OO },
493 { "addkey", no_argument, NULL, OPT_ADDKEY + OO },
494 { "pubkeyrsa", required_argument, NULL, OPT_PUBKEYRSA + OO },
495
496 { "myid", required_argument, NULL, OPT_MYID + OO },
497
498 { "route", no_argument, NULL, OPT_ROUTE + OO },
499 { "unroute", no_argument, NULL, OPT_UNROUTE + OO },
500
501 { "initiate", no_argument, NULL, OPT_INITIATE + OO },
502 { "terminate", no_argument, NULL, OPT_TERMINATE + OO },
503 { "delete", no_argument, NULL, OPT_DELETE + OO },
504 { "deletestate", required_argument, NULL, OPT_DELETESTATE + OO + NUMERIC_ARG },
505 { "crash", required_argument, NULL, OPT_DELETECRASH + OO },
506 { "listen", no_argument, NULL, OPT_LISTEN + OO },
507 { "unlisten", no_argument, NULL, OPT_UNLISTEN + OO },
508
509 { "purgeocsp", no_argument, NULL, OPT_PURGEOCSP + OO },
510
511 { "rereadsecrets", no_argument, NULL, OPT_REREADSECRETS + OO },
512 { "rereadcacerts", no_argument, NULL, OPT_REREADCACERTS + OO },
513 { "rereadaacerts", no_argument, NULL, OPT_REREADAACERTS + OO },
514 { "rereadocspcerts", no_argument, NULL, OPT_REREADOCSPCERTS + OO },
515 { "rereadacerts", no_argument, NULL, OPT_REREADACERTS + OO },
516 { "rereadcrls", no_argument, NULL, OPT_REREADCRLS + OO },
517 { "rereadall", no_argument, NULL, OPT_REREADALL + OO },
518 { "status", no_argument, NULL, OPT_STATUS + OO },
519 { "statusall", no_argument, NULL, OPT_STATUSALL + OO },
520 { "shutdown", no_argument, NULL, OPT_SHUTDOWN + OO },
521
522 { "oppohere", required_argument, NULL, OPT_OPPO_HERE + OO },
523 { "oppothere", required_argument, NULL, OPT_OPPO_THERE + OO },
524
525 { "asynchronous", no_argument, NULL, OPT_ASYNC + OO },
526
527 /* smartcard options */
528
529 { "scencrypt", required_argument, NULL, SC_ENCRYPT + OO },
530 { "scdecrypt", required_argument, NULL, SC_DECRYPT + OO },
531 { "inbase", required_argument, NULL, SC_INBASE + OO },
532 { "outbase", required_argument, NULL, SC_OUTBASE + OO },
533
534 /* list options */
535
536 { "utc", no_argument, NULL, LST_UTC + OO },
537 { "listalgs", no_argument, NULL, LST_ALGS + OO },
538 { "listpubkeys", no_argument, NULL, LST_PUBKEYS + OO },
539 { "listcerts", no_argument, NULL, LST_CERTS + OO },
540 { "listcacerts", no_argument, NULL, LST_CACERTS + OO },
541 { "listacerts", no_argument, NULL, LST_ACERTS + OO },
542 { "listaacerts", no_argument, NULL, LST_AACERTS + OO },
543 { "listocspcerts", no_argument, NULL, LST_OCSPCERTS + OO },
544 { "listgroups", no_argument, NULL, LST_GROUPS + OO },
545 { "listcainfos", no_argument, NULL, LST_CAINFOS + OO },
546 { "listcrls", no_argument, NULL, LST_CRLS + OO },
547 { "listocsp", no_argument, NULL, LST_OCSP + OO },
548 { "listcards", no_argument, NULL, LST_CARDS + OO },
549 { "listall", no_argument, NULL, LST_ALL + OO },
550
551 /* options for an end description */
552
553 { "host", required_argument, NULL, END_HOST + OO },
554 { "id", required_argument, NULL, END_ID + OO },
555 { "cert", required_argument, NULL, END_CERT + OO },
556 { "ca", required_argument, NULL, END_CA + OO },
557 { "sendcert", required_argument, NULL, END_SENDCERT + OO },
558 { "groups", required_argument, NULL, END_GROUPS + OO },
559 { "ikeport", required_argument, NULL, END_IKEPORT + OO + NUMERIC_ARG },
560 { "nexthop", required_argument, NULL, END_NEXTHOP + OO },
561 { "client", required_argument, NULL, END_CLIENT + OO },
562 { "clientwithin", required_argument, NULL, END_CLIENTWITHIN + OO },
563 { "clientprotoport", required_argument, NULL, END_CLIENTPROTOPORT + OO },
564 { "dnskeyondemand", no_argument, NULL, END_DNSKEYONDEMAND + OO },
565 { "srcip", required_argument, NULL, END_SRCIP + OO },
566 { "hostaccess", no_argument, NULL, END_HOSTACCESS + OO },
567 { "updown", required_argument, NULL, END_UPDOWN + OO },
568
569 /* options for a connection description */
570
571 { "to", no_argument, NULL, CD_TO + OO },
572
573 { "psk", no_argument, NULL, CD_PSK + OO },
574 { "rsasig", no_argument, NULL, CD_RSASIG + OO },
575
576 { "encrypt", no_argument, NULL, CD_ENCRYPT + OO },
577 { "authenticate", no_argument, NULL, CD_AUTHENTICATE + OO },
578 { "compress", no_argument, NULL, CD_COMPRESS + OO },
579 { "tunnel", no_argument, NULL, CD_TUNNEL + OO },
580 { "tunnelipv4", no_argument, NULL, CD_TUNNELIPV4 + OO },
581 { "tunnelipv6", no_argument, NULL, CD_TUNNELIPV6 + OO },
582 { "pfs", no_argument, NULL, CD_PFS + OO },
583 { "disablearrivalcheck", no_argument, NULL, CD_DISABLEARRIVALCHECK + OO },
584 { "initiateontraffic", no_argument, NULL
585 , CD_SHUNT0 + (POLICY_SHUNT_TRAP >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
586 { "pass", no_argument, NULL
587 , CD_SHUNT0 + (POLICY_SHUNT_PASS >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
588 { "drop", no_argument, NULL
589 , CD_SHUNT0 + (POLICY_SHUNT_DROP >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
590 { "reject", no_argument, NULL
591 , CD_SHUNT0 + (POLICY_SHUNT_REJECT >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
592 { "failnone", no_argument, NULL
593 , CD_FAIL0 + (POLICY_FAIL_NONE >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
594 { "failpass", no_argument, NULL
595 , CD_FAIL0 + (POLICY_FAIL_PASS >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
596 { "faildrop", no_argument, NULL
597 , CD_FAIL0 + (POLICY_FAIL_DROP >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
598 { "failreject", no_argument, NULL
599 , CD_FAIL0 + (POLICY_FAIL_REJECT >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
600 { "dontrekey", no_argument, NULL, CD_DONT_REKEY + OO },
601 { "ipv4", no_argument, NULL, CD_CONNIPV4 + OO },
602 { "ipv6", no_argument, NULL, CD_CONNIPV6 + OO },
603
604 { "ikelifetime", required_argument, NULL, CD_IKELIFETIME + OO + NUMERIC_ARG },
605 { "ipseclifetime", required_argument, NULL, CD_IPSECLIFETIME + OO + NUMERIC_ARG },
606 { "rekeymargin", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG },
607 { "rekeywindow", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG }, /* OBSOLETE */
608 { "rekeyfuzz", required_argument, NULL, CD_RKFUZZ + OO + NUMERIC_ARG },
609 { "keyingtries", required_argument, NULL, CD_KTRIES + OO + NUMERIC_ARG },
610 { "dpdaction", required_argument, NULL, CD_DPDACTION + OO },
611 { "dpddelay", required_argument, NULL, CD_DPDDELAY + OO + NUMERIC_ARG },
612 { "dpdtimeout", required_argument, NULL, CD_DPDTIMEOUT + OO + NUMERIC_ARG },
613 { "ike", required_argument, NULL, CD_IKE + OO },
614 { "pfsgroup", required_argument, NULL, CD_PFSGROUP + OO },
615 { "esp", required_argument, NULL, CD_ESP + OO },
616
617 /* options for a ca description */
618
619 { "caname", required_argument, NULL, CA_NAME + OO },
620 { "cacert", required_argument, NULL, CA_CERT + OO },
621 { "ldaphost", required_argument, NULL, CA_LDAPHOST + OO },
622 { "ldapbase", required_argument, NULL, CA_LDAPBASE + OO },
623 { "crluri", required_argument, NULL, CA_CRLURI + OO },
624 { "crluri2", required_argument, NULL, CA_CRLURI2 + OO },
625 { "ocspuri", required_argument, NULL, CA_OCSPURI + OO },
626 { "strictcrlpolicy", no_argument, NULL, CA_STRICT + OO },
627
628 #ifdef DEBUG
629 { "debug-none", no_argument, NULL, DBGOPT_NONE + OO },
630 { "debug-all]", no_argument, NULL, DBGOPT_ALL + OO },
631 { "debug-raw", no_argument, NULL, DBGOPT_RAW + OO },
632 { "debug-crypt", no_argument, NULL, DBGOPT_CRYPT + OO },
633 { "debug-parsing", no_argument, NULL, DBGOPT_PARSING + OO },
634 { "debug-emitting", no_argument, NULL, DBGOPT_EMITTING + OO },
635 { "debug-control", no_argument, NULL, DBGOPT_CONTROL + OO },
636 { "debug-lifecycle", no_argument, NULL, DBGOPT_LIFECYCLE + OO },
637 { "debug-klips", no_argument, NULL, DBGOPT_KLIPS + OO },
638 { "debug-dns", no_argument, NULL, DBGOPT_DNS + OO },
639 { "debug-natt", no_argument, NULL, DBGOPT_NATT + OO },
640 { "debug-oppo", no_argument, NULL, DBGOPT_OPPO + OO },
641 { "debug-controlmore", no_argument, NULL, DBGOPT_CONTROLMORE + OO },
642 { "debug-private", no_argument, NULL, DBGOPT_PRIVATE + OO },
643
644 { "impair-delay-adns-key-answer", no_argument, NULL, DBGOPT_IMPAIR_DELAY_ADNS_KEY_ANSWER + OO },
645 { "impair-delay-adns-txt-answer", no_argument, NULL, DBGOPT_IMPAIR_DELAY_ADNS_TXT_ANSWER + OO },
646 { "impair-bust-mi2", no_argument, NULL, DBGOPT_IMPAIR_BUST_MI2 + OO },
647 { "impair-bust-mr2", no_argument, NULL, DBGOPT_IMPAIR_BUST_MR2 + OO },
648 #endif
649 # undef OO
650 { 0,0,0,0 }
651 };
652
653 struct sockaddr_un ctl_addr = { AF_UNIX, DEFAULT_CTLBASE CTL_SUFFIX };
654
655 /* helper variables and function to encode strings from whack message */
656
657 static char
658 *next_str,
659 *str_roof;
660
661 static bool
662 pack_str(char **p)
663 {
664 const char *s = *p == NULL? "" : *p; /* note: NULL becomes ""! */
665 size_t len = strlen(s) + 1;
666
667 if (str_roof - next_str < (ptrdiff_t)len)
668 {
669 return FALSE; /* fishy: no end found */
670 }
671 else
672 {
673 strcpy(next_str, s);
674 next_str += len;
675 *p = NULL; /* don't send pointers on the wire! */
676 return TRUE;
677 }
678 }
679
680 static void
681 check_life_time(time_t life, time_t limit, const char *which
682 , const whack_message_t *msg)
683 {
684 time_t mint = msg->sa_rekey_margin * (100 + msg->sa_rekey_fuzz) / 100;
685
686 if (life > limit)
687 {
688 char buf[200]; /* arbitrary limit */
689
690 snprintf(buf, sizeof(buf)
691 , "%s [%lu seconds] must be less than %lu seconds"
692 , which, (unsigned long)life, (unsigned long)limit);
693 diag(buf);
694 }
695 if ((msg->policy & POLICY_DONT_REKEY) == LEMPTY && life <= mint)
696 {
697 char buf[200]; /* arbitrary limit */
698
699 snprintf(buf, sizeof(buf)
700 , "%s [%lu] must be greater than"
701 " rekeymargin*(100+rekeyfuzz)/100 [%lu*(100+%lu)/100 = %lu]"
702 , which
703 , (unsigned long)life
704 , (unsigned long)msg->sa_rekey_margin
705 , (unsigned long)msg->sa_rekey_fuzz
706 , (unsigned long)mint);
707 diag(buf);
708 }
709 }
710
711 static void
712 clear_end(whack_end_t *e)
713 {
714 zero(e);
715 e->id = NULL;
716 e->cert = NULL;
717 e->ca = NULL;
718 e->updown = NULL;
719 e->host_port = IKE_UDP_PORT;
720 }
721
722 static void
723 update_ports(whack_message_t *m)
724 {
725 int port;
726
727 if (m->left.port != 0) {
728 port = htons(m->left.port);
729 setportof(port, &m->left.host_addr);
730 setportof(port, &m->left.client.addr);
731 }
732 if (m->right.port != 0) {
733 port = htons(m->right.port);
734 setportof(port, &m->right.host_addr);
735 setportof(port, &m->right.client.addr);
736 }
737 }
738
739 static void
740 check_end(whack_end_t *this, whack_end_t *that
741 , bool default_nexthop, sa_family_t caf, sa_family_t taf)
742 {
743 if (caf != addrtypeof(&this->host_addr))
744 diag("address family of host inconsistent");
745
746 if (default_nexthop)
747 {
748 if (isanyaddr(&that->host_addr))
749 diag("our nexthop must be specified when other host is a %any or %opportunistic");
750 this->host_nexthop = that->host_addr;
751 }
752
753 if (caf != addrtypeof(&this->host_nexthop))
754 diag("address family of nexthop inconsistent");
755
756 if (this->has_client)
757 {
758 if (taf != subnettypeof(&this->client))
759 diag("address family of client subnet inconsistent");
760 }
761 else
762 {
763 /* fill in anyaddr-anyaddr as (missing) client subnet */
764 ip_address cn;
765
766 diagq(anyaddr(caf, &cn), NULL);
767 diagq(rangetosubnet(&cn, &cn, &this->client), NULL);
768 }
769
770 /* fill in anyaddr if source IP is not defined */
771 if (!this->has_srcip)
772 diagq(anyaddr(caf, &this->host_srcip), optarg);
773
774 /* check protocol */
775 if (this->protocol != that->protocol)
776 diag("the protocol for leftprotoport and rightprotoport must be the same");
777 }
778
779 static void
780 get_secret(int sock)
781 {
782 const char *buf, *secret;
783 int len;
784
785 fflush(stdout);
786 usleep(20000); /* give fflush time for flushing */
787 buf = getpass("Enter: ");
788 secret = (buf == NULL)? "" : buf;
789
790 /* send the secret to pluto */
791 len = strlen(secret) + 1;
792 if (write(sock, secret, len) != len)
793 {
794 int e = errno;
795
796 fprintf(stderr, "whack: write() failed (%d %s)\n", e, strerror(e));
797 exit(RC_WHACK_PROBLEM);
798 }
799 }
800
801 /* This is a hack for initiating ISAKMP exchanges. */
802
803 int
804 main(int argc, char **argv)
805 {
806 whack_message_t msg;
807 char esp_buf[256]; /* uses snprintf */
808 lset_t
809 opts_seen = LEMPTY,
810 sc_seen = LEMPTY,
811 lst_seen = LEMPTY,
812 cd_seen = LEMPTY,
813 ca_seen = LEMPTY,
814 end_seen = LEMPTY,
815 end_seen_before_to = LEMPTY;
816 const char
817 *af_used_by = NULL,
818 *tunnel_af_used_by = NULL;
819
820 /* check division of numbering space */
821 #ifdef DEBUG
822 assert(OPTION_OFFSET + DBGOPT_LAST < NUMERIC_ARG);
823 #else
824 assert(OPTION_OFFSET + CA_LAST < NUMERIC_ARG);
825 #endif
826 assert(OPT_LAST - OPT_FIRST < (sizeof opts_seen * BITS_PER_BYTE));
827 assert(SC_LAST - SC_FIRST < (sizeof sc_seen * BITS_PER_BYTE));
828 assert(LST_LAST - LST_FIRST < (sizeof lst_seen * BITS_PER_BYTE));
829 assert(END_LAST - END_FIRST < (sizeof end_seen * BITS_PER_BYTE));
830 assert(CD_LAST - CD_FIRST < (sizeof cd_seen * BITS_PER_BYTE));
831 assert(CA_LAST - CA_FIRST < (sizeof ca_seen * BITS_PER_BYTE));
832 #ifdef DEBUG /* must be last so others are less than (sizeof cd_seen * BITS_PER_BYTE) to fit in lset_t */
833 assert(DBGOPT_LAST - DBGOPT_FIRST < (sizeof cd_seen * BITS_PER_BYTE));
834 #endif
835 /* check that POLICY bit assignment matches with CD_ */
836 assert(LELEM(CD_DONT_REKEY - CD_POLICY_FIRST) == POLICY_DONT_REKEY);
837
838 zero(&msg);
839
840 clear_end(&msg.right); /* left set from this after --to */
841
842 msg.name = NULL;
843 msg.keyid = NULL;
844 msg.keyval.ptr = NULL;
845 msg.esp = NULL;
846 msg.ike = NULL;
847 msg.pfsgroup = NULL;
848
849 msg.sa_ike_life_seconds = OAKLEY_ISAKMP_SA_LIFETIME_DEFAULT;
850 msg.sa_ipsec_life_seconds = PLUTO_SA_LIFE_DURATION_DEFAULT;
851 msg.sa_rekey_margin = SA_REPLACEMENT_MARGIN_DEFAULT;
852 msg.sa_rekey_fuzz = SA_REPLACEMENT_FUZZ_DEFAULT;
853 msg.sa_keying_tries = SA_REPLACEMENT_RETRIES_DEFAULT;
854
855 msg.addr_family = AF_INET;
856 msg.tunnel_addr_family = AF_INET;
857
858 msg.cacert = NULL;
859 msg.ldaphost = NULL;
860 msg.ldapbase = NULL;
861 msg.crluri = NULL;
862 msg.crluri2 = NULL;
863 msg.ocspuri = NULL;
864
865 for (;;)
866 {
867 int long_index;
868 unsigned long opt_whole = 0; /* numeric argument for some flags */
869
870 /* Note: we don't like the way short options get parsed
871 * by getopt_long, so we simply pass an empty string as
872 * the list. It could be "hp:d:c:o:eatfs" "NARXPECK".
873 */
874 int c = getopt_long(argc, argv, "", long_opts, &long_index) - OPTION_OFFSET;
875 int aux = 0;
876
877 /* decode a numeric argument, if expected */
878 if (0 <= c)
879 {
880 if (c & NUMERIC_ARG)
881 {
882 char *endptr;
883
884 c -= NUMERIC_ARG;
885 opt_whole = strtoul(optarg, &endptr, 0);
886
887 if (*endptr != '\0' || endptr == optarg)
888 diagq("badly formed numeric argument", optarg);
889 }
890 if (c >= (1 << AUX_SHIFT))
891 {
892 aux = c >> AUX_SHIFT;
893 c -= aux << AUX_SHIFT;
894 }
895 }
896
897 /* per-class option processing */
898 if (0 <= c && c <= OPT_LAST)
899 {
900 /* OPT_* options get added to opts_seen.
901 * Reject repeated options (unless later code intervenes).
902 */
903 lset_t f = LELEM(c);
904
905 if (opts_seen & f)
906 diagq("duplicated flag", long_opts[long_index].name);
907 opts_seen |= f;
908 }
909 else if (SC_FIRST <= c && c <= SC_LAST)
910 {
911 /* SC_* options get added to sc_seen.
912 * Reject repeated options (unless later code intervenes).
913 */
914 lset_t f = LELEM(c - SC_FIRST);
915
916 if (sc_seen & f)
917 diagq("duplicated flag", long_opts[long_index].name);
918 sc_seen |= f;
919 }
920 else if (LST_FIRST <= c && c <= LST_LAST)
921 {
922 /* LST_* options get added to lst_seen.
923 * Reject repeated options (unless later code intervenes).
924 */
925 lset_t f = LELEM(c - LST_FIRST);
926
927 if (lst_seen & f)
928 diagq("duplicated flag", long_opts[long_index].name);
929 lst_seen |= f;
930 }
931 #ifdef DEBUG
932 else if (DBGOPT_FIRST <= c && c <= DBGOPT_LAST)
933 {
934 msg.whack_options = TRUE;
935 }
936 #endif
937 else if (END_FIRST <= c && c <= END_LAST)
938 {
939 /* END_* options are added to end_seen.
940 * Reject repeated options (unless later code intervenes).
941 */
942 lset_t f = LELEM(c - END_FIRST);
943
944 if (end_seen & f)
945 diagq("duplicated flag", long_opts[long_index].name);
946 end_seen |= f;
947 opts_seen |= LELEM(OPT_CD);
948 }
949 else if (CD_FIRST <= c && c <= CD_LAST)
950 {
951 /* CD_* options are added to cd_seen.
952 * Reject repeated options (unless later code intervenes).
953 */
954 lset_t f = LELEM(c - CD_FIRST);
955
956 if (cd_seen & f)
957 diagq("duplicated flag", long_opts[long_index].name);
958 cd_seen |= f;
959 opts_seen |= LELEM(OPT_CD);
960 }
961 else if (CA_FIRST <= c && c <= CA_LAST)
962 {
963 /* CA_* options are added to ca_seen.
964 * Reject repeated options (unless later code intervenes).
965 */
966 lset_t f = LELEM(c - CA_FIRST);
967
968 if (ca_seen & f)
969 diagq("duplicated flag", long_opts[long_index].name);
970 ca_seen |= f;
971 }
972
973 /* Note: "break"ing from switch terminates loop.
974 * most cases should end with "continue".
975 */
976 switch (c)
977 {
978 case EOF - OPTION_OFFSET: /* end of flags */
979 break;
980
981 case 0 - OPTION_OFFSET: /* long option already handled */
982 continue;
983
984 case ':' - OPTION_OFFSET: /* diagnostic already printed by getopt_long */
985 case '?' - OPTION_OFFSET: /* diagnostic already printed by getopt_long */
986 diag(NULL); /* print no additional diagnostic, but exit sadly */
987 break; /* not actually reached */
988
989 case 'h' - OPTION_OFFSET: /* --help */
990 help();
991 return 0; /* GNU coding standards say to stop here */
992
993 case 'v' - OPTION_OFFSET: /* --version */
994 {
995 const char **sp = ipsec_copyright_notice();
996
997 printf("%s\n", ipsec_version_string());
998 for (; *sp != NULL; sp++)
999 puts(*sp);
1000 }
1001 return 0; /* GNU coding standards say to stop here */
1002
1003 case 'l' - OPTION_OFFSET: /* --label <string> */
1004 label = optarg; /* remember for diagnostics */
1005 continue;
1006
1007 case '+' - OPTION_OFFSET: /* --optionsfrom <filename> */
1008 optionsfrom(optarg, &argc, &argv, optind, stderr);
1009 /* does not return on error */
1010 continue;
1011
1012 /* the rest of the options combine in complex ways */
1013
1014 case OPT_CTLBASE: /* --port <ctlbase> */
1015 if (snprintf(ctl_addr.sun_path, sizeof(ctl_addr.sun_path)
1016 , "%s%s", optarg, CTL_SUFFIX) == -1)
1017 diag("<ctlbase>" CTL_SUFFIX " must be fit in a sun_addr");
1018 continue;
1019
1020 case OPT_NAME: /* --name <connection-name> */
1021 name = optarg;
1022 msg.name = optarg;
1023 continue;
1024
1025 case OPT_KEYID: /* --keyid <identity> */
1026 msg.whack_key = !msg.whack_sc_op;
1027 msg.keyid = optarg; /* decoded by Pluto */
1028 continue;
1029
1030 case OPT_MYID: /* --myid <identity> */
1031 msg.whack_myid = TRUE;
1032 msg.myid = optarg; /* decoded by Pluto */
1033 continue;
1034
1035 case OPT_ADDKEY: /* --addkey */
1036 msg.whack_addkey = TRUE;
1037 continue;
1038
1039 case OPT_PUBKEYRSA: /* --pubkeyrsa <key> */
1040 {
1041 static char keyspace[RSA_MAX_ENCODING_BYTES]; /* room for 8K bit key */
1042 char diag_space[TTODATAV_BUF];
1043 const char *ugh = ttodatav(optarg, 0, 0
1044 , keyspace, sizeof(keyspace)
1045 , &msg.keyval.len, diag_space, sizeof(diag_space)
1046 , TTODATAV_SPACECOUNTS);
1047
1048 if (ugh != NULL)
1049 {
1050 char ugh_space[80]; /* perhaps enough space */
1051
1052 snprintf(ugh_space, sizeof(ugh_space)
1053 , "RSA public-key data malformed (%s)", ugh);
1054 diagq(ugh_space, optarg);
1055 }
1056 msg.pubkey_alg = PUBKEY_ALG_RSA;
1057 msg.keyval.ptr = keyspace;
1058 }
1059 continue;
1060
1061 case OPT_ROUTE: /* --route */
1062 msg.whack_route = TRUE;
1063 continue;
1064
1065 case OPT_UNROUTE: /* --unroute */
1066 msg.whack_unroute = TRUE;
1067 continue;
1068
1069 case OPT_INITIATE: /* --initiate */
1070 msg.whack_initiate = TRUE;
1071 continue;
1072
1073 case OPT_TERMINATE: /* --terminate */
1074 msg.whack_terminate = TRUE;
1075 continue;
1076
1077 case OPT_DELETE: /* --delete */
1078 msg.whack_delete = TRUE;
1079 continue;
1080
1081 case OPT_DELETESTATE: /* --deletestate <state_object_number> */
1082 msg.whack_deletestate = TRUE;
1083 msg.whack_deletestateno = opt_whole;
1084 continue;
1085
1086 case OPT_DELETECRASH: /* --crash <ip-address> */
1087 msg.whack_crash = TRUE;
1088 tunnel_af_used_by = long_opts[long_index].name;
1089 diagq(ttoaddr(optarg, 0, msg.tunnel_addr_family, &msg.whack_crash_peer), optarg);
1090 if (isanyaddr(&msg.whack_crash_peer))
1091 diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
1092 continue;
1093
1094 case OPT_LISTEN: /* --listen */
1095 msg.whack_listen = TRUE;
1096 continue;
1097
1098 case OPT_UNLISTEN: /* --unlisten */
1099 msg.whack_unlisten = TRUE;
1100 continue;
1101
1102 case OPT_PURGEOCSP: /* --purgeocsp */
1103 msg.whack_purgeocsp = TRUE;
1104 continue;
1105
1106 case OPT_REREADSECRETS: /* --rereadsecrets */
1107 case OPT_REREADCACERTS: /* --rereadcacerts */
1108 case OPT_REREADAACERTS: /* --rereadaacerts */
1109 case OPT_REREADOCSPCERTS: /* --rereadocspcerts */
1110 case OPT_REREADACERTS: /* --rereadacerts */
1111 case OPT_REREADCRLS: /* --rereadcrls */
1112 msg.whack_reread |= LELEM(c-OPT_REREADSECRETS);
1113 continue;
1114
1115 case OPT_REREADALL: /* --rereadall */
1116 msg.whack_reread = REREAD_ALL;
1117 continue;
1118
1119 case OPT_STATUSALL: /* --statusall */
1120 msg.whack_statusall = TRUE;
1121
1122 case OPT_STATUS: /* --status */
1123 msg.whack_status = TRUE;
1124 continue;
1125
1126 case OPT_SHUTDOWN: /* --shutdown */
1127 msg.whack_shutdown = TRUE;
1128 continue;
1129
1130 case OPT_OPPO_HERE: /* --oppohere <ip-address> */
1131 tunnel_af_used_by = long_opts[long_index].name;
1132 diagq(ttoaddr(optarg, 0, msg.tunnel_addr_family, &msg.oppo_my_client), optarg);
1133 if (isanyaddr(&msg.oppo_my_client))
1134 diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
1135 continue;
1136
1137 case OPT_OPPO_THERE: /* --oppohere <ip-address> */
1138 tunnel_af_used_by = long_opts[long_index].name;
1139 diagq(ttoaddr(optarg, 0, msg.tunnel_addr_family, &msg.oppo_peer_client), optarg);
1140 if (isanyaddr(&msg.oppo_peer_client))
1141 diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
1142 continue;
1143
1144 case OPT_ASYNC:
1145 msg.whack_async = TRUE;
1146 continue;
1147
1148 /* Smartcard options */
1149
1150 case SC_ENCRYPT: /* --scencrypt <plaintext data> */
1151 case SC_DECRYPT: /* --scdecrypt <encrypted data> */
1152 msg.whack_sc_op = 1 + c - SC_ENCRYPT;
1153 msg.whack_key = FALSE;
1154 msg.sc_data = optarg;
1155 continue;
1156
1157 case SC_INBASE: /* --inform <format> */
1158 case SC_OUTBASE: /* --outform <format> */
1159 {
1160 int base = 0;
1161
1162 if (streq(optarg, "16") || strcaseeq(optarg, "hex"))
1163 base = 16;
1164 else if (streq(optarg, "64") || strcaseeq(optarg, "base64"))
1165 base = 64;
1166 else if (streq(optarg, "256") || strcaseeq(optarg, "text")
1167 || strcaseeq(optarg, "ascii"))
1168 base = 256;
1169 else
1170 diagq("not a valid base", optarg);
1171
1172 if (c == SC_INBASE)
1173 msg.inbase = base;
1174 else
1175 msg.outbase = base;
1176 }
1177 continue;
1178
1179 /* List options */
1180
1181 case LST_UTC: /* --utc */
1182 msg.whack_utc = TRUE;
1183 continue;
1184
1185 case LST_ALGS: /* --listalgs */
1186 case LST_PUBKEYS: /* --listpubkeys */
1187 case LST_CERTS: /* --listcerts */
1188 case LST_CACERTS: /* --listcacerts */
1189 case LST_ACERTS: /* --listacerts */
1190 case LST_AACERTS: /* --listaacerts */
1191 case LST_OCSPCERTS: /* --listocspcerts */
1192 case LST_GROUPS: /* --listgroups */
1193 case LST_CAINFOS: /* --listcainfos */
1194 case LST_CRLS: /* --listcrls */
1195 case LST_OCSP: /* --listocsp */
1196 case LST_CARDS: /* --listcards */
1197 msg.whack_list |= LELEM(c - LST_ALGS);
1198 continue;
1199
1200 case LST_ALL: /* --listall */
1201 msg.whack_list = LIST_ALL;
1202 continue;
1203
1204 /* Connection Description options */
1205
1206 case END_HOST: /* --host <ip-address> */
1207 {
1208 lset_t new_policy = LEMPTY;
1209
1210 af_used_by = long_opts[long_index].name;
1211 diagq(anyaddr(msg.addr_family, &msg.right.host_addr), optarg);
1212 if (streq(optarg, "%any"))
1213 {
1214 }
1215 else if (streq(optarg, "%opportunistic"))
1216 {
1217 /* always use tunnel mode; mark as opportunistic */
1218 new_policy |= POLICY_TUNNEL | POLICY_OPPO;
1219 }
1220 else if (streq(optarg, "%group"))
1221 {
1222 /* always use tunnel mode; mark as group */
1223 new_policy |= POLICY_TUNNEL | POLICY_GROUP;
1224 }
1225 else if (streq(optarg, "%opportunisticgroup"))
1226 {
1227 /* always use tunnel mode; mark as opportunistic */
1228 new_policy |= POLICY_TUNNEL | POLICY_OPPO | POLICY_GROUP;
1229 }
1230 else
1231 {
1232 diagq(ttoaddr(optarg, 0, msg.addr_family
1233 , &msg.right.host_addr), optarg);
1234 }
1235
1236 msg.policy |= new_policy;
1237
1238 if (new_policy & (POLICY_OPPO | POLICY_GROUP))
1239 {
1240 if (!LHAS(end_seen, END_CLIENT - END_FIRST))
1241 {
1242 /* set host to 0.0.0 and --client to 0.0.0.0/0
1243 * or IPV6 equivalent
1244 */
1245 ip_address any;
1246
1247 tunnel_af_used_by = optarg;
1248 diagq(anyaddr(msg.tunnel_addr_family, &any), optarg);
1249 diagq(initsubnet(&any, 0, '0', &msg.right.client), optarg);
1250 }
1251 msg.right.has_client = TRUE;
1252 }
1253 if (new_policy & POLICY_GROUP)
1254 {
1255 /* client subnet must not be specified by user:
1256 * it will come from the group's file.
1257 */
1258 if (LHAS(end_seen, END_CLIENT - END_FIRST))
1259 diag("--host %group clashes with --client");
1260
1261 end_seen |= LELEM(END_CLIENT - END_FIRST);
1262 }
1263 if (new_policy & POLICY_OPPO)
1264 msg.right.key_from_DNS_on_demand = TRUE;
1265 continue;
1266 }
1267 case END_ID: /* --id <identity> */
1268 msg.right.id = optarg; /* decoded by Pluto */
1269 continue;
1270
1271 case END_CERT: /* --cert <path> */
1272 msg.right.cert = optarg; /* decoded by Pluto */
1273 continue;
1274
1275 case END_CA: /* --ca <distinguished name> */
1276 msg.right.ca = optarg; /* decoded by Pluto */
1277 continue;
1278
1279 case END_SENDCERT:
1280 if (streq(optarg, "yes") || streq(optarg, "always"))
1281 {
1282 msg.right.sendcert = CERT_ALWAYS_SEND;
1283 }
1284 else if (streq(optarg, "no") || streq(optarg, "never"))
1285 {
1286 msg.right.sendcert = CERT_NEVER_SEND;
1287 }
1288 else if (streq(optarg, "ifasked"))
1289 {
1290 msg.right.sendcert = CERT_SEND_IF_ASKED;
1291 }
1292 else
1293 {
1294 diagq("whack sendcert value is not legal", optarg);
1295 }
1296 continue;
1297
1298 case END_GROUPS:/* --groups <access control groups> */
1299 msg.right.groups = optarg; /* decoded by Pluto */
1300 continue;
1301
1302 case END_IKEPORT: /* --ikeport <port-number> */
1303 if (opt_whole<=0 || opt_whole >= 0x10000)
1304 diagq("<port-number> must be a number between 1 and 65535", optarg);
1305 msg.right.host_port = opt_whole;
1306 continue;
1307
1308 case END_NEXTHOP: /* --nexthop <ip-address> */
1309 af_used_by = long_opts[long_index].name;
1310 if (streq(optarg, "%direct"))
1311 diagq(anyaddr(msg.addr_family
1312 , &msg.right.host_nexthop), optarg);
1313 else
1314 diagq(ttoaddr(optarg, 0, msg.addr_family
1315 , &msg.right.host_nexthop), optarg);
1316 continue;
1317
1318 case END_SRCIP: /* --srcip <ip-address> */
1319 af_used_by = long_opts[long_index].name;
1320 if (streq(optarg, "%modeconfig") || streq(optarg, "%modecfg"))
1321 {
1322 msg.right.modecfg = TRUE;
1323 }
1324 else
1325 {
1326 diagq(ttoaddr(optarg, 0, msg.addr_family
1327 , &msg.right.host_srcip), optarg);
1328 msg.right.has_srcip = TRUE;
1329 }
1330 msg.policy |= POLICY_TUNNEL; /* srcip => tunnel */
1331 continue;
1332
1333 case END_CLIENT: /* --client <subnet> */
1334 if (end_seen & LELEM(END_CLIENTWITHIN - END_FIRST))
1335 diag("--client conflicts with --clientwithin");
1336 tunnel_af_used_by = long_opts[long_index].name;
1337 #ifdef VIRTUAL_IP
1338 if ((strlen(optarg) >= 6 && strncmp(optarg,"vhost:",6) == 0)
1339 || (strlen(optarg) >= 5 && strncmp(optarg,"vnet:",5) == 0))
1340 {
1341 msg.right.virt = optarg;
1342 }
1343 else
1344 {
1345 diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
1346 msg.right.has_client = TRUE;
1347 }
1348 #else
1349 diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
1350 msg.right.has_client = TRUE;
1351 #endif
1352 msg.policy |= POLICY_TUNNEL; /* client => tunnel */
1353 continue;
1354
1355 case END_CLIENTWITHIN: /* --clienwithin <address range> */
1356 if (end_seen & LELEM(END_CLIENT - END_FIRST))
1357 diag("--clientwithin conflicts with --client");
1358 tunnel_af_used_by = long_opts[long_index].name;
1359 diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
1360 msg.right.has_client = TRUE;
1361 msg.policy |= POLICY_TUNNEL; /* client => tunnel */
1362 msg.right.has_client_wildcard = TRUE;
1363 continue;
1364
1365 case END_CLIENTPROTOPORT: /* --clientprotoport <protocol>/<port> */
1366 diagq(ttoprotoport(optarg, 0, &msg.right.protocol, &msg.right.port
1367 , &msg.right.has_port_wildcard), optarg);
1368 continue;
1369
1370 case END_DNSKEYONDEMAND: /* --dnskeyondemand */
1371 msg.right.key_from_DNS_on_demand = TRUE;
1372 continue;
1373
1374 case END_HOSTACCESS: /* --hostaccess */
1375 msg.right.hostaccess = TRUE;
1376 continue;
1377
1378 case END_UPDOWN: /* --updown <updown> */
1379 msg.right.updown = optarg;
1380 continue;
1381
1382 case CD_TO: /* --to */
1383 /* process right end, move it to left, reset it */
1384 if (!LHAS(end_seen, END_HOST - END_FIRST))
1385 diag("connection missing --host before --to");
1386 msg.left = msg.right;
1387 clear_end(&msg.right);
1388 end_seen_before_to = end_seen;
1389 end_seen = LEMPTY;
1390 continue;
1391
1392 case CD_PSK: /* --psk */
1393 case CD_RSASIG: /* --rsasig */
1394 case CD_ENCRYPT: /* --encrypt */
1395 case CD_AUTHENTICATE: /* --authenticate */
1396 case CD_COMPRESS: /* --compress */
1397 case CD_TUNNEL: /* --tunnel */
1398 case CD_PFS: /* --pfs */
1399 case CD_DISABLEARRIVALCHECK: /* --disablearrivalcheck */
1400 case CD_DONT_REKEY: /* --donotrekey */
1401 msg.policy |= LELEM(c - CD_POLICY_FIRST);
1402 continue;
1403
1404 /* --initiateontraffic
1405 * --pass
1406 * --drop
1407 * --reject
1408 */
1409 case CD_SHUNT0:
1410 msg.policy = (msg.policy & ~POLICY_SHUNT_MASK)
1411 | ((lset_t)aux << POLICY_SHUNT_SHIFT);
1412 continue;
1413
1414 /* --failnone
1415 * --failpass
1416 * --faildrop
1417 * --failreject
1418 */
1419 case CD_FAIL0:
1420 msg.policy = (msg.policy & ~POLICY_FAIL_MASK)
1421 | ((lset_t)aux << POLICY_FAIL_SHIFT);
1422 continue;
1423
1424 case CD_IKELIFETIME: /* --ikelifetime <seconds> */
1425 msg.sa_ike_life_seconds = opt_whole;
1426 continue;
1427
1428 case CD_IPSECLIFETIME: /* --ipseclifetime <seconds> */
1429 msg.sa_ipsec_life_seconds = opt_whole;
1430 continue;
1431
1432 case CD_RKMARGIN: /* --rekeymargin <seconds> */
1433 msg.sa_rekey_margin = opt_whole;
1434 continue;
1435
1436 case CD_RKFUZZ: /* --rekeyfuzz <percentage> */
1437 msg.sa_rekey_fuzz = opt_whole;
1438 continue;
1439
1440 case CD_KTRIES: /* --keyingtries <count> */
1441 msg.sa_keying_tries = opt_whole;
1442 continue;
1443
1444 case CD_DPDACTION:
1445 if (streq(optarg, "none"))
1446 msg.dpd_action = DPD_ACTION_NONE;
1447 else if (streq(optarg, "clear"))
1448 msg.dpd_action = DPD_ACTION_CLEAR;
1449 else if (streq(optarg, "hold"))
1450 msg.dpd_action = DPD_ACTION_HOLD;
1451 else if (streq(optarg, "restart"))
1452 msg.dpd_action = DPD_ACTION_RESTART;
1453 else
1454 msg.dpd_action = DPD_ACTION_UNKNOWN;
1455 continue;
1456
1457 case CD_DPDDELAY:
1458 msg.dpd_delay = opt_whole;
1459 continue;
1460
1461 case CD_DPDTIMEOUT:
1462 msg.dpd_timeout = opt_whole;
1463 continue;
1464
1465 case CD_IKE: /* --ike <ike_alg1,ike_alg2,...> */
1466 msg.ike = optarg;
1467 continue;
1468
1469 case CD_PFSGROUP: /* --pfsgroup modpXXXX */
1470 msg.pfsgroup = optarg;
1471 continue;
1472
1473 case CD_ESP: /* --esp <esp_alg1,esp_alg2,...> */
1474 msg.esp = optarg;
1475 continue;
1476
1477 case CD_CONNIPV4:
1478 if (LHAS(cd_seen, CD_CONNIPV6 - CD_FIRST))
1479 diag("--ipv4 conflicts with --ipv6");
1480
1481 /* Since this is the default, the flag is redundant.
1482 * So we don't need to set msg.addr_family
1483 * and we don't need to check af_used_by
1484 * and we don't have to consider defaulting tunnel_addr_family.
1485 */
1486 continue;
1487
1488 case CD_CONNIPV6:
1489 if (LHAS(cd_seen, CD_CONNIPV4 - CD_FIRST))
1490 diag("--ipv6 conflicts with --ipv4");
1491
1492 if (af_used_by != NULL)
1493 diagq("--ipv6 must precede", af_used_by);
1494
1495 af_used_by = long_opts[long_index].name;
1496 msg.addr_family = AF_INET6;
1497
1498 /* Consider defaulting tunnel_addr_family to AF_INET6.
1499 * Do so only if it hasn't yet been specified or used.
1500 */
1501 if (LDISJOINT(cd_seen, LELEM(CD_TUNNELIPV4 - CD_FIRST) | LELEM(CD_TUNNELIPV6 - CD_FIRST))
1502 && tunnel_af_used_by == NULL)
1503 msg.tunnel_addr_family = AF_INET6;
1504 continue;
1505
1506 case CD_TUNNELIPV4:
1507 if (LHAS(cd_seen, CD_TUNNELIPV6 - CD_FIRST))
1508 diag("--tunnelipv4 conflicts with --tunnelipv6");
1509
1510 if (tunnel_af_used_by != NULL)
1511 diagq("--tunnelipv4 must precede", af_used_by);
1512
1513 msg.tunnel_addr_family = AF_INET;
1514 continue;
1515
1516 case CD_TUNNELIPV6:
1517 if (LHAS(cd_seen, CD_TUNNELIPV4 - CD_FIRST))
1518 diag("--tunnelipv6 conflicts with --tunnelipv4");
1519
1520 if (tunnel_af_used_by != NULL)
1521 diagq("--tunnelipv6 must precede", af_used_by);
1522
1523 msg.tunnel_addr_family = AF_INET6;
1524 continue;
1525
1526 case CA_NAME: /* --caname <name> */
1527 msg.name = optarg;
1528 msg.whack_ca = TRUE;
1529 continue;
1530 case CA_CERT: /* --cacert <path> */
1531 msg.cacert = optarg;
1532 continue;
1533 case CA_LDAPHOST: /* --ldaphost <hostname> */
1534 msg.ldaphost = optarg;
1535 continue;
1536 case CA_LDAPBASE: /* --ldapbase <base> */
1537 msg.ldapbase = optarg;
1538 continue;
1539 case CA_CRLURI: /* --crluri <uri> */
1540 msg.crluri = optarg;
1541 continue;
1542 case CA_CRLURI2: /* --crluri2 <uri> */
1543 msg.crluri2 = optarg;
1544 continue;
1545 case CA_OCSPURI: /* --ocspuri <uri> */
1546 msg.ocspuri = optarg;
1547 continue;
1548 case CA_STRICT: /* --strictcrlpolicy */
1549 msg.whack_strict = TRUE;
1550 continue;
1551
1552 #ifdef DEBUG
1553 case DBGOPT_NONE: /* --debug-none */
1554 msg.debugging = DBG_NONE;
1555 continue;
1556
1557 case DBGOPT_ALL: /* --debug-all */
1558 msg.debugging |= DBG_ALL; /* note: does not include PRIVATE */
1559 continue;
1560
1561 case DBGOPT_RAW: /* --debug-raw */
1562 case DBGOPT_CRYPT: /* --debug-crypt */
1563 case DBGOPT_PARSING: /* --debug-parsing */
1564 case DBGOPT_EMITTING: /* --debug-emitting */
1565 case DBGOPT_CONTROL: /* --debug-control */
1566 case DBGOPT_LIFECYCLE: /* --debug-lifecycle */
1567 case DBGOPT_KLIPS: /* --debug-klips */
1568 case DBGOPT_DNS: /* --debug-dns */
1569 case DBGOPT_NATT: /* --debug-natt */
1570 case DBGOPT_OPPO: /* --debug-oppo */
1571 case DBGOPT_CONTROLMORE: /* --debug-controlmore */
1572 case DBGOPT_PRIVATE: /* --debug-private */
1573 case DBGOPT_IMPAIR_DELAY_ADNS_KEY_ANSWER: /* --impair-delay-adns-key-answer */
1574 case DBGOPT_IMPAIR_DELAY_ADNS_TXT_ANSWER: /* --impair-delay-adns-txt-answer */
1575 case DBGOPT_IMPAIR_BUST_MI2: /* --impair_bust_mi2 */
1576 case DBGOPT_IMPAIR_BUST_MR2: /* --impair_bust_mr2 */
1577 msg.debugging |= LELEM(c-DBGOPT_RAW);
1578 continue;
1579 #endif
1580 default:
1581 assert(FALSE); /* unknown return value */
1582 }
1583 break;
1584 }
1585
1586 if (optind != argc)
1587 {
1588 /* If you see this message unexpectedly, perhaps the
1589 * case for the previous option ended with "break"
1590 * instead of "continue"
1591 */
1592 diagq("unexpected argument", argv[optind]);
1593 }
1594
1595 /* For each possible form of the command, figure out if an argument
1596 * suggests whether that form was intended, and if so, whether all
1597 * required information was supplied.
1598 */
1599
1600 /* check opportunistic initiation simulation request */
1601 switch (opts_seen & (LELEM(OPT_OPPO_HERE) | LELEM(OPT_OPPO_THERE)))
1602 {
1603 case LELEM(OPT_OPPO_HERE):
1604 case LELEM(OPT_OPPO_THERE):
1605 diag("--oppohere and --oppothere must be used together");
1606 /*NOTREACHED*/
1607 case LELEM(OPT_OPPO_HERE) | LELEM(OPT_OPPO_THERE):
1608 msg.whack_oppo_initiate = TRUE;
1609 if (LIN(cd_seen, LELEM(CD_TUNNELIPV4 - CD_FIRST) | LELEM(CD_TUNNELIPV6 - CD_FIRST)))
1610 opts_seen &= ~LELEM(OPT_CD);
1611 break;
1612 }
1613
1614 /* check connection description */
1615 if (LHAS(opts_seen, OPT_CD))
1616 {
1617 if (!LHAS(cd_seen, CD_TO-CD_FIRST))
1618 diag("connection description option, but no --to");
1619
1620 if (!LHAS(end_seen, END_HOST-END_FIRST))
1621 diag("connection missing --host after --to");
1622
1623 if (isanyaddr(&msg.left.host_addr)
1624 && isanyaddr(&msg.right.host_addr))
1625 diag("hosts cannot both be 0.0.0.0 or 0::0");
1626
1627 if (msg.policy & POLICY_OPPO)
1628 {
1629 if ((msg.policy & (POLICY_PSK | POLICY_RSASIG)) != POLICY_RSASIG)
1630 diag("only RSASIG is supported for opportunism");
1631 if ((msg.policy & POLICY_PFS) == 0)
1632 diag("PFS required for opportunism");
1633 if ((msg.policy & POLICY_ENCRYPT) == 0)
1634 diag("encryption required for opportunism");
1635 }
1636
1637 check_end(&msg.left, &msg.right, !LHAS(end_seen_before_to, END_NEXTHOP-END_FIRST)
1638 , msg.addr_family, msg.tunnel_addr_family);
1639
1640 check_end(&msg.right, &msg.left, !LHAS(end_seen, END_NEXTHOP-END_FIRST)
1641 , msg.addr_family, msg.tunnel_addr_family);
1642
1643 if (subnettypeof(&msg.left.client) != subnettypeof(&msg.right.client))
1644 diag("endpoints clash: one is IPv4 and the other is IPv6");
1645
1646 if (NEVER_NEGOTIATE(msg.policy))
1647 {
1648 /* we think this is just a shunt (because he didn't specify
1649 * a host authentication method). If he didn't specify a
1650 * shunt type, he's probably gotten it wrong.
1651 */
1652 if ((msg.policy & POLICY_SHUNT_MASK) == POLICY_SHUNT_TRAP)
1653 diag("non-shunt connection must have --psk or --rsasig or both");
1654 }
1655 else
1656 {
1657 /* not just a shunt: a real ipsec connection */
1658 if ((msg.policy & POLICY_ID_AUTH_MASK) == LEMPTY)
1659 diag("must specify --rsasig or --psk for a connection");
1660
1661 if (!HAS_IPSEC_POLICY(msg.policy)
1662 && (msg.left.has_client || msg.right.has_client))
1663 diag("must not specify clients for ISAKMP-only connection");
1664 }
1665
1666 msg.whack_connection = TRUE;
1667 }
1668
1669 /* decide whether --name is mandatory or forbidden */
1670 if (!LDISJOINT(opts_seen
1671 , LELEM(OPT_ROUTE) | LELEM(OPT_UNROUTE)
1672 | LELEM(OPT_INITIATE) | LELEM(OPT_TERMINATE)
1673 | LELEM(OPT_DELETE) | LELEM(OPT_CD)))
1674 {
1675 if (!LHAS(opts_seen, OPT_NAME) && !msg.whack_ca)
1676 diag("missing --name <connection_name>");
1677 }
1678 else if (!msg.whack_options && !msg.whack_status)
1679 {
1680 if (LHAS(opts_seen, OPT_NAME))
1681 diag("no reason for --name");
1682 }
1683
1684 if (!LDISJOINT(opts_seen, LELEM(OPT_PUBKEYRSA) | LELEM(OPT_ADDKEY)))
1685 {
1686 if (!LHAS(opts_seen, OPT_KEYID))
1687 diag("--addkey and --pubkeyrsa require --keyid");
1688 }
1689
1690 if (!(msg.whack_connection || msg.whack_key || msg.whack_myid
1691 || msg.whack_delete || msg.whack_deletestate
1692 || msg.whack_initiate || msg.whack_oppo_initiate || msg.whack_terminate
1693 || msg.whack_route || msg.whack_unroute || msg.whack_listen
1694 || msg.whack_unlisten || msg.whack_list || msg.whack_purgeocsp || msg.whack_reread
1695 || msg.whack_ca || msg.whack_status || msg.whack_options || msg.whack_shutdown
1696 || msg.whack_sc_op))
1697 {
1698 diag("no action specified; try --help for hints");
1699 }
1700
1701 update_ports(&msg);
1702
1703 /* tricky quick and dirty check for wild values */
1704 if (msg.sa_rekey_margin != 0
1705 && msg.sa_rekey_fuzz * msg.sa_rekey_margin * 4 / msg.sa_rekey_margin / 4
1706 != msg.sa_rekey_fuzz)
1707 diag("rekeymargin or rekeyfuzz values are so large that they cause oveflow");
1708
1709 check_life_time (msg.sa_ike_life_seconds, OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM
1710 , "ikelifetime", &msg);
1711
1712 check_life_time(msg.sa_ipsec_life_seconds, SA_LIFE_DURATION_MAXIMUM
1713 , "ipseclifetime", &msg);
1714
1715 if (msg.dpd_action == DPD_ACTION_UNKNOWN)
1716 diag("dpdaction must be \"none\", \"clear\", \"hold\" or \"restart\"");
1717
1718 if (msg.dpd_action != DPD_ACTION_NONE)
1719 {
1720 if (msg.dpd_delay <= 0)
1721 diag("dpddelay must be larger than zero");
1722
1723 if (msg.dpd_timeout <= 0)
1724 diag("dpdtimeout must be larger than zero");
1725
1726 if (msg.dpd_timeout <= msg.dpd_delay)
1727 diag("dpdtimeout must be larger than dpddelay");
1728 }
1729
1730 /* pack strings for inclusion in message */
1731 next_str = msg.string;
1732 str_roof = &msg.string[sizeof(msg.string)];
1733
1734 /* build esp message as esp="<esp>;<pfsgroup>" */
1735 if (msg.pfsgroup) {
1736 snprintf(esp_buf, sizeof (esp_buf), "%s;%s",
1737 msg.esp ? msg.esp : "",
1738 msg.pfsgroup ? msg.pfsgroup : "");
1739 msg.esp=esp_buf;
1740 }
1741 if (!pack_str(&msg.name) /* string 1 */
1742 || !pack_str(&msg.left.id) /* string 2 */
1743 || !pack_str(&msg.left.cert) /* string 3 */
1744 || !pack_str(&msg.left.ca) /* string 4 */
1745 || !pack_str(&msg.left.groups) /* string 5 */
1746 || !pack_str(&msg.left.updown) /* string 6 */
1747 #ifdef VIRTUAL_IP
1748 || !pack_str(&msg.left.virt)
1749 #endif
1750 || !pack_str(&msg.right.id) /* string 7 */
1751 || !pack_str(&msg.right.cert) /* string 8 */
1752 || !pack_str(&msg.right.ca) /* string 9 */
1753 || !pack_str(&msg.right.groups) /* string 10 */
1754 || !pack_str(&msg.right.updown) /* string 11 */
1755 #ifdef VIRTUAL_IP
1756 || !pack_str(&msg.right.virt)
1757 #endif
1758 || !pack_str(&msg.keyid) /* string 12 */
1759 || !pack_str(&msg.myid) /* string 13 */
1760 || !pack_str(&msg.cacert) /* string 14 */
1761 || !pack_str(&msg.ldaphost) /* string 15 */
1762 || !pack_str(&msg.ldapbase) /* string 16 */
1763 || !pack_str(&msg.crluri) /* string 17 */
1764 || !pack_str(&msg.crluri2) /* string 18 */
1765 || !pack_str(&msg.ocspuri) /* string 19 */
1766 || !pack_str(&msg.ike) /* string 20 */
1767 || !pack_str(&msg.esp) /* string 21 */
1768 || !pack_str(&msg.sc_data) /* string 22 */
1769 || str_roof - next_str < (ptrdiff_t)msg.keyval.len) /* chunk (sort of string 5) */
1770 diag("too many bytes of strings to fit in message to pluto");
1771
1772 memcpy(next_str, msg.keyval.ptr, msg.keyval.len);
1773 msg.keyval.ptr = NULL;
1774 next_str += msg.keyval.len;
1775
1776 msg.magic = ((opts_seen & ~LELEM(OPT_SHUTDOWN))
1777 | sc_seen | lst_seen | cd_seen | ca_seen) != LEMPTY
1778 || msg.whack_options
1779 ? WHACK_MAGIC : WHACK_BASIC_MAGIC;
1780
1781 /* send message to Pluto */
1782 if (access(ctl_addr.sun_path, R_OK | W_OK) < 0)
1783 {
1784 int e = errno;
1785
1786 switch (e)
1787 {
1788 case EACCES:
1789 fprintf(stderr, "whack: no right to communicate with pluto (access(\"%s\"))\n"
1790 , ctl_addr.sun_path);
1791 break;
1792 case ENOENT:
1793 fprintf(stderr, "whack: Pluto is not running (no \"%s\")\n"
1794 , ctl_addr.sun_path);
1795 break;
1796 default:
1797 fprintf(stderr, "whack: access(\"%s\") failed with %d %s\n"
1798 , ctl_addr.sun_path, errno, strerror(e));
1799 break;
1800 }
1801 exit(RC_WHACK_PROBLEM);
1802 }
1803 else
1804 {
1805 int sock = socket(AF_UNIX, SOCK_STREAM, 0);
1806 int exit_status = 0;
1807 ssize_t len = next_str - (char *)&msg;
1808
1809 if (sock == -1)
1810 {
1811 int e = errno;
1812
1813 fprintf(stderr, "whack: socket() failed (%d %s)\n", e, strerror(e));
1814 exit(RC_WHACK_PROBLEM);
1815 }
1816
1817 if (connect(sock, (struct sockaddr *)&ctl_addr
1818 , offsetof(struct sockaddr_un, sun_path) + strlen(ctl_addr.sun_path)) < 0)
1819 {
1820 int e = errno;
1821
1822 fprintf(stderr, "whack:%s connect() for \"%s\" failed (%d %s)\n"
1823 , e == ECONNREFUSED? " is Pluto running? " : ""
1824 , ctl_addr.sun_path, e, strerror(e));
1825 exit(RC_WHACK_PROBLEM);
1826 }
1827
1828 if (write(sock, &msg, len) != len)
1829 {
1830 int e = errno;
1831
1832 fprintf(stderr, "whack: write() failed (%d %s)\n", e, strerror(e));
1833 exit(RC_WHACK_PROBLEM);
1834 }
1835
1836 /* for now, just copy reply back to stdout */
1837
1838 {
1839 char buf[4097]; /* arbitrary limit on log line length */
1840 char *be = buf;
1841
1842 for (;;)
1843 {
1844 char *ls = buf;
1845 ssize_t rl = read(sock, be, (buf + sizeof(buf)-1) - be);
1846
1847 if (rl < 0)
1848 {
1849 int e = errno;
1850
1851 fprintf(stderr, "whack: read() failed (%d %s)\n", e, strerror(e));
1852 exit(RC_WHACK_PROBLEM);
1853 }
1854 if (rl == 0)
1855 {
1856 if (be != buf)
1857 fprintf(stderr, "whack: last line from pluto too long or unterminated\n");
1858 break;
1859 }
1860
1861 be += rl;
1862 *be = '\0';
1863
1864 for (;;)
1865 {
1866 char *le = strchr(ls, '\n');
1867
1868 if (le == NULL)
1869 {
1870 /* move last, partial line to start of buffer */
1871 memmove(buf, ls, be-ls);
1872 be -= ls - buf;
1873 break;
1874 }
1875
1876 le++; /* include NL in line */
1877 write(1, ls, le - ls);
1878
1879 /* figure out prefix number
1880 * and how it should affect our exit status
1881 */
1882 {
1883 unsigned long s = strtoul(ls, NULL, 10);
1884
1885 switch (s)
1886 {
1887 case RC_COMMENT:
1888 case RC_LOG:
1889 /* ignore */
1890 break;
1891 case RC_SUCCESS:
1892 /* be happy */
1893 exit_status = 0;
1894 break;
1895 case RC_ENTERSECRET:
1896 get_secret(sock);
1897 break;
1898 /* case RC_LOG_SERIOUS: */
1899 default:
1900 /* pass through */
1901 exit_status = s;
1902 break;
1903 }
1904 }
1905 ls = le;
1906 }
1907 }
1908 }
1909 return exit_status;
1910 }
1911 }