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