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";
58 #define HTTP_PORT_MIN 31000
59 #define HTTP_PORT_MAX 32759
60 #define HTTP_PORT_IS_VALID(port) (HTTP_PORT_MIN <= (port) && (port) <= HTTP_PORT_MAX)
61 #define HTTP_PORT_COUNT (HTTP_PORT_MAX - HTTP_PORT_MIN + 1)
62 #define HTTP_PORT_ACNT ((HTTP_PORT_COUNT + 31) >> 5)
63 #define HTTP_PORT_ASFT(port) (((port) - HTTP_PORT_MIN) & 31)
64 #define HTTP_PORT_AIDX(port) (((port) - HTTP_PORT_MIN) >> 5)
65 #define HTTP_PORT_TEST(array,port) ((((array)[HTTP_PORT_AIDX(port)]) >> HTTP_PORT_ASFT(port)) & 1)
66 #define HTTP_PORT_SET(array,port) (((array)[HTTP_PORT_AIDX(port)]) |= (((uint32_t)1) << HTTP_PORT_ASFT(port)))
68 static uint32_t *port_bits = NULL;
71 * normalize unit files: remove comments, remove heading blanks,
74 static void normalize_unit_file(char *content)
76 char *read, *write, c;
78 read = write = content;
89 do { c = *read++; } while(c && c != '\n');
93 do { *write++ = c = *read++; } while(c && c != '\n');
94 if (write - content >= 2 && write[-2] == '\\')
102 static int get_port_cb(void *closure, const char *name, const char *path, int isuser)
110 rc = getfile(path, &content, &length);
114 /* normalize the unit file */
115 normalize_unit_file(content);
117 /* process the file */
118 iter = strstr(content, key_afm_prefix);
120 iter += sizeof key_afm_prefix - 1;
123 if (!strncmp(iter, key_http_port, sizeof key_http_port - 1)) {
124 iter += sizeof key_http_port - 1;
125 while(*iter && *iter != '=' && *iter != '\n')
128 while(*++iter == ' ');
130 if (HTTP_PORT_IS_VALID(p))
131 HTTP_PORT_SET((uint32_t*)closure, p);
134 iter = strstr(iter, key_afm_prefix);
140 static int update_portbits(uint32_t *portbits)
144 memset(portbits, 0, HTTP_PORT_ACNT * sizeof(uint32_t));
145 rc = systemd_unit_list(0, get_port_cb, portbits);
147 rc = systemd_unit_list(1, get_port_cb, portbits);
149 ERROR("troubles while updating ports");
153 static int first_free_port(uint32_t *portbits)
157 port = HTTP_PORT_MIN;
158 while (port <= HTTP_PORT_MAX && !~portbits[HTTP_PORT_AIDX(port)])
160 while (port <= HTTP_PORT_MAX && HTTP_PORT_TEST(portbits, port))
162 if (port > HTTP_PORT_MAX) {
163 ERROR("Can't compute a valid port");
164 errno = EADDRNOTAVAIL;
170 static int get_port()
174 /* ensure existing port bitmap */
175 if (port_bits == NULL) {
176 port_bits = malloc(HTTP_PORT_ACNT * sizeof(uint32_t));
177 if (port_bits == NULL || update_portbits(port_bits) < 0)
181 /* allocates the port */
182 port = first_free_port(port_bits);
184 HTTP_PORT_SET(port_bits, port);
189 static int check_defined(const void *data, const char *name)
193 ERROR("widget has no defined '%s' (temporary constraints)", name);
198 static int check_valid_string(const char *value, const char *name)
203 if (check_defined(value, name))
208 ERROR("empty string forbidden in '%s' (temporary constraints)", name);
213 if (!isalnum(c) && !strchr(".-_", c)) {
214 ERROR("forbidden char %c in '%s' -> '%s' (temporary constraints)", c, name, value);
223 static int check_temporary_constraints(const struct wgt_desc *desc)
227 result = check_valid_string(desc->id, "id");
228 result |= check_valid_string(desc->version, "version");
229 result |= check_valid_string(desc->ver, "ver");
230 result |= check_defined(desc->content_src, "content");
232 result |= check_defined(desc->icons->src, "icon.src");
236 if (desc->icons && desc->icons->next) {
237 ERROR("widget has more than one icon defined (temporary constraints)");
244 static int set_required_permissions(struct wgt_desc_param *params, int required)
249 /* check if target */
250 if (!strcmp(params->name, string_sharp_target)) {
251 /* do nothing when #target */
253 /* check the value */
254 if (!strcmp(params->value, string_required))
255 optional = !required;
256 else if (!strcmp(params->value, string_optional))
259 ERROR("unexpected parameter value: %s found for %s", params->value, params->name);
263 /* set the permission */
264 if (request_permission(params->name)) {
265 DEBUG("granted permission: %s", params->name);
266 } else if (optional) {
267 INFO("optional permission ungranted: %s", params->name);
269 ERROR("ungranted permission required: %s", params->name);
274 params = params->next;
279 static int check_permissions(const struct wgt_desc *desc)
282 const struct wgt_desc_feature *feature;
285 feature = desc->features;
286 while(result >= 0 && feature) {
287 if (!strcmp(feature->name, feature_required_permission))
288 result = set_required_permissions(feature->params, feature->required);
289 feature = feature->next;
294 static int for_all_content(const struct wgt_desc *desc, int (*action)(const char *src, const char *type))
297 struct wgt_desc_feature *feat;
298 const char *src, *type;
300 rc = action(desc->content_src, desc->content_type);
301 feat = desc->features;
303 if (!strcmp(feat->name, "urn:AGL:widget:provided-unit")) {
304 src = wgt_info_param(feat, "content.src");
305 type = wgt_info_param(feat, "content.type");
306 rc2 = action(src, type);
307 if (rc >= 0 && rc2 < 0)
315 static int set_exec_flag(const char *src, const char *type)
320 i = sizeof exec_type_strings / sizeof *exec_type_strings;
322 if (!strcasecmp(type, exec_type_strings[--i])) {
323 rc = fchmodat(workdirfd, src, 0755, 0);
325 ERROR("can't make executable the file %s", src);
333 static int check_one_content(const char *src, const char *type)
340 ERROR("a content src is missing");
344 /* TODO: when dealing with HTML and languages, the check should
345 * include i18n path search of widgets */
346 rc = fstatat(workdirfd, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
349 fhtdocs = openat(workdirfd, "htdocs", O_DIRECTORY|O_PATH);
351 rc = fstatat(fhtdocs, src, &s, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
358 ERROR("can't get info on content %s: %m", src);
359 else if (!S_ISREG(s.st_mode)) {
360 ERROR("content %s isn't a regular file", src);
368 static int check_content(const struct wgt_desc *desc)
370 return for_all_content(desc, check_one_content);
373 static int check_widget(const struct wgt_desc *desc)
377 result = check_temporary_constraints(desc);
379 result = check_permissions(desc);
381 result = check_content(desc);
385 static int get_target_directory(char target[PATH_MAX], const char *root, const struct wgt_desc *desc)
389 rc = snprintf(target, PATH_MAX, "%s/%s/%s", root, desc->id, desc->ver);
393 ERROR("path too long");
400 static int move_widget_to(const char *destdir, int force)
402 return move_workdir(destdir, 1, force);
405 static int install_icon(const struct wgt_desc *desc)
408 char target[PATH_MAX];
414 create_directory(FWK_ICON_DIR, 0755, 1);
415 rc = snprintf(link, sizeof link, "%s/%s", FWK_ICON_DIR, desc->idaver);
416 if (rc >= (int)sizeof link) {
417 ERROR("link too long in install_icon");
422 rc = snprintf(target, sizeof target, "%s/%s", workdir, desc->icons->src);
423 if (rc >= (int)sizeof target) {
424 ERROR("target too long in install_icon");
430 rc = symlink(target, link);
432 ERROR("can't create link %s -> %s", link, target);
436 static int install_exec_flag(const struct wgt_desc *desc)
438 return for_all_content(desc, set_exec_flag);
441 static int install_file_properties(const struct wgt_desc *desc)
444 struct wgt_desc_feature *feat;
445 struct wgt_desc_param *param;
448 feat = desc->features;
450 if (!strcmp(feat->name, "urn:AGL:widget:file-properties")) {
451 param = feat->params;
453 if (!strcmp(param->value, "executable")) {
454 rc2 = fchmodat(workdirfd, param->name, 0755, 0);
456 ERROR("can't make executable the file %s: %m", param->name);
458 ERROR("unknown file property %s for %s", param->value, param->name);
472 static int install_security(const struct wgt_desc *desc)
474 char path[PATH_MAX], *head;
475 const char *icon, *perm;
477 unsigned int i, n, len, lic, lf;
480 rc = secmgr_init(desc->id);
484 rc = secmgr_path_public_read_only(workdir);
488 /* instal the files */
489 head = stpcpy(path, workdir);
490 assert(head < path + sizeof path);
491 len = (unsigned)((path + sizeof path) - head);
493 ERROR("root path too long in install_security");
494 errno = ENAMETOOLONG;
499 icon = desc->icons ? desc->icons->src : NULL;
500 lic = (unsigned)(icon ? strlen(icon) : 0);
504 f = file_of_index(i++);
505 lf = (unsigned)strlen(f->name);
507 ERROR("path too long in install_security");
508 errno = ENAMETOOLONG;
511 strcpy(head, f->name);
512 if (lf <= lic && icon && !memcmp(f->name, icon, lf) && (!f->name[lf] || f->name[lf] == '/'))
513 rc = secmgr_path_public_read_only(path);
515 rc = secmgr_path_read_only(path);
520 /* install the permissions */
521 perm = first_usable_permission();
523 rc = secmgr_permit(perm);
524 INFO("permitting %s %s", perm, rc ? "FAILED!" : "success");
527 perm = next_usable_permission();
530 rc = secmgr_install();
538 /* install the widget of the file */
539 struct wgt_info *install_widget(const char *wgtfile, const char *root, int force)
541 struct wgt_info *ifo;
542 const struct wgt_desc *desc;
543 char installdir[PATH_MAX];
545 struct unitconf uconf;
547 NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
550 create_directory(root, 0755, 1);
551 if (make_workdir(root, "TMP", 0)) {
552 ERROR("failed to create a working directory");
556 if (zread(wgtfile, 0))
559 if (check_all_signatures(DEFAULT_ALLOW_NO_SIGNATURE))
562 ifo = wgt_info_createat(workdirfd, NULL, 1, 1, 1);
566 reset_requested_permissions();
567 desc = wgt_info_desc(ifo);
568 if (check_widget(desc))
571 if (get_target_directory(installdir, root, desc))
574 if (access(installdir, F_OK) == 0) {
576 ERROR("widget already installed");
580 if (uninstall_widget(desc->idaver, root))
584 if (move_widget_to(installdir, force))
587 if (install_icon(desc))
590 if (install_security(desc))
593 if (install_exec_flag(desc))
596 if (install_file_properties(desc))
599 uconf.installdir = installdir;
600 uconf.icondir = FWK_ICON_DIR;
601 uconf.port = get_port;
602 if (unit_install(ifo, &uconf))