6f3c8e733a3e01ee0ab48df0d9ad6d556a3f854b
[platform/core/security/cert-svc.git] / 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 #include <cstdlib>
23 #include <cstring>
24
25 #include <libxml/tree.h>
26 #include <libxml/xmlmemory.h>
27 #include <libxml/parser.h>
28
29 #ifndef XMLSEC_NO_XSLT
30 #include <libxslt/xslt.h>
31 #endif /*   XMLSEC_NO_XSLT */
32
33 #include <xmlsec/xmlsec.h>
34 #include <xmlsec/xmltree.h>
35 #include <xmlsec/xmldsig.h>
36 #include <xmlsec/crypto.h>
37 #include <xmlsec/io.h>
38 #include <xmlsec/keyinfo.h>
39 #include <xmlsec/errors.h>
40
41 #include <dpl/assert.h>
42 #include <dpl/log/log.h>
43
44 #include <vcore/XmlsecAdapter.h>
45
46 #include <vcore/ValidatorCommon.h>
47
48 #include <dpl/singleton_impl.h>
49 IMPLEMENT_SINGLETON(ValidationCore::XmlSec)
50
51 namespace {
52
53 struct FileWrapper {
54     FileWrapper(void *argFile, bool argReleased)
55       : file(argFile)
56       , released(argReleased)
57     {}
58     void *file;
59     bool released;
60 };
61
62 } // anonymous namespace
63
64 namespace ValidationCore {
65 VC_DECLARE_DELETER(xmlSecKeysMngr, xmlSecKeysMngrDestroy)
66
67 static const char* DIGEST_MD5 = "md5";
68
69 std::string XmlSec::s_prefixPath;
70
71 int XmlSec::fileMatchCallback(const char *filename)
72 {
73     std::string path = s_prefixPath + filename;
74     return xmlFileMatch(path.c_str());
75 }
76
77 void* XmlSec::fileOpenCallback(const char *filename)
78 {
79     std::string path = s_prefixPath + filename;
80
81    // LogDebug("Xmlsec opening : " << path);
82     return new FileWrapper(xmlFileOpen(path.c_str()),false);
83 }
84
85 int XmlSec::fileReadCallback(void *context,
86         char *buffer,
87         int len)
88 {
89     FileWrapper *fw = static_cast<FileWrapper*>(context);
90     if (fw->released) {
91         return 0;
92     }
93     int output = xmlFileRead(fw->file, buffer, len);
94     if (output == 0) {
95         fw->released = true;
96         xmlFileClose(fw->file);
97     }
98     return output;
99 }
100
101 int XmlSec::fileCloseCallback(void *context)
102 {
103     FileWrapper *fw = static_cast<FileWrapper*>(context);
104     int output = 0;
105     if (!(fw->released)) {
106         output = xmlFileClose(fw->file);
107     }
108     delete fw;
109     return output;
110 }
111
112 void XmlSec::fileExtractPrefix(XmlSecContext *context)
113 {
114     if (!(context->workingDirectory.empty())) {
115         s_prefixPath = context->workingDirectory;
116         return;
117     }
118
119     s_prefixPath = context->signatureFile;
120     size_t pos = s_prefixPath.rfind('/');
121     if (pos == std::string::npos) {
122         s_prefixPath.clear();
123     } else {
124         s_prefixPath.erase(pos + 1, std::string::npos);
125     }
126 }
127
128 void LogDebugPrint(const char* file, int line, const char* func, 
129        const char* errorObject, const char* errorSubject, 
130        int reason, const char* msg)
131 {
132     char total[1024];
133     snprintf(total, sizeof(total), "[%s:%d][%s] : [%s] : [%s] : [%s]", file, line, func, errorObject, errorSubject, msg);
134
135     if(reason != 256)
136     {
137        fprintf(stderr, "## [validate error]: %s\n", total);
138        LogError(" " << total);
139     }
140     else
141     {
142        LogDebug(" " << total);
143     }
144 }
145
146 XmlSec::XmlSec() :
147     m_initialized(false),
148     m_noHash(false),
149     m_partialHash(false),
150     m_pList(NULL)
151 {
152     LIBXML_TEST_VERSION
153         xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
154     xmlSubstituteEntitiesDefault(1);
155 #ifndef XMLSEC_NO_XSLT
156     xmlIndentTreeOutput = 1;
157 #endif
158
159     if (xmlSecInit() < 0) {
160         LogError("Xmlsec initialization failed.");
161         ThrowMsg(Exception::InternalError, "Xmlsec initialization failed.");
162     }
163
164     if (xmlSecCheckVersion() != 1) {
165         xmlSecShutdown();
166         LogError("Loaded xmlsec library version is not compatible.");
167         ThrowMsg(Exception::InternalError,
168                  "Loaded xmlsec library version is not compatible.");
169     }
170
171 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
172     if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
173         xmlSecShutdown();
174         LogError(
175             "Error: unable to load default xmlsec-crypto library. Make sure "
176             "that you have it installed and check shared libraries path "
177             "(LD_LIBRARY_PATH) envornment variable.");
178         ThrowMsg(Exception::InternalError,
179                  "Unable to load default xmlsec-crypto library.");
180     }
181 #endif
182
183     if (xmlSecCryptoAppInit(NULL) < 0) {
184         xmlSecShutdown();
185         LogError("Crypto initialization failed.");
186         ThrowMsg(Exception::InternalError, "Crypto initialization failed.");
187     }
188
189     if (xmlSecCryptoInit() < 0) {
190         xmlSecCryptoAppShutdown();
191         xmlSecShutdown();
192         LogError("Xmlsec-crypto initialization failed.");
193         ThrowMsg(Exception::InternalError,
194                  "Xmlsec-crypto initialization failed.");
195     }
196
197     m_initialized = true;
198 }
199
200 void XmlSec::deinitialize(void)
201 {
202     Assert(m_initialized);
203
204     /*   Shutdown xmlsec-crypto library */
205     xmlSecCryptoShutdown();
206
207     /*   Shutdown crypto library */
208     xmlSecCryptoAppShutdown();
209
210     /*   Shutdown xmlsec library */
211     xmlSecShutdown();
212
213     /*   Shutdown libxslt/libxml */
214 #ifndef XMLSEC_NO_XSLT
215     xsltCleanupGlobals();
216 #endif /*   XMLSEC_NO_XSLT */
217
218     s_prefixPath.clear();
219     m_initialized = false;
220 }
221
222 XmlSec::~XmlSec()
223 {
224    m_noHash= false;
225    m_partialHash = false;
226     if (m_initialized) {
227         deinitialize();
228     }
229 }
230
231 XmlSec::Result XmlSec::validateFile(XmlSecContext *context,
232         xmlSecKeysMngrPtr mngr)
233 {
234     xmlDocPtr doc = NULL;
235     xmlNodePtr node = NULL;
236     xmlSecDSigCtxPtr dsigCtx = NULL;
237     int size, res = -1;
238
239     fileExtractPrefix(context);
240     LogDebug("Prefix path : " << s_prefixPath);
241
242     xmlSecIOCleanupCallbacks();
243
244     xmlSecIORegisterCallbacks(
245         fileMatchCallback,
246         fileOpenCallback,
247         fileReadCallback,
248         fileCloseCallback);
249
250     /*   load file */
251     doc = xmlParseFile(context->signatureFile.c_str());
252     if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)) {
253         LogWarning("Unable to parse file " << context->signatureFile);
254         goto done;
255     }
256
257     /*   find start node */
258     node = xmlSecFindNode(xmlDocGetRootElement(
259                               doc), xmlSecNodeSignature, xmlSecDSigNs);
260     if (node == NULL) {
261         LogWarning("Start node not found in " << context->signatureFile);
262         goto done;
263     }
264
265     /*   create signature context */
266     dsigCtx = xmlSecDSigCtxCreate(mngr);
267     if (dsigCtx == NULL) {
268         LogError("Failed to create signature context.");
269         goto done;
270     }
271
272     if (context->allowBrokenChain) {
273         dsigCtx->keyInfoReadCtx.flags |=
274             XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN;
275     }
276
277     if (context->validationTime) {
278         LogDebug("Setting validation time.");
279         dsigCtx->keyInfoReadCtx.certsVerificationTime = context->validationTime;
280     }
281
282     if( m_noHash == true || m_partialHash == true ) {
283         LogDebug("SignatureEx start >> ");
284         if( m_pList == NULL ) {
285             LogWarning("## [validate]: uriList does not exist" );
286             fprintf(stderr, "## [validate]: uriList does not exist\n");
287             res = xmlSecDSigCtxVerifyEx(dsigCtx, node, 1, NULL);
288     } else {
289         int n = 0;
290         int i = 0;
291
292         if(m_pList == NULL)
293         {
294           LogWarning("## [validate]: uriList does not exist" );
295           fprintf(stderr, "## [validate]: uriList does not exist\n");
296           res = -1;
297           goto done;
298         }
299
300         n = m_pList->size();
301
302         char* pList[n + 1];
303         std::list<std::string>::const_iterator itr = m_pList->begin();
304         std::string tmpString;
305         char* uri = NULL;
306         int len;
307
308         for(; itr != m_pList->end(); ++itr) {
309            tmpString = (*itr);
310            uri = (char*)tmpString.c_str();
311            len = strlen(uri);
312            pList[i] = (char*)malloc(len + 1);
313            memcpy(pList[i], uri, len);
314            pList[i][len] = '\0';
315            fprintf(stderr, "## [validate]: uriList[%d] = %s\n", i, pList[i]);
316            ++i;
317         }
318         pList[n] = '\0';
319
320         res = xmlSecDSigCtxVerifyEx(dsigCtx, node, 0, (void*)pList);
321         i = 0;
322         while(pList[i] != NULL) {
323           free(pList[i]);
324           ++i;
325         }
326      }
327
328      if(res < 0) {
329         LogError("SignatureEx verify error.");
330         fprintf(stderr, "## [validate error]: SignatureEx verify error\n");
331         res = -1;
332         goto done;
333      }
334     } else {
335        LogDebug("Signature start >> ");
336
337        /*  Verify signature */
338        if (xmlSecDSigCtxVerify(dsigCtx, node) < 0) {
339          LogError("Signature verify error.");
340          fprintf(stderr, "## [validate error]: Signature verify error\n");
341          res = -1;
342          goto done;
343       }
344     }
345
346     if (dsigCtx->keyInfoReadCtx.flags2 &
347      XMLSEC_KEYINFO_ERROR_FLAGS_BROKEN_CHAIN) {
348         LogWarning("XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN was set to true!");
349         LogWarning("Signature contains broken chain!");
350         context->errorBrokenChain = true;
351     }
352
353     /*   print verification result to stdout */
354     if (dsigCtx->status == xmlSecDSigStatusSucceeded) {
355         LogDebug("Signature is OK");
356         res = 0;
357     } else {
358         LogDebug("Signature is INVALID");
359         res = -1;
360         goto done;
361     }
362
363     if (dsigCtx->c14nMethod && dsigCtx->c14nMethod->id &&
364         dsigCtx->c14nMethod->id->name) {
365        // LogInfo("Canonicalization method: " << (reinterpret_cast<const char *>(dsigCtx->c14nMethod->id->name)).c_str());
366     }
367
368     size = xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences));
369     for (int i = 0; i < size; ++i) {
370         xmlSecDSigReferenceCtxPtr dsigRefCtx =
371             (xmlSecDSigReferenceCtxPtr)xmlSecPtrListGetItem(&(dsigCtx->
372                                                                   signedInfoReferences),
373                                                             i);
374         if (dsigRefCtx && dsigRefCtx->uri) {
375             if (dsigRefCtx->digestMethod && dsigRefCtx->digestMethod->id &&
376                 dsigRefCtx->digestMethod->id->name) {
377                 const char* pDigest =
378                     reinterpret_cast<const char *>(dsigRefCtx->digestMethod->id
379                                                        ->name);
380                 std::string strDigest(pDigest);
381                 /*LogInfo("reference digest method: " << (reinterpret_cast<const char *>(dsigRefCtx->digestMethod->id->name)).c_str());*/
382                 if (strDigest == DIGEST_MD5) {
383                     LogWarning("MD5 digest method used! Please use sha");
384                     res = -1;
385                     break;
386                 }
387             }
388             context->referenceSet.insert(std::string(reinterpret_cast<char *>(
389                                                          dsigRefCtx->uri)));
390         }
391     }
392
393 done:
394     m_pList = NULL;
395     m_noHash = false;
396     m_partialHash = false;
397
398     /*   cleanup */
399     if (dsigCtx != NULL) {
400         xmlSecDSigCtxDestroy(dsigCtx);
401     }
402
403     if (doc != NULL) {
404         xmlFreeDoc(doc);
405     }
406
407     if (res) {
408         return ERROR_INVALID_SIGNATURE;
409     }
410     return NO_ERROR;
411 }
412
413 void XmlSec::loadDERCertificateMemory(XmlSecContext *context,
414         xmlSecKeysMngrPtr mngr)
415 {
416     unsigned char *derCertificate = NULL;
417     int size = i2d_X509(context->certificatePtr->getX509(), &derCertificate);
418
419     if (!derCertificate) {
420         LogError("Failed during x509 conversion to der format.");
421         ThrowMsg(Exception::InternalError,
422                  "Failed during x509 conversion to der format.");
423     }
424
425     if (xmlSecCryptoAppKeysMngrCertLoadMemory(mngr,
426                                               derCertificate,
427                                               size,
428                                               xmlSecKeyDataFormatDer,
429                                               xmlSecKeyDataTypeTrusted) < 0) {
430         OPENSSL_free(derCertificate);
431         LogError("Failed to load der certificate from memory.");
432         ThrowMsg(Exception::InternalError,
433                  "Failed to load der certificate from memory.");
434     }
435
436     OPENSSL_free(derCertificate);
437 }
438
439 void XmlSec::loadPEMCertificateFile(XmlSecContext *context,
440         xmlSecKeysMngrPtr mngr)
441 {
442     if (xmlSecCryptoAppKeysMngrCertLoad(mngr,
443                                         context->certificatePath.c_str(),
444                                         xmlSecKeyDataFormatPem,
445                                         xmlSecKeyDataTypeTrusted) < 0) {
446         LogError("Failed to load PEM certificate from file.");
447         ThrowMsg(Exception::InternalError,
448                  "Failed to load PEM certificate from file.");
449     }
450 }
451
452 XmlSec::Result XmlSec::validate(XmlSecContext *context)
453 {
454     Assert(context);
455     Assert(!(context->signatureFile.empty()));
456     Assert(context->certificatePtr.get() || !(context->certificatePath.empty()));
457
458     xmlSecErrorsSetCallback(LogDebugPrint);
459
460     if (!m_initialized) {
461         LogError("XmlSec is not initialized.");
462         ThrowMsg(Exception::InternalError, "XmlSec is not initialized");
463     }
464
465     AutoPtr<xmlSecKeysMngr> mngr(xmlSecKeysMngrCreate());
466
467     if (!mngr.get()) {
468         LogError("Failed to create keys manager.");
469         ThrowMsg(Exception::InternalError, "Failed to create keys manager.");
470     }
471
472     if (xmlSecCryptoAppDefaultKeysMngrInit(mngr.get()) < 0) {
473         LogError("Failed to initialize keys manager.");
474         ThrowMsg(Exception::InternalError, "Failed to initialize keys manager.");
475     }
476     context->referenceSet.clear();
477
478     if (context->certificatePtr.get()) {
479         loadDERCertificateMemory(context, mngr.get());
480     }
481
482     if (!context->certificatePath.empty()) {
483         loadPEMCertificateFile(context, mngr.get());
484     }
485
486     return validateFile(context, mngr.get());
487 }
488
489 XmlSec::Result XmlSec::validateNoHash(XmlSecContext *context)
490 {
491     xmlSecErrorsSetCallback(LogDebugPrint);
492
493     m_noHash = true;
494     return validate(context);
495 }
496
497 XmlSec::Result XmlSec::validatePartialHash(XmlSecContext *context)
498 {
499     xmlSecErrorsSetCallback(LogDebugPrint);
500
501     m_partialHash = true;
502     return validate(context);
503 }
504
505 XmlSec::Result XmlSec::setPartialHashList(const std::list<std::string>& targetUri)
506 {
507   xmlSecErrorsSetCallback(LogDebugPrint);
508
509     m_pList = (std::list<std::string>*)&targetUri;
510     return NO_ERROR;
511 }
512 } // namespace ValidationCore