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 install_security(const struct wgt_desc *desc)
490 char path[PATH_MAX], *head;
491 const char *icon, *perm;
493 unsigned int i, n, len, lic, lf;
496 rc = secmgr_init(desc->id);
500 rc = secmgr_path_public_read_only(workdir);
504 /* instal the files */
505 head = stpcpy(path, workdir);
506 assert(head < path + sizeof path);
507 len = (unsigned)((path + sizeof path) - head);
509 ERROR("root path too long in install_security");
510 errno = ENAMETOOLONG;
515 icon = desc->icons ? desc->icons->src : NULL;
516 lic = (unsigned)(icon ? strlen(icon) : 0);
520 f = file_of_index(i++);
521 lf = (unsigned)strlen(f->name);
523 ERROR("path too long in install_security");
524 errno = ENAMETOOLONG;
527 strcpy(head, f->name);
528 if (lf <= lic && icon && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
529 rc = secmgr_path_public_read_only(path);
531 rc = secmgr_path_read_only(path);
536 /* install the permissions */
537 perm = first_usable_permission();
539 rc = secmgr_permit(perm);
540 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
543 perm = next_usable_permission();
546 /* install default permissions */
547 n = (unsigned int)(sizeof default_permissions / sizeof *default_permissions);
548 for (i = 0 ; i < n ; i++) {
549 perm = default_permissions[i];
550 rc = secmgr_permit(perm);
551 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
556 rc = secmgr_install();
564 /* install the widget of the file */
565 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
567 struct wgt_info *ifo;
568 const struct wgt_desc *desc;
569 char installdir[PATH_MAX];
571 struct unitconf uconf;
573 NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
576 create_directory(root, 0755, 1);
577 if (make_workdir(root, "TMP", 0)) {
578 ERROR("failed to create a working directory");
582 if (zread(wgtfile, 0))
585 #if defined(ALLOW_NO_SIGNATURE)
586 rc = check_all_signatures(1);
588 rc = check_all_signatures(0);
593 ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
597 reset_requested_permissions();
598 desc = wgt_info_desc(ifo);
599 if (check_widget(desc))
602 if (get_target_directory(installdir, root, desc))
605 if (access(installdir, F_OK) == 0) {
607 ERROR("widget already installed");
611 if (uninstall_widget(desc->idaver, root))
615 if (move_widget_to(installdir, force))
618 if (install_icon(desc))
621 if (install_security(desc))
624 if (install_exec_flag(desc))
627 if (install_file_properties(desc))
630 uconf.installdir = installdir;
631 uconf.icondir = FWK_ICON_DIR;
632 uconf.new_afid = get_new_afid;
633 uconf.base_http_ports = HTTP_PORT_BASE;
634 if (unit_install(ifo, &uconf))