X-Git-Url: https://git.saurik.com/apple/ipsec.git/blobdiff_plain/d1e348cfd503b08e7d34b7683d23aae209af0a71..ef0ae40ec078a7d5126612e50fbbbe09acb80a48:/ipsec-tools/racoon/vpn_control.c diff --git a/ipsec-tools/racoon/vpn_control.c b/ipsec-tools/racoon/vpn_control.c index cc81603..e20c823 100644 --- a/ipsec-tools/racoon/vpn_control.c +++ b/ipsec-tools/racoon/vpn_control.c @@ -4,13 +4,13 @@ * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. - * + * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -18,14 +18,14 @@ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -37,7 +37,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -60,12 +60,12 @@ #include #include -#include +#include #include #ifndef HAVE_NETINET6_IPSEC #include -#else +#else #include #endif @@ -79,6 +79,7 @@ #include #endif #include +#include #include "var.h" #include "misc.h" @@ -95,7 +96,6 @@ #include "isakmp.h" #include "oakley.h" #include "handler.h" -#include "evt.h" #include "pfkey.h" #include "ipsec_doi.h" #include "vpn_control.h" @@ -103,6 +103,8 @@ #include "isakmp_inf.h" #include "session.h" #include "gcmalloc.h" +#include "isakmp_cfg.h" +#include "sainfo.h" #ifdef ENABLE_VPNCONTROL_PORT char *vpncontrolsock_path = VPNCONTROLSOCK_PATH; @@ -111,200 +113,197 @@ gid_t vpncontrolsock_group = 0; mode_t vpncontrolsock_mode = 0600; static struct sockaddr_un sunaddr; -static int vpncontrol_process(struct vpnctl_socket_elem *, char *); -static int vpncontrol_reply(int, char *); -static void vpncontrol_close_comm(struct vpnctl_socket_elem *); -static int checklaunchd(); -extern int vpn_get_config __P((struct ph1handle *, struct vpnctl_status_phase_change **, size_t *)); -extern int vpn_xauth_reply __P((u_int32_t, void *, size_t)); - - -int -checklaunchd() -{ - launch_data_t checkin_response = NULL; - launch_data_t checkin_request = NULL; - launch_data_t sockets_dict, listening_fd_array; - launch_data_t listening_fd; - struct sockaddr_storage fdsockaddr; - socklen_t fdsockaddrlen = sizeof(fdsockaddr); - int socketct; - int i; - int listenerct; +static int vpncontrol_process (struct vpnctl_socket_elem *, char *, size_t); +static int vpncontrol_reply (int, char *); +static void vpncontrol_close_comm (struct vpnctl_socket_elem *); +static int checklaunchd (void); +extern int vpn_get_config (phase1_handle_t *, struct vpnctl_status_phase_change **, size_t *); +extern int vpn_xauth_reply (u_int32_t, void *, size_t); + + +int +checklaunchd() +{ int returnval = 0; - int fd; - - /* check in with launchd */ - if ((checkin_request = launch_data_new_string(LAUNCH_KEY_CHECKIN)) == NULL) { - plog(LLV_ERROR, LOCATION, NULL, - "failed to launch_data_new_string.\n"); - goto done; - } - if ((checkin_response = launch_msg(checkin_request)) == NULL) { - plog(LLV_ERROR, LOCATION, NULL, - "failed to launch_msg.\n"); - goto done; - } - if (LAUNCH_DATA_ERRNO == launch_data_get_type(checkin_response)) { - plog(LLV_ERROR, LOCATION, NULL, - "launch_data_get_type error %d\n", - launch_data_get_errno(checkin_response)); - goto done; - } - if ( (sockets_dict = launch_data_dict_lookup(checkin_response, LAUNCH_JOBKEY_SOCKETS)) == NULL){ - plog(LLV_ERROR, LOCATION, NULL, - "failed to launch_data_dict_lookup.\n"); - goto done; - } - if ( !(socketct = launch_data_dict_get_count(sockets_dict))){ - plog(LLV_ERROR, LOCATION, NULL, - "launch_data_dict_get_count returns no socket defined.\n"); - goto done; - } - - if ( (listening_fd_array = launch_data_dict_lookup(sockets_dict, "Listeners")) == NULL ){ - plog(LLV_ERROR, LOCATION, NULL, - "failed to launch_data_dict_lookup.\n"); - goto done; + int *listening_fd_array = NULL; + size_t fd_count = 0; + + int result = launch_activate_socket("Listeners", &listening_fd_array, &fd_count); + if (result != 0) { + plog(ASL_LEVEL_ERR, "failed to launch_activate_socket with error %s.\n", strerror(result)); + return returnval; } - listenerct = launch_data_array_get_count(listening_fd_array); - for (i = 0; i < listenerct; i++) { - listening_fd = launch_data_array_get_index(listening_fd_array, i); - fd = launch_data_get_fd( listening_fd ); - if ( getsockname( fd , (struct sockaddr*)&fdsockaddr, &fdsockaddrlen)){ - continue; - } - - /* Is this the VPN control socket? */ - if ( (((struct sockaddr*)&fdsockaddr)->sa_family) == AF_UNIX && - (!(strcmp(vpncontrolsock_path, ((struct sockaddr_un *)&fdsockaddr)->sun_path)))) - { - plog(LLV_INFO, LOCATION, NULL, - "found launchd socket.\n"); - returnval = fd; - break; + + if (listening_fd_array != NULL) { + if (fd_count > 0) { + returnval = listening_fd_array[0]; } + free(listening_fd_array); + listening_fd_array = NULL; } - // TODO: check if we have any leaked fd - if ( listenerct == i){ - plog(LLV_ERROR, LOCATION, NULL, - "failed to find launchd socket\n"); - returnval = 0; - } - -done: - if (checkin_request) - launch_data_free(checkin_request); - if (checkin_response) - launch_data_free(checkin_response); - return(returnval); + + return returnval; } -int -vpncontrol_handler() +void +vpncontrol_handler(void *unused) { struct sockaddr_storage from; socklen_t fromlen = sizeof(from); + int sock; struct vpnctl_socket_elem *sock_elem; + - sock_elem = racoon_malloc(sizeof(struct vpnctl_socket_elem)); + sock_elem = racoon_calloc(1, sizeof(struct vpnctl_socket_elem)); if (sock_elem == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "memory error: %s\n", strerror(errno)); - return -1; + return; //%%%%%% terminate } LIST_INIT(&sock_elem->bound_addresses); sock_elem->sock = accept(lcconf->sock_vpncontrol, (struct sockaddr *)&from, &fromlen); if (sock_elem->sock < 0) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "failed to accept vpn_control command: %s\n", strerror(errno)); racoon_free(sock_elem); - return -1; + return; //%%%%% terminate } LIST_INSERT_HEAD(&lcconf->vpnctl_comm_socks, sock_elem, chain); - plog(LLV_NOTIFY, LOCATION, NULL, - "accepted connection on vpn control socket.\n"); - + + sock_elem->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sock_elem->sock, 0, dispatch_get_main_queue()); + if (sock_elem->source == NULL) { + plog(ASL_LEVEL_ERR, "could not create comm socket source."); + racoon_free(sock_elem); + return; //%%%%% terminate + } + dispatch_source_set_event_handler(sock_elem->source, + ^{ + vpncontrol_comm_handler(sock_elem); + }); + sock = sock_elem->sock; + + dispatch_source_t the_source = sock_elem->source; + dispatch_source_set_cancel_handler(sock_elem->source, + ^{ + close(sock); + dispatch_release(the_source); /* Release the source on cancel */ + }); + dispatch_resume(sock_elem->source); + + plog(ASL_LEVEL_NOTICE, + "accepted connection on vpn control socket.\n"); check_auto_exit(); - return 0; + return; } -int +void vpncontrol_comm_handler(struct vpnctl_socket_elem *elem) { struct vpnctl_hdr hdr; - char *combuf = NULL; - int len; + ssize_t len; /* get buffer length */ - while ((len = recv(elem->sock, (char *)&hdr, sizeof(hdr), MSG_PEEK)) < 0) { - if (errno == EINTR) - continue; - plog(LLV_ERROR, LOCATION, NULL, - "failed to recv vpn_control command: %s\n", strerror(errno)); - goto end; - } - if (len == 0) { - plog(LLV_DEBUG, LOCATION, NULL, - "vpn_control socket closed by peer.\n"); - vpncontrol_close_comm(elem); - return -1; - } - - /* sanity check */ - if (len < sizeof(hdr)) { - plog(LLV_ERROR, LOCATION, NULL, - "invalid header length of vpn_control command - len=%d - expected %d\n", len, sizeof(hdr)); - goto end; - } + if (elem->buffer == NULL) { + while ((len = recv(elem->sock, (char *)&hdr, sizeof(hdr), MSG_PEEK)) < 0) { + if (errno == EINTR) + continue; + plog(ASL_LEVEL_ERR, "failed to recv vpn_control command: %s\n", strerror(errno)); + return; + } + if (len == 0) { + plog(ASL_LEVEL_NOTICE, "vpn_control socket closed by peer.\n"); + /* kill all related connections */ + vpncontrol_disconnect_all(elem, ike_session_stopped_by_controller_comm_lost); + vpncontrol_close_comm(elem); + return; // %%%%%% terminate + } - /* get buffer to receive */ - if ((combuf = racoon_malloc(ntohs(hdr.len) + sizeof(hdr))) == 0) { - plog(LLV_ERROR, LOCATION, NULL, - "failed to alloc buffer for vpn_control command\n"); - goto end; + /* sanity check */ + if (len < sizeof(hdr)) { + plog(ASL_LEVEL_ERR, + "invalid header length of vpn_control command - len=%ld - expected %ld\n", len, sizeof(hdr)); + return; + } + + elem->read_bytes_len = 0; // Sanity + elem->pending_bytes_len = ntohs(hdr.len) + sizeof(hdr); + + /* get buffer to receive */ + elem->buffer = racoon_malloc(elem->pending_bytes_len); + if (elem->buffer == NULL) { + plog(ASL_LEVEL_ERR, + "failed to alloc buffer for vpn_control command\n"); + return; + } } /* get real data */ - while ((len = recv(elem->sock, combuf, ntohs(hdr.len) + sizeof(hdr), 0)) < 0) { + while ((len = recv(elem->sock, elem->buffer + elem->read_bytes_len, elem->pending_bytes_len, 0)) < 0) { if (errno == EINTR) continue; - plog(LLV_ERROR, LOCATION, NULL, - "failed to recv vpn_control command: %s\n", - strerror(errno)); - goto end; + plog(ASL_LEVEL_ERR, "failed to recv vpn_control command: %s\n", + strerror(errno)); + return; } - (void)vpncontrol_process(elem, combuf); + if (len == 0) { + plog(ASL_LEVEL_NOTICE, "vpn_control socket closed by peer while reading packet\n"); + /* kill all related connections */ + vpncontrol_disconnect_all(elem, ike_session_stopped_by_controller_comm_lost); + vpncontrol_close_comm(elem); + return; + } -end: - if (combuf) - racoon_free(combuf); - return 0; // return -1 only if a socket is closed + elem->read_bytes_len += len; + + if (len < elem->pending_bytes_len) { + plog(ASL_LEVEL_NOTICE, + "received partial vpn_control command - len=%ld - expected %u\n", len, elem->pending_bytes_len); + elem->pending_bytes_len -= len; + return; + } else { + (void)vpncontrol_process(elem, elem->buffer, elem->read_bytes_len); + free(elem->buffer); + elem->buffer = NULL; + elem->read_bytes_len = 0; + elem->pending_bytes_len = 0; + } } static int -vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf) +vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf, size_t combuf_len) { u_int16_t error = 0; - struct vpnctl_hdr *hdr = (struct vpnctl_hdr *)combuf; + struct vpnctl_hdr *hdr = ALIGNED_CAST(struct vpnctl_hdr *)combuf; switch (ntohs(hdr->msg_type)) { case VPNCTL_CMD_BIND: { - struct vpnctl_cmd_bind *pkt = (struct vpnctl_cmd_bind *)combuf; + if (combuf_len < sizeof(struct vpnctl_cmd_bind)) { + plog(ASL_LEVEL_ERR, "invalid header length for vpnctl bind cmd - len=%ld - expected %ld\n", combuf_len, sizeof(struct vpnctl_cmd_bind)); + error = -1; + break; + } + + struct vpnctl_cmd_bind *pkt = ALIGNED_CAST(struct vpnctl_cmd_bind *)combuf; struct bound_addr *addr; + + if (combuf_len < (sizeof(struct vpnctl_cmd_bind) + ntohs(pkt->vers_len))) { + plog(ASL_LEVEL_ERR, "invalid length for vpnctl bind cmd - len=%ld - expected %ld\n", combuf_len, (sizeof(struct vpnctl_cmd_bind) + ntohs(pkt->vers_len))); + error = -1; + break; + } - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_NOTICE, "received bind command on vpn control socket.\n"); addr = racoon_calloc(1, sizeof(struct bound_addr)); if (addr == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "memory error: %s\n", strerror(errno)); error = -1; break; @@ -312,9 +311,10 @@ vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf) if (ntohs(pkt->vers_len)) { addr->version = vmalloc(ntohs(pkt->vers_len)); if (addr->version == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "memory error: %s\n", strerror(errno)); error = -1; + racoon_free(addr); break; } memcpy(addr->version->v, pkt + 1, ntohs(pkt->vers_len)); @@ -327,11 +327,17 @@ vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf) case VPNCTL_CMD_UNBIND: { - struct vpnctl_cmd_unbind *pkt = (struct vpnctl_cmd_unbind *)combuf; + if (combuf_len < sizeof(struct vpnctl_cmd_unbind)) { + plog(ASL_LEVEL_ERR, "invalid header length for vpnctl unbind cmd - len=%ld - expected %ld\n", combuf_len, sizeof(struct vpnctl_cmd_unbind)); + error = -1; + break; + } + + struct vpnctl_cmd_unbind *pkt = ALIGNED_CAST(struct vpnctl_cmd_unbind *)combuf; struct bound_addr *addr; struct bound_addr *t_addr; - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_NOTICE, "received unbind command on vpn control socket.\n"); LIST_FOREACH_SAFE(addr, &elem->bound_addresses, chain, t_addr) { if (pkt->address == 0xFFFFFFFF || @@ -348,12 +354,18 @@ vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf) case VPNCTL_CMD_REDIRECT: { - struct vpnctl_cmd_redirect *redirect_msg = (struct vpnctl_cmd_redirect *)combuf; + if (combuf_len < sizeof(struct vpnctl_cmd_redirect)) { + plog(ASL_LEVEL_ERR, "invalid header length for vpnctl redirect cmd - len=%ld - expected %ld\n", combuf_len, sizeof(struct vpnctl_cmd_redirect)); + error = -1; + break; + } + + struct vpnctl_cmd_redirect *redirect_msg = ALIGNED_CAST(struct vpnctl_cmd_redirect *)combuf; struct redirect *raddr; struct redirect *t_raddr; int found = 0; - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_NOTICE, "received redirect command on vpn control socket - address = %x.\n", ntohl(redirect_msg->redirect_address)); LIST_FOREACH_SAFE(raddr, &lcconf->redirect_addresses, chain, t_raddr) { @@ -372,7 +384,7 @@ vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf) if (!found) { raddr = racoon_malloc(sizeof(struct redirect)); if (raddr == NULL) { - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "cannot allcoate memory for redirect address.\n"); error = -1; break; @@ -391,12 +403,24 @@ vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf) case VPNCTL_CMD_XAUTH_INFO: { - struct vpnctl_cmd_xauth_info *pkt = (struct vpnctl_cmd_xauth_info *)combuf; + if (combuf_len < sizeof(struct vpnctl_cmd_xauth_info)) { + plog(ASL_LEVEL_ERR, "invalid header length for vpnctl xauth info cmd - len=%ld - expected %ld\n", combuf_len, sizeof(struct vpnctl_cmd_xauth_info)); + error = -1; + break; + } + + struct vpnctl_cmd_xauth_info *pkt = ALIGNED_CAST(struct vpnctl_cmd_xauth_info *)combuf; struct bound_addr *addr; struct bound_addr *t_addr; void *attr_list; - plog(LLV_DEBUG, LOCATION, NULL, + if (combuf_len < (sizeof(struct vpnctl_cmd_xauth_info) + ntohs(pkt->hdr.len) - sizeof(u_int32_t))) { + plog(ASL_LEVEL_ERR, "invalid length for vpnctl xauth info cmd - len=%ld - expected %ld\n", combuf_len, (sizeof(struct vpnctl_cmd_xauth_info) + ntohs(pkt->hdr.len) - sizeof(u_int32_t))); + error = -1; + break; + } + + plog(ASL_LEVEL_NOTICE, "received xauth info command vpn control socket.\n"); LIST_FOREACH_SAFE(addr, &elem->bound_addresses, chain, t_addr) { if (pkt->address == addr->address) { @@ -408,19 +432,54 @@ vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf) } } break; - + + case VPNCTL_CMD_SET_NAT64_PREFIX: + { + if (combuf_len < sizeof(struct vpnctl_cmd_set_nat64_prefix)) { + plog(ASL_LEVEL_ERR, "invalid header length for vpnctl nat64 prefix cmd - len=%ld - expected %ld\n", combuf_len, sizeof(struct vpnctl_cmd_set_nat64_prefix)); + error = -1; + break; + } + + struct vpnctl_cmd_set_nat64_prefix *pkt = ALIGNED_CAST(struct vpnctl_cmd_set_nat64_prefix *)combuf; + struct bound_addr *addr; + struct bound_addr *t_addr; + + plog(ASL_LEVEL_NOTICE, + "received set v6 prefix of len %u command on vpn control socket, adding to all addresses.\n", pkt->nat64_prefix.length); + LIST_FOREACH_SAFE(addr, &elem->bound_addresses, chain, t_addr) { + memcpy(&addr->nat64_prefix, &pkt->nat64_prefix, sizeof(addr->nat64_prefix)); + } + } + break; + case VPNCTL_CMD_CONNECT: { - struct vpnctl_cmd_connect *pkt = (struct vpnctl_cmd_connect *)combuf; + if (combuf_len < sizeof(struct vpnctl_cmd_connect)) { + plog(ASL_LEVEL_ERR, "invalid header length for vpnctl connect cmd - len=%ld - expected %ld\n", combuf_len, sizeof(struct vpnctl_cmd_connect)); + error = -1; + break; + } + + struct vpnctl_cmd_connect *pkt = ALIGNED_CAST(struct vpnctl_cmd_connect *)combuf; struct bound_addr *addr; struct bound_addr *t_addr; - plog(LLV_DEBUG, LOCATION, NULL, + if (pending_signal_handle) { + /* + * This check is done to ensure that a SIGUSR1 signal to re-read the configuration file + * is completed before calling a connect. This is to fix the issue seen in (rdar://problem/25641686) + */ + check_sigreq(); + pending_signal_handle = 0; + } + + plog(ASL_LEVEL_NOTICE, "received connect command on vpn control socket.\n"); LIST_FOREACH_SAFE(addr, &elem->bound_addresses, chain, t_addr) { if (pkt->address == addr->address) { /* start the connection */ - error = vpn_connect(addr); + error = vpn_connect(addr, VPN_STARTED_BY_API); break; } } @@ -429,16 +488,22 @@ vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf) case VPNCTL_CMD_DISCONNECT: { - struct vpnctl_cmd_connect *pkt = (struct vpnctl_cmd_connect *)combuf; + if (combuf_len < sizeof(struct vpnctl_cmd_connect)) { + plog(ASL_LEVEL_ERR, "invalid header length for vpnctl disconnect cmd - len=%ld - expected %ld\n", combuf_len, sizeof(struct vpnctl_cmd_connect)); + error = -1; + break; + } + + struct vpnctl_cmd_connect *pkt = ALIGNED_CAST(struct vpnctl_cmd_connect *)combuf; struct bound_addr *addr; struct bound_addr *t_addr; - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_NOTICE, "received disconnect command on vpn control socket.\n"); LIST_FOREACH_SAFE(addr, &elem->bound_addresses, chain, t_addr) { if (pkt->address == addr->address) { /* stop the connection */ - error = vpn_disconnect(addr); + error = vpn_disconnect(addr, ike_session_stopped_by_vpn_disconnect); break; } } @@ -447,17 +512,21 @@ vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf) case VPNCTL_CMD_START_PH2: { - struct vpnctl_cmd_start_ph2 *pkt = (struct vpnctl_cmd_start_ph2 *)combuf; + if (combuf_len < sizeof(struct vpnctl_cmd_start_ph2)) { + plog(ASL_LEVEL_ERR, "invalid header length for vpnctl start ph2 cmd - len=%ld - expected %ld\n", combuf_len, sizeof(struct vpnctl_cmd_start_ph2)); + error = -1; + break; + } + + struct vpnctl_cmd_start_ph2 *pkt = ALIGNED_CAST(struct vpnctl_cmd_start_ph2 *)combuf; struct bound_addr *addr; struct bound_addr *t_addr; - plog(LLV_DEBUG, LOCATION, NULL, - "received start_ph2 command on vpn control socket.\n"); - plogdump(LLV_DEBUG2, pkt, ntohs(hdr->len) + sizeof(struct vpnctl_hdr)); + plog(ASL_LEVEL_NOTICE, "received start_ph2 command on vpn control socket.\n"); LIST_FOREACH_SAFE(addr, &elem->bound_addresses, chain, t_addr) { if (pkt->address == addr->address) { /* start the connection */ - error = vpn_start_ph2(addr, pkt); + error = vpn_start_ph2(addr, pkt, combuf_len); break; } } @@ -466,32 +535,100 @@ vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf) case VPNCTL_CMD_START_DPD: { - struct vpnctl_cmd_start_dpd *pkt = (struct vpnctl_cmd_start_dpd *)combuf; + if (combuf_len < sizeof(struct vpnctl_cmd_start_dpd)) { + plog(ASL_LEVEL_ERR, "invalid header length for vpnctl start dpd cmd - len=%ld - expected %ld\n", combuf_len, sizeof(struct vpnctl_cmd_start_dpd)); + error = -1; + break; + } + + struct vpnctl_cmd_start_dpd *pkt = ALIGNED_CAST(struct vpnctl_cmd_start_dpd *)combuf; struct bound_addr *srv; struct bound_addr *t_addr; - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_NOTICE, "received start_dpd command on vpn control socket.\n"); LIST_FOREACH_SAFE(srv, &elem->bound_addresses, chain, t_addr) { if (pkt->address == srv->address) { - struct sockaddr_in daddr; + union { // Wcast-align fix - force alignment + struct sockaddr_storage ss; + struct sockaddr_in addr_in; + } daddr; - bzero(&daddr, sizeof(daddr)); - daddr.sin_len = sizeof(daddr); - daddr.sin_addr.s_addr = srv->address; - daddr.sin_port = 0; - daddr.sin_family = AF_INET; + bzero(&daddr, sizeof(struct sockaddr_in)); + daddr.addr_in.sin_len = sizeof(struct sockaddr_in); + daddr.addr_in.sin_addr.s_addr = srv->address; + daddr.addr_in.sin_port = 0; + daddr.addr_in.sin_family = AF_INET; /* start the dpd */ - error = ph1_force_dpd(&daddr); + error = ike_session_ph1_force_dpd(&daddr.ss); break; } } } break; + case VPNCTL_CMD_ASSERT: + { + if (combuf_len < sizeof(struct vpnctl_cmd_assert)) { + plog(ASL_LEVEL_ERR, "invalid header length for vpnctl assert cmd - len=%ld - expected %ld\n", combuf_len, sizeof(struct vpnctl_cmd_assert)); + error = -1; + break; + } + + struct vpnctl_cmd_assert *pkt = ALIGNED_CAST(struct vpnctl_cmd_assert *)combuf; +// struct bound_addr *addr; +// struct bound_addr *t_addr; + struct sockaddr_in saddr; + struct sockaddr_in daddr; + + plogdump(ASL_LEVEL_NOTICE, pkt, ntohs(hdr->len) + sizeof(struct vpnctl_hdr), "received assert command on vpn control socket.\n"); +// LIST_FOREACH_SAFE(addr, &elem->bound_addresses, chain, t_addr) { +// if (pkt->dst_address == addr->address) { + bzero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_addr.s_addr = pkt->src_address; + saddr.sin_port = 0; + saddr.sin_family = AF_INET; + bzero(&daddr, sizeof(daddr)); + daddr.sin_len = sizeof(daddr); + daddr.sin_addr.s_addr = pkt->dst_address; + daddr.sin_port = 0; + daddr.sin_family = AF_INET; + + error = vpn_assert(ALIGNED_CAST(struct sockaddr_storage *)&saddr, ALIGNED_CAST(struct sockaddr_storage *)&daddr); + break; +// } +// } + } + break; + + case VPNCTL_CMD_RECONNECT: + { + if (combuf_len < sizeof(struct vpnctl_cmd_connect)) { + plog(ASL_LEVEL_ERR, "invalid header length for vpnctl reconnect cmd - len=%ld - expected %ld\n", combuf_len, sizeof(struct vpnctl_cmd_connect)); + error = -1; + break; + } + + struct vpnctl_cmd_connect *pkt = ALIGNED_CAST(struct vpnctl_cmd_connect *)combuf; + struct bound_addr *addr; + struct bound_addr *t_addr; + + plog(ASL_LEVEL_NOTICE, + "received reconnect command on vpn control socket.\n"); + LIST_FOREACH_SAFE(addr, &elem->bound_addresses, chain, t_addr) { + if (pkt->address == addr->address) { + /* start the connection */ + error = vpn_connect(addr, VPN_RESTARTED_BY_API); + break; + } + } + } + break; + default: - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "invalid command: %d\n", ntohs(hdr->msg_type)); error = -1; // for now break; @@ -509,11 +646,11 @@ vpncontrol_process(struct vpnctl_socket_elem *elem, char *combuf) static int vpncontrol_reply(int so, char *combuf) { - size_t tlen; + ssize_t tlen; tlen = send(so, combuf, sizeof(struct vpnctl_hdr), 0); if (tlen < 0) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "failed to send vpn_control message: %s\n", strerror(errno)); return -1; } @@ -521,34 +658,52 @@ vpncontrol_reply(int so, char *combuf) return 0; } +bool +vpncontrol_set_nat64_prefix(nw_nat64_prefix_t *prefix) +{ + struct vpnctl_socket_elem *sock_elem; + struct bound_addr *bound_addr; + + LIST_FOREACH(sock_elem, &lcconf->vpnctl_comm_socks, chain) { + LIST_FOREACH(bound_addr, &sock_elem->bound_addresses, chain) { + if (bound_addr->nat64_prefix.length != 0) { + memcpy(prefix, &bound_addr->nat64_prefix, sizeof(*prefix)); + return true; + } + } + } + return false; +} + int -vpncontrol_notify_need_authinfo(struct ph1handle *iph1, void* attr_list, size_t attr_len) +vpncontrol_notify_need_authinfo(phase1_handle_t *iph1, void* attr_list, size_t attr_len) { - struct vpnctl_status_need_authinfo *msg = NULL; + struct vpnctl_status_need_authinfo *msg = NULL; struct vpnctl_socket_elem *sock_elem; struct bound_addr *bound_addr; - size_t tlen, msg_size; + size_t msg_size; + ssize_t tlen; u_int32_t address; void *ptr; if (!iph1) goto end; - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_NOTICE, "sending vpn_control xauth need info status\n"); msg = (struct vpnctl_status_need_authinfo *)racoon_malloc(msg_size = sizeof(struct vpnctl_status_need_authinfo) + attr_len); if (msg == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "unable to allocate space for vpn control message.\n"); return -1; } msg->hdr.flags = 0; - - if (iph1->remote->sa_family == AF_INET) - address = ((struct sockaddr_in *)iph1->remote)->sin_addr.s_addr; - else - goto end; // for now + + address = iph1_get_remote_v4_address(iph1); + if (address == 0) { + goto end; + } msg->hdr.cookie = msg->hdr.reserved = msg->hdr.result = 0; msg->hdr.len = htons((msg_size) - sizeof(struct vpnctl_hdr)); @@ -557,7 +712,7 @@ vpncontrol_notify_need_authinfo(struct ph1handle *iph1, void* attr_list, size_t } else { msg->hdr.msg_type = htons(VPNCTL_STATUS_NEED_REAUTHINFO); } - msg->address = address; + msg->address = iph1_get_remote_v4_address(iph1); ptr = msg + 1; memcpy(ptr, attr_list, attr_len); @@ -565,12 +720,10 @@ vpncontrol_notify_need_authinfo(struct ph1handle *iph1, void* attr_list, size_t LIST_FOREACH(bound_addr, &sock_elem->bound_addresses, chain) { if (bound_addr->address == 0xFFFFFFFF || bound_addr->address == address) { - plog(LLV_DEBUG, LOCATION, NULL, - "vpn control writing %d bytes\n", msg_size); - plogdump(LLV_DEBUG, msg, msg_size); + plog(ASL_LEVEL_DEBUG, "vpn control writing %zu bytes\n", msg_size); tlen = send(sock_elem->sock, msg, msg_size, 0); if (tlen < 0) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "failed to send vpn_control need authinfo status: %s\n", strerror(errno)); } break; @@ -587,16 +740,17 @@ end: int vpncontrol_notify_ike_failed(u_int16_t notify_code, u_int16_t from, u_int32_t address, u_int16_t data_len, u_int8_t *data) { - struct vpnctl_status_failed *msg = NULL; + struct vpnctl_status_failed *msg = NULL; struct vpnctl_socket_elem *sock_elem; struct bound_addr *bound_addr; - size_t tlen, len; + size_t len; + ssize_t tlen; len = sizeof(struct vpnctl_status_failed) + data_len; msg = (struct vpnctl_status_failed *)racoon_malloc(len); if (msg == NULL) { - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "unable to allcate memory for vpn control status message.\n"); return -1; } @@ -609,7 +763,7 @@ vpncontrol_notify_ike_failed(u_int16_t notify_code, u_int16_t from, u_int32_t ad msg->from = htons(from); if (data_len > 0) memcpy(msg->data, data, data_len); - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "sending vpn_control ike failed message - code=%d from=%s.\n", notify_code, (from == FROM_LOCAL ? "local" : "remote")); @@ -619,8 +773,11 @@ vpncontrol_notify_ike_failed(u_int16_t notify_code, u_int16_t from, u_int32_t ad bound_addr->address == address) { tlen = send(sock_elem->sock, msg, len, 0); if (tlen < 0) { - plog(LLV_ERROR, LOCATION, NULL, - "unable to send vpn_control ike notify failed: %s\n", strerror(errno)); + plog(ASL_LEVEL_ERR, + "Unable to send vpn_control ike notify failed: %s\n", strerror(errno)); + } else { + plog(ASL_LEVEL_DEBUG, + "Sent %zd/%zu bytes\n", tlen, len); } break; } @@ -632,20 +789,43 @@ vpncontrol_notify_ike_failed(u_int16_t notify_code, u_int16_t from, u_int32_t ad return 0; } +char * +vpncontrol_status_2_str(u_int16_t msg_type) +{ + switch (msg_type) { + case VPNCTL_STATUS_IKE_FAILED: + return "IKE failed"; + case VPNCTL_STATUS_PH1_START_US: + return "Phase 1 started by us"; + case VPNCTL_STATUS_PH1_START_PEER: + return "Phase 1 started by peer"; + case VPNCTL_STATUS_PH1_ESTABLISHED: + return "Phase 1 established"; + case VPNCTL_STATUS_PH2_START: + return "Phase 2 started"; + case VPNCTL_STATUS_PH2_ESTABLISHED: + return "Phase 2 established"; + case VPNCTL_STATUS_NEED_AUTHINFO: + return "Need authentication info"; + case VPNCTL_STATUS_NEED_REAUTHINFO: + return "Need re-authentication info"; + default: + return ""; + } +} + int -vpncontrol_notify_phase_change(int start, u_int16_t from, struct ph1handle *iph1, struct ph2handle *iph2) +vpncontrol_notify_phase_change(int start, u_int16_t from, phase1_handle_t *iph1, phase2_handle_t *iph2) { - struct vpnctl_status_phase_change *msg; + struct vpnctl_status_phase_change *msg; struct vpnctl_socket_elem *sock_elem; struct bound_addr *bound_addr; - size_t tlen, msg_size; + ssize_t tlen; + size_t msg_size; u_int32_t address; - plog(LLV_DEBUG, LOCATION, NULL, - "sending vpn_control phase change status\n"); - - if (iph1 && !start && iph1->mode_cfg) { + if (iph1 && !start && iph1->mode_cfg && iph1->mode_cfg->xauth.status != XAUTHST_OK) { if (vpn_get_config(iph1, &msg, &msg_size) == 1) return 0; /* mode config not finished yet */ } else { @@ -654,25 +834,32 @@ vpncontrol_notify_phase_change(int start, u_int16_t from, struct ph1handle *iph1 } if (msg == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "unable to allocate space for vpn control message.\n"); return -1; } if (iph1) { - if (iph1->remote->sa_family == AF_INET) - address = ((struct sockaddr_in *)iph1->remote)->sin_addr.s_addr; - else - goto end; // for now - msg->hdr.msg_type = htons(start ? - (from == FROM_LOCAL ? VPNCTL_STATUS_PH1_START_US : VPNCTL_STATUS_PH1_START_PEER) + address = iph1_get_remote_v4_address(iph1); + if (address == 0) { + plog(ASL_LEVEL_ERR, "bad address for ph1 status change.\n"); + goto end; + } + msg->hdr.msg_type = htons(start ? + (from == FROM_LOCAL ? VPNCTL_STATUS_PH1_START_US : VPNCTL_STATUS_PH1_START_PEER) : VPNCTL_STATUS_PH1_ESTABLISHED); + // TODO: indicate version } else { - if (iph2->dst->sa_family == AF_INET) - address = ((struct sockaddr_in *)iph2->dst)->sin_addr.s_addr; - else - goto end; // for now + address = iph2_get_remote_v4_address(iph2); + if (address == 0) { + plog(ASL_LEVEL_ERR, "bad address for ph2 status change.\n"); + goto end; + } msg->hdr.msg_type = htons(start ? VPNCTL_STATUS_PH2_START : VPNCTL_STATUS_PH2_ESTABLISHED); + // TODO: indicate version } + plog(ASL_LEVEL_NOTICE, + ">>>>> phase change status = %s\n", vpncontrol_status_2_str(ntohs(msg->hdr.msg_type))); + msg->hdr.cookie = msg->hdr.reserved = msg->hdr.result = 0; msg->hdr.len = htons((msg_size) - sizeof(struct vpnctl_hdr)); msg->address = address; @@ -681,12 +868,10 @@ vpncontrol_notify_phase_change(int start, u_int16_t from, struct ph1handle *iph1 LIST_FOREACH(bound_addr, &sock_elem->bound_addresses, chain) { if (bound_addr->address == 0xFFFFFFFF || bound_addr->address == address) { - plog(LLV_DEBUG, LOCATION, NULL, - "vpn control writing %d bytes\n", msg_size); - plogdump(LLV_DEBUG, msg, msg_size); + plog(ASL_LEVEL_DEBUG, "vpn control writing %zu bytes\n", msg_size); tlen = send(sock_elem->sock, msg, msg_size, 0); if (tlen < 0) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "failed to send vpn_control phase change status: %s\n", strerror(errno)); } break; @@ -700,20 +885,82 @@ end: return 0; } +static int +vpncontrol_notify_peer_resp (u_int16_t notify_code, u_int32_t address) +{ + struct vpnctl_status_peer_resp msg; + struct vpnctl_socket_elem *sock_elem; + struct bound_addr *bound_addr; + ssize_t tlen; + int rc = -1; + + bzero(&msg, sizeof(msg)); + msg.hdr.msg_type = htons(VPNCTL_STATUS_PEER_RESP); + msg.hdr.cookie = msg.hdr.reserved = msg.hdr.result = 0; + msg.hdr.len = htons(sizeof(msg) - sizeof(msg.hdr)); + msg.address = address; + msg.ike_code = notify_code; + plog(ASL_LEVEL_NOTICE, + "sending vpn_control status (peer response) message - code=%d addr=%x.\n", notify_code, address); + + LIST_FOREACH(sock_elem, &lcconf->vpnctl_comm_socks, chain) { + LIST_FOREACH(bound_addr, &sock_elem->bound_addresses, chain) { + if (bound_addr->address == 0xFFFFFFFF || + bound_addr->address == address) { + tlen = send(sock_elem->sock, &msg, sizeof(msg), 0); + if (tlen < 0) { + plog(ASL_LEVEL_ERR, + "unable to send vpn_control status (peer response): %s\n", strerror(errno)); + } else { + rc = 0; + } + break; + } + } + } + + return rc; +} int -vpncontrol_init() +vpncontrol_notify_peer_resp_ph1 (u_int16_t notify_code, phase1_handle_t *iph1) { - if (vpncontrolsock_path == NULL) { - lcconf->sock_vpncontrol = -1; + if (iph1 && iph1->parent_session && iph1->parent_session->controller_awaiting_peer_resp) { + int rc; + if ((rc = vpncontrol_notify_peer_resp(notify_code, iph1_get_remote_v4_address(iph1))) == 0) { + iph1->parent_session->controller_awaiting_peer_resp = 0; + } + return rc; + } else { + return 0; + } +} + +int +vpncontrol_notify_peer_resp_ph2 (u_int16_t notify_code, phase2_handle_t *iph2) +{ + if (iph2 && iph2->parent_session && iph2->parent_session->controller_awaiting_peer_resp) { + int rc; + if ((rc = vpncontrol_notify_peer_resp(notify_code, iph2_get_remote_v4_address(iph2))) == 0) { + iph2->parent_session->controller_awaiting_peer_resp = 0; + } + return rc; + } else { return 0; } +} + +int +vpncontrol_init(void) +{ + int sock; - if ( (lcconf->sock_vpncontrol = checklaunchd()) ){ + if (vpncontrolsock_path == NULL) { + lcconf->sock_vpncontrol = -1; return 0; } - else { - + + if ( (lcconf->sock_vpncontrol = checklaunchd()) == 0 ) { memset(&sunaddr, 0, sizeof(sunaddr)); sunaddr.sun_family = AF_UNIX; snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), @@ -721,15 +968,19 @@ vpncontrol_init() lcconf->sock_vpncontrol = socket(AF_UNIX, SOCK_STREAM, 0); if (lcconf->sock_vpncontrol == -1) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "socket: %s\n", strerror(errno)); return -1; } + if (fcntl(lcconf->sock_vpncontrol, F_SETFL, O_NONBLOCK) == -1) { + plog(ASL_LEVEL_ERR, "failed to put VPN-Control socket in non-blocking mode\n"); + } + unlink(sunaddr.sun_path); if (bind(lcconf->sock_vpncontrol, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) != 0) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "bind(sockname:%s): %s\n", sunaddr.sun_path, strerror(errno)); (void)close(lcconf->sock_vpncontrol); @@ -737,53 +988,77 @@ vpncontrol_init() } if (chown(sunaddr.sun_path, vpncontrolsock_owner, vpncontrolsock_group) != 0) { - plog(LLV_ERROR, LOCATION, NULL, - "chown(%s, %d, %d): %s\n", - sunaddr.sun_path, vpncontrolsock_owner, + plog(ASL_LEVEL_ERR, + "chown(%s, %d, %d): %s\n", + sunaddr.sun_path, vpncontrolsock_owner, vpncontrolsock_group, strerror(errno)); (void)close(lcconf->sock_vpncontrol); return -1; } if (chmod(sunaddr.sun_path, vpncontrolsock_mode) != 0) { - plog(LLV_ERROR, LOCATION, NULL, - "chmod(%s, 0%03o): %s\n", + plog(ASL_LEVEL_ERR, + "chmod(%s, 0%03o): %s\n", sunaddr.sun_path, vpncontrolsock_mode, strerror(errno)); (void)close(lcconf->sock_vpncontrol); return -1; } if (listen(lcconf->sock_vpncontrol, 5) != 0) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "listen(sockname:%s): %s\n", sunaddr.sun_path, strerror(errno)); (void)close(lcconf->sock_vpncontrol); return -1; } - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_NOTICE, "opened %s as racoon management.\n", sunaddr.sun_path); - - return 0; } + lcconf->vpncontrol_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, lcconf->sock_vpncontrol, 0, dispatch_get_main_queue()); + if (lcconf->vpncontrol_source == NULL) { + plog(ASL_LEVEL_ERR, "could not create vpncontrol socket source."); + return -1; + } + dispatch_source_set_event_handler_f(lcconf->vpncontrol_source, vpncontrol_handler); + sock = lcconf->sock_vpncontrol; + dispatch_source_set_cancel_handler(lcconf->vpncontrol_source, + ^{ + close(sock); + }); + dispatch_resume(lcconf->vpncontrol_source); + return 0; } +void +vpncontrol_disconnect_all(struct vpnctl_socket_elem *elem, const char *reason) +{ + struct bound_addr *addr; + struct bound_addr *t_addr; + + plog(ASL_LEVEL_NOTICE, + "received disconnect all command.\n"); + + LIST_FOREACH_SAFE(addr, &elem->bound_addresses, chain, t_addr) { + /* stop any connections */ + vpn_disconnect(addr, reason); + } +} void vpncontrol_close() { - struct vpnctl_socket_elem *elem; + struct vpnctl_socket_elem *elem; struct vpnctl_socket_elem *t_elem; - plog(LLV_DEBUG, LOCATION, NULL, - "vpncontrol_close.\n"); + plog(ASL_LEVEL_NOTICE, + "vpncontrol_close.\n"); - if (lcconf->sock_vpncontrol != -1) { - close(lcconf->sock_vpncontrol); - lcconf->sock_vpncontrol = -1; - } - LIST_FOREACH_SAFE(elem, &lcconf->vpnctl_comm_socks, chain, t_elem) - vpncontrol_close_comm(elem); - + dispatch_source_cancel(lcconf->vpncontrol_source); + lcconf->vpncontrol_source = NULL; + + lcconf->sock_vpncontrol = -1; + LIST_FOREACH_SAFE(elem, &lcconf->vpnctl_comm_socks, chain, t_elem) + vpncontrol_close_comm(elem); } static void @@ -792,12 +1067,20 @@ vpncontrol_close_comm(struct vpnctl_socket_elem *elem) struct bound_addr *addr; struct bound_addr *t_addr; - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_NOTICE, "vpncontrol_close_comm.\n"); LIST_REMOVE(elem, chain); - if (elem->sock != -1) - close(elem->sock); + if (elem->sock != -1) { + dispatch_source_cancel(elem->source); + elem->sock = -1; + } + if (elem->buffer != NULL) { + free(elem->buffer); + elem->buffer = NULL; + elem->pending_bytes_len = 0; + elem->read_bytes_len = 0; + } LIST_FOREACH_SAFE(addr, &elem->bound_addresses, chain, t_addr) { flushsainfo_dynamic(addr->address); LIST_REMOVE(addr, chain); @@ -807,6 +1090,7 @@ vpncontrol_close_comm(struct vpnctl_socket_elem *elem) } racoon_free(elem); check_auto_exit(); + } int