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