2 Copyright (C) 2015-2020 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_afid[] = "ID";
58 #define HTTP_PORT_BASE 30000
62 #define AFID_IS_VALID(afid) (AFID_MIN <= (afid) && (afid) <= AFID_MAX)
63 #define AFID_COUNT (AFID_MAX - AFID_MIN + 1)
64 #define AFID_ACNT ((AFID_COUNT + 31) >> 5)
65 #define AFID_ASFT(afid) (((afid) - AFID_MIN) & 31)
66 #define AFID_AIDX(afid) (((afid) - AFID_MIN) >> 5)
67 #define AFID_TEST(array,afid) ((((array)[AFID_AIDX(afid)]) >> AFID_ASFT(afid)) & 1)
68 #define AFID_SET(array,afid) (((array)[AFID_AIDX(afid)]) |= (((uint32_t)1) << AFID_ASFT(afid)))
70 static uint32_t *afids_array = NULL;
72 static const char *default_permissions[] = {
77 * normalize unit files: remove comments, remove heading blanks,
80 static void normalize_unit_file(char *content)
82 char *read, *write, c;
84 read = write = content;
95 do { c = *read++; } while(c && c != '\n');
99 do { *write++ = c = *read++; } while(c && c != '\n');
100 if (write - content >= 2 && write[-2] == '\\')
108 static int get_afid_cb(void *closure, const char *name, const char *path, int isuser)
116 rc = getfile(path, &content, &length);
120 /* normalize the unit file */
121 normalize_unit_file(content);
123 /* process the file */
124 iter = strstr(content, key_afm_prefix);
126 iter += sizeof key_afm_prefix - 1;
129 if (!strncmp(iter, key_afid, sizeof key_afid - 1)) {
130 iter += sizeof key_afid - 1;
131 while(*iter && *iter != '=' && *iter != '\n')
134 while(*++iter == ' ');
136 if (AFID_IS_VALID(p))
137 AFID_SET((uint32_t*)closure, p);
140 iter = strstr(iter, key_afm_prefix);
146 static int update_afids(uint32_t *afids)
150 memset(afids, 0, AFID_ACNT * sizeof(uint32_t));
151 rc = systemd_unit_list(0, get_afid_cb, afids);
153 rc = systemd_unit_list(1, get_afid_cb, afids);
155 ERROR("troubles while updating afids");
159 static int first_free_afid(uint32_t *afids)
164 while (afid <= AFID_MAX && !~afids[AFID_AIDX(afid)])
166 while (afid <= AFID_MAX && AFID_TEST(afids, afid))
168 if (afid > AFID_MAX) {
169 ERROR("Can't compute a valid afid");
170 errno = EADDRNOTAVAIL;
176 static int get_new_afid()
180 /* ensure existing afid bitmap */
181 if (afids_array == NULL) {
182 afids_array = malloc(AFID_ACNT * sizeof(uint32_t));
183 if (afids_array == NULL || update_afids(afids_array) < 0)
187 /* allocates the afid */
188 afid = first_free_afid(afids_array);
189 if (afid < 0 && errno == EADDRNOTAVAIL) {
190 /* no more ids, try to rescan */
191 memset(afids_array, 0, AFID_ACNT * sizeof(uint32_t));
192 if (update_afids(afids_array) >= 0)
193 afid = first_free_afid(afids_array);
196 AFID_SET(afids_array, afid);
201 static int check_defined(const void *data, const char *name)
205 ERROR("widget has no defined '%s' (temporary constraints)", name);
210 static int check_valid_string(const char *value, const char *name)
215 if (check_defined(value, name))
220 ERROR("empty string forbidden in '%s' (temporary constraints)", name);
225 if (!isalnum(c) && !strchr(".-_", c)) {
226 ERROR("forbidden char %c in '%s' -> '%s' (temporary constraints)", c, name, value);
235 static int check_temporary_constraints(const struct wgt_desc *desc)
239 result = check_valid_string(desc->id, "id");
240 result |= check_valid_string(desc->version, "version");
241 result |= check_valid_string(desc->ver, "ver");
242 result |= check_defined(desc->content_src, "content");
244 result |= check_defined(desc->icons->src, "icon.src");
248 if (desc->icons && desc->icons->next) {
249 ERROR("widget has more than one icon defined (temporary constraints)");
256 static int set_required_permissions(struct wgt_desc_param *params, int required)
261 /* check if target */
262 if (!strcmp(params->name, string_sharp_target)) {
263 /* do nothing when #target */
265 /* check the value */
266 if (!strcmp(params->value, string_required))
267 optional = !required;
268 else if (!strcmp(params->value, string_optional))
271 ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
275 /* set the permission */
276 if (request_permission(params->name)) {
277 DEBUG("granted permission: %s", params->name);
278 } else if (optional) {
279 INFO("optional permission ungranted: %s", params->name);
281 ERROR("ungranted permission required: %s", params->name);
286 params = params->next;
291 static int check_permissions(const struct wgt_desc *desc)
294 const struct wgt_desc_feature *feature;
297 feature = desc->features;
298 while(result >= 0 && feature) {
299 if (!strcmp(feature->name, feature_required_permission))
300 result = set_required_permissions(feature->params, feature->required);
301 feature = feature->next;
306 static int for_all_content(const struct wgt_desc *desc, int (*action)(const char *src, const char *type))
309 struct wgt_desc_feature *feat;
310 const char *src, *type;
312 rc = action(desc->content_src, desc->content_type);
313 feat = desc->features;
315 if (!strcmp(feat->name, FWK_PREFIX"widget:provided-unit")) {
316 src = wgt_info_param(feat, "content.src");
317 type = wgt_info_param(feat, "content.type");
318 rc2 = action(src, type);
319 if (rc >= 0 && rc2 < 0)
327 static int set_exec_flag(const char *src, const char *type)
332 i = sizeof exec_type_strings / sizeof *exec_type_strings;
334 if (!strcasecmp(type, exec_type_strings[--i])) {
335 rc = fchmodat(workdirfd, src, 0755, 0);
337 ERROR("can't make executable the file %s", src);
345 static int check_one_content(const char *src, const char *type)
352 ERROR("a content src is missing");
356 /* TODO: when dealing with HTML and languages, the check should
357 * include i18n path search of widgets */
358 rc = fstatat(workdirfd, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
361 fhtdocs = openat(workdirfd, "htdocs", O_DIRECTORY|O_PATH);
363 rc = fstatat(fhtdocs, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
370 ERROR("can't get info on content %s: %m", src);
371 else if (!S_ISREG(s.st_mode)) {
372 ERROR("content %s isn't a regular file", src);
380 static int check_content(const struct wgt_desc *desc)
382 return for_all_content(desc, check_one_content);
385 static int check_widget(const struct wgt_desc *desc)
389 result = check_temporary_constraints(desc);
391 result = check_permissions(desc);
393 result = check_content(desc);
397 static int get_target_directory(char target[PATH_MAX], const char *root, const struct wgt_desc *desc)
401 #if DISTINCT_VERSIONS
402 rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
404 rc = snprintf(target, PATH_MAX, "%s/%s", root, desc->id);
409 ERROR("path too long");
416 static int move_widget_to(const char *destdir, int force)
418 return move_workdir(destdir, 1, force);
421 static int install_icon(const struct wgt_desc *desc)
424 char target[PATH_MAX];
430 create_directory(FWK_ICON_DIR, 0755, 1);
431 rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
432 if (rc >= (int)sizeof link) {
433 ERROR("link too long in install_icon");
438 rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
439 if (rc >= (int)sizeof target) {
440 ERROR("target too long in install_icon");
446 rc = symlink(target, link);
448 ERROR("can't create link %s -> %s", link, target);
452 static int install_exec_flag(const struct wgt_desc *desc)
454 return for_all_content(desc, set_exec_flag);
457 static int install_file_properties(const struct wgt_desc *desc)
460 struct wgt_desc_feature *feat;
461 struct wgt_desc_param *param;
464 feat = desc->features;
466 if (!strcmp(feat->name, FWK_PREFIX"widget:file-properties")) {
467 param = feat->params;
469 if (!strcmp(param->value, "executable")) {
470 rc2 = fchmodat(workdirfd, param->name, 0755, 0);
472 ERROR("can't make executable the file %s: %m", param->name);
474 ERROR("unknown file property %s for %s", param->value, param->name);
488 static int is_path_public(const char *path, const struct wgt_desc *desc)
490 const struct wgt_desc_icon *icon;
491 const struct wgt_desc_feature *feat;
492 const struct wgt_desc_param *param;
495 /* icons are public */
497 while (icon != NULL) {
498 len = strlen(icon->src);
499 if (!memcmp(path, icon->src, len) && (path[len] == 0 || path[len] == '/'))
504 /* provided bindings are public */
505 feat = desc->features;
506 while (feat != NULL) {
507 if (strcasecmp(feat->name, "urn:AGL:widget:provided-binding") == 0
508 || strcasecmp(feat->name, "urn:AGL:widget:public-files") == 0) {
509 param = feat->params;
510 while(param != NULL) {
511 if (strcmp(param->value, path) == 0)
523 static int install_security(const struct wgt_desc *desc)
525 char path[PATH_MAX], *head;
528 unsigned int i, n, len, lf, j;
531 struct pathent *next;
538 rc = secmgr_init(desc->id);
542 /* instal the files */
543 head = stpcpy(path, workdir);
544 assert(head < path + sizeof path);
545 len = (unsigned)((path + sizeof path) - head);
547 ERROR("root path too long in install_security");
548 errno = ENAMETOOLONG;
554 /* build root entry */
555 pe0 = malloc(1 + sizeof *pe0);
563 /* build list of entries */
565 for (i = 0 ; i < n ; i++) {
566 f = file_of_index(i);
567 public = is_path_public(f->name, desc);
568 pe0->public |= public;
570 while(f->name[j] == '/')
572 while (f->name[j] != 0) {
573 /* copy next entry of the path */
574 while(f->name[j] && f->name[j] != '/') {
576 ERROR("path too long in install_security");
577 errno = ENAMETOOLONG;
580 head[lf++] = f->name[j++];
584 /* search if it already exists */
587 while (pe2 != NULL && pe2->len < lf) {
591 while (pe2 != NULL && pe2->len == lf && strcmp(head, pe2->name)) {
596 if (pe2 != NULL && pe2->len == lf)
597 /* existing, update public status */
598 pe2->public |= public;
600 /* not existing, create it */
601 pe2 = malloc(lf + 1 + sizeof *pe2);
604 pe2->next = ppe->next;
606 pe2->public = public;
607 memcpy(pe2->name, head, 1 + lf);
611 /* prepare next path entry */
613 while(f->name[j] == '/')
618 /* set the path entries */
619 for (pe2 = pe0 ; pe2 != NULL ; pe2 = pe2->next) {
620 strcpy(head, pe2->name);
622 rc = secmgr_path_public_read_only(path);
624 rc = secmgr_path_private(path);
629 /* install the permissions */
630 perm = first_usable_permission();
632 rc = secmgr_permit(perm);
633 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
636 perm = next_usable_permission();
639 /* install default permissions */
640 n = (unsigned int)(sizeof default_permissions / sizeof *default_permissions);
641 for (i = 0 ; i < n ; i++) {
642 perm = default_permissions[i];
643 rc = secmgr_permit(perm);
644 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
649 rc = secmgr_install();
656 /* free memory of path entries */
657 while (pe0 != NULL) {
665 /* install the widget of the file */
666 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
668 struct wgt_info *ifo;
669 const struct wgt_desc *desc;
670 char installdir[PATH_MAX];
672 struct unitconf uconf;
674 NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
677 create_directory(root, 0755, 1);
678 if (make_workdir(root, "TMP", 0)) {
679 ERROR("failed to create a working directory");
683 if (zread(wgtfile, 0))
686 #if defined(ALLOW_NO_SIGNATURE)
687 rc = check_all_signatures(1);
689 rc = check_all_signatures(0);
694 ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
698 reset_requested_permissions();
699 desc = wgt_info_desc(ifo);
700 if (check_widget(desc))
703 if (get_target_directory(installdir, root, desc))
706 if (access(installdir, F_OK) == 0) {
708 ERROR("widget already installed");
712 if (uninstall_widget(desc->idaver, root))
716 if (move_widget_to(installdir, force))
719 if (install_icon(desc))
722 if (install_security(desc))
725 if (install_exec_flag(desc))
728 if (install_file_properties(desc))
731 uconf.installdir = installdir;
732 uconf.icondir = FWK_ICON_DIR;
733 uconf.new_afid = get_new_afid;
734 uconf.base_http_ports = HTTP_PORT_BASE;
735 if (unit_install(ifo, &uconf))