]> git.saurik.com Git - apple/ipsec.git/blob - ipsec-tools/racoon/session.c
11915b2133ea467fa3daf74ced598336ae89d948
[apple/ipsec.git] / ipsec-tools / racoon / session.c
1 /* $NetBSD: session.c,v 1.7.6.2 2007/08/01 11:52:22 vanhu Exp $ */
2
3 /* $KAME: session.c,v 1.32 2003/09/24 02:01:17 jinmei Exp $ */
4
5 /*
6 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the project nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include "config.h"
35
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/time.h>
39 #include <sys/socket.h>
40 #if HAVE_SYS_WAIT_H
41 # include <sys/wait.h>
42 #endif
43 #ifndef WEXITSTATUS
44 # define WEXITSTATUS(s) ((unsigned)(s) >> 8)
45 #endif
46 #ifndef WIFEXITED
47 # define WIFEXITED(s) (((s) & 255) == 0)
48 #endif
49
50 #ifndef HAVE_NETINET6_IPSEC
51 #include <netinet/ipsec.h>
52 #else
53 #include <netinet6/ipsec.h>
54 #endif
55
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <errno.h>
60 #ifdef HAVE_UNISTD_H
61 #include <unistd.h>
62 #endif
63 #include <signal.h>
64 #include <sys/stat.h>
65 #include <paths.h>
66
67 #include <netinet/in.h>
68 #include <netinet/ip.h>
69 #include <netinet/ip_icmp.h>
70
71 #include <resolv.h>
72 #include <TargetConditionals.h>
73 #include <vproc_priv.h>
74 #include <dispatch/dispatch.h>
75 #include <xpc/xpc.h>
76
77 #include "libpfkey.h"
78
79 #include "var.h"
80 #include "misc.h"
81 #include "vmbuf.h"
82 #include "plog.h"
83 #include "debug.h"
84 #include "plog.h"
85
86 #include "schedule.h"
87 #include "session.h"
88 #include "grabmyaddr.h"
89 #include "cfparse_proto.h"
90 #include "isakmp_var.h"
91 #include "isakmp_xauth.h"
92 #include "isakmp_cfg.h"
93 #include "oakley.h"
94 #include "pfkey.h"
95 #include "handler.h"
96 #include "localconf.h"
97 #include "remoteconf.h"
98 #ifdef ENABLE_NATT
99 #include "nattraversal.h"
100 #endif
101 #include "vpn_control_var.h"
102 #include "policy.h"
103 #include "algorithm.h" /* XXX ??? */
104
105 #include "sainfo.h"
106 #include "power_mgmt.h"
107
108 #include <NetworkExtension/NEPolicy.h>
109 #include <sys/proc_info.h>
110 #include <libproc.h>
111
112
113 extern pid_t racoon_pid;
114 extern int launchdlaunched;
115 static void close_session (int);
116 static int init_signal (void);
117 static int set_signal (int sig, RETSIGTYPE (*func) (int, siginfo_t *, void *));
118 static void check_sigreq (void);
119 static void check_flushsa_stub (void *);
120 static void check_flushsa (void);
121 static void auto_exit_do (void *);
122 static int close_sockets (void);
123
124 static volatile sig_atomic_t sigreq[NSIG + 1];
125 int terminated = 0;
126
127 static int64_t racoon_keepalive = -1;
128
129 dispatch_queue_t main_queue;
130
131 static NEPolicySessionRef policySession = NULL;
132
133 /*
134 * This is used to (manually) update racoon's launchd keepalive, which is needed because racoon is (mostly)
135 * launched on demand and for <rdar://problem/8768510> requires a keepalive on dirty/failure exits.
136 * The launchd plist can't be used for this because RunOnLoad is required to have keepalive on a failure exit.
137 */
138 int64_t
139 launchd_update_racoon_keepalive (Boolean enabled)
140 {
141 if (launchdlaunched) {
142 int64_t val = (__typeof__(val))enabled;
143 /* Set our own KEEPALIVE value */
144 if (vproc_swap_integer(NULL,
145 VPROC_GSK_BASIC_KEEPALIVE,
146 &val,
147 &racoon_keepalive)) {
148 plog(ASL_LEVEL_ERR,
149 "failed to swap launchd keepalive integer %d\n", enabled);
150 }
151 }
152 return racoon_keepalive;
153 }
154
155 static CFUUIDRef
156 copy_racoon_proc_uuid(void)
157 {
158 struct proc_uniqidentifierinfo procu;
159 CFUUIDBytes uuidBytes;
160 int size = 0;
161
162 memset(&procu, 0, sizeof(procu));
163 size = proc_pidinfo(getpid(), PROC_PIDUNIQIDENTIFIERINFO, 1, &procu, PROC_PIDUNIQIDENTIFIERINFO_SIZE);
164 if (size != PROC_PIDUNIQIDENTIFIERINFO_SIZE) {
165 return (NULL);
166 }
167
168 memcpy(&uuidBytes, procu.p_uuid, sizeof(CFUUIDBytes));
169 return CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, uuidBytes);
170 }
171
172 static bool
173 policy_session_init(void)
174 {
175 bool success = true;
176 policySession = NEPolicyCreateSession(kCFAllocatorDefault, CFSTR("racoon"), NULL, NULL);
177 if (policySession == NULL) {
178 return false;
179 }
180
181 CFUUIDRef proc_uuid = copy_racoon_proc_uuid();
182 if (proc_uuid == NULL) {
183 return false;
184 }
185
186 CFMutableArrayRef conditions = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
187 if (conditions) {
188 CFMutableDictionaryRef uuidCondition = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
189 if (uuidCondition) {
190 CFDictionarySetValue(uuidCondition, kNEPolicyConditionType, kNEPolicyValPolicyConditionTypeApplication);
191 CFDictionarySetValue(uuidCondition, kNEPolicyApplicationUUID, proc_uuid);
192 CFArrayAppendValue(conditions, uuidCondition);
193 CFRelease(uuidCondition);
194 } else {
195 success = false;
196 }
197
198 CFMutableDictionaryRef interfacesCondition = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
199 if (interfacesCondition) {
200 CFDictionarySetValue(interfacesCondition, kNEPolicyConditionType, kNEPolicyValPolicyConditionTypeAllInterfaces);
201 CFArrayAppendValue(conditions, interfacesCondition);
202 CFRelease(interfacesCondition);
203 } else {
204 success = false;
205 }
206 } else {
207 success = false;
208 }
209
210 CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
211 if (result) {
212 CFDictionaryAddValue(result, kNEPolicyResult, kNEPolicyValPolicyResultPass);
213 } else {
214 success = false;
215 }
216
217 if (success) {
218 success = (NEPolicyAdd(policySession, 0, conditions, result, NULL) != kNEPolicyIDInvalid);
219 }
220
221 if (result) {
222 CFRelease(result);
223 }
224 if (conditions) {
225 CFRelease(conditions);
226 }
227 if (proc_uuid) {
228 CFRelease(proc_uuid);
229 }
230
231 return (success && NEPolicyApply(policySession));
232 }
233
234 //
235 // Session
236 //
237 // Initialize listening sockets, timers, vpn control etc.,
238 // write the PID file and call dispatch_main.
239 //
240 void
241 session(void)
242 {
243 char pid_file[MAXPATHLEN];
244 FILE *fp;
245 int i;
246
247 main_queue = dispatch_get_main_queue();
248
249 /* initialize schedular */
250 sched_init();
251
252 /* needs to be called after schedular */
253 if (init_power_mgmt() < 0) {
254 plog(ASL_LEVEL_ERR,
255 "failed to initialize power-mgmt.");
256 exit(1);
257 }
258
259 if (lcconf->autograbaddr == 1)
260 if (pfroute_init()) {
261 plog(ASL_LEVEL_ERR, "failed to initialize route socket.\n");
262 exit(1);
263 }
264
265 if (!policy_session_init()) {
266 plog(ASL_LEVEL_ERR, "failed to initialize NEPolicy session.\n");
267 }
268
269 if (initmyaddr()) {
270 plog(ASL_LEVEL_ERR, "failed to initialize listening addresses.\n");
271 exit(1);
272 }
273 if (isakmp_init()) {
274 plog(ASL_LEVEL_ERR, "failed to initialize isakmp");
275 exit(1);
276 }
277 #ifdef ENABLE_VPNCONTROL_PORT
278 if (vpncontrol_init()) {
279 plog(ASL_LEVEL_ERR, "failed to initialize vpn control port");
280 //exit(1);
281 }
282 #endif
283
284 if (init_signal()) {
285 plog(ASL_LEVEL_ERR, "failed to initialize signals.\n");
286 exit(1);
287 }
288
289 for (i = 0; i <= NSIG; i++)
290 sigreq[i] = 0;
291
292 /* write .pid file */
293 if (!f_foreground) {
294 racoon_pid = getpid();
295 if (lcconf->pathinfo[LC_PATHTYPE_PIDFILE] == NULL)
296 strlcpy(pid_file, _PATH_VARRUN "racoon.pid", sizeof(pid_file));
297 else if (lcconf->pathinfo[LC_PATHTYPE_PIDFILE][0] == '/')
298 strlcpy(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], sizeof(pid_file));
299 else {
300 strlcat(pid_file, _PATH_VARRUN, sizeof(pid_file));
301 strlcat(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], sizeof(pid_file));
302 }
303 fp = fopen(pid_file, "w");
304 if (fp) {
305 if (fchmod(fileno(fp),
306 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
307 plog(ASL_LEVEL_ERR, "%s", strerror(errno));
308 fclose(fp);
309 exit(1);
310 }
311 fprintf(fp, "%ld\n", (long)racoon_pid);
312 fclose(fp);
313 } else {
314 plog(ASL_LEVEL_ERR,
315 "cannot open %s", pid_file);
316 }
317 }
318
319 xpc_transaction_begin();
320
321 #if !TARGET_OS_EMBEDDED
322 // enable keepalive for recovery (from crashes and bad exits... after init)
323 (void)launchd_update_racoon_keepalive(true);
324 #endif // !TARGET_OS_EMBEDDED
325
326 // Off to the races!
327 if (!terminated) {
328 dispatch_main();
329 }
330
331 exit(1); // should not be reached!!!
332 }
333
334
335 /* clear all status and exit program. */
336 static void
337 close_session(int error)
338 {
339 sched_killall();
340 cleanup_power_mgmt();
341 if ( terminated )
342 ike_session_flush_all_phase2(false);
343 ike_session_flush_all_phase1(false);
344 close_sockets();
345
346 xpc_transaction_end();
347
348 #if !TARGET_OS_EMBEDDED
349 // a clean exit, so disable launchd keepalive
350 (void)launchd_update_racoon_keepalive(false);
351 #endif // !TARGET_OS_EMBEDDED
352
353 plog(ASL_LEVEL_INFO, "racoon shutdown\n");
354 exit(0);
355 }
356
357
358 /*
359 * waiting the termination of processing until sending DELETE message
360 * for all inbound SA will complete.
361 */
362 static void
363 check_flushsa_stub(p)
364 void *p;
365 {
366 check_flushsa();
367 }
368
369 static void
370 check_flushsa()
371 {
372 vchar_t *buf;
373 struct sadb_msg *msg, *end, *next;
374 struct sadb_sa *sa;
375 caddr_t mhp[SADB_EXT_MAX + 1];
376 int n;
377
378 buf = pfkey_dump_sadb(SADB_SATYPE_UNSPEC);
379 if (buf == NULL) {
380 plog(ASL_LEVEL_DEBUG,
381 "pfkey_dump_sadb: returned nothing.\n");
382 return;
383 }
384
385 msg = ALIGNED_CAST(struct sadb_msg *)buf->v;
386 end = ALIGNED_CAST(struct sadb_msg *)(buf->v + buf->l);
387
388 /* counting SA except of dead one. */
389 n = 0;
390 while (msg < end) {
391 if (PFKEY_UNUNIT64(msg->sadb_msg_len) < sizeof(*msg))
392 break;
393 next = ALIGNED_CAST(struct sadb_msg *)((caddr_t)msg + PFKEY_UNUNIT64(msg->sadb_msg_len)); // Wcast-align fix (void*) - aligned buffer + multiple of 64
394 if (msg->sadb_msg_type != SADB_DUMP) {
395 msg = next;
396 continue;
397 }
398
399 if (pfkey_align(msg, mhp) || pfkey_check(mhp)) {
400 plog(ASL_LEVEL_ERR,
401 "pfkey_check (%s)\n", ipsec_strerror());
402 msg = next;
403 continue;
404 }
405
406 sa = ALIGNED_CAST(struct sadb_sa *)(mhp[SADB_EXT_SA]); // Wcast-align fix (void*) - mhp contains pointers to aligned structs
407 if (!sa) {
408 msg = next;
409 continue;
410 }
411
412 if (sa->sadb_sa_state != SADB_SASTATE_DEAD) {
413 n++;
414 msg = next;
415 continue;
416 }
417
418 msg = next;
419 }
420
421 if (buf != NULL)
422 vfree(buf);
423
424 if (n) {
425 sched_new(1, check_flushsa_stub, NULL);
426 return;
427 }
428
429 #if !TARGET_OS_EMBEDDED
430 if (lcconf->vt)
431 vproc_transaction_end(NULL, lcconf->vt);
432 #endif
433 close_session(0);
434 }
435
436 void
437 auto_exit_do(void *p)
438 {
439 plog(ASL_LEVEL_DEBUG,
440 "performing auto exit\n");
441 #if ENABLE_NO_SA_FLUSH
442 close_session(0);
443 #else
444 pfkey_send_flush(lcconf->sock_pfkey, SADB_SATYPE_UNSPEC);
445 sched_new(1, check_flushsa_stub, NULL);
446 dying();
447 #endif /* ENABLE_NO_SA_FLUSH */
448 }
449
450 void
451 check_auto_exit(void)
452 {
453 if (lcconf->auto_exit_sched) { /* exit scheduled? */
454 if (lcconf->auto_exit_state != LC_AUTOEXITSTATE_ENABLED
455 || vpn_control_connected() /* vpn control connected */
456 || policies_installed() /* policies installed in kernel */
457 || !no_remote_configs(FALSE)) { /* remote or anonymous configs */
458 SCHED_KILL(lcconf->auto_exit_sched);
459 }
460 } else { /* exit not scheduled */
461 if (lcconf->auto_exit_state == LC_AUTOEXITSTATE_ENABLED
462 && !vpn_control_connected()
463 && !policies_installed()
464 && no_remote_configs(FALSE)) {
465 if (lcconf->auto_exit_delay == 0) {
466 auto_exit_do(NULL); /* immediate exit */
467 } else {
468 lcconf->auto_exit_sched = sched_new(lcconf->auto_exit_delay, auto_exit_do, NULL);
469 }
470 }
471 }
472 }
473
474 static int signals[] = {
475 SIGHUP,
476 SIGINT,
477 SIGTERM,
478 SIGUSR1,
479 SIGUSR2,
480 SIGPIPE,
481 0
482 };
483
484
485 static void
486 check_sigreq()
487 {
488 int sig;
489
490 /*
491 * XXX We are not able to tell if we got
492 * several time the same signal. This is
493 * not a problem for the current code,
494 * but we shall remember this limitation.
495 */
496 for (sig = 0; sig <= NSIG; sig++) {
497 if (sigreq[sig] == 0)
498 continue;
499
500 sigreq[sig]--;
501 switch(sig) {
502 case 0:
503 return;
504
505 /* Catch up childs, mainly scripts.
506 */
507
508 case SIGUSR1:
509 case SIGHUP:
510 #ifdef ENABLE_HYBRID
511 if ((isakmp_cfg_init(ISAKMP_CFG_INIT_WARM)) != 0) {
512 plog(ASL_LEVEL_ERR,
513 "ISAKMP mode config structure reset failed, "
514 "not reloading\n");
515 return;
516 }
517 #endif
518 if ( terminated )
519 break;
520
521 /*
522 * if we got a HUP... try graceful teardown of sessions before we close and reopen sockets...
523 * so that info-deletes notifications can make it to the peer.
524 */
525 if (sig == SIGHUP) {
526 ike_session_flush_all_phase2(true);
527 ike_session_flush_all_phase1(true);
528 }
529
530 /* Save old configuration, load new one... */
531 if (cfreparse(sig)) {
532 plog(ASL_LEVEL_ERR,
533 "configuration read failed\n");
534 exit(1);
535 }
536 if (lcconf->logfile_param == NULL && logFileStr[0] == 0)
537 plogresetfile(lcconf->pathinfo[LC_PATHTYPE_LOGFILE]);
538
539 #if TARGET_OS_EMBEDDED
540 if (no_remote_configs(TRUE)) {
541 #if ENABLE_NO_SA_FLUSH
542 close_session(0);
543 #else
544 pfkey_send_flush(lcconf->sock_pfkey, SADB_SATYPE_UNSPEC);
545 #ifdef ENABLE_FASTQUIT
546 close_session(0);
547 #else
548 sched_new(1, check_flushsa_stub, NULL);
549 #endif /* ENABLE_FASTQUIT */
550 dying();
551 #endif /* ENABLE_NO_SA_FLUSH */
552 }
553 #endif
554
555 break;
556
557 case SIGINT:
558 case SIGTERM:
559 plog(ASL_LEVEL_INFO,
560 "caught signal %d\n", sig);
561 #if ENABLE_NO_SA_FLUSH
562 close_session(0);
563 #else
564 pfkey_send_flush(lcconf->sock_pfkey,
565 SADB_SATYPE_UNSPEC);
566 if ( sig == SIGTERM ){
567 terminated = 1; /* in case if it hasn't been set yet */
568 close_session(0);
569 }
570 else
571 sched_new(1, check_flushsa_stub, NULL);
572
573 dying();
574 #endif /* ENABLE_NO_SA_FLUSH */
575 break;
576
577 default:
578 plog(ASL_LEVEL_INFO,
579 "caught signal %d\n", sig);
580 break;
581 }
582 }
583 }
584
585
586 /*
587 * asynchronous requests will actually dispatched in the
588 * main loop in session().
589 */
590 RETSIGTYPE
591 signal_handler(int sig, siginfo_t *sigi, void *ctx)
592 {
593 #if 0
594 plog(ASL_LEVEL_DEBUG,
595 "%s received signal %d from pid %d uid %d\n\n",
596 __FUNCTION__, sig, sigi->si_pid, sigi->si_uid);
597 #endif
598
599 /* Do not just set it to 1, because we may miss some signals by just setting
600 * values to 0/1
601 */
602 sigreq[sig]++;
603 if ( sig == SIGTERM ){
604 terminated = 1;
605 }
606 dispatch_async(main_queue,
607 ^{
608 check_sigreq();
609 });
610 }
611
612
613 static int
614 init_signal()
615 {
616 int i;
617
618 for (i = 0; signals[i] != 0; i++) {
619 if (set_signal(signals[i], signal_handler) < 0) {
620 plog(ASL_LEVEL_ERR,
621 "failed to set_signal (%s)\n",
622 strerror(errno));
623 return (1);
624 }
625 }
626 return 0;
627 }
628
629 static int
630 set_signal(int sig, RETSIGTYPE (*func) (int, siginfo_t *, void *))
631 {
632 struct sigaction sa;
633
634 memset((caddr_t)&sa, 0, sizeof(sa));
635 sa.sa_sigaction = func;
636 sa.sa_flags = SA_RESTART | SA_SIGINFO;
637
638 if (sigemptyset(&sa.sa_mask) < 0)
639 return -1;
640
641 if (sigaction(sig, &sa, (struct sigaction *)0) < 0)
642 return(-1);
643
644 return 0;
645 }
646
647 void
648 fatal_error(int error)
649 {
650 close_session(error == 0 ? -1 : error);
651 }
652
653 /* suspend all socket sources except pf_key */
654 void
655 dying(void)
656 {
657 if (lcconf->rt_source)
658 dispatch_suspend(lcconf->rt_source);
659 if (lcconf->vpncontrol_source)
660 dispatch_suspend(lcconf->vpncontrol_source);
661 isakmp_suspend_sockets();
662 }
663
664 static int
665 close_sockets()
666 {
667 pfroute_close();
668 isakmp_close();
669 pfkey_close();
670 #ifdef ENABLE_VPNCONTROL_PORT
671 vpncontrol_close();
672 #endif
673
674 return 0;
675 }
676
677