2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * @file XmlsecAdapter.cpp
18 * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
24 * Copyright (C) 2002-2003 Aleksey Sanin. All Rights Reserved.
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:
34 * The above copyright notice and this permission notice shall be
35 * included in all copies or substantial portions of the Software.
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.
51 #include <libxml/tree.h>
52 #include <libxml/xmlmemory.h>
53 #include <libxml/parser.h>
55 #ifndef XMLSEC_NO_XSLT
56 #include <libxslt/xslt.h>
57 #endif /* XMLSEC_NO_XSLT */
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>
66 #include <dpl/assert.h>
67 #include <dpl/log/log.h>
69 #include "XmlsecAdapter.h"
70 #include <dpl/singleton_impl.h>
71 IMPLEMENT_SINGLETON(ValidationCore::XmlSec)
73 namespace ValidationCore {
74 VC_DECLARE_DELETER(xmlSecKeysMngr, xmlSecKeysMngrDestroy)
76 static const char* DIGEST_MD5 = "md5";
78 std::string XmlSec::s_prefixPath;
80 int XmlSec::fileMatchCallback(const char *filename)
82 std::string path = s_prefixPath + filename;
83 return xmlFileMatch(path.c_str());
86 void* XmlSec::fileOpenCallback(const char *filename)
88 std::string path = s_prefixPath + filename;
89 LogDebug("Xmlsec opening: " << path);
90 return xmlFileOpen(path.c_str());
93 int XmlSec::fileReadCallback(void *context,
97 return xmlFileRead(context, buffer, len);
100 int XmlSec::fileCloseCallback(void *context)
102 return xmlFileClose(context);
105 void XmlSec::fileExtractPrefix(XmlSecContext *context)
107 if (!(context->workingDirectory.empty())) {
108 s_prefixPath = context->workingDirectory;
112 s_prefixPath = context->signatureFile;
113 size_t pos = s_prefixPath.rfind('/');
114 if (pos == std::string::npos) {
115 s_prefixPath.clear();
117 s_prefixPath.erase(pos + 1, std::string::npos);
125 xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
126 xmlSubstituteEntitiesDefault(1);
127 #ifndef XMLSEC_NO_XSLT
128 xmlIndentTreeOutput = 1;
131 if (xmlSecInit() < 0) {
132 LogError("Xmlsec initialization failed.");
133 ThrowMsg(Exception::InternalError, "Xmlsec initialization failed.");
136 if (xmlSecCheckVersion() != 1) {
138 LogError("Loaded xmlsec library version is not compatible.");
139 ThrowMsg(Exception::InternalError,
140 "Loaded xmlsec library version is not compatible.");
143 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
144 if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
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.");
155 if (xmlSecCryptoAppInit(NULL) < 0) {
157 LogError("Crypto initialization failed.");
158 ThrowMsg(Exception::InternalError, "Crypto initialization failed.");
161 if (xmlSecCryptoInit() < 0) {
162 xmlSecCryptoAppShutdown();
164 LogError("Xmlsec-crypto initialization failed.");
165 ThrowMsg(Exception::InternalError,
166 "Xmlsec-crypto initialization failed.");
169 m_initialized = true;
172 void XmlSec::deinitialize(void)
174 Assert(m_initialized);
176 /* Shutdown xmlsec-crypto library */
177 xmlSecCryptoShutdown();
179 /* Shutdown crypto library */
180 xmlSecCryptoAppShutdown();
182 /* Shutdown xmlsec library */
185 /* Shutdown libxslt/libxml */
186 #ifndef XMLSEC_NO_XSLT
187 xsltCleanupGlobals();
188 #endif /* XMLSEC_NO_XSLT */
190 s_prefixPath.clear();
191 m_initialized = false;
201 XmlSec::Result XmlSec::validateFile(XmlSecContext *context,
202 xmlSecKeysMngrPtr mngr)
204 xmlDocPtr doc = NULL;
205 xmlNodePtr node = NULL;
206 xmlSecDSigCtxPtr dsigCtx = NULL;
209 fileExtractPrefix(context);
210 LogDebug("Prefix path: " << s_prefixPath);
212 xmlSecIOCleanupCallbacks();
214 xmlSecIORegisterCallbacks(
221 doc = xmlParseFile(context->signatureFile.c_str());
222 if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)) {
223 LogWarning("Unable to parse file " << context->signatureFile);
227 /* find start node */
228 node = xmlSecFindNode(xmlDocGetRootElement(
229 doc), xmlSecNodeSignature, xmlSecDSigNs);
231 LogWarning("Start node not found in " << context->signatureFile);
235 /* create signature context */
236 dsigCtx = xmlSecDSigCtxCreate(mngr);
237 if (dsigCtx == NULL) {
238 LogError("Failed to create signature context.");
242 if (context->allowBrokenChain) {
243 dsigCtx->keyInfoReadCtx.flags |=
244 XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN;
247 if (context->validationTime) {
248 LogDebug("Setting validation time.");
249 dsigCtx->keyInfoReadCtx.certsVerificationTime = context->validationTime;
252 /* Verify signature */
253 if (xmlSecDSigCtxVerify(dsigCtx, node) < 0) {
254 LogWarning("Signature verify error.");
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;
265 /* print verification result to stdout */
266 if (dsigCtx->status == xmlSecDSigStatusSucceeded) {
267 LogDebug("Signature is OK");
270 LogDebug("Signature is INVALID");
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));
280 size = xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences));
281 for (int i = 0; i < size; ++i) {
282 xmlSecDSigReferenceCtxPtr dsigRefCtx =
283 (xmlSecDSigReferenceCtxPtr)xmlSecPtrListGetItem(&(dsigCtx->
284 signedInfoReferences),
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
292 std::string strDigest(pDigest);
293 LogInfo("reference digest method: " <<
294 reinterpret_cast<const char *>(dsigRefCtx->digestMethod
297 if (strDigest == DIGEST_MD5) {
298 LogWarning("MD5 digest method used! Please use sha");
303 context->referenceSet.insert(std::string(reinterpret_cast<char *>(
310 if (dsigCtx != NULL) {
311 xmlSecDSigCtxDestroy(dsigCtx);
319 return ERROR_INVALID_SIGNATURE;
324 void XmlSec::loadDERCertificateMemory(XmlSecContext *context,
325 xmlSecKeysMngrPtr mngr)
327 unsigned char *derCertificate = NULL;
328 int size = i2d_X509(context->certificatePtr->getX509(), &derCertificate);
330 if (!derCertificate) {
331 LogError("Failed during x509 conversion to der format.");
332 ThrowMsg(Exception::InternalError,
333 "Failed during x509 conversion to der format.");
336 if (xmlSecCryptoAppKeysMngrCertLoadMemory(mngr,
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.");
347 OPENSSL_free(derCertificate);
350 void XmlSec::loadPEMCertificateFile(XmlSecContext *context,
351 xmlSecKeysMngrPtr mngr)
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.");
363 XmlSec::Result XmlSec::validate(XmlSecContext *context)
366 Assert(!(context->signatureFile.empty()));
367 Assert(context->certificatePtr.Get() || !(context->certificatePath.empty()));
369 if (!m_initialized) {
370 LogError("XmlSec is not initialized.");
371 ThrowMsg(Exception::InternalError, "XmlSec is not initialized");
374 AutoPtr<xmlSecKeysMngr> mngr(xmlSecKeysMngrCreate());
377 LogError("Failed to create keys manager.");
378 ThrowMsg(Exception::InternalError, "Failed to create keys manager.");
381 if (xmlSecCryptoAppDefaultKeysMngrInit(mngr.get()) < 0) {
382 LogError("Failed to initialize keys manager.");
383 ThrowMsg(Exception::InternalError, "Failed to initialize keys manager.");
385 context->referenceSet.clear();
387 if (context->certificatePtr.Get()) {
388 loadDERCertificateMemory(context, mngr.get());
391 if (!context->certificatePath.empty()) {
392 loadPEMCertificateFile(context, mngr.get());
395 return validateFile(context, mngr.get());
397 } // namespace ValidationCore