Require libzip >= 1.0
[src/app-framework-main.git] / 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_id(const char *id)
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, "Id");
80                 if (val != NULL && !strcmp(val, id)) {
81                         if (result != NULL) {
82                                 syslog(LOG_ERR, "duplicated Id %s", id);
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 Id '%s' not found", id);
105         return result;
106 }
107 #endif
108
109 /* check the digest of one element */
110 static int check_one_reference(xmlNodePtr ref)
111 {
112         int rc;
113         char *uri;
114         xmlURIPtr u;
115         struct filedesc *fdesc;
116
117         /* start */
118         rc = -1;
119
120         /* get the uri */
121         uri = xmlGetProp(ref, "URI");
122         if (uri == NULL) {
123                 syslog(LOG_ERR, "attribute URI of element <Reference> not found");
124                 goto error;
125         }
126
127         /* parse the uri */
128         u = xmlParseURI(uri);
129         if (u == NULL) {
130                 syslog(LOG_ERR, "error while parsing URI %s", uri);
131                 goto error2;
132         }
133
134         if (u->scheme || u->opaque || u->authority || u->server || u->user || u->query) {
135                 syslog(LOG_ERR, "unexpected uri component in %s", uri);
136                 goto error3;
137         }
138
139         if (u->path && u->fragment) {
140                 syslog(LOG_ERR, "not allowed to sign foreign fragment in %s", uri);
141                 goto error3;
142         }
143
144         if (u->path) {
145                 fdesc = file_of_name(u->path);
146                 if (fdesc == NULL) {
147                         syslog(LOG_ERR, "reference to unknown file %s", u->path);
148                         goto error3;
149                 }
150                 if (fdesc->type != type_file) {
151                         syslog(LOG_ERR, "reference to directory %s", u->path);
152                         goto error3;
153                 }
154                 if ((fdesc->flags & flag_distributor_signature) != 0) {
155                         syslog(LOG_ERR, "reference to signature %s", u->path);
156                         goto error3;
157                 }
158                 fdesc->flags |= flag_referenced;
159                 rc = 0;
160         } else {
161                 rc = 0;
162         }
163
164 error3:
165         xmlFreeURI(u);
166 error2:
167         xmlFree(uri);
168 error:
169         return rc;
170 }
171
172 static int check_references(xmlNodePtr sinfo)
173 {
174         xmlNodePtr elem;
175
176         elem = sinfo->children;
177         while (elem != NULL) {
178                 if (is_element(elem, "Reference"))
179                         if (check_one_reference(elem))
180                                 return -1;
181                 elem = elem->next;
182         }
183         return 0;
184 }
185
186 static int get_certificates(xmlNodePtr kinfo)
187 {
188         xmlNodePtr n1, n2;
189         char *b;
190         int rc;
191
192         n1 = kinfo->children;
193         while (n1) {
194                 if (is_element(n1, "X509Data")) {
195                         n2 = n1->children;
196                         while (n2) {
197                                 if (is_element(n2, "X509Certificate")) {
198                                         b = xmlNodeGetContent(n2);
199                                         if (b == NULL) {
200                                                 syslog(LOG_ERR, "xmlNodeGetContent of X509Certificate failed");
201                                                 return -1;
202                                         }
203                                         rc = add_certificate_b64(b);
204                                         xmlFree(b);
205                                         if (rc)
206                                                 return rc;
207                                 }
208                                 n2 = n2->next;
209                         }
210                 }
211                 n1 = n1->next;
212         }
213         return 0;
214 }
215
216 static int checkdocument()
217 {
218         int rc;
219         xmlNodePtr sinfo, svalue, kinfo, objs, rootsig;
220
221         rc = -1;
222
223         rootsig = xmlDocGetRootElement(document);
224         if (!is_node(rootsig, "Signature")) {
225                 syslog(LOG_ERR, "root element <Signature> not found");
226                 goto error;
227         }
228
229         sinfo = next_element(rootsig->children);
230         if (!is_node(sinfo, "SignedInfo")) {
231                 syslog(LOG_ERR, "element <SignedInfo> not found");
232                 goto error;
233         }
234
235         svalue = next_element(sinfo->next);
236         if (!is_node(svalue, "SignatureValue")) {
237                 syslog(LOG_ERR, "element <SignatureValue> not found");
238                 goto error;
239         }
240
241         kinfo = next_element(svalue->next);
242         if (is_node(kinfo, "KeyInfo")) {
243                 objs = kinfo->next;
244         } else {
245                 objs = kinfo;
246                 kinfo = NULL;
247         }
248
249         rc = check_references(sinfo);
250         if (rc)
251                 goto error;
252
253         rc = xmlsec_verify(rootsig);
254         if (rc)
255                 goto error;
256
257         rc = get_certificates(kinfo);
258
259 error:
260         return rc;
261 }
262
263 int verify_digsig(struct filedesc *fdesc)
264 {
265         int res;
266
267         assert ((fdesc->flags & flag_signature) != 0);
268 printf("\n\nchecking file %s\n\n",fdesc->name);
269
270         /* reset the flags */
271         file_clear_flags();
272         clear_certificates();
273
274         /* reads and xml parses the signature file */
275         document = xmlReadFile(fdesc->name, NULL, 0);
276         if (document == NULL) {
277                 syslog(LOG_ERR, "xml parse of file %s failed", fdesc->name);
278                 return -1;
279         }
280
281         res = checkdocument();
282         if (res)
283                 syslog(LOG_ERR, "previous error was during check of file %s", fdesc->name);
284
285         xmlFreeDoc(document);
286         return res;
287 }
288
289 int check_all_signatures()
290 {
291         int rc, irc;
292         unsigned int i, n;
293         struct filedesc *fdesc;
294
295         n = signature_count();
296         rc = 0;
297         for (i = n ; i-- > 0 ; ) {
298                 fdesc = signature_of_index(i);
299                 assert ((fdesc->flags & flag_signature) != 0);
300                 irc = verify_digsig(fdesc);
301                 if (!irc)
302                         rc = irc;
303         }
304
305         return rc;
306 }
307
308 int create_digsig(int index, const char *key, const char **certs)
309 {
310         struct filedesc *fdesc;
311         xmlDocPtr doc;
312         int rc, len;
313
314         rc = -1;
315         doc = xmlsec_create(index, key, certs);
316         if (doc == NULL)
317                 goto error;
318
319         fdesc = create_signature(index);
320         if (fdesc == NULL)
321                 goto error2;
322
323         len = xmlSaveFormatFileEnc(fdesc->name, doc, NULL, 0);
324         if (len < 0) {
325                 syslog(LOG_ERR, "xmlSaveFormatFileEnc to %s failed", fdesc->name);
326                 goto error2;
327         }
328
329         rc = 0;
330 error2:
331         xmlFreeDoc(doc);
332 error:
333         return rc;
334 }
335
336