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