add validation of validsubpath
[src/app-framework-main.git] / src / wgt.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16
17 #define _GNU_SOURCE
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <assert.h>
25 #include <limits.h>
26 #include <string.h>
27 #include <ctype.h>
28
29
30 #include "wgt.h"
31
32 struct wgt {
33         int refcount;
34         int rootfd;
35         int nrlocales;
36         char **locales;
37 };
38
39 /* a valid subpath is a relative path not looking deeper than root using .. */
40 static int validsubpath(const char *subpath)
41 {
42         int l = 0, i = 0;
43         if (subpath[i] == '/')
44                 return 0;
45         while(subpath[i]) {
46                 switch(subpath[i++]) {
47                 case '.':
48                         if (!subpath[i])
49                                 break;
50                         if (subpath[i] == '/') {
51                                 i++;
52                                 break;
53                         }
54                         if (subpath[i++] == '.') {
55                                 if (!subpath[i]) {
56                                         l--;
57                                         break;
58                                 }
59                                 if (subpath[i++] == '/') {
60                                         l--;
61                                         break;
62                                 }
63                         }
64                 default:
65                         while(subpath[i] && subpath[i] != '/')
66                                 i++;
67                         if (l >= 0)
68                                 l++;
69                 case '/':
70                         break;
71                 }
72         }
73         return l >= 0;
74 }
75
76 static const char *normalsubpath(const char *subpath)
77 {
78         while(*subpath == '/')
79                 subpath++;
80         return validsubpath(subpath) ? subpath : NULL;
81 }
82
83 struct wgt *wgt_create()
84 {
85         struct wgt *wgt = malloc(sizeof * wgt);
86         if (!wgt)
87                 errno = ENOMEM;
88         else {
89                 wgt->refcount = 1;
90                 wgt->rootfd = -1;
91                 wgt->nrlocales = 0;
92                 wgt->locales = NULL;
93         }
94         return wgt;
95 }
96
97 void wgt_disconnect(struct wgt *wgt)
98 {
99         assert(wgt);
100         if (wgt->rootfd >= 0)
101                 close(wgt->rootfd);
102         wgt->rootfd = -1;
103 }
104
105 void wgt_locales_reset(struct wgt *wgt)
106 {
107         assert(wgt);
108         while(wgt->nrlocales)
109                 free(wgt->locales[--wgt->nrlocales]);
110 }
111
112 void wgt_addref(struct wgt *wgt)
113 {
114         assert(wgt);
115         wgt->refcount++;
116 }
117
118 void wgt_unref(struct wgt *wgt)
119 {
120         assert(wgt);
121         if (!--wgt->refcount) {
122                 wgt_disconnect(wgt);
123                 wgt_locales_reset(wgt);
124                 free(wgt->locales);
125                 free(wgt);
126         }
127 }
128
129 int wgt_connectat(struct wgt *wgt, int dirfd, const char *pathname)
130 {
131         int rfd;
132
133         assert(wgt);
134
135         rfd = dirfd;
136         if (pathname) {
137                 rfd = openat(rfd, pathname, O_PATH|O_DIRECTORY);
138                 if (rfd < 0)
139                         return rfd;
140         }
141         if (wgt->rootfd >= 0)
142                 close(wgt->rootfd);
143         wgt->rootfd = rfd;
144         return 0;
145 }
146
147 int wgt_connect(struct wgt *wgt, const char *pathname)
148 {
149         return wgt_connectat(wgt, AT_FDCWD, pathname);
150 }
151
152 int wgt_is_connected(struct wgt *wgt)
153 {
154         assert(wgt);
155         return wgt->rootfd != -1;
156 }
157
158 int wgt_has(struct wgt *wgt, const char *filename)
159 {
160         assert(wgt);
161         assert(wgt_is_connected(wgt));
162
163         filename = normalsubpath(filename);
164         if (!filename) {
165                 errno = EINVAL;
166                 return -1;
167         }
168         return 0 == faccessat(wgt->rootfd, filename, F_OK, 0);
169 }
170
171 int wgt_open_read(struct wgt *wgt, const char *filename)
172 {
173         assert(wgt);
174         assert(wgt_is_connected(wgt));
175         filename = normalsubpath(filename);
176         if (!filename) {
177                 errno = EINVAL;
178                 return -1;
179         }
180         return openat(wgt->rootfd, filename, O_RDONLY);
181 }
182
183 static int locadd(struct wgt *wgt, const char *locstr, int length)
184 {
185         int i;
186         char *item, **ptr;
187
188         item = strndup(locstr, length);
189         if (item != NULL) {
190                 for (i = 0 ; item[i] ; i++)
191                         item[i] = tolower(item[i]);
192                 for (i = 0 ; i < wgt->nrlocales ; i++)
193                         if (!strcmp(item, wgt->locales[i])) {
194                                 free(item);
195                                 return 0;
196                         }
197
198                 ptr = realloc(wgt->locales, (1 + wgt->nrlocales) * sizeof(wgt->locales[0]));
199                 if (ptr) {
200                         wgt->locales = ptr;
201                         wgt->locales[wgt->nrlocales++] = item;
202                         return 0;
203                 }
204                 free(item);
205         }
206         errno = ENOMEM;
207         return -1;
208 }
209
210 int wgt_locales_add(struct wgt *wgt, const char *locstr)
211 {
212         const char *stop, *next;
213         assert(wgt);
214         while (*locstr) {
215                 stop = strchrnul(locstr, ',');
216                 next = stop + !!*stop;
217                 while (locstr != stop) {
218                         if (locadd(wgt, locstr, stop - locstr))
219                                 return -1;
220                         do { stop--; } while(stop > locstr && *stop != '-');
221                 }
222                 locstr = next;
223         }
224         return 0;
225 }
226
227 int wgt_locales_score(struct wgt *wgt, const char *lang)
228 {
229         int i;
230
231         assert(wgt);
232         if (lang)
233                 for (i = 0 ; i < wgt->nrlocales ; i++)
234                         if (!strcasecmp(lang, wgt->locales[i]))
235                                 return i;
236
237         return INT_MAX;
238 }
239
240 static const char *localize(struct wgt *wgt, const char *filename, char path[PATH_MAX])
241 {
242         int i;
243
244         filename = normalsubpath(filename);
245         if (!filename) {
246                 errno = EINVAL;
247                 return NULL;
248         }
249         for (i = 0 ; i < wgt->nrlocales ; i++) {
250                 if (snprintf(path, PATH_MAX, "locales/%s/%s", wgt->locales[i], filename) >= PATH_MAX) {
251                         errno = EINVAL;
252                         return NULL;
253                 }
254                 if (0 == faccessat(wgt->rootfd, path, F_OK, 0))
255                         return path;
256         }
257         if (0 == faccessat(wgt->rootfd, filename, F_OK, 0))
258                 return filename;
259         errno = ENOENT;
260         return NULL;
261 }
262
263 char *wgt_locales_locate(struct wgt *wgt, const char *filename)
264 {
265         char path[PATH_MAX];
266         char * result;
267         const char * loc;
268
269         assert(wgt);
270         assert(wgt_is_connected(wgt));
271         loc = localize(wgt, filename, path);
272         if (!loc)
273                 result = NULL;
274         else {
275                 result = strdup(loc);
276                 if (!result)
277                         errno = ENOMEM;
278         }
279         return result;
280 }
281
282
283 int wgt_locales_open_read(struct wgt *wgt, const char *filename)
284 {
285         char path[PATH_MAX];
286         const char *loc;
287
288         assert(wgt);
289         assert(wgt_is_connected(wgt));
290         loc = localize(wgt, filename, path);
291         if (!loc)
292                 return -1;
293
294         return openat(wgt->rootfd, loc, O_RDONLY);
295 }
296
297
298 #if defined(TEST_wgt_validsubpath)
299 #include <stdio.h>
300 void t(const char *subpath, int validity) {
301   printf("%s -> %d = %d, %s\n", subpath, validity, validsubpath(subpath), validsubpath(subpath)==validity ? "ok" : "NOT OK");
302 }
303 int main() {
304   t("/",0);
305   t("..",0);
306   t(".",1);
307   t("../a",0);
308   t("a/..",1);
309   t("a/../////..",0);
310   t("a/../b/..",1);
311   t("a/b/c/..",1);
312   t("a/b/c/../..",1);
313   t("a/b/c/../../..",1);
314   t("a/b/c/../../../.",1);
315   t("./..a/././..b/..c/./.././.././../.",1);
316   t("./..a/././..b/..c/./.././.././.././..",0);
317   t("./..a//.//./..b/..c/./.././/./././///.././.././a/a/a/a/a",1);
318   return 0;
319 }
320 #endif
321