Merge branch 'ipsec' into tizen 26/126026/1
authorchleun.moon <chleun.moon@samsung.com>
Thu, 20 Apr 2017 02:00:24 +0000 (11:00 +0900)
committerchleun.moon <chleun.moon@samsung.com>
Thu, 20 Apr 2017 02:00:58 +0000 (11:00 +0900)
Change-Id: I2ecaece689f43bb0c03c9c5929274bfe43e346b0
Signed-off-by: cheoleun <chleun.moon@samsung.com>
15 files changed:
Makefile.am
Makefile.plugins
configure.ac
doc/vpn-config-format.txt
packaging/connman.spec
scripts/ipsec-script.c [new file with mode: 0755]
vpn/plugins/ipsec.c [new file with mode: 0644]
vpn/plugins/ipsec.h [new file with mode: 0644]
vpn/plugins/vici-client.c [new file with mode: 0644]
vpn/plugins/vici-client.h [new file with mode: 0644]
vpn/plugins/vpn.c
vpn/plugins/vpn.h
vpn/vpn-config.c
vpn/vpn-provider.c
vpn/vpn.h

index dd2474c..9fd16a0 100755 (executable)
@@ -155,7 +155,7 @@ vpn_connman_vpnd_SOURCES = $(gdhcp_sources) $(builtin_vpn_sources) \
 
 vpn_connman_vpnd_LDADD = gdbus/libgdbus-internal.la $(builtin_vpn_libadd) \
                                @GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ @GNUTLS_LIBS@ \
-                               @TPKP_GNUTLS_LIBS@ @LIBSYSTEMD_LIBS@\
+                               @TPKP_GNUTLS_LIBS@ @LIBSYSTEMD_LIBS@ \
                                -lresolv -ldl
 
 vpn_connman_vpnd_LDFLAGS = -Wl,--export-dynamic \
@@ -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 \
index 83ad8fb..a745262 100755 (executable)
@@ -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/vici-client.c
+vpn_plugins_ipsec_la_CFLAGS = $(plugin_cflags) -DIPSEC=\"@IPSEC@\" \
+                                       -DVPN_STATEDIR=\""$(vpn_statedir)"\" \
+                                       -DSCRIPTDIR=\""$(build_scriptdir)"\" @GIO_CFLAGS@ @OPENSSL_CFLAGS@
+vpn_plugins_ipsec_la_LDFLAGS = $(plugin_ldflags) @GIO_LIBS@ @OPENSSL_LIBS@
+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
index e765625..cd2013f 100755 (executable)
@@ -107,6 +107,34 @@ 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
+       PKG_CHECK_MODULES(GIO, gio-2.0 >= 2.28, dummy=yes,
+                                       AC_MSG_ERROR(GIO >= 2.28 is required))
+       AC_SUBST(GIO_CFLAGS)
+       AC_SUBST(GIO_LIBS)
+       PKG_CHECK_MODULES(OPENSSL, openssl, dummy=yes,
+                                       AC_MSG_ERROR(openssl library is required))
+       AC_SUBST(OPENSSL_CFLAGS)
+       AC_SUBST(OPENSSL_LIBS)
+       if (test -z "${path_ipsec}"); then
+               AC_PATH_PROG(IPSEC, [charon], [/usr/bin/charon], $PATH:/usr/bin)
+               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 +430,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")
index 23c9c14..b4898eb 100644 (file)
@@ -195,6 +195,35 @@ PPTP VPN supports following options (see pptp(8) and pppd(8) for details)
  PPPD.RequirMPPEStateful mppe-stateful    Allow MPPE to use stateful mode (O)
  PPPD.NoVJ           no-vj-comp           No Van Jacobson compression (O)
 
+IPsec VPN supports following options (see swanctl.conf(5) for details):
+ Option name                   IPSec config value    Description
+ IPsec.Version                 Version               IKE major version to use for connection (M)
+ IPsec.LeftAddrs               local_addrs           Local address(es) to use for IKE communication (M)
+ IPsec.RightAddrs              remote_addrs          Remote address(es) to use for IKE communication (M)
+
+
+ IPsec.LocalAuth               local.auth            Authentication to perform locally (M)
+ IPsec.LocalCerts              local.certs           Certificate candidate to use for authentication (O)
+ IPsec.LocalID                 local.id              IKE identity to use for authentication round (O)
+ IPsec.LocalXauthID            local.xauth_id        Client XAuth username used in the XAuth exchange (O)
+ IPsec.LocalXauthAuth          local-xauth.auth      Xauth round authentication to perform locally (O)
+ IPsec.LocalXauthXauthID       local-xauth.xauth_id  Xauth round client XAuth username used in the XAuth exchange (O)
+
+ IPsec.RemoteAuth              remote.auth           Authentication to expect from remote (M)
+ IPsec.RemoteCerts             remote.certs          Certificate candidate to use for authentication (O)
+ IPsec.RemoteID                remote.id             IKE identity to use for authentication round (O)
+ IPsec.RemoteXauthAuth         remote-xauth.auth     Xauth round authentication to expect from remote (O)
+ IPsec.ChildrenLocalTs         children.local_ts     local selectors to include in CHILD_SA (O)
+ IPsec.ChildrenRemoteTs        children.remote_ts    Remote selectors to include in CHILD_SA (O)
+
+ IPsec.IKEData                 secret.data           IKE PSK raw shared key data
+ IPsec.IKEOwners               secret.Owners         list of shared key owner identities
+ IPsec.XauthData               secret.data           XAUTH raw shared key data
+ IPsec.XauthOwners             secret.Owners         list of shared key owner identities
+
+ IPsec.CertType                cert.type             certificate type, X509|X509_AC|X509_CRL
+ IPsec.CertFlag                cert.flag             X.509 certificate flag, NONE|CA|AA|OCSP
+ IPsec.CertData                cert.data             PEM or DER encoded certificate data
 
 Example
 =======
index 6f12bbb..f9a90f6 100755 (executable)
@@ -1,5 +1,6 @@
 %bcond_with     connman_openconnect
 %bcond_without  connman_openvpn
+%bcond_without  connman_ipsec
 %bcond_without  connman_vpnd
 
 Name:           connman
@@ -13,6 +14,7 @@ Source0:        %{name}-%{version}.tar.gz
 BuildRequires:  systemd-devel
 BuildRequires:  pkgconfig(dbus-1)
 BuildRequires:  pkgconfig(glib-2.0)
+BuildRequires:  pkgconfig(gio-2.0)
 BuildRequires:  pkgconfig(libiptc)
 BuildRequires:  pkgconfig(xtables)
 BuildRequires:  pkgconfig(libsmack)
@@ -24,6 +26,10 @@ BuildRequires:  openconnect
 %if %{with connman_openvpn}
 BuildRequires:  openvpn
 %endif
+%if %{with connman_ipsec}
+BuildRequires:  strongswan
+BuildRequires:  pkgconfig(openssl)
+%endif
 BuildRequires:  ca-certificates-devel
 BuildRequires:  readline-devel
 #%systemd_requires
@@ -64,6 +70,17 @@ Requires:       openvpn
 OpenVPN support for Connman.
 %endif
 
+%if %{with connman_ipsec}
+%package plugin-ipsec
+Summary:        IPsec Support for Connman
+Requires:       %{name} = %{version}
+Requires:       strongswan
+BuildRequires:  pkgconfig(openssl)
+
+%description plugin-ipsec
+OpenVPN support for Connman.
+%endif
+
 %if %{with connman_vpnd}
 %package connman-vpnd
 Summary:        VPN Support for Connman
@@ -136,6 +153,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
@@ -272,6 +292,14 @@ systemctl daemon-reload
 %license COPYING
 %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 (executable)
index 0000000..6ba0d29
--- /dev/null
@@ -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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <libgen.h>
+
+#include <dbus/dbus.h>
+
+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 (file)
index 0000000..5e34770
--- /dev/null
@@ -0,0 +1,1167 @@
+/*
+ *
+ *  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 <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <net/if.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/safestack.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509.h>
+#include <openssl/conf.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/dbus.h>
+#include <connman/ipconfig.h>
+
+#include "../vpn-provider.h"
+
+#include "vpn.h"
+#include "ipsec.h"
+#include "vici-client.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+typedef enum {
+       CERT_TYPE_NONE,
+       CERT_TYPE_DER,
+       CERT_TYPE_PEM,
+       CERT_TYPE_PKCS12,
+       CERT_TYPE_MAX,
+} cert_type_e;
+
+static DBusConnection *connection;
+static VICIClient *vici_client;
+static GFileMonitor* monitor;
+
+struct openssl_private_data {
+       EVP_PKEY *private_key;
+       X509 *local_cert;
+       STACK_OF(X509) *ca_certs;
+};
+
+struct ipsec_private_data {
+       struct vpn_provider *provider;
+       struct openssl_private_data openssl_data;
+       vpn_provider_connect_cb_t cb;
+
+       void *user_data;
+};
+
+struct {
+       const char *cm_opt;
+       const char *vici_key;
+       const char *subsection;
+       vici_add_element add_elem;
+} ipsec_conn_options[] = {
+       {"IPsec.Version", "version", NULL, vici_add_kv},
+       {"IPsec.LeftAddrs", "local_addrs", NULL, vici_add_kvl},
+       {"IPsec.RightAddrs", "remote_addrs", NULL, vici_add_kvl},
+
+       {"IPsec.LocalAuth", "auth", "local", vici_add_kv},
+       {"IPsec.LocalID", "id", "local", vici_add_kv},
+       {"IPsec.LocalXauthID", "xauth_id", "local", vici_add_kv},
+       {"IPsec.LocalXauthAuth", "auth", "local-xauth", vici_add_kv},
+       {"IPsec.LocalXauthXauthID", "xauth_id", "local-xauth", vici_add_kv},
+       {"IPsec.RemoteAuth", "auth", "remote", vici_add_kv},
+       {"IPsec.RemoteID", "id", "remote", vici_add_kv},
+       {"IPsec.RemoteXauthID", "xauth_id", "remote", vici_add_kv},
+       {"IPsec.RemoteXauthAuth", "auth", "remote-xauth", vici_add_kv},
+       {"IPsec.RemoteXauthXauthID", "xauth_id", "remote-xauth", vici_add_kv},
+       {"IPsec.ChildrenLocalTS", "local_ts", "children", vici_add_kvl},
+       {"IPsec.ChildrenRemoteTS", "remote_ts", "children", vici_add_kvl},
+};
+
+struct {
+       const char *cm_opt;
+       const char *vici_type;
+} ipsec_shared_options[] = {
+       {"IPsec.IKEData", "data"},
+       {"IPsec.IKEOwners", "owners"},
+       {"IPsec.XauthData", "data"},
+       {"IPsec.XauthOwners", "owners"},
+};
+
+struct {
+       const char *cm_opt;
+       const char *vici_type;
+       const char *vici_flag;
+} ipsec_cert_options[] = {
+       {"IPsec.CertType", "type", NULL},
+       {"IPsec.CertFlag", "flag", NULL},
+       {"IPsec.CertData", "data", NULL},
+       {"IPsec.CertPass", "data", NULL},
+};
+
+struct {
+       const char *cm_opt;
+       const char *vici_type;
+} ipsec_pkey_options[] = {
+       {"IPsec.PKeyType", "type"},
+       {"IPsec.PKeyData", "data"},
+};
+
+static const char *ikev1_esp_proposals [] ={
+               "aes256-sha256",
+               "aes128-sha256",
+               "aes256-sha1",
+               "aes128-sha1",
+               "aes256-md5",
+               "aes128-md5",
+               "3des-sha1",
+               "3des-md5",
+               NULL,
+};
+
+static const char *ikev1_proposals [] ={
+               "aes256-sha256-modp1024",
+               "aes128-sha256-modp1024",
+               "aes256-sha1-modp1024",
+               "aes128-sha1-modp1024",
+               "aes256-md5-modp1024",
+               "aes128-md5-modp1024",
+               "3des-sha1-modp1024",
+               "3des-md5-modp1024",
+               NULL,
+};
+
+static const char *ikev2_esp_proposals = "aes256-aes128-sha512-sha384-sha256-sha1-modp2048-modp1536-modp1024";
+
+static const char *ikev2_proposals = "aes256-aes128-sha512-sha384-sha256-sha1-modp2048-modp1536-modp1024";
+
+static void init_openssl(void)
+{
+       /* Load the human readable error strings for libcrypto */
+#if OPENSSL_API_COMPAT < 0x10100000L
+       /* TODO :remove this after debug */
+       DBG("openssl version is under 1.01");
+       ERR_load_crypto_strings();
+#else
+       /* As of version 1.1.0 OpenSSL will automatically allocate
+        * all resources that it needs so no explicit initialisation
+        * is required. Similarly it will also automatically
+        * deinitialise as required. */
+       /* OPENSSL_init_crypto(); */
+#endif
+       /* Load all digest and cipher algorithms */
+#if OPENSSL_API_COMPAT < 0x10100000L
+       OpenSSL_add_all_algorithms();
+#else
+        /* As of version 1.1.0 OpenSSL will automatically allocate
+         * all resources that it needs so no explicit initialisation
+         * is required. Similarly it will also automatically
+         * deinitialise as required. */
+        /* OPENSSL_init_crypto(); */
+#endif
+#if OPENSSL_API_COMPAT < 0x10100000L
+       OPENSSL_config(NULL);
+#else
+#endif
+       /* TODO :remove this after debug */
+       DBG("init openssl");
+       return;
+}
+
+static void deinit_openssl(void)
+{
+#if OPENSSL_API_COMPAT < 0x10100000L
+       EVP_cleanup();
+#else
+#endif
+#if OPENSSL_API_COMPAT < 0x10100000L
+       ERR_free_strings();
+#else
+#endif
+       return;
+}
+
+static int print_openssl_error_cb(const char *str, size_t len, void *u)
+{
+       connman_error("%s", str);
+       return 0;
+}
+
+static void print_openssl_error()
+{
+       ERR_print_errors_cb(print_openssl_error_cb, NULL);
+       return;
+}
+
+static int get_cert_type(const char *path)
+{
+       char *down_str = NULL;
+       int cert_type;
+
+       down_str = g_ascii_strdown(path, strlen(path));
+       if (!down_str)
+               return CERT_TYPE_NONE;
+
+       if(g_str_has_suffix(down_str, ".pem"))
+               cert_type = CERT_TYPE_PEM;
+       else if (g_str_has_suffix(down_str, ".der") || g_str_has_suffix(down_str, ".crt"))
+               cert_type = CERT_TYPE_DER;
+       else if (g_str_has_suffix(down_str, ".p12") || g_str_has_suffix(down_str, ".pfx"))
+               cert_type = CERT_TYPE_PKCS12;
+       else
+               cert_type = CERT_TYPE_NONE;
+       g_free(down_str);
+
+       return cert_type;
+}
+
+static void read_der_file(const char *path, X509 **cert)
+{
+       FILE *fp = NULL;
+
+       DBG("der path %s\n", path);
+       fp = fopen(path, "r");
+       *cert = d2i_X509_fp(fp, NULL);
+       fclose(fp);
+       return;
+}
+
+static void read_pem_file(const char *path, X509 **cert)
+{
+       FILE *fp = NULL;
+
+       DBG("pem path %s\n", path);
+       fp = fopen(path, "r");
+       *cert = PEM_read_X509(fp, cert, NULL, NULL);
+       fclose(fp);
+       return;
+}
+
+static void read_pkcs12_file(const char *path, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca)
+{
+       FILE *fp = NULL;
+       PKCS12 *p12;
+
+       DBG("pkcs12 path %s\n", path);
+       fp = fopen(path, "r");
+
+       p12 = d2i_PKCS12_fp(fp, NULL);
+       if (!p12) {
+               print_openssl_error();
+               fclose(fp);
+               return;
+       }
+
+       if (!PKCS12_parse(p12, pass, pkey, cert, ca)) {
+               print_openssl_error();
+               fclose(fp);
+               return;
+       }
+
+       PKCS12_free(p12);
+       return;
+}
+
+static char *get_private_key_str(struct openssl_private_data *data)
+{
+       EVP_PKEY *pkey;
+       BIO* bio;
+       BUF_MEM *buf_ptr;
+       char *private_key_str = NULL;
+
+       if (!data)
+               return NULL;
+
+       if (!(pkey = data->private_key))
+               return NULL;
+
+       bio = BIO_new(BIO_s_mem());
+       if (!bio) {
+               print_openssl_error();
+               return NULL;
+       }
+
+       if (!PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL)) {
+               print_openssl_error();
+               BIO_free(bio);
+               return NULL;
+       }
+
+       BIO_get_mem_ptr(bio, &buf_ptr);
+       if (!buf_ptr) {
+               print_openssl_error();
+               BIO_free(bio);
+               return NULL;
+       }
+
+       private_key_str = g_try_malloc0(buf_ptr->length + 1);
+       if (!private_key_str) {
+               print_openssl_error();
+               BIO_free(bio);
+               return NULL;
+       }
+
+       g_strlcpy(private_key_str, buf_ptr->data, buf_ptr->length);
+
+       BIO_free(bio);
+
+       return private_key_str;
+}
+
+static char *get_cert_str(X509 *cert)
+{
+       BIO* bio;
+       BUF_MEM *buf_ptr;
+       char *cert_str = NULL;
+
+       if (!cert)
+               return NULL;
+
+       bio = BIO_new(BIO_s_mem());
+       if (!bio) {
+               print_openssl_error();
+               return NULL;
+       }
+
+       if (!PEM_write_bio_X509_AUX(bio, cert)) {
+               print_openssl_error();
+               BIO_free(bio);
+               return NULL;
+       }
+
+       BIO_get_mem_ptr(bio, &buf_ptr);
+       if (!buf_ptr) {
+               print_openssl_error();
+               BIO_free(bio);
+               return NULL;
+       }
+
+       cert_str = g_try_malloc0(buf_ptr->length + 1);
+       if (!cert_str) {
+               print_openssl_error();
+               BIO_free(bio);
+               return NULL;
+       }
+
+       g_strlcpy(cert_str, buf_ptr->data, buf_ptr->length);
+
+       BIO_free(bio);
+       return cert_str;
+}
+
+static char * get_local_cert_str(struct openssl_private_data *data)
+{
+       if (!data)
+               return NULL;
+
+       if (!(data->local_cert))
+               return NULL;
+
+       return get_cert_str(data->local_cert);
+}
+
+int get_ca_cert_num(struct openssl_private_data *data)
+{
+       if(!data || !data->ca_certs)
+               return 0;
+
+       return sk_X509_num(data->ca_certs);
+}
+
+static char * get_nth_ca_cert_str(struct openssl_private_data *data, int num)
+{
+       X509 *cert = NULL;
+
+       if (!data)
+               return NULL;
+
+       if (!(data->ca_certs))
+               return NULL;
+
+       cert = sk_X509_value(data->ca_certs, num);
+
+       return get_cert_str(cert);
+}
+
+static void extract_cert_info(const char *path, const char *pass, struct openssl_private_data *data)
+{
+       if(!path || !data) {
+               /* TODO :remove this after debug */
+               DBG("there's no cert data");
+               return;
+       }
+
+       switch (get_cert_type(path)) {
+       case CERT_TYPE_DER:
+               read_der_file(path, &(data->local_cert));
+               break;
+       case CERT_TYPE_PEM:
+               read_pem_file(path, &(data->local_cert));
+               break;
+       case CERT_TYPE_PKCS12:
+               read_pkcs12_file(path, pass, &(data->private_key), &(data->local_cert), &(data->ca_certs));
+               break;
+       default:
+               break;
+       }
+
+       return;
+}
+
+static void free_openssl_private_data(struct openssl_private_data *data)
+{
+       if (!data)
+               return;
+
+       EVP_PKEY *private_key = data->private_key;
+       X509 *local_cert = data->local_cert;
+       STACK_OF(X509) *ca_certs = data->ca_certs;
+
+       if (private_key)
+               EVP_PKEY_free(private_key);
+
+       if (local_cert)
+               X509_free(local_cert);
+
+       if (ca_certs)
+               sk_X509_pop_free(ca_certs, X509_free);
+
+       return;
+}
+
+static void free_private_data(struct ipsec_private_data *data)
+{
+       free_openssl_private_data(&(data->openssl_data));
+       deinit_openssl();
+       g_free(data);
+}
+
+static int ipsec_notify(DBusMessage *msg, struct vpn_provider *provider)
+{
+       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)
+{
+       VICISection *sect;
+       int ret = 0;
+
+       sect = vici_create_section(NULL);
+       if (!sect)
+               return -ENOMEM;
+
+       vici_add_kv(sect, "type", type, NULL);
+       vici_add_kv(sect, "flag", flag, NULL);
+       vici_add_kv(sect, "data", data, NULL);
+
+       ret = vici_send_request(vici_client, VICI_CMD_LOAD_CERT, sect);
+       if (ret < 0)
+               connman_error("vici_send_request failed");
+
+       vici_destroy_section(sect);
+
+       return ret;
+}
+
+static int vici_load_key(const char* type, const char* data)
+{
+       VICISection *sect;
+       sect = vici_create_section(NULL);
+       int ret = 0;
+
+       vici_add_kv(sect, "type", type, NULL);
+       vici_add_kv(sect, "data", data, NULL);
+       ret = vici_send_request(vici_client, VICI_CMD_LOAD_KEY, sect);
+       if (ret < 0)
+               connman_error("vici_send_request failed");
+
+       vici_destroy_section(sect);
+
+       return ret;
+}
+
+static void ipsec_add_default_child_sa_data(struct vpn_provider *provider, VICISection *child)
+{
+       const char *version = vpn_provider_get_string(provider, "IPsec.Version");
+       if (g_strcmp0(version, "1") == 0) {
+               int i = 0;
+               GSList *list;
+
+               for (list = NULL; ikev1_esp_proposals[i] != NULL; i++)
+                       list = g_slist_append(list, g_strdup(ikev1_esp_proposals[i]));
+               vici_add_list(child, "esp_proposals", list, "net");
+               list = NULL;
+       } else {
+               vici_add_kvl(child, "esp_proposals", ikev2_esp_proposals, "net");
+       }
+       return;
+}
+
+static void ipsec_add_default_conn_data(struct vpn_provider *provider, VICISection *conn)
+{
+       const char *version = vpn_provider_get_string(provider, "IPsec.Version");
+       if (g_strcmp0(version, "1") == 0) {
+               int i = 0;
+               GSList *list;
+
+               for (list = NULL; ikev1_proposals[i] != NULL; i++)
+                       list = g_slist_append(list, g_strdup(ikev1_proposals[i]));
+               vici_add_list(conn, "proposals", list, NULL);
+               list = NULL;
+
+               if (g_strcmp0(vpn_provider_get_string(provider, "IPsec.LocalAuth"), "psk") == 0)
+                       vici_add_kv(conn, "aggressive", "yes", NULL);
+
+       } else {
+               vici_add_kvl(conn, "proposals", ikev2_proposals, NULL);
+       }
+
+       vici_add_kvl(conn, "vips", "0.0.0.0", NULL);
+       return;
+}
+
+
+static int ipsec_load_conn(struct vpn_provider *provider, struct ipsec_private_data *data)
+{
+       const char *key;
+       const char *value;
+       const char *subsection;
+       char *local_cert_str;
+       VICISection *conn;
+       VICISection *children;
+       int i;
+       int ret = 0;
+
+       if (!provider || !data) {
+               connman_error("invalid provider or data");
+               return -EINVAL;
+       }
+
+       value = vpn_provider_get_string(provider, "Name");
+       DBG("Name: %s", value);
+       conn = vici_create_section(value);
+       children = vici_create_section("children");
+       add_subsection("children", children, conn);
+
+       for (i = 0; i < (int)ARRAY_SIZE(ipsec_conn_options); i++) {
+               value = vpn_provider_get_string(provider, ipsec_conn_options[i].cm_opt);
+               if (!value)
+                       continue;
+
+               key = ipsec_conn_options[i].vici_key;
+               subsection = ipsec_conn_options[i].subsection;
+               ipsec_conn_options[i].add_elem(conn, key, value, subsection);
+       }
+
+       local_cert_str = get_local_cert_str(&(data->openssl_data));
+       if (local_cert_str) {
+               /* TODO :remove this after debug */
+               DBG("There's local certification to add local section");
+               vici_add_kvl(conn, "certs", local_cert_str, "local");
+               g_free(local_cert_str);
+       }
+
+       ipsec_add_default_conn_data(provider, conn);
+       ipsec_add_default_child_sa_data(provider, children);
+
+       ret = vici_send_request(vici_client, VICI_CMD_LOAD_CONN, conn);
+       if (ret < 0)
+               connman_error("vici_send_request failed");
+
+       vici_destroy_section(conn);
+
+       return ret;
+}
+
+static int ipsec_load_private_data(struct ipsec_private_data *data)
+{
+       char *private_key_str;
+       char *ca_cert_str;
+       int ca_cert_num;
+       int i;
+       int ret = 0;
+
+       private_key_str = get_private_key_str(&(data->openssl_data));
+       if (private_key_str) {
+               /* TODO :remove this after debug */
+               DBG("load private key");
+               ret = vici_load_key("RSA", private_key_str);
+               g_free(private_key_str);
+       }
+
+       if (ret < 0)
+               return ret;
+
+       ca_cert_num = get_ca_cert_num(&(data->openssl_data));
+       if (ca_cert_num < 1)
+               return 0;
+
+       for (i = 0; i < ca_cert_num; i++) {
+               /* TODO :remove this after debug */
+               DBG("load CA cert");
+               ca_cert_str = get_nth_ca_cert_str(&(data->openssl_data), i);
+               ret = vici_load_cert("X509", "CA", ca_cert_str);
+               g_free(ca_cert_str);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static int ipsec_load_shared_psk(struct vpn_provider *provider)
+{
+       const char *data;
+       const char *owner;
+       VICISection *sect;
+       int ret = 0;
+
+       if (!provider) {
+               connman_error("invalid provider");
+               ret = -EINVAL;
+       }
+
+       data = vpn_provider_get_string(provider, "IPsec.IKEData");
+       owner = vpn_provider_get_string(provider, "IPsec.IKEOwners");
+       DBG("IKEData: %s, IKEOwners: %s", data, owner);
+
+       if (!data)
+               return 0;
+
+       sect = vici_create_section(NULL);
+       if (!sect) {
+               return -ENOMEM;
+       }
+
+       vici_add_kv(sect, "type", VICI_SHARED_TYPE_PSK, NULL);
+       vici_add_kv(sect, "data", data, NULL);
+       vici_add_kvl(sect, "owners", owner, NULL);
+
+       ret = vici_send_request(vici_client, VICI_CMD_LOAD_SHARED, sect);
+       if (ret < 0)
+               connman_error("vici_send_request failed");
+
+       vici_destroy_section(sect);
+
+       return ret;
+}
+
+static int ipsec_load_shared_xauth(struct vpn_provider *provider)
+{
+       const char *data;
+       const char *owner;
+       VICISection *sect;
+       int ret = 0;
+
+       if (!provider) {
+               connman_error("invalid provider");
+               return -EINVAL;
+       }
+
+       data = vpn_provider_get_string(provider, "IPsec.XauthData");
+       owner = vpn_provider_get_string(provider, "IPsec.XauthOwners");
+       DBG("XauthData: %s, XauthOwners: %s", data, owner);
+
+       if (!data)
+               return 0;
+
+       sect = vici_create_section(NULL);
+
+       vici_add_kv(sect, "type", VICI_SHARED_TYPE_XAUTH, NULL);
+       vici_add_kv(sect, "data", data, NULL);
+       vici_add_kvl(sect, "owners", owner, NULL);
+
+       ret = vici_send_request(vici_client, VICI_CMD_LOAD_SHARED, sect);
+       if (ret < 0)
+               connman_error("vici_send_request failed");
+
+       vici_destroy_section(sect);
+
+       return ret;
+}
+
+static char *load_file_from_path(const char *path)
+{
+       struct stat st;
+       FILE *fp = NULL;
+       int fd = 0;
+       unsigned long long file_size = 0;
+       char *file_buff = NULL;
+
+       if (file_buff) {
+               connman_error("File path is NULL\n");
+               return NULL;
+       }
+
+       fp = fopen(path, "rb");
+       if (!fp) {
+               connman_error("fopen %s is failed\n", path);
+               return NULL;
+       }
+
+       fd = fileno(fp);
+       fstat(fd, &st);
+       file_size = st.st_size;
+       file_buff = g_try_malloc0(sizeof(char)*st.st_size);
+       if (file_buff == NULL) {
+               connman_error("g_try_malloc0 failed\n");
+               fclose(fp);
+               return NULL;
+       }
+
+       if (fread(file_buff, 1, file_size, fp) != file_size) {
+               connman_error("file size not matched\n");
+               g_free(file_buff);
+               file_buff = NULL;
+       }
+
+       fclose(fp);
+       return file_buff;
+}
+
+static int ipsec_load_key(struct vpn_provider *provider)
+{
+       const char *type;
+       const char *path;
+       char *data;
+       VICISection *sect;
+       int ret = 0;
+
+       if (!provider) {
+               connman_error("invalid provider");
+               return -EINVAL;
+       }
+
+       type = vpn_provider_get_string(provider, "IPsec.PKeyType");
+       path = vpn_provider_get_string(provider, "IPsec.PKeyData");
+       DBG("PKeyType: %s, PKeyData: %s", type, path);
+
+       if (!type || !path)
+               return 0;
+
+       data = load_file_from_path(path);
+       if (!data)
+               return 0;
+
+       sect = vici_create_section(NULL);
+       if (!sect)
+               return -ENOMEM;
+
+       vici_add_kv(sect, "type", type, NULL);
+       vici_add_kv(sect, "data", data, NULL);
+
+       ret = vici_send_request(vici_client, VICI_CMD_LOAD_KEY, sect);
+       if (ret < 0)
+               connman_error("vici_send_request failed");
+
+       vici_destroy_section(sect);
+       g_free(data);
+
+       return ret;
+}
+
+static int ipsec_initiate(struct vpn_provider *provider)
+{
+       VICISection *sect;
+       int ret = 0;
+
+       sect = vici_create_section(NULL);
+       if (!sect)
+               return -ENOMEM;
+
+       vici_add_kv(sect, "child", "net", NULL);
+       ret = vici_send_request(vici_client, VICI_CMD_INITIATE, sect);
+       if (ret < 0)
+               connman_error("vici_send_request failed");
+
+       vici_destroy_section(sect);
+
+       return ret;
+}
+
+static int ipsec_load_cert(struct vpn_provider *provider)
+{
+       const char *type;
+       const char *flag;
+       char *data;
+       const char *local_auth_type;
+       const char *remote_auth_type;
+       int ret = 0;
+
+       if (!provider) {
+               connman_error("invalid provider");
+               return -EINVAL;
+       }
+
+       local_auth_type = vpn_provider_get_string(provider, "IPsec.LocalAuth");
+       remote_auth_type = vpn_provider_get_string(provider, "IPsec.RemoteAuth");
+       if (!ipsec_is_same_auth(local_auth_type, "pubkey") &&
+                       !ipsec_is_same_auth(remote_auth_type, "pubkey")) {
+               DBG("invalid auth type");
+               return 0;
+       }
+
+       type = vpn_provider_get_string(provider, "IPsec.CertType");
+       flag = vpn_provider_get_string(provider, "IPsec.CertFlag");
+       data = load_file_from_path(vpn_provider_get_string(provider, "IPsec.CertData"));
+       DBG("CertType: %s, CertFalg: %s,CertData: %s", type, flag, data);
+
+       ret = vici_load_cert(type, flag, data);
+       if (ret < 0)
+               connman_error("failed to load cert");
+
+       g_free(data);
+
+       return ret;
+}
+
+void connect_reply_cb(int err, void *user_data)
+{
+       struct ipsec_private_data *data;
+
+       data = (struct ipsec_private_data *)user_data;
+       data->cb(data->provider, data->user_data, err);
+
+       free_private_data(data);
+}
+
+static struct ipsec_private_data* create_ipsec_private_data(struct vpn_provider *provider,
+               vpn_provider_connect_cb_t cb, void* user_data)
+{
+       struct ipsec_private_data *data;
+       data = g_try_new0(struct ipsec_private_data, 1);
+       if (!data) {
+               connman_error("out of memory");
+               return NULL;
+       }
+
+       init_openssl();
+
+       data->provider = provider;
+       data->cb = cb;
+       data->user_data = user_data;
+       return data;
+}
+
+static void vici_connect(struct ipsec_private_data *data)
+{
+       struct vpn_provider *provider = NULL;
+       vpn_provider_connect_cb_t cb = NULL;
+       int err = 0;
+
+       if (!data)
+               IPSEC_ERROR_CHECK_GOTO(-1, done, "Invalid data parameter");
+
+       provider = data->provider;
+       cb = data->cb;
+       if (!provider || !cb)
+               IPSEC_ERROR_CHECK_GOTO(-1, done, "Invalid provider or callback");
+
+       DBG("data %p, provider %p", data, provider);
+
+       /*
+        * Initialize vici client
+        */
+       err = vici_initialize(&vici_client);
+       IPSEC_ERROR_CHECK_GOTO(err, done, "failed to initialize vici_client");
+
+       /* TODO :remove this after debug */
+       DBG("success to initialize vici socket");
+
+       vici_set_connect_reply_cb(vici_client, (vici_connect_reply_cb)connect_reply_cb, data);
+
+       /*
+        * Send the load-conn command
+        */
+       err = ipsec_load_conn(provider, data);
+       IPSEC_ERROR_CHECK_GOTO(err, done, "load-conn failed");
+
+       /* TODO :remove this after debug */
+       DBG("success to ipsec_load_conn");
+
+       err = ipsec_load_private_data(data);
+       IPSEC_ERROR_CHECK_GOTO(err, done, "load private data failed");
+
+       /* TODO :remove this after debug */
+       DBG("success to ipsec_load_private_data");
+
+       /*
+        * Send the load-shared command for PSK
+        */
+       err = ipsec_load_shared_psk(provider);
+       IPSEC_ERROR_CHECK_GOTO(err, done, "load-shared failed");
+
+       /* TODO :remove this after debug */
+       DBG("success to ipsec_load_shared_psk");
+
+       /*
+        * Send the load-shared command for XAUTH
+        */
+       err = ipsec_load_shared_xauth(provider);
+       IPSEC_ERROR_CHECK_GOTO(err, done, "load-shared failed");
+
+       /* TODO :remove this after debug */
+       DBG("success to ipsec_load_shared_xauth");
+       /*
+        * Send the load-cert command
+        */
+       err = ipsec_load_cert(provider);
+       IPSEC_ERROR_CHECK_GOTO(err, done, "load-cert failed");
+
+       /* TODO :remove this after debug */
+       DBG("success to ipsec_load_cert");
+
+       /*
+        * Send the load-key command
+        */
+       err = ipsec_load_key(provider);
+       IPSEC_ERROR_CHECK_GOTO(err, done, "load-key failed");
+
+       /* TODO :remove this after debug */
+       DBG("success to ipsec_load_cert");
+       /*
+        * Send the initiate command
+        */
+       err = ipsec_initiate(provider);
+       IPSEC_ERROR_CHECK_GOTO(err, done, "initiate failed");
+
+       /* TODO :remove this after debug */
+       DBG("success to ipsec_initiate");
+
+done:
+       /* refer to connect_cb on vpn-provider.c for cb */
+       if(err != 0 && cb)
+               cb(provider, data->user_data, -err);
+
+       return;
+}
+
+static void monitor_changed(GFileMonitor *monitor, GFile *file, GFile *other_file,
+               GFileMonitorEvent  event_type, gpointer user_data)
+{
+       DBG("file %s", g_file_get_path(file));
+       if (event_type == G_FILE_MONITOR_EVENT_CREATED) {
+               if (g_file_test(VICI_DEFAULT_URI, G_FILE_TEST_EXISTS)) {
+                       DBG("file created: %s", VICI_DEFAULT_URI);
+                       struct ipsec_private_data *data = user_data;
+                       vici_connect(data);
+                       g_object_unref(monitor);
+               }
+       }
+}
+
+static void monitor_vici_socket(struct ipsec_private_data *data)
+{
+       GError *error = NULL;
+       GFile* file;
+
+       file = g_file_new_for_path(VICI_DEFAULT_URI);
+       monitor = g_file_monitor_file(file, G_FILE_MONITOR_SEND_MOVED, NULL, &error);
+       if (error) {
+               connman_error("g_file_monitor_directory failed: %s / %d", error->message, error->code);
+               g_error_free(error);
+               return;
+       }
+       /* TODO :remove this after debug */
+       DBG("starting to monitor vici socket");
+       g_signal_connect(monitor, "changed", G_CALLBACK(monitor_changed), data);
+       g_object_unref(file);
+}
+
+static void check_vici_socket(struct ipsec_private_data *data)
+{
+       DBG("data %p", data);
+       if (g_file_test(VICI_DEFAULT_URI, G_FILE_TEST_EXISTS)) {
+               DBG("file exists: %s", VICI_DEFAULT_URI);
+               vici_connect(data);
+       } else {
+               monitor_vici_socket(data);
+       }
+}
+
+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)
+{
+       struct ipsec_private_data *data;
+       const char *path;
+       const char *pass;
+       int err = 0;
+
+       data = create_ipsec_private_data(provider, cb, user_data);
+       /*
+        * Start charon daemon using ipsec script of strongSwan.
+        */
+       err = connman_task_run(task, vpn_died, provider, NULL, NULL, NULL);
+       if (err < 0) {
+               connman_error("charon start failed");
+               if (cb)
+                       cb(provider, user_data, err);
+               return err;
+       }
+
+       path = vpn_provider_get_string(provider, "IPsec.LocalCerts");
+       pass = vpn_provider_get_string(provider, "IPsec.LocalCertPass");
+       extract_cert_info(path, pass, &(data->openssl_data));
+
+       check_vici_socket(data);
+//     g_usleep(G_USEC_PER_SEC);
+
+       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)
+{
+       int i;
+       const char *option;
+
+       DBG("");
+       /*
+        * Save IKE connection configurations
+        */
+       for (i = 0; i < (int)ARRAY_SIZE(ipsec_conn_options); i++) {
+               option = vpn_provider_get_string(provider, ipsec_conn_options[i].cm_opt);
+               if (option)
+                       g_key_file_set_string(keyfile,
+                                       vpn_provider_get_save_group(provider),
+                                       ipsec_conn_options[i].cm_opt,
+                                       option);
+       }
+
+       /*
+        * Save shared IKE PSK, EAP or XAUTH secret
+        */
+       for (i = 0; i < (int)ARRAY_SIZE(ipsec_shared_options); i++) {
+               option = vpn_provider_get_string(provider, ipsec_shared_options[i].cm_opt);
+               if (option)
+                       g_key_file_set_string(keyfile,
+                                       vpn_provider_get_save_group(provider),
+                                       ipsec_shared_options[i].cm_opt,
+                                       option);
+       }
+
+       /*
+        * Save certification
+        */
+       for (i = 0; i < (int)ARRAY_SIZE(ipsec_cert_options); i++) {
+               option = vpn_provider_get_string(provider, ipsec_cert_options[i].cm_opt);
+               if (option)
+                       g_key_file_set_string(keyfile,
+                                       vpn_provider_get_save_group(provider),
+                                       ipsec_cert_options[i].cm_opt,
+                                       option);
+       }
+
+       /*
+        * Save private key
+        */
+       for (i = 0; i < (int)ARRAY_SIZE(ipsec_pkey_options); i++) {
+               option = vpn_provider_get_string(provider, ipsec_pkey_options[i].cm_opt);
+               if (option)
+                       g_key_file_set_string(keyfile,
+                                       vpn_provider_get_save_group(provider),
+                                       ipsec_pkey_options[i].cm_opt,
+                                       option);
+       }
+
+       /*
+        * Save local certification
+        */
+       option = vpn_provider_get_string(provider, "IPsec.LocalCerts");
+       if (option)
+               g_key_file_set_string(keyfile,
+                               vpn_provider_get_save_group(provider),
+                               "IPsec.LocalCerts",
+                               option);
+       option = vpn_provider_get_string(provider, "IPsec.LocalCertPass");
+       if (option)
+               g_key_file_set_string(keyfile,
+                               vpn_provider_get_save_group(provider),
+                               "IPsec.LocalCertPass",
+                               option);
+       /*
+        * Save CA certification directory
+        */
+       option = vpn_provider_get_string(provider, "IPsec.CACertsDir");
+       if (option)
+               g_key_file_set_string(keyfile,
+                               vpn_provider_get_save_group(provider),
+                               "IPsec.CACertsDir",
+                               option);
+
+       return 0;
+}
+
+static void ipsec_disconnect(struct vpn_provider *provider)
+{
+       int err = 0;
+
+       err = vici_deinitialize(vici_client);
+       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 (file)
index 0000000..6fe50a0
--- /dev/null
@@ -0,0 +1,32 @@
+#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); \
+               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)
+
+#endif /* __CONNMAN_VPND_PLUGIN_IPSEC_H */
diff --git a/vpn/plugins/vici-client.c b/vpn/plugins/vici-client.c
new file mode 100644 (file)
index 0000000..6513367
--- /dev/null
@@ -0,0 +1,1090 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+
+#include <connman/log.h>
+#include "ipsec.h"
+#include "vici-client.h"
+
+#define SOCK_FD_MIN 3
+#define VICI_REQUEST_TIMEOUT 5000
+
+enum vici_element {
+       VICI_END = 0,
+       VICI_SECTION_START = 1,
+       VICI_SECTION_END = 2,
+       VICI_KEY_VALUE = 3,
+       VICI_LIST_START = 4,
+       VICI_LIST_ITEM = 5,
+       VICI_LIST_END = 6,
+};
+
+enum vici_packet_type {
+       VICI_CMD_REQUEST = 0,
+       VICI_CMD_RESPONSE = 1,
+       VICI_CMD_UNKNOWN = 2,
+       VICI_EVENT_REGISTER = 3,
+       VICI_EVENT_UNREGISTER = 4,
+       VICI_EVENT_CONFIRM = 5,
+       VICI_EVENT_UNKNOWN = 6,
+       VICI_EVENT = 7,
+};
+
+static const char *vici_cmd_str[] = {
+       "load-conn",
+       "load-shared",
+       "load-cert",
+       "load-authority",
+       "unload-authority",
+       "load-key",
+       "initiate",
+       NULL,
+};
+
+struct request {
+       unsigned int allocated;
+       unsigned int used;
+       unsigned int hdr_len;
+       char *sndbuf;
+       int cmd;
+       int err;
+       /* process reply */
+       unsigned int rcv_pkt_size;
+       char *rcvbuf;
+       /* davici_cb cb; */
+       void *user;
+};
+
+struct _VICIClient {
+       /* io data */
+       int client_sock_fd;
+       int client_watch;
+       GSList *request_list;
+       vici_connect_reply_cb reply;
+       void *ipsec_user_data;
+};
+
+struct _VICISection {
+       char *name;
+       GHashTable *kvs;
+       GHashTable *kvls;
+       GHashTable *subsection;
+};
+
+static void remove_list(gpointer data)
+{
+       if (data == NULL)
+               return;
+
+       g_slist_free_full((GSList *)data, g_free);
+}
+
+void vici_destroy_section(VICISection* section)
+{
+       g_free(section->name);
+       g_hash_table_destroy(section->kvs);
+       g_hash_table_destroy(section->kvls);
+       g_hash_table_destroy(section->subsection);
+       g_free(section);
+}
+
+static void free_section(gpointer data)
+{
+       VICISection* section = (VICISection*)data;
+       vici_destroy_section(section);
+}
+
+VICISection* vici_create_section(const char* name)
+{
+       VICISection* section;
+
+       section = g_try_new0(VICISection, 1);
+       if (!section) {
+               connman_error("Failed to create section");
+               return NULL;
+       }
+
+       if (name)
+               section->name = g_strdup(name);
+       section->kvs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+       section->kvls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, remove_list);
+       section->subsection = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_section);
+       return section;
+}
+
+int add_subsection(const char* name, VICISection* child, VICISection* section)
+{
+       if (section == NULL || name == NULL || child == NULL) {
+               connman_error("invalid parameter");
+               return -1;
+       }
+
+       g_hash_table_insert(section->subsection, g_strdup(name), child);
+       return 0;
+}
+
+static int add_kvl_to_section(const char* key, const char* value, VICISection* section)
+{
+       GSList *list = NULL;
+       if (section == NULL || key == NULL || value == NULL) {
+               connman_error("invalid parameter");
+               return -1;
+       }
+
+       list = g_hash_table_lookup(section->kvls, key);
+       if (list == NULL)
+               list = g_slist_alloc();
+
+       list = g_slist_prepend(list, g_strdup(value));
+       g_hash_table_replace(section->kvls, g_strdup(key), list);
+       return 0;
+}
+
+static int add_kv_to_section(const char* key, const char* value, VICISection* section)
+{
+       if (section == NULL || key == NULL || value == NULL) {
+               connman_error("invalid parameter");
+               return -1;
+       }
+
+       g_hash_table_insert(section->kvs, g_strdup(key), g_strdup(value));
+       return 0;
+}
+
+static VICISection* get_subsection(VICISection* section, const char* name)
+{
+       VICISection* sub = g_hash_table_lookup(section->subsection, name);
+       if (sub == NULL) {
+               sub = vici_create_section(name);
+               add_subsection(name, sub, section);
+       }
+       return sub;
+}
+
+int vici_add_kv(VICISection* section, const char* key,
+               const char* value, const char* subsection)
+{
+       VICISection* target = section;
+       DBG("key: %s, value: %s, subsection: %s", key, value, subsection);
+
+       if (section == NULL || key == NULL) {
+               connman_error("invalid parameter");
+               return -1;
+       }
+
+       if (subsection)
+               target = get_subsection(section, subsection);
+
+       add_kv_to_section(key, value, target);
+       return 0;
+}
+
+int vici_add_kvl(VICISection* section, const char* key,
+               const char* value, const char* subsection)
+{
+       VICISection* target = section;
+
+       DBG("key: %s, value: %s, subsection: %s", key, value, subsection);
+       if (section == NULL || key == NULL) {
+               connman_error("invalid parameter");
+               return -1;
+       }
+
+       if (subsection)
+               target = get_subsection(section, subsection);
+
+       if (g_strcmp0(subsection, "children") == 0)
+               target = get_subsection(target, "net");
+
+       add_kvl_to_section(key, value, target);
+       return 0;
+}
+
+static void add_list_to_section(char *key, GSList *list, VICISection *section)
+{
+       if (section == NULL || key == NULL || list == NULL)
+               return;
+
+       g_hash_table_insert(section->kvls, g_strdup(key), g_slist_copy(list));
+       return;
+}
+
+int vici_add_list(VICISection* section, char *key, GSList *list, const char* subsection)
+{
+       VICISection* target = section;
+
+       DBG("key: %s, subsection: %s", key, subsection);
+       if (section == NULL || key == NULL) {
+               connman_error("invalid parameter");
+               return -1;
+       }
+
+       if (subsection)
+               target = get_subsection(section, subsection);
+
+       if (g_strcmp0(subsection, "children") == 0)
+               target = get_subsection(target, "net");
+
+       add_list_to_section(key, list, target);
+       return 0;
+}
+
+static char *load_cert_from_path(const char *path)
+{
+       struct stat st;
+       FILE *fp = NULL;
+       int fd = 0;
+       unsigned long long file_size = 0;
+       char *file_buff = NULL;
+
+       fp = fopen(path, "rb");
+       fd = fileno(fp);
+       fstat(fd, &st);
+       file_size = st.st_size;
+       file_buff = g_try_malloc0(sizeof(char)*st.st_size);
+       if (file_buff == NULL) {
+               connman_error("g_try_malloc0 failed\n");
+               fclose(fp);
+               return NULL;
+       }
+
+       if (fread(file_buff, 1, file_size, fp) != file_size) {
+               connman_error("file size not matched\n");
+               g_free(file_buff);
+               file_buff = NULL;
+       }
+
+       fclose(fp);
+       return file_buff;
+}
+
+int vici_add_cert_kv(VICISection *section, const char *key,
+               const char *value, const char *subsection)
+{
+       char *cert = NULL;
+       int ret = 0;
+
+       if (value == NULL) {
+               DBG("value is null");
+               return 0;
+       }
+
+       cert = load_cert_from_path(value);
+       if (!cert)
+               return -1;
+
+       ret = vici_add_kv(section, key, (const char *)cert, subsection);
+       g_free(cert);
+       return ret;
+}
+
+int vici_add_cert_kvl(VICISection *section, const char *key,
+               const char *value, const char *subsection)
+{
+       char *cert = NULL;
+       int ret = 0;
+
+       cert = load_cert_from_path(value);
+       if (!cert)
+               return -1;
+
+       ret = vici_add_kvl(section, key, (const char *)cert, subsection);
+       g_free(cert);
+       return ret;
+}
+
+static void *add_element(struct request *r, enum vici_element type,
+                                                unsigned int size)
+{
+       unsigned int newlen;
+       void *ret, *new;
+
+       if (r->used + size + 1 > r->allocated) {
+               newlen = r->allocated;
+               while (newlen < r->used + size + 1) {
+                       newlen *= 2;
+               }
+               new = realloc(r->sndbuf, newlen);
+               if (!new) {
+                       r->err = -errno;
+                       return NULL;
+               }
+               r->sndbuf = new;
+               r->allocated = newlen;
+       }
+       r->sndbuf[r->used++] = type;
+       ret = r->sndbuf + r->used;
+       r->used += size;
+       return ret;
+}
+
+static void section_start(struct request *r, const char *name)
+{
+       uint8_t nlen;
+       char *pos;
+
+       nlen = strlen(name);
+       pos = add_element(r, VICI_SECTION_START, 1 + nlen);
+       if (pos) {
+               pos[0] = nlen;
+               memcpy(pos + 1, name, nlen);
+       }
+}
+
+static void section_end(struct request *r)
+{
+       add_element(r, VICI_SECTION_END, 0);
+}
+
+static void key_value(struct request *r, const char *name,
+                          const void *buf, unsigned int buflen)
+{
+       uint8_t nlen;
+       uint16_t vlen;
+       char *pos;
+
+       nlen = strlen(name);
+       pos = add_element(r, VICI_KEY_VALUE, 1 + nlen + sizeof(vlen) + buflen);
+       if (pos) {
+               pos[0] = nlen;
+               memcpy(pos + 1, name, nlen);
+               vlen = htons(buflen);
+               memcpy(pos + 1 + nlen, &vlen, sizeof(vlen));
+               memcpy(pos + 1 + nlen + sizeof(vlen), buf, buflen);
+       }
+}
+
+
+static void list_start(struct request *r, const char *name)
+{
+       uint8_t nlen;
+       char *pos;
+
+       nlen = strlen(name);
+       pos = add_element(r, VICI_LIST_START, 1 + nlen);
+       if (pos) {
+               pos[0] = nlen;
+               memcpy(pos + 1, name, nlen);
+       }
+}
+
+static void list_item(struct request *r, const void *buf,
+                                         unsigned int buflen)
+{
+       uint16_t vlen;
+       char *pos;
+
+       pos = add_element(r, VICI_LIST_ITEM, sizeof(vlen) + buflen);
+       if (pos) {
+               vlen = htons(buflen);
+               memcpy(pos, &vlen, sizeof(vlen));
+               memcpy(pos + sizeof(vlen), buf, buflen);
+       }
+}
+
+static void list_end(struct request *r)
+{
+       add_element(r, VICI_LIST_END, 0);
+}
+
+static void destroy_vici_request(gpointer data)
+{
+       struct request *req = (struct request *)data;
+       if(!req)
+               return;
+
+       g_free(req->sndbuf);
+       g_free(req->rcvbuf);
+       g_free(req);
+}
+
+static int create_vici_request(enum vici_packet_type type, VICIClientCmd cmd,
+                                                 struct request **rp)
+{
+       struct request *req = NULL;
+
+       if (cmd >= VICI_CMD_MAX || !rp)
+               return -EINVAL;
+
+       req = g_try_new0(struct request, 1);
+       if (!req) {
+               connman_error("g_try_new0 failed");
+               return -ENOMEM;
+       }
+
+       req->used = 2;
+       req->used += strlen(vici_cmd_str[cmd]);
+       req->allocated = MIN(32, req->used);
+       req->sndbuf = g_try_new0(char, req->allocated);
+       if (!req->sndbuf) {
+               connman_error("g_try_new0 failed");
+               g_free(req);
+               return -ENOMEM;
+       }
+
+       req->sndbuf[0] = type;
+       req->sndbuf[1] = req->used - 2; /* except for type and name length */
+       memcpy(req->sndbuf + 2, vici_cmd_str[cmd], req->used - 2);
+       req->hdr_len = req->used;
+       req->cmd = cmd;
+
+       *rp = req;
+
+       return 0;
+}
+
+static void write_section_kvs(VICISection *section, struct request *req)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       if (section == NULL || req == NULL)
+               return;
+
+       g_hash_table_iter_init (&iter, section->kvs);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               if (!key || !value)
+                       continue;
+               key_value(req, (const char*)key, (const void *)value, strlen((char *)value));
+       }
+
+       return;
+}
+
+static void write_list_item(gpointer data, gpointer user_data)
+{
+       struct request *req = NULL;
+       char *value = NULL;
+
+       if (!data || !user_data)
+               return;
+
+       value = (char *)data;
+       req = (struct request *)user_data;
+       list_item(req, value, strlen(value));
+
+       return;
+}
+
+static void write_section_kvls(VICISection *section, struct request *req)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       if (section == NULL || req == NULL)
+               return;
+
+       g_hash_table_iter_init (&iter, section->kvls);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               if (!key || !value)
+                       continue;
+
+               list_start(req, key);
+               g_slist_foreach((GSList *)value, (GFunc)write_list_item, (gpointer)req);
+               list_end(req);
+       }
+
+       return;
+}
+
+static void write_section(struct request *req, VICISection *section)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       if (req == NULL || section == NULL)
+               return;
+
+       if (section->name)
+               section_start(req, section->name);
+
+       write_section_kvs(section, req);
+       write_section_kvls(section, req);
+
+       g_hash_table_iter_init(&iter, section->subsection);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               if (!key || !value)
+                       continue;
+               write_section(req, (VICISection *)value);
+       }
+
+       if (section->name)
+               section_end(req);
+       return;
+}
+
+static int check_socket(int sock)
+{
+       struct pollfd p_fd;
+       int res = 0;
+
+       p_fd.fd = sock;
+       p_fd.events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL;
+       res = poll((struct pollfd *) &p_fd, 1, 1);
+
+       if (res < 0) {
+               connman_error("Polling error from socket\n");
+               return -1;
+       } else if (res == 0) {
+               connman_error( "poll timeout. socket is busy\n");
+               return 1;
+       } else {
+
+               if (p_fd.revents & POLLERR) {
+                       connman_error("Error! POLLERR from socket[%d]\n", sock);
+                       return -1;
+               } else if (p_fd.revents & POLLHUP) {
+                       connman_error("Error! POLLHUP from socket[%d]\n", sock);
+                       return -1;
+               } else if (p_fd.revents & POLLNVAL) {
+                       connman_error("Error! POLLNVAL from socket[%d]\n", sock);
+                       return -1;
+               } else if (p_fd.revents & POLLIN) {
+                       return 0;
+               } else if (p_fd.revents & POLLOUT) {
+                       return 0;
+               }
+       }
+
+       connman_error("Unknown poll event [%d]\n", p_fd.revents);
+       return -1;
+}
+
+static int write_socket(int sock, char *data, int data_len)
+{
+       int wbytes = 0;
+       int left_len = data_len;
+       char *ptr = data;
+       int res = 0;
+
+       if (sock < SOCK_FD_MIN || !data || data_len < 0)
+               return -1;
+
+       res = check_socket(sock);
+       if (res < 0)
+               return -1;
+       else if (res > 0)
+               return -2;
+
+       errno = 0;
+       while (left_len) {
+               wbytes = write(sock, ptr, left_len);
+               if (wbytes <= 0) {
+                       connman_error("Failed to write data into socket[%d].\n", sock);
+                       break;
+               }else if (wbytes < left_len) {
+                       left_len -= wbytes;
+                       ptr += wbytes;
+               } else if (wbytes == left_len) {
+                       left_len = 0;
+               } else {
+                       connman_error("Unknown error occurred.\n");
+                       break;
+               }
+       }
+
+       if (left_len)
+               return -1;
+       else
+               return 0;
+}
+
+int send_vici_command(struct request *req, VICIClient *vici_client)
+{
+       unsigned int size = 0;
+       int res = 0;
+
+       if (req == NULL) {
+               connman_error("request is NULL\n");
+               return -EINVAL;
+       }
+
+       size = htonl(req->used);
+       res = write_socket(vici_client->client_sock_fd, (char *)&size, sizeof(size));
+       if (res != 0) {
+               connman_error("failed to send size with network byte order\n");
+               return -EIO;
+       }
+
+       res = write_socket(vici_client->client_sock_fd, req->sndbuf, req->used);
+       if (res != 0) {
+               connman_error("failed to send pkt\n");
+               return -EIO;
+       }
+
+       vici_client->request_list = g_slist_append(vici_client->request_list, req);
+       return res;
+}
+
+static void print_vici_element(int elem_type, char *value, int sections)
+{
+       int i = 0;
+
+
+       switch (elem_type) {
+       case VICI_SECTION_START:
+               for (i = 0; i < sections - 1; i++)
+                       DBG("\t");
+               DBG("%s = {\n", value);
+               break;
+       case VICI_SECTION_END:
+               for (i = 0; i < sections; i++)
+                       DBG("\t");
+               DBG("}\n");
+               break;
+       case VICI_KEY_VALUE:
+               for (i = 0; i < sections; i++)
+                       DBG("\t");
+               DBG("%s\n", value);
+               break;
+       case VICI_LIST_START:
+               for (i = 0; i < sections; i++)
+                       DBG("\t");
+               DBG("%s = [", value);
+               break;
+       case VICI_LIST_ITEM:
+               DBG("%s, ", value);
+               break;
+       case VICI_LIST_END:
+               DBG("]\n");
+               break;
+       default:
+               break;
+       }
+       return;
+}
+
+static void debug_vici_message(char *buf, unsigned int size)
+{
+       char temp[255];
+       unsigned int pos = 0;
+       int len = 0;
+       int sections = 0;
+       int type = -1;
+
+       if (buf == NULL || size == 0)
+               return;
+
+       pos = 1;
+       while (pos < size) {
+
+               type = buf[pos];
+               pos++;
+               switch (type) {
+               case VICI_SECTION_START:
+               {
+                       len = buf[pos];
+                       pos++;
+                       g_strlcpy(temp, (const gchar *)&buf[pos], len + 1);
+                       pos += len;
+                       sections++;
+               }
+                       break;
+               case VICI_SECTION_END:
+               {
+                       sections--;
+               }
+                       break;
+               case VICI_KEY_VALUE:
+               {
+                       int key_len = 0;
+                       int value_len = 0;
+
+                       key_len = buf[pos];
+                       pos++;
+                       g_strlcpy(temp, (const gchar *)&buf[pos], key_len + 1);
+                       temp[key_len] = '=';
+                       pos += (key_len + 1);
+                       value_len = buf[pos];
+                       pos++;
+                       g_strlcpy(temp + key_len + 1, (const gchar *)&buf[pos], value_len + 1);
+                       pos += value_len;
+                       len = key_len + 1 + value_len;
+               }
+                       break;
+               case VICI_LIST_START:
+               {
+                       len = buf[pos];
+                       pos++;
+                       g_strlcpy(temp, (const gchar *)&buf[pos], len + 1);
+                       pos += len;
+               }
+                       break;
+               case VICI_LIST_ITEM:
+               {
+                       pos++;
+                       len = buf[pos];
+                       pos++;
+                       g_strlcpy(temp, (const gchar *)&buf[pos], len + 1);
+                       pos += len;
+               }
+                       break;
+               case VICI_LIST_END:
+                       break;
+               default:
+                       break;
+               }
+               print_vici_element(type, temp, sections);
+       }
+       return;
+}
+
+static unsigned int extract_key_value(char *buf, unsigned int pos, char **key, char **value)
+{
+       int key_len = 0;
+       int value_len = 0;
+
+       key_len = buf[pos];
+       pos++;
+       *key = g_strndup((const gchar *)&buf[pos], key_len);
+       pos+=(key_len + 1);
+       value_len = buf[pos];
+       pos++;
+       *value = g_strndup((const gchar *)&buf[pos], value_len);
+       pos+=value_len;
+       return pos;
+}
+
+static gboolean extract_request_result(char *buf, unsigned int size, char **err)
+{
+       gboolean success = FALSE;
+       unsigned int pos = 0;
+       int type = -1;
+
+       pos = 1;
+       while (pos < size) {
+
+               type = buf[pos];//3
+               pos++;
+               if (type == VICI_KEY_VALUE) {
+                       char *key = NULL;
+                       char *value = NULL;
+                       pos = extract_key_value(buf, pos, &key, &value);
+                       DBG("pos : %d size : %d\n", pos, size);
+
+                       /* TODO :remove this after debug */
+                       DBG("key : %s value : %s\n", key, value);
+                       if (g_strcmp0(key, "success") == 0)
+                               (g_strcmp0(value, "yes") == 0)?(success = TRUE):(success = FALSE);
+
+                       if (g_strcmp0(key, "errmsg"))
+                               *err = g_strdup(value);
+                       g_free(key);
+                       g_free(value);
+               }
+       }
+       return success;
+}
+
+static int handle_vici_result(gboolean success, int cmd, char * err)
+{
+       int ret = 0;
+       if (success)
+               return 0;
+
+       DBG(" %s failed with %s!\n", vici_cmd_str[cmd], err);
+       g_free(err);
+
+       switch (cmd) {
+       case    VICI_CMD_LOAD_CONN:
+               ret = EINVAL;
+               break;
+       case    VICI_CMD_LOAD_SHARED:
+               ret = EINVAL;
+               break;
+       case    VICI_CMD_LOAD_CERT:
+               ret = EINVAL;
+               break;
+       case    VICI_CMD_LOAD_AUTH:
+               ret = 0;
+               break;
+       case    VICI_CMD_LOAD_KEY:
+               ret = EINVAL;
+               break;
+       case    VICI_CMD_INITIATE:
+               ret = ECONNABORTED;
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static int process_vici_response(struct request * req)
+{
+       char *err = NULL;
+       gboolean success = FALSE;
+       int ret = 0;
+
+       if (!req)
+               return -1;
+
+       if (!req->rcvbuf || req->rcvbuf[0] != VICI_CMD_RESPONSE)
+               return -1;
+
+       //TODO: remove below when there's no further problem.
+       debug_vici_message(req->rcvbuf, req->rcv_pkt_size);
+
+       success = extract_request_result(req->rcvbuf, req->rcv_pkt_size, &err);
+       ret = handle_vici_result(success, req->cmd, err);
+
+       return ret;
+}
+
+int vici_send_request(VICIClient *vici_client, VICIClientCmd cmd, VICISection *root)
+{
+       struct request *req = NULL;
+       int ret;
+
+       DBG("%s", vici_cmd_str[cmd]);
+       ret = create_vici_request(VICI_CMD_REQUEST, cmd, &req);
+       if (ret < 0) {
+               connman_error("error on create_request\n");
+               return ret;
+       }
+
+       write_section(req, root);
+       //TODO: remove below when there's no further problem.
+       debug_vici_message(req->sndbuf + req->hdr_len - 1, req->used - req->hdr_len + 1);
+
+       ret = send_vici_command(req, vici_client);
+       if (ret < 0) {
+               destroy_vici_request(req);
+               connman_error("error on send_command\n");
+       }
+
+       return ret;
+}
+
+static int get_socket_from_source(GIOChannel *source, GIOCondition condition)
+{
+       int sock = -1;
+       /* check socket */
+       sock = g_io_channel_unix_get_fd(source);
+       if (sock < SOCK_FD_MIN)
+               return -1;
+
+       if ((condition & G_IO_ERR) || (condition & G_IO_HUP) || (condition & G_IO_NVAL)) {
+               connman_error("G_IO_ERR/G_IO_HUP/G_IO_NVAL received sock [%d] condition [%d]\n", sock, condition);
+               //TODO: handle the breaking socket
+               return -1;
+       }
+       return sock;
+}
+
+static int read_socket(int sock, char *data, unsigned int data_len)
+{
+       int rbytes = 0;
+       int total_rbytes = 0;
+
+       if (sock < SOCK_FD_MIN || !data || data_len <= 0)
+               return -1;
+
+       while (data_len > 0) {
+               errno = 0;
+               rbytes = read(sock, data, data_len);
+               if (rbytes <= 0)
+                       return -1;
+
+               total_rbytes += rbytes;
+               data += rbytes;
+               data_len -= rbytes;
+       }
+
+       return total_rbytes;
+}
+
+static int recv_vici_pkt(int sock, struct request *req)
+{
+       if(!req)
+               return -1;
+
+       if (req->rcv_pkt_size == 0) {
+               unsigned int pkt_size = 0;
+               if (read_socket(sock, (char *)&pkt_size, sizeof(pkt_size)) < 0)
+                       return -1;
+
+               req->rcv_pkt_size = ntohl(pkt_size);
+               /* TODO :REMOVE THIS AFTER DEBUG */
+               DBG("rcv_pkt_size [%d] will be recved\n", req->rcv_pkt_size);
+       } else {
+
+               char *buf = NULL;
+               buf = g_try_malloc0(req->rcv_pkt_size);
+               if (buf == NULL)
+                       return -1;
+
+               if (read_socket(sock, buf, req->rcv_pkt_size) < 0) {
+                       g_free(buf);
+                       return -1;
+               }
+               req->rcvbuf = buf;
+       }
+
+       return 0;
+}
+
+static struct request *pop_vici_request(VICIClient *vici_client)
+{
+       GSList *list = NULL;
+
+       if (!vici_client)
+               return NULL;
+
+       list = vici_client->request_list;
+       if(!list)
+               return NULL;
+
+       return list->data;
+}
+
+static gboolean process_reply(GIOChannel *source,
+                                          GIOCondition condition,
+                                          gpointer user_data)
+{
+       VICIClient *vici_client = NULL;
+       struct request * req = NULL;
+       int sock = 0;
+       int ret = 0;
+
+       vici_client = (VICIClient *)user_data;
+       if (!vici_client)
+               return FALSE;
+
+       sock = get_socket_from_source(source, condition);
+       if (sock < 0)
+               return FALSE;
+
+       /* get first request */
+       req = pop_vici_request((VICIClient *)user_data);
+       if (!req)
+               return FALSE;
+
+       if(recv_vici_pkt(sock, req) < 0)
+               return FALSE;
+
+       if (!req->rcvbuf) {
+               return TRUE;
+       }
+
+       ret = process_vici_response(req);
+       if (ret != 0)
+               vici_client->reply(ret, vici_client->ipsec_user_data);
+
+       vici_client->request_list = g_slist_remove(vici_client->request_list, req);
+       destroy_vici_request(req);
+
+       /* TODO :remove this after debug */
+       DBG("left request reply : %d", g_slist_length(vici_client->request_list));
+       if(g_slist_length(vici_client->request_list) == 0)
+               vici_client->reply(ret, vici_client->ipsec_user_data);
+
+       return TRUE;
+}
+
+static int str_to_socket_addr(const char *uri, struct sockaddr_un *addr)
+{
+       memset(addr, 0, sizeof(*addr));
+       addr->sun_family = AF_UNIX;
+       strncpy(addr->sun_path, uri, sizeof(addr->sun_path));
+
+       addr->sun_path[sizeof(addr->sun_path)-1] = '\0';
+
+       return offsetof(struct sockaddr_un, sun_path) + strlen(addr->sun_path);
+}
+
+static int connect_socket(const char *uri)
+{
+       struct sockaddr_un addr;
+       int len, fd;
+
+       fd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (fd < 0) {
+               connman_error("socket() failed");
+               return -errno;
+       }
+
+       len = str_to_socket_addr(uri, &addr);
+       if (len == -1) {
+               connman_error("str_to_socket_addr failed");
+               close(fd);
+               return -1;
+       }
+
+       if (connect(fd, (struct sockaddr*)&addr, len) < 0) {
+               connman_error("connect failed. errno %d/%s", errno, strerror(errno));
+               close(fd);
+               return -errno;
+       }
+
+       return fd;
+}
+
+int vici_initialize(VICIClient **vici_client)
+{
+       GIOChannel *vici_channel;
+
+       *vici_client = g_try_new0(VICIClient, 1);
+       if (!*vici_client) {
+               connman_error("out of memory");
+               return -ENOMEM;
+       }
+
+       (*vici_client)->client_sock_fd = connect_socket(VICI_DEFAULT_URI);
+       if ((*vici_client)->client_sock_fd < 0) {
+               connman_error("connect_socket failed");
+               g_free(*vici_client);
+               return -EIO;
+       }
+
+       vici_channel = g_io_channel_unix_new((*vici_client)->client_sock_fd);
+       if (!vici_channel) {
+               connman_error("g_io_channel_unix_new failed");
+               close((*vici_client)->client_sock_fd);
+               g_free(*vici_client);
+               return -ENOMEM;
+       }
+
+       (*vici_client)->client_watch = g_io_add_watch_full(vici_channel,
+                                                G_PRIORITY_LOW,
+                                                G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                                (GIOFunc)process_reply,
+                                                (gpointer)*vici_client,
+                                                NULL);
+       g_io_channel_unref(vici_channel);
+
+       DBG("connected");
+       return 0;
+}
+
+void vici_set_connect_reply_cb(VICIClient *vici_client, vici_connect_reply_cb reply_cb, gpointer user_data)
+{
+       vici_client->reply = reply_cb;
+       vici_client->ipsec_user_data = user_data;
+}
+
+int vici_deinitialize(VICIClient *vici_client)
+{
+       if (vici_client->client_watch > 0) {
+               g_source_remove(vici_client->client_watch);
+               vici_client->client_watch = 0;
+       }
+
+       close(vici_client->client_sock_fd);
+       g_slist_free_full(vici_client->request_list, destroy_vici_request);
+       g_free(vici_client);
+
+       return 0;
+}
diff --git a/vpn/plugins/vici-client.h b/vpn/plugins/vici-client.h
new file mode 100644 (file)
index 0000000..91200e5
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef __VICI_CLIENT_H
+#define __VICI_CLIENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* strongswan VICI plugin client part*/
+struct _VICIClient;
+typedef struct _VICIClient VICIClient;
+
+struct _VICISection;
+typedef struct _VICISection VICISection;
+
+typedef enum {
+       VICI_CMD_LOAD_CONN,
+       VICI_CMD_LOAD_SHARED,
+       VICI_CMD_LOAD_CERT,
+       VICI_CMD_LOAD_AUTH,
+       VICI_CMD_UNLOAD_AUTH,
+       VICI_CMD_LOAD_KEY,
+       VICI_CMD_INITIATE,
+       VICI_CMD_MAX,
+} VICIClientCmd;
+
+#define VICI_DEFAULT_URI "/var/run/charon.vici"
+
+typedef int (*vici_add_element)(VICISection *sect, const char *key,
+               const char *value, const char *subsection);
+
+typedef void (*vici_connect_reply_cb)(int err, void *user_data);
+
+VICISection* vici_create_section(const char *name);
+int add_subsection(const char* name, VICISection* child, VICISection* section);
+void vici_destroy_section(VICISection *sect);
+int vici_add_kv(VICISection *sect, const char *key,
+               const char *value, const char *subsection);
+int vici_add_kvl(VICISection *sect, const char *key,
+               const char *value, const char *subsection);
+int vici_add_list(VICISection* section, char *key,
+               GSList *list, const char* subsection);
+int vici_add_cert_kv(VICISection *section, const char *key,
+               const char *value, const char *subsection);
+int vici_add_cert_kvl(VICISection *section, const char *key,
+               const char *value, const char *subsection);
+
+int vici_initialize(VICIClient **vici_client);
+int vici_deinitialize(VICIClient *vici_client);
+void vici_set_connect_reply_cb(VICIClient *vici_client, vici_connect_reply_cb reply_cb, gpointer user_data);
+int vici_send_request(VICIClient *vici_client, VICIClientCmd cmd, VICISection *root);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VICI_CLIENT_H */
index b438d06..3e40e4f 100755 (executable)
@@ -307,6 +307,99 @@ static DBusMessage *vpn_notify(struct connman_task *task,
        return NULL;
 }
 
+static void vpn_event(struct vpn_provider *provider, int state)
+{
+       struct vpn_data *data;
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
+       int index, err;
+
+       data = vpn_provider_get_data(provider);
+
+       name = vpn_provider_get_driver_name(provider);
+
+       if (!name) {
+               DBG("Cannot find VPN driver for provider %p", provider);
+               vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
+               return;
+       }
+
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+       if (!vpn_driver_data) {
+               DBG("Cannot find VPN driver data for name %s", name);
+               vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
+               return;
+       }
+
+       DBG("provider %p driver %s state %d", provider, name, state);
+
+       switch (state) {
+       case VPN_STATE_CONNECT:
+       case VPN_STATE_READY:
+               if (data->state == VPN_STATE_READY) {
+                       /*
+                        * This is the restart case, in which case we must
+                        * just set the IP address.
+                        *
+                        * We need to remove first the old address, just
+                        * replacing the old address will not work as expected
+                        * because the old address will linger in the interface
+                        * and not disapper so the clearing is needed here.
+                        *
+                        * Also the state must change, otherwise the routes
+                        * will not be set properly.
+                        */
+                       vpn_provider_set_state(provider,
+                                               VPN_PROVIDER_STATE_CONNECT);
+
+                       vpn_provider_clear_address(provider, AF_INET);
+                       vpn_provider_clear_address(provider, AF_INET6);
+
+                       vpn_provider_change_address(provider);
+                       vpn_provider_set_state(provider,
+                                               VPN_PROVIDER_STATE_READY);
+                       break;
+               }
+
+               index = vpn_provider_get_index(provider);
+               vpn_provider_ref(provider);
+               data->watch = vpn_rtnl_add_newlink_watch(index,
+                                                    vpn_newlink, provider);
+               err = connman_inet_ifup(index);
+               if (err < 0) {
+                       if (err == -EALREADY)
+                               /*
+                                * So the interface is up already, that is just
+                                * great. Unfortunately in this case the
+                                * newlink watch might not have been called at
+                                * all. We must manually call it here so that
+                                * the provider can go to ready state and the
+                                * routes are setup properly.
+                                */
+                               vpn_newlink(IFF_UP, 0, provider);
+                       else
+                               DBG("Cannot take interface %d up err %d/%s",
+                                       index, -err, strerror(-err));
+               }
+               break;
+
+       case VPN_STATE_UNKNOWN:
+       case VPN_STATE_IDLE:
+       case VPN_STATE_DISCONNECT:
+       case VPN_STATE_FAILURE:
+               vpn_provider_set_state(provider,
+                                       VPN_PROVIDER_STATE_DISCONNECT);
+               break;
+
+       case VPN_STATE_AUTH_FAILURE:
+               vpn_provider_indicate_error(provider,
+                                       VPN_PROVIDER_ERROR_AUTH_FAILED);
+               break;
+       }
+
+       return;
+}
+
 static int vpn_create_tun(struct vpn_provider *provider)
 {
        struct vpn_data *data = vpn_provider_get_data(provider);
@@ -454,6 +547,10 @@ static int vpn_connect(struct vpn_provider *provider,
                goto exist_err;
        }
 
+
+       if(vpn_driver_data->vpn_driver->set_event_cb)
+               vpn_driver_data->vpn_driver->set_event_cb(vpn_event, provider);
+
        ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
                                                data->if_name, cb, dbus_sender,
                                                user_data);
index bf56728..6175863 100755 (executable)
@@ -40,9 +40,12 @@ enum vpn_state {
        VPN_STATE_AUTH_FAILURE  = 6,
 };
 
+typedef void (*vpn_event_callback)(struct vpn_provider *provider, int state);
+
 struct vpn_driver {
        int flags;
        int (*notify) (DBusMessage *msg, struct vpn_provider *provider);
+       void (*set_event_cb) (vpn_event_callback event_cb, struct vpn_provider *provider);
        int (*connect) (struct vpn_provider *provider,
                        struct connman_task *task, const char *if_name,
                        vpn_provider_connect_cb_t cb, const char *dbus_sender,
index 293c64e..a5be332 100755 (executable)
@@ -203,7 +203,11 @@ static int load_provider(GKeyFile *keyfile, const char *group,
                                struct vpn_config *config, enum what action)
 {
        struct vpn_config_provider *config_provider;
+#if !defined TIZEN_EXT
        const char *ident, *host, *domain;
+#else
+       const char *ident, *host, *domain, *name;
+#endif
        int err;
 
        /* Strip off "provider_" prefix */
@@ -229,8 +233,14 @@ static int load_provider(GKeyFile *keyfile, const char *group,
 
        host = get_string(config_provider, "Host");
        domain = get_string(config_provider, "Domain");
+#if !defined TIZEN_EXT
        if (host && domain) {
                char *id = __vpn_provider_create_identifier(host, domain);
+#else
+       name = get_string(config_provider, "Name");
+       if (host && domain && name) {
+               char *id = __vpn_provider_create_identifier(host, domain, name);
+#endif
 
                struct vpn_provider *provider;
                provider = __vpn_provider_lookup(id);
@@ -252,7 +262,11 @@ static int load_provider(GKeyFile *keyfile, const char *group,
 
                DBG("provider identifier %s", id);
        } else {
+#if !defined TIZEN_EXT
                DBG("invalid values host %s domain %s", host, domain);
+#else
+               DBG("invalid values host %s domain %s name %s", host, domain, name);
+#endif
                err = -EINVAL;
                goto err;
        }
index 16c0c2b..925f699 100755 (executable)
@@ -1757,6 +1757,7 @@ static void provider_create_all_from_type(const char *provider_type)
        g_strfreev(providers);
 }
 
+#if !defined TIZEN_EXT
 char *__vpn_provider_create_identifier(const char *host, const char *domain)
 {
        char *ident;
@@ -1769,6 +1770,20 @@ char *__vpn_provider_create_identifier(const char *host, const char *domain)
 
        return ident;
 }
+#else
+char *__vpn_provider_create_identifier(const char *host, const char *domain, const char *name)
+{
+       char *ident;
+
+       ident = g_strdup_printf("%s_%s_%s", host, domain, name);
+       if (!ident)
+               return NULL;
+
+       provider_dbus_ident(ident);
+
+       return ident;
+}
+#endif
 
 int __vpn_provider_create(DBusMessage *msg)
 {
@@ -1822,7 +1837,11 @@ int __vpn_provider_create(DBusMessage *msg)
        if (!type || !name)
                return -EOPNOTSUPP;
 
+#if !defined TIZEN_EXT
        ident = __vpn_provider_create_identifier(host, domain);
+#else
+       ident = __vpn_provider_create_identifier(host, domain, name);
+#endif
        DBG("ident %s", ident);
 
        provider = __vpn_provider_lookup(ident);
@@ -2011,7 +2030,11 @@ int __vpn_provider_create_from_config(GHashTable *settings,
                goto fail;
        }
 
+#if !defined TIZEN_EXT
        ident = __vpn_provider_create_identifier(host, domain);
+#else
+       ident = __vpn_provider_create_identifier(host, domain, name);
+#endif
        DBG("ident %s", ident);
 
        provider = __vpn_provider_lookup(ident);
index 8bf86bd..26b13d7 100755 (executable)
--- a/vpn/vpn.h
+++ b/vpn/vpn.h
@@ -71,8 +71,11 @@ int __vpn_ipconfig_init(void);
 void __vpn_ipconfig_cleanup(void);
 
 #include "vpn-provider.h"
-
+#if !defined TIZEN_EXT
 char *__vpn_provider_create_identifier(const char *host, const char *domain);
+#else
+char *__vpn_provider_create_identifier(const char *host, const char *domain, const char *name);
+#endif
 bool __vpn_provider_check_routes(struct vpn_provider *provider);
 int __vpn_provider_append_user_route(struct vpn_provider *provider,
                                int family, const char *network,