2 Copyright 2015, 2016, 2017 IoT.bzh
4 author: José Bollo <jose.bollo@iot.bzh>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
27 #include <libxml/uri.h>
28 #include <libxml/xmlsave.h>
32 #include "wgtpkg-files.h"
33 #include "wgtpkg-workdir.h"
34 #include "wgtpkg-certs.h"
35 #include "wgtpkg-xmlsec.h"
36 #include "wgtpkg-digsig.h"
40 static const char uri_role_author[] = "http://www.w3.org/ns/widgets-digsig#role-author";
41 static const char uri_role_distributor[] = "http://www.w3.org/ns/widgets-digsig#role-distributor";
42 static const char uri_profile[] = "http://www.w3.org/ns/widgets-digsig#profile";
46 static xmlDocPtr document; /* the document */
48 /* facility to get the first element node (skip text nodes) starting with 'node' */
49 static xmlNodePtr next_element(xmlNodePtr node)
51 while (node && node->type != XML_ELEMENT_NODE)
56 /* is the 'node' an element node of 'name'? */
57 static int is_element(xmlNodePtr node, const char *name)
59 return node->type == XML_ELEMENT_NODE
60 && !strcmp(name, node->name);
63 /* is the 'node' an element node of 'name'? */
64 static int is_node(xmlNodePtr node, const char *name)
66 return node != NULL && is_element(node, name);
70 /* facility to get the first element node (skip text nodes) starting with 'node' */
71 static xmlNodePtr next_element_type(xmlNodePtr node, const char *name)
73 while (node && node->type != XML_ELEMENT_NODE && strcmp(name, node->name))
78 /* search the element node of id. NOTE : not optimized at all */
79 static xmlNodePtr search_for(const char *attrname, const char *value)
82 xmlNodePtr iter, next;
86 iter = xmlDocGetRootElement(document);
87 while (iter != NULL) {
88 val = xmlGetProp(iter, attrname);
89 if (val != NULL && !strcmp(val, value)) {
91 ERROR("duplicated %s %s", attrname, value);
98 next = next_element(iter->children);
100 /* no child, try sibling */
101 next = next_element(iter->next);
104 while (iter != NULL && next == NULL) {
105 next = next_element(iter->next);
113 ERROR("node of %s '%s' not found", attrname, value);
117 /* search the element node of id. NOTE : not optimized at all */
118 static xmlNodePtr search_id(const char *id)
120 return search_for("Id", id);
124 /* check the digest of one element */
125 static int check_one_reference(xmlNodePtr ref)
130 struct filedesc *fdesc;
136 uri = xmlGetProp(ref, "URI");
138 ERROR("attribute URI of element <Reference> not found");
143 u = xmlParseURI(uri);
145 ERROR("error while parsing URI %s", uri);
149 /* check that unexpected parts are not there */
150 if (u->scheme || u->opaque || u->authority || u->server || u->user || u->query) {
151 ERROR("unexpected uri component in %s", uri);
155 /* check path and fragment */
156 if (!u->path && !u->fragment) {
157 ERROR("invalid uri %s", uri);
160 if (u->path && u->fragment) {
161 ERROR("not allowed to sign foreign fragment in %s", uri);
166 /* check that the path is valid */
167 fdesc = file_of_name(u->path);
169 ERROR("reference to unknown file %s", u->path);
172 if (fdesc->type != type_file) {
173 ERROR("reference to directory %s", u->path);
176 if ((fdesc->flags & flag_distributor_signature) != 0) {
177 ERROR("reference to signature %s", u->path);
180 fdesc->flags |= flag_referenced;
194 static int check_references(xmlNodePtr sinfo)
196 unsigned int i, n, flags;
202 elem = sinfo->children;
203 while (elem != NULL) {
204 if (is_element(elem, "Reference"))
205 if (check_one_reference(elem))
213 f = file_of_index(i++);
214 if (f->type == type_file) {
216 if (!(flags & (flag_signature | flag_referenced))) {
217 ERROR("file not referenced in signature: %s", f->name);
227 static int get_certificates(xmlNodePtr kinfo)
233 n1 = kinfo->children;
235 if (is_element(n1, "X509Data")) {
238 if (is_element(n2, "X509Certificate")) {
239 b = xmlNodeGetContent(n2);
241 ERROR("xmlNodeGetContent of X509Certificate failed");
244 rc = add_certificate_b64(b);
257 /* checks the current document */
258 static int checkdocument()
261 xmlNodePtr sinfo, svalue, kinfo, objs, rootsig;
265 rootsig = xmlDocGetRootElement(document);
266 if (!is_node(rootsig, "Signature")) {
267 ERROR("root element <Signature> not found");
271 sinfo = next_element(rootsig->children);
272 if (!is_node(sinfo, "SignedInfo")) {
273 ERROR("element <SignedInfo> not found");
277 svalue = next_element(sinfo->next);
278 if (!is_node(svalue, "SignatureValue")) {
279 ERROR("element <SignatureValue> not found");
283 kinfo = next_element(svalue->next);
284 if (is_node(kinfo, "KeyInfo")) {
291 rc = check_references(sinfo);
295 rc = xmlsec_verify(rootsig);
299 rc = get_certificates(kinfo);
305 /* verify the digital signature of the file described by 'fdesc' */
306 int verify_digsig(struct filedesc *fdesc)
310 assert ((fdesc->flags & flag_signature) != 0);
311 DEBUG("-- checking file %s",fdesc->name);
313 /* reset the flags */
315 clear_certificates();
317 /* reads and xml parses the signature file */
318 fd = openat(workdirfd, fdesc->name, O_RDONLY);
320 ERROR("cant't open file %s", fdesc->name);
323 document = xmlReadFd(fd, fdesc->name, NULL, 0);
325 if (document == NULL) {
326 ERROR("xml parse of file %s failed", fdesc->name);
330 res = checkdocument();
332 ERROR("previous error was during check of file %s", fdesc->name);
334 xmlFreeDoc(document);
338 /* check all the signature files */
339 int check_all_signatures()
343 struct filedesc *fdesc;
345 n = signature_count();
347 for (i = n ; i-- > 0 ; ) {
348 fdesc = signature_of_index(i);
349 irc = verify_digsig(fdesc);
357 /* create a signature of 'index' (0 for author, other values for distributors)
358 using the private 'key' (filename) and the certificates 'certs' (filenames)
360 int create_digsig(unsigned int index, const char *key, const char **certs)
362 struct filedesc *fdesc;
371 doc = xmlsec_create(index, key, certs);
375 /* instanciate the filename */
376 fdesc = create_signature(index);
380 /* save the doc as file */
381 fd = openat(workdirfd, fdesc->name, O_WRONLY|O_CREAT|O_TRUNC, 0644);
383 ERROR("cant open %s for write", fdesc->name);
386 ctx = xmlSaveToFd(fd, NULL, XML_SAVE_FORMAT);
388 ERROR("xmlSaveToFd failed for %s", fdesc->name);
391 len = xmlSaveDoc(ctx, doc);
393 ERROR("xmlSaveDoc to %s failed", fdesc->name);