Send connman mailing list submissions to
        [email protected]

To subscribe or unsubscribe via email, send a message with subject or
body 'help' to
        [email protected]

You can reach the person managing the list at
        [email protected]

When replying, please edit your Subject line so it is more specific
than "Re: Contents of connman digest..."

Today's Topics:

   1. [PATCH 1/5] vpn-util: Create utility file for VPN core and plugins
      (Jussi Laakkonen)
   2. [PATCH 0/5] Add VPN utility functions and system user list to 
DACPrivileges
      (Jussi Laakkonen)
   3. [PATCH 2/5] vpn: Add CAP_CHOWN CAP_FOWNER capabilities for path creation
      (Jussi Laakkonen)
   4. [PATCH 3/5] vpn-settings: Add SystemBinaryUsers conf option and system 
user check
      (Jussi Laakkonen)
   5. [PATCH 4/5] vpnc: Support setting the pid file path to /var/run/user
      (Jussi Laakkonen)
   6. [PATCH 5/5] vpn: Use util functions for getting uid and gid
      (Jussi Laakkonen)


----------------------------------------------------------------------

Date: Tue, 20 Oct 2020 17:29:34 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH 1/5] vpn-util: Create utility file for VPN core and
        plugins
To: [email protected]
Message-ID: <[email protected]>

Create vpn-util.c for VPN core and plugins to use. This is a first step
in establishing some sort of lib for them.

Add functionality to get the user struct passwd and struct group using
the username and groupname:
 - vpn_util_get_passwd()
 - vpn_util_get_group()

Add function vpn_util_create_path() to create dir for a VPN plugin.
The dirname is extracted with g_path_get_dirname() and it must have a
prefix of "/var/run/connman-vpn/", "/var/run/user/" or "/tmp/" and must
not contain ".." or "./". One directory element must be provided after
the prefix, e.g. "/tmp/dir/". If the basename path is an existing dir
permissions and ownership is changed according to the request ones,
otherwise the file is removed and then created, as if it never existed.
g_unlink() is used to handle safe removals of symlink, which also enables
detection of parent dir write permissions - error is returned unless the
dir exists, in which case ownership and permissions are attempted to be
set accordingly.
---
 Makefile.am    |   2 +-
 vpn/vpn-util.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++++
 vpn/vpn.h      |   5 ++
 3 files changed, 219 insertions(+), 1 deletion(-)
 create mode 100644 vpn/vpn-util.c

diff --git a/Makefile.am b/Makefile.am
index 5971ca9b..8b4b4996 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -181,7 +181,7 @@ vpn_connman_vpnd_SOURCES = $(builtin_vpn_sources) 
$(backtrace_sources) \
                        vpn/vpn-ipconfig.c src/inet.c vpn/vpn-rtnl.c \
                        src/dbus.c src/storage.c src/ipaddress.c src/agent.c \
                        vpn/vpn-agent.c vpn/vpn-agent.h src/inotify.c \
-                       vpn/vpn-config.c vpn/vpn-settings.c
+                       vpn/vpn-config.c vpn/vpn-settings.c vpn/vpn-util.c
 
 vpn_connman_vpnd_LDADD = gdbus/libgdbus-internal.la $(builtin_vpn_libadd) \
                                @GLIB_LIBS@ @DBUS_LIBS@ @GNUTLS_LIBS@ \
diff --git a/vpn/vpn-util.c b/vpn/vpn-util.c
new file mode 100644
index 00000000..72b24f43
--- /dev/null
+++ b/vpn/vpn-util.c
@@ -0,0 +1,213 @@
+/*
+ *  ConnMan VPN daemon utils
+ *
+ *  Copyright (C) 2020  Jolla Ltd. All rights reserved.
+ *  Copyright (C) 2020  Open Mobile Platform LLC.
+ *  Contact: [email protected]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <glib/gstdio.h>
+
+#include <connman/log.h>
+
+#include "vpn.h"
+
+static bool is_string_digits(const char *str)
+{
+       int i;
+
+       if (!str || !*str)
+               return false;
+
+       for (i = 0; str[i]; i++) {
+               if (!g_ascii_isdigit(str[i]))
+                       return false;
+       }
+
+       return true;
+}
+
+static uid_t get_str_id(const char *username)
+{
+       if (!username || !*username)
+               return 0;
+
+       return (uid_t)g_ascii_strtoull(username, NULL, 10);
+}
+
+struct passwd *vpn_util_get_passwd(const char *username)
+{
+       struct passwd *pwd;
+       uid_t uid;
+
+       if (!username || !*username)
+               return NULL;
+
+       if (is_string_digits(username)) {
+               uid = get_str_id(username);
+               pwd = getpwuid(uid);
+       } else {
+               pwd = getpwnam(username);
+       }
+
+       return pwd;
+}
+
+struct group *vpn_util_get_group(const char *groupname)
+{
+       struct group *grp;
+       gid_t gid;
+
+       if (!groupname || !*groupname)
+               return NULL;
+
+       if (is_string_digits(groupname)) {
+               gid = get_str_id(groupname);
+               grp = getgrgid(gid);
+       } else {
+               grp = getgrnam(groupname);
+       }
+
+       return grp;
+}
+
+static const char *allowed_prefixes[] = { "/var/run/connman-vpn/",
+                                       "/var/run/user/", "/tmp/", NULL };
+
+static int is_path_allowed(const char *path)
+{
+       int err = -EPERM;
+       int i;
+
+       if (!path || !*path || !g_path_is_absolute(path))
+               return -EINVAL;
+
+       if (g_strrstr(path, "..") || g_strrstr(path, "./"))
+               return -EPERM;
+
+       for (i = 0; allowed_prefixes[i]; i++) {
+               if (g_str_has_prefix(path, allowed_prefixes[i])) {
+                       const char *suffix = path +
+                                               strlen(allowed_prefixes[i]);
+
+                       /*
+                        * Don't allow plain prefixes, an additional dir must
+                        * be included after the prexix in the requested path.
+                        */
+                       if (suffix && *suffix != G_DIR_SEPARATOR &&
+                                               g_strrstr(suffix,
+                                                       G_DIR_SEPARATOR_S)) {
+                               DBG("allowed %s, has suffix %s", path, suffix);
+                               err = 0;
+                       }
+
+                       break;
+               }
+       }
+
+       return err;
+}
+
+int vpn_util_create_path(const char *path, uid_t uid, gid_t grp, int mode)
+{
+       mode_t old_umask;
+       char *dir_p;
+       int err;
+
+       err = is_path_allowed(path);
+       if (err)
+               return err;
+
+       dir_p = g_path_get_dirname(path);
+       if (!dir_p)
+               return -ENOMEM;
+
+       err = g_unlink(dir_p);
+       if (err)
+               err = -errno;
+
+       switch (err) {
+       case 0:
+               /* Removed */
+       case -ENOENT:
+               /* Did not exist */
+               break;
+       case -EACCES:
+               /*
+                * Cannot get write access to the containing directory, check
+                * if the path exists.
+                */
+               if (!g_file_test(dir_p, G_FILE_TEST_EXISTS))
+                       goto out;
+
+               /* If the dir does not exist new one cannot be created */
+               if (!g_file_test(dir_p, G_FILE_TEST_IS_DIR))
+                       goto out;
+
+               /* Fall through to chmod as the dir exists */
+       case -EISDIR:
+               /* Exists as dir, just chmod and change owner */
+               err = g_chmod(dir_p, mode);
+               if (err) {
+                       connman_warn("chmod %s failed, err %d", dir_p, err);
+                       err = -errno;
+               }
+
+               goto chown;
+       default:
+               /* Any other error that is not handled here */
+               connman_warn("remove %s failed, err %d", dir_p, err);
+               goto out;
+       }
+
+       /* Set dir creation mask to correspond to the mode */
+       old_umask = umask(~mode & 0777);
+
+       DBG("mkdir %s", dir_p);
+       err = g_mkdir_with_parents(dir_p, mode);
+
+       umask(old_umask);
+
+       if (err) {
+               connman_warn("mkdir %s failed, err %d", dir_p, err);
+               err = -errno;
+               goto out;
+       }
+
+chown:
+       if (uid && grp) {
+               err = chown(dir_p, uid, grp);
+               if (err) {
+                       err = -errno;
+                       connman_warn("chown %s failed for %d/%d, err %d",
+                                                       dir_p, uid, grp, err);
+               }
+       }
+
+out:
+       g_free(dir_p);
+
+       return err;
+}
+
diff --git a/vpn/vpn.h b/vpn/vpn.h
index 3356d53e..8d7c63d6 100644
--- a/vpn/vpn.h
+++ b/vpn/vpn.h
@@ -131,3 +131,8 @@ const char * vpn_settings_get_binary_user(struct 
vpn_plugin_data *data);
 const char * vpn_settings_get_binary_group(struct vpn_plugin_data *data);
 char ** vpn_settings_get_binary_supplementary_groups(
        struct vpn_plugin_data *data);
+bool vpn_settings_is_system_user(const char *user);
+
+struct passwd *vpn_util_get_passwd(const char *username);
+struct group *vpn_util_get_group(const char *groupname);
+int vpn_util_create_path(const char *path, uid_t uid, gid_t grp, int mode);
-- 
2.20.1

------------------------------

Date: Tue, 20 Oct 2020 17:29:33 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH 0/5] Add VPN utility functions and system user list to
        DACPrivileges
To: [email protected]
Message-ID: <[email protected]>

Add vpn-util.c for utility functions for VPN core and plugins to use.
Implemented:
 - vpn_util_get_passwd() to get struct passwd using user/uid as string
 - vpn_util_get_group() to get struct group using group/gid as string
 - vpn_util_create_path() to create the path for the requested file, and to
   set the ownership and permissions thus, requiring additional capabilities

With the help of vpn_util_create_path() VPNC now can set the pid file to a
location where the running user can create it. Also, replaced some code in
vpn.c in favor of using vpn-util.c functions.

Added "SystemBinaryUsers" string list to DACPrivileges group for vpnd main
config only. This, with the help of the added vpn_settings_is_system_user(),
a plugin, for example, can check if the user set to run the VPN binary is root
or other pre-defined system user to determine appropriate action, like it is
the case with VPNC.

Jussi Laakkonen (5):
  vpn-util: Create utility file for VPN core and plugins
  vpn: Add CAP_CHOWN CAP_FOWNER capabilities for path creation
  vpn-settings: Add SystemBinaryUsers conf option and system user check
  vpnc: Support setting the pid file path to /var/run/user
  vpn: Use util functions for getting uid and gid

 Makefile.am                |   2 +-
 vpn/connman-vpn.service.in |   2 +-
 vpn/plugins/vpn.c          |  55 ++--------
 vpn/plugins/vpnc.c         |  54 ++++++++++
 vpn/vpn-settings.c         |  63 ++++++++++-
 vpn/vpn-util.c             | 213 +++++++++++++++++++++++++++++++++++++
 vpn/vpn.h                  |   5 +
 7 files changed, 346 insertions(+), 48 deletions(-)
 create mode 100644 vpn/vpn-util.c

-- 
2.20.1

------------------------------

Date: Tue, 20 Oct 2020 17:29:35 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH 2/5] vpn: Add CAP_CHOWN CAP_FOWNER capabilities for
        path creation
To: [email protected]
Message-ID: <[email protected]>

vpnd needs these permissions because of vpn-util.c needs to change the
owner/group and permissions for the created/existing dir.
---
 vpn/connman-vpn.service.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vpn/connman-vpn.service.in b/vpn/connman-vpn.service.in
index dd15bab4..3bb52e12 100644
--- a/vpn/connman-vpn.service.in
+++ b/vpn/connman-vpn.service.in
@@ -6,7 +6,7 @@ Type=dbus
 BusName=net.connman.vpn
 ExecStart=@sbindir@/connman-vpnd -n
 StandardOutput=null
-CapabilityBoundingSet=CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW 
CAP_SETGID CAP_SETUID
+CapabilityBoundingSet=CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW 
CAP_SETGID CAP_SETUID CAP_CHOWN CAP_FOWNER
 ProtectHome=read-only
 ProtectSystem=full
 
-- 
2.20.1

------------------------------

Date: Tue, 20 Oct 2020 17:29:36 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH 3/5] vpn-settings: Add SystemBinaryUsers conf option
        and system user check
To: [email protected]
Message-ID: <[email protected]>

Add "SystemBinaryUsers" user list to vpnd main config only to allow
defining which users should be regarded as system wide "privileged"
users. The user list is read as a string list from DACPrivileges group.

Add function for checking if the user is a system user, either root,
effective uid of the process or one in the "SystemBinaryUsers":
vpn_settings_is_system_user(). This function can be used by VPN plugins
to check if the user set to run the VPN binary is a regular or system
user since different behavior can be expected for run-time file
creation.
---
 vpn/vpn-settings.c | 63 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 62 insertions(+), 1 deletion(-)

diff --git a/vpn/vpn-settings.c b/vpn/vpn-settings.c
index 0eca2bc8..e78e5019 100644
--- a/vpn/vpn-settings.c
+++ b/vpn/vpn-settings.c
@@ -2,7 +2,7 @@
  *  ConnMan VPN daemon settings
  *
  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
- *  Copyright (C) 2018-2019 Jolla Ltd. All rights reserved.
+ *  Copyright (C) 2018-2020 Jolla Ltd. All rights reserved.
  *  Contact: [email protected]
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/types.h>
 
 #include <connman/log.h>
 
@@ -37,11 +40,13 @@ static struct {
        char *binary_user;
        char *binary_group;
        char **binary_supplementary_groups;
+       char **system_binary_users;
 } connman_vpn_settings  = {
        .timeout_inputreq               = DEFAULT_INPUT_REQUEST_TIMEOUT,
        .binary_user                    = NULL,
        .binary_group                   = NULL,
        .binary_supplementary_groups    = NULL,
+       .system_binary_users            = NULL,
 };
 
 struct vpn_plugin_data {
@@ -52,6 +57,58 @@ struct vpn_plugin_data {
 
 GHashTable *plugin_hash = NULL;
 
+bool vpn_settings_is_system_user(const char *user)
+{
+       struct passwd *pwd;
+       struct passwd *system_pwd;
+       int i;
+
+       /*
+        * The username is not set = override should not be used. This is the
+        * case after the override is reset.
+        */
+       if (!user)
+               return true;
+
+       DBG("check user \"%s\"", user);
+
+       /*
+        * Ignore errors if no entry was found. Treat as system user to
+        * prevent using an invalid override.
+        */
+       pwd = vpn_util_get_passwd(user);
+       if (!pwd)
+               return true;
+
+       if (!connman_vpn_settings.system_binary_users) {
+               DBG("no binary users set");
+
+               /*
+                * Check if the user is root, or the uid equals to process
+                * effective uid.
+                */
+               return !pwd->pw_uid || pwd->pw_uid == geteuid();
+       }
+
+       /* Root set as user or the effective user id */
+       if (!pwd->pw_uid || pwd->pw_uid == geteuid())
+               return true;
+
+       for (i = 0; connman_vpn_settings.system_binary_users[i]; i++) {
+               const char *system_user =
+                               connman_vpn_settings.system_binary_users[i];
+
+               system_pwd = vpn_util_get_passwd(system_user);
+               if (!system_pwd)
+                       continue;
+
+               if (pwd->pw_uid == system_pwd->pw_uid)
+                       return true;
+       }
+
+       return false;
+}
+
 const char *vpn_settings_get_binary_user(struct vpn_plugin_data *data)
 {
        if (data && data->binary_user)
@@ -129,6 +186,9 @@ static void parse_config(GKeyFile *config, const char *file)
        connman_vpn_settings.binary_supplementary_groups = get_string_list(
                                                config, VPN_GROUP,
                                                "SupplementaryGroups");
+       connman_vpn_settings.system_binary_users = get_string_list(
+                                               config, VPN_GROUP,
+                                               "SystemBinaryUsers");
 }
 
 struct vpn_plugin_data *vpn_settings_get_vpn_plugin_config(const char *name)
@@ -245,6 +305,7 @@ void __vpn_settings_cleanup()
        g_free(connman_vpn_settings.binary_user);
        g_free(connman_vpn_settings.binary_group);
        g_strfreev(connman_vpn_settings.binary_supplementary_groups);
+       g_strfreev(connman_vpn_settings.system_binary_users);
 
        if (plugin_hash) {
                g_hash_table_destroy(plugin_hash);
-- 
2.20.1

------------------------------

Date: Tue, 20 Oct 2020 17:29:37 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH 4/5] vpnc: Support setting the pid file path to
        /var/run/user
To: [email protected]
Message-ID: <[email protected]>

Use the new util and settings functions to get the user that is used to
run the VPNC plugin in order to set a correct pid file path. If system
user is used utilize the default path.

Path prefix is set to /var/run/user, and suffix vpnc/pid is added as
well. With user 1000 this results to pid file path of
/var/run/user/1000/vpnc/pid.
---
 vpn/plugins/vpnc.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/vpn/plugins/vpnc.c b/vpn/plugins/vpnc.c
index 8350fc3c..db4b5c04 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,
@@ -430,14 +435,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 +513,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");
-- 
2.20.1

------------------------------

Date: Tue, 20 Oct 2020 17:29:38 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH 5/5] vpn: Use util functions for getting uid and gid
To: [email protected]
Message-ID: <[email protected]>

Change to use vpn-util.c functions for getting struct passwd and struct
group using the username/uid as string. Change gint to respective uid_t
and gid_t.
---
 vpn/plugins/vpn.c | 55 +++++++++--------------------------------------
 1 file changed, 10 insertions(+), 45 deletions(-)

diff --git a/vpn/plugins/vpn.c b/vpn/plugins/vpn.c
index 67e7b30b..cb0d304b 100644
--- a/vpn/plugins/vpn.c
+++ b/vpn/plugins/vpn.c
@@ -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;
-- 
2.20.1

------------------------------

Subject: Digest Footer

_______________________________________________
connman mailing list -- [email protected]
To unsubscribe send an email to [email protected]


------------------------------

End of connman Digest, Vol 60, Issue 19
***************************************

Reply via email to