2 Copyright (C) 2015-2019 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);
190 AFID_SET(afids_array, afid);
195 static int check_defined(const void *data, const char *name)
199 ERROR("widget has no defined '%s' (temporary constraints)", name);
204 static int check_valid_string(const char *value, const char *name)
209 if (check_defined(value, name))
214 ERROR("empty string forbidden in '%s' (temporary constraints)", name);
219 if (!isalnum(c) && !strchr(".-_", c)) {
220 ERROR("forbidden char %c in '%s' -> '%s' (temporary constraints)", c, name, value);
229 static int check_temporary_constraints(const struct wgt_desc *desc)
233 result = check_valid_string(desc->id, "id");
234 result |= check_valid_string(desc->version, "version");
235 result |= check_valid_string(desc->ver, "ver");
236 result |= check_defined(desc->content_src, "content");
238 result |= check_defined(desc->icons->src, "icon.src");
242 if (desc->icons && desc->icons->next) {
243 ERROR("widget has more than one icon defined (temporary constraints)");
250 static int set_required_permissions(struct wgt_desc_param *params, int required)
255 /* check if target */
256 if (!strcmp(params->name, string_sharp_target)) {
257 /* do nothing when #target */
259 /* check the value */
260 if (!strcmp(params->value, string_required))
261 optional = !required;
262 else if (!strcmp(params->value, string_optional))
265 ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
269 /* set the permission */
270 if (request_permission(params->name)) {
271 DEBUG("granted permission: %s", params->name);
272 } else if (optional) {
273 INFO("optional permission ungranted: %s", params->name);
275 ERROR("ungranted permission required: %s", params->name);
280 params = params->next;
285 static int check_permissions(const struct wgt_desc *desc)
288 const struct wgt_desc_feature *feature;
291 feature = desc->features;
292 while(result >= 0 && feature) {
293 if (!strcmp(feature->name, feature_required_permission))
294 result = set_required_permissions(feature->params, feature->required);
295 feature = feature->next;
300 static int for_all_content(const struct wgt_desc *desc, int (*action)(const char *src, const char *type))
303 struct wgt_desc_feature *feat;
304 const char *src, *type;
306 rc = action(desc->content_src, desc->content_type);
307 feat = desc->features;
309 if (!strcmp(feat->name, FWK_PREFIX"widget:provided-unit")) {
310 src = wgt_info_param(feat, "content.src");
311 type = wgt_info_param(feat, "content.type");
312 rc2 = action(src, type);
313 if (rc >= 0 && rc2 < 0)
321 static int set_exec_flag(const char *src, const char *type)
326 i = sizeof exec_type_strings / sizeof *exec_type_strings;
328 if (!strcasecmp(type, exec_type_strings[--i])) {
329 rc = fchmodat(workdirfd, src, 0755, 0);
331 ERROR("can't make executable the file %s", src);
339 static int check_one_content(const char *src, const char *type)
346 ERROR("a content src is missing");
350 /* TODO: when dealing with HTML and languages, the check should
351 * include i18n path search of widgets */
352 rc = fstatat(workdirfd, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
355 fhtdocs = openat(workdirfd, "htdocs", O_DIRECTORY|O_PATH);
357 rc = fstatat(fhtdocs, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
364 ERROR("can't get info on content %s: %m", src);
365 else if (!S_ISREG(s.st_mode)) {
366 ERROR("content %s isn't a regular file", src);
374 static int check_content(const struct wgt_desc *desc)
376 return for_all_content(desc, check_one_content);
379 static int check_widget(const struct wgt_desc *desc)
383 result = check_temporary_constraints(desc);
385 result = check_permissions(desc);
387 result = check_content(desc);
391 static int get_target_directory(char target[PATH_MAX], const char *root, const struct wgt_desc *desc)
395 rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
399 ERROR("path too long");
406 static int move_widget_to(const char *destdir, int force)
408 return move_workdir(destdir, 1, force);
411 static int install_icon(const struct wgt_desc *desc)
414 char target[PATH_MAX];
420 create_directory(FWK_ICON_DIR, 0755, 1);
421 rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
422 if (rc >= (int)sizeof link) {
423 ERROR("link too long in install_icon");
428 rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
429 if (rc >= (int)sizeof target) {
430 ERROR("target too long in install_icon");
436 rc = symlink(target, link);
438 ERROR("can't create link %s -> %s", link, target);
442 static int install_exec_flag(const struct wgt_desc *desc)
444 return for_all_content(desc, set_exec_flag);
447 static int install_file_properties(const struct wgt_desc *desc)
450 struct wgt_desc_feature *feat;
451 struct wgt_desc_param *param;
454 feat = desc->features;
456 if (!strcmp(feat->name, FWK_PREFIX"widget:file-properties")) {
457 param = feat->params;
459 if (!strcmp(param->value, "executable")) {
460 rc2 = fchmodat(workdirfd, param->name, 0755, 0);
462 ERROR("can't make executable the file %s: %m", param->name);
464 ERROR("unknown file property %s for %s", param->value, param->name);
478 static int install_security(const struct wgt_desc *desc)
480 char path[PATH_MAX], *head;
481 const char *icon, *perm;
483 unsigned int i, n, len, lic, lf;
486 rc = secmgr_init(desc->id);
490 rc = secmgr_path_public_read_only(workdir);
494 /* instal the files */
495 head = stpcpy(path, workdir);
496 assert(head < path + sizeof path);
497 len = (unsigned)((path + sizeof path) - head);
499 ERROR("root path too long in install_security");
500 errno = ENAMETOOLONG;
505 icon = desc->icons ? desc->icons->src : NULL;
506 lic = (unsigned)(icon ? strlen(icon) : 0);
510 f = file_of_index(i++);
511 lf = (unsigned)strlen(f->name);
513 ERROR("path too long in install_security");
514 errno = ENAMETOOLONG;
517 strcpy(head, f->name);
518 if (lf <= lic && icon && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
519 rc = secmgr_path_public_read_only(path);
521 rc = secmgr_path_read_only(path);
526 /* install the permissions */
527 perm = first_usable_permission();
529 rc = secmgr_permit(perm);
530 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
533 perm = next_usable_permission();
536 /* install default permissions */
537 n = (unsigned int)(sizeof default_permissions / sizeof *default_permissions);
538 for (i = 0 ; i < n ; i++) {
539 perm = default_permissions[i];
540 rc = secmgr_permit(perm);
541 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
546 rc = secmgr_install();
554 /* install the widget of the file */
555 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
557 struct wgt_info *ifo;
558 const struct wgt_desc *desc;
559 char installdir[PATH_MAX];
561 struct unitconf uconf;
563 NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
566 create_directory(root, 0755, 1);
567 if (make_workdir(root, "TMP", 0)) {
568 ERROR("failed to create a working directory");
572 if (zread(wgtfile, 0))
575 if (check_all_signatures(DEFAULT_ALLOW_NO_SIGNATURE))
578 ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
582 reset_requested_permissions();
583 desc = wgt_info_desc(ifo);
584 if (check_widget(desc))
587 if (get_target_directory(installdir, root, desc))
590 if (access(installdir, F_OK) == 0) {
592 ERROR("widget already installed");
596 if (uninstall_widget(desc->idaver, root))
600 if (move_widget_to(installdir, force))
603 if (install_icon(desc))
606 if (install_security(desc))
609 if (install_exec_flag(desc))
612 if (install_file_properties(desc))
615 uconf.installdir = installdir;
616 uconf.icondir = FWK_ICON_DIR;
617 uconf.new_afid = get_new_afid;
618 uconf.base_http_ports = HTTP_PORT_BASE;
619 if (unit_install(ifo, &uconf))