diff options
-rwxr-xr-x | Makefile.am | 2 | ||||
-rwxr-xr-x | Makefile.plugins | 4 | ||||
-rwxr-xr-x | configure.ac | 8 | ||||
-rw-r--r-- | doc/vpn-config-format.txt | 4 | ||||
-rwxr-xr-x | packaging/connman.spec | 3 | ||||
-rwxr-xr-x | src/log.c | 2 | ||||
-rw-r--r-- | vpn/plugins/ipsec.c | 1001 | ||||
-rw-r--r-- | vpn/plugins/ipsec.h | 3 | ||||
-rw-r--r-- | vpn/plugins/vici-client.c | 821 | ||||
-rw-r--r-- | vpn/plugins/vici-client.h | 49 | ||||
-rwxr-xr-x | vpn/plugins/vpn.c | 97 | ||||
-rwxr-xr-x | vpn/plugins/vpn.h | 3 | ||||
-rwxr-xr-x | vpn/vpn-config.c | 14 | ||||
-rwxr-xr-x | vpn/vpn-provider.c | 23 | ||||
-rwxr-xr-x | vpn/vpn.h | 5 |
15 files changed, 1800 insertions, 239 deletions
diff --git a/Makefile.am b/Makefile.am index e287363c..9fd16a0d 100755 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/Makefile.plugins b/Makefile.plugins index e1c0a11c..a7452622 100755 --- a/Makefile.plugins +++ b/Makefile.plugins @@ -122,8 +122,8 @@ 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)"\" -vpn_plugins_ipsec_la_LDFLAGS = $(plugin_ldflags) + -DSCRIPTDIR=\""$(build_scriptdir)"\" @GIO_CFLAGS@ @OPENSSL_CFLAGS@ +vpn_plugins_ipsec_la_LDFLAGS = $(plugin_ldflags) @GIO_LIBS@ @OPENSSL_LIBS@ endif endif diff --git a/configure.ac b/configure.ac index 95aa0a4f..cd2013f4 100755 --- a/configure.ac +++ b/configure.ac @@ -114,6 +114,14 @@ 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 diff --git a/doc/vpn-config-format.txt b/doc/vpn-config-format.txt index 15d3a261..b4898eb0 100644 --- a/doc/vpn-config-format.txt +++ b/doc/vpn-config-format.txt @@ -216,8 +216,8 @@ IPsec VPN supports following options (see swanctl.conf(5) for details): 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.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 diff --git a/packaging/connman.spec b/packaging/connman.spec index 2b7dfd42..6cf5b37d 100755 --- a/packaging/connman.spec +++ b/packaging/connman.spec @@ -14,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) @@ -27,6 +28,7 @@ BuildRequires: openvpn %endif %if %{with connman_ipsec} BuildRequires: strongswan +BuildRequires: pkgconfig(openssl) %endif BuildRequires: ca-certificates-devel BuildRequires: readline-devel @@ -73,6 +75,7 @@ OpenVPN support for Connman. Summary: IPsec Support for Connman Requires: %{name} = %{version} Requires: strongswan +BuildRequires: pkgconfig(openssl) %description plugin-ipsec OpenVPN support for Connman. @@ -216,6 +216,7 @@ void connman_error(const char *format, ...) vsyslog(LOG_ERR, format, ap); va_end(ap); + fflush(log_file); } /** @@ -234,6 +235,7 @@ void connman_debug(const char *format, ...) vsyslog(LOG_DEBUG, format, ap); va_end(ap); + fflush(log_file); } static void print_backtrace(unsigned int offset) diff --git a/vpn/plugins/ipsec.c b/vpn/plugins/ipsec.c index de66cea7..5e347704 100644 --- a/vpn/plugins/ipsec.c +++ b/vpn/plugins/ipsec.c @@ -26,9 +26,19 @@ #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> @@ -45,40 +55,62 @@ #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_section_add_element add_elem; + vici_add_element add_elem; } ipsec_conn_options[] = { - {"IPsec.Version", "version", NULL, vici_section_add_kv}, - {"IPsec.LeftAddrs", "local_addrs", NULL, vici_section_add_kvl}, - {"IPsec.RightAddrs", "remote_addrs", NULL, vici_section_add_kvl}, - - {"IPsec.LocalAuth", "auth", "local", vici_section_add_kv}, - {"IPsec.LocalCerts", "certs", "local", vici_section_add_kv}, - {"IPsec.LocalID", "id", "local", vici_section_add_kv}, - {"IPsec.LocalXauthID", "xauth_id", "local", vici_section_add_kv}, - {"IPsec.LocalXauthAuth", "auth", "local-xauth", vici_section_add_kv}, - {"IPsec.LocalXauthXauthID", "xauth_id", "local-xauth", vici_section_add_kv}, - {"IPsec.RemoteAuth", "auth", "remote", vici_section_add_kv}, - {"IPsec.RemoteCerts", "certs", "remote", vici_section_add_kv}, - {"IPsec.RemoteID", "id", "remote", vici_section_add_kv}, - {"IPsec.RemoteXauthID", "xauth_id", "remote", vici_section_add_kv}, - {"IPsec.RemoteXauthAuth", "auth", "remote-xauth", vici_section_add_kv}, - {"IPsec.RemoteXauthXauthID", "xauth_id", "remote-xauth", vici_section_add_kv}, - {"IPsec.ChildrenLocalTs", "local_ts", "children", vici_section_add_kvl}, - {"IPsec.ChildrenRemoteTs", "remote_ts", "children", vici_section_add_kvl}, + {"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.IKEData", "data"}, + {"IPsec.IKEOwners", "owners"}, {"IPsec.XauthData", "data"}, {"IPsec.XauthOwners", "owners"}, }; @@ -91,8 +123,345 @@ struct { {"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) { @@ -109,41 +478,169 @@ static int ipsec_is_same_auth(const char* req, const char* target) static int vici_load_cert(const char* type, const char* flag, const char* data) { VICISection *sect; + int ret = 0; + sect = vici_create_section(NULL); - vici_section_add_kv(sect, "type", type, NULL); - vici_section_add_kv(sect, "flag", flag, NULL); - vici_section_add_kv(sect, "data", data, NULL); + if (!sect) + return -ENOMEM; - vici_client_send_request(VICI_REQUEST_LOAD_CERT, sect); + 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 0; + 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 int ipsec_load_conn(struct vpn_provider *provider) +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; - VICISection *sect; + 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"); - sect = vici_create_section(value); + 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++) { - key = ipsec_conn_options[i].vici_key; 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(sect, key, value, subsection); + ipsec_conn_options[i].add_elem(conn, key, value, subsection); } - vici_client_send_request(VICI_REQUEST_LOAD_CONN, sect); + 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); + } - vici_destroy_section(sect); + ipsec_add_default_conn_data(provider, conn); + ipsec_add_default_child_sa_data(provider, children); - return 0; + 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) @@ -151,122 +648,399 @@ static int ipsec_load_shared_psk(struct vpn_provider *provider) const char *data; const char *owner; VICISection *sect; + int ret = 0; - data = vpn_provider_get_string(provider, "IPsec.IkeData"); - owner = vpn_provider_get_string(provider, "IPsec.IkeOwners"); + 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_section_add_kv(sect, "type", VICI_SHARED_TYPE_PSK, NULL); - vici_section_add_kv(sect, "data", data, NULL); - vici_section_add_kvl(sect, "owners", owner, NULL); + vici_add_kv(sect, "type", VICI_SHARED_TYPE_PSK, NULL); + vici_add_kv(sect, "data", data, NULL); + vici_add_kvl(sect, "owners", owner, NULL); - vici_client_send_request(VICI_REQUEST_LOAD_SHARED, sect); + 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 0; + 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_section_add_kv(sect, "type", VICI_SHARED_TYPE_XAUTH, NULL); - vici_section_add_kv(sect, "data", data, NULL); - vici_section_add_kvl(sect, "owners", owner, 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); - vici_client_send_request(VICI_REQUEST_LOAD_SHARED, sect); + 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 0; + 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; - const char *data; - const char *auth_type; - int i; + char *data; + const char *local_auth_type; + const char *remote_auth_type; + int ret = 0; + + if (!provider) { + connman_error("invalid provider"); + return -EINVAL; + } - 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; + 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; } - 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); + 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; } - return 0; + init_openssl(); + + data->provider = provider; + data->cb = cb; + data->user_data = user_data; + return 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) +static void vici_connect(struct ipsec_private_data *data) { + struct vpn_provider *provider = NULL; + vpn_provider_connect_cb_t cb = NULL; 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"); + 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_client_initialize(); + 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); + 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: - if (cb) - cb(provider, user_data, err); + /* 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; } @@ -278,6 +1052,83 @@ static int ipsec_error_code(struct vpn_provider *provider, int exit_code) 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; } @@ -285,7 +1136,7 @@ static void ipsec_disconnect(struct vpn_provider *provider) { int err = 0; - err = vici_client_deinitialize(); + err = vici_deinitialize(vici_client); IPSEC_ERROR_CHECK_RETURN(err, "failed to deinitialize vici_client"); } diff --git a/vpn/plugins/ipsec.h b/vpn/plugins/ipsec.h index 0081a439..6fe50a09 100644 --- a/vpn/plugins/ipsec.h +++ b/vpn/plugins/ipsec.h @@ -6,12 +6,11 @@ #define IPSEC_AUTH_XAUTH "XAUTH" #define VICI_SHARED_TYPE_PSK "IKE" -#define VICI_SHARED_TYPE_XAUTH "XAUTH" +#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) diff --git a/vpn/plugins/vici-client.c b/vpn/plugins/vici-client.c index dbf14084..6513367f 100644 --- a/vpn/plugins/vici-client.c +++ b/vpn/plugins/vici-client.c @@ -1,11 +1,14 @@ +#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> @@ -14,8 +17,6 @@ #include "ipsec.h" #include "vici-client.h" -#define VICI_DEFAULT_URI "/var/run/charon.vici" - #define SOCK_FD_MIN 3 #define VICI_REQUEST_TIMEOUT 5000 @@ -40,28 +41,38 @@ enum vici_packet_type { VICI_EVENT = 7, }; -typedef void (*process_return)(unsigned char *buf, unsigned int size); +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 *buf; + char *sndbuf; + int cmd; int err; - int client_source_idle_id; - int client_source_timeout_id; /* process reply */ unsigned int rcv_pkt_size; - process_return handler; + char *rcvbuf; /* davici_cb cb; */ void *user; }; struct _VICIClient { /* io data */ - int client_sock; + int client_sock_fd; int client_watch; - GList *request_list; + GSList *request_list; + vici_connect_reply_cb reply; + void *ipsec_user_data; }; struct _VICISection { @@ -71,7 +82,7 @@ struct _VICISection { GHashTable *subsection; }; -static void _remove_list(gpointer data) +static void remove_list(gpointer data) { if (data == NULL) return; @@ -88,7 +99,7 @@ void vici_destroy_section(VICISection* section) g_free(section); } -static void _free_section(gpointer data) +static void free_section(gpointer data) { VICISection* section = (VICISection*)data; vici_destroy_section(section); @@ -107,66 +118,65 @@ VICISection* vici_create_section(const char* name) 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); + 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; } -static int _vici_section_add_kvl(VICISection* section, const char* key, const char* value) +int add_subsection(const char* name, VICISection* child, VICISection* section) { - GSList *list = NULL; - if (section == NULL || key == NULL || value == NULL) { + if (section == NULL || name == NULL || child == NULL) { connman_error("invalid parameter"); return -1; } - list = g_hash_table_lookup(section->kvls, key); - if (list == NULL) { - list = g_slist_alloc(); - g_hash_table_insert(section->kvls, g_strdup(key), list); - } - - list = g_slist_prepend(list, g_strdup(value)); - + g_hash_table_insert(section->subsection, g_strdup(name), child); return 0; } -static int _vici_section_add_kv(VICISection* section, const char* key, const char* value) +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; } - g_hash_table_insert(section->kvs, g_strdup(key), g_strdup(value)); + 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 vici_section_add_subsection(VICISection* section, const char* name, VICISection* child) +static int add_kv_to_section(const char* key, const char* value, VICISection* section) { - if (section == NULL || name == NULL || child == NULL) { + if (section == NULL || key == NULL || value == NULL) { connman_error("invalid parameter"); return -1; } - g_hash_table_insert(section->subsection, g_strdup(name), child); + g_hash_table_insert(section->kvs, g_strdup(key), g_strdup(value)); return 0; } -static VICISection* _vici_section_get_subsection(VICISection* section, const char* name) +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); - vici_section_add_subsection(section, name, sub); + add_subsection(name, sub, section); } return sub; } -int vici_section_add_kv(VICISection* section, const char* key, +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"); @@ -174,77 +184,170 @@ int vici_section_add_kv(VICISection* section, const char* key, } if (subsection) - target = _vici_section_get_subsection(section, subsection); + target = get_subsection(section, subsection); - _vici_section_add_kv(target, key, value); + add_kv_to_section(key, value, target); return 0; } -int vici_section_add_kvl(VICISection* section, const char* key, +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 = _vici_section_get_subsection(section, subsection); + target = get_subsection(section, subsection); - _vici_section_add_kvl(target, key, value); + if (g_strcmp0(subsection, "children") == 0) + target = get_subsection(target, "net"); + + add_list_to_section(key, list, target); return 0; } -static void* _add_element(struct request *r, enum vici_element type, +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) - { + if (r->used + size + 1 > r->allocated) { newlen = r->allocated; - while (newlen < r->used + size + 1) - { + while (newlen < r->used + size + 1) { newlen *= 2; } - new = realloc(r->buf, newlen); - if (!new) - { + new = realloc(r->sndbuf, newlen); + if (!new) { r->err = -errno; return NULL; } - r->buf = new; + r->sndbuf = new; r->allocated = newlen; } - r->buf[r->used++] = type; - ret = r->buf + r->used; + r->sndbuf[r->used++] = type; + ret = r->sndbuf + r->used; r->used += size; return ret; } -static void vici_section_start(struct request *r, const char *name) +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 = add_element(r, VICI_SECTION_START, 1 + nlen); + if (pos) { pos[0] = nlen; memcpy(pos + 1, name, nlen); } } -static void vici_section_end(struct request *r) +static void section_end(struct request *r) { - _add_element(r, VICI_SECTION_END, 0); + add_element(r, VICI_SECTION_END, 0); } -static void vici_kv(struct request *r, const char *name, +static void key_value(struct request *r, const char *name, const void *buf, unsigned int buflen) { uint8_t nlen; @@ -252,9 +355,8 @@ static void vici_kv(struct request *r, const char *name, char *pos; nlen = strlen(name); - pos = _add_element(r, VICI_KEY_VALUE, 1 + nlen + sizeof(vlen) + buflen); - if (pos) - { + 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); @@ -264,66 +366,78 @@ static void vici_kv(struct request *r, const char *name, } -static void vici_list_start(struct request *r, const char *name) +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 = add_element(r, VICI_LIST_START, 1 + nlen); + if (pos) { pos[0] = nlen; memcpy(pos + 1, name, nlen); } } -static void vici_list_item(struct request *r, const void *buf, +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) - { + 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 vici_list_end(struct request *r) +static void list_end(struct request *r) { - _add_element(r, VICI_LIST_END, 0); + 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, const char *name, +static int create_vici_request(enum vici_packet_type type, VICIClientCmd cmd, struct request **rp) { struct request *req = NULL; - if (!name || !rp) - return -1; + if (cmd >= VICI_CMD_MAX || !rp) + return -EINVAL; req = g_try_new0(struct request, 1); - if (!req) - return -1; + if (!req) { + connman_error("g_try_new0 failed"); + return -ENOMEM; + } req->used = 2; - req->used += strlen(name); + req->used += strlen(vici_cmd_str[cmd]); req->allocated = MIN(32, req->used); - req->buf = g_try_new0(char, req->allocated); - if (!req->buf) { + req->sndbuf = g_try_new0(char, req->allocated); + if (!req->sndbuf) { + connman_error("g_try_new0 failed"); g_free(req); - return -1; + return -ENOMEM; } - req->buf[0] = type; - req->buf[1] = req->used - 2; /* except for type and name length */ - memcpy(req->buf + 2, name, req->used - 2); + 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; @@ -339,17 +453,16 @@ static void write_section_kvs(VICISection *section, struct request *req) return; g_hash_table_iter_init (&iter, section->kvs); - while (g_hash_table_iter_next (&iter, &key, &value)) - { + while (g_hash_table_iter_next (&iter, &key, &value)) { if (!key || !value) continue; - vici_kv(req, (const char*)key, (const void *)value, strlen((char *)value)); + key_value(req, (const char*)key, (const void *)value, strlen((char *)value)); } return; } -static void _write_vl(gpointer data, gpointer user_data) +static void write_list_item(gpointer data, gpointer user_data) { struct request *req = NULL; char *value = NULL; @@ -359,7 +472,7 @@ static void _write_vl(gpointer data, gpointer user_data) value = (char *)data; req = (struct request *)user_data; - vici_list_item(req, value, strlen(value)); + list_item(req, value, strlen(value)); return; } @@ -373,87 +486,515 @@ static void write_section_kvls(VICISection *section, struct request *req) return; g_hash_table_iter_init (&iter, section->kvls); - while (g_hash_table_iter_next (&iter, &key, &value)) - { + while (g_hash_table_iter_next (&iter, &key, &value)) { if (!key || !value) continue; - vici_list_start(req, key); - g_slist_foreach((GSList *)value, (GFunc)_write_vl, (gpointer)req); - vici_list_end(req); + 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) +static void write_section(struct request *req, VICISection *section) { GHashTableIter iter; gpointer key, value; - VICISection *subsection; if (req == NULL || section == NULL) return; if (section->name) - vici_section_start(req, 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)) - { + while (g_hash_table_iter_next (&iter, &key, &value)) { if (!key || !value) continue; - subsection = value; - _write_section(req, subsection); + write_section(req, (VICISection *)value); } if (section->name) - vici_section_end(req); + section_end(req); return; } -static int vici_client_send_command(struct request *req) +static int check_socket(int sock) { - return 0; + 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 destroy_vici_request(struct request *req) +static int write_socket(int sock, char *data, int data_len) { - return 0; + 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; } -int vici_client_send_request(const char *cmd, VICISection *root) +static int process_vici_response(struct request * req) { - struct request* req = NULL; + 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 -1; + return ret; } - _write_section(req, root); + 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 = vici_client_send_command(req); + 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; +} - ret = destroy_vici_request(req); - if (ret < 0) { - connman_error("error on destroy_request \n"); +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 int str_to_sock_addr(const char *uri, struct sockaddr_un *addr) +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; @@ -464,23 +1005,26 @@ static int str_to_sock_addr(const char *uri, struct sockaddr_un *addr) return offsetof(struct sockaddr_un, sun_path) + strlen(addr->sun_path); } -static int connect_sock(const char *uri) +static int connect_socket(const char *uri) { struct sockaddr_un addr; int len, fd; fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) + if (fd < 0) { + connman_error("socket() failed"); return -errno; + } - len = str_to_sock_addr(uri, &addr); - if (len == -1) - { + 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; } @@ -488,53 +1032,58 @@ static int connect_sock(const char *uri) return fd; } -static gboolean process_reply(GIOChannel *source, GIOChannel condtion, gpointer user_data) -{ - return TRUE; -} -int vici_client_initialize() +int vici_initialize(VICIClient **vici_client) { GIOChannel *vici_channel; - VICIClient *vici_client = NULL; - vici_client = g_try_new0(VICIClient, 1); - if (!vici_client) { - return VICI_CLIENT_ERROR_NOMEM; + *vici_client = g_try_new0(VICIClient, 1); + if (!*vici_client) { + connman_error("out of memory"); + return -ENOMEM; } - vici_client->client_sock = connect_sock(VICI_DEFAULT_URI); - if (vici_client->client_sock < 0) { - g_free(vici_client); + (*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); + vici_channel = g_io_channel_unix_new((*vici_client)->client_sock_fd); if (!vici_channel) { - close(vici_client->client_sock); - g_free(vici_client); + 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, + (*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, + (gpointer)*vici_client, NULL); g_io_channel_unref(vici_channel); + DBG("connected"); return 0; } -int vici_client_deinitialize() +void vici_set_connect_reply_cb(VICIClient *vici_client, vici_connect_reply_cb reply_cb, gpointer user_data) { - VICIClient *vici_client = NULL; + 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); + 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 index a49f0a78..91200e5a 100644 --- a/vpn/plugins/vici-client.h +++ b/vpn/plugins/vici-client.h @@ -13,32 +13,41 @@ struct _VICISection; typedef struct _VICISection VICISection; typedef enum { - VICI_CLIENT_ERROR_NONE, - VICI_CLIENT_ERROR_NOMEM, -} VICIClientError; - -typedef enum { - VICI_CLIENT_EVENT, -} VICIClientEvent; - -#define VICI_REQUEST_LOAD_CONN "load-conn" -#define VICI_REQUEST_LOAD_SHARED "load-shared" -#define VICI_REQUEST_LOAD_CERT "load-cert" -#define VICI_REQUEST_LOAD_INITIATE "initiate" - -typedef int (*vici_section_add_element)(VICISection *sect, const char *key, + 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 vici_section_add_kv(VICISection *sect, const char *key, +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_section_add_kvl(VICISection *sect, const char *key, +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); -void vici_destroy_section(VICISection *sect); -int vici_client_initialize(); -int vici_client_deinitialize(); -int vici_client_send_request(const char *cmd, VICISection *root); +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 } diff --git a/vpn/plugins/vpn.c b/vpn/plugins/vpn.c index b438d06e..3e40e4fe 100755 --- a/vpn/plugins/vpn.c +++ b/vpn/plugins/vpn.c @@ -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); diff --git a/vpn/plugins/vpn.h b/vpn/plugins/vpn.h index bf56728d..61758636 100755 --- a/vpn/plugins/vpn.h +++ b/vpn/plugins/vpn.h @@ -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, diff --git a/vpn/vpn-config.c b/vpn/vpn-config.c index 293c64e0..a5be332d 100755 --- a/vpn/vpn-config.c +++ b/vpn/vpn-config.c @@ -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; } diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c index 16c0c2be..925f6997 100755 --- a/vpn/vpn-provider.c +++ b/vpn/vpn-provider.c @@ -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); @@ -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, |