From 376f28aa4b82ec003c8579968ba42ff3050b10c5 Mon Sep 17 00:00:00 2001 From: "chleun.moon" Date: Fri, 24 Feb 2017 14:36:58 +0900 Subject: [PATCH] Add ipsec plugin Change-Id: Iafe144233e8dd8bc066e00340a5853e4e16d925a Signed-off-by: cheoleun --- Makefile.am | 1 + Makefile.plugins | 25 ++++ configure.ac | 21 +++ packaging/connman.spec | 25 ++++ scripts/ipsec-script.c | 146 +++++++++++++++++++ vpn/plugins/ipsec.c | 363 ++++++++++++++++++++++++++++++++++++++++++++++ vpn/plugins/ipsec.h | 39 +++++ vpn/plugins/vici-client.c | 46 ++++++ vpn/plugins/vici-client.h | 14 ++ 9 files changed, 680 insertions(+) create mode 100755 scripts/ipsec-script.c create mode 100644 vpn/plugins/ipsec.c create mode 100644 vpn/plugins/ipsec.h create mode 100644 vpn/plugins/vici-client.c create mode 100644 vpn/plugins/vici-client.h diff --git a/Makefile.am b/Makefile.am index dd2474c..e287363 100755 --- a/Makefile.am +++ b/Makefile.am @@ -407,6 +407,7 @@ DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles \ --enable-hh2serial-gps \ --enable-openconnect \ --enable-openvpn \ + --enable-ipsec \ --enable-vpnc \ --enable-session-policy-local \ --enable-nmcompat \ diff --git a/Makefile.plugins b/Makefile.plugins index 83ad8fb..c8ae2d1 100755 --- a/Makefile.plugins +++ b/Makefile.plugins @@ -108,6 +108,25 @@ vpn_plugins_openvpn_la_LDFLAGS = $(plugin_ldflags) endif endif +if IPSEC +if IPSEC_BUILTIN +builtin_vpn_modules += ipsec +builtin_vpn_sources += vpn/plugins/ipsec.h vpn/plugins/ipsec.c +builtin_vpn_sources += vpn/plugins/vici-client.h vpn/plugins/vici-client.c +builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h +builtin_vpn_cflags += -DIPSEC=\"@IPSEC@\" +else +vpn_plugin_LTLIBRARIES += vpn/plugins/ipsec.la +vpn_plugin_objects += $(plugins_ipsec_la_OBJECTS) +vpn_plugins_ipsec_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \ + vpn/plugins/ipsec.c +vpn_plugins_ipsec_la_CFLAGS = $(plugin_cflags) -DIPSEC=\"@IPSEC@\" \ + -DVPN_STATEDIR=\""$(vpn_statedir)"\" \ + -DSCRIPTDIR=\""$(build_scriptdir)"\" +vpn_plugins_ipsec_la_LDFLAGS = $(plugin_ldflags) +endif +endif + if VPNC if VPNC_BUILTIN builtin_vpn_modules += vpnc @@ -225,6 +244,12 @@ script_PROGRAMS += scripts/openvpn-script scripts_openvpn_script_LDADD = @DBUS_LIBS@ endif +if IPSEC +script_PROGRAMS += scripts/ipsec-script + +scripts_ipsec_script_LDADD = @DBUS_LIBS@ +endif + if NMCOMPAT builtin_modules += nmcompat builtin_sources += plugins/nmcompat.c diff --git a/configure.ac b/configure.ac index e765625..bd71140 100755 --- a/configure.ac +++ b/configure.ac @@ -107,6 +107,26 @@ fi AM_CONDITIONAL(OPENVPN, test "${enable_openvpn}" != "no") AM_CONDITIONAL(OPENVPN_BUILTIN, test "${enable_openvpn}" = "builtin") +AC_ARG_WITH(ipsec, AC_HELP_STRING([--with-ipsec=PROGRAM], + [specify location of ipsec binary]), [path_ipsec=${withval}]) + +AC_ARG_ENABLE(ipsec, + AC_HELP_STRING([--enable-ipsec], [enable ipsec support]), + [enable_ipsec=${enableval}], [enable_ipsec="no"]) +if (test "${enable_ipsec}" != "no"); then + if (test -z "${path_ipsec}"); then + AC_PATH_PROG(IPSEC, [ipsec], [], $PATH:/sbin:/usr/sbin) + if (test -z "${IPSEC}"); then + AC_MSG_ERROR(ipsec binary not found) + fi + else + IPSEC="${path_ipsec}" + AC_SUBST(IPSEC) + fi +fi +AM_CONDITIONAL(IPSEC, test "${enable_ipsec}" != "no") +AM_CONDITIONAL(IPSEC_BUILTIN, test "${enable_ipsec}" = "builtin") + AC_ARG_WITH(vpnc, AC_HELP_STRING([--with-vpnc=PROGRAM], [specify location of vpnc binary]), [path_vpnc=${withval}]) @@ -402,6 +422,7 @@ fi AM_CONDITIONAL(VPN, test "${enable_openconnect}" != "no" -o \ "${enable_openvpn}" != "no" -o \ + "${enable_ipsec}" != "no" -o \ "${enable_vpnc}" != "no" -o \ "${enable_l2tp}" != "no" -o \ "${enable_pptp}" != "no") diff --git a/packaging/connman.spec b/packaging/connman.spec index b2e1d6a..9923a4d 100755 --- a/packaging/connman.spec +++ b/packaging/connman.spec @@ -1,5 +1,6 @@ %bcond_with connman_openconnect %bcond_without connman_openvpn +%bcond_without connman_ipsec %bcond_without connman_vpnd Name: connman @@ -24,6 +25,9 @@ BuildRequires: openconnect %if %{with connman_openvpn} BuildRequires: openvpn %endif +%if %{with connman_ipsec} +BuildRequires: strongswan +%endif BuildRequires: ca-certificates-devel BuildRequires: readline-devel #%systemd_requires @@ -64,6 +68,16 @@ Requires: openvpn OpenVPN support for Connman. %endif +%if %{with connman_ipsec} +%package plugin-ipsec +Summary: Openvpn Support for Connman +Requires: %{name} = %{version} +Requires: ipsec + +%description plugin-ipsec +OpenVPN support for Connman. +%endif + %if %{with connman_vpnd} %package connman-vpnd Summary: VPN Support for Connman @@ -136,6 +150,9 @@ chmod +x bootstrap %if %{with connman_openvpn} --enable-openvpn \ %endif +%if %{with connman_ipsec} + --enable-ipsec \ +%endif %if 0%{?enable_connman_features} %connman_features \ %endif @@ -274,6 +291,14 @@ systemctl daemon-reload %{_datadir}/dbus-1/system-services/net.connman.vpn.service %endif +%if %{with connman_ipsec} +%files plugin-ipsec +%manifest %{name}.manifest +%{_libdir}/%{name}/plugins-vpn/ipsec.so +%{_libdir}/%{name}/scripts/ipsec-script +%{_datadir}/dbus-1/system-services/net.connman.vpn.service +%endif + %if %{with connman_vpnd} %files connman-vpnd %manifest %{name}.manifest diff --git a/scripts/ipsec-script.c b/scripts/ipsec-script.c new file mode 100755 index 0000000..6ba0d29 --- /dev/null +++ b/scripts/ipsec-script.c @@ -0,0 +1,146 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. + * Copyright (C) 2010,2012-2014 BMW Car IT GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include + +extern char **environ; + +static void print(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vsyslog(LOG_INFO, format, ap); + va_end(ap); +} + +static void append(DBusMessageIter *dict, const char *pattern) +{ + DBusMessageIter entry; + const char *key, *value; + char *delim; + + delim = strchr(pattern, '='); + *delim = '\0'; + + key = pattern; + value = delim + 1; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value); + + dbus_message_iter_close_container(dict, &entry); +} + +int main(int argc, char *argv[]) +{ + DBusConnection *conn; + DBusError error; + DBusMessage *msg; + DBusMessageIter iter, dict; + char **envp, *busname, *interface, *path, *reason; + int ret = 0; + + openlog(basename(argv[0]), LOG_NDELAY | LOG_PID, LOG_DAEMON); + + busname = getenv("CONNMAN_BUSNAME"); + interface = getenv("CONNMAN_INTERFACE"); + path = getenv("CONNMAN_PATH"); + + reason = getenv("script_type"); + + if (!busname || !interface || !path || !reason) { + print("Required environment variables not set; " + "bus=%s iface=%s path=%s reason=%s", + busname, interface, path, reason); + ret = 1; + goto out; + } + dbus_error_init(&error); + + conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (!conn) { + if (dbus_error_is_set(&error)) { + print("%s", error.message); + dbus_error_free(&error); + } else + print("Failed to get on system bus"); + + goto out; + } + + msg = dbus_message_new_method_call(busname, path, + interface, "notify"); + if (!msg) { + dbus_connection_unref(conn); + print("Failed to allocate method call"); + goto out; + } + + dbus_message_set_no_reply(msg, TRUE); + + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &reason, + DBUS_TYPE_INVALID); + + dbus_message_iter_init_append(msg, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + for (envp = environ; envp && *envp; envp++) + append(&dict, *envp); + + dbus_message_iter_close_container(&iter, &dict); + + if (!dbus_connection_send(conn, msg, NULL)) { + print("Failed to send message"); + goto out; + } + + dbus_connection_flush(conn); + + dbus_message_unref(msg); + + dbus_connection_unref(conn); + +out: + closelog(); + + return ret; +} diff --git a/vpn/plugins/ipsec.c b/vpn/plugins/ipsec.c new file mode 100644 index 0000000..7cf0a5c --- /dev/null +++ b/vpn/plugins/ipsec.c @@ -0,0 +1,363 @@ +/* + * + * ConnMan VPN daemon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#define CONNMAN_API_SUBJECT_TO_CHANGE +#include +#include +#include +#include +#include + +#include "../vpn-provider.h" + +#include "vpn.h" +#include "ipsec.h" +#include "vici-client.h" + +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) + +static DBusConnection *connection; + +struct { + const char *cm_opt; + const char *vici_key; + const char *section; +} ipsec_conn_options[] = { + {"IPsec.Version", "version", NULL}, + {"IPsec.LocalAddrs", "local_addrs", NULL}, + {"IPsec.RemoteAddrs", "remote_addrs", NULL}, + {"IPsec.LocalAuth", "auth", "local"}, + {"IPsec.RemoteAuth", "auth", "remote"}, +}; + +/* + * IPsec.LocalID + * IPsec.RemoteTS + */ +struct { + const char *cm_opt; + const char *vici_type; +} ipsec_shared_options[] = { + {"IPsec.LocalXauthID", NULL}, + {"IPsec.XauthSecret", "XAUTH"}, + {"IPsec.IKESecret", "IKE"}, +}; + +struct { + const char *cm_opt; + const char *vici_type; + const char *vici_flag; +} ipsec_cert_options[] = { + {"IPsec.LocalCert", "X509", NULL}, + {"IPsec.RemoteCert", "X509", NULL}, + {"IPsec.CACert", "X509", "CA"}, +}; + + +static int ipsec_notify(DBusMessage *msg, struct vpn_provider *provider) +{ + return 0; +} + +static void vici_destroy_section(struct section* sect) +{ + g_hash_table_destroy(sect->elm); + g_hash_table_destroy(sect->subsection); + g_free(sect); +} + +static void free_section(gpointer data) +{ + struct section* sect = (struct section*)data; + vici_destroy_section(sect); +} + +static struct section* vici_create_section(const char* name) +{ + struct section* sect; + + sect = g_try_new0(struct section, 1); + if (!sect) { + connman_error("Failed to create section"); + return NULL; + } + + sect->name = g_strdup(name); + sect->elm = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + sect->subsection = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_section); + return sect; +} + +static int vici_section_add_kv(struct section* sect, const char* key, const char* value) +{ + if (sect == NULL || key == NULL || value == NULL) { + connman_error("invalid parameter"); + return -1; + } + + g_hash_table_insert(sect->elm, g_strdup(key), g_strdup(value)); + return 0; +} + +static int vici_section_add_subsection(struct section* sect, const char* name, struct section* child) +{ + if (sect == NULL || name == NULL || child == NULL) { + connman_error("invalid parameter"); + return -1; + } + + g_hash_table_insert(sect->subsection, g_strdup(name), child); + return 0; +} + + +static struct section* vici_section_get_subsection(struct section* sect, const char* name) +{ + struct section* sub = g_hash_table_lookup(sect->subsection, name); + if (sub == NULL) { + sub = vici_create_section(name); + vici_section_add_subsection(sect, name, sub); + } + return sub; +} + +static int vici_section_add_element(struct section* sect, const char* key, + const char* value, const char* subsection) +{ + struct section* target = sect; + + if (sect == NULL || key == NULL) { + connman_error("invalid parameter"); + return -1; + } + + if (subsection) + target = vici_section_get_subsection(sect, subsection); + + vici_section_add_kv(target, key, value); + return 0; +} + +static int ipsec_is_same_auth(const char* req, const char* target) +{ + if (req == NULL || target == NULL) + return 0; + return (g_strcmp0(req, target) == 0); +} + +static int vici_load_cert(const char* type, const char* flag, const char* data) +{ + struct section *sect; + sect = vici_create_section(""); + vici_section_add_element(sect, "type", type, NULL); + vici_section_add_element(sect, "flag", flag, NULL); + vici_section_add_element(sect, "data", data, NULL); + + vici_client_send_request(VICI_REQUEST_LOAD_CERT, sect); + + vici_destroy_section(sect); + + return 0; +} + +static int ipsec_load_conn(struct vpn_provider *provider) +{ + const char *key; + const char *value; + const char *subsection; + struct section *sect; + int i; + + value = vpn_provider_get_string(provider, "Name"); + sect = vici_create_section(value); + + for (i = 0; i < (int)ARRAY_SIZE(ipsec_conn_options); i++) { + key = ipsec_conn_options[i].vici_key; + value = vpn_provider_get_string(provider, ipsec_conn_options[i].cm_opt); + subsection = ipsec_conn_options[i].section; + vici_section_add_element(sect, key, value, subsection); + } + + vici_client_send_request(VICI_REQUEST_LOAD_CONN, sect); + + vici_destroy_section(sect); + + return 0; +} + +static int ipsec_load_shared(struct vpn_provider *provider) +{ + const char *type; + const char *data; + const char *owner; + const char *auth_type; + struct section *sect; + + sect = vici_create_section(""); + + auth_type = vpn_provider_get_string(provider, "IPsec.LocalAuth"); + if (ipsec_is_same_auth(auth_type, IPSEC_AUTH_PSK)) { + type = VICI_SHARED_TYPE_PSK; + data = vpn_provider_get_string(provider, "IPsec.IKESecret"); + } else if (ipsec_is_same_auth(auth_type, IPSEC_AUTH_XAUTH)) { + type = VICI_SHARED_TYPE_XAUTH; + data = vpn_provider_get_string(provider, "IPsec.XauthSecret"); + } else { + connman_error("invalid auth type: %s", auth_type); + return -1; + } + + owner = vpn_provider_get_string(provider, "IPsec.LocalXauthID"); + + vici_section_add_element(sect, "type", type, NULL); + vici_section_add_element(sect, "data", data, NULL); + vici_section_add_element(sect, "owner", owner, NULL); + + vici_client_send_request(VICI_REQUEST_LOAD_SHARED, sect); + + vici_destroy_section(sect); + + return 0; +} + +static int ipsec_load_cert(struct vpn_provider *provider) +{ + const char *type; + const char *flag; + const char *data; + const char *auth_type; + int i; + + auth_type = vpn_provider_get_string(provider, "IPsec.LocalAuth"); + if (!ipsec_is_same_auth(auth_type, IPSEC_AUTH_RSA)) { + connman_error("invalid auth type: %s", auth_type); + return -1; + } + + for (i = 0; i < (int)ARRAY_SIZE(ipsec_cert_options); i++) { + type = ipsec_cert_options[i].vici_type;; + flag = ipsec_cert_options[i].vici_flag; + data = vpn_provider_get_string(provider, ipsec_cert_options[i].cm_opt); + vici_load_cert(type, flag, data); + } + + return 0; +} + +static int ipsec_connect(struct vpn_provider *provider, + struct connman_task *task, const char *if_name, + vpn_provider_connect_cb_t cb, const char *dbus_sender, + void *user_data) +{ + int err = 0; + + /* + * Start charon daemon using ipsec script of strongSwan. + */ + connman_task_add_argument(task, "start", NULL); + err = connman_task_run(task, vpn_died, provider, NULL, NULL, NULL); + IPSEC_ERROR_CHECK_GOTO(err, done, "ipsec start failed"); + + /* + * Initialize vici client + */ + err = vici_client_initialize(); + IPSEC_ERROR_CHECK_GOTO(err, done, "failed to initialize vici_client"); + + /* + * Send the load-conn command + */ + err = ipsec_load_conn(provider); + IPSEC_ERROR_CHECK_GOTO(err, done, "load-conn failed"); + + /* + * Send the load-shared command for PSK or XAUTH + */ + err = ipsec_load_shared(provider); + IPSEC_ERROR_CHECK_GOTO(err, done, "load-shared failed"); + + /* + * Send the load-cert command + */ + err = ipsec_load_cert(provider); + IPSEC_ERROR_CHECK_GOTO(err, done, "load-cert failed"); + +done: + if (cb) + cb(provider, user_data, err); + + return err; +} + +static int ipsec_error_code(struct vpn_provider *provider, int exit_code) +{ + return 0; +} + +static int ipsec_save(struct vpn_provider *provider, GKeyFile *keyfile) +{ + return 0; +} + +static void ipsec_disconnect(struct vpn_provider *provider) +{ + int err = 0; + + err = vici_client_deinitialize(); + IPSEC_ERROR_CHECK_RETURN(err, "failed to deinitialize vici_client"); +} + +static struct vpn_driver vpn_driver = { + .flags = VPN_FLAG_NO_TUN, + .notify = ipsec_notify, + .connect = ipsec_connect, + .error_code = ipsec_error_code, + .save = ipsec_save, + .disconnect = ipsec_disconnect, +}; + +static int ipsec_init(void) +{ + connection = connman_dbus_get_connection(); + + return vpn_register("ipsec", &vpn_driver, IPSEC); +} + +static void ipsec_exit(void) +{ + vpn_unregister("ipsec"); + + dbus_connection_unref(connection); +} + +CONNMAN_PLUGIN_DEFINE(ipsec, "IPSec plugin", VERSION, + CONNMAN_PLUGIN_PRIORITY_DEFAULT, ipsec_init, ipsec_exit) diff --git a/vpn/plugins/ipsec.h b/vpn/plugins/ipsec.h new file mode 100644 index 0000000..b9c146d --- /dev/null +++ b/vpn/plugins/ipsec.h @@ -0,0 +1,39 @@ +#ifndef __CONNMAN_VPND_PLUGIN_IPSEC_H +#define __CONNMAN_VPND_PLUGIN_IPSEC_H + +#define IPSEC_AUTH_PSK "PSK" +#define IPSEC_AUTH_RSA "RSA" +#define IPSEC_AUTH_XAUTH "XAUTH" + +#define VICI_SHARED_TYPE_PSK "IKE" +#define VICI_SHARED_TYPE_XAUTH "XAUTH" + +#define IPSEC_ERROR_CHECK_GOTO(err, target, fmt, arg...) do { \ + if (err < 0) { \ + connman_error(fmt, ## arg); \ + err = -1; \ + goto target; \ + } \ +} while (0) + +#define IPSEC_ERROR_CHECK_RETURN(err, fmt, arg...) do { \ + if (err < 0) { \ + connman_error(fmt, ## arg); \ + return; \ + } \ +} while (0) + +#define IPSEC_ERROR_CHECK_RETURN_VAL(err, ret, fmt, arg...) do { \ + if (err < 0) { \ + connman_error(fmt, ## arg); \ + return ret; \ + } \ +} while (0) + +struct section { + const char *name; + GHashTable *elm; + GHashTable *subsection; +}; + +#endif /* __CONNMAN_VPND_PLUGIN_IPSEC_H */ diff --git a/vpn/plugins/vici-client.c b/vpn/plugins/vici-client.c new file mode 100644 index 0000000..e4cfa9a --- /dev/null +++ b/vpn/plugins/vici-client.c @@ -0,0 +1,46 @@ +#include + +#include +#include "ipsec.h" +#include "vici-client.h" + +struct request { +}; + +static struct request* vici_client_create_request(struct section* root) +{ + struct request* req; + + req = g_try_new0(struct req, 1); + if (!req) { + comman_error("Failed to create request"); + return NULL; + } + + return req; +} + +static int vici_client_send_command(struct request* req) +{ + return 0; +} + +int vici_client_initialize() +{ + /* + * Open socket to connect vici plugin + */ + return 0; +} + +int vici_client_deinitialize() +{ + return 0; +} + +int vici_client_send_request(const char* cmd, struct section* root) +{ + struct request* req = vici_client_send_request(root); + vici_client_send_command(req); + return 0; +} diff --git a/vpn/plugins/vici-client.h b/vpn/plugins/vici-client.h new file mode 100644 index 0000000..3a60f31 --- /dev/null +++ b/vpn/plugins/vici-client.h @@ -0,0 +1,14 @@ +#ifndef __VICI_CLIENT_H +#define __VICI_CLIENT_H + +#define VICI_DEFAULT_URI "/var/run/charon.vici" + +#define VICI_REQUEST_LOAD_CONN "load-conn" +#define VICI_REQUEST_LOAD_SHARED "load-shared" +#define VICI_REQUEST_LOAD_CERT "load-cert" + +int vici_client_initialize(); +int vici_client_deinitialize(); +int vici_client_send_request(const char* cmd, struct section* root); + +#endif /* __VICI_CLIENT_H */ -- 2.7.4