2 Copyright (C) 2015-2019 IoT.bzh
4 author: José Bollo <jose.bollo@iot.bzh>
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
10 http://www.apache.org/licenses/LICENSE-2.0
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.
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 "wgtpkg-uninstall.h"
44 #include "secmgr-wrap.h"
45 #include "utils-dir.h"
46 #include "wgtpkg-unit.h"
47 #include "utils-systemd.h"
48 #include "utils-file.h"
50 static const char* exec_type_strings[] = {
51 "application/x-executable",
52 "application/vnd.agl.native"
55 static const char key_afm_prefix[] = "X-AFM-";
56 static const char key_http_port[] = "http-port";
58 #define HTTP_PORT_MIN 31000
59 #define HTTP_PORT_MAX 32759
60 #define HTTP_PORT_IS_VALID(port) (HTTP_PORT_MIN <= (port) && (port) <= HTTP_PORT_MAX)
61 #define HTTP_PORT_COUNT (HTTP_PORT_MAX - HTTP_PORT_MIN + 1)
62 #define HTTP_PORT_ACNT ((HTTP_PORT_COUNT + 31) >> 5)
63 #define HTTP_PORT_ASFT(port) (((port) - HTTP_PORT_MIN) & 31)
64 #define HTTP_PORT_AIDX(port) (((port) - HTTP_PORT_MIN) >> 5)
65 #define HTTP_PORT_TEST(array,port) ((((array)[HTTP_PORT_AIDX(port)]) >> HTTP_PORT_ASFT(port)) & 1)
66 #define HTTP_PORT_SET(array,port) (((array)[HTTP_PORT_AIDX(port)]) |= (((uint32_t)1) << HTTP_PORT_ASFT(port)))
68 static uint32_t *port_bits = NULL;
70 static const char *default_permissions[] = {
75 * normalize unit files: remove comments, remove heading blanks,
78 static void normalize_unit_file(char *content)
80 char *read, *write, c;
82 read = write = content;
93 do { c = *read++; } while(c && c != '\n');
97 do { *write++ = c = *read++; } while(c && c != '\n');
98 if (write - content >= 2 && write[-2] == '\\')
106 static int get_port_cb(void *closure, const char *name, const char *path, int isuser)
114 rc = getfile(path, &content, &length);
118 /* normalize the unit file */
119 normalize_unit_file(content);
121 /* process the file */
122 iter = strstr(content, key_afm_prefix);
124 iter += sizeof key_afm_prefix - 1;
127 if (!strncmp(iter, key_http_port, sizeof key_http_port - 1)) {
128 iter += sizeof key_http_port - 1;
129 while(*iter && *iter != '=' && *iter != '\n')
132 while(*++iter == ' ');
134 if (HTTP_PORT_IS_VALID(p))
135 HTTP_PORT_SET((uint32_t*)closure, p);
138 iter = strstr(iter, key_afm_prefix);
144 static int update_portbits(uint32_t *portbits)
148 memset(portbits, 0, HTTP_PORT_ACNT * sizeof(uint32_t));
149 rc = systemd_unit_list(0, get_port_cb, portbits);
151 rc = systemd_unit_list(1, get_port_cb, portbits);
153 ERROR("troubles while updating ports");
157 static int first_free_port(uint32_t *portbits)
161 port = HTTP_PORT_MIN;
162 while (port <= HTTP_PORT_MAX && !~portbits[HTTP_PORT_AIDX(port)])
164 while (port <= HTTP_PORT_MAX && HTTP_PORT_TEST(portbits, port))
166 if (port > HTTP_PORT_MAX) {
167 ERROR("Can't compute a valid port");
168 errno = EADDRNOTAVAIL;
174 static int get_port()
178 /* ensure existing port bitmap */
179 if (port_bits == NULL) {
180 port_bits = malloc(HTTP_PORT_ACNT * sizeof(uint32_t));
181 if (port_bits == NULL || update_portbits(port_bits) < 0)
185 /* allocates the port */
186 port = first_free_port(port_bits);
188 HTTP_PORT_SET(port_bits, port);
193 static int check_defined(const void *data, const char *name)
197 ERROR("widget has no defined '%s' (temporary constraints)", name);
202 static int check_valid_string(const char *value, const char *name)
207 if (check_defined(value, name))
212 ERROR("empty string forbidden in '%s' (temporary constraints)", name);
217 if (!isalnum(c) && !strchr(".-_", c)) {
218 ERROR("forbidden char %c in '%s' -> '%s' (temporary constraints)", c, name, value);
227 static int check_temporary_constraints(const struct wgt_desc *desc)
231 result = check_valid_string(desc->id, "id");
232 result |= check_valid_string(desc->version, "version");
233 result |= check_valid_string(desc->ver, "ver");
234 result |= check_defined(desc->content_src, "content");
236 result |= check_defined(desc->icons->src, "icon.src");
240 if (desc->icons && desc->icons->next) {
241 ERROR("widget has more than one icon defined (temporary constraints)");
248 static int set_required_permissions(struct wgt_desc_param *params, int required)
253 /* check if target */
254 if (!strcmp(params->name, string_sharp_target)) {
255 /* do nothing when #target */
257 /* check the value */
258 if (!strcmp(params->value, string_required))
259 optional = !required;
260 else if (!strcmp(params->value, string_optional))
263 ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
267 /* set the permission */
268 if (request_permission(params->name)) {
269 DEBUG("granted permission: %s", params->name);
270 } else if (optional) {
271 INFO("optional permission ungranted: %s", params->name);
273 ERROR("ungranted permission required: %s", params->name);
278 params = params->next;
283 static int check_permissions(const struct wgt_desc *desc)
286 const struct wgt_desc_feature *feature;
289 feature = desc->features;
290 while(result >= 0 && feature) {
291 if (!strcmp(feature->name, feature_required_permission))
292 result = set_required_permissions(feature->params, feature->required);
293 feature = feature->next;
298 static int for_all_content(const struct wgt_desc *desc, int (*action)(const char *src, const char *type))
301 struct wgt_desc_feature *feat;
302 const char *src, *type;
304 rc = action(desc->content_src, desc->content_type);
305 feat = desc->features;
307 if (!strcmp(feat->name, FWK_PREFIX"widget:provided-unit")) {
308 src = wgt_info_param(feat, "content.src");
309 type = wgt_info_param(feat, "content.type");
310 rc2 = action(src, type);
311 if (rc >= 0 && rc2 < 0)
319 static int set_exec_flag(const char *src, const char *type)
324 i = sizeof exec_type_strings / sizeof *exec_type_strings;
326 if (!strcasecmp(type, exec_type_strings[--i])) {
327 rc = fchmodat(workdirfd, src, 0755, 0);
329 ERROR("can't make executable the file %s", src);
337 static int check_one_content(const char *src, const char *type)
344 ERROR("a content src is missing");
348 /* TODO: when dealing with HTML and languages, the check should
349 * include i18n path search of widgets */
350 rc = fstatat(workdirfd, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
353 fhtdocs = openat(workdirfd, "htdocs", O_DIRECTORY|O_PATH);
355 rc = fstatat(fhtdocs, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
362 ERROR("can't get info on content %s: %m", src);
363 else if (!S_ISREG(s.st_mode)) {
364 ERROR("content %s isn't a regular file", src);
372 static int check_content(const struct wgt_desc *desc)
374 return for_all_content(desc, check_one_content);
377 static int check_widget(const struct wgt_desc *desc)
381 result = check_temporary_constraints(desc);
383 result = check_permissions(desc);
385 result = check_content(desc);
389 static int get_target_directory(char target[PATH_MAX], const char *root, const struct wgt_desc *desc)
393 rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
397 ERROR("path too long");
404 static int move_widget_to(const char *destdir, int force)
406 return move_workdir(destdir, 1, force);
409 static int install_icon(const struct wgt_desc *desc)
412 char target[PATH_MAX];
418 create_directory(FWK_ICON_DIR, 0755, 1);
419 rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
420 if (rc >= (int)sizeof link) {
421 ERROR("link too long in install_icon");
426 rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
427 if (rc >= (int)sizeof target) {
428 ERROR("target too long in install_icon");
434 rc = symlink(target, link);
436 ERROR("can't create link %s -> %s", link, target);
440 static int install_exec_flag(const struct wgt_desc *desc)
442 return for_all_content(desc, set_exec_flag);
445 static int install_file_properties(const struct wgt_desc *desc)
448 struct wgt_desc_feature *feat;
449 struct wgt_desc_param *param;
452 feat = desc->features;
454 if (!strcmp(feat->name, FWK_PREFIX"widget:file-properties")) {
455 param = feat->params;
457 if (!strcmp(param->value, "executable")) {
458 rc2 = fchmodat(workdirfd, param->name, 0755, 0);
460 ERROR("can't make executable the file %s: %m", param->name);
462 ERROR("unknown file property %s for %s", param->value, param->name);
476 static int install_security(const struct wgt_desc *desc)
478 char path[PATH_MAX], *head;
479 const char *icon, *perm;
481 unsigned int i, n, len, lic, lf;
484 rc = secmgr_init(desc->id);
488 rc = secmgr_path_public_read_only(workdir);
492 /* instal the files */
493 head = stpcpy(path, workdir);
494 assert(head < path + sizeof path);
495 len = (unsigned)((path + sizeof path) - head);
497 ERROR("root path too long in install_security");
498 errno = ENAMETOOLONG;
503 icon = desc->icons ? desc->icons->src : NULL;
504 lic = (unsigned)(icon ? strlen(icon) : 0);
508 f = file_of_index(i++);
509 lf = (unsigned)strlen(f->name);
511 ERROR("path too long in install_security");
512 errno = ENAMETOOLONG;
515 strcpy(head, f->name);
516 if (lf <= lic && icon && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
517 rc = secmgr_path_public_read_only(path);
519 rc = secmgr_path_read_only(path);
524 /* install the permissions */
525 perm = first_usable_permission();
527 rc = secmgr_permit(perm);
528 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
531 perm = next_usable_permission();
534 /* install default permissions */
535 n = (unsigned int)(sizeof default_permissions / sizeof *default_permissions);
536 for (i = 0 ; i < n ; i++) {
537 perm = default_permissions[i];
538 rc = secmgr_permit(perm);
539 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
544 rc = secmgr_install();
552 /* install the widget of the file */
553 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
555 struct wgt_info *ifo;
556 const struct wgt_desc *desc;
557 char installdir[PATH_MAX];
559 struct unitconf uconf;
561 NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
564 create_directory(root, 0755, 1);
565 if (make_workdir(root, "TMP", 0)) {
566 ERROR("failed to create a working directory");
570 if (zread(wgtfile, 0))
573 if (check_all_signatures(DEFAULT_ALLOW_NO_SIGNATURE))
576 ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
580 reset_requested_permissions();
581 desc = wgt_info_desc(ifo);
582 if (check_widget(desc))
585 if (get_target_directory(installdir, root, desc))
588 if (access(installdir, F_OK) == 0) {
590 ERROR("widget already installed");
594 if (uninstall_widget(desc->idaver, root))
598 if (move_widget_to(installdir, force))
601 if (install_icon(desc))
604 if (install_security(desc))
607 if (install_exec_flag(desc))
610 if (install_file_properties(desc))
613 uconf.installdir = installdir;
614 uconf.icondir = FWK_ICON_DIR;
615 uconf.port = get_port;
616 if (unit_install(ifo, &uconf))