Refactor ALLOW_NO_SIGNATURE compile flag
[src/app-framework-main.git] / src / wgtpkg-install.c
1 /*
2  Copyright (C) 2015-2020 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 "wgtpkg-uninstall.h"
44 #include "secmgr-wrap.h"
45 #include "utils-dir.h"
46 #include "wgtpkg-unit.h"
47 #include "utils-systemd.h"
48 #include "utils-file.h"
49
50 static const char* exec_type_strings[] = {
51         "application/x-executable",
52         "application/vnd.agl.native"
53 };
54
55 static const char key_afm_prefix[] = "X-AFM-";
56 static const char key_afid[] = "ID";
57
58 #define HTTP_PORT_BASE          30000
59
60 #define AFID_MIN                1
61 #define AFID_MAX                1999
62 #define AFID_IS_VALID(afid)     (AFID_MIN <= (afid) && (afid) <= AFID_MAX)
63 #define AFID_COUNT              (AFID_MAX - AFID_MIN + 1)
64 #define AFID_ACNT               ((AFID_COUNT + 31) >> 5)
65 #define AFID_ASFT(afid)         (((afid) - AFID_MIN) & 31)
66 #define AFID_AIDX(afid)         (((afid) - AFID_MIN) >> 5)
67 #define AFID_TEST(array,afid)   ((((array)[AFID_AIDX(afid)]) >> AFID_ASFT(afid)) & 1)
68 #define AFID_SET(array,afid)    (((array)[AFID_AIDX(afid)]) |= (((uint32_t)1) << AFID_ASFT(afid)))
69
70 static uint32_t *afids_array = NULL;
71
72 static const char *default_permissions[] = {
73         "urn:AGL:token:valid"
74 };
75
76 /*
77  * normalize unit files: remove comments, remove heading blanks,
78  * make single lines
79  */
80 static void normalize_unit_file(char *content)
81 {
82         char *read, *write, c;
83
84         read = write = content;
85         c = *read++;
86         while (c) {
87                 switch (c) {
88                 case '\n':
89                 case ' ':
90                 case '\t':
91                         c = *read++;
92                         break;
93                 case '#':
94                 case ';':
95                         do { c = *read++; } while(c && c != '\n');
96                         break;
97                 default:
98                         *write++ = c;
99                         do { *write++ = c = *read++; } while(c && c != '\n');
100                         if (write - content >= 2 && write[-2] == '\\')
101                                 (--write)[-1] = ' ';
102                         break;
103                 }
104         }
105         *write = c;
106 }
107
108 static int get_afid_cb(void *closure, const char *name, const char *path, int isuser)
109 {
110         char *iter;
111         char *content;
112         size_t length;
113         int rc, p;
114
115         /* reads the file */
116         rc = getfile(path, &content, &length);
117         if (rc < 0)
118                 return rc;
119
120         /* normalize the unit file */
121         normalize_unit_file(content);
122
123         /* process the file */
124         iter = strstr(content, key_afm_prefix);
125         while (iter) {
126                 iter += sizeof key_afm_prefix - 1;
127                 if (*iter == '-')
128                         iter++;
129                 if (!strncmp(iter, key_afid, sizeof key_afid - 1)) {
130                         iter += sizeof key_afid - 1;
131                         while(*iter && *iter != '=' && *iter != '\n')
132                                 iter++;
133                         if (*iter == '=') {
134                                 while(*++iter == ' ');
135                                 p = atoi(iter);
136                                 if (AFID_IS_VALID(p))
137                                         AFID_SET((uint32_t*)closure, p);
138                         }
139                 }
140                 iter = strstr(iter, key_afm_prefix);
141         }
142         free(content);
143         return 0;
144 }
145
146 static int update_afids(uint32_t *afids)
147 {
148         int rc;
149
150         memset(afids, 0, AFID_ACNT * sizeof(uint32_t));
151         rc = systemd_unit_list(0, get_afid_cb, afids);
152         if (rc >= 0)
153                 rc = systemd_unit_list(1, get_afid_cb, afids);
154         if (rc < 0)
155                 ERROR("troubles while updating afids");
156         return rc;
157 }
158
159 static int first_free_afid(uint32_t *afids)
160 {
161         int afid;
162
163         afid = AFID_MIN;
164         while (afid <= AFID_MAX && !~afids[AFID_AIDX(afid)])
165                 afid += 32;
166         while (afid <= AFID_MAX && AFID_TEST(afids, afid))
167                 afid++;
168         if (afid > AFID_MAX) {
169                 ERROR("Can't compute a valid afid");
170                 errno = EADDRNOTAVAIL;
171                 afid = -1;
172         }
173         return afid;
174 }
175
176 static int get_new_afid()
177 {
178         int afid;
179
180         /* ensure existing afid bitmap */
181         if (afids_array == NULL) {
182                 afids_array = malloc(AFID_ACNT * sizeof(uint32_t));
183                 if (afids_array == NULL || update_afids(afids_array) < 0)
184                         return -1;
185         }
186
187         /* allocates the afid */
188         afid = first_free_afid(afids_array);
189         if (afid < 0 && errno == EADDRNOTAVAIL) {
190                 /* no more ids, try to rescan */
191                 memset(afids_array, 0, AFID_ACNT * sizeof(uint32_t));
192                 if (update_afids(afids_array) >= 0)
193                         afid = first_free_afid(afids_array);
194         }
195         if (afid >= 0)
196                 AFID_SET(afids_array, afid);
197
198         return afid;
199 }
200
201 static int check_defined(const void *data, const char *name)
202 {
203         if (data)
204                 return 0;
205         ERROR("widget has no defined '%s' (temporary constraints)", name);
206         errno = EINVAL;
207         return -1;
208 }
209
210 static int check_valid_string(const char *value, const char *name)
211 {
212         int pos;
213         char c;
214
215         if (check_defined(value, name))
216                 return -1;
217         pos = 0;
218         c = value[pos];
219         if (c == 0) {
220                 ERROR("empty string forbidden in '%s' (temporary constraints)", name);
221                 errno = EINVAL;
222                 return -1;
223         }
224         do {
225                 if (!isalnum(c) && !strchr(".-_", c)) {
226                         ERROR("forbidden char %c in '%s' -> '%s' (temporary constraints)", c, name, value);
227                         errno = EINVAL;
228                         return -1;
229                 }
230                 c = value[++pos];
231         } while(c);
232         return 0;
233 }
234
235 static int check_temporary_constraints(const struct wgt_desc *desc)
236 {
237         int result;
238
239         result  = check_valid_string(desc->id, "id");
240         result |= check_valid_string(desc->version, "version");
241         result |= check_valid_string(desc->ver, "ver");
242         result |= check_defined(desc->content_src, "content");
243         if (desc->icons)
244                 result |= check_defined(desc->icons->src, "icon.src");
245         if (result)
246                 return result;
247
248         if (desc->icons && desc->icons->next) {
249                 ERROR("widget has more than one icon defined (temporary constraints)");
250                 errno = EINVAL;
251                 result = -1;
252         }
253         return 0;
254 }
255
256 static int set_required_permissions(struct wgt_desc_param *params, int required)
257 {
258         int optional;
259
260         while (params) {
261                 /* check if target */
262                 if (!strcmp(params->name, string_sharp_target)) {
263                         /* do nothing when #target */
264                 } else {
265                         /* check the value */
266                         if (!strcmp(params->value, string_required))
267                                 optional = !required;
268                         else if (!strcmp(params->value, string_optional))
269                                 optional = 1;
270                         else {
271                                 ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
272                                 errno = EPERM;
273                                 return -1;
274                         }
275                         /* set the permission */
276                         if (request_permission(params->name)) {
277                                 DEBUG("granted permission: %s", params->name);
278                         } else if (optional) {
279                                 INFO("optional permission ungranted: %s", params->name);
280                         } else {
281                                 ERROR("ungranted permission required: %s", params->name);
282                                 errno = EPERM;
283                                 return -1;
284                         }
285                 }
286                 params = params->next;
287         }
288         return 0;
289 }
290
291 static int check_permissions(const struct wgt_desc *desc)
292 {
293         int result;
294         const struct wgt_desc_feature *feature;
295
296         result = 0;
297         feature = desc->features;
298         while(result >= 0 && feature) {
299                 if (!strcmp(feature->name, feature_required_permission))
300                         result = set_required_permissions(feature->params, feature->required);
301                 feature = feature->next;
302         }
303         return result;
304 }
305
306 static int for_all_content(const struct wgt_desc *desc, int (*action)(const char *src, const char *type))
307 {
308         int rc, rc2;
309         struct wgt_desc_feature *feat;
310         const char *src, *type;
311
312         rc = action(desc->content_src, desc->content_type);
313         feat = desc->features;
314         while (feat) {
315                 if (!strcmp(feat->name, FWK_PREFIX"widget:provided-unit")) {
316                         src = wgt_info_param(feat, "content.src");
317                         type = wgt_info_param(feat, "content.type");
318                         rc2 = action(src, type);
319                         if (rc >= 0 && rc2 < 0)
320                                 rc = rc2;
321                 }
322                 feat = feat->next;
323         }
324         return rc;
325 }
326
327 static int set_exec_flag(const char *src, const char *type)
328 {
329         int i, rc;
330
331         if (src && type) {
332                 i = sizeof exec_type_strings / sizeof *exec_type_strings;
333                 while (i) {
334                         if (!strcasecmp(type, exec_type_strings[--i])) {
335                                 rc = fchmodat(workdirfd, src, 0755, 0);
336                                 if (rc < 0)
337                                         ERROR("can't make executable the file %s", src);
338                                 return rc;
339                         }
340                 }
341         }
342         return 0;
343 }
344
345 static int check_one_content(const char *src, const char *type)
346 {
347         int rc;
348         struct stat s;
349         int fhtdocs, serr;
350
351         if (!src) {
352                 ERROR("a content src is missing");
353                 errno = EINVAL;
354                 rc = -1;
355         } else {
356                 /* TODO: when dealing with HTML and languages, the check should
357                  * include i18n path search of widgets */
358                 rc = fstatat(workdirfd, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
359                 if (rc < 0) {
360                         serr = errno;
361                         fhtdocs = openat(workdirfd, "htdocs", O_DIRECTORY|O_PATH);
362                         if (fhtdocs >= 0) {
363                                 rc = fstatat(fhtdocs, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
364                                 serr = errno;
365                                 close(fhtdocs);
366                         }
367                         errno = serr;
368                 }
369                 if (rc < 0)
370                         ERROR("can't get info on content %s: %m", src);
371                 else if (!S_ISREG(s.st_mode)) {
372                         ERROR("content %s isn't a regular file", src);
373                         errno = EINVAL;
374                         rc = -1;
375                 }
376         }
377         return rc;
378 }
379
380 static int check_content(const struct wgt_desc *desc)
381 {
382         return for_all_content(desc, check_one_content);
383 }
384
385 static int check_widget(const struct wgt_desc *desc)
386 {
387         int result;
388
389         result = check_temporary_constraints(desc);
390         if (result >= 0)
391                 result = check_permissions(desc);
392         if (result >= 0)
393                 result = check_content(desc);
394         return result;
395 }
396
397 static int get_target_directory(char target[PATH_MAX], const char *root, const struct wgt_desc *desc)
398 {
399         int rc;
400
401 #if DISTINCT_VERSIONS
402         rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
403 #else
404         rc = snprintf(target, PATH_MAX, "%s/%s", root, desc->id);
405 #endif
406         if (rc < PATH_MAX)
407                 rc = 0;
408         else {
409                 ERROR("path too long");
410                 errno = EINVAL;
411                 rc = -1;
412         }
413         return rc;
414 }
415
416 static int move_widget_to(const char *destdir, int force)
417 {
418         return move_workdir(destdir, 1, force);
419 }
420
421 static int install_icon(const struct wgt_desc *desc)
422 {
423         char link[PATH_MAX];
424         char target[PATH_MAX];
425         int rc;
426
427         if (!desc->icons)
428                 return 0;
429
430         create_directory(FWK_ICON_DIR, 0755, 1);
431         rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
432         if (rc >= (int)sizeof link) {
433                 ERROR("link too long in install_icon");
434                 errno = EINVAL;
435                 return -1;
436         }
437
438         rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
439         if (rc >= (int)sizeof target) {
440                 ERROR("target too long in install_icon");
441                 errno = EINVAL;
442                 return -1;
443         }
444
445         unlink(link);
446         rc = symlink(target, link);
447         if (rc)
448                 ERROR("can't create link %s -> %s", link, target);
449         return rc;
450 }
451
452 static int install_exec_flag(const struct wgt_desc *desc)
453 {
454         return for_all_content(desc, set_exec_flag);
455 }
456
457 static int install_file_properties(const struct wgt_desc *desc)
458 {
459         int rc, rc2;
460         struct wgt_desc_feature *feat;
461         struct wgt_desc_param *param;
462
463         rc = 0;
464         feat = desc->features;
465         while (feat) {
466                 if (!strcmp(feat->name, FWK_PREFIX"widget:file-properties")) {
467                         param = feat->params;
468                         while (param) {
469                                 if (!strcmp(param->value, "executable")) {
470                                         rc2 = fchmodat(workdirfd, param->name, 0755, 0);
471                                         if (rc2 < 0)
472                                                 ERROR("can't make executable the file %s: %m", param->name);
473                                 } else {
474                                         ERROR("unknown file property %s for %s", param->value, param->name);
475                                         errno = EINVAL;
476                                         rc2 = -1;
477                                 }
478                                 if (rc2 < 0 && !rc)
479                                         rc = rc2;
480                                 param = param->next;
481                         }
482                 }
483                 feat = feat->next;
484         }
485         return rc;
486 }
487
488 static int install_security(const struct wgt_desc *desc)
489 {
490         char path[PATH_MAX], *head;
491         const char *icon, *perm;
492         int rc;
493         unsigned int i, n, len, lic, lf;
494         struct filedesc *f;
495
496         rc = secmgr_init(desc->id);
497         if (rc)
498                 goto error;
499
500         rc = secmgr_path_public_read_only(workdir);
501         if (rc)
502                 goto error2;
503
504         /* instal the files */
505         head = stpcpy(path, workdir);
506         assert(head < path + sizeof path);
507         len = (unsigned)((path + sizeof path) - head);
508         if (!len) {
509                 ERROR("root path too long in install_security");
510                 errno = ENAMETOOLONG;
511                 goto error2;
512         }
513         len--;
514         *head++ = '/';
515         icon = desc->icons ? desc->icons->src : NULL;
516         lic = (unsigned)(icon ? strlen(icon) : 0);
517         n = file_count();
518         i = 0;
519         while(i < n) {
520                 f = file_of_index(i++);
521                 lf = (unsigned)strlen(f->name);
522                 if (lf >= len) {
523                         ERROR("path too long in install_security");
524                         errno = ENAMETOOLONG;
525                         goto error2;
526                 }
527                 strcpy(head, f->name);
528                 if (lf <= lic && icon && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
529                         rc = secmgr_path_public_read_only(path);
530                 else
531                         rc = secmgr_path_read_only(path);
532                 if (rc)
533                         goto error2;
534         }
535
536         /* install the permissions */
537         perm = first_usable_permission();
538         while(perm) {
539                 rc = secmgr_permit(perm);
540                 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
541                 if (rc)
542                         goto error2;
543                 perm = next_usable_permission();
544         }
545
546         /* install default permissions */
547         n = (unsigned int)(sizeof default_permissions / sizeof *default_permissions);
548         for (i = 0 ; i < n ; i++) {
549                 perm = default_permissions[i];
550                 rc = secmgr_permit(perm);
551                 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
552                 if (rc)
553                         goto error2;
554         }
555
556         rc = secmgr_install();
557         return rc;
558 error2:
559         secmgr_cancel();
560 error:
561         return -1;
562 }
563
564 /* install the widget of the file */
565 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
566 {
567         struct wgt_info *ifo;
568         const struct wgt_desc *desc;
569         char installdir[PATH_MAX];
570         int err, rc;
571         struct unitconf uconf;
572
573         NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
574
575         /* workdir */
576         create_directory(root, 0755, 1);
577         if (make_workdir(root, "TMP", 0)) {
578                 ERROR("failed to create a working directory");
579                 goto error1;
580         }
581
582         if (zread(wgtfile, 0))
583                 goto error2;
584
585 #if defined(ALLOW_NO_SIGNATURE)
586         rc = check_all_signatures(1);
587 #else
588         rc = check_all_signatures(0);
589 #endif
590         if (rc)
591                 goto error2;
592
593         ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
594         if (!ifo)
595                 goto error2;
596
597         reset_requested_permissions();
598         desc = wgt_info_desc(ifo);
599         if (check_widget(desc))
600                 goto error3;
601
602         if (get_target_directory(installdir, root, desc))
603                 goto error3;
604
605         if (access(installdir, F_OK) == 0) {
606                 if (!force) {
607                         ERROR("widget already installed");
608                         errno = EEXIST;
609                         goto error3;
610                 }
611                 if (uninstall_widget(desc->idaver, root))
612                         goto error3;
613         }
614
615         if (move_widget_to(installdir, force))
616                 goto error3;
617
618         if (install_icon(desc))
619                 goto error3;
620
621         if (install_security(desc))
622                 goto error4;
623
624         if (install_exec_flag(desc))
625                 goto error4;
626
627         if (install_file_properties(desc))
628                 goto error4;
629
630         uconf.installdir = installdir;
631         uconf.icondir = FWK_ICON_DIR;
632         uconf.new_afid = get_new_afid;
633         uconf.base_http_ports = HTTP_PORT_BASE;
634         if (unit_install(ifo, &uconf))
635                 goto error4;
636
637         file_reset();
638         return ifo;
639
640 error4:
641         /* TODO: cleanup */
642
643 error3:
644         wgt_info_unref(ifo);
645
646 error2:
647         err = errno;
648         remove_workdir();
649         errno = err;
650
651 error1:
652         file_reset();
653         return NULL;
654 }
655