2 Copyright (C) 2015-2018 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_http_port[] = "http-port";
59 * normalize unit files: remove comments, remove heading blanks,
62 static void normalize_unit_file(char *content)
64 char *read, *write, c;
66 read = write = content;
77 do { c = *read++; } while(c && c != '\n');
81 do { *write++ = c = *read++; } while(c && c != '\n');
82 if (write - content >= 2 && write[-2] == '\\')
90 static int get_port_cb(void *closure, const char *name, const char *path, int isuser)
98 rc = getfile(path, &content, &length);
102 /* normalize the unit file */
103 normalize_unit_file(content);
105 /* process the file */
106 iter = strstr(content, key_afm_prefix);
108 iter += sizeof key_afm_prefix - 1;
111 if (!strncmp(iter, key_http_port, sizeof key_http_port - 1)) {
112 iter += sizeof key_http_port - 1;
113 while(*iter && *iter != '=' && *iter != '\n')
116 while(*++iter == ' ');
118 if (p >= 0 && p < 32768)
119 ((uint32_t*)closure)[p >> 5] |= (uint32_t)1 << (p & 31);
122 iter = strstr(iter, key_afm_prefix);
128 static int get_port()
131 uint32_t ports[1024]; /* 1024 * 32 = 32768 */
133 memset(ports, 0, sizeof ports);
134 rc = systemd_unit_list(0, get_port_cb, &ports);
136 rc = systemd_unit_list(1, get_port_cb, ports);
138 for (rc = 1024 ; rc < 32768 && !~ports[rc >> 5] ; rc += 32);
140 ERROR("Can't compute a valid port");
141 errno = EADDRNOTAVAIL;
144 while (1 & (ports[rc >> 5] >> (rc & 31))) rc++;
151 static int check_defined(const void *data, const char *name)
155 ERROR("widget has no defined '%s' (temporary constraints)", name);
160 static int check_valid_string(const char *value, const char *name)
165 if (check_defined(value, name))
170 ERROR("empty string forbidden in '%s' (temporary constraints)", name);
175 if (!isalnum(c) && !strchr(".-_", c)) {
176 ERROR("forbidden char %c in '%s' -> '%s' (temporary constraints)", c, name, value);
185 static int check_temporary_constraints(const struct wgt_desc *desc)
189 result = check_valid_string(desc->id, "id");
190 result |= check_valid_string(desc->version, "version");
191 result |= check_valid_string(desc->ver, "ver");
192 result |= check_defined(desc->content_src, "content");
194 result |= check_defined(desc->icons->src, "icon.src");
198 if (desc->icons && desc->icons->next) {
199 ERROR("widget has more than one icon defined (temporary constraints)");
206 static int set_required_permissions(struct wgt_desc_param *params, int required)
211 /* check if target */
212 if (!strcmp(params->name, string_sharp_target)) {
213 /* do nothing when #target */
215 /* check the value */
216 if (!strcmp(params->value, string_required))
217 optional = !required;
218 else if (!strcmp(params->value, string_optional))
221 ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
225 /* set the permission */
226 if (request_permission(params->name)) {
227 DEBUG("granted permission: %s", params->name);
228 } else if (optional) {
229 INFO("optional permission ungranted: %s", params->name);
231 ERROR("ungranted permission required: %s", params->name);
236 params = params->next;
241 static int check_permissions(const struct wgt_desc *desc)
244 const struct wgt_desc_feature *feature;
247 feature = desc->features;
248 while(result >= 0 && feature) {
249 if (!strcmp(feature->name, feature_required_permission))
250 result = set_required_permissions(feature->params, feature->required);
251 feature = feature->next;
256 static int for_all_content(const struct wgt_desc *desc, int (*action)(const char *src, const char *type))
259 struct wgt_desc_feature *feat;
260 const char *src, *type;
262 rc = action(desc->content_src, desc->content_type);
263 feat = desc->features;
265 if (!strcmp(feat->name, "urn:AGL:widget:provided-unit")) {
266 src = wgt_info_param(feat, "content.src");
267 type = wgt_info_param(feat, "content.type");
268 rc2 = action(src, type);
269 if (rc >= 0 && rc2 < 0)
277 static int set_exec_flag(const char *src, const char *type)
282 i = sizeof exec_type_strings / sizeof *exec_type_strings;
284 if (!strcasecmp(type, exec_type_strings[--i])) {
285 rc = fchmodat(workdirfd, src, 0755, 0);
287 ERROR("can't make executable the file %s", src);
295 static int check_one_content(const char *src, const char *type)
302 ERROR("a content src is missing");
306 /* TODO: when dealing with HTML and languages, the check should
307 * include i18n path search of widgets */
308 rc = fstatat(workdirfd, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
311 fhtdocs = openat(workdirfd, "htdocs", O_DIRECTORY|O_PATH);
313 rc = fstatat(fhtdocs, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
320 ERROR("can't get info on content %s: %m", src);
321 else if (!S_ISREG(s.st_mode)) {
322 ERROR("content %s isn't a regular file", src);
330 static int check_content(const struct wgt_desc *desc)
332 return for_all_content(desc, check_one_content);
335 static int check_widget(const struct wgt_desc *desc)
339 result = check_temporary_constraints(desc);
341 result = check_permissions(desc);
343 result = check_content(desc);
347 static int get_target_directory(char target[PATH_MAX], const char *root, const struct wgt_desc *desc)
351 rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
355 ERROR("path too long");
362 static int move_widget_to(const char *destdir, int force)
364 return move_workdir(destdir, 1, force);
367 static int install_icon(const struct wgt_desc *desc)
370 char target[PATH_MAX];
376 create_directory(FWK_ICON_DIR, 0755, 1);
377 rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
378 if (rc >= (int)sizeof link) {
379 ERROR("link too long in install_icon");
384 rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
385 if (rc >= (int)sizeof target) {
386 ERROR("target too long in install_icon");
392 rc = symlink(target, link);
394 ERROR("can't create link %s -> %s", link, target);
398 static int install_exec_flag(const struct wgt_desc *desc)
400 return for_all_content(desc, set_exec_flag);
403 static int install_file_properties(const struct wgt_desc *desc)
406 struct wgt_desc_feature *feat;
407 struct wgt_desc_param *param;
410 feat = desc->features;
412 if (!strcmp(feat->name, "urn:AGL:widget:file-properties")) {
413 param = feat->params;
415 if (!strcmp(param->value, "executable")) {
416 rc2 = fchmodat(workdirfd, param->name, 0755, 0);
418 ERROR("can't make executable the file %s: %m", param->name);
420 ERROR("unknown file property %s for %s", param->value, param->name);
434 static int install_security(const struct wgt_desc *desc)
436 char path[PATH_MAX], *head;
437 const char *icon, *perm;
439 unsigned int i, n, len, lic, lf;
442 rc = secmgr_init(desc->id);
446 rc = secmgr_path_public_read_only(workdir);
450 /* instal the files */
451 head = stpcpy(path, workdir);
452 assert(head < path + sizeof path);
453 len = (unsigned)((path + sizeof path) - head);
455 ERROR("root path too long in install_security");
456 errno = ENAMETOOLONG;
461 icon = desc->icons ? desc->icons->src : NULL;
462 lic = (unsigned)(icon ? strlen(icon) : 0);
466 f = file_of_index(i++);
467 lf = (unsigned)strlen(f->name);
469 ERROR("path too long in install_security");
470 errno = ENAMETOOLONG;
473 strcpy(head, f->name);
474 if (lf <= lic && icon && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
475 rc = secmgr_path_public_read_only(path);
477 rc = secmgr_path_read_only(path);
482 /* install the permissions */
483 perm = first_usable_permission();
485 rc = secmgr_permit(perm);
486 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
489 perm = next_usable_permission();
492 rc = secmgr_install();
500 /* install the widget of the file */
501 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
503 struct wgt_info *ifo;
504 const struct wgt_desc *desc;
505 char installdir[PATH_MAX];
507 struct unitconf uconf;
509 NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
512 create_directory(root, 0755, 1);
513 if (make_workdir(root, "TMP", 0)) {
514 ERROR("failed to create a working directory");
518 if (zread(wgtfile, 0))
521 if (check_all_signatures(DEFAULT_ALLOW_NO_SIGNATURE))
524 ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
528 reset_requested_permissions();
529 desc = wgt_info_desc(ifo);
530 if (check_widget(desc))
533 if (get_target_directory(installdir, root, desc))
536 if (access(installdir, F_OK) == 0) {
538 ERROR("widget already installed");
542 if (uninstall_widget(desc->idaver, root))
546 if (move_widget_to(installdir, force))
549 if (install_icon(desc))
552 if (install_security(desc))
555 if (install_exec_flag(desc))
558 if (install_file_properties(desc))
565 uconf.installdir = installdir;
566 uconf.icondir = FWK_ICON_DIR;
568 if (unit_install(ifo, &uconf))