/*
- Copyright 2015, 2016, 2017 IoT.bzh
+ Copyright (C) 2015-2020 IoT.bzh
author: José Bollo <jose.bollo@iot.bzh>
#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"
};
static const char key_afm_prefix[] = "X-AFM-";
-static const char key_http_port[] = "http-port";
+static const char key_afid[] = "ID";
+
+#define HTTP_PORT_BASE 30000
+
+#define AFID_MIN 1
+#define AFID_MAX 1999
+#define AFID_IS_VALID(afid) (AFID_MIN <= (afid) && (afid) <= AFID_MAX)
+#define AFID_COUNT (AFID_MAX - AFID_MIN + 1)
+#define AFID_ACNT ((AFID_COUNT + 31) >> 5)
+#define AFID_ASFT(afid) (((afid) - AFID_MIN) & 31)
+#define AFID_AIDX(afid) (((afid) - AFID_MIN) >> 5)
+#define AFID_TEST(array,afid) ((((array)[AFID_AIDX(afid)]) >> AFID_ASFT(afid)) & 1)
+#define AFID_SET(array,afid) (((array)[AFID_AIDX(afid)]) |= (((uint32_t)1) << AFID_ASFT(afid)))
+
+static uint32_t *afids_array = NULL;
+
+static const char *default_permissions[] = {
+ "urn:AGL:token:valid"
+};
/*
* normalize unit files: remove comments, remove heading blanks,
*write = c;
}
-static int get_port_cb(void *closure, const char *name, const char *path, int isuser)
+static int get_afid_cb(void *closure, const char *name, const char *path, int isuser)
{
char *iter;
char *content;
iter += sizeof key_afm_prefix - 1;
if (*iter == '-')
iter++;
- if (!strncmp(iter, key_http_port, sizeof key_http_port - 1)) {
- iter += sizeof key_http_port - 1;
+ if (!strncmp(iter, key_afid, sizeof key_afid - 1)) {
+ iter += sizeof key_afid - 1;
while(*iter && *iter != '=' && *iter != '\n')
iter++;
if (*iter == '=') {
while(*++iter == ' ');
p = atoi(iter);
- if (p >= 0 && p < 32768)
- ((uint32_t*)closure)[p >> 5] |= (uint32_t)1 << (p & 31);
+ if (AFID_IS_VALID(p))
+ AFID_SET((uint32_t*)closure, p);
}
}
iter = strstr(iter, key_afm_prefix);
return 0;
}
-static int get_port()
+static int update_afids(uint32_t *afids)
{
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(afids, 0, AFID_ACNT * sizeof(uint32_t));
+ rc = systemd_unit_list(0, get_afid_cb, afids);
+ if (rc >= 0)
+ rc = systemd_unit_list(1, get_afid_cb, afids);
+ if (rc < 0)
+ ERROR("troubles while updating afids");
return rc;
}
+static int first_free_afid(uint32_t *afids)
+{
+ int afid;
+
+ afid = AFID_MIN;
+ while (afid <= AFID_MAX && !~afids[AFID_AIDX(afid)])
+ afid += 32;
+ while (afid <= AFID_MAX && AFID_TEST(afids, afid))
+ afid++;
+ if (afid > AFID_MAX) {
+ ERROR("Can't compute a valid afid");
+ errno = EADDRNOTAVAIL;
+ afid = -1;
+ }
+ return afid;
+}
+
+static int get_new_afid()
+{
+ int afid;
+
+ /* ensure existing afid bitmap */
+ if (afids_array == NULL) {
+ afids_array = malloc(AFID_ACNT * sizeof(uint32_t));
+ if (afids_array == NULL || update_afids(afids_array) < 0)
+ return -1;
+ }
+
+ /* allocates the afid */
+ afid = first_free_afid(afids_array);
+ if (afid < 0 && errno == EADDRNOTAVAIL) {
+ /* no more ids, try to rescan */
+ memset(afids_array, 0, AFID_ACNT * sizeof(uint32_t));
+ if (update_afids(afids_array) >= 0)
+ afid = first_free_afid(afids_array);
+ }
+ if (afid >= 0)
+ AFID_SET(afids_array, afid);
+
+ return afid;
+}
+
static int check_defined(const void *data, const char *name)
{
if (data)
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);
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);
{
int rc;
struct stat s;
+ int fhtdocs, serr;
if (!src) {
ERROR("a content src is missing");
/* 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)) {
{
int rc;
+#if DISTINCT_VERSIONS
rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
+#else
+ rc = snprintf(target, PATH_MAX, "%s/%s", root, desc->id);
+#endif
if (rc < PATH_MAX)
rc = 0;
else {
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 is_path_public(const char *path, const struct wgt_desc *desc)
+{
+ const struct wgt_desc_icon *icon;
+ const struct wgt_desc_feature *feat;
+ const struct wgt_desc_param *param;
+ size_t len;
+
+ /* icons are public */
+ icon = desc->icons;
+ while (icon != NULL) {
+ len = strlen(icon->src);
+ if (!memcmp(path, icon->src, len) && (path[len] == 0 || path[len] == '/'))
+ return 1;
+ icon = icon->next;
+ }
+
+ /* provided bindings are public */
+ feat = desc->features;
+ while (feat != NULL) {
+ if (strcasecmp(feat->name, "urn:AGL:widget:provided-binding") == 0
+ || strcasecmp(feat->name, "urn:AGL:widget:public-files") == 0) {
+ param = feat->params;
+ while(param != NULL) {
+ if (strcmp(param->value, path) == 0)
+ return 1;
+ param = param->next;
+ }
+ }
+ feat = feat->next;
+ }
+
+ /* otherwise no */
+ return 0;
+}
+
static int install_security(const struct wgt_desc *desc)
{
char path[PATH_MAX], *head;
- const char *icon, *perm;
- int rc;
- unsigned int i, n, len, lic, lf;
+ const char *perm;
+ int rc, public;
+ unsigned int i, n, len, lf, j;
struct filedesc *f;
-
+ struct pathent {
+ struct pathent *next;
+ unsigned int len;
+ int public;
+ char name[];
+ } *pe0, *pe2, *ppe;
+
+ pe0 = NULL;
rc = secmgr_init(desc->id);
if (rc)
goto error;
- rc = secmgr_path_public_read_only(workdir);
- if (rc)
- goto error2;
-
/* instal the files */
head = stpcpy(path, workdir);
assert(head < path + sizeof path);
}
len--;
*head++ = '/';
- icon = desc->icons ? desc->icons->src : NULL;
- lic = (unsigned)strlen(icon);
+
+ /* build root entry */
+ pe0 = malloc(1 + sizeof *pe0);
+ if (pe0 == NULL)
+ goto error2;
+ pe0->next = NULL;
+ pe0->len = 0;
+ pe0->public = 0;
+ pe0->name[0] = 0;
+
+ /* build list of entries */
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;
+ for (i = 0 ; i < n ; i++) {
+ f = file_of_index(i);
+ public = is_path_public(f->name, desc);
+ pe0->public |= public;
+ lf = j = 0;
+ while(f->name[j] == '/')
+ j++;
+ while (f->name[j] != 0) {
+ /* copy next entry of the path */
+ while(f->name[j] && f->name[j] != '/') {
+ if (lf + 1 >= len) {
+ ERROR("path too long in install_security");
+ errno = ENAMETOOLONG;
+ goto error2;
+ }
+ head[lf++] = f->name[j++];
+ }
+ head[lf] = 0;
+
+ /* search if it already exists */
+ ppe = pe0;
+ pe2 = pe0->next;
+ while (pe2 != NULL && pe2->len < lf) {
+ ppe = pe2;
+ pe2 = pe2->next;
+ }
+ while (pe2 != NULL && pe2->len == lf && strcmp(head, pe2->name)) {
+ ppe = pe2;
+ pe2 = pe2->next;
+ }
+
+ if (pe2 != NULL && pe2->len == lf)
+ /* existing, update public status */
+ pe2->public |= public;
+ else {
+ /* not existing, create it */
+ pe2 = malloc(lf + 1 + sizeof *pe2);
+ if (pe2 == NULL)
+ goto error2;
+ pe2->next = ppe->next;
+ pe2->len = lf;
+ pe2->public = public;
+ memcpy(pe2->name, head, 1 + lf);
+ ppe->next = pe2;
+ }
+
+ /* prepare next path entry */
+ head[lf++] = '/';
+ while(f->name[j] == '/')
+ j++;
}
- strcpy(head, f->name);
- if (lf <= lic && icon && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
+ }
+
+ /* set the path entries */
+ for (pe2 = pe0 ; pe2 != NULL ; pe2 = pe2->next) {
+ strcpy(head, pe2->name);
+ if (pe2->public)
rc = secmgr_path_public_read_only(path);
else
- rc = secmgr_path_read_only(path);
+ rc = secmgr_path_private(path);
if (rc)
goto error2;
}
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;
+ goto end;
error2:
secmgr_cancel();
error:
- return -1;
+ rc = -1;
+end:
+ /* free memory of path entries */
+ while (pe0 != NULL) {
+ ppe = pe0;
+ pe0 = pe0->next;
+ free(ppe);
+ }
+ return rc;
}
/* install the widget of the file */
struct wgt_info *ifo;
const struct wgt_desc *desc;
char installdir[PATH_MAX];
- int port;
+ int err, rc;
struct unitconf uconf;
NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
if (zread(wgtfile, 0))
goto error2;
- if (check_all_signatures())
+#if defined(ALLOW_NO_SIGNATURE)
+ rc = check_all_signatures(1);
+#else
+ rc = check_all_signatures(0);
+#endif
+ if (rc)
goto error2;
ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
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;
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.new_afid = get_new_afid;
+ uconf.base_http_ports = HTTP_PORT_BASE;
if (unit_install(ifo, &uconf))
goto error4;
wgt_info_unref(ifo);
error2:
+ err = errno;
remove_workdir();
+ errno = err;
error1:
file_reset();