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>
36 static const char uri_role_author[] = "http://www.w3.org/ns/widgets-digsig#role-author";
37 static const char uri_role_distributor[] = "http://www.w3.org/ns/widgets-digsig#role-distributor";
38 static const char uri_profile[] = "http://www.w3.org/ns/widgets-digsig#profile";
42 static xmlDocPtr document; /* the document */
44 /* facility to get the first element node (skip text nodes) starting with 'node' */
45 static xmlNodePtr next_element(xmlNodePtr node)
47 while (node && node->type != XML_ELEMENT_NODE)
52 /* is the 'node' an element node of 'name'? */
53 static int is_element(xmlNodePtr node, const char *name)
55 return node->type == XML_ELEMENT_NODE
56 && !strcmp(name, node->name);
59 /* is the 'node' an element node of 'name'? */
60 static int is_node(xmlNodePtr node, const char *name)
62 return node != NULL && is_element(node, name);
66 /* facility to get the first element node (skip text nodes) starting with 'node' */
67 static xmlNodePtr next_element_type(xmlNodePtr node, const char *name)
69 while (node && node->type != XML_ELEMENT_NODE && strcmp(name, node->name))
74 /* search the element node of id. NOTE : not optimized at all */
75 static xmlNodePtr search_for(const char *attrname, const char *value)
78 xmlNodePtr iter, next;
82 iter = xmlDocGetRootElement(document);
83 while (iter != NULL) {
84 val = xmlGetProp(iter, attrname);
85 if (val != NULL && !strcmp(val, value)) {
87 ERROR("duplicated %s %s", attrname, value);
94 next = next_element(iter->children);
96 /* no child, try sibling */
97 next = next_element(iter->next);
100 while (iter != NULL && next == NULL) {
101 next = next_element(iter->next);
109 ERROR("node of %s '%s' not found", attrname, value);
113 /* search the element node of id. NOTE : not optimized at all */
114 static xmlNodePtr search_id(const char *id)
116 return search_for("Id", id);
120 /* check the digest of one element */
121 static int check_one_reference(xmlNodePtr ref)
126 struct filedesc *fdesc;
132 uri = xmlGetProp(ref, "URI");
134 ERROR("attribute URI of element <Reference> not found");
139 u = xmlParseURI(uri);
141 ERROR("error while parsing URI %s", uri);
145 /* check that unexpected parts are not there */
146 if (u->scheme || u->opaque || u->authority || u->server || u->user || u->query) {
147 ERROR("unexpected uri component in %s", uri);
151 /* check path and fragment */
152 if (!u->path && !u->fragment) {
153 ERROR("invalid uri %s", uri);
156 if (u->path && u->fragment) {
157 ERROR("not allowed to sign foreign fragment in %s", uri);
162 /* check that the path is valid */
163 fdesc = file_of_name(u->path);
165 ERROR("reference to unknown file %s", u->path);
168 if (fdesc->type != type_file) {
169 ERROR("reference to directory %s", u->path);
172 if ((fdesc->flags & flag_distributor_signature) != 0) {
173 ERROR("reference to signature %s", u->path);
176 fdesc->flags |= flag_referenced;
190 static int check_references(xmlNodePtr sinfo)
192 unsigned int i, n, flags;
198 elem = sinfo->children;
199 while (elem != NULL) {
200 if (is_element(elem, "Reference"))
201 if (check_one_reference(elem))
209 f = file_of_index(i++);
210 if (f->type == type_file) {
212 if (!(flags & (flag_signature | flag_referenced))) {
213 ERROR("file not referenced in signature: %s", f->name);
223 static int get_certificates(xmlNodePtr kinfo)
229 n1 = kinfo->children;
231 if (is_element(n1, "X509Data")) {
234 if (is_element(n2, "X509Certificate")) {
235 b = xmlNodeGetContent(n2);
237 ERROR("xmlNodeGetContent of X509Certificate failed");
240 rc = add_certificate_b64(b);
253 /* checks the current document */
254 static int checkdocument()
257 xmlNodePtr sinfo, svalue, kinfo, objs, rootsig;
261 rootsig = xmlDocGetRootElement(document);
262 if (!is_node(rootsig, "Signature")) {
263 ERROR("root element <Signature> not found");
267 sinfo = next_element(rootsig->children);
268 if (!is_node(sinfo, "SignedInfo")) {
269 ERROR("element <SignedInfo> not found");
273 svalue = next_element(sinfo->next);
274 if (!is_node(svalue, "SignatureValue")) {
275 ERROR("element <SignatureValue> not found");
279 kinfo = next_element(svalue->next);
280 if (is_node(kinfo, "KeyInfo")) {
287 rc = check_references(sinfo);
291 rc = xmlsec_verify(rootsig);
295 rc = get_certificates(kinfo);
301 /* verify the digital signature of the file described by 'fdesc' */
302 int verify_digsig(struct filedesc *fdesc)
306 assert ((fdesc->flags & flag_signature) != 0);
307 DEBUG("-- checking file %s",fdesc->name);
309 /* reset the flags */
311 clear_certificates();
313 /* reads and xml parses the signature file */
314 fd = openat(workdirfd, fdesc->name, O_RDONLY);
316 ERROR("cant't open file %s", fdesc->name);
319 document = xmlReadFd(fd, fdesc->name, NULL, 0);
321 if (document == NULL) {
322 ERROR("xml parse of file %s failed", fdesc->name);
326 res = checkdocument();
328 ERROR("previous error was during check of file %s", fdesc->name);
330 xmlFreeDoc(document);
334 /* check all the signature files */
335 int check_all_signatures()
339 struct filedesc *fdesc;
341 n = signature_count();
343 for (i = n ; i-- > 0 ; ) {
344 fdesc = signature_of_index(i);
345 irc = verify_digsig(fdesc);
353 /* create a signature of 'index' (0 for author, other values for distributors)
354 using the private 'key' (filename) and the certificates 'certs' (filenames)
356 int create_digsig(int index, const char *key, const char **certs)
358 struct filedesc *fdesc;
366 doc = xmlsec_create(index, key, certs);
370 /* instanciate the filename */
371 fdesc = create_signature(index);
375 /* save the doc as file */
376 fd = openat(workdirfd, fdesc->name, O_WRONLY|O_CREAT|O_TRUNC, 0644);
378 ERROR("cant open %s for write", fdesc->name);
381 ctx = xmlSaveToFd(fd, NULL, XML_SAVE_FORMAT);
383 ERROR("xmlSaveToFd failed for %s", fdesc->name);
386 len = xmlSaveDoc(ctx, doc);
388 ERROR("xmlSaveDoc to %s failed", fdesc->name);