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