wgtpkg-install: Add default permissions
[src/app-framework-main.git] / src / wgtpkg-install.c
index cec3b75..0122eda 100644 (file)
@@ -1,5 +1,5 @@
 /*
- Copyright 2015, 2016, 2017 IoT.bzh
+ Copyright (C) 2015-2019 IoT.bzh
 
  author: José Bollo <jose.bollo@iot.bzh>
 
@@ -40,6 +40,7 @@
 #include "wgtpkg-permissions.h"
 #include "wgtpkg-digsig.h"
 #include "wgtpkg-install.h"
+#include "wgtpkg-uninstall.h"
 #include "secmgr-wrap.h"
 #include "utils-dir.h"
 #include "wgtpkg-unit.h"
@@ -54,6 +55,22 @@ static const char* exec_type_strings[] = {
 static const char key_afm_prefix[] = "X-AFM-";
 static const char key_http_port[] = "http-port";
 
+#define HTTP_PORT_MIN                  31000
+#define HTTP_PORT_MAX                  32759
+#define HTTP_PORT_IS_VALID(port)       (HTTP_PORT_MIN <= (port) && (port) <= HTTP_PORT_MAX)
+#define HTTP_PORT_COUNT                        (HTTP_PORT_MAX - HTTP_PORT_MIN + 1)
+#define HTTP_PORT_ACNT                 ((HTTP_PORT_COUNT + 31) >> 5)
+#define HTTP_PORT_ASFT(port)           (((port) - HTTP_PORT_MIN) & 31)
+#define HTTP_PORT_AIDX(port)           (((port) - HTTP_PORT_MIN) >> 5)
+#define HTTP_PORT_TEST(array,port)     ((((array)[HTTP_PORT_AIDX(port)]) >> HTTP_PORT_ASFT(port)) & 1)
+#define HTTP_PORT_SET(array,port)      (((array)[HTTP_PORT_AIDX(port)]) |= (((uint32_t)1) << HTTP_PORT_ASFT(port)))
+
+static uint32_t *port_bits = NULL;
+
+static const char *default_permissions[] = {
+       "urn:AGL:token:valid"
+};
+
 /*
  * normalize unit files: remove comments, remove heading blanks,
  * make single lines
@@ -114,8 +131,8 @@ static int get_port_cb(void *closure, const char *name, const char *path, int is
                        if (*iter == '=') {
                                while(*++iter == ' ');
                                p = atoi(iter);
-                               if (p >= 0 && p < 32768)
-                                       ((uint32_t*)closure)[p >> 5] |= (uint32_t)1 << (p & 31);
+                               if (HTTP_PORT_IS_VALID(p))
+                                       HTTP_PORT_SET((uint32_t*)closure, p);
                        }
                }
                iter = strstr(iter, key_afm_prefix);
@@ -124,29 +141,55 @@ static int get_port_cb(void *closure, const char *name, const char *path, int is
        return 0;
 }
 
-static int get_port()
+static int update_portbits(uint32_t *portbits)
 {
        int rc;
-       uint32_t ports[1024]; /* 1024 * 32 = 32768 */
-
-       memset(ports, 0, sizeof ports);
-       rc = systemd_unit_list(0, get_port_cb, &ports);
-       if (rc >= 0) {
-               rc = systemd_unit_list(1, get_port_cb, ports);
-               if (rc >= 0) {
-                       for (rc = 1024 ; rc < 32768 && !~ports[rc >> 5] ; rc += 32);
-                       if (rc == 32768) {
-                               ERROR("Can't compute a valid port");
-                               errno = EADDRNOTAVAIL;
-                               rc = -1;
-                       } else {
-                               while (1 & (ports[rc >> 5] >> (rc & 31))) rc++;
-                       }
-               }
-       }
+
+       memset(portbits, 0, HTTP_PORT_ACNT * sizeof(uint32_t));
+       rc = systemd_unit_list(0, get_port_cb, portbits);
+       if (rc >= 0)
+               rc = systemd_unit_list(1, get_port_cb, portbits);
+       if (rc < 0)
+               ERROR("troubles while updating ports");
        return rc;
 }
 
+static int first_free_port(uint32_t *portbits)
+{
+       int port;
+
+       port = HTTP_PORT_MIN;
+       while (port <= HTTP_PORT_MAX && !~portbits[HTTP_PORT_AIDX(port)])
+               port += 32;
+       while (port <= HTTP_PORT_MAX && HTTP_PORT_TEST(portbits, port))
+               port++;
+       if (port > HTTP_PORT_MAX) {
+               ERROR("Can't compute a valid port");
+               errno = EADDRNOTAVAIL;
+               port = -1;
+       }
+       return port;
+}
+
+static int get_port()
+{
+       int port;
+
+       /* ensure existing port bitmap */
+       if (port_bits == NULL) {
+               port_bits = malloc(HTTP_PORT_ACNT * sizeof(uint32_t));
+               if (port_bits == NULL || update_portbits(port_bits) < 0)
+                       return -1;
+       }
+
+       /* allocates the port */
+       port = first_free_port(port_bits);
+       if (port >= 0)
+               HTTP_PORT_SET(port_bits, port);
+
+       return port;
+}
+
 static int check_defined(const void *data, const char *name)
 {
        if (data)
@@ -168,13 +211,13 @@ static int check_valid_string(const char *value, const char *name)
        if (c == 0) {
                ERROR("empty string forbidden in '%s' (temporary constraints)", name);
                errno = EINVAL;
-               return -1;                      
+               return -1;
        }
        do {
                if (!isalnum(c) && !strchr(".-_", c)) {
                        ERROR("forbidden char %c in '%s' -> '%s' (temporary constraints)", c, name, value);
                        errno = EINVAL;
-                       return -1;                      
+                       return -1;
                }
                c = value[++pos];
        } while(c);
@@ -188,12 +231,13 @@ static int check_temporary_constraints(const struct wgt_desc *desc)
        result  = check_valid_string(desc->id, "id");
        result |= check_valid_string(desc->version, "version");
        result |= check_valid_string(desc->ver, "ver");
-       result |= check_defined(desc->icons, "icon");
        result |= check_defined(desc->content_src, "content");
+       if (desc->icons)
+               result |= check_defined(desc->icons->src, "icon.src");
        if (result)
                return result;
 
-       if (desc->icons->next) {
+       if (desc->icons && desc->icons->next) {
                ERROR("widget has more than one icon defined (temporary constraints)");
                errno = EINVAL;
                result = -1;
@@ -260,7 +304,7 @@ static int for_all_content(const struct wgt_desc *desc, int (*action)(const char
        rc = action(desc->content_src, desc->content_type);
        feat = desc->features;
        while (feat) {
-               if (!strcmp(feat->name, "urn:AGL:widget:provided-unit")) {
+               if (!strcmp(feat->name, FWK_PREFIX"widget:provided-unit")) {
                        src = wgt_info_param(feat, "content.src");
                        type = wgt_info_param(feat, "content.type");
                        rc2 = action(src, type);
@@ -294,6 +338,7 @@ static int check_one_content(const char *src, const char *type)
 {
        int rc;
        struct stat s;
+       int fhtdocs, serr;
 
        if (!src) {
                ERROR("a content src is missing");
@@ -303,6 +348,16 @@ static int check_one_content(const char *src, const char *type)
                /* TODO: when dealing with HTML and languages, the check should
                 * include i18n path search of widgets */
                rc = fstatat(workdirfd, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
+               if (rc < 0) {
+                       serr = errno;
+                       fhtdocs = openat(workdirfd, "htdocs", O_DIRECTORY|O_PATH);
+                       if (fhtdocs >= 0) {
+                               rc = fstatat(fhtdocs, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
+                               serr = errno;
+                               close(fhtdocs);
+                       }
+                       errno = serr;
+               }
                if (rc < 0)
                        ERROR("can't get info on content %s: %m", src);
                else if (!S_ISREG(s.st_mode)) {
@@ -357,6 +412,9 @@ static int install_icon(const struct wgt_desc *desc)
        char target[PATH_MAX];
        int rc;
 
+       if (!desc->icons)
+               return 0;
+
        create_directory(FWK_ICON_DIR, 0755, 1);
        rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
        if (rc >= (int)sizeof link) {
@@ -384,6 +442,37 @@ static int install_exec_flag(const struct wgt_desc *desc)
        return for_all_content(desc, set_exec_flag);
 }
 
+static int install_file_properties(const struct wgt_desc *desc)
+{
+       int rc, rc2;
+       struct wgt_desc_feature *feat;
+       struct wgt_desc_param *param;
+
+       rc = 0;
+       feat = desc->features;
+       while (feat) {
+               if (!strcmp(feat->name, FWK_PREFIX"widget:file-properties")) {
+                       param = feat->params;
+                       while (param) {
+                               if (!strcmp(param->value, "executable")) {
+                                       rc2 = fchmodat(workdirfd, param->name, 0755, 0);
+                                       if (rc2 < 0)
+                                               ERROR("can't make executable the file %s: %m", param->name);
+                               } else {
+                                       ERROR("unknown file property %s for %s", param->value, param->name);
+                                       errno = EINVAL;
+                                       rc2 = -1;
+                               }
+                               if (rc2 < 0 && !rc)
+                                       rc = rc2;
+                               param = param->next;
+                       }
+               }
+               feat = feat->next;
+       }
+       return rc;
+}
+
 static int install_security(const struct wgt_desc *desc)
 {
        char path[PATH_MAX], *head;
@@ -411,8 +500,8 @@ static int install_security(const struct wgt_desc *desc)
        }
        len--;
        *head++ = '/';
-       icon = desc->icons->src;
-       lic = (unsigned)strlen(icon);
+       icon = desc->icons ? desc->icons->src : NULL;
+       lic = (unsigned)(icon ? strlen(icon) : 0);
        n = file_count();
        i = 0;
        while(i < n) {
@@ -424,7 +513,7 @@ static int install_security(const struct wgt_desc *desc)
                        goto error2;
                }
                strcpy(head, f->name);
-               if (lf <= lic && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
+               if (lf <= lic && icon && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
                        rc = secmgr_path_public_read_only(path);
                else
                        rc = secmgr_path_read_only(path);
@@ -442,6 +531,16 @@ static int install_security(const struct wgt_desc *desc)
                perm = next_usable_permission();
        }
 
+       /* install default permissions */
+       n = (unsigned int)(sizeof default_permissions / sizeof *default_permissions);
+       for (i = 0 ; i < n ; i++) {
+               perm = default_permissions[i];
+               rc = secmgr_permit(perm);
+               INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
+               if (rc)
+                       goto error2;
+       }
+
        rc = secmgr_install();
        return rc;
 error2:
@@ -456,7 +555,7 @@ struct wgt_info *install_widget(const char *wgtfile, const char *root, int force
        struct wgt_info *ifo;
        const struct wgt_desc *desc;
        char installdir[PATH_MAX];
-       int port;
+       int err;
        struct unitconf uconf;
 
        NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
@@ -471,7 +570,7 @@ struct wgt_info *install_widget(const char *wgtfile, const char *root, int force
        if (zread(wgtfile, 0))
                goto error2;
 
-       if (check_all_signatures())
+       if (check_all_signatures(DEFAULT_ALLOW_NO_SIGNATURE))
                goto error2;
 
        ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
@@ -486,6 +585,16 @@ struct wgt_info *install_widget(const char *wgtfile, const char *root, int force
        if (get_target_directory(installdir, root, desc))
                goto error3;
 
+       if (access(installdir, F_OK) == 0) {
+               if (!force) {
+                       ERROR("widget already installed");
+                       errno = EEXIST;
+                       goto error3;
+               }
+               if (uninstall_widget(desc->idaver, root))
+                       goto error3;
+       }
+
        if (move_widget_to(installdir, force))
                goto error3;
 
@@ -498,13 +607,12 @@ struct wgt_info *install_widget(const char *wgtfile, const char *root, int force
        if (install_exec_flag(desc))
                goto error4;
 
-       port = get_port();
-       if (port < 0)
+       if (install_file_properties(desc))
                goto error4;
 
        uconf.installdir = installdir;
        uconf.icondir = FWK_ICON_DIR;
-       uconf.port = port;
+       uconf.port = get_port;
        if (unit_install(ifo, &uconf))
                goto error4;
 
@@ -518,7 +626,9 @@ error3:
        wgt_info_unref(ifo);
 
 error2:
+       err = errno;
        remove_workdir();
+       errno = err;
 
 error1:
        file_reset();