Imported Upstream version 1.40
[platform/upstream/connman.git] / vpn / vpn-settings.c
1 /*
2  *  ConnMan VPN daemon settings
3  *
4  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
5  *  Copyright (C) 2018-2020 Jolla Ltd. All rights reserved.
6  *  Contact: jussi.laakkonen@jolla.com
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <pwd.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29
30 #include <connman/log.h>
31
32 #include "vpn.h"
33
34 #define DEFAULT_INPUT_REQUEST_TIMEOUT 300 * 1000
35 #define PLUGIN_CONFIGDIR CONFIGDIR "/vpn-plugin"
36 #define VPN_GROUP "DACPrivileges"
37
38 static struct {
39         unsigned int timeout_inputreq;
40         char *binary_user;
41         char *binary_group;
42         char **binary_supplementary_groups;
43         char **system_binary_users;
44 } connman_vpn_settings  = {
45         .timeout_inputreq               = DEFAULT_INPUT_REQUEST_TIMEOUT,
46         .binary_user                    = NULL,
47         .binary_group                   = NULL,
48         .binary_supplementary_groups    = NULL,
49         .system_binary_users            = NULL,
50 };
51
52 struct vpn_plugin_data {
53         char *binary_user;
54         char *binary_group;
55         char **binary_supplementary_groups;
56 };
57
58 GHashTable *plugin_hash = NULL;
59
60 bool vpn_settings_is_system_user(const char *user)
61 {
62         struct passwd *pwd;
63         struct passwd *system_pwd;
64         int i;
65
66         /*
67          * The username is not set = override should not be used. This is the
68          * case after the override is reset.
69          */
70         if (!user)
71                 return true;
72
73         DBG("check user \"%s\"", user);
74
75         /*
76          * Ignore errors if no entry was found. Treat as system user to
77          * prevent using an invalid override.
78          */
79         pwd = vpn_util_get_passwd(user);
80         if (!pwd)
81                 return true;
82
83         if (!connman_vpn_settings.system_binary_users) {
84                 DBG("no binary users set");
85
86                 /*
87                  * Check if the user is root, or the uid equals to process
88                  * effective uid.
89                  */
90                 return !pwd->pw_uid || pwd->pw_uid == geteuid();
91         }
92
93         /* Root set as user or the effective user id */
94         if (!pwd->pw_uid || pwd->pw_uid == geteuid())
95                 return true;
96
97         for (i = 0; connman_vpn_settings.system_binary_users[i]; i++) {
98                 const char *system_user =
99                                 connman_vpn_settings.system_binary_users[i];
100
101                 system_pwd = vpn_util_get_passwd(system_user);
102                 if (!system_pwd)
103                         continue;
104
105                 if (pwd->pw_uid == system_pwd->pw_uid)
106                         return true;
107         }
108
109         return false;
110 }
111
112 const char *vpn_settings_get_binary_user(struct vpn_plugin_data *data)
113 {
114         if (data && data->binary_user)
115                 return data->binary_user;
116
117         return connman_vpn_settings.binary_user;
118 }
119
120 const char *vpn_settings_get_binary_group(struct vpn_plugin_data *data)
121 {
122         if (data && data->binary_group)
123                 return data->binary_group;
124
125         return connman_vpn_settings.binary_group;
126 }
127
128 char **vpn_settings_get_binary_supplementary_groups(struct vpn_plugin_data *data)
129 {
130         if (data && data->binary_supplementary_groups)
131                 return data->binary_supplementary_groups;
132
133         return connman_vpn_settings.binary_supplementary_groups;
134 }
135
136 unsigned int __vpn_settings_get_timeout_inputreq()
137 {
138         return connman_vpn_settings.timeout_inputreq;
139 }
140
141 static char *get_string(GKeyFile *config, const char *group, const char *key)
142 {
143         char *str = g_key_file_get_string(config, group, key, NULL);
144         return str ? g_strstrip(str) : NULL;
145 }
146
147 static char **get_string_list(GKeyFile *config, const char *group,
148                                 const char *key)
149 {
150         gsize len = 0;
151         char **str = g_key_file_get_string_list(config, group, key, &len, NULL);
152
153         if (str) {
154                 guint i = 0;
155
156                 for (i = 0; i < len ; i++) {
157                         str[i] = g_strstrip(str[i]);
158                 }
159         }
160
161         return str;
162 }
163
164 static void parse_config(GKeyFile *config, const char *file)
165 {
166         const char *group = "General";
167         GError *error = NULL;
168         int timeout;
169
170         if (!config)
171                 return;
172
173         DBG("parsing %s", file);
174
175         timeout = g_key_file_get_integer(config, group,
176                         "InputRequestTimeout", &error);
177         if (!error && timeout >= 0)
178                 connman_vpn_settings.timeout_inputreq = timeout * 1000;
179
180         g_clear_error(&error);
181
182         connman_vpn_settings.binary_user = get_string(config, VPN_GROUP,
183                                                 "User");
184         connman_vpn_settings.binary_group = get_string(config, VPN_GROUP,
185                                                 "Group");
186         connman_vpn_settings.binary_supplementary_groups = get_string_list(
187                                                 config, VPN_GROUP,
188                                                 "SupplementaryGroups");
189         connman_vpn_settings.system_binary_users = get_string_list(
190                                                 config, VPN_GROUP,
191                                                 "SystemBinaryUsers");
192 }
193
194 struct vpn_plugin_data *vpn_settings_get_vpn_plugin_config(const char *name)
195 {
196         struct vpn_plugin_data *data = NULL;
197
198         if (plugin_hash)
199                 data = g_hash_table_lookup(plugin_hash, name);
200
201         return data;
202 }
203
204 static void vpn_plugin_data_free(gpointer data)
205 {
206         struct vpn_plugin_data *plugin_data = (struct vpn_plugin_data*)data;
207
208         g_free(plugin_data->binary_user);
209         g_free(plugin_data->binary_group);
210         g_strfreev(plugin_data->binary_supplementary_groups);
211
212         g_free(data);
213 }
214
215 int vpn_settings_parse_vpn_plugin_config(const char *name)
216 {
217         struct vpn_plugin_data *data;
218         gchar *file;
219         gchar *ext = ".conf";
220         GKeyFile *config;
221         gint err = 0;
222
223         if (!name || !*name)
224                 return -EINVAL;
225
226         if (vpn_settings_get_vpn_plugin_config(name))
227                 return -EALREADY;
228
229         file = g_strconcat(PLUGIN_CONFIGDIR, "/", name, ext, NULL);
230
231         config =  __vpn_settings_load_config(file);
232
233         if (!config) {
234                 err = -ENOENT;
235                 DBG("Cannot load config %s for %s", file, name);
236                 goto out;
237         }
238
239         data = g_try_new0(struct vpn_plugin_data, 1);
240
241         data->binary_user = get_string(config, VPN_GROUP, "User");
242         data->binary_group = get_string(config, VPN_GROUP, "Group");
243         data->binary_supplementary_groups = get_string_list(config, VPN_GROUP,
244                                                 "SupplementaryGroups");
245
246         DBG("Loaded settings for %s: %s - %s",
247                 name, data->binary_user, data->binary_group);
248
249         if (!plugin_hash)
250                 plugin_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
251                                                 g_free, vpn_plugin_data_free);
252
253         g_hash_table_replace(plugin_hash, g_strdup(name), data);
254
255         g_key_file_unref(config);
256
257 out:
258         g_free(file);
259         return err;
260 }
261
262 void vpn_settings_delete_vpn_plugin_config(const char *name)
263 {
264         if (plugin_hash && name)
265                 g_hash_table_remove(plugin_hash, name);
266 }
267
268 GKeyFile *__vpn_settings_load_config(const char *file)
269 {
270         GError *err = NULL;
271         GKeyFile *keyfile;
272
273         keyfile = g_key_file_new();
274
275         g_key_file_set_list_separator(keyfile, ',');
276
277         if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
278                 if (err->code != G_FILE_ERROR_NOENT) {
279                         connman_error("Parsing %s failed: %s", file,
280                                                                 err->message);
281                 }
282
283                 g_error_free(err);
284                 g_key_file_unref(keyfile);
285                 return NULL;
286         }
287
288         return keyfile;
289 }
290
291 int __vpn_settings_init(const char *file)
292 {
293         GKeyFile *config;
294
295         config = __vpn_settings_load_config(file);
296         parse_config(config, file);
297         if (config)
298                 g_key_file_unref(config);
299
300         return 0;
301 }
302
303 void __vpn_settings_cleanup()
304 {
305         g_free(connman_vpn_settings.binary_user);
306         g_free(connman_vpn_settings.binary_group);
307         g_strfreev(connman_vpn_settings.binary_supplementary_groups);
308         g_strfreev(connman_vpn_settings.system_binary_users);
309
310         if (plugin_hash) {
311                 g_hash_table_destroy(plugin_hash);
312                 plugin_hash = NULL;
313         }
314 }