refactoring sources
[src/app-framework-main.git] / src / wgtpkg-digsig.c
diff --git a/src/wgtpkg-digsig.c b/src/wgtpkg-digsig.c
new file mode 100644 (file)
index 0000000..284acd1
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ Copyright 2015 IoT.bzh
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+
+#include <string.h>
+#include <syslog.h>
+#include <assert.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/uri.h>
+
+
+#include "wgtpkg.h"
+
+
+
+static const char uri_role_author[] = "http://www.w3.org/ns/widgets-digsig#role-author";
+static const char uri_role_distributor[] = "http://www.w3.org/ns/widgets-digsig#role-distributor";
+static const char uri_profile[] = "http://www.w3.org/ns/widgets-digsig#profile";
+
+
+/* global data */
+static xmlDocPtr document;  /* the document */
+
+/* facility to get the first element node (skip text nodes) starting with 'node' */
+static xmlNodePtr next_element(xmlNodePtr node)
+{
+       while (node && node->type != XML_ELEMENT_NODE)
+               node = node->next;
+       return node;
+}
+
+/* is the 'node' an element node of 'name'? */
+static int is_element(xmlNodePtr node, const char *name)
+{
+       return node->type == XML_ELEMENT_NODE
+               && !strcmp(name, node->name);
+}
+
+/* is the 'node' an element node of 'name'? */
+static int is_node(xmlNodePtr node, const char *name)
+{
+       return node != NULL && is_element(node, name);
+}
+
+#if 0
+/* facility to get the first element node (skip text nodes) starting with 'node' */
+static xmlNodePtr next_element_type(xmlNodePtr node, const char *name)
+{
+       while (node && node->type != XML_ELEMENT_NODE && strcmp(name, node->name))
+               node = node->next;
+       return node;
+}
+
+/* search the element node of id. NOTE : not optimized at all */
+static xmlNodePtr search_for(const char *attrname, const char *value)
+{
+       char *val;
+       xmlNodePtr iter, next;
+       xmlNodePtr result;
+
+       result = NULL;
+       iter = xmlDocGetRootElement(document);
+       while (iter != NULL) {
+               val = xmlGetProp(iter, attrname);
+               if (val != NULL && !strcmp(val, value)) {
+                       if (result != NULL) {
+                               syslog(LOG_ERR, "duplicated %s %s", attrname, value);
+                               free(val);
+                               return NULL;
+                       }
+                       result = iter;
+               }
+               free(val);
+               next = next_element(iter->children);
+               if (next == NULL) {
+                       /* no child, try sibling */
+                       next = next_element(iter->next);
+                       if (next == NULL) {
+                               iter = iter->parent;
+                               while (iter != NULL && next == NULL) {
+                                       next = next_element(iter->next);
+                                       iter = iter->parent;
+                               }
+                       }
+               }
+               iter = next;
+       }
+       if (result == NULL)
+               syslog(LOG_ERR, "node of %s '%s' not found", attrname, value);
+       return result;
+}
+
+/* search the element node of id. NOTE : not optimized at all */
+static xmlNodePtr search_id(const char *id)
+{
+       return search_for("Id", id);
+}
+#endif
+
+/* check the digest of one element */
+static int check_one_reference(xmlNodePtr ref)
+{
+       int rc;
+       char *uri;
+       xmlURIPtr u;
+       struct filedesc *fdesc;
+
+       /* start */
+       rc = -1;
+
+       /* get the uri */
+       uri = xmlGetProp(ref, "URI");
+       if (uri == NULL) {
+               syslog(LOG_ERR, "attribute URI of element <Reference> not found");
+               goto error;
+       }
+
+       /* parse the uri */
+       u = xmlParseURI(uri);
+       if (!u) {
+               syslog(LOG_ERR, "error while parsing URI %s", uri);
+               goto error2;
+       }
+
+       /* check that unexpected parts are not there */
+       if (u->scheme || u->opaque || u->authority || u->server || u->user || u->query) {
+               syslog(LOG_ERR, "unexpected uri component in %s", uri);
+               goto error3;
+       }
+
+       /* check path and fragment */
+       if (!u->path && !u->fragment) {
+               syslog(LOG_ERR, "invalid uri %s", uri);
+               goto error3;
+       }
+       if (u->path && u->fragment) {
+               syslog(LOG_ERR, "not allowed to sign foreign fragment in %s", uri);
+               goto error3;
+       }
+
+       if (u->path) {
+               /* check that the path is valid */
+               fdesc = file_of_name(u->path);
+               if (fdesc == NULL) {
+                       syslog(LOG_ERR, "reference to unknown file %s", u->path);
+                       goto error3;
+               }
+               if (fdesc->type != type_file) {
+                       syslog(LOG_ERR, "reference to directory %s", u->path);
+                       goto error3;
+               }
+               if ((fdesc->flags & flag_distributor_signature) != 0) {
+                       syslog(LOG_ERR, "reference to signature %s", u->path);
+                       goto error3;
+               }
+               fdesc->flags |= flag_referenced;
+               rc = 0;
+       } else {
+               rc = 0;
+       }
+
+error3:
+       xmlFreeURI(u);
+error2:
+       xmlFree(uri);
+error:
+       return rc;
+}
+
+static int check_references(xmlNodePtr sinfo)
+{
+       xmlNodePtr elem;
+
+       elem = sinfo->children;
+       while (elem != NULL) {
+               if (is_element(elem, "Reference"))
+                       if (check_one_reference(elem))
+                               return -1;
+               elem = elem->next;
+       }
+       return 0;
+}
+
+static int get_certificates(xmlNodePtr kinfo)
+{
+       xmlNodePtr n1, n2;
+       char *b;
+       int rc;
+
+       n1 = kinfo->children;
+       while (n1) {
+               if (is_element(n1, "X509Data")) {
+                       n2 = n1->children;
+                       while (n2) {
+                               if (is_element(n2, "X509Certificate")) {
+                                       b = xmlNodeGetContent(n2);
+                                       if (b == NULL) {
+                                               syslog(LOG_ERR, "xmlNodeGetContent of X509Certificate failed");
+                                               return -1;
+                                       }
+                                       rc = add_certificate_b64(b);
+                                       xmlFree(b);
+                                       if (rc)
+                                               return rc;
+                               }
+                               n2 = n2->next;
+                       }
+               }
+               n1 = n1->next;
+       }
+       return 0;
+}
+
+/* checks the current document */
+static int checkdocument()
+{
+       int rc;
+       xmlNodePtr sinfo, svalue, kinfo, objs, rootsig;
+
+       rc = -1;
+
+       rootsig = xmlDocGetRootElement(document);
+       if (!is_node(rootsig, "Signature")) {
+               syslog(LOG_ERR, "root element <Signature> not found");
+               goto error;
+       }
+
+       sinfo = next_element(rootsig->children);
+       if (!is_node(sinfo, "SignedInfo")) {
+               syslog(LOG_ERR, "element <SignedInfo> not found");
+               goto error;
+       }
+
+       svalue = next_element(sinfo->next);
+       if (!is_node(svalue, "SignatureValue")) {
+               syslog(LOG_ERR, "element <SignatureValue> not found");
+               goto error;
+       }
+
+       kinfo = next_element(svalue->next);
+       if (is_node(kinfo, "KeyInfo")) {
+               objs = kinfo->next;
+       } else {
+               objs = kinfo;
+               kinfo = NULL;
+       }
+
+       rc = check_references(sinfo);
+       if (rc)
+               goto error;
+
+       rc = xmlsec_verify(rootsig);
+       if (rc)
+               goto error;
+
+       rc = get_certificates(kinfo);
+
+error:
+       return rc;
+}
+
+/* verify the digital signature of the file described by 'fdesc' */
+int verify_digsig(struct filedesc *fdesc)
+{
+       int res;
+
+       assert ((fdesc->flags & flag_signature) != 0);
+       debug("-- checking file %s",fdesc->name);
+
+       /* reset the flags */
+       file_clear_flags();
+       clear_certificates();
+
+       /* reads and xml parses the signature file */
+       document = xmlReadFile(fdesc->name, NULL, 0);
+       if (document == NULL) {
+               syslog(LOG_ERR, "xml parse of file %s failed", fdesc->name);
+               return -1;
+       }
+
+       res = checkdocument();
+       if (res)
+               syslog(LOG_ERR, "previous error was during check of file %s", fdesc->name);
+
+       xmlFreeDoc(document);
+       return res;
+}
+
+/* check all the signature files */
+int check_all_signatures()
+{
+       int rc, irc;
+       unsigned int i, n;
+       struct filedesc *fdesc;
+
+       n = signature_count();
+       rc = 0;
+       for (i = n ; i-- > 0 ; ) {
+               fdesc = signature_of_index(i);
+               irc = verify_digsig(fdesc);
+               if (!irc)
+                       rc = irc;
+       }
+
+       return rc;
+}
+
+/* create a signature of 'index' (0 for author, other values for distributors)
+using the private 'key' (filename) and the certificates 'certs' (filenames)
+as trusted chain */
+int create_digsig(int index, const char *key, const char **certs)
+{
+       struct filedesc *fdesc;
+       xmlDocPtr doc;
+       int rc, len;
+
+       rc = -1;
+
+       /* create the doc */
+       doc = xmlsec_create(index, key, certs);
+       if (doc == NULL)
+               goto error;
+
+       /* instanciate the filename */
+       fdesc = create_signature(index);
+       if (fdesc == NULL)
+               goto error2;
+
+       /* save the doc as file */
+       len = xmlSaveFormatFileEnc(fdesc->name, doc, NULL, 0);
+       if (len < 0) {
+               syslog(LOG_ERR, "xmlSaveFormatFileEnc to %s failed", fdesc->name);
+               goto error2;
+       }
+
+       rc = 0;
+error2:
+       xmlFreeDoc(doc);
+error:
+       return rc;
+}
+
+