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