install: improve port detection
[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->icons, "icon");
192         result |= check_defined(desc->content_src, "content");
193         if (result)
194                 return result;
195
196         if (desc->icons->next) {
197                 ERROR("widget has more than one icon defined (temporary constraints)");
198                 errno = EINVAL;
199                 result = -1;
200         }
201         return 0;
202 }
203
204 static int set_required_permissions(struct wgt_desc_param *params, int required)
205 {
206         int optional;
207
208         while (params) {
209                 /* check if target */
210                 if (!strcmp(params->name, string_sharp_target)) {
211                         /* do nothing when #target */
212                 } else {
213                         /* check the value */
214                         if (!strcmp(params->value, string_required))
215                                 optional = !required;
216                         else if (!strcmp(params->value, string_optional))
217                                 optional = 1;
218                         else {
219                                 ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
220                                 errno = EPERM;
221                                 return -1;
222                         }
223                         /* set the permission */
224                         if (request_permission(params->name)) {
225                                 DEBUG("granted permission: %s", params->name);
226                         } else if (optional) {
227                                 INFO("optional permission ungranted: %s", params->name);
228                         } else {
229                                 ERROR("ungranted permission required: %s", params->name);
230                                 errno = EPERM;
231                                 return -1;
232                         }
233                 }
234                 params = params->next;
235         }
236         return 0;
237 }
238
239 static int check_permissions(const struct wgt_desc *desc)
240 {
241         int result;
242         const struct wgt_desc_feature *feature;
243
244         result = 0;
245         feature = desc->features;
246         while(result >= 0 && feature) {
247                 if (!strcmp(feature->name, feature_required_permission))
248                         result = set_required_permissions(feature->params, feature->required);
249                 feature = feature->next;
250         }
251         return result;
252 }
253
254 static int for_all_content(const struct wgt_desc *desc, int (*action)(const char *src, const char *type))
255 {
256         int rc, rc2;
257         struct wgt_desc_feature *feat;
258         const char *src, *type;
259
260         rc = action(desc->content_src, desc->content_type);
261         feat = desc->features;
262         while (feat) {
263                 if (!strcmp(feat->name, "urn:AGL:widget:provided-unit")) {
264                         src = wgt_info_param(feat, "content.src");
265                         type = wgt_info_param(feat, "content.type");
266                         rc2 = action(src, type);
267                         if (rc >= 0 && rc2 < 0)
268                                 rc = rc2;
269                 }
270                 feat = feat->next;
271         }
272         return rc;
273 }
274
275 static int set_exec_flag(const char *src, const char *type)
276 {
277         int i, rc;
278
279         if (src && type) {
280                 i = sizeof exec_type_strings / sizeof *exec_type_strings;
281                 while (i) {
282                         if (!strcasecmp(type, exec_type_strings[--i])) {
283                                 rc = fchmodat(workdirfd, src, 0755, 0);
284                                 if (rc < 0)
285                                         ERROR("can't make executable the file %s", src);
286                                 return rc;
287                         }
288                 }
289         }
290         return 0;
291 }
292
293 static int check_one_content(const char *src, const char *type)
294 {
295         int rc;
296         struct stat s;
297
298         if (!src) {
299                 ERROR("a content src is missing");
300                 errno = EINVAL;
301                 rc = -1;
302         } else {
303                 /* TODO: when dealing with HTML and languages, the check should
304                  * include i18n path search of widgets */
305                 rc = fstatat(workdirfd, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
306                 if (rc < 0)
307                         ERROR("can't get info on content %s: %m", src);
308                 else if (!S_ISREG(s.st_mode)) {
309                         ERROR("content %s isn't a regular file", src);
310                         errno = EINVAL;
311                         rc = -1;
312                 }
313         }
314         return rc;
315 }
316
317 static int check_content(const struct wgt_desc *desc)
318 {
319         return for_all_content(desc, check_one_content);
320 }
321
322 static int check_widget(const struct wgt_desc *desc)
323 {
324         int result;
325
326         result = check_temporary_constraints(desc);
327         if (result >= 0)
328                 result = check_permissions(desc);
329         if (result >= 0)
330                 result = check_content(desc);
331         return result;
332 }
333
334 static int get_target_directory(char target[PATH_MAX], const char *root, const struct wgt_desc *desc)
335 {
336         int rc;
337
338         rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
339         if (rc < PATH_MAX)
340                 rc = 0;
341         else {
342                 ERROR("path too long");
343                 errno = EINVAL;
344                 rc = -1;
345         }
346         return rc;
347 }
348
349 static int move_widget_to(const char *destdir, int force)
350 {
351         return move_workdir(destdir, 1, force);
352 }
353
354 static int install_icon(const struct wgt_desc *desc)
355 {
356         char link[PATH_MAX];
357         char target[PATH_MAX];
358         int rc;
359
360         create_directory(FWK_ICON_DIR, 0755, 1);
361         rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
362         if (rc >= (int)sizeof link) {
363                 ERROR("link too long in install_icon");
364                 errno = EINVAL;
365                 return -1;
366         }
367
368         rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
369         if (rc >= (int)sizeof target) {
370                 ERROR("target too long in install_icon");
371                 errno = EINVAL;
372                 return -1;
373         }
374
375         unlink(link);
376         rc = symlink(target, link);
377         if (rc)
378                 ERROR("can't create link %s -> %s", link, target);
379         return rc;
380 }
381
382 static int install_exec_flag(const struct wgt_desc *desc)
383 {
384         return for_all_content(desc, set_exec_flag);
385 }
386
387 static int install_security(const struct wgt_desc *desc)
388 {
389         char path[PATH_MAX], *head;
390         const char *icon, *perm;
391         int rc;
392         unsigned int i, n, len, lic, lf;
393         struct filedesc *f;
394
395         rc = secmgr_init(desc->id);
396         if (rc)
397                 goto error;
398
399         rc = secmgr_path_public_read_only(workdir);
400         if (rc)
401                 goto error2;
402
403         /* instal the files */
404         head = stpcpy(path, workdir);
405         assert(head < path + sizeof path);
406         len = (unsigned)((path + sizeof path) - head);
407         if (!len) {
408                 ERROR("root path too long in install_security");
409                 errno = ENAMETOOLONG;
410                 goto error2;
411         }
412         len--;
413         *head++ = '/';
414         icon = desc->icons->src;
415         lic = (unsigned)strlen(icon);
416         n = file_count();
417         i = 0;
418         while(i < n) {
419                 f = file_of_index(i++);
420                 lf = (unsigned)strlen(f->name);
421                 if (lf >= len) {
422                         ERROR("path too long in install_security");
423                         errno = ENAMETOOLONG;
424                         goto error2;
425                 }
426                 strcpy(head, f->name);
427                 if (lf <= lic && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
428                         rc = secmgr_path_public_read_only(path);
429                 else
430                         rc = secmgr_path_read_only(path);
431                 if (rc)
432                         goto error2;
433         }
434
435         /* install the permissions */
436         perm = first_usable_permission();
437         while(perm) {
438                 rc = secmgr_permit(perm);
439                 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
440                 if (rc)
441                         goto error2;
442                 perm = next_usable_permission();
443         }
444
445         rc = secmgr_install();
446         return rc;
447 error2:
448         secmgr_cancel();
449 error:
450         return -1;
451 }
452
453 /* install the widget of the file */
454 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
455 {
456         struct wgt_info *ifo;
457         const struct wgt_desc *desc;
458         char installdir[PATH_MAX];
459         int port;
460         struct unitconf uconf;
461
462         NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
463
464         /* workdir */
465         create_directory(root, 0755, 1);
466         if (make_workdir(root, "TMP", 0)) {
467                 ERROR("failed to create a working directory");
468                 goto error1;
469         }
470
471         if (zread(wgtfile, 0))
472                 goto error2;
473
474         if (check_all_signatures())
475                 goto error2;
476
477         ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
478         if (!ifo)
479                 goto error2;
480
481         reset_requested_permissions();
482         desc = wgt_info_desc(ifo);
483         if (check_widget(desc))
484                 goto error3;
485
486         if (get_target_directory(installdir, root, desc))
487                 goto error3;
488
489         if (move_widget_to(installdir, force))
490                 goto error3;
491
492         if (install_icon(desc))
493                 goto error3;
494
495         if (install_security(desc))
496                 goto error4;
497
498         if (install_exec_flag(desc))
499                 goto error4;
500
501         port = get_port();
502         if (port < 0)
503                 goto error4;
504
505         uconf.installdir = installdir;
506         uconf.icondir = FWK_ICON_DIR;
507         uconf.port = port;
508         if (unit_install(ifo, &uconf))
509                 goto error4;
510
511         file_reset();
512         return ifo;
513
514 error4:
515         /* TODO: cleanup */
516
517 error3:
518         wgt_info_unref(ifo);
519
520 error2:
521         remove_workdir();
522
523 error1:
524         file_reset();
525         return NULL;
526 }
527