a403b777088df1917852359edab957571b308405
[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 "wgtpkg.h"
37
38 static int initstatus;
39 static int initdone;
40 static xmlSecKeysMngrPtr keymgr;
41
42 #ifndef CA_ROOT_DIRECTORY
43 #define CA_ROOT_DIRECTORY "./ca-certificates"
44 #endif
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                 syslog(LOG_ERR, "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                 syslog(LOG_ERR, "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         syslog(LOG_ERR, "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()
99 {
100         int err;
101         DIR *dir;
102         struct dirent *ent;
103         char path[PATH_MAX], *e;
104
105         e = stpcpy(path, CA_ROOT_DIRECTORY);
106         dir = opendir(path);
107         if (!dir) {
108                 syslog(LOG_ERR, "opendir %s failed in fill_trusted_keys", path);
109                 return -1;
110         }
111
112         *e++ = '/';
113         ent = readdir(dir);
114         while (ent != NULL) {
115                 if (ent->d_type == DT_REG) {
116                         strcpy(e, ent->d_name);
117                         err = xmlSecCryptoAppKeysMngrCertLoad(keymgr, path, xmlSecKeyDataFormatPem, xmlSecKeyDataTypeTrusted);
118                         if (err < 0) {
119                                 syslog(LOG_ERR, "xmlSecCryptoAppKeysMngrCertLoadMemory failed for %s", path);
120                                 closedir(dir);
121                                 return -1;
122                         }
123                 }
124                 ent = readdir(dir);
125         }
126
127         closedir(dir);
128         return 0;
129         
130 }
131
132 /* initialisation of access to xmlsec */
133 int xmlsec_init()
134 {
135
136         if (initdone)
137                 goto end;
138
139         initdone = 1;
140         initstatus = -1;
141
142         if(xmlSecInit() < 0) {
143                 syslog(LOG_ERR, "xmlSecInit failed.");
144                 goto end;
145         }
146
147 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
148         if(xmlSecCryptoDLLoadLibrary(XMLSEC_CRYPTO) < 0) {
149                 syslog(LOG_ERR, "xmlSecCryptoDLLoadLibrary %s failed.", XMLSEC_CRYPTO);
150                 goto end;
151         }
152 #endif
153
154         if(xmlSecCryptoAppInit(NULL) < 0) {
155                 syslog(LOG_ERR, "xmlSecCryptoAppInit failed.");
156                 goto end;
157         }
158
159         if(xmlSecCryptoInit() < 0) {
160                 syslog(LOG_ERR, "xmlSecCryptoInit failed.");
161                 goto end;
162         }
163
164         xmlSecErrorsSetCallback(errors_cb);
165
166         xmlSecIOCleanupCallbacks();
167         if (xmlSecIORegisterCallbacks(file_match_cb,
168                                         file_open_cb, file_read_cb, file_close_cb)) {
169                 syslog(LOG_ERR, "xmlSecIORegisterCallbacks failed.");
170                 goto end;
171         }
172
173         keymgr = xmlSecKeysMngrCreate();
174         if (keymgr == NULL) {
175                 syslog(LOG_ERR, "xmlSecKeysMngrCreate failed.");
176                 goto end;
177         }
178
179         if(xmlSecCryptoAppDefaultKeysMngrInit(keymgr) < 0) {
180                 syslog(LOG_ERR, "xmlSecCryptoAppDefaultKeysMngrInit failed.");
181                 goto end;
182         }
183         fill_trusted_keys();
184
185         initstatus = 0;
186 end:
187         return initstatus;
188 }
189
190 /* shuting down accesses to xmlsec */
191 void xmlsec_shutdown()
192 {
193         xmlSecKeysMngrDestroy(keymgr);
194
195         xmlSecCryptoShutdown();
196         
197         xmlSecCryptoAppShutdown();
198         
199         xmlSecShutdown();
200 }
201
202 /* verify a signature */
203 int xmlsec_verify(xmlNodePtr node)
204 {
205         int rc;
206         xmlSecDSigCtxPtr dsigctx;
207
208         assert(initdone && !initstatus);
209
210         dsigctx = xmlSecDSigCtxCreate(keymgr);
211         if (dsigctx == NULL) {
212                 syslog(LOG_ERR, "xmlSecDSigCtxCreate failed.");
213                 rc = -1;
214         } else {
215                 rc = xmlSecDSigCtxVerify(dsigctx, node);
216                 if (rc)
217                         syslog(LOG_ERR, "xmlSecDSigCtxVerify failed.");
218                 else if (dsigctx->status != xmlSecDSigStatusSucceeded) {
219                         syslog(LOG_ERR, "invalid signature.");
220                         rc = -1;
221                 }
222                 xmlSecDSigCtxDestroy(dsigctx);
223         }
224
225         return rc;
226 }
227
228 /* templates for properties of signature files */
229 static const struct { const char *id; const char *xml; } properties[2] = {
230         {
231                 .id = "AuthorSignature", /* template of properties for author signature */
232                 .xml =
233                         "<SignatureProperties xmlns:dsp=\"http://www.w3.org/2009/xmldsig-properties\">"
234                          "<SignatureProperty Id=\"profile\" Target=\"#AuthorSignature\">"
235                           "<dsp:Profile URI=\"http://www.w3.org/ns/widgets-digsig#profile\"></dsp:Profile>"
236                          "</SignatureProperty>"
237                          "<SignatureProperty Id=\"role\" Target=\"#AuthorSignature\">"
238                            "<dsp:Role URI=\"http://www.w3.org/ns/widgets-digsig#role-author\"></dsp:Role>"
239                          "</SignatureProperty>"
240                          "<SignatureProperty Id=\"identifier\" Target=\"#AuthorSignature\">"
241                            "<dsp:Identifier></dsp:Identifier>"
242                          "</SignatureProperty>"
243                         "</SignatureProperties>"
244         },
245         {
246                 .id = "DistributorSignature", /* template of properties for distributor signature */
247                 .xml = 
248                         "<SignatureProperties xmlns:dsp=\"http://www.w3.org/2009/xmldsig-properties\">"
249                          "<SignatureProperty Id=\"profile\" Target=\"#DistributorSignature\">"
250                           "<dsp:Profile URI=\"http://www.w3.org/ns/widgets-digsig#profile\"></dsp:Profile>"
251                          "</SignatureProperty>"
252                          "<SignatureProperty Id=\"role\" Target=\"#DistributorSignature\">"
253                            "<dsp:Role URI=\"http://www.w3.org/ns/widgets-digsig#role-distributor\"></dsp:Role>"
254                          "</SignatureProperty>"
255                          "<SignatureProperty Id=\"identifier\" Target=\"#DistributorSignature\">"
256                            "<dsp:Identifier></dsp:Identifier>"
257                          "</SignatureProperty>"
258                         "</SignatureProperties>"
259         }
260 };
261
262 /* create a signature of 'index' (0 for author, other values for distributors)
263 using the private 'key' (filename) and the certificates 'certs' (filenames)
264 as trusted chain */
265 xmlDocPtr xmlsec_create(int index, const char *key, const char **certs)
266 {
267         unsigned int i, fc, mask;
268         struct filedesc *fdesc;
269         xmlNodePtr sign, obj, ref, kinfo, props;
270         xmlDocPtr doc;
271         int rc;
272         xmlSecDSigCtxPtr dsigctx;
273
274         assert(initdone && !initstatus);
275
276         /* create the document */
277         doc = xmlNewDoc("1.0");
278         if (doc == NULL) {
279                 syslog(LOG_ERR, "xmlNewDoc failed");
280                 goto error;
281         }
282
283         /* create the root signature node */
284         sign = xmlSecTmplSignatureCreate(doc, xmlSecTransformInclC14N11Id, xmlSecTransformRsaSha256Id, properties[!!index].id);
285         if (sign == NULL) {
286                 syslog(LOG_ERR, "xmlSecTmplSignatureCreate failed");
287                 goto error2;
288         }
289         xmlDocSetRootElement(doc, sign);
290
291         /* create the object and its reference */
292         obj = xmlSecTmplSignatureAddObject(sign, "prop", NULL, NULL);
293         if (obj == NULL) {
294                 syslog(LOG_ERR, "xmlSecTmplSignatureAddObject failed");
295                 goto error2;
296         }
297         rc = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, properties[!!index].xml, &props);
298         if (rc) {
299                 syslog(LOG_ERR, "xmlParseBalancedChunkMemory failed");
300                 goto error2;
301         }
302         if (NULL == xmlAddChild(obj, props)) {
303                 syslog(LOG_ERR, "filling object node failed");
304                 xmlFreeNode(obj);
305                 goto error2;
306         }
307
308         /* create references to files */
309         mask = index ? flag_distributor_signature : flag_signature;
310         fc = file_count();
311         for (i = 0 ; i < fc ; i++) {
312                 fdesc = file_of_index(i);
313                 if (fdesc->type == type_file && (fdesc->flags & mask) == 0) {
314                         ref = xmlSecTmplSignatureAddReference(sign, xmlSecTransformSha256Id, NULL, fdesc->name, NULL);
315                         if (ref == NULL) {
316                                 syslog(LOG_ERR, "creation of reference to %s failed", fdesc->name);
317                                 goto error2;
318                         }
319                 }
320         }
321
322         /* create reference to object having properties */
323         ref =  xmlSecTmplSignatureAddReference(sign, xmlSecTransformSha256Id, NULL, "#prop", NULL);
324         if (ref == NULL) {
325                 syslog(LOG_ERR, "creation of reference to #prop failed");
326                 goto error2;
327         }
328         if (NULL == xmlSecTmplReferenceAddTransform(ref, xmlSecTransformInclC14N11Id)) {
329                 syslog(LOG_ERR, "setting transform reference to #prop failed");
330                 goto error2;
331         }
332
333         /* adds the X509 data */
334         kinfo = xmlSecTmplSignatureEnsureKeyInfo(sign, NULL);
335         if (kinfo == NULL) {
336                 syslog(LOG_ERR, "xmlSecTmplSignatureEnsureKeyInfo failed");
337                 goto error2;
338         }
339         if (NULL == xmlSecTmplKeyInfoAddX509Data(kinfo)) {
340                 syslog(LOG_ERR, "xmlSecTmplKeyInfoAddX509Data failed");
341                 goto error2;
342         }
343
344         /* sign now */
345         dsigctx = xmlSecDSigCtxCreate(keymgr);
346         if (dsigctx == NULL) {
347                 syslog(LOG_ERR, "xmlSecDSigCtxCreate failed.");
348                 goto error3;
349         }
350         dsigctx->signKey = xmlSecCryptoAppKeyLoad(key, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
351         if (dsigctx->signKey == NULL) {
352                 syslog(LOG_ERR, "loading key %s failed.", key);
353                 goto error3;
354         }
355         while (*certs) {
356                 if(xmlSecCryptoAppKeyCertLoad(dsigctx->signKey, *certs, xmlSecKeyDataFormatPem) < 0) {
357                         syslog(LOG_ERR, "loading certificate %s failed.", *certs);
358                         goto error3;
359                 }
360                 certs++;
361         }
362         if(xmlSecDSigCtxSign(dsigctx, sign) < 0) {
363                 syslog(LOG_ERR, "signing the document failed.");
364                 goto error3;
365         }
366         xmlSecDSigCtxDestroy(dsigctx);
367         return doc;
368
369 error3:
370         xmlSecDSigCtxDestroy(dsigctx);
371 error2:
372         xmlFreeDoc(doc);
373 error:
374         return NULL;
375 }
376