diff options
author | Niraj Kumar Goit <niraj.g@samsung.com> | 2022-02-15 10:23:07 +0530 |
---|---|---|
committer | Niraj Kumar Goit <niraj.g@samsung.com> | 2022-02-15 10:25:05 +0530 |
commit | f89b473dfd8e916314b534b3397442f8c869c783 (patch) | |
tree | b6f15a191886ac5b78e98b1b2b210adefae69cf2 /vpn/plugins | |
parent | dd3cccc5e67548dcc2dd6c6254ed6c97859085d5 (diff) | |
download | connman-f89b473dfd8e916314b534b3397442f8c869c783.tar.gz connman-f89b473dfd8e916314b534b3397442f8c869c783.tar.bz2 connman-f89b473dfd8e916314b534b3397442f8c869c783.zip |
Imported Upstream version 1.40upstream/1.40
Change-Id: Id3e405d088ee3fb19fd0ca049e1cb7f812b40fca
Signed-off-by: Niraj Kumar Goit <niraj.g@samsung.com>
Diffstat (limited to 'vpn/plugins')
-rw-r--r-- | vpn/plugins/l2tp.c | 2 | ||||
-rw-r--r-- | vpn/plugins/openconnect.c | 899 | ||||
-rw-r--r-- | vpn/plugins/openvpn.c | 32 | ||||
-rw-r--r-- | vpn/plugins/pptp.c | 32 | ||||
-rw-r--r-- | vpn/plugins/vpn.c | 61 | ||||
-rw-r--r-- | vpn/plugins/vpn.h | 2 | ||||
-rw-r--r-- | vpn/plugins/vpnc.c | 76 | ||||
-rw-r--r-- | vpn/plugins/wireguard.c | 96 |
8 files changed, 736 insertions, 464 deletions
diff --git a/vpn/plugins/l2tp.c b/vpn/plugins/l2tp.c index 48894aa5..1e4fcd1f 100644 --- a/vpn/plugins/l2tp.c +++ b/vpn/plugins/l2tp.c @@ -246,6 +246,8 @@ static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider) connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask, gateway); + connman_ipaddress_set_p2p(ipaddress, true); + vpn_provider_set_ipaddress(provider, ipaddress); vpn_provider_set_nameservers(provider, nameservers); diff --git a/vpn/plugins/openconnect.c b/vpn/plugins/openconnect.c index d600e61e..fc6ceff0 100644 --- a/vpn/plugins/openconnect.c +++ b/vpn/plugins/openconnect.c @@ -42,6 +42,8 @@ #include <connman/setting.h> #include <connman/vpn-dbus.h> +#include <openconnect.h> + #include "../vpn-provider.h" #include "../vpn-agent.h" @@ -89,7 +91,6 @@ enum oc_connect_type { static const char *connect_types[] = {"cookie", "cookie_with_userpass", "userpass", "publickey", "pkcs", NULL}; -static const char *protocols[] = { "anyconnect", "nc", "gp", NULL}; struct oc_private_data { struct vpn_provider *provider; @@ -98,21 +99,46 @@ struct oc_private_data { char *dbus_sender; vpn_provider_connect_cb_t cb; void *user_data; + + GThread *cookie_thread; + struct openconnect_info *vpninfo; + int fd_cmd; + int err; + int fd_in; - int out_ch_id; int err_ch_id; - GIOChannel *out_ch; GIOChannel *err_ch; enum oc_connect_type connect_type; - bool interactive; + bool tried_passphrase; }; +typedef void (*request_input_reply_cb_t) (DBusMessage *reply, + void *user_data); + +static int run_connect(struct oc_private_data *data, const char *cookie); +static int request_input_credentials_full( + struct oc_private_data *data, + request_input_reply_cb_t cb, + void *user_data); + static bool is_valid_protocol(const char* protocol) { + int num_protocols; + int i; + struct oc_vpn_proto *protos; + if (!protocol || !*protocol) return false; - return g_strv_contains(protocols, protocol); + num_protocols = openconnect_get_supported_protocols(&protos); + + for (i = 0; i < num_protocols; i++) + if (!strcmp(protos[i].name, protocol)) + break; + + openconnect_free_supported_protocols(protos); + + return i < num_protocols; } static void oc_connect_done(struct oc_private_data *data, int err) @@ -139,11 +165,7 @@ static void close_io_channel(struct oc_private_data *data, GIOChannel *channel) if (!data || !channel) return; - if (data->out_ch == channel) { - id = data->out_ch_id; - data->out_ch = NULL; - data->out_ch_id = 0; - } else if (data->err_ch == channel) { + if (data->err_ch == channel) { id = data->err_ch_id; data->err_ch = NULL; data->err_ch_id = 0; @@ -167,6 +189,9 @@ static void free_private_data(struct oc_private_data *data) connman_info("provider %p", data->provider); + if (data->vpninfo) + openconnect_vpninfo_free(data->vpninfo); + if (vpn_provider_get_plugin_data(data->provider) == data) vpn_provider_set_plugin_data(data->provider, NULL); @@ -175,7 +200,6 @@ static void free_private_data(struct oc_private_data *data) if (data->fd_in > 0) close(data->fd_in); data->fd_in = -1; - close_io_channel(data, data->out_ch); close_io_channel(data, data->err_ch); g_free(data->dbus_sender); @@ -357,6 +381,8 @@ static int oc_notify(DBusMessage *msg, struct vpn_provider *provider) else connman_ipaddress_set_ipv6(ipaddress, addressv6, prefix_len, gateway); + + connman_ipaddress_set_p2p(ipaddress, true); vpn_provider_set_ipaddress(provider, ipaddress); vpn_provider_set_domain(provider, domain); @@ -371,7 +397,7 @@ static int oc_notify(DBusMessage *msg, struct vpn_provider *provider) return VPN_STATE_CONNECT; } -static ssize_t full_write(int fd, const void *buf, size_t len) +static ssize_t full_write(int fd, const char *buf, size_t len) { ssize_t byte_write; @@ -426,37 +452,6 @@ static void oc_died(struct connman_task *task, int exit_code, void *user_data) free_private_data(data); } -static gboolean io_channel_out_cb(GIOChannel *source, GIOCondition condition, - gpointer user_data) -{ - struct oc_private_data *data; - char *str; - - data = user_data; - - if (data->out_ch != source) - return G_SOURCE_REMOVE; - - if ((condition & G_IO_IN) && - g_io_channel_read_line(source, &str, NULL, NULL, NULL) == - G_IO_STATUS_NORMAL) { - - g_strchomp(str); - - /* Only cookie is printed to stdout */ - vpn_provider_set_string_hide_value(data->provider, - "OpenConnect.Cookie", str); - - g_free(str); - } else if (condition & (G_IO_ERR | G_IO_HUP)) { - connman_info("Out channel termination"); - close_io_channel(data, source); - return G_SOURCE_REMOVE; - } - - return G_SOURCE_CONTINUE; -} - static bool strv_contains_prefix(const char *strv[], const char *str) { int i; @@ -472,56 +467,170 @@ static bool strv_contains_prefix(const char *strv[], const char *str) return false; } -static void clear_provider_credentials(struct vpn_provider *provider) +static void clear_provider_credentials(struct vpn_provider *provider, + bool clear_pkcs_pass) { - const char *keys[] = { "OpenConnect.Username", + const char *keys[] = { "OpenConnect.PKCSPassword", + "OpenConnect.Username", "OpenConnect.Password", - "OpenConnect.PKCSPassword", "OpenConnect.Cookie", NULL }; - int i; + size_t i; connman_info("provider %p", provider); - for (i = 0; keys[i]; i++) { + for (i = !clear_pkcs_pass; keys[i]; i++) { if (!vpn_provider_get_string_immutable(provider, keys[i])) vpn_provider_set_string_hide_value(provider, keys[i], "-"); } } -typedef void (* request_input_reply_cb_t) (DBusMessage *reply, - void *user_data); +static void __attribute__ ((format(printf, 3, 4))) oc_progress(void *user_data, + int level, const char *fmt, ...) +{ + va_list ap; + char *msg; -static int request_input_credentials(struct oc_private_data *data, - request_input_reply_cb_t cb); + va_start(ap, fmt); + msg = g_strdup_vprintf(fmt, ap); + + connman_debug("%s", msg); + g_free(msg); + + va_end(ap); +} + +/* + * There is no enum / defines for these in openconnect.h, but these values + * are based on the comment for openconnect_validate_peer_cert_vfn. + */ +enum oc_cert_status { + OC_CERT_ACCEPT = 0, + OC_CERT_REJECT = 1 +}; + +struct validate_cert_data { + GMutex mutex; + GCond cond; + const char *reason; + struct oc_private_data *data; + bool processed; + enum oc_cert_status status; +}; + +static gboolean validate_cert(void *user_data) +{ + struct validate_cert_data *cert_data = user_data; + struct oc_private_data *data; + const char *server_cert; + bool allow_self_signed; + + DBG(""); + + g_mutex_lock(&cert_data->mutex); + + data = cert_data->data; + server_cert = vpn_provider_get_string(data->provider, + "OpenConnect.ServerCert"); + allow_self_signed = vpn_provider_get_boolean(data->provider, + "OpenConnect.AllowSelfSignedCert", + false); + if (!allow_self_signed) { + cert_data->status = OC_CERT_REJECT; + } else if (server_cert) { + /* + * Check peer cert hash may return negative values on errors, + * but anything non-zero is acceptable. + */ + cert_data->status = openconnect_check_peer_cert_hash( + data->vpninfo, + server_cert); + } else { + /* + * We could verify this from the agent at this point, and + * release the thread upon reply. + */ + DBG("Server cert hash: %s", + openconnect_get_peer_cert_hash(data->vpninfo)); + vpn_provider_set_string(data->provider, + "OpenConnect.ServerCert", + openconnect_get_peer_cert_hash(data->vpninfo)); + cert_data->status = OC_CERT_ACCEPT; + } + + cert_data->processed = true; + g_cond_signal(&cert_data->cond); + g_mutex_unlock(&cert_data->mutex); + + return G_SOURCE_REMOVE; +} + +static int oc_validate_peer_cert(void *user_data, const char *reason) +{ + struct validate_cert_data data = { .reason = reason, + .data = user_data, + .processed = false }; + + g_cond_init(&data.cond); + g_mutex_init(&data.mutex); + + g_mutex_lock(&data.mutex); + + g_idle_add(validate_cert, &data); + + while (!data.processed) + g_cond_wait(&data.cond, &data.mutex); + + g_mutex_unlock(&data.mutex); + + g_mutex_clear(&data.mutex); + g_cond_clear(&data.cond); + + return data.status; +} + +struct process_form_data { + GMutex mutex; + GCond cond; + struct oc_auth_form *form; + struct oc_private_data *data; + bool processed; + int status; +}; static void request_input_pkcs_reply(DBusMessage *reply, void *user_data) { - struct oc_private_data *data = user_data; - const char *password = NULL; + struct process_form_data *form_data = user_data; + struct oc_private_data *data = form_data->data; + struct oc_form_opt *opt; const char *key; + const char *password = NULL; DBusMessageIter iter, dict; - int err; connman_info("provider %p", data->provider); - if (!reply) + if (!reply) { + data->err = ENOENT; goto err; + } - err = vpn_agent_check_and_process_reply_error(reply, data->provider, - data->task, data->cb, data->user_data); - if (err) { - /* Ensure cb is called only once */ + if ((data->err = vpn_agent_check_and_process_reply_error(reply, + data->provider, + data->task, + data->cb, + data->user_data))) { data->cb = NULL; data->user_data = NULL; goto err; } - if (!vpn_agent_check_reply_has_dict(reply)) + if (!vpn_agent_check_reply_has_dict(reply)) { + data->err = ENOENT; goto err; + } dbus_message_iter_init(reply, &iter); dbus_message_iter_recurse(&iter, &dict); @@ -551,39 +660,44 @@ static void request_input_pkcs_reply(DBusMessage *reply, void *user_data) dbus_message_iter_next(&dict); } - if (data->connect_type != OC_CONNECT_PKCS || !password) + if (!password) goto err; - if (write_data(data->fd_in, password) != 0) { - connman_error("openconnect failed to take PKCS pass phrase on" - " stdin"); - goto err; + for (opt = form_data->form->opts; opt; opt = opt->next) { + if (opt->flags & OC_FORM_OPT_IGNORE) + continue; + + if (opt->type == OC_FORM_OPT_PASSWORD && + g_str_has_prefix(opt->name, + "openconnect_pkcs")) { + opt->_value = strdup(password); + form_data->status = OC_FORM_RESULT_OK; + data->tried_passphrase = true; + break; + } } - clear_provider_credentials(data->provider); + goto out; - return; err: - oc_connect_done(data, EACCES); + form_data->status = OC_FORM_RESULT_ERR; + +out: + form_data->processed = true; + g_cond_signal(&form_data->cond); + g_mutex_unlock(&form_data->mutex); } static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, - gpointer user_data) + gpointer user_data) { struct oc_private_data *data; const char *auth_failures[] = { - /* Login failed */ - "Got HTTP response: HTTP/1.1 401 Unauthorized", - "Failed to obtain WebVPN cookie", /* Cookie not valid */ "Got inappropriate HTTP CONNECT response: " "HTTP/1.1 401 Unauthorized", /* Invalid cookie */ "VPN service unavailable", - /* Problem with certificates */ - "SSL connection failure", - "Creating SSL connection failed", - "SSL connection cancelled", NULL }; const char *conn_failures[] = { @@ -591,21 +705,8 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, "Failed to open HTTPS connection to", NULL }; - /* Handle both PKCS#12 and PKCS#8 failures */ - const char *pkcs_failures[] = { - "Failed to decrypt PKCS#12 certificate file", - "Failed to decrypt PKCS#8 certificate file", - NULL - }; - /* Handle both PKCS#12 and PKCS#8 requests */ - const char *pkcs_requests[] = { - "Enter PKCS#12 pass phrase", - "Enter PKCS#8 pass phrase", - NULL - }; - const char *server_key_hash = " --servercert"; + const char *server_key_hash = " --servercert "; char *str; - bool close = false; int err = 0; data = user_data; @@ -618,51 +719,12 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, if ((condition & G_IO_IN)) { gsize len; - int pos; - - if (!data->interactive) { - if (g_io_channel_read_line(source, &str, &len, NULL, - NULL) != G_IO_STATUS_NORMAL) - err = EIO; - else - str[len - 1] = '\0'; - } else { - GIOStatus status; - str = g_try_new0(char, OC_MAX_READBUF_LEN); - if (!str) - return G_SOURCE_REMOVE; - for (pos = 0; pos < OC_MAX_READBUF_LEN - 1 ; ++pos) { - status = g_io_channel_read_chars(source, - str+pos, 1, &len, NULL); - - if (status == G_IO_STATUS_EOF) { - break; - } else if (status != G_IO_STATUS_NORMAL) { - err = EIO; - break; - } - - /* Ignore control chars and digits at start */ - if (!pos && (g_ascii_iscntrl(str[pos]) || - g_ascii_isdigit(str[pos]))) - --pos; - - /* Read zero length or no more to read */ - if (!len || g_io_channel_get_buffer_condition( - source) != G_IO_IN || - str[pos] == '\n') - break; - } - - /* - * When self signed certificates are allowed and server - * SHA1 fingerprint is printed to stderr there is a - * newline char at the end of SHA1 fingerprint. - */ - if (str[pos] == '\n') - str[pos] = '\0'; - } + if (g_io_channel_read_line(source, &str, &len, NULL, + NULL) != G_IO_STATUS_NORMAL) + err = EIO; + else + g_strchomp(str); connman_info("openconnect: %s", str); @@ -687,44 +749,12 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, vpn_provider_set_string(data->provider, "OpenConnect.ServerCert", - fingerprint); - - /* - * OpenConnect waits for "yes" or "no" as - * response to certificate acceptance request. - */ - if (write_data(data->fd_in, "yes") != 0) - connman_error("openconnect: cannot " - "write answer to certificate " - "accept request"); - + str + strlen(server_key_hash)); } else { connman_warn("Self signed certificate is not " - " allowed"); - - /* - * Close IO channel to avoid deadlock as an - * answer is expected for the certificate - * accept request. - */ - close = true; + "allowed"); err = ECONNREFUSED; } - } else if (strv_contains_prefix(pkcs_failures, str)) { - connman_warn("PKCS failure: %s", str); - close = true; - err = EACCES; - } else if (strv_contains_prefix(pkcs_requests, str)) { - connman_info("PKCS file pass phrase request: %s", str); - err = request_input_credentials(data, - request_input_pkcs_reply); - - if (err != -EINPROGRESS) { - err = EACCES; - close = true; - } else { - err = 0; - } } else if (strv_contains_prefix(auth_failures, str)) { connman_warn("authentication failed: %s", str); err = EACCES; @@ -736,13 +766,14 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, g_free(str); } else if (condition & (G_IO_ERR | G_IO_HUP)) { connman_info("Err channel termination"); - close = true; + close_io_channel(data, source); + return G_SOURCE_REMOVE; } if (err) { switch (err) { case EACCES: - clear_provider_credentials(data->provider); + clear_provider_credentials(data->provider, true); break; case ECONNREFUSED: /* @@ -756,168 +787,278 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, oc_connect_done(data, err); } - if (close) { - close_io_channel(data, source); - return G_SOURCE_REMOVE; - } - return G_SOURCE_CONTINUE; } -static int run_connect(struct oc_private_data *data) +static gboolean process_auth_form(void *user_data) { - struct vpn_provider *provider; - struct connman_task *task; - const char *vpnhost; - const char *vpncookie = NULL; - const char *username; - const char *password = NULL; - const char *certificate = NULL; - const char *private_key; - const char *setting_str; - bool setting; - bool use_stdout = false; - int fd_out = -1; - int fd_err; - int err = 0; - - if (!data) - return -EINVAL; + struct process_form_data *form_data = user_data; + struct oc_private_data *data = form_data->data; + struct oc_form_opt *opt; + const char *password; - provider = data->provider; - task = data->task; + g_mutex_lock(&form_data->mutex); - connman_info("provider %p task %p", provider, task); + DBG(""); switch (data->connect_type) { - case OC_CONNECT_COOKIE: - vpncookie = vpn_provider_get_string(provider, - "OpenConnect.Cookie"); - if (!vpncookie || !g_strcmp0(vpncookie, "-")) { - err = -EACCES; - goto done; - } - - connman_task_add_argument(task, "--cookie-on-stdin", NULL); - break; + case OC_CONNECT_USERPASS: case OC_CONNECT_COOKIE_WITH_USERPASS: - vpncookie = vpn_provider_get_string(provider, - "OpenConnect.Cookie"); - /* No cookie set yet, username and password used first */ - if (!vpncookie || !g_strcmp0(vpncookie, "-")) { - username = vpn_provider_get_string(provider, - "OpenConnect.Username"); - password = vpn_provider_get_string(provider, - "OpenConnect.Password"); - if (!username || !password || - !g_strcmp0(username, "-") || - !g_strcmp0(password, "-")) { - err = -EACCES; - goto done; - } + break; + + case OC_CONNECT_PKCS: + password = vpn_provider_get_string(data->provider, + "OpenConnect.PKCSPassword"); - connman_task_add_argument(task, "--cookieonly", NULL); - connman_task_add_argument(task, "--user", username); - connman_task_add_argument(task, "--passwd-on-stdin", - NULL); + for (opt = form_data->form->opts; opt; opt = opt->next) { + if (opt->flags & OC_FORM_OPT_IGNORE) + continue; - /* Use stdout only when cookie is to be read. */ - use_stdout = true; - } else { - connman_task_add_argument(task, "--cookie-on-stdin", - NULL); + if (opt->type == OC_FORM_OPT_PASSWORD && + g_str_has_prefix(opt->name, + "openconnect_pkcs")) + break; } - break; - case OC_CONNECT_USERPASS: - username = vpn_provider_get_string(provider, - "OpenConnect.Username"); - password = vpn_provider_get_string(provider, - "OpenConnect.Password"); - if (!username || !password || !g_strcmp0(username, "-") || - !g_strcmp0(password, "-")) { - err = -EACCES; - goto done; + if (opt) { + if (password && g_strcmp0(password, "-")) { + opt->_value = strdup(password); + data->tried_passphrase = true; + form_data->status = OC_FORM_RESULT_OK; + goto out; + } else { + if (data->tried_passphrase) { + vpn_provider_add_error(data->provider, + VPN_PROVIDER_ERROR_AUTH_FAILED); + clear_provider_credentials( + data->provider, + true); + } + request_input_credentials_full(data, + request_input_pkcs_reply, + form_data); + return G_SOURCE_REMOVE; + } } - connman_task_add_argument(task, "--user", username); - connman_task_add_argument(task, "--passwd-on-stdin", NULL); - break; + /* fall-through */ + + /* + * In case of public key, reaching here means that the + * passphrase previously provided was incorrect. + */ case OC_CONNECT_PUBLICKEY: - certificate = vpn_provider_get_string(provider, - "OpenConnect.ClientCert"); - private_key = vpn_provider_get_string(provider, - "OpenConnect.UserPrivateKey"); + data->err = -EACCES; + clear_provider_credentials(data->provider, true); - if (!certificate || !private_key) { - err = -EACCES; - goto done; - } + /* fall-through */ + default: + form_data->status = OC_FORM_RESULT_ERR; + goto out; + } - connman_task_add_argument(task, "--certificate", certificate); - connman_task_add_argument(task, "--sslkey", private_key); - break; - case OC_CONNECT_PKCS: - certificate = vpn_provider_get_string(provider, - "OpenConnect.PKCSClientCert"); - if (!certificate) { - err = -EACCES; - goto done; + /* + * Form values are released with free(), so always use strdup() + * instead of g_strdup() + */ + for (opt = form_data->form->opts; opt; opt = opt->next) { + if (opt->flags & OC_FORM_OPT_IGNORE) + continue; + + if (opt->type == OC_FORM_OPT_TEXT && + g_str_has_prefix(opt->name, "user")) { + const char *user = vpn_provider_get_string( + data->provider, + "OpenConnect.Username"); + if (user) + opt->_value = strdup(user); + } else if (opt->type == OC_FORM_OPT_PASSWORD) { + const char *pass = vpn_provider_get_string( + data->provider, + "OpenConnect.Password"); + if (pass) + opt->_value = strdup(pass); } + } - connman_task_add_argument(task, "--certificate", certificate); + form_data->status = OC_FORM_RESULT_OK; - password = vpn_provider_get_string(data->provider, - "OpenConnect.PKCSPassword"); - /* Add password only if it is has been set */ - if (!password || !g_strcmp0(password, "-")) - break; +out: + form_data->processed = true; + g_cond_signal(&form_data->cond); + g_mutex_unlock(&form_data->mutex); + + return G_SOURCE_REMOVE; +} + +static int oc_process_auth_form(void *user_data, struct oc_auth_form *form) +{ + struct process_form_data data = { .form = form, + .data = user_data, + .processed = false }; + + DBG(""); + + g_cond_init(&data.cond); + g_mutex_init(&data.mutex); + + g_mutex_lock(&data.mutex); + g_idle_add(process_auth_form, &data); + + while (!data.processed) + g_cond_wait(&data.cond, &data.mutex); + + g_mutex_unlock(&data.mutex); + + g_mutex_clear(&data.mutex); + g_cond_clear(&data.cond); + + return data.status; +} + +static gboolean authenticated(void *user_data) +{ + struct oc_private_data *data = user_data; + int rv = GPOINTER_TO_INT(g_thread_join(data->cookie_thread)); + + DBG(""); + + data->cookie_thread = NULL; + + if (rv == 0) + rv = run_connect(data, openconnect_get_cookie(data->vpninfo)); + else if (rv < 0) + clear_provider_credentials(data->provider, true); + + openconnect_vpninfo_free(data->vpninfo); + data->vpninfo = NULL; + + if (rv != -EINPROGRESS) { + oc_connect_done(data, data->err ? data->err : rv); + free_private_data(data); + } - connman_task_add_argument(task, "--passwd-on-stdin", NULL); + return G_SOURCE_REMOVE; +} + +static void *obtain_cookie_thread(void *user_data) +{ + struct oc_private_data *data = user_data; + int ret; + + DBG("%p", data->vpninfo); + + ret = openconnect_obtain_cookie(data->vpninfo); + + g_idle_add(authenticated, data); + + return GINT_TO_POINTER(ret); +} + +static int authenticate(struct oc_private_data *data) +{ + const char *cert = NULL; + const char *key = NULL; + const char *urlpath; + const char *vpnhost; + + DBG(""); + + switch (data->connect_type) { + case OC_CONNECT_PKCS: + cert = vpn_provider_get_string(data->provider, + "OpenConnect.PKCSClientCert"); break; + case OC_CONNECT_PUBLICKEY: + cert = vpn_provider_get_string(data->provider, + "OpenConnect.ClientCert"); + key = vpn_provider_get_string(data->provider, + "OpenConnect.UserPrivateKey"); + break; + + case OC_CONNECT_USERPASS: + case OC_CONNECT_COOKIE_WITH_USERPASS: + break; + + default: + return -EINVAL; } - vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost"); + openconnect_init_ssl(); + data->vpninfo = openconnect_vpninfo_new("ConnMan VPN Agent", + oc_validate_peer_cert, + NULL, + oc_process_auth_form, + oc_progress, + data); + + /* Replicating how openconnect's --usergroup argument works */ + urlpath = vpn_provider_get_string(data->provider, + "OpenConnect.Usergroup"); + if (urlpath) + openconnect_set_urlpath(data->vpninfo, urlpath); + + if (vpn_provider_get_boolean(data->provider, + "OpenConnect.DisableIPv6", false)) + openconnect_disable_ipv6(data->vpninfo); + + vpnhost = vpn_provider_get_string(data->provider, + "OpenConnect.VPNHost"); if (!vpnhost || !*vpnhost) - vpnhost = vpn_provider_get_string(provider, "Host"); + vpnhost = vpn_provider_get_string(data->provider, "Host"); - task_append_config_data(provider, task); + openconnect_set_hostname(data->vpninfo, vpnhost); + + if (cert) + openconnect_set_client_cert(data->vpninfo, cert, key); + + data->fd_cmd = openconnect_setup_cmd_pipe(data->vpninfo); /* - * To clarify complex situation, if cookie is expected to be printed - * to stdout all other output must go to syslog. But with PKCS all - * output must be caught in order to get message about file decryption - * error. For this reason, the mode has to be interactive as well. + * openconnect_obtain_cookie blocks, so run it in background thread + * instead */ - switch (data->connect_type) { - case OC_CONNECT_COOKIE: - /* fall through */ - case OC_CONNECT_COOKIE_WITH_USERPASS: - /* fall through */ - case OC_CONNECT_USERPASS: - /* fall through */ - case OC_CONNECT_PUBLICKEY: - connman_task_add_argument(task, "--syslog", NULL); + data->cookie_thread = g_thread_try_new("obtain_cookie", + obtain_cookie_thread, + data, NULL); + + if (!data->cookie_thread) + return -EIO; + + return -EINPROGRESS; +} - setting = vpn_provider_get_boolean(provider, +static int run_connect(struct oc_private_data *data, const char *cookie) +{ + struct vpn_provider *provider; + struct connman_task *task; + const char *vpnhost; + int fd_err; + int err = 0; + bool allow_self_signed; + const char *server_cert; + + if (!data || !cookie) + return -EINVAL; + + provider = data->provider; + task = data->task; + + server_cert = vpn_provider_get_string(provider, + "OpenConnect.ServerCert"); + allow_self_signed = vpn_provider_get_boolean(provider, "OpenConnect.AllowSelfSignedCert", false); - setting_str = vpn_provider_get_string(provider, - "OpenConnect.ServerCert"); - /* - * Run in interactive mode if self signed certificates are - * allowed and there is no set server SHA1 fingerprint. - */ - if (setting_str || !setting) - connman_task_add_argument(task, "--non-inter", NULL); - else - data->interactive = true; - break; - case OC_CONNECT_PKCS: - data->interactive = true; - break; - } + DBG("provider %p task %p", provider, task); + + connman_task_add_argument(task, "--cookie-on-stdin", NULL); + + vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost"); + if (!vpnhost || !*vpnhost) + vpnhost = vpn_provider_get_string(provider, "Host"); + + task_append_config_data(provider, task); connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script"); @@ -925,66 +1066,30 @@ static int run_connect(struct oc_private_data *data) connman_task_add_argument(task, (char *)vpnhost, NULL); - err = connman_task_run(task, oc_died, data, &data->fd_in, use_stdout ? - &fd_out : NULL, &fd_err); + err = connman_task_run(task, oc_died, data, &data->fd_in, + NULL, &fd_err); if (err < 0) { err = -EIO; goto done; } - switch (data->connect_type) { - case OC_CONNECT_COOKIE: - if (write_data(data->fd_in, vpncookie) != 0) { - connman_error("openconnect failed to take cookie on " - "stdin"); - err = -EIO; - } - - break; - case OC_CONNECT_USERPASS: - if (write_data(data->fd_in, password) != 0) { - connman_error("openconnect failed to take password on " - "stdin"); - err = -EIO; - } - - break; - case OC_CONNECT_COOKIE_WITH_USERPASS: - if (!vpncookie || !g_strcmp0(vpncookie, "-")) { - if (write_data(data->fd_in, password) != 0) { - connman_error("openconnect failed to take " - "password on stdin"); - err = -EIO; - } - } else { - if (write_data(data->fd_in, vpncookie) != 0) { - connman_error("openconnect failed to take " - "cookie on stdin"); - err = -EIO; - } - } - - break; - case OC_CONNECT_PUBLICKEY: - break; - case OC_CONNECT_PKCS: - if (!password || !g_strcmp0(password, "-")) - break; + if (write_data(data->fd_in, cookie) != 0) { + connman_error("openconnect failed to take cookie on " + "stdin"); + err = -EIO; + } - if (write_data(data->fd_in, password) != 0) { - connman_error("openconnect failed to take PKCS " - "pass phrase on stdin"); + if (!server_cert || !allow_self_signed) { + if (write_data(data->fd_in, + (allow_self_signed ? "yes" : "no"))) { + connman_error("openconnect failed to take certificate " + "acknowledgement on stdin"); err = -EIO; } - - break; } if (err) { - if (fd_out > 0) - close(fd_out); - - if (fd_err > 0) + if (fd_err >= 0) close(fd_err); goto done; @@ -992,22 +1097,6 @@ static int run_connect(struct oc_private_data *data) err = -EINPROGRESS; - if (use_stdout) { - data->out_ch = g_io_channel_unix_new(fd_out); - - /* Use ASCII encoding only */ - if (g_io_channel_set_encoding(data->out_ch, NULL, NULL) != - G_IO_STATUS_NORMAL) { - close_io_channel(data, data->out_ch); - err = -EIO; - } else { - data->out_ch_id = g_io_add_watch(data->out_ch, - G_IO_IN | G_IO_ERR | G_IO_HUP, - (GIOFunc)io_channel_out_cb, - data); - } - } - data->err_ch = g_io_channel_unix_new(fd_err); /* Use ASCII encoding only */ @@ -1022,7 +1111,7 @@ static int run_connect(struct oc_private_data *data) } done: - clear_provider_credentials(data->provider); + clear_provider_credentials(data->provider, err != -EINPROGRESS); return err; } @@ -1119,8 +1208,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) connman_info("provider %p", data->provider); - if (!reply) + if (!reply) { + err = ENOENT; goto err; + } err = vpn_agent_check_and_process_reply_error(reply, data->provider, data->task, data->cb, data->user_data); @@ -1131,8 +1222,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) goto out; } - if (!vpn_agent_check_reply_has_dict(reply)) + if (!vpn_agent_check_reply_has_dict(reply)) { + err = ENOENT; goto err; + } dbus_message_iter_init(reply, &iter); dbus_message_iter_recurse(&iter, &dict); @@ -1224,41 +1317,53 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) switch (data->connect_type) { case OC_CONNECT_COOKIE: - if (!cookie) + if (!cookie) { + err = EACCES; goto err; + } break; case OC_CONNECT_USERPASS: /* fall through */ case OC_CONNECT_COOKIE_WITH_USERPASS: - if (!username || !password) + if (!username || !password) { + err = EACCES; goto err; + } break; case OC_CONNECT_PUBLICKEY: break; // This should not be reached. case OC_CONNECT_PKCS: - if (!pkcspassword) + if (!pkcspassword) { + err = EACCES; goto err; + } break; } - err = run_connect(data); + if (cookie) + err = run_connect(data, cookie); + else + err = authenticate(data); + if (err != -EINPROGRESS) goto err; return; err: - oc_connect_done(data, EACCES); + oc_connect_done(data, err); out: free_private_data(data); } -static int request_input_credentials(struct oc_private_data *data, - request_input_reply_cb_t cb) +static int request_input_credentials_full( + struct oc_private_data *data, + request_input_reply_cb_t cb, + void *user_data) { DBusMessage *message; const char *path; @@ -1299,7 +1404,7 @@ static int request_input_credentials(struct oc_private_data *data, /* * For backwards compatibility add OpenConnect.ServerCert and - * OpenConnect.VPNHost as madnatory only in the default authentication + * OpenConnect.VPNHost as mandatory only in the default authentication * mode. Otherwise. add the fields as informational. These should be * set in provider settings and not to be queried with every connection * attempt. @@ -1343,6 +1448,16 @@ static int request_input_credentials(struct oc_private_data *data, request_input_append_informational, "OpenConnect.PKCSClientCert"); + /* Do not allow to store or retrieve the encrypted PKCS pass */ + vpn_agent_append_allow_credential_storage(&dict, false); + vpn_agent_append_allow_credential_retrieval(&dict, false); + + /* + * Indicate to keep credentials, the PKCS password should not + * affect the credential storing. + */ + vpn_agent_append_keep_credentials(&dict, true); + request_input_append_to_dict(data->provider, &dict, request_input_append_password, "OpenConnect.PKCSPassword"); @@ -1354,7 +1469,7 @@ static int request_input_credentials(struct oc_private_data *data, connman_dbus_dict_close(&iter, &dict); err = connman_agent_queue_message(data->provider, message, - connman_timeout_input_request(), cb, data, agent); + connman_timeout_input_request(), cb, user_data, agent); dbus_message_unref(message); @@ -1366,6 +1481,12 @@ static int request_input_credentials(struct oc_private_data *data, return -EINPROGRESS; } +static int request_input_credentials(struct oc_private_data *data, + request_input_reply_cb_t cb) +{ + return request_input_credentials_full(data, cb, data); +} + static enum oc_connect_type get_authentication_type( struct vpn_provider *provider) { @@ -1395,7 +1516,7 @@ static int oc_connect(struct vpn_provider *provider, const char *dbus_sender, void *user_data) { struct oc_private_data *data; - const char *vpncookie; + const char *vpncookie = NULL; const char *certificate; const char *username; const char *password; @@ -1481,7 +1602,9 @@ static int oc_connect(struct vpn_provider *provider, break; } - return run_connect(data); + if (vpncookie && g_strcmp0(vpncookie, "-")) + return run_connect(data, vpncookie); + return authenticate(data); request_input: err = request_input_credentials(data, request_input_credentials_reply); @@ -1497,6 +1620,8 @@ request_input: static void oc_disconnect(struct vpn_provider *provider) { + struct oc_private_data *data; + connman_info("provider %p", provider); if (!provider) @@ -1508,6 +1633,19 @@ static void oc_disconnect(struct vpn_provider *provider) * agent request to avoid having multiple ones visible. */ connman_agent_cancel(provider); + + data = vpn_provider_get_plugin_data(provider); + + if (!data) + return; + + if (data->cookie_thread) { + char cmd = OC_CMD_CANCEL; + int w = write(data->fd_cmd, &cmd, 1); + if (w != 1) + DBG("Write failed, might be leaking a thread"); + } + } static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile) @@ -1547,7 +1685,7 @@ static int oc_error_code(struct vpn_provider *provider, int exit_code) switch (exit_code) { case 2: /* Cookie has failed */ - clear_provider_credentials(provider); + clear_provider_credentials(provider, false); return VPN_PROVIDER_ERROR_LOGIN_FAILED; case 1: /* fall through */ @@ -1557,7 +1695,8 @@ static int oc_error_code(struct vpn_provider *provider, int exit_code) } static int oc_route_env_parse(struct vpn_provider *provider, const char *key, - int *family, unsigned long *idx, enum vpn_provider_route_type *type) + int *family, unsigned long *idx, + enum vpn_provider_route_type *type) { char *end; const char *start; diff --git a/vpn/plugins/openvpn.c b/vpn/plugins/openvpn.c index bc0303c2..daf66cd5 100644 --- a/vpn/plugins/openvpn.c +++ b/vpn/plugins/openvpn.c @@ -51,7 +51,6 @@ #include "../vpn-agent.h" #include "vpn.h" -#include "../vpn.h" #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) @@ -84,6 +83,9 @@ struct { { "OpenVPN.ConfigFile", "--config", 1 }, { "OpenVPN.DeviceType", NULL, 1 }, { "OpenVPN.Verb", "--verb", 1 }, + { "OpenVPN.Ping", "--ping", 1}, + { "OpenVPN.PingExit", "--ping-exit", 1}, + { "OpenVPN.RemapUsr1", "--remap-usr1", 1}, }; struct ov_private_data { @@ -290,6 +292,7 @@ static int ov_notify(DBusMessage *msg, struct vpn_provider *provider) connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway); connman_ipaddress_set_peer(ipaddress, peer); + connman_ipaddress_set_p2p(ipaddress, true); vpn_provider_set_ipaddress(provider, ipaddress); if (nameserver_list) { @@ -497,16 +500,13 @@ static int run_connect(struct ov_private_data *data, connman_task_add_argument(task, "--ifconfig-noexec", NULL); /* - * Disable client restarts because we can't handle this at the - * moment. The problem is that when OpenVPN decides to switch + * Disable client restarts with TCP because we can't handle this at + * the moment. The problem is that when OpenVPN decides to switch * from CONNECTED state to RECONNECTING and then to RESOLVE, * it is not possible to do a DNS lookup. The DNS server is * not accessible through the tunnel anymore and so we end up * trying to resolve the OpenVPN servers address. - */ - connman_task_add_argument(task, "--ping-restart", "0"); - - /* + * * Disable connetion retrying when OpenVPN is connected over TCP. * With TCP OpenVPN attempts to handle reconnection silently without * reporting the error back when establishing a connection or @@ -516,8 +516,24 @@ static int run_connect(struct ov_private_data *data, * including DNS. */ option = vpn_provider_get_string(provider, "OpenVPN.Proto"); - if (option && g_str_has_prefix(option, "tcp")) + if (option && g_str_has_prefix(option, "tcp")) { + option = vpn_provider_get_string(provider, "OpenVPN.PingExit"); + if (!option) + connman_task_add_argument(task, "--ping-restart", "0"); + connman_task_add_argument(task, "--connect-retry-max", "1"); + /* Apply defaults for --ping and --ping-exit only with UDP protocol. */ + } else { + /* Apply default of 10 second interval for ping if omitted. */ + option = vpn_provider_get_string(provider, "OpenVPN.Ping"); + if (!option) + connman_task_add_argument(task, "--ping", "10"); + + /* Apply default of 60 seconds for ping exit if omitted. */ + option = vpn_provider_get_string(provider, "OpenVPN.PingExit"); + if (!option) + connman_task_add_argument(task, "--ping-exit", "60"); + } err = connman_task_run(task, ov_died, data, NULL, NULL, NULL); if (err < 0) { diff --git a/vpn/plugins/pptp.c b/vpn/plugins/pptp.c index 5fc861e4..4a704bb1 100644 --- a/vpn/plugins/pptp.c +++ b/vpn/plugins/pptp.c @@ -54,15 +54,18 @@ enum { OPT_STRING = 1, OPT_BOOL = 2, + OPT_PPTP_ONLY = 3, }; struct { const char *cm_opt; const char *pptp_opt; - const char *vpnc_default; + const char *pptp_default; int type; } pptp_options[] = { { "PPTP.User", "user", NULL, OPT_STRING }, + { "PPTP.IdleWait", "--idle-wait", NULL, OPT_PPTP_ONLY}, + { "PPTP.MaxEchoWait", "--max-echo-wait", NULL, OPT_PPTP_ONLY}, { "PPPD.EchoFailure", "lcp-echo-failure", "0", OPT_STRING }, { "PPPD.EchoInterval", "lcp-echo-interval", "0", OPT_STRING }, { "PPPD.Debug", "debug", NULL, OPT_STRING }, @@ -204,6 +207,7 @@ static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider) connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask, gateway); + connman_ipaddress_set_p2p(ipaddress, true); vpn_provider_set_ipaddress(provider, ipaddress); vpn_provider_set_nameservers(provider, nameservers); @@ -436,7 +440,9 @@ static int run_connect(struct vpn_provider *provider, vpn_provider_connect_cb_t cb, void *user_data, const char *username, const char *password) { - const char *opt_s, *host; + GString *pptp_opt_s; + const char *opt_s; + const char *host; char *str; int err, i; @@ -450,16 +456,11 @@ static int run_connect(struct vpn_provider *provider, DBG("username %s password %p", username, password); host = vpn_provider_get_string(provider, "Host"); - str = g_strdup_printf("%s %s --nolaunchpppd --loglevel 2", - PPTP, host); - if (!str) { - connman_error("can not allocate memory"); - err = -ENOMEM; - goto done; - } - connman_task_add_argument(task, "pty", str); - g_free(str); + /* Create PPTP options for pppd "pty" */ + pptp_opt_s = g_string_new(NULL); + g_string_append_printf(pptp_opt_s, "%s %s --nolaunchpppd --loglevel 2", + PPTP, host); connman_task_add_argument(task, "nodetach", NULL); connman_task_add_argument(task, "lock", NULL); @@ -474,7 +475,7 @@ static int run_connect(struct vpn_provider *provider, opt_s = vpn_provider_get_string(provider, pptp_options[i].cm_opt); if (!opt_s) - opt_s = pptp_options[i].vpnc_default; + opt_s = pptp_options[i].pptp_default; if (!opt_s) continue; @@ -485,8 +486,15 @@ static int run_connect(struct vpn_provider *provider, else if (pptp_options[i].type == OPT_BOOL) pptp_write_bool_option(task, pptp_options[i].pptp_opt, opt_s); + else if (pptp_options[i].type == OPT_PPTP_ONLY) + g_string_append_printf(pptp_opt_s, " %s %s", + pptp_options[i].pptp_opt, opt_s); } + str = g_string_free(pptp_opt_s, FALSE); + connman_task_add_argument(task, "pty", str); + g_free(str); + connman_task_add_argument(task, "plugin", SCRIPTDIR "/libppp-plugin.so"); diff --git a/vpn/plugins/vpn.c b/vpn/plugins/vpn.c index e04670c8..cb0d304b 100644 --- a/vpn/plugins/vpn.c +++ b/vpn/plugins/vpn.c @@ -65,7 +65,7 @@ struct vpn_data { struct vpn_driver_data { const char *name; const char *program; - struct vpn_driver *vpn_driver; + const struct vpn_driver *vpn_driver; struct vpn_provider_driver provider_driver; }; @@ -421,61 +421,26 @@ exist_err: return ret; } -static gboolean is_numeric(const char *str) +static gid_t get_gid(const char *group_name) { - gint i; - - if(!str || !(*str)) - return false; - - for(i = 0; str[i] ; i++) { - if(!g_ascii_isdigit(str[i])) - return false; - } - - return true; -} - -static gint get_gid(const char *group_name) -{ - gint gid = -1; struct group *grp; - if(!group_name || !(*group_name)) - return gid; - - if (is_numeric(group_name)) { - gid_t group_id = (gid_t)g_ascii_strtoull(group_name, NULL, 10); - grp = getgrgid(group_id); - } else { - grp = getgrnam(group_name); - } - + grp = vpn_util_get_group(group_name); if (grp) - gid = grp->gr_gid; + return grp->gr_gid; - return gid; + return -1; } -static gint get_uid(const char *user_name) +static uid_t get_uid(const char *user_name) { - gint uid = -1; struct passwd *pw; - if(!user_name || !(*user_name)) - return uid; - - if (is_numeric(user_name)) { - uid_t user_id = (uid_t)g_ascii_strtoull(user_name, NULL, 10); - pw = getpwuid(user_id); - } else { - pw = getpwnam(user_name); - } - + pw = vpn_util_get_passwd(user_name); if (pw) - uid = pw->pw_uid; + return pw->pw_uid; - return uid; + return -1; } static gint get_supplementary_gids(gchar **groups, gid_t **gid_list) @@ -508,8 +473,8 @@ static gint get_supplementary_gids(gchar **groups, gid_t **gid_list) static void vpn_task_setup(gpointer user_data) { struct vpn_plugin_data *data; - gint uid; - gint gid; + uid_t uid; + gid_t gid; gid_t *gid_list = NULL; size_t gid_list_size; const gchar *user; @@ -632,7 +597,7 @@ static int vpn_connect(struct vpn_provider *provider, vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_DAEMON) { ret = vpn_driver_data->vpn_driver->connect(provider, - NULL, NULL, NULL, NULL, NULL); + NULL, NULL, cb, dbus_sender, user_data); if (ret) { stop_vpn(provider); goto exist_err; @@ -812,7 +777,7 @@ static int vpn_route_env_parse(struct vpn_provider *provider, const char *key, return 0; } -int vpn_register(const char *name, struct vpn_driver *vpn_driver, +int vpn_register(const char *name, const struct vpn_driver *vpn_driver, const char *program) { struct vpn_driver_data *data; diff --git a/vpn/plugins/vpn.h b/vpn/plugins/vpn.h index 71e04f61..fd10addf 100644 --- a/vpn/plugins/vpn.h +++ b/vpn/plugins/vpn.h @@ -58,7 +58,7 @@ struct vpn_driver { enum vpn_provider_route_type *type); }; -int vpn_register(const char *name, struct vpn_driver *driver, +int vpn_register(const char *name, const struct vpn_driver *driver, const char *program); void vpn_unregister(const char *provider_name); void vpn_died(struct connman_task *task, int exit_code, void *user_data); diff --git a/vpn/plugins/vpnc.c b/vpn/plugins/vpnc.c index 8350fc3c..d11b9111 100644 --- a/vpn/plugins/vpnc.c +++ b/vpn/plugins/vpnc.c @@ -30,6 +30,10 @@ #include <stdio.h> #include <net/if.h> #include <linux/if_tun.h> +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> #include <glib.h> @@ -50,6 +54,7 @@ #include "../vpn.h" #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#define PID_PATH_ROOT "/var/run/user" enum { OPT_STRING = 1, @@ -240,6 +245,7 @@ static int vc_notify(DBusMessage *msg, struct vpn_provider *provider) } connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway); + connman_ipaddress_set_p2p(ipaddress, true); vpn_provider_set_ipaddress(provider, ipaddress); g_free(address); @@ -430,14 +436,49 @@ static gboolean io_channel_cb(GIOChannel *source, GIOCondition condition, return G_SOURCE_CONTINUE; } +static char *create_pid_path(const char *user, const char *group) +{ + struct passwd *pwd; + struct group *grp; + char *uid_str; + char *pid_path = NULL; + int mode = S_IRWXU|S_IRWXG; + gid_t gid; + + if (!user || !*user) + return NULL; + + if (vpn_settings_is_system_user(user)) + return NULL; + + pwd = vpn_util_get_passwd(user); + uid_str = g_strdup_printf("%d", pwd->pw_uid); + + grp = vpn_util_get_group(group); + gid = grp ? grp->gr_gid : pwd->pw_gid; + + pid_path = g_build_filename(PID_PATH_ROOT, uid_str, "vpnc", "pid", + NULL); + if (vpn_util_create_path(pid_path, pwd->pw_uid, gid, mode)) { + g_free(pid_path); + pid_path = NULL; + } + + g_free(uid_str); + + return pid_path; +} + static int run_connect(struct vc_private_data *data) { struct vpn_provider *provider; struct connman_task *task; + struct vpn_plugin_data *plugin_data; const char *credentials[] = {"VPNC.IPSec.Secret", "VPNC.Xauth.Username", "VPNC.Xauth.Password", NULL}; const char *if_name; const char *option; + char *pid_path; int err; int fd_in; int fd_err; @@ -473,6 +514,20 @@ static int run_connect(struct vc_private_data *data) connman_task_add_argument(task, "--ifmode", "tun"); } + plugin_data = vpn_settings_get_vpn_plugin_config("vpnc"); + + option = vpn_settings_get_binary_user(plugin_data); + if (option) { + pid_path = create_pid_path(option, + vpn_settings_get_binary_group( + plugin_data)); + if (pid_path) + connman_task_add_argument(task, "--pid-file", + pid_path); + + g_free(pid_path); + } + connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script"); option = vpn_provider_get_string(provider, "VPNC.Debug"); @@ -619,8 +674,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) DBG("provider %p", data->provider); - if (!reply) + if (!reply) { + err = ENOENT; goto err; + } err = vpn_agent_check_and_process_reply_error(reply, data->provider, data->task, data->cb, data->user_data); @@ -631,8 +688,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) return; } - if (!vpn_agent_check_reply_has_dict(reply)) + if (!vpn_agent_check_reply_has_dict(reply)) { + err = ENOENT; goto err; + } dbus_message_iter_init(reply, &iter); dbus_message_iter_recurse(&iter, &dict); @@ -687,17 +746,22 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) dbus_message_iter_next(&dict); } - if (!secret || !username || !password) + if (!secret || !username || !password) { + vpn_provider_indicate_error(data->provider, + VPN_PROVIDER_ERROR_AUTH_FAILED); + err = EACCES; goto err; + } - err = run_connect(data); - if (err != -EINPROGRESS) + /* vpn_provider.c:connect_cb() expects positive errors */ + err = -run_connect(data); + if (err != EINPROGRESS) goto err; return; err: - vc_connect_done(data, EACCES); + vc_connect_done(data, err); } static int request_input_credentials(struct vc_private_data *data, diff --git a/vpn/plugins/wireguard.c b/vpn/plugins/wireguard.c index de2dbda3..03658943 100644 --- a/vpn/plugins/wireguard.c +++ b/vpn/plugins/wireguard.c @@ -49,6 +49,24 @@ #include "vpn.h" #include "wireguard.h" +#define DNS_RERESOLVE_TIMEOUT 20 + +struct wireguard_info { + struct wg_device device; + struct wg_peer peer; + char *endpoint_fqdn; + char *port; + int reresolve_id; +}; + +struct sockaddr_u { + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + }; +}; + static int parse_key(const char *str, wg_key key) { unsigned char *buf; @@ -116,7 +134,7 @@ static int parse_allowed_ips(const char *allowed_ips, wg_peer *peer) return 0; } -static int parse_endpoint(const char *host, const char *port, wg_peer *peer) +static int parse_endpoint(const char *host, const char *port, struct sockaddr_u *addr) { struct addrinfo hints; struct addrinfo *result, *rp; @@ -151,7 +169,7 @@ static int parse_endpoint(const char *host, const char *port, wg_peer *peer) return -EINVAL; } - memcpy(&peer->endpoint.addr, rp->ai_addr, rp->ai_addrlen); + memcpy(addr, rp->ai_addr, rp->ai_addrlen); freeaddrinfo(result); return 0; @@ -194,6 +212,8 @@ static int parse_address(const char *address, const char *gateway, err = -EINVAL; } + connman_ipaddress_set_p2p(*ipaddress, true); + g_strfreev(tokens); if (err) connman_ipaddress_free(*ipaddress); @@ -225,7 +245,7 @@ static char *get_ifname(void) for (i = 0; i < 256; i++) { data.ifname = g_strdup_printf("wg%d", i); data.found = false; - __vpn_ipconfig_foreach(ifname_check_cb, &data); + vpn_ipconfig_foreach(ifname_check_cb, &data); if (!data.found) return data.ifname; @@ -236,10 +256,53 @@ static char *get_ifname(void) return NULL; } -struct wireguard_info { - struct wg_device device; - struct wg_peer peer; -}; +static bool sockaddr_cmp_addr(struct sockaddr_u *a, struct sockaddr_u *b) +{ + if (a->sa.sa_family != b->sa.sa_family) + return false; + + if (a->sa.sa_family == AF_INET) + return !memcmp(&a->sin, &b->sin, sizeof(struct sockaddr_in)); + else if (a->sa.sa_family == AF_INET6) + return !memcmp(a->sin6.sin6_addr.s6_addr, + b->sin6.sin6_addr.s6_addr, + sizeof(a->sin6.sin6_addr.s6_addr)); + + return false; +} + +static gboolean wg_dns_reresolve_cb(gpointer user_data) +{ + struct wireguard_info *info = user_data; + struct sockaddr_u addr; + int err; + + DBG(""); + + err = parse_endpoint(info->endpoint_fqdn, + info->port, &addr); + if (err) + return TRUE; + + if (sockaddr_cmp_addr(&addr, + (struct sockaddr_u *)&info->peer.endpoint.addr)) + return TRUE; + + if (addr.sa.sa_family == AF_INET) + memcpy(&info->peer.endpoint.addr, &addr.sin, + sizeof(info->peer.endpoint.addr4)); + else + memcpy(&info->peer.endpoint.addr, &addr.sin6, + sizeof(info->peer.endpoint.addr6)); + + DBG("Endpoint address has changed, udpate WireGuard device"); + err = wg_set_device(&info->device); + if (err) + DBG("Failed to update Endpoint address for WireGuard device %s", + info->device.name); + + return TRUE; +} static int wg_connect(struct vpn_provider *provider, struct connman_task *task, const char *if_name, @@ -323,10 +386,14 @@ static int wg_connect(struct vpn_provider *provider, option = "51820"; gateway = vpn_provider_get_string(provider, "Host"); - err = parse_endpoint(gateway, option, &info->peer); + err = parse_endpoint(gateway, option, + (struct sockaddr_u *)&info->peer.endpoint.addr); if (err) goto done; + info->endpoint_fqdn = g_strdup(gateway); + info->port = g_strdup(option); + option = vpn_provider_get_string(provider, "WireGuard.Address"); if (!option) { DBG("Missing WireGuard.Address configuration"); @@ -342,7 +409,7 @@ static int wg_connect(struct vpn_provider *provider, err = -ENOENT; goto done; } - stpncpy(info->device.name, ifname, sizeof(info->device.name)); + stpncpy(info->device.name, ifname, sizeof(info->device.name) - 1); g_free(ifname); err = wg_add_device(info->device.name); @@ -367,6 +434,11 @@ done: connman_ipaddress_free(ipaddress); + if (!err) + info->reresolve_id = + g_timeout_add_seconds(DNS_RERESOLVE_TIMEOUT, + wg_dns_reresolve_cb, info); + return err; } @@ -377,10 +449,16 @@ static void wg_disconnect(struct vpn_provider *provider) info = vpn_provider_get_plugin_data(provider); if (!info) return; + + if (info->reresolve_id > 0) + g_source_remove(info->reresolve_id); + vpn_provider_set_plugin_data(provider, NULL); wg_del_device(info->device.name); + g_free(info->endpoint_fqdn); + g_free(info->port); g_free(info); } |