DA: Skip initializing failed_bssids list when eapol failure case
[platform/upstream/connman.git] / vpn / vpn-util.c
1 /*
2  *  ConnMan VPN daemon utils
3  *
4  *  Copyright (C) 2020  Jolla Ltd. All rights reserved.
5  *  Copyright (C) 2020  Open Mobile Platform LLC.
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 <errno.h>
24 #include <unistd.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <pwd.h>
28 #include <grp.h>
29
30 #include <glib/gstdio.h>
31
32 #include <connman/log.h>
33
34 #include "vpn.h"
35
36 static bool is_string_digits(const char *str)
37 {
38         int i;
39
40         if (!str || !*str)
41                 return false;
42
43         for (i = 0; str[i]; i++) {
44                 if (!g_ascii_isdigit(str[i]))
45                         return false;
46         }
47
48         return true;
49 }
50
51 static uid_t get_str_id(const char *username)
52 {
53         if (!username || !*username)
54                 return 0;
55
56         return (uid_t)g_ascii_strtoull(username, NULL, 10);
57 }
58
59 struct passwd *vpn_util_get_passwd(const char *username)
60 {
61         struct passwd *pwd;
62         uid_t uid;
63
64         if (!username || !*username)
65                 return NULL;
66
67         if (is_string_digits(username)) {
68                 uid = get_str_id(username);
69                 pwd = getpwuid(uid);
70         } else {
71                 pwd = getpwnam(username);
72         }
73
74         return pwd;
75 }
76
77 struct group *vpn_util_get_group(const char *groupname)
78 {
79         struct group *grp;
80         gid_t gid;
81
82         if (!groupname || !*groupname)
83                 return NULL;
84
85         if (is_string_digits(groupname)) {
86                 gid = get_str_id(groupname);
87                 grp = getgrgid(gid);
88         } else {
89                 grp = getgrnam(groupname);
90         }
91
92         return grp;
93 }
94
95 /*
96  * These prefixes are used for checking if the requested path for
97  * vpn_util_create_path() is acceptable. Allow only prefixes meant for run-time
98  * or temporary use to limit the access to any system resources.
99  *
100  * VPN core and plugins would need to create only temporary dirs for the
101  * run-time use. The requested dirs can be created for a specific user when
102  * running a VPN plugin as a different user and thus, user specific run dir is
103  * allowed and limitation to access any other system dir is restricted.
104  */
105 static const char *allowed_prefixes[] = { "/var/run/connman-vpn/",
106                                         "/var/run/user/", "/tmp/", NULL };
107
108 static int is_path_allowed(const char *path)
109 {
110         int err = -EPERM;
111         int i;
112
113         if (!path || !*path || !g_path_is_absolute(path))
114                 return -EINVAL;
115
116         if (g_strrstr(path, "..") || g_strrstr(path, "./"))
117                 return -EPERM;
118
119         for (i = 0; allowed_prefixes[i]; i++) {
120                 if (g_str_has_prefix(path, allowed_prefixes[i])) {
121                         const char *suffix = path +
122                                                 strlen(allowed_prefixes[i]);
123
124                         /*
125                          * Don't allow plain prefixes, an additional dir must
126                          * be included after the prexix in the requested path.
127                          */
128                         if (suffix && *suffix != G_DIR_SEPARATOR &&
129                                                 g_strrstr(suffix,
130                                                         G_DIR_SEPARATOR_S)) {
131                                 DBG("allowed %s, has suffix %s", path, suffix);
132                                 err = 0;
133                         }
134
135                         break;
136                 }
137         }
138
139         return err;
140 }
141
142 int vpn_util_create_path(const char *path, uid_t uid, gid_t grp, int mode)
143 {
144         mode_t old_umask;
145         char *dir_p;
146         int err;
147
148         err = is_path_allowed(path);
149         if (err)
150                 return err;
151
152         dir_p = g_path_get_dirname(path);
153         if (!dir_p)
154                 return -ENOMEM;
155
156         err = g_unlink(dir_p);
157         if (err)
158                 err = -errno;
159
160         switch (err) {
161         case 0:
162                 /* Removed */
163         case -ENOENT:
164                 /* Did not exist */
165                 break;
166         case -EACCES:
167                 /*
168                  * Cannot get write access to the containing directory, check
169                  * if the path exists.
170                  */
171                 if (!g_file_test(dir_p, G_FILE_TEST_EXISTS))
172                         goto out;
173
174                 /* If the dir does not exist new one cannot be created */
175                 if (!g_file_test(dir_p, G_FILE_TEST_IS_DIR))
176                         goto out;
177
178                 /* Do a chmod as the dir exists */
179                 /* fallthrough */
180         case -EISDIR:
181                 /* Exists as dir, just chmod and change owner */
182                 err = g_chmod(dir_p, mode);
183                 if (err) {
184                         connman_warn("chmod %s failed, err %d", dir_p, err);
185                         err = -errno;
186                 }
187
188                 goto chown;
189         default:
190                 /* Any other error that is not handled here */
191                 connman_warn("remove %s failed, err %d", dir_p, err);
192                 goto out;
193         }
194
195         /* Set dir creation mask to correspond to the mode */
196         old_umask = umask(~mode & 0777);
197
198         DBG("mkdir %s", dir_p);
199         err = g_mkdir_with_parents(dir_p, mode);
200
201         umask(old_umask);
202
203         if (err) {
204                 connman_warn("mkdir %s failed, err %d", dir_p, err);
205                 err = -errno;
206                 goto out;
207         }
208
209 chown:
210         if (uid && grp) {
211                 err = chown(dir_p, uid, grp);
212                 if (err) {
213                         err = -errno;
214                         connman_warn("chown %s failed for %d/%d, err %d",
215                                                         dir_p, uid, grp, err);
216                 }
217         }
218
219 out:
220         g_free(dir_p);
221
222         return err;
223 }
224