summaryrefslogtreecommitdiff
path: root/vpn/plugins
diff options
context:
space:
mode:
authorNiraj Kumar Goit <niraj.g@samsung.com>2022-02-15 10:23:07 +0530
committerNiraj Kumar Goit <niraj.g@samsung.com>2022-02-15 10:25:05 +0530
commitf89b473dfd8e916314b534b3397442f8c869c783 (patch)
treeb6f15a191886ac5b78e98b1b2b210adefae69cf2 /vpn/plugins
parentdd3cccc5e67548dcc2dd6c6254ed6c97859085d5 (diff)
downloadconnman-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.c2
-rw-r--r--vpn/plugins/openconnect.c899
-rw-r--r--vpn/plugins/openvpn.c32
-rw-r--r--vpn/plugins/pptp.c32
-rw-r--r--vpn/plugins/vpn.c61
-rw-r--r--vpn/plugins/vpn.h2
-rw-r--r--vpn/plugins/vpnc.c76
-rw-r--r--vpn/plugins/wireguard.c96
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);
}