]> git.saurik.com Git - apple/ipsec.git/blob - ipsec-tools/racoon/session.c
ipsec-326.81.1.tar.gz
[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 <TargetConditionals.h>
72 #include <vproc_priv.h>
73 #include <dispatch/dispatch.h>
74 #include <xpc/xpc.h>
75 #include <os/transaction_private.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 #define IKEv1_TRANSACTION "IKEv1_Transaction"
114
115 extern pid_t racoon_pid;
116 extern int launchdlaunched;
117 static void close_session (int);
118 static int init_signal (void);
119 static int set_signal (int sig, RETSIGTYPE (*func) (int, siginfo_t *, void *));
120 static void check_flushsa_stub (void *);
121 static void check_flushsa (void);
122 static void auto_exit_do (void *);
123 static int close_sockets (void);
124
125 static volatile sig_atomic_t sigreq[NSIG + 1];
126 int terminated = 0;
127 int pending_signal_handle = 0;
128
129 static int64_t racoon_keepalive = -1;
130
131 dispatch_queue_t main_queue;
132
133 static NEPolicySessionRef policySession = NULL;
134
135 static os_transaction_t g_ikev1_transaction = NULL;
136
137 /*
138 * This is used to (manually) update racoon's launchd keepalive, which is needed because racoon is (mostly)
139 * launched on demand and for <rdar://problem/8768510> requires a keepalive on dirty/failure exits.
140 * The launchd plist can't be used for this because RunOnLoad is required to have keepalive on a failure exit.
141 */
142 int64_t
143 launchd_update_racoon_keepalive (Boolean enabled)
144 {
145 if (launchdlaunched) {
146 int64_t val = (__typeof__(val))enabled;
147 /* Set our own KEEPALIVE value */
148 if (vproc_swap_integer(NULL,
149 VPROC_GSK_BASIC_KEEPALIVE,
150 &val,
151 &racoon_keepalive)) {
152 plog(ASL_LEVEL_ERR,
153 "failed to swap launchd keepalive integer %d\n", enabled);
154 }
155 }
156 return racoon_keepalive;
157 }
158
159 static CFUUIDRef
160 copy_racoon_proc_uuid(void)
161 {
162 struct proc_uniqidentifierinfo procu;
163 CFUUIDBytes uuidBytes;
164 int size = 0;
165
166 memset(&procu, 0, sizeof(procu));
167 size = proc_pidinfo(getpid(), PROC_PIDUNIQIDENTIFIERINFO, 1, &procu, PROC_PIDUNIQIDENTIFIERINFO_SIZE);
168 if (size != PROC_PIDUNIQIDENTIFIERINFO_SIZE) {
169 return (NULL);
170 }
171
172 memcpy(&uuidBytes, procu.p_uuid, sizeof(CFUUIDBytes));
173 return CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, uuidBytes);
174 }
175
176 static bool
177 policy_session_init(void)
178 {
179 bool success = true;
180 policySession = NEPolicyCreateSession(kCFAllocatorDefault, CFSTR("racoon"), NULL, NULL);
181 if (policySession == NULL) {
182 return false;
183 }
184
185 CFUUIDRef proc_uuid = copy_racoon_proc_uuid();
186 if (proc_uuid == NULL) {
187 return false;
188 }
189
190 CFMutableArrayRef conditions = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
191 if (conditions) {
192 CFMutableDictionaryRef uuidCondition = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
193 if (uuidCondition) {
194 CFDictionarySetValue(uuidCondition, kNEPolicyConditionType, kNEPolicyValPolicyConditionTypeApplication);
195 CFDictionarySetValue(uuidCondition, kNEPolicyApplicationUUID, proc_uuid);
196 CFArrayAppendValue(conditions, uuidCondition);
197 CFRelease(uuidCondition);
198 } else {
199 success = false;
200 }
201
202 CFMutableDictionaryRef interfacesCondition = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
203 if (interfacesCondition) {
204 CFDictionarySetValue(interfacesCondition, kNEPolicyConditionType, kNEPolicyValPolicyConditionTypeAllInterfaces);
205 CFArrayAppendValue(conditions, interfacesCondition);
206 CFRelease(interfacesCondition);
207 } else {
208 success = false;
209 }
210 } else {
211 success = false;
212 }
213
214 CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
215 if (result) {
216 CFDictionaryAddValue(result, kNEPolicyResult, kNEPolicyValPolicyResultPass);
217 } else {
218 success = false;
219 }
220
221 if (success) {
222 success = (NEPolicyAdd(policySession, 0, conditions, result, NULL) != kNEPolicyIDInvalid);
223 }
224
225 if (result) {
226 CFRelease(result);
227 }
228 if (conditions) {
229 CFRelease(conditions);
230 }
231 if (proc_uuid) {
232 CFRelease(proc_uuid);
233 }
234
235 return (success && NEPolicyApply(policySession));
236 }
237
238 //
239 // Session
240 //
241 // Initialize listening sockets, timers, vpn control etc.,
242 // write the PID file and call dispatch_main.
243 //
244 void
245 session(void)
246 {
247 char pid_file[MAXPATHLEN];
248 FILE *fp;
249 int i;
250
251 main_queue = dispatch_get_main_queue();
252
253 /* initialize schedular */
254 sched_init();
255
256 /* needs to be called after schedular */
257 if (init_power_mgmt() < 0) {
258 plog(ASL_LEVEL_ERR,
259 "failed to initialize power-mgmt.");
260 exit(1);
261 }
262
263 if (lcconf->autograbaddr == 1)
264 if (pfroute_init()) {
265 plog(ASL_LEVEL_ERR, "failed to initialize route socket.\n");
266 exit(1);
267 }
268
269 if (!policy_session_init()) {
270 plog(ASL_LEVEL_ERR, "failed to initialize NEPolicy session.\n");
271 }
272
273 if (initmyaddr()) {
274 plog(ASL_LEVEL_ERR, "failed to initialize listening addresses.\n");
275 exit(1);
276 }
277 if (isakmp_init()) {
278 plog(ASL_LEVEL_ERR, "failed to initialize isakmp");
279 exit(1);
280 }
281 #ifdef ENABLE_VPNCONTROL_PORT
282 if (vpncontrol_init()) {
283 plog(ASL_LEVEL_ERR, "failed to initialize vpn control port");
284 //exit(1);
285 }
286 #endif
287
288 if (init_signal()) {
289 plog(ASL_LEVEL_ERR, "failed to initialize signals.\n");
290 exit(1);
291 }
292
293 for (i = 0; i <= NSIG; i++)
294 sigreq[i] = 0;
295
296 /* write .pid file */
297 if (!f_foreground) {
298 racoon_pid = getpid();
299 if (lcconf->pathinfo[LC_PATHTYPE_PIDFILE] == NULL)
300 strlcpy(pid_file, _PATH_VARRUN "racoon.pid", sizeof(pid_file));
301 else if (lcconf->pathinfo[LC_PATHTYPE_PIDFILE][0] == '/')
302 strlcpy(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], sizeof(pid_file));
303 else {
304 strlcat(pid_file, _PATH_VARRUN, sizeof(pid_file));
305 strlcat(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], sizeof(pid_file));
306 }
307 fp = fopen(pid_file, "w");
308 if (fp) {
309 if (fchmod(fileno(fp),
310 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
311 plog(ASL_LEVEL_ERR, "%s", strerror(errno));
312 fclose(fp);
313 exit(1);
314 }
315 fprintf(fp, "%ld\n", (long)racoon_pid);
316 fclose(fp);
317 } else {
318 plog(ASL_LEVEL_ERR,
319 "cannot open %s", pid_file);
320 }
321 }
322
323 if (g_ikev1_transaction == NULL) {
324 g_ikev1_transaction = os_transaction_create(IKEv1_TRANSACTION);
325 }
326
327 #if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
328 // enable keepalive for recovery (from crashes and bad exits... after init)
329 (void)launchd_update_racoon_keepalive(true);
330 #endif // !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
331
332 // Off to the races!
333 if (!terminated) {
334 dispatch_main();
335 }
336
337 exit(1); // should not be reached!!!
338 }
339
340
341 /* clear all status and exit program. */
342 static void
343 close_session(int error)
344 {
345 sched_killall();
346 cleanup_power_mgmt();
347 if ( terminated )
348 ike_session_flush_all_phase2(false);
349 ike_session_flush_all_phase1(false);
350 close_sockets();
351
352 if (g_ikev1_transaction != NULL) {
353 os_release(g_ikev1_transaction);
354 g_ikev1_transaction = NULL;
355 }
356
357 #if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
358 // a clean exit, so disable launchd keepalive
359 (void)launchd_update_racoon_keepalive(false);
360 #endif // !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
361
362 plog(ASL_LEVEL_NOTICE, "racoon shutdown\n");
363 exit(0);
364 }
365
366
367 /*
368 * waiting the termination of processing until sending DELETE message
369 * for all inbound SA will complete.
370 */
371 static void
372 check_flushsa_stub(p)
373 void *p;
374 {
375 check_flushsa();
376 }
377
378 static void
379 check_flushsa()
380 {
381 vchar_t *buf;
382 struct sadb_msg *msg, *end, *next;
383 struct sadb_sa *sa;
384 caddr_t mhp[SADB_EXT_MAX + 1];
385 int n;
386
387 buf = pfkey_dump_sadb(SADB_SATYPE_UNSPEC);
388 if (buf == NULL) {
389 plog(ASL_LEVEL_DEBUG,
390 "pfkey_dump_sadb: returned nothing.\n");
391 return;
392 }
393
394 msg = ALIGNED_CAST(struct sadb_msg *)buf->v;
395 end = ALIGNED_CAST(struct sadb_msg *)(buf->v + buf->l);
396
397 /* counting SA except of dead one. */
398 n = 0;
399 while (msg < end) {
400 if (PFKEY_UNUNIT64(msg->sadb_msg_len) < sizeof(*msg))
401 break;
402 next = ALIGNED_CAST(struct sadb_msg *)((caddr_t)msg + PFKEY_UNUNIT64(msg->sadb_msg_len)); // Wcast-align fix (void*) - aligned buffer + multiple of 64
403 if (msg->sadb_msg_type != SADB_DUMP) {
404 msg = next;
405 continue;
406 }
407
408 if (pfkey_align(msg, mhp) || pfkey_check(mhp)) {
409 plog(ASL_LEVEL_ERR,
410 "pfkey_check (%s)\n", ipsec_strerror());
411 msg = next;
412 continue;
413 }
414
415 sa = ALIGNED_CAST(struct sadb_sa *)(mhp[SADB_EXT_SA]); // Wcast-align fix (void*) - mhp contains pointers to aligned structs
416 if (!sa) {
417 msg = next;
418 continue;
419 }
420
421 if (sa->sadb_sa_state != SADB_SASTATE_DEAD) {
422 n++;
423 msg = next;
424 continue;
425 }
426
427 msg = next;
428 }
429
430 if (buf != NULL)
431 vfree(buf);
432
433 if (n) {
434 sched_new(1, check_flushsa_stub, NULL);
435 return;
436 }
437
438 #if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
439 if (lcconf->vt)
440 vproc_transaction_end(NULL, lcconf->vt);
441 #endif // !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
442 close_session(0);
443 }
444
445 void
446 auto_exit_do(void *p)
447 {
448 plog(ASL_LEVEL_NOTICE,
449 "performing auto exit\n");
450 #if ENABLE_NO_SA_FLUSH
451 close_session(0);
452 #else
453 pfkey_send_flush(lcconf->sock_pfkey, SADB_SATYPE_UNSPEC);
454 sched_new(1, check_flushsa_stub, NULL);
455 dying();
456 #endif /* ENABLE_NO_SA_FLUSH */
457 }
458
459 void
460 check_auto_exit(void)
461 {
462 if (lcconf->auto_exit_sched) { /* exit scheduled? */
463 if (lcconf->auto_exit_state != LC_AUTOEXITSTATE_ENABLED
464 || vpn_control_connected() /* vpn control connected */
465 || policies_installed() /* policies installed in kernel */
466 || !no_remote_configs(FALSE)) { /* remote or anonymous configs */
467 SCHED_KILL(lcconf->auto_exit_sched);
468 }
469 } else { /* exit not scheduled */
470 if (lcconf->auto_exit_state == LC_AUTOEXITSTATE_ENABLED
471 && !vpn_control_connected()
472 && !policies_installed()
473 && no_remote_configs(FALSE)) {
474 if (lcconf->auto_exit_delay == 0) {
475 auto_exit_do(NULL); /* immediate exit */
476 } else {
477 lcconf->auto_exit_sched = sched_new(lcconf->auto_exit_delay, auto_exit_do, NULL);
478 }
479 }
480 }
481 }
482
483 static int signals[] = {
484 SIGHUP,
485 SIGINT,
486 SIGTERM,
487 SIGUSR1,
488 SIGUSR2,
489 SIGPIPE,
490 0
491 };
492
493
494 void
495 check_sigreq()
496 {
497 int sig;
498
499 /*
500 * XXX We are not able to tell if we got
501 * several time the same signal. This is
502 * not a problem for the current code,
503 * but we shall remember this limitation.
504 */
505 for (sig = 0; sig <= NSIG; sig++) {
506 if (sigreq[sig] == 0)
507 continue;
508
509 sigreq[sig]--;
510 switch(sig) {
511 case 0:
512 return;
513
514 /* Catch up childs, mainly scripts.
515 */
516
517 case SIGUSR1:
518 case SIGHUP:
519 #ifdef ENABLE_HYBRID
520 if ((isakmp_cfg_init(ISAKMP_CFG_INIT_WARM)) != 0) {
521 plog(ASL_LEVEL_ERR,
522 "ISAKMP mode config structure reset failed, "
523 "not reloading\n");
524 return;
525 }
526 #endif
527 if ( terminated )
528 break;
529
530 /*
531 * if we got a HUP... try graceful teardown of sessions before we close and reopen sockets...
532 * so that info-deletes notifications can make it to the peer.
533 */
534 if (sig == SIGHUP) {
535 ike_session_flush_all_phase2(true);
536 ike_session_flush_all_phase1(true);
537 }
538
539 /* Save old configuration, load new one... */
540 if (cfreparse(sig)) {
541 plog(ASL_LEVEL_ERR,
542 "configuration read failed\n");
543 exit(1);
544 }
545 if (lcconf->logfile_param == NULL && logFileStr[0] == 0)
546 plogresetfile(lcconf->pathinfo[LC_PATHTYPE_LOGFILE]);
547
548 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
549 if (no_remote_configs(TRUE)) {
550 #if ENABLE_NO_SA_FLUSH
551 close_session(0);
552 #else
553 pfkey_send_flush(lcconf->sock_pfkey, SADB_SATYPE_UNSPEC);
554 #ifdef ENABLE_FASTQUIT
555 close_session(0);
556 #else
557 sched_new(1, check_flushsa_stub, NULL);
558 #endif /* ENABLE_FASTQUIT */
559 dying();
560 #endif /* ENABLE_NO_SA_FLUSH */
561 }
562 #endif // (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
563
564 break;
565
566 case SIGINT:
567 case SIGTERM:
568 plog(ASL_LEVEL_NOTICE,
569 "caught signal %d\n", sig);
570 #if ENABLE_NO_SA_FLUSH
571 close_session(0);
572 #else
573 pfkey_send_flush(lcconf->sock_pfkey,
574 SADB_SATYPE_UNSPEC);
575 if ( sig == SIGTERM ){
576 terminated = 1; /* in case if it hasn't been set yet */
577 close_session(0);
578 }
579 else
580 sched_new(1, check_flushsa_stub, NULL);
581
582 dying();
583 #endif /* ENABLE_NO_SA_FLUSH */
584 break;
585
586 default:
587 plog(ASL_LEVEL_NOTICE,
588 "caught signal %d\n", sig);
589 break;
590 }
591 }
592 }
593
594
595 /*
596 * asynchronous requests will actually dispatched in the
597 * main loop in session().
598 */
599 RETSIGTYPE
600 signal_handler(int sig, siginfo_t *sigi, void *ctx)
601 {
602 #if 0
603 plog(ASL_LEVEL_NOTICE,
604 "%s received signal %d from pid %d uid %d\n\n",
605 __FUNCTION__, sig, sigi->si_pid, sigi->si_uid);
606 #endif
607
608 /* Do not just set it to 1, because we may miss some signals by just setting
609 * values to 0/1
610 */
611 sigreq[sig]++;
612 if ( sig == SIGTERM ){
613 terminated = 1;
614 }
615
616 pending_signal_handle = 1;
617 dispatch_async(main_queue,
618 ^{
619 if (pending_signal_handle) {
620 check_sigreq();
621 pending_signal_handle = 0;
622 }
623 });
624 }
625
626
627 static int
628 init_signal()
629 {
630 int i;
631
632 for (i = 0; signals[i] != 0; i++) {
633 if (set_signal(signals[i], signal_handler) < 0) {
634 plog(ASL_LEVEL_ERR,
635 "failed to set_signal (%s)\n",
636 strerror(errno));
637 return (1);
638 }
639 }
640 return 0;
641 }
642
643 static int
644 set_signal(int sig, RETSIGTYPE (*func) (int, siginfo_t *, void *))
645 {
646 struct sigaction sa;
647
648 memset((caddr_t)&sa, 0, sizeof(sa));
649 sa.sa_sigaction = func;
650 sa.sa_flags = SA_RESTART | SA_SIGINFO;
651
652 if (sigemptyset(&sa.sa_mask) < 0)
653 return -1;
654
655 if (sigaction(sig, &sa, (struct sigaction *)0) < 0)
656 return(-1);
657
658 return 0;
659 }
660
661 void
662 fatal_error(int error)
663 {
664 close_session(error == 0 ? -1 : error);
665 }
666
667 /* suspend all socket sources except pf_key */
668 void
669 dying(void)
670 {
671 if (lcconf->rt_source)
672 dispatch_suspend(lcconf->rt_source);
673 if (lcconf->vpncontrol_source)
674 dispatch_suspend(lcconf->vpncontrol_source);
675 isakmp_suspend_sockets();
676 }
677
678 static int
679 close_sockets()
680 {
681 pfroute_close();
682 isakmp_close();
683 pfkey_close();
684 #ifdef ENABLE_VPNCONTROL_PORT
685 vpncontrol_close();
686 #endif
687
688 return 0;
689 }
690
691