daa0cff2198f91ec5f056f17e699efd6e7be7855
[src/app-framework-main.git] / src / wgtpkg-xmlsec.c
1 /*
2  Copyright (C) 2015-2018 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 <unistd.h>
21 #include <stdio.h>
22 #include <dirent.h>
23 #include <string.h>
24 #include <assert.h>
25 #include <fcntl.h>
26
27 #include <libxml/tree.h>
28 #include <xmlsec/xmlsec.h>
29 #include <xmlsec/xmltree.h>
30 #include <xmlsec/xmldsig.h>
31 #include <xmlsec/crypto.h>
32 #include <xmlsec/templates.h>
33 #include <xmlsec/errors.h>
34 #include <xmlsec/io.h>
35
36
37 #include "verbose.h"
38 #include "wgtpkg-files.h"
39 #include "wgtpkg-workdir.h"
40 #include "wgtpkg-xmlsec.h"
41
42 static int initstatus;
43 static int initdone;
44 static xmlSecKeysMngrPtr keymgr;
45
46 static const char trusted_certificates_directory[] = WGTPKG_TRUSTED_CERT_DIR;
47
48 /* checks if a file match  uri (should not be a distributor signature) */
49 static int file_match_cb(const char *uri)
50 {
51         struct filedesc *fdesc = file_of_name(uri);
52         return fdesc != NULL && fdesc->type == type_file && (fdesc->flags & flag_distributor_signature) == 0;
53 }
54
55 /* open the file of uri */
56 static void *file_open_cb(const char *file)
57 {
58         struct filedesc *fdesc;
59         int fd;
60         FILE *f;
61
62         fdesc = file_of_name(file);
63         if (fdesc == NULL) {
64                 ERROR("shouldn't open uri %s", file);
65                 return NULL;
66         }
67
68         fd = openat(workdirfd, file, O_RDONLY);
69         f = fd < 0 ? NULL : fdopen(fd, "r");
70         if (f == NULL) {
71                 ERROR("can't open file %s for reading", file);
72                 if (fd >= 0)
73                         close(fd);
74         } else
75                 fdesc->flags |= flag_opened;
76
77         return f;
78 }
79
80 /* read the opened file */
81 static int file_read_cb(void *context, char *buffer, int len)
82 {
83         size_t r = fread(buffer, 1, (unsigned)len, (FILE*)context);
84         return r ? (int)r : feof((FILE*)context) ? 0 : - 1;
85 }
86
87 /* close the opened file */
88 static int file_close_cb(void *context)
89 {
90         return (int)fclose((FILE*)context);
91 }
92
93 /* echo an error message */
94 static void errors_cb(const char *file, int line, const char *func, const char *errorObject, const char *errorSubject, int reason, const char *msg)
95 {
96         ERROR("xmlSec error %3d: %s (subject=\"%s\", object=\"%s\")", reason, msg, errorSubject ? errorSubject : "?", errorObject ? errorObject : "?");
97 }
98
99 /* fills database with trusted keys */
100 static int fill_trusted_keys_file(const char *file)
101 {
102         int err = xmlSecCryptoAppKeysMngrCertLoad(keymgr, file, xmlSecKeyDataFormatPem, xmlSecKeyDataTypeTrusted);
103         if (err < 0) {
104                 ERROR("xmlSecCryptoAppKeysMngrCertLoadMemory failed for %s", file);
105                 return -1;
106         }
107         return 0;
108 }
109
110 /* fills database with trusted keys */
111 static int fill_trusted_keys_dir(const char *directory)
112 {
113         int err;
114         DIR *dir;
115         struct dirent *ent;
116         char path[PATH_MAX], *e;
117
118         e = stpcpy(path, directory);
119         dir = opendir(path);
120         if (!dir) {
121                 ERROR("opendir %s failed in fill_trusted_keys_dir", path);
122                 return -1;
123         }
124
125         *e++ = '/';
126         ent = readdir(dir);
127         while (ent != NULL) {
128                 if (ent->d_type == DT_REG) {
129                         strcpy(e, ent->d_name);
130                         err = fill_trusted_keys_file(path);
131                         if (err < 0) {
132                                 closedir(dir);
133                                 return -1;
134                         }
135                 }
136                 ent = readdir(dir);
137         }
138
139         closedir(dir);
140         return 0;
141         
142 }
143
144 /* initialisation of access to xmlsec */
145 int xmlsec_init()
146 {
147
148         if (initdone)
149                 goto end;
150
151         initdone = 1;
152         initstatus = -1;
153
154         if(xmlSecInit() < 0) {
155                 ERROR("xmlSecInit failed.");
156                 goto end;
157         }
158
159 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
160         if(xmlSecCryptoDLLoadLibrary(XMLSEC_CRYPTO) < 0) {
161                 ERROR("xmlSecCryptoDLLoadLibrary %s failed.", XMLSEC_CRYPTO);
162                 goto end;
163         }
164 #endif
165
166         if(xmlSecCryptoAppInit(NULL) < 0) {
167                 ERROR("xmlSecCryptoAppInit failed.");
168                 goto end;
169         }
170
171         if(xmlSecCryptoInit() < 0) {
172                 ERROR("xmlSecCryptoInit failed.");
173                 goto end;
174         }
175
176         xmlSecErrorsSetCallback(errors_cb);
177
178         xmlSecIOCleanupCallbacks();
179         if (xmlSecIORegisterCallbacks(file_match_cb,
180                                         file_open_cb, file_read_cb, file_close_cb)) {
181                 ERROR("xmlSecIORegisterCallbacks failed.");
182                 goto end;
183         }
184
185         keymgr = xmlSecKeysMngrCreate();
186         if (keymgr == NULL) {
187                 ERROR("xmlSecKeysMngrCreate failed.");
188                 goto end;
189         }
190
191         if(xmlSecCryptoAppDefaultKeysMngrInit(keymgr) < 0) {
192                 ERROR("xmlSecCryptoAppDefaultKeysMngrInit failed.");
193                 goto end;
194         }
195         fill_trusted_keys_dir(trusted_certificates_directory);
196
197         initstatus = 0;
198 end:
199         return initstatus;
200 }
201
202 /* shuting down accesses to xmlsec */
203 void xmlsec_shutdown()
204 {
205         xmlSecKeysMngrDestroy(keymgr);
206
207         xmlSecCryptoShutdown();
208         
209         xmlSecCryptoAppShutdown();
210         
211         xmlSecShutdown();
212 }
213
214 /* verify a signature */
215 int xmlsec_verify(xmlNodePtr node)
216 {
217         int rc;
218         xmlSecDSigCtxPtr dsigctx;
219
220         assert(initdone && !initstatus);
221
222         dsigctx = xmlSecDSigCtxCreate(keymgr);
223         if (dsigctx == NULL) {
224                 ERROR("xmlSecDSigCtxCreate failed.");
225                 rc = -1;
226         } else {
227                 rc = xmlSecDSigCtxVerify(dsigctx, node);
228                 if (rc)
229                         ERROR("xmlSecDSigCtxVerify failed.");
230                 else if (dsigctx->status != xmlSecDSigStatusSucceeded) {
231                         ERROR("invalid signature.");
232                         rc = -1;
233                 }
234                 xmlSecDSigCtxDestroy(dsigctx);
235         }
236
237         return rc;
238 }
239
240 /* templates for properties of signature files */
241 static const struct { const char *id; const char *xml; } properties[2] = {
242         {
243                 .id = "AuthorSignature", /* template of properties for author signature */
244                 .xml =
245                         "<SignatureProperties xmlns:dsp=\"http://www.w3.org/2009/xmldsig-properties\">"
246                          "<SignatureProperty Id=\"profile\" Target=\"#AuthorSignature\">"
247                           "<dsp:Profile URI=\"http://www.w3.org/ns/widgets-digsig#profile\"></dsp:Profile>"
248                          "</SignatureProperty>"
249                          "<SignatureProperty Id=\"role\" Target=\"#AuthorSignature\">"
250                            "<dsp:Role URI=\"http://www.w3.org/ns/widgets-digsig#role-author\"></dsp:Role>"
251                          "</SignatureProperty>"
252                          "<SignatureProperty Id=\"identifier\" Target=\"#AuthorSignature\">"
253                            "<dsp:Identifier></dsp:Identifier>"
254                          "</SignatureProperty>"
255                         "</SignatureProperties>"
256         },
257         {
258                 .id = "DistributorSignature", /* template of properties for distributor signature */
259                 .xml = 
260                         "<SignatureProperties xmlns:dsp=\"http://www.w3.org/2009/xmldsig-properties\">"
261                          "<SignatureProperty Id=\"profile\" Target=\"#DistributorSignature\">"
262                           "<dsp:Profile URI=\"http://www.w3.org/ns/widgets-digsig#profile\"></dsp:Profile>"
263                          "</SignatureProperty>"
264                          "<SignatureProperty Id=\"role\" Target=\"#DistributorSignature\">"
265                            "<dsp:Role URI=\"http://www.w3.org/ns/widgets-digsig#role-distributor\"></dsp:Role>"
266                          "</SignatureProperty>"
267                          "<SignatureProperty Id=\"identifier\" Target=\"#DistributorSignature\">"
268                            "<dsp:Identifier></dsp:Identifier>"
269                          "</SignatureProperty>"
270                         "</SignatureProperties>"
271         }
272 };
273
274 /* create a signature of 'index' (0 for author, other values for distributors)
275 using the private 'key' (filename) and the certificates 'certs' (filenames)
276 as trusted chain */
277 xmlDocPtr xmlsec_create(unsigned int index, const char *key, const char **certs)
278 {
279         unsigned int i, fc, mask;
280         struct filedesc *fdesc;
281         xmlNodePtr sign, obj, ref, kinfo, props;
282         xmlDocPtr doc;
283         int rc;
284         xmlSecDSigCtxPtr dsigctx;
285
286         assert(initdone && !initstatus);
287
288         /* create the document */
289         doc = xmlNewDoc("1.0");
290         if (doc == NULL) {
291                 ERROR("xmlNewDoc failed");
292                 goto error;
293         }
294
295         /* create the root signature node */
296         sign = xmlSecTmplSignatureCreate(doc, xmlSecTransformInclC14N11Id, xmlSecTransformRsaSha256Id, properties[!!index].id);
297         if (sign == NULL) {
298                 ERROR("xmlSecTmplSignatureCreate failed");
299                 goto error2;
300         }
301         xmlDocSetRootElement(doc, sign);
302
303         /* create the object and its reference */
304         obj = xmlSecTmplSignatureAddObject(sign, "prop", NULL, NULL);
305         if (obj == NULL) {
306                 ERROR("xmlSecTmplSignatureAddObject failed");
307                 goto error2;
308         }
309         rc = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, properties[!!index].xml, &props);
310         if (rc) {
311                 ERROR("xmlParseBalancedChunkMemory failed");
312                 goto error2;
313         }
314         if (NULL == xmlAddChild(obj, props)) {
315                 ERROR("filling object node failed");
316                 xmlFreeNode(obj);
317                 goto error2;
318         }
319
320         /* create references to files */
321         mask = index ? flag_distributor_signature : flag_signature;
322         fc = file_count();
323         for (i = 0 ; i < fc ; i++) {
324                 fdesc = file_of_index(i);
325                 if (fdesc->type == type_file && (fdesc->flags & mask) == 0) {
326                         ref = xmlSecTmplSignatureAddReference(sign, xmlSecTransformSha256Id, NULL, fdesc->name, NULL);
327                         if (ref == NULL) {
328                                 ERROR("creation of reference to %s failed", fdesc->name);
329                                 goto error2;
330                         }
331                 }
332         }
333
334         /* create reference to object having properties */
335         ref =  xmlSecTmplSignatureAddReference(sign, xmlSecTransformSha256Id, NULL, "#prop", NULL);
336         if (ref == NULL) {
337                 ERROR("creation of reference to #prop failed");
338                 goto error2;
339         }
340         if (NULL == xmlSecTmplReferenceAddTransform(ref, xmlSecTransformInclC14N11Id)) {
341                 ERROR("setting transform reference to #prop failed");
342                 goto error2;
343         }
344
345         /* adds the X509 data */
346         kinfo = xmlSecTmplSignatureEnsureKeyInfo(sign, NULL);
347         if (kinfo == NULL) {
348                 ERROR("xmlSecTmplSignatureEnsureKeyInfo failed");
349                 goto error2;
350         }
351         if (NULL == xmlSecTmplKeyInfoAddX509Data(kinfo)) {
352                 ERROR("xmlSecTmplKeyInfoAddX509Data failed");
353                 goto error2;
354         }
355
356         /* sign now */
357         dsigctx = xmlSecDSigCtxCreate(keymgr);
358         if (dsigctx == NULL) {
359                 ERROR("xmlSecDSigCtxCreate failed.");
360                 goto error3;
361         }
362         dsigctx->signKey = xmlSecCryptoAppKeyLoad(key, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
363         if (dsigctx->signKey == NULL) {
364                 ERROR("loading key %s failed.", key);
365                 goto error3;
366         }
367         while (*certs) {
368                 if(xmlSecCryptoAppKeyCertLoad(dsigctx->signKey, *certs, xmlSecKeyDataFormatPem) < 0) {
369                         ERROR("loading certificate %s failed.", *certs);
370                         goto error3;
371                 }
372                 certs++;
373         }
374         if(xmlSecDSigCtxSign(dsigctx, sign) < 0) {
375                 ERROR("signing the document failed.");
376                 goto error3;
377         }
378         xmlSecDSigCtxDestroy(dsigctx);
379         return doc;
380
381 error3:
382         xmlSecDSigCtxDestroy(dsigctx);
383 error2:
384         xmlFreeDoc(doc);
385 error:
386         return NULL;
387 }
388