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 rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
405 ERROR("path too long");
412 static int move_widget_to(const char *destdir, int force)
414 return move_workdir(destdir, 1, force);
417 static int install_icon(const struct wgt_desc *desc)
420 char target[PATH_MAX];
426 create_directory(FWK_ICON_DIR, 0755, 1);
427 rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
428 if (rc >= (int)sizeof link) {
429 ERROR("link too long in install_icon");
434 rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
435 if (rc >= (int)sizeof target) {
436 ERROR("target too long in install_icon");
442 rc = symlink(target, link);
444 ERROR("can't create link %s -> %s", link, target);
448 static int install_exec_flag(const struct wgt_desc *desc)
450 return for_all_content(desc, set_exec_flag);
453 static int install_file_properties(const struct wgt_desc *desc)
456 struct wgt_desc_feature *feat;
457 struct wgt_desc_param *param;
460 feat = desc->features;
462 if (!strcmp(feat->name, FWK_PREFIX"widget:file-properties")) {
463 param = feat->params;
465 if (!strcmp(param->value, "executable")) {
466 rc2 = fchmodat(workdirfd, param->name, 0755, 0);
468 ERROR("can't make executable the file %s: %m", param->name);
470 ERROR("unknown file property %s for %s", param->value, param->name);
484 static int install_security(const struct wgt_desc *desc)
486 char path[PATH_MAX], *head;
487 const char *icon, *perm;
489 unsigned int i, n, len, lic, lf;
492 rc = secmgr_init(desc->id);
496 rc = secmgr_path_public_read_only(workdir);
500 /* instal the files */
501 head = stpcpy(path, workdir);
502 assert(head < path + sizeof path);
503 len = (unsigned)((path + sizeof path) - head);
505 ERROR("root path too long in install_security");
506 errno = ENAMETOOLONG;
511 icon = desc->icons ? desc->icons->src : NULL;
512 lic = (unsigned)(icon ? strlen(icon) : 0);
516 f = file_of_index(i++);
517 lf = (unsigned)strlen(f->name);
519 ERROR("path too long in install_security");
520 errno = ENAMETOOLONG;
523 strcpy(head, f->name);
524 if (lf <= lic && icon && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
525 rc = secmgr_path_public_read_only(path);
527 rc = secmgr_path_private(path);
532 /* install the permissions */
533 perm = first_usable_permission();
535 rc = secmgr_permit(perm);
536 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
539 perm = next_usable_permission();
542 /* install default permissions */
543 n = (unsigned int)(sizeof default_permissions / sizeof *default_permissions);
544 for (i = 0 ; i < n ; i++) {
545 perm = default_permissions[i];
546 rc = secmgr_permit(perm);
547 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
552 rc = secmgr_install();
560 /* install the widget of the file */
561 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
563 struct wgt_info *ifo;
564 const struct wgt_desc *desc;
565 char installdir[PATH_MAX];
567 struct unitconf uconf;
569 NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
572 create_directory(root, 0755, 1);
573 if (make_workdir(root, "TMP", 0)) {
574 ERROR("failed to create a working directory");
578 if (zread(wgtfile, 0))
581 if (check_all_signatures(DEFAULT_ALLOW_NO_SIGNATURE))
584 ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
588 reset_requested_permissions();
589 desc = wgt_info_desc(ifo);
590 if (check_widget(desc))
593 if (get_target_directory(installdir, root, desc))
596 if (access(installdir, F_OK) == 0) {
598 ERROR("widget already installed");
602 if (uninstall_widget(desc->idaver, root))
606 if (move_widget_to(installdir, force))
609 if (install_icon(desc))
612 if (install_security(desc))
615 if (install_exec_flag(desc))
618 if (install_file_properties(desc))
621 uconf.installdir = installdir;
622 uconf.icondir = FWK_ICON_DIR;
623 uconf.new_afid = get_new_afid;
624 uconf.base_http_ports = HTTP_PORT_BASE;
625 if (unit_install(ifo, &uconf))