5c65217a4fcb971b6aad1d560d170a4d0106db8a
[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         if(xmlSecCryptoAppInit(NULL) < 0) {
136                 syslog(LOG_ERR, "xmlSecCryptoAppInit failed.");
137                 goto end;
138         }
139
140         if(xmlSecCryptoInit() < 0) {
141                 syslog(LOG_ERR, "xmlSecCryptoInit failed.");
142                 goto end;
143         }
144
145         xmlSecErrorsSetCallback(errors_cb);
146
147         xmlSecIOCleanupCallbacks();
148         if (xmlSecIORegisterCallbacks(file_match_cb,
149                                         file_open_cb, file_read_cb, file_close_cb)) {
150                 syslog(LOG_ERR, "xmlSecIORegisterCallbacks failed.");
151                 goto end;
152         }
153
154         keymgr = xmlSecKeysMngrCreate();
155         if (keymgr == NULL) {
156                 syslog(LOG_ERR, "xmlSecKeysMngrCreate failed.");
157                 goto end;
158         }
159
160         if(xmlSecCryptoAppDefaultKeysMngrInit(keymgr) < 0) {
161                 syslog(LOG_ERR, "xmlSecCryptoAppDefaultKeysMngrInit failed.");
162                 goto end;
163         }
164         fill_trusted_keys();
165
166         initstatus = 0;
167 end:
168         return initstatus;
169 }
170
171
172 void xmlsec_shutdown()
173 {
174         xmlSecKeysMngrDestroy(keymgr);
175
176         xmlSecCryptoShutdown();
177         
178         xmlSecCryptoAppShutdown();
179         
180         xmlSecShutdown();
181 }
182
183 int xmlsec_verify(xmlNodePtr node)
184 {
185         int rc;
186         xmlSecDSigCtxPtr dsigctx;
187
188         assert(initdone && !initstatus);
189
190         dsigctx = xmlSecDSigCtxCreate(keymgr);
191         if (dsigctx == NULL) {
192                 syslog(LOG_ERR, "xmlSecDSigCtxCreate failed.");
193                 rc = -1;
194         } else {
195                 rc = xmlSecDSigCtxVerify(dsigctx, node);
196                 if (rc)
197                         syslog(LOG_ERR, "xmlSecDSigCtxVerify failed.");
198                 else if (dsigctx->status != xmlSecDSigStatusSucceeded) {
199                         syslog(LOG_ERR, "invalid signature.");
200                         rc = -1;
201                 }
202                 xmlSecDSigCtxDestroy(dsigctx);
203         }
204
205         return rc;
206 }
207
208 static const struct { const char *id; const char *xml; } properties[2] = {
209         {
210                 .id = "AuthorSignature",
211                 .xml =
212                         "<SignatureProperties xmlns:dsp=\"http://www.w3.org/2009/xmldsig-properties\">"
213                          "<SignatureProperty Id=\"profile\" Target=\"#AuthorSignature\">"
214                           "<dsp:Profile URI=\"http://www.w3.org/ns/widgets-digsig#profile\"></dsp:Profile>"
215                          "</SignatureProperty>"
216                          "<SignatureProperty Id=\"role\" Target=\"#AuthorSignature\">"
217                            "<dsp:Role URI=\"http://www.w3.org/ns/widgets-digsig#role-author\"></dsp:Role>"
218                          "</SignatureProperty>"
219                          "<SignatureProperty Id=\"identifier\" Target=\"#AuthorSignature\">"
220                            "<dsp:Identifier></dsp:Identifier>"
221                          "</SignatureProperty>"
222                         "</SignatureProperties>"
223         },
224         {
225                 .id = "DistributorSignature",
226                 .xml = 
227                         "<SignatureProperties xmlns:dsp=\"http://www.w3.org/2009/xmldsig-properties\">"
228                          "<SignatureProperty Id=\"profile\" Target=\"#DistributorSignature\">"
229                           "<dsp:Profile URI=\"http://www.w3.org/ns/widgets-digsig#profile\"></dsp:Profile>"
230                          "</SignatureProperty>"
231                          "<SignatureProperty Id=\"role\" Target=\"#DistributorSignature\">"
232                            "<dsp:Role URI=\"http://www.w3.org/ns/widgets-digsig#role-distributor\"></dsp:Role>"
233                          "</SignatureProperty>"
234                          "<SignatureProperty Id=\"identifier\" Target=\"#DistributorSignature\">"
235                            "<dsp:Identifier></dsp:Identifier>"
236                          "</SignatureProperty>"
237                         "</SignatureProperties>"
238         }
239 };
240
241 xmlDocPtr xmlsec_create(int index, const char *key, const char **certs)
242 {
243         unsigned int i, fc, mask;
244         struct filedesc *fdesc;
245         xmlNodePtr sign, obj, ref, kinfo, props;
246         xmlDocPtr doc;
247         int rc;
248         xmlSecDSigCtxPtr dsigctx;
249
250         assert(initdone && !initstatus);
251
252         /* create the document */
253         doc = xmlNewDoc("1.0");
254         if (doc == NULL) {
255                 syslog(LOG_ERR, "xmlNewDoc failed");
256                 goto error;
257         }
258
259         /* create the root signature node */
260         sign = xmlSecTmplSignatureCreate(doc, xmlSecTransformInclC14N11Id, xmlSecTransformRsaSha256Id, properties[!!index].id);
261         if (sign == NULL) {
262                 syslog(LOG_ERR, "xmlSecTmplSignatureCreate failed");
263                 goto error2;
264         }
265         xmlDocSetRootElement(doc, sign);
266
267         /* create the object and its reference */
268         obj = xmlSecTmplSignatureAddObject(sign, "prop", NULL, NULL);
269         if (obj == NULL) {
270                 syslog(LOG_ERR, "xmlSecTmplSignatureAddObject failed");
271                 goto error2;
272         }
273         rc = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, properties[!!index].xml, &props);
274         if (rc) {
275                 syslog(LOG_ERR, "xmlParseBalancedChunkMemory failed");
276                 goto error2;
277         }
278         if (NULL == xmlAddChild(obj, props)) {
279                 syslog(LOG_ERR, "filling object node failed");
280                 xmlFreeNode(obj);
281                 goto error2;
282         }
283
284         /* create references to files */
285         mask = index ? flag_distributor_signature : flag_signature;
286         fc = file_count();
287         for (i = 0 ; i < fc ; i++) {
288                 fdesc = file_of_index(i);
289                 if (fdesc->type == type_file && (fdesc->flags & mask) == 0) {
290                         ref = xmlSecTmplSignatureAddReference(sign, xmlSecTransformSha256Id, NULL, fdesc->name, NULL);
291                         if (ref == NULL) {
292                                 syslog(LOG_ERR, "creation of reference to %s failed", fdesc->name);
293                                 goto error2;
294                         }
295                 }
296         }
297
298         /* create reference to object having properties */
299         ref =  xmlSecTmplSignatureAddReference(sign, xmlSecTransformSha256Id, NULL, "#prop", NULL);
300         if (ref == NULL) {
301                 syslog(LOG_ERR, "creation of reference to #prop failed");
302                 goto error2;
303         }
304         if (NULL == xmlSecTmplReferenceAddTransform(ref, xmlSecTransformInclC14N11Id)) {
305                 syslog(LOG_ERR, "setting transform reference to #prop failed");
306                 goto error2;
307         }
308
309         /* adds the X509 data */
310         kinfo = xmlSecTmplSignatureEnsureKeyInfo(sign, NULL);
311         if (kinfo == NULL) {
312                 syslog(LOG_ERR, "xmlSecTmplSignatureEnsureKeyInfo failed");
313                 goto error2;
314         }
315         if (NULL == xmlSecTmplKeyInfoAddX509Data(kinfo)) {
316                 syslog(LOG_ERR, "xmlSecTmplKeyInfoAddX509Data failed");
317                 goto error2;
318         }
319
320         /* sign now */
321         dsigctx = xmlSecDSigCtxCreate(keymgr);
322         if (dsigctx == NULL) {
323                 syslog(LOG_ERR, "xmlSecDSigCtxCreate failed.");
324                 goto error3;
325         }
326         dsigctx->signKey = xmlSecCryptoAppKeyLoad(key, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
327         if (dsigctx->signKey == NULL) {
328                 syslog(LOG_ERR, "loading key %s failed.", key);
329                 goto error3;
330         }
331         while (*certs) {
332                 if(xmlSecCryptoAppKeyCertLoad(dsigctx->signKey, *certs, xmlSecKeyDataFormatPem) < 0) {
333                         syslog(LOG_ERR, "loading certificate %s failed.", *certs);
334                         goto error3;
335                 }
336                 certs++;
337         }
338         if(xmlSecDSigCtxSign(dsigctx, sign) < 0) {
339                 syslog(LOG_ERR, "signing the document failed.");
340                 goto error3;
341         }
342         xmlSecDSigCtxDestroy(dsigctx);
343         return doc;
344
345 error3:
346         xmlSecDSigCtxDestroy(dsigctx);
347 error2:
348         xmlFreeDoc(doc);
349 error:
350         return NULL;
351 }
352