2 * Copyright (c) 2016 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;
304 // Set proxy data to dsigCtx
305 if (context.isProxyMode && !context.proxySet.empty()) {
306 LogDebug("Set proxy data to xmlsec1 handle.");
307 for (auto data : context.proxySet) {
308 if (!strcmp(data.c_str(), "#prop"))
311 if(xmlSecProxyCtxAdd(&(dsigCtx.get()->proxyCtxPtr),
312 reinterpret_cast<const xmlChar *>(data.c_str())))
313 ThrowMsg(Exception::InternalError, "Failed to add proxy data.");
321 case ValidateMode::NORMAL: {
322 if (context.isProxyMode)
323 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_SKIP_PROXY;
325 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
329 case ValidateMode::NO_HASH:
330 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_IGNORE_REFERENCES;
331 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
334 case ValidateMode::PARTIAL_HASH: {
335 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_CHECK_PROXY;
336 for (auto uri : *m_pList) {
337 if(xmlSecProxyCtxAdd(&(dsigCtx.get()->proxyCtxPtr),
338 reinterpret_cast<const xmlChar *>(uri.c_str())))
339 ThrowMsg(Exception::InternalError, "PARTIAL_HASH mode failed.");
341 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
342 xmlSecProxyCtxDestroy(dsigCtx.get()->proxyCtxPtr);
347 ThrowMsg(Exception::InternalError, "ValidateMode is invalid");
351 ThrowMsg(Exception::InvalidSig, "Signature verify error.");
353 if (dsigCtx->keyInfoReadCtx.flags2 & XMLSEC_KEYINFO_ERROR_FLAGS_BROKEN_CHAIN) {
354 LogWarning("Signature contains broken chain!");
355 context.errorBrokenChain = true;
358 if (dsigCtx->status != xmlSecDSigStatusSucceeded)
359 ThrowMsg(Exception::InvalidSig, "Signature status is not succedded.");
361 // Set references for reverse reference check by ReferenceValidator
362 if (context.isProxyMode)
363 for (auto &proxy : context.proxySet)
364 context.referenceSet.insert(proxy);
366 xmlSecSize refSize = xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences));
367 for (xmlSecSize i = 0; i < refSize; ++i) {
368 xmlSecDSigReferenceCtxPtr dsigRefCtx = static_cast<xmlSecDSigReferenceCtxPtr>(
369 xmlSecPtrListGetItem(&(dsigCtx->signedInfoReferences), i));
371 if (!dsigRefCtx || !dsigRefCtx->uri)
374 if (dsigRefCtx->digestMethod
375 && dsigRefCtx->digestMethod->id
376 && dsigRefCtx->digestMethod->id->name) {
377 auto digest = reinterpret_cast<const char *const>(
378 dsigRefCtx->digestMethod->id->name);
380 if (DIGEST_MD5.compare(digest) == 0)
381 ThrowMsg(Exception::InvalidFormat,
382 "MD5 digest method used! Please use sha");
385 context.referenceSet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
386 if (context.isProxyMode)
387 context.proxySet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
391 void XmlSec::loadDERCertificateMemory(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
396 derCert = context.certificatePtr->getDER();
397 } catch (Certificate::Exception::Base &e) {
398 ThrowMsg(Exception::InternalError,
399 "Failed during x509 conversion to der format: " << e.DumpToString());
402 if (xmlSecCryptoAppKeysMngrCertLoadMemory(
404 reinterpret_cast<const xmlSecByte *>(derCert.data()),
405 static_cast<xmlSecSize>(derCert.length()),
406 xmlSecKeyDataFormatDer,
407 xmlSecKeyDataTypeTrusted) < 0)
408 ThrowMsg(Exception::InternalError, "Failed to load der cert from memory.");
411 void XmlSec::loadPEMCertificateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
413 if (xmlSecCryptoAppKeysMngrCertLoad(
415 context.certificatePath.c_str(),
416 xmlSecKeyDataFormatPem,
417 xmlSecKeyDataTypeTrusted) < 0)
418 ThrowMsg(Exception::InternalError, "Failed to load PEM cert from file.");
421 void XmlSec::validateInternal(XmlSecContext &context)
423 LogDebug("Start to validate.");
424 Assert(!context.signatureFile.empty());
425 Assert(!!context.certificatePtr || !context.certificatePath.empty());
426 xmlSecErrorsSetCallback(LogDebugPrint);
429 ThrowMsg(Exception::InternalError, "XmlSec is not initialized");
431 CustomPtr<xmlSecKeysMngrPtr> mngrPtr(xmlSecKeysMngrCreate(), xmlSecKeysMngrDestroy);
434 ThrowMsg(Exception::InternalError, "Failed to create keys manager.");
436 if (xmlSecCryptoAppDefaultKeysMngrInit(mngrPtr.get()) < 0)
437 ThrowMsg(Exception::InternalError, "Failed to initialize keys manager.");
439 context.referenceSet.clear();
441 if (!!context.certificatePtr)
442 loadDERCertificateMemory(context, mngrPtr.get());
444 if (!context.certificatePath.empty())
445 loadPEMCertificateFile(context, mngrPtr.get());
447 validateFile(context, mngrPtr.get());
450 void XmlSec::validate(XmlSecContext &context)
452 m_mode = ValidateMode::NORMAL;
453 validateInternal(context);
456 void XmlSec::validateNoHash(XmlSecContext &context)
458 m_mode = ValidateMode::NO_HASH;
459 validateInternal(context);
462 void XmlSec::validatePartialHash(XmlSecContext &context, const std::list<std::string> &targetUri)
464 m_mode = ValidateMode::PARTIAL_HASH;
465 m_pList = &targetUri;
466 validateInternal(context);
469 } // namespace ValidationCore