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