wgtpkg-install: set exec flag for application/vnd.agl.native
[src/app-framework-main.git] / src / wgtpkg-install.c
index 1d79591..71a0ff6 100644 (file)
@@ -1,5 +1,7 @@
 /*
- Copyright 2015 IoT.bzh
+ Copyright 2015, 2016 IoT.bzh
+
+ author: José Bollo <jose.bollo@iot.bzh>
 
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  limitations under the License.
 */
 
-#define _BSD_SOURCE /* see readdir */
+#define _GNU_SOURCE
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <dirent.h>
-#include <unistd.h>
 #include <limits.h>
 #include <errno.h>
-#include <syslog.h>
-#include <getopt.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
 
 #include "verbose.h"
-#include "wgtpkg.h"
 #include "wgt.h"
 #include "wgt-info.h"
+#include "wgtpkg-files.h"
+#include "wgtpkg-workdir.h"
+#include "wgtpkg-zip.h"
+#include "wgtpkg-permissions.h"
+#include "wgtpkg-digsig.h"
+#include "wgtpkg-install.h"
+#include "secmgr-wrap.h"
+#include "utils-dir.h"
+
+static const char permission_required[] = "required";
+static const char permission_optional[] = "optional";
+static const char feature_required_permissions[] = FWK_PREFIX "required-permissions";
+static const char* exec_type_strings[] = {
+       "application/x-executable",
+       "application/vnd.agl.native"
+};
 
-static const char appname[] = "wgtpkg-install";
-static const char *root;
-static char **permissions = NULL;
-static int force;
-
-static void install(const char *wgtfile);
-static void add_permissions(const char *list);
-
-static void usage()
+static int check_defined(const void *data, const char *name)
 {
-       printf(
-               "usage: %s [-f] [-q] [-v] [-p list] rootdir wgtfile...\n"
-               "\n"
-               "   rootdir       the root directory for installing\n"
-               "   -p list       a list of comma separated permissions to allow\n"
-               "   -f            force overwriting\n"
-               "   -q            quiet\n"
-               "   -v            verbose\n"
-               "\n",
-               appname
-       );
+       if (data)
+               return 0;
+       ERROR("widget has no defined '%s' (temporary constraints)", name);
+       errno = EINVAL;
+       return -1;
 }
 
-static struct option options[] = {
-       { "permissions", required_argument, NULL, 'p' },
-       { "force",       no_argument,       NULL, 'f' },
-       { "help",        no_argument,       NULL, 'h' },
-       { "quiet",       no_argument,       NULL, 'q' },
-       { "verbose",     no_argument,       NULL, 'v' },
-       { NULL, 0, NULL, 0 }
-};
-
-/* install the widgets of the list */
-int main(int ac, char **av)
+static int check_valid_string(const char *value, const char *name)
 {
-       int i;
-       char *wpath;
-
-       openlog(appname, LOG_PERROR, LOG_AUTH);
-
-       xmlsec_init();
-
-       force = 0;
-       for (;;) {
-               i = getopt_long(ac, av, "hfqvp:", options, NULL);
-               if (i < 0)
-                       break;
-               switch (i) {
-               case 'f':
-                       force = 1;
-                       break;
-               case 'h':
-                       usage();
-                       return 0;
-               case 'q':
-                       if (verbosity)
-                               verbosity--;
-                       break;
-               case 'v':
-                       verbosity++;
-                       break;
-               case 'p':
-                       add_permissions(optarg);
-                       break;
-               case ':':
-                       syslog(LOG_ERR, "missing argument value");
-                       return 1;
-               default:
-                       syslog(LOG_ERR, "unrecognized option");
-                       return 1;
-               }
-       }
+       int pos;
+       char c;
 
-       ac -= optind;
-       if (ac < 2) {
-               syslog(LOG_ERR, "arguments are missing");
-               return 1;
+       if (check_defined(value, name))
+               return -1;
+       pos = 0;
+       c = value[pos];
+       if (c == 0) {
+               ERROR("empty string forbidden in '%s' (temporary constraints)", name);
+               errno = EINVAL;
+               return -1;                      
        }
-
-       /* canonic names for files */
-       av += optind;
-       for (i = 0 ; av[i] != NULL ; i++) {
-               wpath = realpath(av[i], NULL);
-               if (wpath == NULL) {
-                       syslog(LOG_ERR, "error while getting realpath of %dth widget: %s", i+1, av[i]);
-                       return 1;
+       do {
+               if (!isalnum(c) && !strchr(".-_", c)) {
+                       ERROR("forbidden char %c in '%s' -> '%s' (temporary constraints)", c, name, value);
+                       errno = EINVAL;
+                       return -1;                      
                }
-               av[i] = wpath;
-       }
-       root = *av++;
-
-       /* install widgets */
-       for ( ; *av ; av++)
-               install(*av);
+               c = value[++pos];
+       } while(c);
+       return 0;
+}
 
+static int check_temporary_constraints(const struct wgt_desc *desc)
+{
+       int 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 (result)
+               return result;
+       if (desc->icons->next) {
+               ERROR("widget has more than one icon defined (temporary constraints)");
+               errno = EINVAL;
+               result = -1;
+       }
        return 0;
 }
 
-static int has_permission(const char *name)
+static int set_required_permissions(struct wgt_desc_param *params, int required)
 {
-       char **p = permissions;
-       if (p) {
-               while(*p) {
-                       if (0 == strcmp(*p, name))
-                               return 1;
-                       p++;
+       int optional;
+
+       while (params) {
+               /* check the value */
+               if (!strcmp(params->value, permission_required))
+                       optional = !required;
+               else if (!strcmp(params->value, permission_optional))
+                       optional = 1;
+               else {
+                       ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
+                       errno = EPERM;
+                       return -1;
                }
+               /* set the permission */
+               if (request_permission(params->name)) {
+                       DEBUG("granted permission: %s", params->name);
+               } else if (optional) {
+                       INFO("optional permission ungranted: %s", params->name);
+               } else {
+                       ERROR("ungranted permission required: %s", params->name);
+                       errno = EPERM;
+                       return -1;
+               }
+               params = params->next;
        }
        return 0;
 }
 
-static void add_permissions(const char *list)
+static int check_widget(const struct wgt_desc *desc)
 {
-       char **ps, *p;
-       const char *iter;
-       int n, on;
-       static const char separators[] = " \t\n\r,";
-
-       n = 0;
-       iter = list + strspn(list, separators);
-       while(*iter) {
-               n++;
-               iter += strcspn(iter, separators);
-               iter += strspn(iter, separators);
-       }
-       if (n == 0)
-               return;
-
-       on = 0;
-       ps = permissions;
-       if (ps)
-               while(*ps++)
-                       on++;
-
-       ps = realloc(permissions, (1 + on + n) * sizeof * ps);
-       if (!ps) {
-               syslog(LOG_ERR, "Can't allocate memory for permissions");
-               exit(1);
+       int result;
+       const struct wgt_desc_feature *feature;
+
+       result = check_temporary_constraints(desc);
+       feature = desc->features;
+       while(result >= 0 && feature) {
+               if (!strcmp(feature->name, feature_required_permissions))
+                       result = set_required_permissions(feature->params, feature->required);
+               feature = feature->next;
        }
+       return result;
+}
 
-       permissions = ps;
-       ps[on] = NULL;
+static int move_widget(const char *root, const struct wgt_desc *desc, int force)
+{
+       char newdir[PATH_MAX];
+       int rc;
 
-       iter = list + strspn(list, separators);
-       while(*iter) {
-               n = strcspn(iter, separators);
-               p = strndup(iter, n);
-               if (!p) {
-                       syslog(LOG_ERR, "Can't allocate permission");
-                       exit(1);
-               }
-               if (has_permission(p))
-                       free(p);
-               else {
-                       ps[on] = p;
-                       ps[++on] = NULL;
-               }
-               iter += n;
-               iter += strspn(iter, separators);
+       rc = snprintf(newdir, sizeof newdir, "%s/%s/%s", root, desc->id, desc->ver);
+       if (rc >= (int)sizeof newdir) {
+               ERROR("path too long in move_widget");
+               errno = EINVAL;
+               return -1;
        }
+
+       return move_workdir(newdir, 1, force);
 }
 
-static struct wgt *wgt_at_workdir()
+static int install_icon(const struct wgt_desc *desc)
 {
-       int rc, wfd;
-       struct wgt *wgt;
-
-       wfd = workdirfd();
-       if (wfd < 0)
-               return NULL;
-
-       wgt = wgt_create();
-       if (!wgt) {
-               syslog(LOG_ERR, "failed to allocate wgt");
-               close(wfd);
-               return NULL;
+       char link[PATH_MAX];
+       char target[PATH_MAX];
+       int rc;
+
+       create_directory(FWK_ICON_DIR, 0755, 1);
+       rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
+       if (rc >= (int)sizeof link) {
+               ERROR("link too long in install_icon");
+               errno = EINVAL;
+               return -1;
        }
 
-       rc = wgt_connectat(wgt, wfd, NULL);
-       if (rc) {
-               syslog(LOG_ERR, "failed to connect wgt to workdir");
-               close(wfd);
-               wgt_unref(wgt);
-               return NULL;
+       rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
+       if (rc >= (int)sizeof target) {
+               ERROR("target too long in install_icon");
+               errno = EINVAL;
+               return -1;
        }
 
-       return wgt;
+       unlink(link);
+       rc = symlink(target, link);
+       if (rc)
+               ERROR("can't create link %s -> %s", link, target);
+       return rc;
 }
 
+static int install_exec_flag(const struct wgt_desc *desc)
+{
+       int i;
 
-static int check_and_place()
+       if (desc->content_type) {
+               i = sizeof exec_type_strings / sizeof *exec_type_strings;
+               while (i) {
+                       if (!strcasecmp(desc->content_type, exec_type_strings[--i]))
+                               return fchmodat(workdirfd, desc->content_src, 0755, 0);
+               }
+       }
+       return 0;
+}
+
+static int install_security(const struct wgt_desc *desc)
 {
-       struct wgt *wgt;
-       struct wgt_info *ifo;
+       char path[PATH_MAX], *head;
+       const char *icon, *perm;
+       int rc;
+       unsigned int i, n, len, lic, lf;
+       struct filedesc *f;
+
+       rc = secmgr_init(desc->id);
+       if (rc)
+               goto error;
+
+       rc = secmgr_path_public_read_only(workdir);
+       if (rc)
+               goto error2;
 
-       wgt = wgt_at_workdir();
-       if (!wgt)
-               return -1;
+       /* instal the files */
+       head = stpcpy(path, workdir);
+       assert(head < path + sizeof path);
+       len = (unsigned)((path + sizeof path) - head);
+       if (!len) {
+               ERROR("root path too long in install_security");
+               errno = ENAMETOOLONG;
+               goto error2;
+       }
+       len--;
+       *head++ = '/';
+       icon = desc->icons->src;
+       lic = (unsigned)strlen(icon);
+       n = file_count();
+       i = 0;
+       while(i < n) {
+               f = file_of_index(i++);
+               lf = (unsigned)strlen(f->name);
+               if (lf >= len) {
+                       ERROR("path too long in install_security");
+                       errno = ENAMETOOLONG;
+                       goto error2;
+               }
+               strcpy(head, f->name);
+               if (lf <= lic && !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);
+               if (rc)
+                       goto error2;
+       }
 
-       ifo = wgt_info_get(wgt, 1, 1, 1);
-       if (!ifo) {
-               wgt_unref(wgt);
-               return -1;
+       /* install the permissions */
+       perm = first_usable_permission();
+       while(perm) {
+               rc = secmgr_permit(perm);
+               if (rc)
+                       goto error2;
+               perm = next_usable_permission();
        }
-       wgt_info_dump(ifo, 1, "");
-       wgt_info_unref(ifo);
-       wgt_unref(wgt);
-       return 0;
+
+       rc = secmgr_install();
+       return rc;
+error2:
+       secmgr_cancel();
+error:
+       return -1;
 }
 
 /* install the widget of the file */
-static void install(const char *wgtfile)
+struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
 {
-       notice("-- INSTALLING widget %s --", wgtfile);
+       struct wgt_info *ifo;
+       const struct wgt_desc *desc;
+
+       NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
 
        /* workdir */
-       if (make_workdir_base(root, "UNPACK", 0)) {
-               syslog(LOG_ERR, "failed to create a working directory");
+       create_directory(root, 0755, 1);
+       if (make_workdir(root, "TMP", 0)) {
+               ERROR("failed to create a working directory");
                goto error1;
        }
 
-       if (enter_workdir(0))
-               goto error2;
-
        if (zread(wgtfile, 0))
                goto error2;
 
        if (check_all_signatures())
                goto error2;
 
-       if (check_and_place())
+       ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
+       if (!ifo)
                goto error2;
-       
-       return;
+
+       reset_requested_permissions();
+       desc = wgt_info_desc(ifo);
+       if (check_widget(desc))
+               goto error3;
+
+       if (move_widget(root, desc, force))
+               goto error3;
+
+       if (install_icon(desc))
+               goto error3;
+
+       if (install_exec_flag(desc))
+               goto error3;
+
+       if (install_security(desc))
+               goto error3;
+
+       file_reset();
+       return ifo;
+
+error3:
+       wgt_info_unref(ifo);
 
 error2:
        remove_workdir();
 
 error1:
-       return;
+       file_reset();
+       return NULL;
 }
 
-