Tizen 2.1 base
[framework/web/wrt-commons.git] / modules / vcore / src / vcore / XmlsecAdapter.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
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  * @file        XmlsecAdapter.cpp
18  * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
19  * @version     1.0
20  * @brief
21  */
22
23 /* 
24  * Copyright (C) 2002-2003 Aleksey Sanin.  All Rights Reserved.
25  *
26  * Permission is hereby granted, free of charge, to any person
27  * obtaining a copy of this software and associated documentation
28  * files (the "Software"), to deal in the Software without
29  * restriction, including without limitation the rights to use,
30  * copy, modify, merge, publish, distribute, sublicense, and/or sell
31  * copies of the Software, and to permit persons to whom the
32  * Software is furnished to do so, subject to the following conditions:
33
34  * The above copyright notice and this permission notice shall be
35  * included in all copies or substantial portions of the Software.
36
37  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
38  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
39  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
40  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
41  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
42  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
43  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
44  * OTHER DEALINGS IN THE SOFTWARE.
45  */
46
47
48 #include <cstdlib>
49 #include <cstring>
50
51 #include <libxml/tree.h>
52 #include <libxml/xmlmemory.h>
53 #include <libxml/parser.h>
54
55 #ifndef XMLSEC_NO_XSLT
56 #include <libxslt/xslt.h>
57 #endif /*   XMLSEC_NO_XSLT */
58
59 #include <xmlsec/xmlsec.h>
60 #include <xmlsec/xmltree.h>
61 #include <xmlsec/xmldsig.h>
62 #include <xmlsec/crypto.h>
63 #include <xmlsec/io.h>
64 #include <xmlsec/keyinfo.h>
65
66 #include <dpl/assert.h>
67 #include <dpl/log/log.h>
68
69 #include "XmlsecAdapter.h"
70 #include <dpl/singleton_impl.h>
71 IMPLEMENT_SINGLETON(ValidationCore::XmlSec)
72
73 namespace ValidationCore {
74 VC_DECLARE_DELETER(xmlSecKeysMngr, xmlSecKeysMngrDestroy)
75
76 static const char* DIGEST_MD5 = "md5";
77
78 std::string XmlSec::s_prefixPath;
79
80 int XmlSec::fileMatchCallback(const char *filename)
81 {
82     std::string path = s_prefixPath + filename;
83     return xmlFileMatch(path.c_str());
84 }
85
86 void* XmlSec::fileOpenCallback(const char *filename)
87 {
88     std::string path = s_prefixPath + filename;
89     LogDebug("Xmlsec opening: " << path);
90     return xmlFileOpen(path.c_str());
91 }
92
93 int XmlSec::fileReadCallback(void *context,
94         char *buffer,
95         int len)
96 {
97     return xmlFileRead(context, buffer, len);
98 }
99
100 int XmlSec::fileCloseCallback(void *context)
101 {
102     return xmlFileClose(context);
103 }
104
105 void XmlSec::fileExtractPrefix(XmlSecContext *context)
106 {
107     if (!(context->workingDirectory.empty())) {
108         s_prefixPath = context->workingDirectory;
109         return;
110     }
111
112     s_prefixPath = context->signatureFile;
113     size_t pos = s_prefixPath.rfind('/');
114     if (pos == std::string::npos) {
115         s_prefixPath.clear();
116     } else {
117         s_prefixPath.erase(pos + 1, std::string::npos);
118     }
119 }
120
121 XmlSec::XmlSec() :
122     m_initialized(false)
123 {
124     LIBXML_TEST_VERSION
125         xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
126     xmlSubstituteEntitiesDefault(1);
127 #ifndef XMLSEC_NO_XSLT
128     xmlIndentTreeOutput = 1;
129 #endif
130
131     if (xmlSecInit() < 0) {
132         LogError("Xmlsec initialization failed.");
133         ThrowMsg(Exception::InternalError, "Xmlsec initialization failed.");
134     }
135
136     if (xmlSecCheckVersion() != 1) {
137         xmlSecShutdown();
138         LogError("Loaded xmlsec library version is not compatible.");
139         ThrowMsg(Exception::InternalError,
140                  "Loaded xmlsec library version is not compatible.");
141     }
142
143 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
144     if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
145         xmlSecShutdown();
146         LogError(
147             "Error: unable to load default xmlsec-crypto library. Make sure "
148             "that you have it installed and check shared libraries path "
149             "(LD_LIBRARY_PATH) envornment variable.");
150         ThrowMsg(Exception::InternalError,
151                  "Unable to load default xmlsec-crypto library.");
152     }
153 #endif
154
155     if (xmlSecCryptoAppInit(NULL) < 0) {
156         xmlSecShutdown();
157         LogError("Crypto initialization failed.");
158         ThrowMsg(Exception::InternalError, "Crypto initialization failed.");
159     }
160
161     if (xmlSecCryptoInit() < 0) {
162         xmlSecCryptoAppShutdown();
163         xmlSecShutdown();
164         LogError("Xmlsec-crypto initialization failed.");
165         ThrowMsg(Exception::InternalError,
166                  "Xmlsec-crypto initialization failed.");
167     }
168
169     m_initialized = true;
170 }
171
172 void XmlSec::deinitialize(void)
173 {
174     Assert(m_initialized);
175
176     /*   Shutdown xmlsec-crypto library */
177     xmlSecCryptoShutdown();
178
179     /*   Shutdown crypto library */
180     xmlSecCryptoAppShutdown();
181
182     /*   Shutdown xmlsec library */
183     xmlSecShutdown();
184
185     /*   Shutdown libxslt/libxml */
186 #ifndef XMLSEC_NO_XSLT
187     xsltCleanupGlobals();
188 #endif /*   XMLSEC_NO_XSLT */
189
190     s_prefixPath.clear();
191     m_initialized = false;
192 }
193
194 XmlSec::~XmlSec()
195 {
196     if (m_initialized) {
197         deinitialize();
198     }
199 }
200
201 XmlSec::Result XmlSec::validateFile(XmlSecContext *context,
202         xmlSecKeysMngrPtr mngr)
203 {
204     xmlDocPtr doc = NULL;
205     xmlNodePtr node = NULL;
206     xmlSecDSigCtxPtr dsigCtx = NULL;
207     int size, res = -1;
208
209     fileExtractPrefix(context);
210     LogDebug("Prefix path: " << s_prefixPath);
211
212     xmlSecIOCleanupCallbacks();
213
214     xmlSecIORegisterCallbacks(
215         fileMatchCallback,
216         fileOpenCallback,
217         fileReadCallback,
218         fileCloseCallback);
219
220     /*   load file */
221     doc = xmlParseFile(context->signatureFile.c_str());
222     if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)) {
223         LogWarning("Unable to parse file " << context->signatureFile);
224         goto done;
225     }
226
227     /*   find start node */
228     node = xmlSecFindNode(xmlDocGetRootElement(
229                               doc), xmlSecNodeSignature, xmlSecDSigNs);
230     if (node == NULL) {
231         LogWarning("Start node not found in " << context->signatureFile);
232         goto done;
233     }
234
235     /*   create signature context */
236     dsigCtx = xmlSecDSigCtxCreate(mngr);
237     if (dsigCtx == NULL) {
238         LogError("Failed to create signature context.");
239         goto done;
240     }
241
242     if (context->allowBrokenChain) {
243         dsigCtx->keyInfoReadCtx.flags |=
244             XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN;
245     }
246
247     if (context->validationTime) {
248         LogDebug("Setting validation time.");
249         dsigCtx->keyInfoReadCtx.certsVerificationTime = context->validationTime;
250     }
251
252     /*   Verify signature */
253     if (xmlSecDSigCtxVerify(dsigCtx, node) < 0) {
254         LogWarning("Signature verify error.");
255         goto done;
256     }
257
258     if (dsigCtx->keyInfoReadCtx.flags2 &
259         XMLSEC_KEYINFO_ERROR_FLAGS_BROKEN_CHAIN) {
260         LogWarning("XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN was set to true!");
261         LogWarning("Signature contains broken chain!");
262         context->errorBrokenChain = true;
263     }
264
265     /*   print verification result to stdout */
266     if (dsigCtx->status == xmlSecDSigStatusSucceeded) {
267         LogDebug("Signature is OK");
268         res = 0;
269     } else {
270         LogDebug("Signature is INVALID");
271         goto done;
272     }
273
274     if (dsigCtx->c14nMethod && dsigCtx->c14nMethod->id &&
275         dsigCtx->c14nMethod->id->name) {
276         LogInfo("Canonicalization method: " <<
277                 reinterpret_cast<const char *>(dsigCtx->c14nMethod->id->name));
278     }
279
280     size = xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences));
281     for (int i = 0; i < size; ++i) {
282         xmlSecDSigReferenceCtxPtr dsigRefCtx =
283             (xmlSecDSigReferenceCtxPtr)xmlSecPtrListGetItem(&(dsigCtx->
284                                                                   signedInfoReferences),
285                                                             i);
286         if (dsigRefCtx && dsigRefCtx->uri) {
287             if (dsigRefCtx->digestMethod && dsigRefCtx->digestMethod->id &&
288                 dsigRefCtx->digestMethod->id->name) {
289                 const char* pDigest =
290                     reinterpret_cast<const char *>(dsigRefCtx->digestMethod->id
291                                                        ->name);
292                 std::string strDigest(pDigest);
293                 LogInfo("reference digest method: " <<
294                         reinterpret_cast<const char *>(dsigRefCtx->digestMethod
295                                                            ->id
296                                                            ->name));
297                 if (strDigest == DIGEST_MD5) {
298                     LogWarning("MD5 digest method used! Please use sha");
299                     res = -1;
300                     break;
301                 }
302             }
303             context->referenceSet.insert(std::string(reinterpret_cast<char *>(
304                                                          dsigRefCtx->uri)));
305         }
306     }
307
308 done:
309     /*   cleanup */
310     if (dsigCtx != NULL) {
311         xmlSecDSigCtxDestroy(dsigCtx);
312     }
313
314     if (doc != NULL) {
315         xmlFreeDoc(doc);
316     }
317
318     if (res) {
319         return ERROR_INVALID_SIGNATURE;
320     }
321     return NO_ERROR;
322 }
323
324 void XmlSec::loadDERCertificateMemory(XmlSecContext *context,
325         xmlSecKeysMngrPtr mngr)
326 {
327     unsigned char *derCertificate = NULL;
328     int size = i2d_X509(context->certificatePtr->getX509(), &derCertificate);
329
330     if (!derCertificate) {
331         LogError("Failed during x509 conversion to der format.");
332         ThrowMsg(Exception::InternalError,
333                  "Failed during x509 conversion to der format.");
334     }
335
336     if (xmlSecCryptoAppKeysMngrCertLoadMemory(mngr,
337                                               derCertificate,
338                                               size,
339                                               xmlSecKeyDataFormatDer,
340                                               xmlSecKeyDataTypeTrusted) < 0) {
341         OPENSSL_free(derCertificate);
342         LogError("Failed to load der certificate from memory.");
343         ThrowMsg(Exception::InternalError,
344                  "Failed to load der certificate from memory.");
345     }
346
347     OPENSSL_free(derCertificate);
348 }
349
350 void XmlSec::loadPEMCertificateFile(XmlSecContext *context,
351         xmlSecKeysMngrPtr mngr)
352 {
353     if (xmlSecCryptoAppKeysMngrCertLoad(mngr,
354                                         context->certificatePath.c_str(),
355                                         xmlSecKeyDataFormatPem,
356                                         xmlSecKeyDataTypeTrusted) < 0) {
357         LogError("Failed to load PEM certificate from file.");
358         ThrowMsg(Exception::InternalError,
359                  "Failed to load PEM certificate from file.");
360     }
361 }
362
363 XmlSec::Result XmlSec::validate(XmlSecContext *context)
364 {
365     Assert(context);
366     Assert(!(context->signatureFile.empty()));
367     Assert(context->certificatePtr.Get() || !(context->certificatePath.empty()));
368
369     if (!m_initialized) {
370         LogError("XmlSec is not initialized.");
371         ThrowMsg(Exception::InternalError, "XmlSec is not initialized");
372     }
373
374     AutoPtr<xmlSecKeysMngr> mngr(xmlSecKeysMngrCreate());
375
376     if (!mngr.get()) {
377         LogError("Failed to create keys manager.");
378         ThrowMsg(Exception::InternalError, "Failed to create keys manager.");
379     }
380
381     if (xmlSecCryptoAppDefaultKeysMngrInit(mngr.get()) < 0) {
382         LogError("Failed to initialize keys manager.");
383         ThrowMsg(Exception::InternalError, "Failed to initialize keys manager.");
384     }
385     context->referenceSet.clear();
386
387     if (context->certificatePtr.Get()) {
388         loadDERCertificateMemory(context, mngr.get());
389     }
390
391     if (!context->certificatePath.empty()) {
392         loadPEMCertificateFile(context, mngr.get());
393     }
394
395     return validateFile(context, mngr.get());
396 }
397 } // namespace ValidationCore