Handle file-properties for setting eXecutable flag
[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_file_properties(const struct wgt_desc *desc)
392 {
393         int rc, rc2;
394         struct wgt_desc_feature *feat;
395         struct wgt_desc_param *param;
396
397         rc = 0;
398         feat = desc->features;
399         while (feat) {
400                 if (!strcmp(feat->name, "urn:AGL:widget:file-properties")) {
401                         param = feat->params;
402                         while (param) {
403                                 if (!strcmp(param->value, "executable")) {
404                                         rc2 = fchmodat(workdirfd, param->name, 0755, 0);
405                                         if (rc2 < 0)
406                                                 ERROR("can't make executable the file %s: %m", param->name);
407                                 } else {
408                                         ERROR("unknown file property %s for %s", param->value, param->name);
409                                         errno = EINVAL;
410                                         rc2 = -1;
411                                 }
412                                 if (rc2 < 0 && !rc)
413                                         rc = rc2;
414                                 param = param->next;
415                         }
416                 }
417                 feat = feat->next;
418         }
419         return rc;
420 }
421
422 static int install_security(const struct wgt_desc *desc)
423 {
424         char path[PATH_MAX], *head;
425         const char *icon, *perm;
426         int rc;
427         unsigned int i, n, len, lic, lf;
428         struct filedesc *f;
429
430         rc = secmgr_init(desc->id);
431         if (rc)
432                 goto error;
433
434         rc = secmgr_path_public_read_only(workdir);
435         if (rc)
436                 goto error2;
437
438         /* instal the files */
439         head = stpcpy(path, workdir);
440         assert(head < path + sizeof path);
441         len = (unsigned)((path + sizeof path) - head);
442         if (!len) {
443                 ERROR("root path too long in install_security");
444                 errno = ENAMETOOLONG;
445                 goto error2;
446         }
447         len--;
448         *head++ = '/';
449         icon = desc->icons ? desc->icons->src : NULL;
450         lic = (unsigned)strlen(icon);
451         n = file_count();
452         i = 0;
453         while(i < n) {
454                 f = file_of_index(i++);
455                 lf = (unsigned)strlen(f->name);
456                 if (lf >= len) {
457                         ERROR("path too long in install_security");
458                         errno = ENAMETOOLONG;
459                         goto error2;
460                 }
461                 strcpy(head, f->name);
462                 if (lf <= lic && icon && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
463                         rc = secmgr_path_public_read_only(path);
464                 else
465                         rc = secmgr_path_read_only(path);
466                 if (rc)
467                         goto error2;
468         }
469
470         /* install the permissions */
471         perm = first_usable_permission();
472         while(perm) {
473                 rc = secmgr_permit(perm);
474                 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
475                 if (rc)
476                         goto error2;
477                 perm = next_usable_permission();
478         }
479
480         rc = secmgr_install();
481         return rc;
482 error2:
483         secmgr_cancel();
484 error:
485         return -1;
486 }
487
488 /* install the widget of the file */
489 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
490 {
491         struct wgt_info *ifo;
492         const struct wgt_desc *desc;
493         char installdir[PATH_MAX];
494         int port;
495         struct unitconf uconf;
496
497         NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
498
499         /* workdir */
500         create_directory(root, 0755, 1);
501         if (make_workdir(root, "TMP", 0)) {
502                 ERROR("failed to create a working directory");
503                 goto error1;
504         }
505
506         if (zread(wgtfile, 0))
507                 goto error2;
508
509         if (check_all_signatures())
510                 goto error2;
511
512         ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
513         if (!ifo)
514                 goto error2;
515
516         reset_requested_permissions();
517         desc = wgt_info_desc(ifo);
518         if (check_widget(desc))
519                 goto error3;
520
521         if (get_target_directory(installdir, root, desc))
522                 goto error3;
523
524         if (move_widget_to(installdir, force))
525                 goto error3;
526
527         if (install_icon(desc))
528                 goto error3;
529
530         if (install_security(desc))
531                 goto error4;
532
533         if (install_exec_flag(desc))
534                 goto error4;
535
536         if (install_file_properties(desc))
537                 goto error4;
538
539         port = get_port();
540         if (port < 0)
541                 goto error4;
542
543         uconf.installdir = installdir;
544         uconf.icondir = FWK_ICON_DIR;
545         uconf.port = port;
546         if (unit_install(ifo, &uconf))
547                 goto error4;
548
549         file_reset();
550         return ifo;
551
552 error4:
553         /* TODO: cleanup */
554
555 error3:
556         wgt_info_unref(ifo);
557
558 error2:
559         remove_workdir();
560
561 error1:
562         file_reset();
563         return NULL;
564 }
565