aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LICENSE.txt202
-rw-r--r--Makefile29
-rw-r--r--wgtpkg-base64.c203
-rw-r--r--wgtpkg-certs.c74
-rw-r--r--wgtpkg-digsig.c336
-rw-r--r--wgtpkg-files.c312
-rw-r--r--wgtpkg-install.c82
-rw-r--r--wgtpkg-pack.c156
-rw-r--r--wgtpkg-sign.c195
-rw-r--r--wgtpkg-workdir.c166
-rw-r--r--wgtpkg-xmlsec.c352
-rw-r--r--wgtpkg-zip.c317
-rw-r--r--wgtpkg.h115
13 files changed, 2539 insertions, 0 deletions
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..521bc19
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+.PHONY: all
+
+all: wgtpkg-install wgtpkg-pack wgtpkg-sign
+
+O = -DPREDIR='""'
+
+INCS = wgtpkg.h
+
+COMSRCS = \
+ wgtpkg-base64.c \
+ wgtpkg-certs.c \
+ wgtpkg-digsig.c \
+ wgtpkg-files.c \
+ wgtpkg-workdir.c \
+ wgtpkg-xmlsec.c \
+ wgtpkg-zip.c
+
+
+INSTALLSRCS = wgtpkg-install.c $(COMSRCS)
+
+PACKSRCS = wgtpkg-install.c $(COMSRCS)
+
+XMLSECOPT = $(shell pkg-config --cflags --libs xmlsec1)
+
+wgtpkg-%: wgtpkg-%.c $(COMSRCS) $(INCS)
+ gcc $O -g -o wgtpkg-$* wgtpkg-$*.c $(COMSRCS) -lzip -lxml2 -lcrypto $(XMLSECOPT) -Wall -Wno-pointer-sign
+
+
+
diff --git a/wgtpkg-base64.c b/wgtpkg-base64.c
new file mode 100644
index 0000000..5cf6f8a
--- /dev/null
+++ b/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/wgtpkg-certs.c b/wgtpkg-certs.c
new file mode 100644
index 0000000..c103c51
--- /dev/null
+++ b/wgtpkg-certs.c
@@ -0,0 +1,74 @@
+/*
+ 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 unsigned char *b = (const unsigned char *)bin;
+ X509 *x = d2i_X509(NULL, &b, len);
+ if (x == NULL) {
+ syslog(LOG_ERR, "d2i_X509 failed");
+ return -1;
+ }
+ rc = add_certificate_x509(x);
+ if (rc)
+ X509_free(x);
+ return rc;
+}
+
+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/wgtpkg-digsig.c b/wgtpkg-digsig.c
new file mode 100644
index 0000000..96f4280
--- /dev/null
+++ b/wgtpkg-digsig.c
@@ -0,0 +1,336 @@
+/*
+ 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_id(const char *id)
+{
+ char *val;
+ xmlNodePtr iter, next;
+ xmlNodePtr result;
+
+ result = NULL;
+ iter = xmlDocGetRootElement(document);
+ while (iter != NULL) {
+ val = xmlGetProp(iter, "Id");
+ if (val != NULL && !strcmp(val, id)) {
+ if (result != NULL) {
+ syslog(LOG_ERR, "duplicated Id %s", id);
+ 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 Id '%s' not found", id);
+ return result;
+}
+#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 == NULL) {
+ syslog(LOG_ERR, "error while parsing URI %s", uri);
+ goto error2;
+ }
+
+ if (u->scheme || u->opaque || u->authority || u->server || u->user || u->query) {
+ syslog(LOG_ERR, "unexpected uri component in %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) {
+ 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;
+}
+
+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;
+}
+
+int verify_digsig(struct filedesc *fdesc)
+{
+ int res;
+
+ assert ((fdesc->flags & flag_signature) != 0);
+printf("\n\nchecking file %s\n\n",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;
+}
+
+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);
+ assert ((fdesc->flags & flag_signature) != 0);
+ irc = verify_digsig(fdesc);
+ if (!irc)
+ rc = irc;
+ }
+
+ return rc;
+}
+
+int create_digsig(int index, const char *key, const char **certs)
+{
+ struct filedesc *fdesc;
+ xmlDocPtr doc;
+ int rc, len;
+
+ rc = -1;
+ doc = xmlsec_create(index, key, certs);
+ if (doc == NULL)
+ goto error;
+
+ fdesc = create_signature(index);
+ if (fdesc == NULL)
+ goto error2;
+
+ 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/wgtpkg-files.c b/wgtpkg-files.c
new file mode 100644
index 0000000..17e909a
--- /dev/null
+++ b/wgtpkg-files.c
@@ -0,0 +1,312 @@
+/*
+ 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;
+}
+
+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/wgtpkg-install.c b/wgtpkg-install.c
new file mode 100644
index 0000000..7781c62
--- /dev/null
+++ b/wgtpkg-install.c
@@ -0,0 +1,82 @@
+/*
+ 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)
+{
+printf("\n\nINSTALLING widget %s\n", 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();
+
+ /* 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/wgtpkg-pack.c b/wgtpkg-pack.c
new file mode 100644
index 0000000..9164447
--- /dev/null
+++ b/wgtpkg-pack.c
@@ -0,0 +1,156 @@
+/*
+ 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"
+ "\n",
+ appname
+ );
+}
+
+static struct option options[] = {
+ { "output", required_argument, NULL, 'o' },
+ { "force", no_argument, NULL, 'f' },
+ { "help", no_argument, NULL, 'h' },
+ { 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, "hfo:", options, NULL);
+ if (i < 0)
+ break;
+ switch (i) {
+ case 'o':
+ wgtfile = optarg;
+ 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;
+ }
+
+printf("\n\nPACKING widget %s from directory %s\n", 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/wgtpkg-sign.c b/wgtpkg-sign.c
new file mode 100644
index 0000000..6a6a72a
--- /dev/null
+++ b/wgtpkg-sign.c
@@ -0,0 +1,195 @@
+/*
+ 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"
+ "\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' },
+ { 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;
+ }
+
+printf("\n\nSIGNING content of directory %s for number %u\n", directory, number);
+
+ certfiles[ncert] = NULL;
+ return !!create_digsig(number, keyfile, (const char**)certfiles);
+}
+
diff --git a/wgtpkg-workdir.c b/wgtpkg-workdir.c
new file mode 100644
index 0000000..4b0b08c
--- /dev/null
+++ b/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 "/tmp/"
+#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/wgtpkg-xmlsec.c b/wgtpkg-xmlsec.c
new file mode 100644
index 0000000..5c65217
--- /dev/null
+++ b/wgtpkg-xmlsec.c
@@ -0,0 +1,352 @@
+/*
+ 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
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+static int file_close_cb(void *context)
+{
+ return (int)fclose((FILE*)context);
+}
+
+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 : "?");
+}
+
+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;
+
+}
+
+int xmlsec_init()
+{
+
+ if (initdone)
+ goto end;
+
+ initdone = 1;
+ initstatus = -1;
+
+ if(xmlSecInit() < 0) {
+ syslog(LOG_ERR, "xmlSecInit failed.");
+ goto end;
+ }
+
+ 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;
+}
+
+
+void xmlsec_shutdown()
+{
+ xmlSecKeysMngrDestroy(keymgr);
+
+ xmlSecCryptoShutdown();
+
+ xmlSecCryptoAppShutdown();
+
+ xmlSecShutdown();
+}
+
+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;
+}
+
+static const struct { const char *id; const char *xml; } properties[2] = {
+ {
+ .id = "AuthorSignature",
+ .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",
+ .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>"
+ }
+};
+
+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/wgtpkg-zip.c b/wgtpkg-zip.c
new file mode 100644
index 0000000..98501ff
--- /dev/null
+++ b/wgtpkg-zip.c
@@ -0,0 +1,317 @@
+/*
+ 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
+
+
+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;
+}
+
+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 (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)) {
+ closedir(dir);
+ syslog(LOG_ERR, "name too long in zwr");
+ errno = ENAMETOOLONG;
+ return -1;
+ } else {
+ memcpy(zws->name + offset, ent->d_name, 1+len);
+ 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);
+ closedir(dir);
+ return -1;
+ }
+ err = zwr(zws, offset + len);
+ if (err) {
+ closedir(dir);
+ return -1;
+ }
+ 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);
+ closedir(dir);
+ return -1;
+ }
+ 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);
+ closedir(dir);
+ return -1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ ent = readdir(dir);
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+/* 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/wgtpkg.h b/wgtpkg.h
new file mode 100644
index 0000000..14ab662
--- /dev/null
+++ b/wgtpkg.h
@@ -0,0 +1,115 @@
+/*
+ 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>
+
+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-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);
+
+