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)
28 #include <libxml/tree.h>
29 #include <libxml/xmlmemory.h>
30 #include <libxml/parser.h>
32 #ifndef XMLSEC_NO_XSLT
33 #include <libxslt/xslt.h>
36 #include <xmlsec/xmlsec.h>
37 #include <xmlsec/xmltree.h>
38 #include <xmlsec/xmldsig.h>
39 #include <xmlsec/crypto.h>
40 #include <xmlsec/io.h>
41 #include <xmlsec/keyinfo.h>
42 #include <xmlsec/errors.h>
44 #include <dpl/assert.h>
45 #include <dpl/log/log.h>
47 #include <vcore/XmlsecAdapter.h>
49 #define VCORE_ERRORS_BUFFER_SIZE 1024
54 FileWrapper(void *argFile, bool argReleased)
56 , released(argReleased)
62 } // anonymous namespace
64 namespace ValidationCore {
66 static const std::string DIGEST_MD5 = "md5";
68 std::string XmlSec::s_prefixPath;
70 int XmlSec::fileMatchCallback(const char *filename)
72 std::string path = s_prefixPath + filename;
73 return xmlFileMatch(path.c_str());
76 void *XmlSec::fileOpenCallback(const char *filename)
78 std::string path = s_prefixPath + filename;
79 LogDebug("Xmlsec opening : " << path);
80 return new FileWrapper(xmlFileOpen(path.c_str()), false);
83 int XmlSec::fileReadCallback(void *context,
87 FileWrapper *fw = static_cast<FileWrapper *>(context);
92 int output = xmlFileRead(fw->file, buffer, len);
96 xmlFileClose(fw->file);
102 int XmlSec::fileCloseCallback(void *context)
104 FileWrapper *fw = static_cast<FileWrapper *>(context);
108 output = xmlFileClose(fw->file);
114 void XmlSec::fileExtractPrefix(XmlSecContext &context)
116 if (!context.workingDirectory.empty()) {
117 s_prefixPath = context.workingDirectory;
121 s_prefixPath = context.signatureFile;
122 size_t pos = s_prefixPath.rfind('/');
124 if (pos == std::string::npos)
125 s_prefixPath.clear();
127 s_prefixPath.erase(pos + 1, std::string::npos);
130 void LogDebugPrint(const char *file,
133 const char *errorObject,
134 const char *errorSubject,
138 // Get error message from xmlsec.
139 const char *errorMsg = NULL;
140 for (xmlSecSize i = 0; (i < XMLSEC_ERRORS_MAX_NUMBER) &&
141 (xmlSecErrorsGetMsg(i) != NULL); ++i) {
142 if (xmlSecErrorsGetCode(i) == reason) {
143 errorMsg = xmlSecErrorsGetMsg(i);
148 // Make full error message.
149 char buff[VCORE_ERRORS_BUFFER_SIZE];
152 "[%s:%d] %s(): obj=%s, subj=%s, error=%d:%s:%s\n",
156 (errorObject != NULL) ? errorObject : "",
157 (errorSubject != NULL) ? errorSubject : "",
159 (errorMsg != NULL) ? errorMsg : "",
160 (msg != NULL) ? msg : "");
162 if (reason == XMLSEC_ERRORS_MAX_NUMBER)
169 m_mode(ValidateMode::NORMAL),
173 xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
174 xmlSubstituteEntitiesDefault(1);
175 #ifndef XMLSEC_NO_XSLT
176 xmlIndentTreeOutput = 1;
179 if (xmlSecInit() < 0)
180 ThrowMsg(Exception::InternalError, "Xmlsec initialization failed.");
182 if (xmlSecCheckVersion() != 1) {
184 ThrowMsg(Exception::InternalError,
185 "Loaded xmlsec library version is not compatible.");
188 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
190 if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
193 "Error: unable to load default xmlsec-crypto library. Make sure "
194 "that you have it installed and check shared libraries path "
195 "(LD_LIBRARY_PATH) envornment variable.");
196 ThrowMsg(Exception::InternalError,
197 "Unable to load default xmlsec-crypto library.");
202 if (xmlSecCryptoAppInit(nullptr) < 0) {
204 ThrowMsg(Exception::InternalError, "Crypto initialization failed.");
207 if (xmlSecCryptoInit() < 0) {
208 xmlSecCryptoAppShutdown();
210 ThrowMsg(Exception::InternalError,
211 "Xmlsec-crypto initialization failed.");
217 xmlSecCryptoShutdown();
218 xmlSecCryptoAppShutdown();
220 #ifndef XMLSEC_NO_XSLT
221 xsltCleanupGlobals();
223 s_prefixPath.clear();
226 void XmlSec::validateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
228 fileExtractPrefix(context);
229 LogDebug("Prefix path : " << s_prefixPath);
230 xmlSecIOCleanupCallbacks();
231 xmlSecIORegisterCallbacks(
236 std::unique_ptr<xmlDoc, std::function<void(xmlDocPtr)>> docPtr(
237 xmlParseFile(context.signatureFile.c_str()), xmlFreeDoc);
239 if (!docPtr || xmlDocGetRootElement(docPtr.get()) == nullptr)
240 ThrowMsg(Exception::InvalidFormat,
241 "Unable to parse sig xml file: " << context.signatureFile);
243 xmlNodePtr node = xmlSecFindNode(
244 xmlDocGetRootElement(docPtr.get()),
249 ThrowMsg(Exception::InvalidFormat,
250 "Start node not found in " << context.signatureFile);
252 std::unique_ptr<xmlSecDSigCtx, std::function<void(xmlSecDSigCtxPtr)>> dsigCtx(
253 xmlSecDSigCtxCreate(mngrPtr),
254 [](xmlSecDSigCtxPtr dsigCtx) {
255 xmlSecProxyCtxDestroy(dsigCtx->skipReferences);
256 xmlSecProxyCtxDestroy(dsigCtx->checkReferences);
257 xmlSecDSigCtxDestroy(dsigCtx);
261 ThrowMsg(Exception::OutOfMemory, "Failed to create signature context.");
263 if (context.allowBrokenChain)
264 dsigCtx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN;
266 if (context.validationTime) {
267 LogDebug("Setting validation time.");
268 dsigCtx->keyInfoReadCtx.certsVerificationTime = context.validationTime;
271 // Set proxy data to dsigCtx
272 if (context.isProxyMode && !context.proxySet.empty()) {
273 LogDebug("Set proxy data to xmlsec1 handle.");
274 for (auto data : context.proxySet) {
275 if (!strcmp(data.c_str(), "#prop"))
278 if(xmlSecProxyCtxAdd(&(dsigCtx.get()->skipReferences),
279 reinterpret_cast<const xmlChar *>(data.c_str())))
280 ThrowMsg(Exception::InternalError, "Failed to add proxy data.");
282 LogDebug("Add [" << data << "] to proxy.");
290 case ValidateMode::NORMAL: {
291 if (context.isProxyMode)
292 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_SKIP_PROXY;
294 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
298 case ValidateMode::NO_HASH:
299 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_IGNORE_REFERENCES;
300 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
303 case ValidateMode::PARTIAL_HASH: {
304 if (context.isProxyMode)
305 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_SKIP_PROXY;
307 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_CHECK_PROXY;
308 for (auto uri : *m_pList) {
309 if(xmlSecProxyCtxAdd(&(dsigCtx.get()->checkReferences),
310 reinterpret_cast<const xmlChar *>(uri.c_str())))
311 ThrowMsg(Exception::InternalError, "PARTIAL_HASH mode failed.");
313 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
318 ThrowMsg(Exception::InternalError, "ValidateMode is invalid");
322 ThrowMsg(Exception::InvalidSig, "Signature verify error.");
324 if (dsigCtx->keyInfoReadCtx.flags2 & XMLSEC_KEYINFO_ERROR_FLAGS_BROKEN_CHAIN) {
325 LogWarning("Signature contains broken chain!");
326 context.errorBrokenChain = true;
329 if (dsigCtx->status != xmlSecDSigStatusSucceeded)
330 ThrowMsg(Exception::InvalidSig, "Signature status is not succedded.");
332 // Set references for reverse reference check by ReferenceValidator
333 if (context.isProxyMode)
334 for (auto &proxy : context.proxySet)
335 context.referenceSet.insert(proxy);
337 xmlSecSize refSize = xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences));
338 for (xmlSecSize i = 0; i < refSize; ++i) {
339 xmlSecDSigReferenceCtxPtr dsigRefCtx = static_cast<xmlSecDSigReferenceCtxPtr>(
340 xmlSecPtrListGetItem(&(dsigCtx->signedInfoReferences), i));
342 if (!dsigRefCtx || !dsigRefCtx->uri)
345 if (dsigRefCtx->digestMethod
346 && dsigRefCtx->digestMethod->id
347 && dsigRefCtx->digestMethod->id->name) {
348 auto digest = reinterpret_cast<const char *const>(
349 dsigRefCtx->digestMethod->id->name);
351 if (DIGEST_MD5.compare(digest) == 0)
352 ThrowMsg(Exception::InvalidFormat,
353 "MD5 digest method used! Please use sha");
356 context.referenceSet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
357 if (context.isProxyMode)
358 context.proxySet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
362 void XmlSec::loadDERCertificateMemory(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
367 derCert = context.certificatePtr->getDER();
368 } catch (Certificate::Exception::Base &e) {
369 ThrowMsg(Exception::InternalError,
370 "Failed during x509 conversion to der format: " << e.DumpToString());
373 if (xmlSecCryptoAppKeysMngrCertLoadMemory(
375 reinterpret_cast<const xmlSecByte *>(derCert.data()),
376 static_cast<xmlSecSize>(derCert.length()),
377 xmlSecKeyDataFormatDer,
378 xmlSecKeyDataTypeTrusted) < 0)
379 ThrowMsg(Exception::InternalError, "Failed to load der cert from memory.");
382 void XmlSec::loadPEMCertificateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
384 if (xmlSecCryptoAppKeysMngrCertLoad(
386 context.certificatePath.c_str(),
387 xmlSecKeyDataFormatPem,
388 xmlSecKeyDataTypeTrusted) < 0)
389 ThrowMsg(Exception::InternalError, "Failed to load PEM cert from file.");
392 void XmlSec::validateInternal(XmlSecContext &context)
394 LogDebug("Start to validate.");
395 Assert(!context.signatureFile.empty());
396 Assert(!!context.certificatePtr || !context.certificatePath.empty());
397 xmlSecErrorsSetCallback(LogDebugPrint);
399 std::unique_ptr<xmlSecKeysMngr, std::function<void(xmlSecKeysMngrPtr)>>
400 mngrPtr(xmlSecKeysMngrCreate(), xmlSecKeysMngrDestroy);
403 ThrowMsg(Exception::InternalError, "Failed to create keys manager.");
405 if (xmlSecCryptoAppDefaultKeysMngrInit(mngrPtr.get()) < 0)
406 ThrowMsg(Exception::InternalError, "Failed to initialize keys manager.");
408 context.referenceSet.clear();
410 if (!!context.certificatePtr)
411 loadDERCertificateMemory(context, mngrPtr.get());
413 if (!context.certificatePath.empty())
414 loadPEMCertificateFile(context, mngrPtr.get());
416 validateFile(context, mngrPtr.get());
419 void XmlSec::validate(XmlSecContext &context)
421 m_mode = ValidateMode::NORMAL;
422 validateInternal(context);
425 void XmlSec::validateNoHash(XmlSecContext &context)
427 m_mode = ValidateMode::NO_HASH;
428 validateInternal(context);
431 void XmlSec::validatePartialHash(XmlSecContext &context, const std::list<std::string> &targetUri)
433 m_mode = ValidateMode::PARTIAL_HASH;
434 m_pList = &targetUri;
435 validateInternal(context);
438 } // namespace ValidationCore