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