* 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,
* 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:
* 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
#include <sys/stat.h>
#include <sys/un.h>
-#include <System/net/pfkeyv2.h>
+#include <net/pfkeyv2.h>
#include <netinet/in.h>
#ifndef HAVE_NETINET6_IPSEC
#include <netinet/ipsec.h>
-#else
+#else
#include <netinet6/ipsec.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
+#include <launch.h>
+#include <fcntl.h>
#include "var.h"
#include "misc.h"
#include "isakmp.h"
#include "oakley.h"
#include "handler.h"
-#include "evt.h"
#include "pfkey.h"
#include "ipsec_doi.h"
#include "vpn_control.h"
#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;
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 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
-vpncontrol_handler()
+checklaunchd()
+{
+ int returnval = 0;
+ 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;
+ }
+
+ if (listening_fd_array != NULL) {
+ if (fd_count > 0) {
+ returnval = listening_fd_array[0];
+ }
+ free(listening_fd_array);
+ listening_fd_array = NULL;
+ }
+
+ return returnval;
+}
+
+
+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_NOTIFY, 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;
+ }
+
+ 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;
}
- (void)vpncontrol_process(elem, combuf);
+ elem->read_bytes_len += len;
-end:
- if (combuf)
- racoon_free(combuf);
- return 0; // return -1 only if a socket is closed
+ 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_malloc(sizeof(struct bound_addr));
+ 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;
}
+ if (ntohs(pkt->vers_len)) {
+ addr->version = vmalloc(ntohs(pkt->vers_len));
+ if (addr->version == 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));
+ }
addr->address = pkt->address;
LIST_INSERT_HEAD(&elem->bound_addresses, addr, chain);
lcconf->auto_exit_state |= LC_AUTOEXITSTATE_CLIENT; /* client side */
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 ||
pkt->address == addr->address) {
+ flushsainfo_dynamic(addr->address);
LIST_REMOVE(addr, chain);
+ if (addr->version)
+ vfree(addr->version);
racoon_free(addr);
}
}
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) {
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;
break;
case VPNCTL_CMD_PING:
- break; // just reply for now
+ break; /* just reply for now */
+
+ case VPNCTL_CMD_XAUTH_INFO:
+ {
+ 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;
+
+ 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) {
+ /* reply to the last xauth request */
+ attr_list = pkt + 1;
+ error = vpn_xauth_reply(pkt->address, attr_list, ntohs(pkt->hdr.len) - sizeof(u_int32_t));
+ break;
+ }
+ }
+ }
+ 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:
+ {
+ 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;
+
+ 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, VPN_STARTED_BY_API);
+ break;
+ }
+ }
+ }
+ break;
+
+ case VPNCTL_CMD_DISCONNECT:
+ {
+ 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(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, ike_session_stopped_by_vpn_disconnect);
+ break;
+ }
+ }
+ }
+ break;
+
+ case VPNCTL_CMD_START_PH2:
+ {
+ 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(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, combuf_len);
+ break;
+ }
+ }
+ }
+ break;
+
+ case VPNCTL_CMD_START_DPD:
+ {
+ 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(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) {
+ union { // Wcast-align fix - force alignment
+ struct sockaddr_storage ss;
+ struct sockaddr_in addr_in;
+ } daddr;
+
+ 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 = 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;
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;
}
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(phase1_handle_t *iph1, void* attr_list, size_t attr_len)
+{
+ struct vpnctl_status_need_authinfo *msg = NULL;
+ struct vpnctl_socket_elem *sock_elem;
+ struct bound_addr *bound_addr;
+ size_t msg_size;
+ ssize_t tlen;
+ u_int32_t address;
+ void *ptr;
+
+ if (!iph1)
+ goto end;
+
+ 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(ASL_LEVEL_ERR,
+ "unable to allocate space for vpn control message.\n");
+ return -1;
+ }
+ msg->hdr.flags = 0;
+
+ 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));
+ if (!ike_session_is_client_ph1_rekey(iph1)) {
+ msg->hdr.msg_type = htons(VPNCTL_STATUS_NEED_AUTHINFO);
+ } else {
+ msg->hdr.msg_type = htons(VPNCTL_STATUS_NEED_REAUTHINFO);
+ }
+ msg->address = iph1_get_remote_v4_address(iph1);
+ ptr = msg + 1;
+ memcpy(ptr, attr_list, attr_len);
+
+ 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) {
+ 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(ASL_LEVEL_ERR,
+ "failed to send vpn_control need authinfo status: %s\n", strerror(errno));
+ }
+ break;
+ }
+ }
+ }
+
+end:
+ if (msg)
+ racoon_free(msg);
+ return 0;
+}
+
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;
+ 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;
}
msg->from = htons(from);
if (data_len > 0)
memcpy(msg->data, data, data_len);
- plog(LLV_DEBUG, LOCATION, NULL,
- "sending vpn_control ike notify failed message - code=%d from=%s.\n", notify_code,
+ plog(ASL_LEVEL_ERR,
+ "sending vpn_control ike failed message - code=%d from=%s.\n", notify_code,
(from == FROM_LOCAL ? "local" : "remote"));
LIST_FOREACH(sock_elem, &lcconf->vpnctl_comm_socks, chain) {
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;
}
}
}
+
+ if (msg)
+ racoon_free(msg);
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;
+ ssize_t tlen;
+ size_t msg_size;
u_int32_t address;
+
+ 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 {
+ msg = racoon_malloc(msg_size = sizeof(struct vpnctl_status_phase_change));
+ msg->hdr.flags = 0;
+ }
+ if (msg == 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
- return 0; // 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
- return 0; // for now
- msg.hdr.msg_type = htons(start ? VPNCTL_STATUS_PH2_START : VPNCTL_STATUS_PH2_ESTABLISHED);
+ 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
}
- msg.hdr.flags = msg.hdr.cookie = msg.hdr.reserved = msg.hdr.result = 0;
- msg.hdr.len = htons(sizeof(struct vpnctl_status_phase_change) - sizeof(struct vpnctl_hdr));
- msg.address = address;
+ 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;
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(struct vpnctl_status_phase_change), 0);
+ 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;
}
}
+end:
+ if (msg)
+ racoon_free(msg);
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 (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 (vpncontrolsock_path == NULL) {
lcconf->sock_vpncontrol = -1;
return 0;
}
- memset(&sunaddr, 0, sizeof(sunaddr));
- sunaddr.sun_family = AF_UNIX;
- snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path),
- "%s", vpncontrolsock_path);
+ if ( (lcconf->sock_vpncontrol = checklaunchd()) == 0 ) {
+ memset(&sunaddr, 0, sizeof(sunaddr));
+ sunaddr.sun_family = AF_UNIX;
+ snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path),
+ "%s", vpncontrolsock_path);
- lcconf->sock_vpncontrol = socket(AF_UNIX, SOCK_STREAM, 0);
- if (lcconf->sock_vpncontrol == -1) {
- plog(LLV_ERROR, LOCATION, NULL,
- "socket: %s\n", strerror(errno));
- return -1;
- }
+ lcconf->sock_vpncontrol = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (lcconf->sock_vpncontrol == -1) {
+ plog(ASL_LEVEL_ERR,
+ "socket: %s\n", strerror(errno));
+ return -1;
+ }
- unlink(sunaddr.sun_path);
- if (bind(lcconf->sock_vpncontrol, (struct sockaddr *)&sunaddr,
- sizeof(sunaddr)) != 0) {
- plog(LLV_ERROR, LOCATION, NULL,
- "bind(sockname:%s): %s\n",
- sunaddr.sun_path, strerror(errno));
- (void)close(lcconf->sock_vpncontrol);
- 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");
+ }
- 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,
- vpncontrolsock_group, strerror(errno));
- (void)close(lcconf->sock_vpncontrol);
- return -1;
- }
+ unlink(sunaddr.sun_path);
+ if (bind(lcconf->sock_vpncontrol, (struct sockaddr *)&sunaddr,
+ sizeof(sunaddr)) != 0) {
+ plog(ASL_LEVEL_ERR,
+ "bind(sockname:%s): %s\n",
+ sunaddr.sun_path, 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",
- sunaddr.sun_path, vpncontrolsock_mode, strerror(errno));
- (void)close(lcconf->sock_vpncontrol);
- return -1;
- }
+ if (chown(sunaddr.sun_path, vpncontrolsock_owner, vpncontrolsock_group) != 0) {
+ 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 (listen(lcconf->sock_vpncontrol, 5) != 0) {
- plog(LLV_ERROR, LOCATION, NULL,
- "listen(sockname:%s): %s\n",
- sunaddr.sun_path, strerror(errno));
- (void)close(lcconf->sock_vpncontrol);
- return -1;
- }
- plog(LLV_DEBUG, LOCATION, NULL,
- "opened %s as racoon management.\n", sunaddr.sun_path);
+ if (chmod(sunaddr.sun_path, vpncontrolsock_mode) != 0) {
+ plog(ASL_LEVEL_ERR,
+ "chmod(%s, 0%03o): %s\n",
+ sunaddr.sun_path, vpncontrolsock_mode, strerror(errno));
+ (void)close(lcconf->sock_vpncontrol);
+ return -1;
+ }
- return 0;
+ if (listen(lcconf->sock_vpncontrol, 5) != 0) {
+ plog(ASL_LEVEL_ERR,
+ "listen(sockname:%s): %s\n",
+ sunaddr.sun_path, strerror(errno));
+ (void)close(lcconf->sock_vpncontrol);
+ return -1;
+ }
+ plog(ASL_LEVEL_NOTICE,
+ "opened %s as racoon management.\n", sunaddr.sun_path);
+ }
+ 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;
- 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);
+ plog(ASL_LEVEL_NOTICE,
+ "vpncontrol_close.\n");
+
+ 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
{
struct bound_addr *addr;
struct bound_addr *t_addr;
+
+ 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);
+ if (addr->version)
+ vfree(addr->version);
racoon_free(addr);
}
racoon_free(elem);
check_auto_exit();
+
}
int