summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJosé Bollo <jose.bollo@iot.bzh>2015-12-09 14:35:04 +0100
committerJosé Bollo <jose.bollo@iot.bzh>2015-12-09 14:35:04 +0100
commitbf7b5918fcc07713a29b9ca32f766b65b15a4ec2 (patch)
treea4ad318995f3213cee35a2065fc3c4911e2012dc /src
parent0270b7281b783cbea5c1f0ebb4440d2be1bd79fa (diff)
refactoring sources
Change-Id: Id6d52eee86b706958972e9b345ec0d4d1e488146 Signed-off-by: José Bollo <jose.bollo@iot.bzh>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am32
-rw-r--r--src/wgt-config-xml.c236
-rw-r--r--src/wgt-locales.c121
-rw-r--r--src/wgt-rootdir.c97
-rw-r--r--src/wgt-strings.c31
-rw-r--r--src/wgt.h69
-rw-r--r--src/wgtpkg-base64.c203
-rw-r--r--src/wgtpkg-certs.c80
-rw-r--r--src/wgtpkg-digsig.c358
-rw-r--r--src/wgtpkg-files.c313
-rw-r--r--src/wgtpkg-install.c84
-rw-r--r--src/wgtpkg-pack.c167
-rw-r--r--src/wgtpkg-sign.c199
-rw-r--r--src/wgtpkg-verbose.c36
-rw-r--r--src/wgtpkg-workdir.c166
-rw-r--r--src/wgtpkg-xmlsec.c371
-rw-r--r--src/wgtpkg-zip.c342
-rw-r--r--src/wgtpkg.h125
18 files changed, 3030 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..6c76395
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,32 @@
+bin_PROGRAMS = wgtpkg-install wgtpkg-pack wgtpkg-sign
+
+COMMONSRCS = \
+ wgtpkg-base64.c \
+ wgtpkg-certs.c \
+ wgtpkg-digsig.c \
+ wgtpkg-files.c \
+ wgtpkg-verbose.c \
+ wgtpkg-workdir.c \
+ wgtpkg-xmlsec.c \
+ wgtpkg-zip.c
+
+WGTSRCS = \
+ wgt-config-xml.c \
+ wgt-locales.c \
+ wgt-rootdir.c \
+ wgt-strings.c
+
+AM_CFLAGS = -Wall -Wno-pointer-sign
+AM_CFLAGS += -ffunction-sections -fdata-sections
+AM_CFLAGS += ${ZIP_CFLAGS} ${XML2_CFLAGS} ${OPENSSL_CFLAGS} ${XMLSEC_CFLAGS}
+
+AM_LDFLAGS = -Wl,--gc-sections
+
+LDADD = ${ZIP_LIBS} ${XML2_LIBS} ${OPENSSL_LIBS} ${XMLSEC_LIBS}
+
+wgtpkg_install_SOURCES = wgtpkg-install.c ${WGTSRCS} ${COMMONSRCS}
+
+wgtpkg_sign_SOURCES = wgtpkg-sign.c ${COMMONSRCS}
+
+wgtpkg_pack_SOURCES = wgtpkg-pack.c ${COMMONSRCS}
+
diff --git a/src/wgt-config-xml.c b/src/wgt-config-xml.c
new file mode 100644
index 0000000..f92ae3a
--- /dev/null
+++ b/src/wgt-config-xml.c
@@ -0,0 +1,236 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/uri.h>
+
+
+#include "wgt.h"
+
+static xmlDocPtr configxml = NULL;
+
+static xmlNodePtr next(xmlNodePtr node, const char *type)
+{
+ while (node && node->type != XML_ELEMENT_NODE && strcmp(type, node->name))
+ node = node->next;
+ return node;
+}
+
+static xmlNodePtr first(const char *type)
+{
+ xmlNodePtr root;
+ if (configxml) {
+ root = xmlDocGetRootElement(configxml);
+ if (root)
+ return next(root->children, type);
+ }
+ return NULL;
+}
+
+static int scorelang(xmlNodePtr node)
+{
+ char *lang = xmlNodeGetLang(node);
+ int score = locales_score(lang);
+ xmlFree(lang);
+ return score;
+}
+
+static xmlNodePtr element_based_localisation(const char *type)
+{
+ xmlNodePtr resu, elem;
+ int sr, s;
+
+ resu = first(type);
+ if (resu) {
+ sr = scorelang(resu);
+ elem = next(resu->next, type);
+ while (resu) {
+ s = scorelang(elem);
+ if (s < sr) {
+ resu = elem;
+ sr = s;
+ }
+ elem = next(elem->next, type);
+ }
+ }
+ return resu;
+}
+
+void confixml_close()
+{
+ if (configxml) {
+ xmlFreeDoc(configxml);
+ configxml = NULL;
+ }
+}
+
+int confixml_open()
+{
+ int fd;
+ assert(!configxml);
+ fd = widget_open_read(_config_xml_);
+ if (fd < 0) {
+ syslog(LOG_ERR, "can't open config file %s", _config_xml_);
+ return fd;
+ }
+ configxml = xmlReadFd(fd, "_config_xml_", NULL, 0);
+ close(fd);
+ if (configxml == NULL) {
+ syslog(LOG_ERR, "xml parse of config file %s failed", _config_xml_);
+ return -1;
+ }
+ return 0;
+}
+
+/* elements based on localisation */
+xmlNodePtr confixml_name()
+{
+ return element_based_localisation(_name_);
+}
+
+xmlNodePtr confixml_description()
+{
+ return element_based_localisation(_description_);
+}
+
+xmlNodePtr confixml_license()
+{
+ return element_based_localisation(_license_);
+}
+
+/* elements based on path localisation */
+xmlNodePtr confixml_author()
+{
+ return first(_author_);
+}
+
+xmlNodePtr confixml_content()
+{
+ return first(_content_);
+}
+
+/* element multiple */
+
+xmlNodePtr confixml_first_feature()
+{
+ return first(_feature_);
+}
+
+xmlNodePtr confixml_next_feature(xmlNodePtr node)
+{
+ return next(node->next, _feature_);
+}
+
+xmlNodePtr confixml_first_preference()
+{
+ return first(_preference_);
+}
+
+xmlNodePtr confixml_next_preference(xmlNodePtr node)
+{
+ return next(node->next, _preference_);
+}
+
+xmlNodePtr confixml_first_icon()
+{
+ return first(_icon_);
+}
+
+xmlNodePtr confixml_next_icon(xmlNodePtr node)
+{
+ return next(node->next, _icon_);
+}
+
+/* best sized icon */
+
+static int score_dim(xmlNodePtr ref, xmlNodePtr x, const char *dim, int request)
+{
+ int r, iref, ix;
+ char *sref, *sx;
+
+ sref = xmlGetProp(ref, dim);
+ if (sref) {
+ iref = atoi(sref);
+ xmlFree(sref);
+ sx = xmlGetProp(x, dim);
+ if (sx) {
+ /* sref && sx */
+ ix = atoi(sx);
+ xmlFree(sx);
+ if (ix >= request) {
+ if (iref >= request)
+ r = ix - iref;
+ else
+ r = request - ix;
+ } else {
+ if (iref >= request)
+ r = iref - request;
+ else
+ r = iref - ix;
+ }
+ } else {
+ /* sref && !sx */
+ if (iref >= request)
+ r = iref - request;
+ else
+ r = 0;
+ }
+ } else {
+ sx = xmlGetProp(x, dim);
+ if (sx) {
+ /* !sref && sx */
+ ix = atoi(sx);
+ xmlFree(sx);
+ if (ix >= request)
+ r = request - ix;
+ else
+ r = 0;
+ } else {
+ /* !sref && !sx */
+ r = 0;
+ }
+ }
+ return r;
+}
+
+static int better_icon(xmlNodePtr ref, xmlNodePtr x, int width, int height)
+{
+ int sw = score_dim(ref, x, _width_, width);
+ int sh = score_dim(ref, x, _height_, height);
+ return sw+sh < 0;
+}
+
+xmlNodePtr confixml_icon(int width, int height)
+{
+ xmlNodePtr resu, icon;
+
+ resu = confixml_first_icon();
+ icon = confixml_next_icon(resu);
+ while (icon) {
+ if (better_icon(resu, icon, width, height))
+ resu = icon;
+ icon = confixml_next_icon(icon);
+ }
+ return resu;
+}
+
diff --git a/src/wgt-locales.c b/src/wgt-locales.c
new file mode 100644
index 0000000..9b97cc4
--- /dev/null
+++ b/src/wgt-locales.c
@@ -0,0 +1,121 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "wgt.h"
+
+struct locarr {
+ int count;
+ char **arr;
+};
+
+static struct locarr locarr = { 0, NULL };
+
+static int add(const char *locstr, int length)
+{
+ int i;
+ char *item, **ptr;
+
+ item = strndup(locstr, length);
+ if (item != NULL) {
+ for (i = 0 ; item[i] ; i++)
+ item[i] = tolower(item[i]);
+ for (i = 0 ; i < locarr.count ; i++)
+ if (!strcmp(item, locarr.arr[i])) {
+ free(item);
+ return 0;
+ }
+
+ ptr = realloc(locarr.arr, (1 + locarr.count) * sizeof(locarr.arr[0]));
+ if (ptr) {
+ locarr.arr = ptr;
+ locarr.arr[locarr.count++] = item;
+ return 0;
+ }
+ free(item);
+ }
+ errno = ENOMEM;
+ return -1;
+}
+
+void locales_reset()
+{
+ while (locarr.count)
+ free(locarr.arr[--locarr.count]);
+}
+
+int locales_add(const char *locstr)
+{
+ const char *stop, *next;
+ while (*locstr) {
+ stop = strchrnul(locstr, ',');
+ next = stop + !!*stop;
+ while (locstr != stop) {
+ if (add(locstr, stop - locstr))
+ return -1;
+ do { stop--; } while(stop > locstr && *stop != '-');
+ }
+ locstr = next;
+ }
+ return 0;
+}
+
+int locales_score(const char *lang)
+{
+ int i;
+
+ if (lang)
+ for (i = 0 ; i < locarr.count ; i++)
+ if (!strcasecmp(lang, locarr.arr[i]))
+ return i;
+
+ return INT_MAX;
+}
+
+char *locales_locate_file(const char *filename)
+{
+ int i;
+ char path[PATH_MAX];
+ char * result;
+
+ for (i = 0 ; i < locarr.count ; i++) {
+ if (snprintf(path, sizeof path, "locales/%s/%s", locarr.arr[i], filename) >= (int)(sizeof path)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (widget_has(path)) {
+ result = strdup(path);
+ if (!result)
+ errno = ENOMEM;
+ return result;
+ }
+ }
+ if (widget_has(filename)) {
+ result = strdup(filename);
+ if (!result)
+ errno = ENOMEM;
+ return result;
+ }
+ errno = ENOENT;
+ return NULL;
+}
+
diff --git a/src/wgt-rootdir.c b/src/wgt-rootdir.c
new file mode 100644
index 0000000..4df1705
--- /dev/null
+++ b/src/wgt-rootdir.c
@@ -0,0 +1,97 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#define _GNU_SOURCE
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "wgt.h"
+
+
+static int rootfd = AT_FDCWD;
+
+int widget_set_rootdir(const char *pathname)
+{
+ int rfd;
+
+ if (!pathname)
+ rfd = AT_FDCWD;
+ else {
+ rfd = openat(AT_FDCWD, pathname, O_PATH|O_DIRECTORY);
+ if (rfd < 0)
+ return rfd;
+ }
+ if (rootfd >= 0)
+ close(rootfd);
+ rootfd = AT_FDCWD;
+ return 0;
+}
+
+static int validsubpath(const char *subpath)
+{
+ int l = 0, i = 0;
+ if (subpath[i] == '/')
+ return 0;
+ while(subpath[i]) {
+ switch(subpath[i++]) {
+ case '.':
+ if (!subpath[i])
+ break;
+ if (subpath[i] == '/') {
+ i++;
+ break;
+ }
+ if (subpath[i++] == '.') {
+ if (!subpath[i]) {
+ l--;
+ break;
+ }
+ if (subpath[i++] == '/') {
+ l--;
+ break;
+ }
+ }
+ default:
+ while(subpath[i] && subpath[i] != '/')
+ i++;
+ l++;
+ case '/':
+ break;
+ }
+ }
+ return l >= 0;
+}
+
+int widget_has(const char *filename)
+{
+ if (!validsubpath(filename)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0 == faccessat(rootfd, filename, F_OK, 0);
+}
+
+int widget_open_read(const char *filename)
+{
+ if (!validsubpath(filename)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return openat(rootfd, filename, O_RDONLY);
+}
+
diff --git a/src/wgt-strings.c b/src/wgt-strings.c
new file mode 100644
index 0000000..a2fff39
--- /dev/null
+++ b/src/wgt-strings.c
@@ -0,0 +1,31 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "wgt.h"
+
+const char _config_xml_[] = "config.xml";
+const char _name_[] = "name";
+const char _description_[] = "description";
+const char _author_[] = "author";
+const char _license_[] = "license";
+const char _icon_[] = "icon";
+const char _content_[] = "content";
+const char _feature_[] = "feature";
+const char _preference_[] = "preference";
+const char _width_[] = "width";
+const char _height_[] = "height";
+
+
diff --git a/src/wgt.h b/src/wgt.h
new file mode 100644
index 0000000..d95e65b
--- /dev/null
+++ b/src/wgt.h
@@ -0,0 +1,69 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+
+#include <libxml/tree.h>
+#include "config.h"
+
+
+/**************************************************************/
+/* from wgt-config-xml */
+
+extern int confixml_open();
+extern void confixml_close();
+extern xmlNodePtr confixml_name();
+extern xmlNodePtr confixml_description();
+extern xmlNodePtr confixml_license();
+extern xmlNodePtr confixml_author();
+extern xmlNodePtr confixml_content();
+extern xmlNodePtr confixml_icon(int width, int height);
+extern xmlNodePtr confixml_first_feature();
+extern xmlNodePtr confixml_next_feature(xmlNodePtr node);
+extern xmlNodePtr confixml_first_preference();
+extern xmlNodePtr confixml_next_preference(xmlNodePtr node);
+extern xmlNodePtr confixml_first_icon();
+extern xmlNodePtr confixml_next_icon(xmlNodePtr node);
+
+/**************************************************************/
+/* from wgt-locales */
+
+extern void locales_reset();
+extern int locales_add(const char *locstr);
+extern int locales_score(const char *lang);
+extern char *locales_locate_file(const char *filename);
+
+/**************************************************************/
+/* from wgt-rootdir */
+
+extern int widget_set_rootdir(const char *pathname);
+extern int widget_has(const char *filename);
+extern int widget_open_read(const char *filename);
+
+/**************************************************************/
+/* from wgt-strings */
+
+extern const char _config_xml_[];
+extern const char _name_[];
+extern const char _description_[];
+extern const char _author_[];
+extern const char _license_[];
+extern const char _icon_[];
+extern const char _content_[];
+extern const char _feature_[];
+extern const char _preference_[];
+extern const char _width_[];
+extern const char _height_[];
+
diff --git a/src/wgtpkg-base64.c b/src/wgtpkg-base64.c
new file mode 100644
index 0000000..5cf6f8a
--- /dev/null
+++ b/src/wgtpkg-base64.c
@@ -0,0 +1,203 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "wgtpkg.h"
+
+static char tob64(char x)
+{
+ if (x < 26)
+ return 'A' + x;
+ if (x < 52)
+ return 'a' + x - 26;
+ if (x < 62)
+ return '0' + x - 52;
+ return x == 62 ? '+' : '/';
+}
+
+char *base64encw(const char *buffer, int length, int width)
+{
+ int remain, in, out;
+ char *result;
+
+ if (width == 0 || width % 4) {
+ syslog(LOG_ERR, "bad width in base64enc");
+ return NULL;
+ }
+ result = malloc(2 + 4 * ((length + 2) / 3) + (length / width));
+ if (result == NULL) {
+ syslog(LOG_ERR, "malloc failed in base64enc");
+ return NULL;
+ }
+ in = out = 0;
+ remain = length;
+ while (remain >= 3) {
+ if (out % (width + 1) == width)
+ result[out++] = '\n';
+ result[out] = tob64((buffer[in] >> 2) & '\x3f');
+ result[out+1] = tob64(((buffer[in] << 4) & '\x30') | ((buffer[in+1] >> 4) & '\x0f'));
+ result[out+2] = tob64(((buffer[in+1] << 2) & '\x3c') | ((buffer[in+2] >> 6) & '\x03'));
+ result[out+3] = tob64(buffer[in+2] & '\x3f');
+ remain -= 3;
+ in += 3;
+ out += 4;
+ }
+ if (remain != 0) {
+ if (out % (width + 1) == width)
+ result[out++] = '\n';
+ result[out] = tob64((buffer[in] >> 2) & '\x3f');
+ if (remain == 1) {
+ result[out+1] = tob64((buffer[in] << 4) & '\x30');
+ result[out+2] = '=';
+ } else {
+ result[out+1] = tob64(((buffer[in] << 4) & '\x30') | ((buffer[in+1] >> 4) & '\x0f'));
+ result[out+2] = tob64((buffer[in+1] << 2) & '\x3c');
+ }
+ result[out+3] = '=';
+ out += 4;
+ }
+ result[out] = 0;
+ return result;
+}
+
+char *base64enc(const char *buffer, int length)
+{
+ return base64encw(buffer, length, 76);
+}
+
+static char fromb64(char x)
+{
+ if ('A' <= x && x <= 'Z')
+ return x - 'A';
+ if ('a' <= x && x <= 'z')
+ return x - 'a' + 26;
+ if ('0' <= x && x <= '9')
+ return x - '0' + 52;
+ if (x == '+')
+ return 62;
+ if (x == '/')
+ return 63;
+ if (x == '=')
+ return '@';
+ return 'E';
+}
+
+int base64dec(const char *buffer, char **output)
+{
+ int len, in, out;
+ char *result;
+ unsigned char x0, x1, x2, x3;
+
+ len = strlen(buffer);
+ result = malloc(3 * ((3 + len) / 4));
+ if (result == NULL) {
+ syslog(LOG_ERR, "malloc failed in base64dec");
+ return -1;
+ }
+ in = out = 0;
+ while (buffer[in] == '\r' || buffer[in] == '\n')
+ in++;
+ while (buffer[in]) {
+ if (in + 4 > len) {
+ syslog(LOG_ERR, "unexpected input size in base64dec");
+ free(result);
+ return -1;
+ }
+ x0 = (unsigned char)fromb64(buffer[in]);
+ x1 = (unsigned char)fromb64(buffer[in+1]);
+ x2 = (unsigned char)fromb64(buffer[in+2]);
+ x3 = (unsigned char)fromb64(buffer[in+3]);
+ in += 4;
+ if (x0 == 'E' || x1 == 'E' || x2 == 'E' || x3 == 'E') {
+ syslog(LOG_ERR, "unexpected input character in base64dec");
+ free(result);
+ return -1;
+ }
+ if (x0 == '@' || x1 == '@' || (x2 == '@' && x3 != '@')) {
+ syslog(LOG_ERR, "unexpected termination character in base64dec");
+ free(result);
+ return -1;
+ }
+ result[out] = (char)((x0 << 2) | (x1 >> 4));
+ result[out+1] = (char)((x1 << 4) | ((x2 >> 2) & 15));
+ result[out+2] = (char)((x2 << 6) | (x3 & 63));
+ while (buffer[in] == '\r' || buffer[in] == '\n')
+ in++;
+ if (x3 != '@')
+ out += 3;
+ else if (!buffer[in])
+ out += 1 + (x2 != '@');
+ else {
+ syslog(LOG_ERR, "unexpected continuation in base64dec");
+ free(result);
+ return -1;
+ }
+ }
+ *output = result;
+ return out;
+}
+
+int base64eq(const char *buf1, const char *buf2)
+{
+ for(;;) {
+ while(*buf1 == '\n' || *buf1 == '\r')
+ buf1++;
+ while(*buf2 == '\n' || *buf2 == '\r')
+ buf2++;
+ if (*buf1 != *buf2)
+ return 0;
+ if (!*buf1)
+ return 1;
+ buf1++;
+ buf2++;
+ }
+}
+
+#ifdef TESTBASE64
+#include <fcntl.h>
+#include <string.h>
+
+int main(int ac, char **av)
+{
+ char buffer[32768];
+ int fd, l0, l1, l2;
+ char *p1, *p2;
+
+ while(*++av) {
+ fd = open(*av, O_RDONLY);
+ if (fd < 0) continue;
+ l0 = read(fd, buffer, sizeof buffer);
+ if (l0 <= 0) continue;
+ close(fd);
+ p1 = base64enc(buffer, l0);
+ if (!p1) continue;
+ l1 = strlen(p1);
+ l2 = base64dec(p1, &p2);
+ if (l2 <= 0) continue;
+printf("[[[%.*s]]]\n",l2,p2);
+ if (l0 != l2) printf("length mismatch\n");
+ else if (memcmp(buffer, p2, l0)) printf("content mismatch\n");
+ free(p1);
+ free(p2);
+ }
+ return 0;
+}
+
+#endif
diff --git a/src/wgtpkg-certs.c b/src/wgtpkg-certs.c
new file mode 100644
index 0000000..1d8b976
--- /dev/null
+++ b/src/wgtpkg-certs.c
@@ -0,0 +1,80 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+
+#include <syslog.h>
+#include <openssl/x509.h>
+
+#include "wgtpkg.h"
+
+struct x509l {
+ int count;
+ X509 **certs;
+};
+
+static struct x509l certificates = { .count = 0, .certs = NULL };
+
+static int add_certificate_x509(X509 *x)
+{
+ X509 **p = realloc(certificates.certs, (certificates.count + 1) * sizeof(X509*));
+ if (!p) {
+ syslog(LOG_ERR, "reallocation failed for certificate");
+ return -1;
+ }
+ certificates.certs = p;
+ p[certificates.count++] = x;
+ return 0;
+}
+
+static int add_certificate_bin(const char *bin, int len)
+{
+ int rc;
+ const char *b, *e;
+ b = bin;
+ e = bin + len;
+ while (len) {
+ X509 *x = d2i_X509(NULL, (const unsigned char **)&b, e-b);
+ if (x == NULL) {
+ syslog(LOG_ERR, "d2i_X509 failed");
+ return -1;
+ }
+ rc = add_certificate_x509(x);
+ if (rc) {
+ X509_free(x);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+int add_certificate_b64(const char *b64)
+{
+ char *d;
+ int l = base64dec(b64, &d);
+ if (l > 0) {
+ l = add_certificate_bin(d, l);
+ free(d);
+ }
+ return l;
+}
+
+void clear_certificates()
+{
+ while(certificates.count)
+ X509_free(certificates.certs[--certificates.count]);
+}
+
+
diff --git a/src/wgtpkg-digsig.c b/src/wgtpkg-digsig.c
new file mode 100644
index 0000000..284acd1
--- /dev/null
+++ b/src/wgtpkg-digsig.c
@@ -0,0 +1,358 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+
+#include <string.h>
+#include <syslog.h>
+#include <assert.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/uri.h>
+
+
+#include "wgtpkg.h"
+
+
+
+static const char uri_role_author[] = "http://www.w3.org/ns/widgets-digsig#role-author";
+static const char uri_role_distributor[] = "http://www.w3.org/ns/widgets-digsig#role-distributor";
+static const char uri_profile[] = "http://www.w3.org/ns/widgets-digsig#profile";
+
+
+/* global data */
+static xmlDocPtr document; /* the document */
+
+/* facility to get the first element node (skip text nodes) starting with 'node' */
+static xmlNodePtr next_element(xmlNodePtr node)
+{
+ while (node && node->type != XML_ELEMENT_NODE)
+ node = node->next;
+ return node;
+}
+
+/* is the 'node' an element node of 'name'? */
+static int is_element(xmlNodePtr node, const char *name)
+{
+ return node->type == XML_ELEMENT_NODE
+ && !strcmp(name, node->name);
+}
+
+/* is the 'node' an element node of 'name'? */
+static int is_node(xmlNodePtr node, const char *name)
+{
+ return node != NULL && is_element(node, name);
+}
+
+#if 0
+/* facility to get the first element node (skip text nodes) starting with 'node' */
+static xmlNodePtr next_element_type(xmlNodePtr node, const char *name)
+{
+ while (node && node->type != XML_ELEMENT_NODE && strcmp(name, node->name))
+ node = node->next;
+ return node;
+}
+
+/* search the element node of id. NOTE : not optimized at all */
+static xmlNodePtr search_for(const char *attrname, const char *value)
+{
+ char *val;
+ xmlNodePtr iter, next;
+ xmlNodePtr result;
+
+ result = NULL;
+ iter = xmlDocGetRootElement(document);
+ while (iter != NULL) {
+ val = xmlGetProp(iter, attrname);
+ if (val != NULL && !strcmp(val, value)) {
+ if (result != NULL) {
+ syslog(LOG_ERR, "duplicated %s %s", attrname, value);
+ free(val);
+ return NULL;
+ }
+ result = iter;
+ }
+ free(val);
+ next = next_element(iter->children);
+ if (next == NULL) {
+ /* no child, try sibling */
+ next = next_element(iter->next);
+ if (next == NULL) {
+ iter = iter->parent;
+ while (iter != NULL && next == NULL) {
+ next = next_element(iter->next);
+ iter = iter->parent;
+ }
+ }
+ }
+ iter = next;
+ }
+ if (result == NULL)
+ syslog(LOG_ERR, "node of %s '%s' not found", attrname, value);
+ return result;
+}
+
+/* search the element node of id. NOTE : not optimized at all */
+static xmlNodePtr search_id(const char *id)
+{
+ return search_for("Id", id);
+}
+#endif
+
+/* check the digest of one element */
+static int check_one_reference(xmlNodePtr ref)
+{
+ int rc;
+ char *uri;
+ xmlURIPtr u;
+ struct filedesc *fdesc;
+
+ /* start */
+ rc = -1;
+
+ /* get the uri */
+ uri = xmlGetProp(ref, "URI");
+ if (uri == NULL) {
+ syslog(LOG_ERR, "attribute URI of element <Reference> not found");
+ goto error;
+ }
+
+ /* parse the uri */
+ u = xmlParseURI(uri);
+ if (!u) {
+ syslog(LOG_ERR, "error while parsing URI %s", uri);
+ goto error2;
+ }
+
+ /* check that unexpected parts are not there */
+ if (u->scheme || u->opaque || u->authority || u->server || u->user || u->query) {
+ syslog(LOG_ERR, "unexpected uri component in %s", uri);
+ goto error3;
+ }
+
+ /* check path and fragment */
+ if (!u->path && !u->fragment) {
+ syslog(LOG_ERR, "invalid uri %s", uri);
+ goto error3;
+ }
+ if (u->path && u->fragment) {
+ syslog(LOG_ERR, "not allowed to sign foreign fragment in %s", uri);
+ goto error3;
+ }
+
+ if (u->path) {
+ /* check that the path is valid */
+ fdesc = file_of_name(u->path);
+ if (fdesc == NULL) {
+ syslog(LOG_ERR, "reference to unknown file %s", u->path);
+ goto error3;
+ }
+ if (fdesc->type != type_file) {
+ syslog(LOG_ERR, "reference to directory %s", u->path);
+ goto error3;
+ }
+ if ((fdesc->flags & flag_distributor_signature) != 0) {
+ syslog(LOG_ERR, "reference to signature %s", u->path);
+ goto error3;
+ }
+ fdesc->flags |= flag_referenced;
+ rc = 0;
+ } else {
+ rc = 0;
+ }
+
+error3:
+ xmlFreeURI(u);
+error2:
+ xmlFree(uri);
+error:
+ return rc;
+}
+
+static int check_references(xmlNodePtr sinfo)
+{
+ xmlNodePtr elem;
+
+ elem = sinfo->children;
+ while (elem != NULL) {
+ if (is_element(elem, "Reference"))
+ if (check_one_reference(elem))
+ return -1;
+ elem = elem->next;
+ }
+ return 0;
+}
+
+static int get_certificates(xmlNodePtr kinfo)
+{
+ xmlNodePtr n1, n2;
+ char *b;
+ int rc;
+
+ n1 = kinfo->children;
+ while (n1) {
+ if (is_element(n1, "X509Data")) {
+ n2 = n1->children;
+ while (n2) {
+ if (is_element(n2, "X509Certificate")) {
+ b = xmlNodeGetContent(n2);
+ if (b == NULL) {
+ syslog(LOG_ERR, "xmlNodeGetContent of X509Certificate failed");
+ return -1;
+ }
+ rc = add_certificate_b64(b);
+ xmlFree(b);
+ if (rc)
+ return rc;
+ }
+ n2 = n2->next;
+ }
+ }
+ n1 = n1->next;
+ }
+ return 0;
+}
+
+/* checks the current document */
+static int checkdocument()
+{
+ int rc;
+ xmlNodePtr sinfo, svalue, kinfo, objs, rootsig;
+
+ rc = -1;
+
+ rootsig = xmlDocGetRootElement(document);
+ if (!is_node(rootsig, "Signature")) {
+ syslog(LOG_ERR, "root element <Signature> not found");
+ goto error;
+ }
+
+ sinfo = next_element(rootsig->children);
+ if (!is_node(sinfo, "SignedInfo")) {
+ syslog(LOG_ERR, "element <SignedInfo> not found");
+ goto error;
+ }
+
+ svalue = next_element(sinfo->next);
+ if (!is_node(svalue, "SignatureValue")) {
+ syslog(LOG_ERR, "element <SignatureValue> not found");
+ goto error;
+ }
+
+ kinfo = next_element(svalue->next);
+ if (is_node(kinfo, "KeyInfo")) {
+ objs = kinfo->next;
+ } else {
+ objs = kinfo;
+ kinfo = NULL;
+ }
+
+ rc = check_references(sinfo);
+ if (rc)
+ goto error;
+
+ rc = xmlsec_verify(rootsig);
+ if (rc)
+ goto error;
+
+ rc = get_certificates(kinfo);
+
+error:
+ return rc;
+}
+
+/* verify the digital signature of the file described by 'fdesc' */
+int verify_digsig(struct filedesc *fdesc)
+{
+ int res;
+
+ assert ((fdesc->flags & flag_signature) != 0);
+ debug("-- checking file %s",fdesc->name);
+
+ /* reset the flags */
+ file_clear_flags();
+ clear_certificates();
+
+ /* reads and xml parses the signature file */
+ document = xmlReadFile(fdesc->name, NULL, 0);
+ if (document == NULL) {
+ syslog(LOG_ERR, "xml parse of file %s failed", fdesc->name);
+ return -1;
+ }
+
+ res = checkdocument();
+ if (res)
+ syslog(LOG_ERR, "previous error was during check of file %s", fdesc->name);
+
+ xmlFreeDoc(document);
+ return res;
+}
+
+/* check all the signature files */
+int check_all_signatures()
+{
+ int rc, irc;
+ unsigned int i, n;
+ struct filedesc *fdesc;
+
+ n = signature_count();
+ rc = 0;
+ for (i = n ; i-- > 0 ; ) {
+ fdesc = signature_of_index(i);
+ irc = verify_digsig(fdesc);
+ if (!irc)
+ rc = irc;
+ }
+
+ return rc;
+}
+
+/* create a signature of 'index' (0 for author, other values for distributors)
+using the private 'key' (filename) and the certificates 'certs' (filenames)
+as trusted chain */
+int create_digsig(int index, const char *key, const char **certs)
+{
+ struct filedesc *fdesc;
+ xmlDocPtr doc;
+ int rc, len;
+
+ rc = -1;
+
+ /* create the doc */
+ doc = xmlsec_create(index, key, certs);
+ if (doc == NULL)
+ goto error;
+
+ /* instanciate the filename */
+ fdesc = create_signature(index);
+ if (fdesc == NULL)
+ goto error2;
+
+ /* save the doc as file */
+ len = xmlSaveFormatFileEnc(fdesc->name, doc, NULL, 0);
+ if (len < 0) {
+ syslog(LOG_ERR, "xmlSaveFormatFileEnc to %s failed", fdesc->name);
+ goto error2;
+ }
+
+ rc = 0;
+error2:
+ xmlFreeDoc(doc);
+error:
+ return rc;
+}
+
+
diff --git a/src/wgtpkg-files.c b/src/wgtpkg-files.c
new file mode 100644
index 0000000..06aac83
--- /dev/null
+++ b/src/wgtpkg-files.c
@@ -0,0 +1,313 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <syslog.h>
+#include <dirent.h>
+#include <stdio.h>
+
+#include "wgtpkg.h"
+
+struct fdb {
+ unsigned int count;
+ struct filedesc **files;
+};
+
+static struct fdb allfiles = { .count = 0, .files = NULL };
+static struct fdb allsignatures = { .count = 0, .files = NULL };
+
+static const char author_file[] = "author-signature.xml";
+static const char distributor_file_prefix[] = "signature";
+static const char distributor_file_suffix[] = ".xml";
+
+static unsigned int what_signature(const char *name)
+{
+ unsigned int len, id, nid;
+
+ if (!strcmp(name, author_file))
+ return UINT_MAX;
+
+ len = sizeof(distributor_file_prefix)-1;
+ if (memcmp(name, distributor_file_prefix, len))
+ return 0;
+ if (name[len] <= '0' || name[len] > '9')
+ return 0;
+ id = (unsigned int)(name[len++] - '0');
+ while ('0' <= name[len] && name[len] <= '9') {
+ nid = 10 * id + (unsigned int)(name[len++] - '0');
+ if (nid < id || nid == UINT_MAX) {
+ syslog(LOG_WARNING, "number too big for %s", name);
+ return 0;
+ }
+ id = nid;
+ }
+ if (strcmp(name+len, distributor_file_suffix))
+ return 0;
+
+ return id;
+}
+
+static struct filedesc *get_filedesc(const char *name, int create)
+{
+ int cmp;
+ unsigned int low, up, mid, sig;
+ struct filedesc *result, **grow;
+
+ /* search */
+ low = 0;
+ up = allfiles.count;
+ while(low < up) {
+ mid = (low + up) >> 1;
+ result = allfiles.files[mid];
+ cmp = strcmp(result->name, name);
+ if (!cmp)
+ return result; /* found */
+ if (cmp > 0)
+ up = mid;
+ else
+ low = mid + 1;
+ }
+
+ /* not found, can create ? */
+ if (!create)
+ return NULL;
+
+ sig = what_signature(name);
+
+ /* allocations */
+ grow = realloc(allfiles.files, (allfiles.count + 1) * sizeof(struct filedesc *));
+ if (grow == NULL) {
+ syslog(LOG_ERR, "realloc failed in get_filedesc");
+ return NULL;
+ }
+ allfiles.files = grow;
+
+ if (sig) {
+ grow = realloc(allsignatures.files, (allsignatures.count + 1) * sizeof(struct filedesc *));
+ if (grow == NULL) {
+ syslog(LOG_ERR, "second realloc failed in get_filedesc");
+ return NULL;
+ }
+ allsignatures.files = grow;
+ }
+
+ result = malloc(sizeof(struct filedesc) + strlen(name));
+ if (!result) {
+ syslog(LOG_ERR, "calloc failed in get_filedesc");
+ return NULL;
+ }
+
+ /* initialisation */
+ result->type = type_unset;
+ result->flags = sig == 0 ? 0 : sig == UINT_MAX ? flag_author_signature : flag_distributor_signature;
+ result->zindex = 0;
+ result->signum = sig;
+ strcpy(result->name, name);
+
+ /* insertion */
+ if (low < allfiles.count)
+ memmove(allfiles.files+low+1, allfiles.files+low, (allfiles.count - low) * sizeof(struct filedesc *));
+ allfiles.files[low] = result;
+ allfiles.count++;
+ if (sig) {
+ for (low = 0 ; low < allsignatures.count && sig > allsignatures.files[low]->signum ; low++);
+ if (low < allsignatures.count)
+ memmove(allsignatures.files+low+1, allsignatures.files+low, (allsignatures.count - low) * sizeof(struct filedesc *));
+ allsignatures.files[low] = result;
+ allsignatures.count++;
+ }
+
+ return result;
+}
+
+
+static struct filedesc *file_add(const char *name, enum entrytype type)
+{
+ struct filedesc *desc;
+
+ desc = get_filedesc(name, 1);
+ if (!desc)
+ errno = ENOMEM;
+ else if (desc->type == type_unset)
+ desc->type = type;
+ else {
+ syslog(LOG_ERR, "redeclaration of %s in file_add", name);
+ errno = EEXIST;
+ desc = NULL;
+ }
+ return desc;
+}
+
+void file_reset()
+{
+ unsigned int i;
+
+ allsignatures.count = 0;
+ for (i = 0 ; i < allfiles.count ; i++)
+ free(allfiles.files[i]);
+ allfiles.count = 0;
+}
+
+unsigned int file_count()
+{
+ return allfiles.count;
+}
+
+struct filedesc *file_of_index(unsigned int index)
+{
+ assert(index < allfiles.count);
+ return allfiles.files[index];
+}
+
+struct filedesc *file_of_name(const char *name)
+{
+ return get_filedesc(name, 0);
+}
+
+struct filedesc *file_add_directory(const char *name)
+{
+ return file_add(name, type_directory);
+}
+
+struct filedesc *file_add_file(const char *name)
+{
+ return file_add(name, type_file);
+}
+
+unsigned int signature_count()
+{
+ return allsignatures.count;
+}
+
+struct filedesc *signature_of_index(unsigned int index)
+{
+ assert(index < allsignatures.count);
+ return allsignatures.files[index];
+}
+
+struct filedesc *get_signature(unsigned int number)
+{
+ unsigned int idx;
+
+ if (number == 0)
+ number = UINT_MAX;
+ for (idx = 0 ; idx < allsignatures.count ; idx++)
+ if (allsignatures.files[idx]->signum == number)
+ return allsignatures.files[idx];
+ return NULL;
+}
+
+struct filedesc *create_signature(unsigned int number)
+{
+ struct filedesc *result;
+ char *name;
+ int len;
+
+ result = NULL;
+ if (number == 0 || number == UINT_MAX)
+ len = asprintf(&name, "%s", author_file);
+ else
+ len = asprintf(&name, "%s%u%s", distributor_file_prefix, number, distributor_file_suffix);
+
+ if (len < 0)
+ syslog(LOG_ERR, "asprintf failed in create_signature");
+ else {
+ assert(len > 0);
+ result = file_of_name(name);
+ if (result == NULL)
+ result = file_add_file(name);
+ free(name);
+ }
+
+ return result;
+}
+
+/* remove flags that are not related to being signature */
+void file_clear_flags()
+{
+ unsigned int i;
+ for (i = 0 ; i < allfiles.count ; i++)
+ allfiles.files[i]->flags &= flag_signature;
+}
+
+static int fill_files_rec(char name[PATH_MAX], int offset)
+{
+ int len, err;
+ DIR *dir;
+ struct dirent *ent;
+
+ if (offset == 0)
+ dir = opendir(".");
+ else {
+ dir = opendir(name);
+ name[offset++] = '/';
+ }
+ if (!dir) {
+ syslog(LOG_ERR, "opendir %.*s failed in zwr", offset, name);
+ return -1;
+ }
+
+ ent = readdir(dir);
+ while (ent != NULL) {
+ len = strlen(ent->d_name);
+ if (ent->d_name[0] == '.' && (len == 1 ||
+ (ent->d_name[1] == '.' && len == 2)))
+ ;
+ else if (offset + len >= PATH_MAX) {
+ closedir(dir);
+ syslog(LOG_ERR, "name too long in fill_files_rec");
+ errno = ENAMETOOLONG;
+ return -1;
+ } else {
+ memcpy(name + offset, ent->d_name, 1+len);
+ switch (ent->d_type) {
+ case DT_DIR:
+ if (file_add_directory(name) == NULL) {
+ closedir(dir);
+ return -1;
+ }
+ err = fill_files_rec(name, offset + len);
+ if (err) {
+ closedir(dir);
+ return err;
+ }
+ break;
+ case DT_REG:
+ if (file_add_file(name) == NULL) {
+ closedir(dir);
+ return -1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ ent = readdir(dir);
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+int fill_files()
+{
+ char name[PATH_MAX];
+ return fill_files_rec(name, 0);
+}
+
diff --git a/src/wgtpkg-install.c b/src/wgtpkg-install.c
new file mode 100644
index 0000000..7a88ebf
--- /dev/null
+++ b/src/wgtpkg-install.c
@@ -0,0 +1,84 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#define _BSD_SOURCE /* see readdir */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include "wgtpkg.h"
+
+/* install the widget of the file */
+static void install(const char *wgtfile)
+{
+ notice("-- INSTALLING widget %s", wgtfile);
+
+ if (enter_workdir(1))
+ goto error;
+
+ if (zread(wgtfile, 0))
+ goto error;
+
+ if (check_all_signatures())
+ goto error;
+
+ return;
+
+error:
+ return;
+ exit(1);
+}
+
+/* install the widgets of the list */
+int main(int ac, char **av)
+{
+ int i, kwd;
+
+ openlog("wgtpkg-install", LOG_PERROR, LOG_AUTH);
+
+ xmlsec_init();
+
+ ac = verbose_scan_args(ac, av);
+
+ /* canonic names for files */
+ for (i = 1 ; av[i] != NULL ; i++)
+ if ((av[i] = realpath(av[i], NULL)) == NULL) {
+ syslog(LOG_ERR, "error while getting realpath of %dth argument", i);
+ return 1;
+ }
+
+ /* workdir */
+ kwd = 1;
+ if (make_workdir(kwd)) {
+ syslog(LOG_ERR, "failed to create a working directory");
+ return 1;
+ }
+ if (!kwd)
+ atexit(remove_workdir);
+
+ /* install widgets */
+ for (av++ ; *av ; av++)
+ install(*av);
+
+ exit(0);
+ return 0;
+}
+
diff --git a/src/wgtpkg-pack.c b/src/wgtpkg-pack.c
new file mode 100644
index 0000000..a8aaa05
--- /dev/null
+++ b/src/wgtpkg-pack.c
@@ -0,0 +1,167 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "wgtpkg.h"
+
+#if !defined(MAXCERT)
+#define MAXCERT 20
+#endif
+#if !defined(DEFAULT_KEY_FILE)
+#define DEFAULT_KEY_FILE "key.pem"
+#endif
+#if !defined(DEFAULT_CERT_FILE)
+#define DEFAULT_CERT_FILE "cert.pem"
+#endif
+
+const char appname[] = "wgtpkg-pack";
+
+static void usage()
+{
+ printf(
+ "usage: %s [-f] [-o wgtfile] directory\n"
+ "\n"
+ " -o wgtfile the output widget file\n"
+ " -f force overwriting\n"
+ " -q quiet\n"
+ " -v verbose\n"
+ "\n",
+ appname
+ );
+}
+
+static struct option options[] = {
+ { "output", required_argument, NULL, 'o' },
+ { "force", no_argument, NULL, 'f' },
+ { "help", no_argument, NULL, 'h' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "verbose", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+};
+
+/* install the widgets of the list */
+int main(int ac, char **av)
+{
+ int i, force;
+ char *wgtfile, *directory, *x;
+ struct stat s;
+
+ openlog(appname, LOG_PERROR, LOG_USER);
+
+ force = 0;
+ wgtfile = directory = NULL;
+ for (;;) {
+ i = getopt_long(ac, av, "qvhfo:", options, NULL);
+ if (i < 0)
+ break;
+ switch (i) {
+ case 'o':
+ wgtfile = optarg;
+ break;
+ case 'q':
+ if (verbosity)
+ verbosity--;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 'h':
+ usage();
+ return 0;
+ case ':':
+ syslog(LOG_ERR, "missing argument");
+ return 1;
+ default:
+ syslog(LOG_ERR, "unrecognized option");
+ return 1;
+ }
+ }
+
+ /* remaining arguments and final checks */
+ if (optind >= ac) {
+ syslog(LOG_ERR, "no directory set");
+ return 1;
+ }
+ directory = av[optind++];
+ if (optind < ac) {
+ syslog(LOG_ERR, "extra parameters found");
+ return 1;
+ }
+
+ /* set default values */
+ if (wgtfile == NULL && 0 > asprintf(&wgtfile, "%s.wgt", directory)) {
+ syslog(LOG_ERR, "asprintf failed");
+ return 1;
+ }
+
+ /* check values */
+ if (stat(directory, &s)) {
+ syslog(LOG_ERR, "can't find directory %s", directory);
+ return 1;
+ }
+ if (!S_ISDIR(s.st_mode)) {
+ syslog(LOG_ERR, "%s isn't a directory", directory);
+ return 1;
+ }
+ if (access(wgtfile, F_OK) == 0 && force == 0) {
+ syslog(LOG_ERR, "can't overwrite existing %s", wgtfile);
+ return 1;
+ }
+
+ notice("-- PACKING widget %s from directory %s", wgtfile, directory);
+
+ /* creates an existing widget (for realpath it must exist) */
+ i = open(wgtfile, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666);
+ if (i < 0) {
+ syslog(LOG_ERR, "can't write widget %s", wgtfile);
+ return 1;
+ }
+ close(i);
+
+ /* compute absolutes paths */
+ x = realpath(wgtfile, NULL);
+ if (x == NULL) {
+ syslog(LOG_ERR, "realpath failed for %s",wgtfile);
+ return 1;
+ }
+ wgtfile = x;
+
+ /* set and enter the workdir */
+ if (set_workdir(directory, 0) || enter_workdir(0))
+ return 1;
+
+
+ if (fill_files())
+ return 1;
+
+ return !!zwrite(wgtfile);
+}
+
+
diff --git a/src/wgtpkg-sign.c b/src/wgtpkg-sign.c
new file mode 100644
index 0000000..cd506fc
--- /dev/null
+++ b/src/wgtpkg-sign.c
@@ -0,0 +1,199 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "wgtpkg.h"
+
+#if !defined(MAXCERT)
+#define MAXCERT 20
+#endif
+#if !defined(DEFAULT_KEY_FILE)
+#define DEFAULT_KEY_FILE "key.pem"
+#endif
+#if !defined(DEFAULT_CERT_FILE)
+#define DEFAULT_CERT_FILE "cert.pem"
+#endif
+
+const char appname[] = "wgtpkg-sign";
+
+static unsigned int get_number(const char *value)
+{
+ char *end;
+ unsigned long int val;
+
+ val = strtoul(value, &end, 10);
+ if (*end || 0 == val || val >= UINT_MAX || *value == '-') {
+ syslog(LOG_ERR, "bad number value %s", value);
+ exit(1);
+ }
+ return (unsigned int)val;
+}
+
+static void usage()
+{
+ printf(
+ "usage: %s [-f] [-k keyfile] [-c certfile]... [-o wgtfile] [-d number | -a] directory\n"
+ "\n"
+ " -k keyfile the private key to use for author signing\n"
+ " -c certfile the certificate(s) to use for author signing\n"
+ " -d number the number of the distributor signature (zero for automatic)\n"
+ " -a the author signature\n"
+ " -f force overwriting\n"
+ " -q quiet\n"
+ " -v verbose\n"
+ "\n",
+ appname
+ );
+}
+
+static struct option options[] = {
+ { "key", required_argument, NULL, 'k' },
+ { "certificate", required_argument, NULL, 'c' },
+ { "distributor", required_argument, NULL, 'd' },
+ { "author", no_argument, NULL, 'a' },
+ { "force", no_argument, NULL, 'f' },
+ { "help", no_argument, NULL, 'h' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "verbose", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+};
+
+/* install the widgets of the list */
+int main(int ac, char **av)
+{
+ int i, force, ncert, author;
+ unsigned int number;
+ char *keyfile, *certfiles[MAXCERT+1], *directory, **x;
+ struct stat s;
+
+ openlog(appname, LOG_PERROR, LOG_USER);
+
+ force = ncert = author = 0;
+ number = UINT_MAX;
+ keyfile = directory = NULL;
+ for (;;) {
+ i = getopt_long(ac, av, "hfak:c:d:", options, NULL);
+ if (i < 0)
+ break;
+ switch (i) {
+ case 'c':
+ if (ncert == MAXCERT) {
+ syslog(LOG_ERR, "maximum count of certificates reached");
+ return 1;
+ }
+ certfiles[ncert++] = optarg;
+ continue;
+ case 'k': x = &keyfile; break;
+ case 'd': number = get_number(optarg); continue;
+ case 'f': force = 1; continue;
+ case 'a': author = 1; continue;
+ case 'h': usage(); return 0;
+ case ':':
+ syslog(LOG_ERR, "missing argument");
+ return 1;
+ default:
+ syslog(LOG_ERR, "unrecognized option");
+ return 1;
+ }
+ if (*x != NULL) {
+ syslog(LOG_ERR, "option set twice");
+ return 1;
+ }
+ *x = optarg;
+ }
+
+ /* remaining arguments and final checks */
+ if (optind >= ac) {
+ syslog(LOG_ERR, "no directory set");
+ return 1;
+ }
+ directory = av[optind++];
+ if (optind < ac) {
+ syslog(LOG_ERR, "extra parameters found");
+ return 1;
+ }
+
+ /* set default values */
+ if (keyfile == NULL)
+ keyfile = DEFAULT_KEY_FILE;
+ if (ncert == 0)
+ certfiles[ncert++] = DEFAULT_CERT_FILE;
+
+ /* check values */
+ if (stat(directory, &s)) {
+ syslog(LOG_ERR, "can't find directory %s", directory);
+ return 1;
+ }
+ if (!S_ISDIR(s.st_mode)) {
+ syslog(LOG_ERR, "%s isn't a directory", directory);
+ return 1;
+ }
+ if (access(keyfile, R_OK) != 0) {
+ syslog(LOG_ERR, "can't access private key %s", keyfile);
+ return 1;
+ }
+ for(i = 0 ; i < ncert ; i++)
+ if (access(certfiles[i], R_OK) != 0) {
+ syslog(LOG_ERR, "can't access certificate %s", certfiles[i]);
+ return 1;
+ }
+
+ /* init xmlsec module */
+ if (xmlsec_init())
+ return 1;
+
+
+ /* compute absolutes paths */
+#define rp(x) do { char *p = realpath(x, NULL); if (p != NULL) x = p; else { syslog(LOG_ERR, "realpath failed for %s",x); return 1; } } while(0)
+ rp(keyfile);
+ for(i = 0 ; i < ncert ; i++)
+ rp(certfiles[i]);
+#undef rp
+
+ /* set and enter the workdir */
+ if (set_workdir(directory, 0) || enter_workdir(0))
+ return 1;
+
+ if (fill_files())
+ return 1;
+
+ if (author)
+ number = 0;
+ else if (number == UINT_MAX)
+ for (number = 1; get_signature(number) != NULL ; number++);
+
+ if (!force && get_signature(number) != NULL) {
+ syslog(LOG_ERR, "can't overwrite existing signature %s", get_signature(number)->name);
+ return 1;
+ }
+
+ notice("-- SIGNING content of directory %s for number %u", directory, number);
+
+ certfiles[ncert] = NULL;
+ return !!create_digsig(number, keyfile, (const char**)certfiles);
+}
+
diff --git a/src/wgtpkg-verbose.c b/src/wgtpkg-verbose.c
new file mode 100644
index 0000000..1472a90
--- /dev/null
+++ b/src/wgtpkg-verbose.c
@@ -0,0 +1,36 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include <string.h>
+
+int verbosity = 1;
+
+int verbose_scan_args(int argc, char **argv)
+{
+ int i, r;
+ for (i=r=0 ; i < argc ; i++) {
+ if (!strcmp(argv[i], "-q"))
+ verbosity = verbosity ? verbosity-1 : 0;
+ else if (!strcmp(argv[i], "-v"))
+ verbosity++;
+ else
+ argv[r++] = argv[i];
+ }
+ argv[r] = NULL;
+ return r;
+}
+
+
diff --git a/src/wgtpkg-workdir.c b/src/wgtpkg-workdir.c
new file mode 100644
index 0000000..0c184a5
--- /dev/null
+++ b/src/wgtpkg-workdir.c
@@ -0,0 +1,166 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <syslog.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "wgtpkg.h"
+
+#ifndef PREDIR
+#define PREDIR "./"
+#endif
+
+static int mode = 0700;
+static char workdir[PATH_MAX];
+
+/* removes recursively the content of a directory */
+static int clean_dir()
+{
+ int cr;
+ DIR *dir;
+ struct dirent *ent;
+ struct {
+ struct dirent entry;
+ char spare[PATH_MAX];
+ } entry;
+
+ dir = opendir(".");
+ if (dir == NULL) {
+ syslog(LOG_ERR, "opendir failed in clean_dir");
+ return -1;
+ }
+
+ cr = -1;
+ for (;;) {
+ if (readdir_r(dir, &entry.entry, &ent) != 0) {
+ syslog(LOG_ERR, "readdir_r failed in clean_dir");
+ goto error;
+ }
+ if (ent == NULL)
+ break;
+ if (ent->d_name[0] == '.' && (ent->d_name[1] == 0
+ || (ent->d_name[1] == '.' && ent->d_name[2] == 0)))
+ continue;
+ cr = unlink(ent->d_name);
+ if (!cr)
+ continue;
+ if (errno != EISDIR) {
+ syslog(LOG_ERR, "unlink of %s failed in clean_dir", ent->d_name);
+ goto error;
+ }
+ if (chdir(ent->d_name)) {
+ syslog(LOG_ERR, "enter directory %s failed in clean_dir", ent->d_name);
+ goto error;
+ }
+ cr = clean_dir();
+ if (cr)
+ goto error;
+ if (chdir(".."))
+ goto error;
+ cr = rmdir(ent->d_name);
+ if (cr) {
+ syslog(LOG_ERR, "rmdir of %s failed in clean_dir", ent->d_name);
+ goto error;
+ }
+ }
+ cr = 0;
+error:
+ closedir(dir);
+ return cr;
+}
+
+/* removes the content of the working directory */
+int enter_workdir(int clean)
+{
+ int rc = chdir(workdir);
+ if (rc)
+ syslog(LOG_ERR, "entring workdir %s failed", workdir);
+ else if (clean)
+ rc = clean_dir();
+ return rc;
+}
+
+/* removes the working directory */
+void remove_workdir()
+{
+ enter_workdir(1);
+ chdir("..");
+ unlink(workdir);
+}
+
+int set_workdir(const char *name, int create)
+{
+ int rc;
+ size_t length;
+ struct stat s;
+
+ /* check the length */
+ length = strlen(name);
+ if (length >= sizeof workdir) {
+ syslog(LOG_ERR, "workdir name too long");
+ return -1;
+ }
+
+ rc = stat(name, &s);
+ if (rc) {
+ if (!create) {
+ syslog(LOG_ERR, "no workdir %s", name);
+ return -1;
+ }
+ rc = mkdir(name, mode);
+ if (rc) {
+ syslog(LOG_ERR, "can't create workdir %s", name);
+ return -1;
+ }
+
+ } else if (!S_ISDIR(s.st_mode)) {
+ syslog(LOG_ERR, "%s isn't a directory", name);
+ return -1;
+ }
+ memcpy(workdir, name, 1+length);
+ return 0;
+}
+
+/* install the widgets of the list */
+int make_workdir(int reuse)
+{
+ int i;
+
+ /* create a temporary directory */
+ for (i = 0 ; ; i++) {
+ if (i == INT_MAX) {
+ syslog(LOG_ERR, "exhaustion of workdirs");
+ return -1;
+ }
+ sprintf(workdir, PREDIR "PACK%d", i);
+ if (!mkdir(workdir, mode))
+ break;
+ if (errno != EEXIST) {
+ syslog(LOG_ERR, "error in creation of workdir %s: %m", workdir);
+ return -1;
+ }
+ if (reuse)
+ break;
+ }
+
+ return 0;
+}
+
diff --git a/src/wgtpkg-xmlsec.c b/src/wgtpkg-xmlsec.c
new file mode 100644
index 0000000..843ea2b
--- /dev/null
+++ b/src/wgtpkg-xmlsec.c
@@ -0,0 +1,371 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <string.h>
+#include <assert.h>
+
+#include <libxml/tree.h>
+#include <xmlsec/xmlsec.h>
+#include <xmlsec/xmltree.h>
+#include <xmlsec/xmldsig.h>
+#include <xmlsec/crypto.h>
+#include <xmlsec/templates.h>
+#include <xmlsec/errors.h>
+#include <xmlsec/io.h>
+
+
+#include "wgtpkg.h"
+
+static int initstatus;
+static int initdone;
+static xmlSecKeysMngrPtr keymgr;
+
+#ifndef CA_ROOT_DIRECTORY
+#define CA_ROOT_DIRECTORY "./ca-certificates"
+#endif
+
+/* checks if a file match uri (should not be a distributor signature) */
+static int file_match_cb(const char *uri)
+{
+ struct filedesc *fdesc = file_of_name(uri);
+ return fdesc != NULL && fdesc->type == type_file && (fdesc->flags & flag_distributor_signature) == 0;
+}
+
+/* open the file of uri */
+static void *file_open_cb(const char *file)
+{
+ struct filedesc *fdesc;
+ FILE *f;
+
+ fdesc = file_of_name(file);
+ if (fdesc == NULL) {
+ syslog(LOG_ERR, "shouldn't open uri %s", file);
+ return NULL;
+ }
+
+ f = fopen(file, "r");
+ if (f == NULL)
+ syslog(LOG_ERR, "can't open file %s for reading", file);
+ else
+ fdesc->flags |= flag_opened;
+
+ return f;
+}
+
+/* read the opened file */
+static int file_read_cb(void *context, char *buffer, int len)
+{
+ size_t r = fread(buffer, 1, len, (FILE*)context);
+ return r ? (int)r : feof((FILE*)context) ? 0 : - 1;
+}
+
+/* close the opened file */
+static int file_close_cb(void *context)
+{
+ return (int)fclose((FILE*)context);
+}
+
+/* echo an error message */
+static void errors_cb(const char *file, int line, const char *func, const char *errorObject, const char *errorSubject, int reason, const char *msg)
+{
+ syslog(LOG_ERR, "xmlSec error %3d: %s (subject=\"%s\", object=\"%s\")", reason, msg, errorSubject ? errorSubject : "?", errorObject ? errorObject : "?");
+}
+
+/* fills database with trusted keys */
+static int fill_trusted_keys()
+{
+ int err;
+ DIR *dir;
+ struct dirent *ent;
+ char path[PATH_MAX], *e;
+
+ e = stpcpy(path, CA_ROOT_DIRECTORY);
+ dir = opendir(path);
+ if (!dir) {
+ syslog(LOG_ERR, "opendir %s failed in fill_trusted_keys", path);
+ return -1;
+ }
+
+ *e++ = '/';
+ ent = readdir(dir);
+ while (ent != NULL) {
+ if (ent->d_type == DT_REG) {
+ strcpy(e, ent->d_name);
+ err = xmlSecCryptoAppKeysMngrCertLoad(keymgr, path, xmlSecKeyDataFormatPem, xmlSecKeyDataTypeTrusted);
+ if (err < 0) {
+ syslog(LOG_ERR, "xmlSecCryptoAppKeysMngrCertLoadMemory failed for %s", path);
+ closedir(dir);
+ return -1;
+ }
+ }
+ ent = readdir(dir);
+ }
+
+ closedir(dir);
+ return 0;
+
+}
+
+/* initialisation of access to xmlsec */
+int xmlsec_init()
+{
+
+ if (initdone)
+ goto end;
+
+ initdone = 1;
+ initstatus = -1;
+
+ if(xmlSecInit() < 0) {
+ syslog(LOG_ERR, "xmlSecInit failed.");
+ goto end;
+ }
+
+#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
+ if(xmlSecCryptoDLLoadLibrary(XMLSEC_CRYPTO) < 0) {
+ syslog(LOG_ERR, "xmlSecCryptoDLLoadLibrary %s failed.", XMLSEC_CRYPTO);
+ goto end;
+ }
+#endif
+
+ if(xmlSecCryptoAppInit(NULL) < 0) {
+ syslog(LOG_ERR, "xmlSecCryptoAppInit failed.");
+ goto end;
+ }
+
+ if(xmlSecCryptoInit() < 0) {
+ syslog(LOG_ERR, "xmlSecCryptoInit failed.");
+ goto end;
+ }
+
+ xmlSecErrorsSetCallback(errors_cb);
+
+ xmlSecIOCleanupCallbacks();
+ if (xmlSecIORegisterCallbacks(file_match_cb,
+ file_open_cb, file_read_cb, file_close_cb)) {
+ syslog(LOG_ERR, "xmlSecIORegisterCallbacks failed.");
+ goto end;
+ }
+
+ keymgr = xmlSecKeysMngrCreate();
+ if (keymgr == NULL) {
+ syslog(LOG_ERR, "xmlSecKeysMngrCreate failed.");
+ goto end;
+ }
+
+ if(xmlSecCryptoAppDefaultKeysMngrInit(keymgr) < 0) {
+ syslog(LOG_ERR, "xmlSecCryptoAppDefaultKeysMngrInit failed.");
+ goto end;
+ }
+ fill_trusted_keys();
+
+ initstatus = 0;
+end:
+ return initstatus;
+}
+
+/* shuting down accesses to xmlsec */
+void xmlsec_shutdown()
+{
+ xmlSecKeysMngrDestroy(keymgr);
+
+ xmlSecCryptoShutdown();
+
+ xmlSecCryptoAppShutdown();
+
+ xmlSecShutdown();
+}
+
+/* verify a signature */
+int xmlsec_verify(xmlNodePtr node)
+{
+ int rc;
+ xmlSecDSigCtxPtr dsigctx;
+
+ assert(initdone && !initstatus);
+
+ dsigctx = xmlSecDSigCtxCreate(keymgr);
+ if (dsigctx == NULL) {
+ syslog(LOG_ERR, "xmlSecDSigCtxCreate failed.");
+ rc = -1;
+ } else {
+ rc = xmlSecDSigCtxVerify(dsigctx, node);
+ if (rc)
+ syslog(LOG_ERR, "xmlSecDSigCtxVerify failed.");
+ else if (dsigctx->status != xmlSecDSigStatusSucceeded) {
+ syslog(LOG_ERR, "invalid signature.");
+ rc = -1;
+ }
+ xmlSecDSigCtxDestroy(dsigctx);
+ }
+
+ return rc;
+}
+
+/* templates for properties of signature files */
+static const struct { const char *id; const char *xml; } properties[2] = {
+ {
+ .id = "AuthorSignature", /* template of properties for author signature */
+ .xml =
+ "<SignatureProperties xmlns:dsp=\"http://www.w3.org/2009/xmldsig-properties\">"
+ "<SignatureProperty Id=\"profile\" Target=\"#AuthorSignature\">"
+ "<dsp:Profile URI=\"http://www.w3.org/ns/widgets-digsig#profile\"></dsp:Profile>"
+ "</SignatureProperty>"
+ "<SignatureProperty Id=\"role\" Target=\"#AuthorSignature\">"
+ "<dsp:Role URI=\"http://www.w3.org/ns/widgets-digsig#role-author\"></dsp:Role>"
+ "</SignatureProperty>"
+ "<SignatureProperty Id=\"identifier\" Target=\"#AuthorSignature\">"
+ "<dsp:Identifier></dsp:Identifier>"
+ "</SignatureProperty>"
+ "</SignatureProperties>"
+ },
+ {
+ .id = "DistributorSignature", /* template of properties for distributor signature */
+ .xml =
+ "<SignatureProperties xmlns:dsp=\"http://www.w3.org/2009/xmldsig-properties\">"
+ "<SignatureProperty Id=\"profile\" Target=\"#DistributorSignature\">"
+ "<dsp:Profile URI=\"http://www.w3.org/ns/widgets-digsig#profile\"></dsp:Profile>"
+ "</SignatureProperty>"
+ "<SignatureProperty Id=\"role\" Target=\"#DistributorSignature\">"
+ "<dsp:Role URI=\"http://www.w3.org/ns/widgets-digsig#role-distributor\"></dsp:Role>"
+ "</SignatureProperty>"
+ "<SignatureProperty Id=\"identifier\" Target=\"#DistributorSignature\">"
+ "<dsp:Identifier></dsp:Identifier>"
+ "</SignatureProperty>"
+ "</SignatureProperties>"
+ }
+};
+
+/* create a signature of 'index' (0 for author, other values for distributors)
+using the private 'key' (filename) and the certificates 'certs' (filenames)
+as trusted chain */
+xmlDocPtr xmlsec_create(int index, const char *key, const char **certs)
+{
+ unsigned int i, fc, mask;
+ struct filedesc *fdesc;
+ xmlNodePtr sign, obj, ref, kinfo, props;
+ xmlDocPtr doc;
+ int rc;
+ xmlSecDSigCtxPtr dsigctx;
+
+ assert(initdone && !initstatus);
+
+ /* create the document */
+ doc = xmlNewDoc("1.0");
+ if (doc == NULL) {
+ syslog(LOG_ERR, "xmlNewDoc failed");
+ goto error;
+ }
+
+ /* create the root signature node */
+ sign = xmlSecTmplSignatureCreate(doc, xmlSecTransformInclC14N11Id, xmlSecTransformRsaSha256Id, properties[!!index].id);
+ if (sign == NULL) {
+ syslog(LOG_ERR, "xmlSecTmplSignatureCreate failed");
+ goto error2;
+ }
+ xmlDocSetRootElement(doc, sign);
+
+ /* create the object and its reference */
+ obj = xmlSecTmplSignatureAddObject(sign, "prop", NULL, NULL);
+ if (obj == NULL) {
+ syslog(LOG_ERR, "xmlSecTmplSignatureAddObject failed");
+ goto error2;
+ }
+ rc = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, properties[!!index].xml, &props);
+ if (rc) {
+ syslog(LOG_ERR, "xmlParseBalancedChunkMemory failed");
+ goto error2;
+ }
+ if (NULL == xmlAddChild(obj, props)) {
+ syslog(LOG_ERR, "filling object node failed");
+ xmlFreeNode(obj);
+ goto error2;
+ }
+
+ /* create references to files */
+ mask = index ? flag_distributor_signature : flag_signature;
+ fc = file_count();
+ for (i = 0 ; i < fc ; i++) {
+ fdesc = file_of_index(i);
+ if (fdesc->type == type_file && (fdesc->flags & mask) == 0) {
+ ref = xmlSecTmplSignatureAddReference(sign, xmlSecTransformSha256Id, NULL, fdesc->name, NULL);
+ if (ref == NULL) {
+ syslog(LOG_ERR, "creation of reference to %s failed", fdesc->name);
+ goto error2;
+ }
+ }
+ }
+
+ /* create reference to object having properties */
+ ref = xmlSecTmplSignatureAddReference(sign, xmlSecTransformSha256Id, NULL, "#prop", NULL);
+ if (ref == NULL) {
+ syslog(LOG_ERR, "creation of reference to #prop failed");
+ goto error2;
+ }
+ if (NULL == xmlSecTmplReferenceAddTransform(ref, xmlSecTransformInclC14N11Id)) {
+ syslog(LOG_ERR, "setting transform reference to #prop failed");
+ goto error2;
+ }
+
+ /* adds the X509 data */
+ kinfo = xmlSecTmplSignatureEnsureKeyInfo(sign, NULL);
+ if (kinfo == NULL) {
+ syslog(LOG_ERR, "xmlSecTmplSignatureEnsureKeyInfo failed");
+ goto error2;
+ }
+ if (NULL == xmlSecTmplKeyInfoAddX509Data(kinfo)) {
+ syslog(LOG_ERR, "xmlSecTmplKeyInfoAddX509Data failed");
+ goto error2;
+ }
+
+ /* sign now */
+ dsigctx = xmlSecDSigCtxCreate(keymgr);
+ if (dsigctx == NULL) {
+ syslog(LOG_ERR, "xmlSecDSigCtxCreate failed.");
+ goto error3;
+ }
+ dsigctx->signKey = xmlSecCryptoAppKeyLoad(key, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
+ if (dsigctx->signKey == NULL) {
+ syslog(LOG_ERR, "loading key %s failed.", key);
+ goto error3;
+ }
+ while (*certs) {
+ if(xmlSecCryptoAppKeyCertLoad(dsigctx->signKey, *certs, xmlSecKeyDataFormatPem) < 0) {
+ syslog(LOG_ERR, "loading certificate %s failed.", *certs);
+ goto error3;
+ }
+ certs++;
+ }
+ if(xmlSecDSigCtxSign(dsigctx, sign) < 0) {
+ syslog(LOG_ERR, "signing the document failed.");
+ goto error3;
+ }
+ xmlSecDSigCtxDestroy(dsigctx);
+ return doc;
+
+error3:
+ xmlSecDSigCtxDestroy(dsigctx);
+error2:
+ xmlFreeDoc(doc);
+error:
+ return NULL;
+}
+
diff --git a/src/wgtpkg-zip.c b/src/wgtpkg-zip.c
new file mode 100644
index 0000000..9e51538
--- /dev/null
+++ b/src/wgtpkg-zip.c
@@ -0,0 +1,342 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#define _BSD_SOURCE /* see readdir */
+
+#include <limits.h>
+#include <zip.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+#include <dirent.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "wgtpkg.h"
+
+
+#if !defined(MODE_OF_FILE_CREATION)
+#define MODE_OF_FILE_CREATION 0640
+#endif
+#if !defined(MODE_OF_DIRECTORY_CREATION)
+#define MODE_OF_DIRECTORY_CREATION 0750
+#endif
+
+static int is_valid_filename(const char *filename)
+{
+ int lastsp = 0;
+ int index = 0;
+ unsigned char c;
+
+ c = (unsigned char)filename[index];
+ while (c) {
+ if ((c < 0x1f)
+ || ((lastsp = (c == 0x20)) && index == 0)
+ || c == 0x7f || c == 0x3c || c == 0x3e
+ || c == 0x3a || c == 0x22
+ || c == 0x5c || c == 0x7c || c == 0x3f
+ || c == 0x2a || c == 0x5e || c == 0x60
+ || c == 0x7b || c == 0x7d || c == 0x21)
+ return 0;
+ c = (unsigned char)filename[++index];
+ }
+ return !lastsp;
+}
+
+static int create_directory(char *file, int mode)
+{
+ int rc;
+ char *last = strrchr(file, '/');
+ if (last != NULL)
+ *last = 0;
+ rc = mkdir(file, mode);
+ if (rc) {
+ if (errno == EEXIST)
+ rc = 0;
+ else if (errno == ENOENT) {
+ rc = create_directory(file, mode);
+ if (!rc)
+ rc = mkdir(file, mode);
+ }
+ }
+ if (rc)
+ syslog(LOG_ERR, "can't create directory %s", file);
+ if (last != NULL)
+ *last = '/';
+ return rc;
+}
+
+static int create_file(char *file, int fmode, int dmode)
+{
+ int fd = creat(file, fmode);
+ if (fd < 0 && errno == ENOENT) {
+ if (!create_directory(file, dmode))
+ fd = creat(file, fmode);
+ }
+ if (fd < 0)
+ syslog(LOG_ERR, "can't create file %s", file);
+ return fd;
+}
+
+/* read (extract) 'zipfile' in current directory */
+int zread(const char *zipfile, unsigned long long maxsize)
+{
+ struct filedesc *fdesc;
+ int err, fd, len;
+ struct zip *zip;
+ zip_int64_t z64;
+ unsigned int count, index;
+ struct zip_file *zfile;
+ struct zip_stat zstat;
+ char buffer[32768];
+ ssize_t sizr, sizw;
+ size_t esize;
+
+ /* open the zip file */
+ zip = zip_open(zipfile, ZIP_CHECKCONS, &err);
+ if (!zip) {
+ syslog(LOG_ERR, "Can't connect to file %s", zipfile);
+ return -1;
+ }
+
+ z64 = zip_get_num_entries(zip, 0);
+ if (z64 < 0 || z64 > UINT_MAX) {
+ syslog(LOG_ERR, "too many entries in %s", zipfile);
+ goto error;
+ }
+ count = (unsigned int)z64;
+
+ /* records the files */
+ file_reset();
+ esize = 0;
+ for (index = 0 ; index < count ; index++) {
+ err = zip_stat_index(zip, index, ZIP_FL_ENC_GUESS, &zstat);
+ /* check the file name */
+ if (!is_valid_filename(zstat.name)) {
+ syslog(LOG_ERR, "invalid entry %s found in %s", zstat.name, zipfile);
+ goto error;
+ }
+ if (zstat.name[0] == '/') {
+ syslog(LOG_ERR, "absolute entry %s found in %s", zstat.name, zipfile);
+ goto error;
+ }
+ len = strlen(zstat.name);
+ if (len == 0) {
+ syslog(LOG_ERR, "empty entry found in %s", zipfile);
+ goto error;
+ }
+ if (zstat.size == 0) {
+ /* directory name */
+ if (zstat.name[len - 1] != '/') {
+ syslog(LOG_ERR, "bad directory name %s in %s", zstat.name, zipfile);
+ goto error;
+ }
+ /* record */
+ fdesc = file_add_directory(zstat.name);
+ } else {
+ /* directory name */
+ if (zstat.name[len - 1] == '/') {
+ syslog(LOG_ERR, "bad file name %s in %s", zstat.name, zipfile);
+ goto error;
+ }
+ /* get the size */
+ esize += zstat.size;
+ /* record */
+ fdesc = file_add_file(zstat.name);
+ }
+ if (!fdesc)
+ goto error;
+ fdesc->zindex = index;
+ }
+
+ /* check the size */
+ if (maxsize && esize > maxsize) {
+ syslog(LOG_ERR, "extracted size %zu greater than allowed size %llu", esize, maxsize);
+ goto error;
+ }
+
+ /* unpack the recorded files */
+ assert(count == file_count());
+ for (index = 0 ; index < count ; index++) {
+ fdesc = file_of_index(index);
+ assert(fdesc != NULL);
+ err = zip_stat_index(zip, fdesc->zindex, ZIP_FL_ENC_GUESS, &zstat);
+ assert(zstat.name[0] != '/');
+ if (zstat.size == 0) {
+ /* directory name */
+ err = create_directory((char*)zstat.name, MODE_OF_DIRECTORY_CREATION);
+ if (err && errno != EEXIST)
+ goto error;
+ } else {
+ /* file name */
+ zfile = zip_fopen_index(zip, fdesc->zindex, 0);
+ if (!zfile) {
+ syslog(LOG_ERR, "Can't open %s in %s", zstat.name, zipfile);
+ goto error;
+ }
+ fd = create_file((char*)zstat.name, MODE_OF_FILE_CREATION, MODE_OF_DIRECTORY_CREATION);
+ if (fd < 0)
+ goto errorz;
+ /* extract */
+ z64 = zstat.size;
+ while (z64) {
+ sizr = zip_fread(zfile, buffer, sizeof buffer);
+ if (sizr < 0) {
+ syslog(LOG_ERR, "error while reading %s in %s", zstat.name, zipfile);
+ goto errorzf;
+ }
+ sizw = write(fd, buffer, sizr);
+ if (sizw < 0) {
+ syslog(LOG_ERR, "error while writing %s", zstat.name);
+ goto errorzf;
+ }
+ z64 -= sizw;
+ }
+ close(fd);
+ zip_fclose(zfile);
+ }
+ }
+
+ zip_close(zip);
+ return 0;
+
+errorzf:
+ close(fd);
+errorz:
+ zip_fclose(zfile);
+error:
+ zip_close(zip);
+ return -1;
+}
+
+struct zws {
+ struct zip *zip;
+ char name[PATH_MAX];
+ char buffer[32768];
+};
+
+static int zwr(struct zws *zws, int offset)
+{
+ int len, err;
+ DIR *dir;
+ struct dirent *ent;
+ zip_int64_t z64;
+ struct zip_source *zsrc;
+
+ if (offset == 0)
+ dir = opendir(".");
+ else {
+ dir = opendir(zws->name);
+ zws->name[offset++] = '/';
+ }
+ if (!dir) {
+ syslog(LOG_ERR, "opendir %.*s failed in zwr", offset, zws->name);
+ return -1;
+ }
+
+ ent = readdir(dir);
+ while (ent != NULL) {
+ len = strlen(ent->d_name);
+ if (ent->d_name[0] == '.' && (len == 1 ||
+ (ent->d_name[1] == '.' && len == 2)))
+ ;
+ else if (offset + len >= sizeof(zws->name)) {
+ syslog(LOG_ERR, "name too long in zwr");
+ errno = ENAMETOOLONG;
+ goto error;
+ } else {
+ memcpy(zws->name + offset, ent->d_name, 1+len);
+ if (!is_valid_filename(ent->d_name)) {
+ syslog(LOG_ERR, "invalid name %s", zws->name);
+ goto error;
+ }
+ switch (ent->d_type) {
+ case DT_DIR:
+ z64 = zip_dir_add(zws->zip, zws->name, ZIP_FL_ENC_UTF_8);
+ if (z64 < 0) {
+ syslog(LOG_ERR, "zip_dir_add of %s failed", zws->name);
+ goto error;
+ }
+ err = zwr(zws, offset + len);
+ if (err)
+ goto error;
+ break;
+ case DT_REG:
+ zsrc = zip_source_file(zws->zip, zws->name, 0, 0);
+ if (zsrc == NULL) {
+ syslog(LOG_ERR, "zip_source_file of %s failed", zws->name);
+ goto error;
+ }
+ z64 = zip_file_add(zws->zip, zws->name, zsrc, ZIP_FL_ENC_UTF_8);
+ if (z64 < 0) {
+ syslog(LOG_ERR, "zip_file_add of %s failed", zws->name);
+ zip_source_free(zsrc);
+ goto error;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ ent = readdir(dir);
+ }
+
+ closedir(dir);
+ return 0;
+error:
+ closedir(dir);
+ return -1;
+}
+
+/* write (pack) content of the current directory in 'zipfile' */
+int zwrite(const char *zipfile)
+{
+ int err;
+ struct zws zws;
+
+ zws.zip = zip_open(zipfile, ZIP_CREATE|ZIP_TRUNCATE, &err);
+ if (!zws.zip) {
+ syslog(LOG_ERR, "Can't open %s for write", zipfile);
+ return -1;
+ }
+
+ err = zwr(&zws, 0);
+ zip_close(zws.zip);
+ return err;
+}
+
+
+#if defined(TEST_READ)
+int main(int ac, char **av)
+{
+ for(av++ ; *av ; av++)
+ zread(*av);
+ return 0;
+}
+#endif
+
+#if defined(TEST_WRITE)
+int main(int ac, char **av)
+{
+ for(av++ ; *av ; av++)
+ zwrite(*av);
+ return 0;
+}
+#endif
+
diff --git a/src/wgtpkg.h b/src/wgtpkg.h
new file mode 100644
index 0000000..d35b7f4
--- /dev/null
+++ b/src/wgtpkg.h
@@ -0,0 +1,125 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+
+#include <libxml/tree.h>
+#include "config.h"
+
+struct filedesc;
+
+/**************************************************************/
+/* from wgtpkg-base64 */
+
+extern char *base64encw(const char *buffer, int length, int width);
+extern char *base64enc(const char *buffer, int length);
+extern int base64dec(const char *buffer, char **output);
+extern int base64eq(const char *buf1, const char *buf2);
+
+/**************************************************************/
+/* from wgtpkg-certs */
+
+extern void clear_certificates();
+extern int add_certificate_b64(const char *b64);
+
+/**************************************************************/
+/* from wgtpkg-digsig */
+
+/* verify the digital signature in file */
+extern int verify_digsig(struct filedesc *fdesc);
+
+/* create a digital signature */
+extern int create_digsig(int index, const char *key, const char **certs);
+
+/* check the signatures of the current directory */
+extern int check_all_signatures();
+
+/**************************************************************/
+/* from wgtpkg-files */
+
+enum entrytype {
+ type_unset = 0,
+ type_file = 1,
+ type_directory = 2
+};
+
+enum fileflag {
+ flag_referenced = 1,
+ flag_opened = 2,
+ flag_author_signature = 4,
+ flag_distributor_signature = 8,
+ flag_signature = 12
+};
+
+struct filedesc {
+ enum entrytype type;
+ unsigned int flags;
+ unsigned int signum;
+ unsigned int zindex;
+ char name[1];
+};
+
+extern void file_reset();
+extern void file_clear_flags();
+extern unsigned int file_count();
+extern struct filedesc *file_of_index(unsigned int index);
+extern struct filedesc *file_of_name(const char *name);
+extern struct filedesc *file_add_directory(const char *name);
+extern struct filedesc *file_add_file(const char *name);
+extern int fill_files();
+
+extern unsigned int signature_count();
+extern struct filedesc *signature_of_index(unsigned int index);
+extern struct filedesc *create_signature(unsigned int number);
+extern struct filedesc *get_signature(unsigned int number);
+
+extern int file_set_prop(struct filedesc *file, const char *name, const char *value);
+extern const char *file_get_prop(struct filedesc *file, const char *name);
+
+/**************************************************************/
+/* from wgtpkg-verbose */
+extern int verbosity;
+#define warning(...) do{if(verbosity)syslog(LOG_WARNING,__VA_ARGS__);}while(0)
+#define notice(...) do{if(verbosity)syslog(LOG_NOTICE,__VA_ARGS__);}while(0)
+#define info(...) do{if(verbosity)syslog(LOG_INFO,__VA_ARGS__);}while(0)
+#define debug(...) do{if(verbosity>1)syslog(LOG_DEBUG,__VA_ARGS__);}while(0)
+extern int verbose_scan_args(int argc, char **argv);
+
+/**************************************************************/
+/* from wgtpkg-workdir */
+
+extern int enter_workdir(int clean);
+extern void remove_workdir();
+extern int make_workdir(int reuse);
+extern int set_workdir(const char *name, int create);
+
+/**************************************************************/
+/* from wgtpkg-xmlsec */
+
+extern int xmlsec_init();
+extern void xmlsec_shutdown();
+extern int xmlsec_verify(xmlNodePtr node);
+extern xmlDocPtr xmlsec_create(int index, const char *key, const char **certs);
+
+/**************************************************************/
+/* from wgtpkg-zip */
+
+/* read (extract) 'zipfile' in current directory */
+extern int zread(const char *zipfile, unsigned long long maxsize);
+
+/* write (pack) content of the current directory in 'zipfile' */
+extern int zwrite(const char *zipfile);
+
+