Check content existing and fix executable
[src/app-framework-main.git] / src / wgtpkg-install.c
1 /*
2  Copyright 2015, 2016, 2017 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
6  Licensed under the Apache License, Version 2.0 (the "License");
7  you may not use this file except in compliance with the License.
8  You may obtain a copy of the License at
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 */
18
19 #define _GNU_SOURCE
20
21 #include <limits.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <assert.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32
33 #include "verbose.h"
34 #include "wgt.h"
35 #include "wgt-info.h"
36 #include "wgt-strings.h"
37 #include "wgtpkg-files.h"
38 #include "wgtpkg-workdir.h"
39 #include "wgtpkg-zip.h"
40 #include "wgtpkg-permissions.h"
41 #include "wgtpkg-digsig.h"
42 #include "wgtpkg-install.h"
43 #include "secmgr-wrap.h"
44 #include "utils-dir.h"
45 #include "wgtpkg-unit.h"
46 #include "utils-systemd.h"
47 #include "utils-file.h"
48
49 static const char* exec_type_strings[] = {
50         "application/x-executable",
51         "application/vnd.agl.native"
52 };
53
54 static const char key_http_port[] = "X-AFM--http-port";
55
56 static int get_port_cb(void *closure, const char *name, const char *path, int isuser)
57 {
58         char *iter;
59         char *content;
60         size_t length;
61         int rc, p;
62
63         /* reads the file */
64         rc = getfile(path, &content, &length);
65         if (rc < 0)
66                 return rc;
67
68         /* process the file */
69         iter = strstr(content, key_http_port);
70         while (iter) {
71                 iter += sizeof key_http_port - 1;
72                 while(*iter && *iter != '=' && *iter != '\n')
73                         iter++;
74                 if (*iter == '=') {
75                         while(*++iter == ' ');
76                         p = atoi(iter);
77                         if (p >= 0 && p < 32768)
78                                 ((uint32_t*)closure)[p >> 5] |= (uint32_t)1 << (p & 31);
79                 }
80                 iter = strstr(iter, key_http_port);
81         }
82         free(content);
83         return 0;
84 }
85
86 static int get_port()
87 {
88         int rc;
89         uint32_t ports[1024]; /* 1024 * 32 = 32768 */
90
91         memset(ports, 0, sizeof ports);
92         rc = systemd_unit_list(0, get_port_cb, &ports);
93         if (rc >= 0) {
94                 rc = systemd_unit_list(1, get_port_cb, ports);
95                 if (rc >= 0) {
96                         for (rc = 1024 ; rc < 32768 && !~ports[rc >> 5] ; rc += 32);
97                         if (rc == 32768) {
98                                 errno = EADDRNOTAVAIL;
99                                 rc = -1;
100                         } else {
101                                 while (1 & (ports[rc >> 5] >> (rc & 31))) rc++;
102                         }
103                 }
104         }
105         return rc;
106 }
107
108 static int check_defined(const void *data, const char *name)
109 {
110         if (data)
111                 return 0;
112         ERROR("widget has no defined '%s' (temporary constraints)", name);
113         errno = EINVAL;
114         return -1;
115 }
116
117 static int check_valid_string(const char *value, const char *name)
118 {
119         int pos;
120         char c;
121
122         if (check_defined(value, name))
123                 return -1;
124         pos = 0;
125         c = value[pos];
126         if (c == 0) {
127                 ERROR("empty string forbidden in '%s' (temporary constraints)", name);
128                 errno = EINVAL;
129                 return -1;                      
130         }
131         do {
132                 if (!isalnum(c) && !strchr(".-_", c)) {
133                         ERROR("forbidden char %c in '%s' -> '%s' (temporary constraints)", c, name, value);
134                         errno = EINVAL;
135                         return -1;                      
136                 }
137                 c = value[++pos];
138         } while(c);
139         return 0;
140 }
141
142 static int check_temporary_constraints(const struct wgt_desc *desc)
143 {
144         int result;
145
146         result  = check_valid_string(desc->id, "id");
147         result |= check_valid_string(desc->version, "version");
148         result |= check_valid_string(desc->ver, "ver");
149         result |= check_defined(desc->icons, "icon");
150         result |= check_defined(desc->content_src, "content");
151         if (result)
152                 return result;
153
154         if (desc->icons->next) {
155                 ERROR("widget has more than one icon defined (temporary constraints)");
156                 errno = EINVAL;
157                 result = -1;
158         }
159         return 0;
160 }
161
162 static int set_required_permissions(struct wgt_desc_param *params, int required)
163 {
164         int optional;
165
166         while (params) {
167                 /* check if target */
168                 if (!strcmp(params->name, string_sharp_target)) {
169                         /* do nothing when #target */
170                 } else {
171                         /* check the value */
172                         if (!strcmp(params->value, string_required))
173                                 optional = !required;
174                         else if (!strcmp(params->value, string_optional))
175                                 optional = 1;
176                         else {
177                                 ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
178                                 errno = EPERM;
179                                 return -1;
180                         }
181                         /* set the permission */
182                         if (request_permission(params->name)) {
183                                 DEBUG("granted permission: %s", params->name);
184                         } else if (optional) {
185                                 INFO("optional permission ungranted: %s", params->name);
186                         } else {
187                                 ERROR("ungranted permission required: %s", params->name);
188                                 errno = EPERM;
189                                 return -1;
190                         }
191                 }
192                 params = params->next;
193         }
194         return 0;
195 }
196
197 static int check_permissions(const struct wgt_desc *desc)
198 {
199         int result;
200         const struct wgt_desc_feature *feature;
201
202         result = 0;
203         feature = desc->features;
204         while(result >= 0 && feature) {
205                 if (!strcmp(feature->name, feature_required_permission))
206                         result = set_required_permissions(feature->params, feature->required);
207                 feature = feature->next;
208         }
209         return result;
210 }
211
212 static int for_all_content(const struct wgt_desc *desc, int (*action)(const char *src, const char *type))
213 {
214         int rc, rc2;
215         struct wgt_desc_feature *feat;
216         const char *src, *type;
217
218         rc = action(desc->content_src, desc->content_type);
219         feat = desc->features;
220         while (feat) {
221                 if (!strcmp(feat->name, "urn:AGL:widget:provided-unit")) {
222                         src = wgt_info_param(feat, "content.src");
223                         type = wgt_info_param(feat, "content.type");
224                         rc2 = action(src, type);
225                         if (rc >= 0 && rc2 < 0)
226                                 rc = rc2;
227                 }
228                 feat = feat->next;
229         }
230         return rc;
231 }
232
233 static int set_exec_flag(const char *src, const char *type)
234 {
235         int i, rc;
236
237         if (src && type) {
238                 i = sizeof exec_type_strings / sizeof *exec_type_strings;
239                 while (i) {
240                         if (!strcasecmp(type, exec_type_strings[--i])) {
241                                 rc = fchmodat(workdirfd, src, 0755, 0);
242                                 if (rc < 0)
243                                         ERROR("can't make executable the file %s", src);
244                                 return rc;
245                         }
246                 }
247         }
248         return 0;
249 }
250
251 static int check_one_content(const char *src, const char *type)
252 {
253         int rc;
254         struct stat s;
255
256         if (!src) {
257                 ERROR("a content src is missing");
258                 errno = EINVAL;
259                 rc = -1;
260         } else {
261                 /* TODO: when dealing with HTML and languages, the check should
262                  * include i18n path search of widgets */
263                 rc = fstatat(workdirfd, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
264                 if (rc < 0)
265                         ERROR("can't get info on content %s: %m", src);
266                 else if (!S_ISREG(s.st_mode)) {
267                         ERROR("content %s isn't a regular file", src);
268                         errno = EINVAL;
269                         rc = -1;
270                 }
271         }
272         return rc;
273 }
274
275 static int check_content(const struct wgt_desc *desc)
276 {
277         return for_all_content(desc, check_one_content);
278 }
279
280 static int check_widget(const struct wgt_desc *desc)
281 {
282         int result;
283
284         result = check_temporary_constraints(desc);
285         if (result >= 0)
286                 result = check_permissions(desc);
287         if (result >= 0)
288                 result = check_content(desc);
289         return result;
290 }
291
292 static int get_target_directory(char target[PATH_MAX], const char *root, const struct wgt_desc *desc)
293 {
294         int rc;
295
296         rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
297         if (rc < PATH_MAX)
298                 rc = 0;
299         else {
300                 ERROR("path too long");
301                 errno = EINVAL;
302                 rc = -1;
303         }
304         return rc;
305 }
306
307 static int move_widget_to(const char *destdir, int force)
308 {
309         return move_workdir(destdir, 1, force);
310 }
311
312 static int install_icon(const struct wgt_desc *desc)
313 {
314         char link[PATH_MAX];
315         char target[PATH_MAX];
316         int rc;
317
318         create_directory(FWK_ICON_DIR, 0755, 1);
319         rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
320         if (rc >= (int)sizeof link) {
321                 ERROR("link too long in install_icon");
322                 errno = EINVAL;
323                 return -1;
324         }
325
326         rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
327         if (rc >= (int)sizeof target) {
328                 ERROR("target too long in install_icon");
329                 errno = EINVAL;
330                 return -1;
331         }
332
333         unlink(link);
334         rc = symlink(target, link);
335         if (rc)
336                 ERROR("can't create link %s -> %s", link, target);
337         return rc;
338 }
339
340 static int install_exec_flag(const struct wgt_desc *desc)
341 {
342         return for_all_content(desc, set_exec_flag);
343 }
344
345 static int install_security(const struct wgt_desc *desc)
346 {
347         char path[PATH_MAX], *head;
348         const char *icon, *perm;
349         int rc;
350         unsigned int i, n, len, lic, lf;
351         struct filedesc *f;
352
353         rc = secmgr_init(desc->id);
354         if (rc)
355                 goto error;
356
357         rc = secmgr_path_public_read_only(workdir);
358         if (rc)
359                 goto error2;
360
361         /* instal the files */
362         head = stpcpy(path, workdir);
363         assert(head < path + sizeof path);
364         len = (unsigned)((path + sizeof path) - head);
365         if (!len) {
366                 ERROR("root path too long in install_security");
367                 errno = ENAMETOOLONG;
368                 goto error2;
369         }
370         len--;
371         *head++ = '/';
372         icon = desc->icons->src;
373         lic = (unsigned)strlen(icon);
374         n = file_count();
375         i = 0;
376         while(i < n) {
377                 f = file_of_index(i++);
378                 lf = (unsigned)strlen(f->name);
379                 if (lf >= len) {
380                         ERROR("path too long in install_security");
381                         errno = ENAMETOOLONG;
382                         goto error2;
383                 }
384                 strcpy(head, f->name);
385                 if (lf <= lic && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
386                         rc = secmgr_path_public_read_only(path);
387                 else
388                         rc = secmgr_path_read_only(path);
389                 if (rc)
390                         goto error2;
391         }
392
393         /* install the permissions */
394         perm = first_usable_permission();
395         while(perm) {
396                 rc = secmgr_permit(perm);
397                 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
398                 if (rc)
399                         goto error2;
400                 perm = next_usable_permission();
401         }
402
403         rc = secmgr_install();
404         return rc;
405 error2:
406         secmgr_cancel();
407 error:
408         return -1;
409 }
410
411 /* install the widget of the file */
412 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
413 {
414         struct wgt_info *ifo;
415         const struct wgt_desc *desc;
416         char installdir[PATH_MAX];
417         int port;
418         struct unitconf uconf;
419
420         NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
421
422         /* workdir */
423         create_directory(root, 0755, 1);
424         if (make_workdir(root, "TMP", 0)) {
425                 ERROR("failed to create a working directory");
426                 goto error1;
427         }
428
429         if (zread(wgtfile, 0))
430                 goto error2;
431
432         if (check_all_signatures())
433                 goto error2;
434
435         ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
436         if (!ifo)
437                 goto error2;
438
439         reset_requested_permissions();
440         desc = wgt_info_desc(ifo);
441         if (check_widget(desc))
442                 goto error3;
443
444         if (get_target_directory(installdir, root, desc))
445                 goto error3;
446
447         if (move_widget_to(installdir, force))
448                 goto error3;
449
450         if (install_icon(desc))
451                 goto error3;
452
453         if (install_security(desc))
454                 goto error4;
455
456         if (install_exec_flag(desc))
457                 goto error4;
458
459         port = get_port();
460         if (port < 0)
461                 goto error4;
462
463         uconf.installdir = installdir;
464         uconf.icondir = FWK_ICON_DIR;
465         uconf.port = port;
466         if (unit_install(ifo, &uconf))
467                 goto error4;
468
469         file_reset();
470         return ifo;
471
472 error4:
473         /* TODO: cleanup */
474
475 error3:
476         wgt_info_unref(ifo);
477
478 error2:
479         remove_workdir();
480
481 error1:
482         file_reset();
483         return NULL;
484 }
485