2 * Copyright (c) 2015 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)
19 * @author Sangwan Kwon (sangwan.kwon@samsung.com)
27 #include <libxml/tree.h>
28 #include <libxml/xmlmemory.h>
29 #include <libxml/parser.h>
31 #ifndef XMLSEC_NO_XSLT
32 #include <libxslt/xslt.h>
35 #include <xmlsec/xmlsec.h>
36 #include <xmlsec/xmltree.h>
37 #include <xmlsec/xmldsig.h>
38 #include <xmlsec/crypto.h>
39 #include <xmlsec/io.h>
40 #include <xmlsec/keyinfo.h>
41 #include <xmlsec/errors.h>
43 #include <dpl/assert.h>
44 #include <dpl/log/log.h>
45 #include <dpl/singleton_impl.h>
47 #include <vcore/XmlsecAdapter.h>
49 #define VCORE_ERRORS_BUFFER_SIZE 1024
51 IMPLEMENT_SINGLETON(ValidationCore::XmlSec)
55 template <typename Type>
58 std::function<void(Type)> deleter;
62 explicit CustomPtr(Type in, std::function<void(Type)> d)
71 inline Type get(void) const
76 inline Type operator->() const
81 inline bool operator!() const
83 return (ptr == nullptr) ? true : false;
88 FileWrapper(void *argFile, bool argReleased)
90 , released(argReleased)
96 } // anonymous namespace
98 namespace ValidationCore {
100 static const std::string DIGEST_MD5 = "md5";
102 std::string XmlSec::s_prefixPath;
104 int XmlSec::fileMatchCallback(const char *filename)
106 std::string path = s_prefixPath + filename;
107 return xmlFileMatch(path.c_str());
110 void *XmlSec::fileOpenCallback(const char *filename)
112 std::string path = s_prefixPath + filename;
113 LogDebug("Xmlsec opening : " << path);
114 return new FileWrapper(xmlFileOpen(path.c_str()), false);
117 int XmlSec::fileReadCallback(void *context,
121 FileWrapper *fw = static_cast<FileWrapper *>(context);
126 int output = xmlFileRead(fw->file, buffer, len);
130 xmlFileClose(fw->file);
136 int XmlSec::fileCloseCallback(void *context)
138 FileWrapper *fw = static_cast<FileWrapper *>(context);
142 output = xmlFileClose(fw->file);
148 void XmlSec::fileExtractPrefix(XmlSecContext &context)
150 if (!context.workingDirectory.empty()) {
151 s_prefixPath = context.workingDirectory;
155 s_prefixPath = context.signatureFile;
156 size_t pos = s_prefixPath.rfind('/');
158 if (pos == std::string::npos)
159 s_prefixPath.clear();
161 s_prefixPath.erase(pos + 1, std::string::npos);
164 void LogDebugPrint(const char *file,
167 const char *errorObject,
168 const char *errorSubject,
172 // Get error message from xmlsec.
173 const char *errorMsg = NULL;
174 for (xmlSecSize i = 0; (i < XMLSEC_ERRORS_MAX_NUMBER) &&
175 (xmlSecErrorsGetMsg(i) != NULL); ++i) {
176 if (xmlSecErrorsGetCode(i) == reason) {
177 errorMsg = xmlSecErrorsGetMsg(i);
182 // Make full error message.
183 char buff[VCORE_ERRORS_BUFFER_SIZE];
186 "[%s:%d] %s(): obj=%s, subj=%s, error=%d:%s:%s\n",
190 (errorObject != NULL) ? errorObject : "",
191 (errorSubject != NULL) ? errorSubject : "",
193 (errorMsg != NULL) ? errorMsg : "",
194 (msg != NULL) ? msg : "");
196 if (reason == XMLSEC_ERRORS_MAX_NUMBER)
203 : m_initialized(false)
207 xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
208 xmlSubstituteEntitiesDefault(1);
209 #ifndef XMLSEC_NO_XSLT
210 xmlIndentTreeOutput = 1;
213 if (xmlSecInit() < 0)
214 ThrowMsg(Exception::InternalError, "Xmlsec initialization failed.");
216 if (xmlSecCheckVersion() != 1) {
218 ThrowMsg(Exception::InternalError,
219 "Loaded xmlsec library version is not compatible.");
222 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
224 if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
227 "Error: unable to load default xmlsec-crypto library. Make sure "
228 "that you have it installed and check shared libraries path "
229 "(LD_LIBRARY_PATH) envornment variable.");
230 ThrowMsg(Exception::InternalError,
231 "Unable to load default xmlsec-crypto library.");
236 if (xmlSecCryptoAppInit(nullptr) < 0) {
238 ThrowMsg(Exception::InternalError, "Crypto initialization failed.");
241 if (xmlSecCryptoInit() < 0) {
242 xmlSecCryptoAppShutdown();
244 ThrowMsg(Exception::InternalError,
245 "Xmlsec-crypto initialization failed.");
248 m_initialized = true;
256 xmlSecCryptoShutdown();
257 xmlSecCryptoAppShutdown();
259 #ifndef XMLSEC_NO_XSLT
260 xsltCleanupGlobals();
262 s_prefixPath.clear();
263 m_initialized = false;
266 void XmlSec::validateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
268 fileExtractPrefix(context);
269 LogDebug("Prefix path : " << s_prefixPath);
270 xmlSecIOCleanupCallbacks();
271 xmlSecIORegisterCallbacks(
276 CustomPtr<xmlDocPtr> docPtr(xmlParseFile(context.signatureFile.c_str()), xmlFreeDoc);
278 if (!docPtr || xmlDocGetRootElement(docPtr.get()) == nullptr)
279 ThrowMsg(Exception::InvalidFormat,
280 "Unable to parse sig xml file: " << context.signatureFile);
282 xmlNodePtr node = xmlSecFindNode(
283 xmlDocGetRootElement(docPtr.get()),
288 ThrowMsg(Exception::InvalidFormat,
289 "Start node not found in " << context.signatureFile);
291 CustomPtr<xmlSecDSigCtxPtr> dsigCtx(xmlSecDSigCtxCreate(mngrPtr), xmlSecDSigCtxDestroy);
294 ThrowMsg(Exception::OutOfMemory, "Failed to create signature context.");
296 if (context.allowBrokenChain)
297 dsigCtx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN;
299 if (context.validationTime) {
300 LogDebug("Setting validation time.");
301 dsigCtx->keyInfoReadCtx.certsVerificationTime = context.validationTime;
307 case ValidateMode::NORMAL: {
308 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
312 case ValidateMode::NO_HASH:
313 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_IGNORE_REFERENCES;
314 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
317 case ValidateMode::PARTIAL_HASH: {
318 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_CHECK_PROXY;
319 for (auto uri : *m_pList) {
320 if(xmlSecProxyCtxAdd(&(dsigCtx.get()->proxyCtxPtr),
321 reinterpret_cast<const xmlChar *>(uri.c_str())))
322 ThrowMsg(Exception::InternalError, "PARTIAL_HASH mode failed.");
324 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
325 xmlSecProxyCtxDestroy(dsigCtx.get()->proxyCtxPtr);
330 ThrowMsg(Exception::InternalError, "ValidateMode is invalid");
334 ThrowMsg(Exception::InvalidSig, "Signature verify error.");
336 if (dsigCtx->keyInfoReadCtx.flags2 & XMLSEC_KEYINFO_ERROR_FLAGS_BROKEN_CHAIN) {
337 LogWarning("Signature contains broken chain!");
338 context.errorBrokenChain = true;
341 if (dsigCtx->status != xmlSecDSigStatusSucceeded)
342 ThrowMsg(Exception::InvalidSig, "Signature status is not succedded.");
344 xmlSecSize refSize = xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences));
346 for (xmlSecSize i = 0; i < refSize; ++i) {
347 xmlSecDSigReferenceCtxPtr dsigRefCtx = static_cast<xmlSecDSigReferenceCtxPtr>(
348 xmlSecPtrListGetItem(&(dsigCtx->signedInfoReferences), i));
350 if (!dsigRefCtx || !dsigRefCtx->uri)
353 if (dsigRefCtx->digestMethod
354 && dsigRefCtx->digestMethod->id
355 && dsigRefCtx->digestMethod->id->name) {
356 auto digest = reinterpret_cast<const char *const>(
357 dsigRefCtx->digestMethod->id->name);
359 if (DIGEST_MD5.compare(digest) == 0)
360 ThrowMsg(Exception::InvalidFormat,
361 "MD5 digest method used! Please use sha");
364 context.referenceSet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
368 void XmlSec::loadDERCertificateMemory(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
373 derCert = context.certificatePtr->getDER();
374 } catch (Certificate::Exception::Base &e) {
375 ThrowMsg(Exception::InternalError,
376 "Failed during x509 conversion to der format: " << e.DumpToString());
379 if (xmlSecCryptoAppKeysMngrCertLoadMemory(
381 reinterpret_cast<const xmlSecByte *>(derCert.data()),
382 static_cast<xmlSecSize>(derCert.length()),
383 xmlSecKeyDataFormatDer,
384 xmlSecKeyDataTypeTrusted) < 0)
385 ThrowMsg(Exception::InternalError, "Failed to load der cert from memory.");
388 void XmlSec::loadPEMCertificateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
390 if (xmlSecCryptoAppKeysMngrCertLoad(
392 context.certificatePath.c_str(),
393 xmlSecKeyDataFormatPem,
394 xmlSecKeyDataTypeTrusted) < 0)
395 ThrowMsg(Exception::InternalError, "Failed to load PEM cert from file.");
398 void XmlSec::validateInternal(XmlSecContext &context)
400 LogDebug("Start to validate.");
401 Assert(!context.signatureFile.empty());
402 Assert(!!context.certificatePtr || !context.certificatePath.empty());
403 xmlSecErrorsSetCallback(LogDebugPrint);
406 ThrowMsg(Exception::InternalError, "XmlSec is not initialized");
408 CustomPtr<xmlSecKeysMngrPtr> mngrPtr(xmlSecKeysMngrCreate(), xmlSecKeysMngrDestroy);
411 ThrowMsg(Exception::InternalError, "Failed to create keys manager.");
413 if (xmlSecCryptoAppDefaultKeysMngrInit(mngrPtr.get()) < 0)
414 ThrowMsg(Exception::InternalError, "Failed to initialize keys manager.");
416 context.referenceSet.clear();
418 if (!!context.certificatePtr)
419 loadDERCertificateMemory(context, mngrPtr.get());
421 if (!context.certificatePath.empty())
422 loadPEMCertificateFile(context, mngrPtr.get());
424 validateFile(context, mngrPtr.get());
427 void XmlSec::validate(XmlSecContext &context)
429 m_mode = ValidateMode::NORMAL;
430 validateInternal(context);
433 void XmlSec::validateNoHash(XmlSecContext &context)
435 m_mode = ValidateMode::NO_HASH;
436 validateInternal(context);
439 void XmlSec::validatePartialHash(XmlSecContext &context, const std::list<std::string> &targetUri)
441 m_mode = ValidateMode::PARTIAL_HASH;
442 m_pList = &targetUri;
443 validateInternal(context);
446 } // namespace ValidationCore