work in progress
[src/app-framework-main.git] / src / wgtpkg-digsig.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16
17
18 #include <string.h>
19 #include <assert.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22
23 #include <libxml/parser.h>
24 #include <libxml/tree.h>
25 #include <libxml/uri.h>
26 #include <libxml/xmlsave.h>
27
28
29 #include "verbose.h"
30 #include "wgtpkg.h"
31
32
33
34 static const char uri_role_author[] = "http://www.w3.org/ns/widgets-digsig#role-author";
35 static const char uri_role_distributor[] = "http://www.w3.org/ns/widgets-digsig#role-distributor";
36 static const char uri_profile[] = "http://www.w3.org/ns/widgets-digsig#profile";
37
38
39 /* global data */
40 static xmlDocPtr document;  /* the document */
41
42 /* facility to get the first element node (skip text nodes) starting with 'node' */
43 static xmlNodePtr next_element(xmlNodePtr node)
44 {
45         while (node && node->type != XML_ELEMENT_NODE)
46                 node = node->next;
47         return node;
48 }
49
50 /* is the 'node' an element node of 'name'? */
51 static int is_element(xmlNodePtr node, const char *name)
52 {
53         return node->type == XML_ELEMENT_NODE
54                 && !strcmp(name, node->name);
55 }
56
57 /* is the 'node' an element node of 'name'? */
58 static int is_node(xmlNodePtr node, const char *name)
59 {
60         return node != NULL && is_element(node, name);
61 }
62
63 #if 0
64 /* facility to get the first element node (skip text nodes) starting with 'node' */
65 static xmlNodePtr next_element_type(xmlNodePtr node, const char *name)
66 {
67         while (node && node->type != XML_ELEMENT_NODE && strcmp(name, node->name))
68                 node = node->next;
69         return node;
70 }
71
72 /* search the element node of id. NOTE : not optimized at all */
73 static xmlNodePtr search_for(const char *attrname, const char *value)
74 {
75         char *val;
76         xmlNodePtr iter, next;
77         xmlNodePtr result;
78
79         result = NULL;
80         iter = xmlDocGetRootElement(document);
81         while (iter != NULL) {
82                 val = xmlGetProp(iter, attrname);
83                 if (val != NULL && !strcmp(val, value)) {
84                         if (result != NULL) {
85                                 ERROR("duplicated %s %s", attrname, value);
86                                 free(val);
87                                 return NULL;
88                         }
89                         result = iter;
90                 }
91                 free(val);
92                 next = next_element(iter->children);
93                 if (next == NULL) {
94                         /* no child, try sibling */
95                         next = next_element(iter->next);
96                         if (next == NULL) {
97                                 iter = iter->parent;
98                                 while (iter != NULL && next == NULL) {
99                                         next = next_element(iter->next);
100                                         iter = iter->parent;
101                                 }
102                         }
103                 }
104                 iter = next;
105         }
106         if (result == NULL)
107                 ERROR("node of %s '%s' not found", attrname, value);
108         return result;
109 }
110
111 /* search the element node of id. NOTE : not optimized at all */
112 static xmlNodePtr search_id(const char *id)
113 {
114         return search_for("Id", id);
115 }
116 #endif
117
118 /* check the digest of one element */
119 static int check_one_reference(xmlNodePtr ref)
120 {
121         int rc;
122         char *uri;
123         xmlURIPtr u;
124         struct filedesc *fdesc;
125
126         /* start */
127         rc = -1;
128
129         /* get the uri */
130         uri = xmlGetProp(ref, "URI");
131         if (uri == NULL) {
132                 ERROR("attribute URI of element <Reference> not found");
133                 goto error;
134         }
135
136         /* parse the uri */
137         u = xmlParseURI(uri);
138         if (!u) {
139                 ERROR("error while parsing URI %s", uri);
140                 goto error2;
141         }
142
143         /* check that unexpected parts are not there */
144         if (u->scheme || u->opaque || u->authority || u->server || u->user || u->query) {
145                 ERROR("unexpected uri component in %s", uri);
146                 goto error3;
147         }
148
149         /* check path and fragment */
150         if (!u->path && !u->fragment) {
151                 ERROR("invalid uri %s", uri);
152                 goto error3;
153         }
154         if (u->path && u->fragment) {
155                 ERROR("not allowed to sign foreign fragment in %s", uri);
156                 goto error3;
157         }
158
159         if (u->path) {
160                 /* check that the path is valid */
161                 fdesc = file_of_name(u->path);
162                 if (fdesc == NULL) {
163                         ERROR("reference to unknown file %s", u->path);
164                         goto error3;
165                 }
166                 if (fdesc->type != type_file) {
167                         ERROR("reference to directory %s", u->path);
168                         goto error3;
169                 }
170                 if ((fdesc->flags & flag_distributor_signature) != 0) {
171                         ERROR("reference to signature %s", u->path);
172                         goto error3;
173                 }
174                 fdesc->flags |= flag_referenced;
175                 rc = 0;
176         } else {
177                 rc = 0;
178         }
179
180 error3:
181         xmlFreeURI(u);
182 error2:
183         xmlFree(uri);
184 error:
185         return rc;
186 }
187
188 static int check_references(xmlNodePtr sinfo)
189 {
190         unsigned int i, n, flags;
191         struct filedesc *f;
192         int result;
193         xmlNodePtr elem;
194
195         result = 0;
196         elem = sinfo->children;
197         while (elem != NULL) {
198                 if (is_element(elem, "Reference"))
199                         if (check_one_reference(elem))
200                                 result = -1;
201                 elem = elem->next;
202         }
203
204         n = file_count();
205         i = 0;
206         while(i < n) {
207                 f = file_of_index(i++);
208                 if (f->type == type_file) {
209                         flags = f->flags;
210                         if (!(flags & (flag_signature | flag_referenced))) {
211                                 ERROR("file not referenced in signature: %s", f->name);
212                                 result = -1;
213                         }
214                 }
215         }
216
217         return result;
218 }
219
220
221 static int get_certificates(xmlNodePtr kinfo)
222 {
223         xmlNodePtr n1, n2;
224         char *b;
225         int rc;
226
227         n1 = kinfo->children;
228         while (n1) {
229                 if (is_element(n1, "X509Data")) {
230                         n2 = n1->children;
231                         while (n2) {
232                                 if (is_element(n2, "X509Certificate")) {
233                                         b = xmlNodeGetContent(n2);
234                                         if (b == NULL) {
235                                                 ERROR("xmlNodeGetContent of X509Certificate failed");
236                                                 return -1;
237                                         }
238                                         rc = add_certificate_b64(b);
239                                         xmlFree(b);
240                                         if (rc)
241                                                 return rc;
242                                 }
243                                 n2 = n2->next;
244                         }
245                 }
246                 n1 = n1->next;
247         }
248         return 0;
249 }
250
251 /* checks the current document */
252 static int checkdocument()
253 {
254         int rc;
255         xmlNodePtr sinfo, svalue, kinfo, objs, rootsig;
256
257         rc = -1;
258
259         rootsig = xmlDocGetRootElement(document);
260         if (!is_node(rootsig, "Signature")) {
261                 ERROR("root element <Signature> not found");
262                 goto error;
263         }
264
265         sinfo = next_element(rootsig->children);
266         if (!is_node(sinfo, "SignedInfo")) {
267                 ERROR("element <SignedInfo> not found");
268                 goto error;
269         }
270
271         svalue = next_element(sinfo->next);
272         if (!is_node(svalue, "SignatureValue")) {
273                 ERROR("element <SignatureValue> not found");
274                 goto error;
275         }
276
277         kinfo = next_element(svalue->next);
278         if (is_node(kinfo, "KeyInfo")) {
279                 objs = kinfo->next;
280         } else {
281                 objs = kinfo;
282                 kinfo = NULL;
283         }
284
285         rc = check_references(sinfo);
286         if (rc)
287                 goto error;
288
289         rc = xmlsec_verify(rootsig);
290         if (rc)
291                 goto error;
292
293         rc = get_certificates(kinfo);
294
295 error:
296         return rc;
297 }
298
299 /* verify the digital signature of the file described by 'fdesc' */
300 int verify_digsig(struct filedesc *fdesc)
301 {
302         int res, fd;
303
304         assert ((fdesc->flags & flag_signature) != 0);
305         DEBUG("-- checking file %s",fdesc->name);
306
307         /* reset the flags */
308         file_clear_flags();
309         clear_certificates();
310
311         /* reads and xml parses the signature file */
312         fd = openat(workdirfd, fdesc->name, O_RDONLY);
313         if (fd < 0) {
314                 ERROR("cant't open file %s", fdesc->name);
315                 return -1;
316         }
317         document = xmlReadFd(fd, fdesc->name, NULL, 0);
318         close(fd);
319         if (document == NULL) {
320                 ERROR("xml parse of file %s failed", fdesc->name);
321                 return -1;
322         }
323
324         res = checkdocument();
325         if (res)
326                 ERROR("previous error was during check of file %s", fdesc->name);
327
328         xmlFreeDoc(document);
329         return res;
330 }
331
332 /* check all the signature files */
333 int check_all_signatures()
334 {
335         int rc, irc;
336         unsigned int i, n;
337         struct filedesc *fdesc;
338
339         n = signature_count();
340         rc = 0;
341         for (i = n ; i-- > 0 ; ) {
342                 fdesc = signature_of_index(i);
343                 irc = verify_digsig(fdesc);
344                 if (!irc)
345                         rc = irc;
346         }
347
348         return rc;
349 }
350
351 /* create a signature of 'index' (0 for author, other values for distributors)
352 using the private 'key' (filename) and the certificates 'certs' (filenames)
353 as trusted chain */
354 int create_digsig(int index, const char *key, const char **certs)
355 {
356         struct filedesc *fdesc;
357         xmlDocPtr doc;
358         int rc, len, fd;
359         xmlSaveCtxtPtr ctx;
360
361         rc = -1;
362
363         /* create the doc */
364         doc = xmlsec_create(index, key, certs);
365         if (doc == NULL)
366                 goto error;
367
368         /* instanciate the filename */
369         fdesc = create_signature(index);
370         if (fdesc == NULL)
371                 goto error2;
372
373         /* save the doc as file */
374         fd = openat(workdirfd, fdesc->name, O_WRONLY|O_CREAT|O_TRUNC, 0644);
375         if (fd < 0) {
376                 ERROR("cant open %s for write", fdesc->name);
377                 goto error2;
378         }
379         ctx = xmlSaveToFd(fd, NULL, XML_SAVE_FORMAT);
380         if (!ctx) {
381                 ERROR("xmlSaveToFd failed for %s", fdesc->name);
382                 goto error3;
383         }
384         len = xmlSaveDoc(ctx, doc);
385         if (len < 0) {
386                 ERROR("xmlSaveDoc to %s failed", fdesc->name);
387                 goto error4;
388         }
389
390         rc = 0;
391 error4:
392         xmlSaveClose(ctx);
393 error3:
394         close(fd);
395 error2:
396         xmlFreeDoc(doc);
397 error:
398         return rc;
399 }
400
401