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