initial commit
authorJosé Bollo <jose.bollo@iot.bzh>
Mon, 9 Nov 2015 20:58:30 +0000 (21:58 +0100)
committerJosé Bollo <jose.bollo@iot.bzh>
Mon, 9 Nov 2015 20:58:30 +0000 (21:58 +0100)
Change-Id: Ib882fe057690e03e2f5d32ee7e0c594697dd46c8
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
13 files changed:
LICENSE.txt [new file with mode: 0644]
Makefile [new file with mode: 0644]
wgtpkg-base64.c [new file with mode: 0644]
wgtpkg-certs.c [new file with mode: 0644]
wgtpkg-digsig.c [new file with mode: 0644]
wgtpkg-files.c [new file with mode: 0644]
wgtpkg-install.c [new file with mode: 0644]
wgtpkg-pack.c [new file with mode: 0644]
wgtpkg-sign.c [new file with mode: 0644]
wgtpkg-workdir.c [new file with mode: 0644]
wgtpkg-xmlsec.c [new file with mode: 0644]
wgtpkg-zip.c [new file with mode: 0644]
wgtpkg.h [new file with mode: 0644]

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..5cf6f8a
--- /dev/null
@@ -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 (file)
index 0000000..c103c51
--- /dev/null
@@ -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 (file)
index 0000000..96f4280
--- /dev/null
@@ -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 (file)
index 0000000..17e909a
--- /dev/null
@@ -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 (file)
index 0000000..7781c62
--- /dev/null
@@ -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 (file)
index 0000000..9164447
--- /dev/null
@@ -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 (file)
index 0000000..6a6a72a
--- /dev/null
@@ -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 (file)
index 0000000..4b0b08c
--- /dev/null
@@ -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 (file)
index 0000000..5c65217
--- /dev/null
@@ -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 (file)
index 0000000..98501ff
--- /dev/null
@@ -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 (file)
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);
+
+