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