wgtpkg-install: choose the port
[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 <sys/stat.h>
31
32 #include "verbose.h"
33 #include "wgt.h"
34 #include "wgt-info.h"
35 #include "wgt-strings.h"
36 #include "wgtpkg-files.h"
37 #include "wgtpkg-workdir.h"
38 #include "wgtpkg-zip.h"
39 #include "wgtpkg-permissions.h"
40 #include "wgtpkg-digsig.h"
41 #include "wgtpkg-install.h"
42 #include "secmgr-wrap.h"
43 #include "utils-dir.h"
44 #include "wgtpkg-unit.h"
45 #include "utils-systemd.h"
46 #include "utils-file.h"
47
48 static const char* exec_type_strings[] = {
49         "application/x-executable",
50         "application/vnd.agl.native"
51 };
52
53 static const char key_http_port[] = "X-AFM--http-port";
54
55 static int get_port_cb(void *closure, const char *name, const char *path, int isuser)
56 {
57         char *iter;
58         char *content;
59         size_t length;
60         int rc, p;
61
62         /* reads the file */
63         rc = getfile(path, &content, &length);
64         if (rc < 0)
65                 return rc;
66
67         /* process the file */
68         iter = strstr(content, key_http_port);
69         while (iter) {
70                 iter += sizeof key_http_port - 1;
71                 while(*iter && *iter != '=' && *iter != '\n')
72                         iter++;
73                 if (*iter == '=') {
74                         while(*++iter == ' ');
75                         p = atoi(iter);
76                         if (p >= 0 && p < 32768)
77                                 ((uint32_t*)closure)[p >> 5] |= (uint32_t)1 << (p & 31);
78                 }
79                 iter = strstr(iter, key_http_port);
80         }
81         free(content);
82         return 0;
83 }
84
85 static int get_port()
86 {
87         int rc;
88         uint32_t ports[1024]; /* 1024 * 32 = 32768 */
89
90         memset(ports, 0, sizeof ports);
91         rc = systemd_unit_list(0, get_port_cb, &ports);
92         if (rc >= 0) {
93                 rc = systemd_unit_list(1, get_port_cb, ports);
94                 if (rc >= 0) {
95                         for (rc = 1024 ; rc < 32768 && !~ports[rc >> 5] ; rc += 32);
96                         if (rc == 32768) {
97                                 errno = EADDRNOTAVAIL;
98                                 rc = -1;
99                         } else {
100                                 while (1 & (ports[rc >> 5] >> (rc & 31))) rc++;
101                         }
102                 }
103         }
104         return rc;
105 }
106
107 static int check_defined(const void *data, const char *name)
108 {
109         if (data)
110                 return 0;
111         ERROR("widget has no defined '%s' (temporary constraints)", name);
112         errno = EINVAL;
113         return -1;
114 }
115
116 static int check_valid_string(const char *value, const char *name)
117 {
118         int pos;
119         char c;
120
121         if (check_defined(value, name))
122                 return -1;
123         pos = 0;
124         c = value[pos];
125         if (c == 0) {
126                 ERROR("empty string forbidden in '%s' (temporary constraints)", name);
127                 errno = EINVAL;
128                 return -1;                      
129         }
130         do {
131                 if (!isalnum(c) && !strchr(".-_", c)) {
132                         ERROR("forbidden char %c in '%s' -> '%s' (temporary constraints)", c, name, value);
133                         errno = EINVAL;
134                         return -1;                      
135                 }
136                 c = value[++pos];
137         } while(c);
138         return 0;
139 }
140
141 static int check_temporary_constraints(const struct wgt_desc *desc)
142 {
143         int result;
144
145         result  = check_valid_string(desc->id, "id");
146         result |= check_valid_string(desc->version, "version");
147         result |= check_valid_string(desc->ver, "ver");
148         result |= check_defined(desc->icons, "icon");
149         result |= check_defined(desc->content_src, "content");
150         if (result)
151                 return result;
152
153         if (desc->icons->next) {
154                 ERROR("widget has more than one icon defined (temporary constraints)");
155                 errno = EINVAL;
156                 result = -1;
157         }
158         return 0;
159 }
160
161 static int set_required_permissions(struct wgt_desc_param *params, int required)
162 {
163         int optional;
164
165         while (params) {
166                 /* check if target */
167                 if (!strcmp(params->name, string_sharp_target)) {
168                         /* do nothing when #target */
169                 } else {
170                         /* check the value */
171                         if (!strcmp(params->value, string_required))
172                                 optional = !required;
173                         else if (!strcmp(params->value, string_optional))
174                                 optional = 1;
175                         else {
176                                 ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
177                                 errno = EPERM;
178                                 return -1;
179                         }
180                         /* set the permission */
181                         if (request_permission(params->name)) {
182                                 DEBUG("granted permission: %s", params->name);
183                         } else if (optional) {
184                                 INFO("optional permission ungranted: %s", params->name);
185                         } else {
186                                 ERROR("ungranted permission required: %s", params->name);
187                                 errno = EPERM;
188                                 return -1;
189                         }
190                 }
191                 params = params->next;
192         }
193         return 0;
194 }
195
196 static int check_permissions(const struct wgt_desc *desc)
197 {
198         int result;
199         const struct wgt_desc_feature *feature;
200
201         result = 0;
202         feature = desc->features;
203         while(result >= 0 && feature) {
204                 if (!strcmp(feature->name, feature_required_permission))
205                         result = set_required_permissions(feature->params, feature->required);
206                 feature = feature->next;
207         }
208         return result;
209 }
210
211 static int check_widget(const struct wgt_desc *desc)
212 {
213         int result;
214
215         result = check_temporary_constraints(desc);
216         if (result >= 0)
217                 result = check_permissions(desc);
218         return result;
219 }
220
221 static int get_target_directory(char target[PATH_MAX], const char *root, const struct wgt_desc *desc)
222 {
223         int rc;
224
225         rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
226         if (rc < PATH_MAX)
227                 rc = 0;
228         else {
229                 ERROR("path too long");
230                 errno = EINVAL;
231                 rc = -1;
232         }
233         return rc;
234 }
235
236 static int move_widget_to(const char *destdir, int force)
237 {
238         return move_workdir(destdir, 1, force);
239 }
240
241 static int install_icon(const struct wgt_desc *desc)
242 {
243         char link[PATH_MAX];
244         char target[PATH_MAX];
245         int rc;
246
247         create_directory(FWK_ICON_DIR, 0755, 1);
248         rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
249         if (rc >= (int)sizeof link) {
250                 ERROR("link too long in install_icon");
251                 errno = EINVAL;
252                 return -1;
253         }
254
255         rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
256         if (rc >= (int)sizeof target) {
257                 ERROR("target too long in install_icon");
258                 errno = EINVAL;
259                 return -1;
260         }
261
262         unlink(link);
263         rc = symlink(target, link);
264         if (rc)
265                 ERROR("can't create link %s -> %s", link, target);
266         return rc;
267 }
268
269 static int install_exec_flag(const struct wgt_desc *desc)
270 {
271         int i;
272
273         if (desc->content_type) {
274                 i = sizeof exec_type_strings / sizeof *exec_type_strings;
275                 while (i) {
276                         if (!strcasecmp(desc->content_type, exec_type_strings[--i]))
277                                 return fchmodat(workdirfd, desc->content_src, 0755, 0);
278                 }
279         }
280         return 0;
281 }
282
283 static int install_security(const struct wgt_desc *desc)
284 {
285         char path[PATH_MAX], *head;
286         const char *icon, *perm;
287         int rc;
288         unsigned int i, n, len, lic, lf;
289         struct filedesc *f;
290
291         rc = secmgr_init(desc->id);
292         if (rc)
293                 goto error;
294
295         rc = secmgr_path_public_read_only(workdir);
296         if (rc)
297                 goto error2;
298
299         /* instal the files */
300         head = stpcpy(path, workdir);
301         assert(head < path + sizeof path);
302         len = (unsigned)((path + sizeof path) - head);
303         if (!len) {
304                 ERROR("root path too long in install_security");
305                 errno = ENAMETOOLONG;
306                 goto error2;
307         }
308         len--;
309         *head++ = '/';
310         icon = desc->icons->src;
311         lic = (unsigned)strlen(icon);
312         n = file_count();
313         i = 0;
314         while(i < n) {
315                 f = file_of_index(i++);
316                 lf = (unsigned)strlen(f->name);
317                 if (lf >= len) {
318                         ERROR("path too long in install_security");
319                         errno = ENAMETOOLONG;
320                         goto error2;
321                 }
322                 strcpy(head, f->name);
323                 if (lf <= lic && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
324                         rc = secmgr_path_public_read_only(path);
325                 else
326                         rc = secmgr_path_read_only(path);
327                 if (rc)
328                         goto error2;
329         }
330
331         /* install the permissions */
332         perm = first_usable_permission();
333         while(perm) {
334                 rc = secmgr_permit(perm);
335                 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
336                 if (rc)
337                         goto error2;
338                 perm = next_usable_permission();
339         }
340
341         rc = secmgr_install();
342         return rc;
343 error2:
344         secmgr_cancel();
345 error:
346         return -1;
347 }
348
349 /* install the widget of the file */
350 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
351 {
352         struct wgt_info *ifo;
353         const struct wgt_desc *desc;
354         char installdir[PATH_MAX];
355         int port;
356
357         NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
358
359         /* workdir */
360         create_directory(root, 0755, 1);
361         if (make_workdir(root, "TMP", 0)) {
362                 ERROR("failed to create a working directory");
363                 goto error1;
364         }
365
366         if (zread(wgtfile, 0))
367                 goto error2;
368
369         if (check_all_signatures())
370                 goto error2;
371
372         ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
373         if (!ifo)
374                 goto error2;
375
376         reset_requested_permissions();
377         desc = wgt_info_desc(ifo);
378         if (check_widget(desc))
379                 goto error3;
380
381         if (get_target_directory(installdir, root, desc))
382                 goto error3;
383
384         if (move_widget_to(installdir, force))
385                 goto error3;
386
387         if (install_icon(desc))
388                 goto error3;
389
390         if (install_security(desc))
391                 goto error4;
392
393         if (install_exec_flag(desc))
394                 goto error4;
395
396         port = get_port();
397         if (port < 0)
398                 goto error4;
399
400         if (unit_install(ifo, installdir, FWK_ICON_DIR, port))
401                 goto error4;
402
403         file_reset();
404         return ifo;
405
406 error4:
407         /* todo: cleanup */
408
409 error3:
410         wgt_info_unref(ifo);
411
412 error2:
413         remove_workdir();
414
415 error1:
416         file_reset();
417         return NULL;
418 }
419