3f5ab928d53f8ab6861edbda6bf2e0f8b3f2806d
[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_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
299         if (!src) {
300                 ERROR("a content src is missing");
301                 errno = EINVAL;
302                 rc = -1;
303         } else {
304                 /* TODO: when dealing with HTML and languages, the check should
305                  * include i18n path search of widgets */
306                 rc = fstatat(workdirfd, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
307                 if (rc < 0)
308                         ERROR("can't get info on content %s: %m", src);
309                 else if (!S_ISREG(s.st_mode)) {
310                         ERROR("content %s isn't a regular file", src);
311                         errno = EINVAL;
312                         rc = -1;
313                 }
314         }
315         return rc;
316 }
317
318 static int check_content(const struct wgt_desc *desc)
319 {
320         return for_all_content(desc, check_one_content);
321 }
322
323 static int check_widget(const struct wgt_desc *desc)
324 {
325         int result;
326
327         result = check_temporary_constraints(desc);
328         if (result >= 0)
329                 result = check_permissions(desc);
330         if (result >= 0)
331                 result = check_content(desc);
332         return result;
333 }
334
335 static int get_target_directory(char target[PATH_MAX], const char *root, const struct wgt_desc *desc)
336 {
337         int rc;
338
339         rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
340         if (rc < PATH_MAX)
341                 rc = 0;
342         else {
343                 ERROR("path too long");
344                 errno = EINVAL;
345                 rc = -1;
346         }
347         return rc;
348 }
349
350 static int move_widget_to(const char *destdir, int force)
351 {
352         return move_workdir(destdir, 1, force);
353 }
354
355 static int install_icon(const struct wgt_desc *desc)
356 {
357         char link[PATH_MAX];
358         char target[PATH_MAX];
359         int rc;
360
361         if (!desc->icons)
362                 return 0;
363
364         create_directory(FWK_ICON_DIR, 0755, 1);
365         rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
366         if (rc >= (int)sizeof link) {
367                 ERROR("link too long in install_icon");
368                 errno = EINVAL;
369                 return -1;
370         }
371
372         rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
373         if (rc >= (int)sizeof target) {
374                 ERROR("target too long in install_icon");
375                 errno = EINVAL;
376                 return -1;
377         }
378
379         unlink(link);
380         rc = symlink(target, link);
381         if (rc)
382                 ERROR("can't create link %s -> %s", link, target);
383         return rc;
384 }
385
386 static int install_exec_flag(const struct wgt_desc *desc)
387 {
388         return for_all_content(desc, set_exec_flag);
389 }
390
391 static int install_security(const struct wgt_desc *desc)
392 {
393         char path[PATH_MAX], *head;
394         const char *icon, *perm;
395         int rc;
396         unsigned int i, n, len, lic, lf;
397         struct filedesc *f;
398
399         rc = secmgr_init(desc->id);
400         if (rc)
401                 goto error;
402
403         rc = secmgr_path_public_read_only(workdir);
404         if (rc)
405                 goto error2;
406
407         /* instal the files */
408         head = stpcpy(path, workdir);
409         assert(head < path + sizeof path);
410         len = (unsigned)((path + sizeof path) - head);
411         if (!len) {
412                 ERROR("root path too long in install_security");
413                 errno = ENAMETOOLONG;
414                 goto error2;
415         }
416         len--;
417         *head++ = '/';
418         icon = desc->icons ? desc->icons->src : NULL;
419         lic = (unsigned)strlen(icon);
420         n = file_count();
421         i = 0;
422         while(i < n) {
423                 f = file_of_index(i++);
424                 lf = (unsigned)strlen(f->name);
425                 if (lf >= len) {
426                         ERROR("path too long in install_security");
427                         errno = ENAMETOOLONG;
428                         goto error2;
429                 }
430                 strcpy(head, f->name);
431                 if (lf <= lic && icon && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
432                         rc = secmgr_path_public_read_only(path);
433                 else
434                         rc = secmgr_path_read_only(path);
435                 if (rc)
436                         goto error2;
437         }
438
439         /* install the permissions */
440         perm = first_usable_permission();
441         while(perm) {
442                 rc = secmgr_permit(perm);
443                 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
444                 if (rc)
445                         goto error2;
446                 perm = next_usable_permission();
447         }
448
449         rc = secmgr_install();
450         return rc;
451 error2:
452         secmgr_cancel();
453 error:
454         return -1;
455 }
456
457 /* install the widget of the file */
458 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
459 {
460         struct wgt_info *ifo;
461         const struct wgt_desc *desc;
462         char installdir[PATH_MAX];
463         int port;
464         struct unitconf uconf;
465
466         NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
467
468         /* workdir */
469         create_directory(root, 0755, 1);
470         if (make_workdir(root, "TMP", 0)) {
471                 ERROR("failed to create a working directory");
472                 goto error1;
473         }
474
475         if (zread(wgtfile, 0))
476                 goto error2;
477
478         if (check_all_signatures())
479                 goto error2;
480
481         ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
482         if (!ifo)
483                 goto error2;
484
485         reset_requested_permissions();
486         desc = wgt_info_desc(ifo);
487         if (check_widget(desc))
488                 goto error3;
489
490         if (get_target_directory(installdir, root, desc))
491                 goto error3;
492
493         if (move_widget_to(installdir, force))
494                 goto error3;
495
496         if (install_icon(desc))
497                 goto error3;
498
499         if (install_security(desc))
500                 goto error4;
501
502         if (install_exec_flag(desc))
503                 goto error4;
504
505         port = get_port();
506         if (port < 0)
507                 goto error4;
508
509         uconf.installdir = installdir;
510         uconf.icondir = FWK_ICON_DIR;
511         uconf.port = port;
512         if (unit_install(ifo, &uconf))
513                 goto error4;
514
515         file_reset();
516         return ifo;
517
518 error4:
519         /* TODO: cleanup */
520
521 error3:
522         wgt_info_unref(ifo);
523
524 error2:
525         remove_workdir();
526
527 error1:
528         file_reset();
529         return NULL;
530 }
531