refactoring sources
[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 <syslog.h>
20 #include <assert.h>
21
22 #include <libxml/parser.h>
23 #include <libxml/tree.h>
24 #include <libxml/uri.h>
25
26
27 #include "wgtpkg.h"
28
29
30
31 static const char uri_role_author[] = "http://www.w3.org/ns/widgets-digsig#role-author";
32 static const char uri_role_distributor[] = "http://www.w3.org/ns/widgets-digsig#role-distributor";
33 static const char uri_profile[] = "http://www.w3.org/ns/widgets-digsig#profile";
34
35
36 /* global data */
37 static xmlDocPtr document;  /* the document */
38
39 /* facility to get the first element node (skip text nodes) starting with 'node' */
40 static xmlNodePtr next_element(xmlNodePtr node)
41 {
42         while (node && node->type != XML_ELEMENT_NODE)
43                 node = node->next;
44         return node;
45 }
46
47 /* is the 'node' an element node of 'name'? */
48 static int is_element(xmlNodePtr node, const char *name)
49 {
50         return node->type == XML_ELEMENT_NODE
51                 && !strcmp(name, node->name);
52 }
53
54 /* is the 'node' an element node of 'name'? */
55 static int is_node(xmlNodePtr node, const char *name)
56 {
57         return node != NULL && is_element(node, name);
58 }
59
60 #if 0
61 /* facility to get the first element node (skip text nodes) starting with 'node' */
62 static xmlNodePtr next_element_type(xmlNodePtr node, const char *name)
63 {
64         while (node && node->type != XML_ELEMENT_NODE && strcmp(name, node->name))
65                 node = node->next;
66         return node;
67 }
68
69 /* search the element node of id. NOTE : not optimized at all */
70 static xmlNodePtr search_for(const char *attrname, const char *value)
71 {
72         char *val;
73         xmlNodePtr iter, next;
74         xmlNodePtr result;
75
76         result = NULL;
77         iter = xmlDocGetRootElement(document);
78         while (iter != NULL) {
79                 val = xmlGetProp(iter, attrname);
80                 if (val != NULL && !strcmp(val, value)) {
81                         if (result != NULL) {
82                                 syslog(LOG_ERR, "duplicated %s %s", attrname, value);
83                                 free(val);
84                                 return NULL;
85                         }
86                         result = iter;
87                 }
88                 free(val);
89                 next = next_element(iter->children);
90                 if (next == NULL) {
91                         /* no child, try sibling */
92                         next = next_element(iter->next);
93                         if (next == NULL) {
94                                 iter = iter->parent;
95                                 while (iter != NULL && next == NULL) {
96                                         next = next_element(iter->next);
97                                         iter = iter->parent;
98                                 }
99                         }
100                 }
101                 iter = next;
102         }
103         if (result == NULL)
104                 syslog(LOG_ERR, "node of %s '%s' not found", attrname, value);
105         return result;
106 }
107
108 /* search the element node of id. NOTE : not optimized at all */
109 static xmlNodePtr search_id(const char *id)
110 {
111         return search_for("Id", id);
112 }
113 #endif
114
115 /* check the digest of one element */
116 static int check_one_reference(xmlNodePtr ref)
117 {
118         int rc;
119         char *uri;
120         xmlURIPtr u;
121         struct filedesc *fdesc;
122
123         /* start */
124         rc = -1;
125
126         /* get the uri */
127         uri = xmlGetProp(ref, "URI");
128         if (uri == NULL) {
129                 syslog(LOG_ERR, "attribute URI of element <Reference> not found");
130                 goto error;
131         }
132
133         /* parse the uri */
134         u = xmlParseURI(uri);
135         if (!u) {
136                 syslog(LOG_ERR, "error while parsing URI %s", uri);
137                 goto error2;
138         }
139
140         /* check that unexpected parts are not there */
141         if (u->scheme || u->opaque || u->authority || u->server || u->user || u->query) {
142                 syslog(LOG_ERR, "unexpected uri component in %s", uri);
143                 goto error3;
144         }
145
146         /* check path and fragment */
147         if (!u->path && !u->fragment) {
148                 syslog(LOG_ERR, "invalid uri %s", uri);
149                 goto error3;
150         }
151         if (u->path && u->fragment) {
152                 syslog(LOG_ERR, "not allowed to sign foreign fragment in %s", uri);
153                 goto error3;
154         }
155
156         if (u->path) {
157                 /* check that the path is valid */
158                 fdesc = file_of_name(u->path);
159                 if (fdesc == NULL) {
160                         syslog(LOG_ERR, "reference to unknown file %s", u->path);
161                         goto error3;
162                 }
163                 if (fdesc->type != type_file) {
164                         syslog(LOG_ERR, "reference to directory %s", u->path);
165                         goto error3;
166                 }
167                 if ((fdesc->flags & flag_distributor_signature) != 0) {
168                         syslog(LOG_ERR, "reference to signature %s", u->path);
169                         goto error3;
170                 }
171                 fdesc->flags |= flag_referenced;
172                 rc = 0;
173         } else {
174                 rc = 0;
175         }
176
177 error3:
178         xmlFreeURI(u);
179 error2:
180         xmlFree(uri);
181 error:
182         return rc;
183 }
184
185 static int check_references(xmlNodePtr sinfo)
186 {
187         xmlNodePtr elem;
188
189         elem = sinfo->children;
190         while (elem != NULL) {
191                 if (is_element(elem, "Reference"))
192                         if (check_one_reference(elem))
193                                 return -1;
194                 elem = elem->next;
195         }
196         return 0;
197 }
198
199 static int get_certificates(xmlNodePtr kinfo)
200 {
201         xmlNodePtr n1, n2;
202         char *b;
203         int rc;
204
205         n1 = kinfo->children;
206         while (n1) {
207                 if (is_element(n1, "X509Data")) {
208                         n2 = n1->children;
209                         while (n2) {
210                                 if (is_element(n2, "X509Certificate")) {
211                                         b = xmlNodeGetContent(n2);
212                                         if (b == NULL) {
213                                                 syslog(LOG_ERR, "xmlNodeGetContent of X509Certificate failed");
214                                                 return -1;
215                                         }
216                                         rc = add_certificate_b64(b);
217                                         xmlFree(b);
218                                         if (rc)
219                                                 return rc;
220                                 }
221                                 n2 = n2->next;
222                         }
223                 }
224                 n1 = n1->next;
225         }
226         return 0;
227 }
228
229 /* checks the current document */
230 static int checkdocument()
231 {
232         int rc;
233         xmlNodePtr sinfo, svalue, kinfo, objs, rootsig;
234
235         rc = -1;
236
237         rootsig = xmlDocGetRootElement(document);
238         if (!is_node(rootsig, "Signature")) {
239                 syslog(LOG_ERR, "root element <Signature> not found");
240                 goto error;
241         }
242
243         sinfo = next_element(rootsig->children);
244         if (!is_node(sinfo, "SignedInfo")) {
245                 syslog(LOG_ERR, "element <SignedInfo> not found");
246                 goto error;
247         }
248
249         svalue = next_element(sinfo->next);
250         if (!is_node(svalue, "SignatureValue")) {
251                 syslog(LOG_ERR, "element <SignatureValue> not found");
252                 goto error;
253         }
254
255         kinfo = next_element(svalue->next);
256         if (is_node(kinfo, "KeyInfo")) {
257                 objs = kinfo->next;
258         } else {
259                 objs = kinfo;
260                 kinfo = NULL;
261         }
262
263         rc = check_references(sinfo);
264         if (rc)
265                 goto error;
266
267         rc = xmlsec_verify(rootsig);
268         if (rc)
269                 goto error;
270
271         rc = get_certificates(kinfo);
272
273 error:
274         return rc;
275 }
276
277 /* verify the digital signature of the file described by 'fdesc' */
278 int verify_digsig(struct filedesc *fdesc)
279 {
280         int res;
281
282         assert ((fdesc->flags & flag_signature) != 0);
283         debug("-- checking file %s",fdesc->name);
284
285         /* reset the flags */
286         file_clear_flags();
287         clear_certificates();
288
289         /* reads and xml parses the signature file */
290         document = xmlReadFile(fdesc->name, NULL, 0);
291         if (document == NULL) {
292                 syslog(LOG_ERR, "xml parse of file %s failed", fdesc->name);
293                 return -1;
294         }
295
296         res = checkdocument();
297         if (res)
298                 syslog(LOG_ERR, "previous error was during check of file %s", fdesc->name);
299
300         xmlFreeDoc(document);
301         return res;
302 }
303
304 /* check all the signature files */
305 int check_all_signatures()
306 {
307         int rc, irc;
308         unsigned int i, n;
309         struct filedesc *fdesc;
310
311         n = signature_count();
312         rc = 0;
313         for (i = n ; i-- > 0 ; ) {
314                 fdesc = signature_of_index(i);
315                 irc = verify_digsig(fdesc);
316                 if (!irc)
317                         rc = irc;
318         }
319
320         return rc;
321 }
322
323 /* create a signature of 'index' (0 for author, other values for distributors)
324 using the private 'key' (filename) and the certificates 'certs' (filenames)
325 as trusted chain */
326 int create_digsig(int index, const char *key, const char **certs)
327 {
328         struct filedesc *fdesc;
329         xmlDocPtr doc;
330         int rc, len;
331
332         rc = -1;
333
334         /* create the doc */
335         doc = xmlsec_create(index, key, certs);
336         if (doc == NULL)
337                 goto error;
338
339         /* instanciate the filename */
340         fdesc = create_signature(index);
341         if (fdesc == NULL)
342                 goto error2;
343
344         /* save the doc as file */
345         len = xmlSaveFormatFileEnc(fdesc->name, doc, NULL, 0);
346         if (len < 0) {
347                 syslog(LOG_ERR, "xmlSaveFormatFileEnc to %s failed", fdesc->name);
348                 goto error2;
349         }
350
351         rc = 0;
352 error2:
353         xmlFreeDoc(doc);
354 error:
355         return rc;
356 }
357
358