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