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