2 * Copyright (c) 2016-2023 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 LogDebug("Xmlsec opening:" << filename);
80 std::string path = s_prefixPath + filename;
81 return new FileWrapper(xmlFileOpen(path.c_str()), false);
84 int XmlSec::fileReadCallback(void *context,
88 FileWrapper *fw = static_cast<FileWrapper *>(context);
93 int output = xmlFileRead(fw->file, buffer, len);
97 xmlFileClose(fw->file);
103 int XmlSec::fileCloseCallback(void *context)
105 LogDebug("Xmlsec closing");
107 FileWrapper *fw = static_cast<FileWrapper *>(context);
111 output = xmlFileClose(fw->file);
117 void XmlSec::fileExtractPrefix(XmlSecContext &context)
119 if (!context.workingDirectory.empty()) {
120 s_prefixPath = context.workingDirectory;
124 s_prefixPath = context.signatureFile;
125 size_t pos = s_prefixPath.rfind('/');
127 if (pos == std::string::npos)
128 s_prefixPath.clear();
130 s_prefixPath.erase(pos + 1, std::string::npos);
133 void LogErrorPrint(const char *file,
136 const char *errorObject,
137 const char *errorSubject,
141 // Get error message from xmlsec.
142 const char *errorMsg = NULL;
143 for (xmlSecSize i = 0; (i < XMLSEC_ERRORS_MAX_NUMBER) &&
144 (xmlSecErrorsGetMsg(i) != NULL); ++i) {
145 if (xmlSecErrorsGetCode(i) == reason) {
146 errorMsg = xmlSecErrorsGetMsg(i);
151 // Make full error message.
152 char buff[VCORE_ERRORS_BUFFER_SIZE];
155 "[%s:%d] %s(): obj=%s, subj=%s, error=%d:%s:%s\n",
159 (errorObject != NULL) ? errorObject : "",
160 (errorSubject != NULL) ? errorSubject : "",
162 (errorMsg != NULL) ? errorMsg : "",
163 (msg != NULL) ? msg : "");
164 buff[sizeof(buff) - 1] = '\0';
170 m_mode(ValidateMode::NORMAL),
174 xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
175 xmlSubstituteEntitiesDefault(1);
176 #ifndef XMLSEC_NO_XSLT
177 xmlIndentTreeOutput = 1;
180 if (xmlSecInit() < 0)
181 ThrowMsg(Exception::InternalError, "Xmlsec initialization failed.");
183 if (xmlSecCheckVersion() != 1) {
185 ThrowMsg(Exception::InternalError,
186 "Loaded xmlsec library version is not compatible.");
189 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
191 if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
194 "Error: unable to load default xmlsec-crypto library. Make sure "
195 "that you have it installed and check shared libraries path "
196 "(LD_LIBRARY_PATH) envornment variable.");
197 ThrowMsg(Exception::InternalError,
198 "Unable to load default xmlsec-crypto library.");
203 if (xmlSecCryptoAppInit(nullptr) < 0) {
205 ThrowMsg(Exception::InternalError, "Crypto initialization failed.");
208 if (xmlSecCryptoInit() < 0) {
209 xmlSecCryptoAppShutdown();
211 ThrowMsg(Exception::InternalError,
212 "Xmlsec-crypto initialization failed.");
218 xmlSecCryptoShutdown();
219 xmlSecCryptoAppShutdown();
221 #ifndef XMLSEC_NO_XSLT
222 xsltCleanupGlobals();
224 s_prefixPath.clear();
227 void XmlSec::validateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
229 fileExtractPrefix(context);
230 LogDebug("Prefix:" << s_prefixPath);
231 xmlSecIOCleanupCallbacks();
232 if (xmlSecIORegisterCallbacks(
236 fileCloseCallback) < 0)
237 ThrowMsg(Exception::InternalError,
238 "Error in xmlSecIORegisterCallbacks");
240 std::unique_ptr<xmlDoc, std::function<void(xmlDocPtr)>> docPtr(
241 xmlParseFile(context.signatureFile.c_str()), xmlFreeDoc);
243 if (!docPtr || xmlDocGetRootElement(docPtr.get()) == nullptr)
244 ThrowMsg(Exception::InvalidFormat,
245 "Unable to parse sig xml file: " << context.signatureFile);
247 xmlNodePtr node = xmlSecFindNode(
248 xmlDocGetRootElement(docPtr.get()),
253 ThrowMsg(Exception::InvalidFormat,
254 "Start node not found in " << context.signatureFile);
256 std::unique_ptr<xmlSecDSigCtx, std::function<void(xmlSecDSigCtxPtr)>> dsigCtx(
257 xmlSecDSigCtxCreate(mngrPtr),
258 [](xmlSecDSigCtxPtr dsigCtx) {
259 xmlSecProxyCtxDestroy(dsigCtx->skipReferences);
260 xmlSecProxyCtxDestroy(dsigCtx->checkReferences);
261 xmlSecDSigCtxDestroy(dsigCtx);
265 ThrowMsg(Exception::OutOfMemory, "Failed to create signature context.");
267 if (context.allowBrokenChain)
268 dsigCtx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN;
270 if (context.validationTime) {
271 LogDebug("Setting validation time.");
272 dsigCtx->keyInfoReadCtx.certsVerificationTime = context.validationTime;
275 // Set proxy data to dsigCtx
276 if (context.isProxyMode && !context.proxySet.empty()) {
277 LogDebug("Set proxy data to xmlsec1 handle.");
278 for (auto &data : context.proxySet) {
279 if (!strcmp(data.c_str(), "#prop"))
282 if(xmlSecProxyCtxAdd(&(dsigCtx.get()->skipReferences),
283 reinterpret_cast<const xmlChar *>(data.c_str())))
284 ThrowMsg(Exception::InternalError, "Failed to add proxy data.");
286 LogDebug("Add [" << data << "] to proxy.");
294 case ValidateMode::NORMAL: {
295 if (context.isProxyMode)
296 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_SKIP_PROXY;
298 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
302 case ValidateMode::NO_HASH:
303 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_IGNORE_REFERENCES;
304 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
307 case ValidateMode::PARTIAL_HASH: {
308 if (context.isProxyMode)
309 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_SKIP_PROXY;
311 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_CHECK_PROXY;
312 for (auto &uri : *m_pList) {
313 if(xmlSecProxyCtxAdd(&(dsigCtx.get()->checkReferences),
314 reinterpret_cast<const xmlChar *>(uri.c_str())))
315 ThrowMsg(Exception::InternalError, "PARTIAL_HASH mode failed.");
317 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
322 ThrowMsg(Exception::InternalError, "ValidateMode is invalid");
326 ThrowMsg(Exception::InvalidSig, "Signature verify error.");
328 if (dsigCtx->keyInfoReadCtx.flags2 & XMLSEC_KEYINFO_ERROR_FLAGS_BROKEN_CHAIN) {
329 LogWarning("Signature contains broken chain!");
330 context.errorBrokenChain = true;
333 if (dsigCtx->status != xmlSecDSigStatusSucceeded)
334 ThrowMsg(Exception::InvalidSig, "Signature status is not succedded.");
336 // Set references for reverse reference check by ReferenceValidator
337 if (context.isProxyMode)
338 for (auto &proxy : context.proxySet)
339 context.referenceSet.insert(proxy);
341 xmlSecSize refSize = xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences));
342 for (xmlSecSize i = 0; i < refSize; ++i) {
343 xmlSecDSigReferenceCtxPtr dsigRefCtx = static_cast<xmlSecDSigReferenceCtxPtr>(
344 xmlSecPtrListGetItem(&(dsigCtx->signedInfoReferences), i));
346 if (!dsigRefCtx || !dsigRefCtx->uri)
349 if (dsigRefCtx->digestMethod
350 && dsigRefCtx->digestMethod->id
351 && dsigRefCtx->digestMethod->id->name) {
352 auto digest = reinterpret_cast<const char *>(
353 dsigRefCtx->digestMethod->id->name);
355 if (DIGEST_MD5.compare(digest) == 0)
356 ThrowMsg(Exception::InvalidFormat,
357 "MD5 digest method used! Please use sha");
360 context.referenceSet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
361 if (context.isProxyMode)
362 context.proxySet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
366 void XmlSec::loadDERCertificateMemory(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
371 derCert = context.certificatePtr->getDER();
372 } catch (Certificate::Exception::Base &e) {
373 ThrowMsg(Exception::InternalError,
374 "Failed during x509 conversion to der format: " << e.DumpToString());
377 if (xmlSecCryptoAppKeysMngrCertLoadMemory(
379 reinterpret_cast<const xmlSecByte *>(derCert.data()),
380 static_cast<xmlSecSize>(derCert.length()),
381 xmlSecKeyDataFormatDer,
382 xmlSecKeyDataTypeTrusted) < 0)
383 ThrowMsg(Exception::InternalError, "Failed to load der cert from memory.");
386 void XmlSec::loadPEMCertificateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
388 if (xmlSecCryptoAppKeysMngrCertLoad(
390 context.certificatePath.c_str(),
391 xmlSecKeyDataFormatPem,
392 xmlSecKeyDataTypeTrusted) < 0)
393 ThrowMsg(Exception::InternalError, "Failed to load PEM cert from file.");
396 void XmlSec::validateInternal(XmlSecContext &context)
398 LogDebug("Start to validate.");
399 Assert(!context.signatureFile.empty());
400 Assert(!!context.certificatePtr || !context.certificatePath.empty());
401 xmlSecErrorsSetCallback(LogErrorPrint);
403 std::unique_ptr<xmlSecKeysMngr, std::function<void(xmlSecKeysMngrPtr)>>
404 mngrPtr(xmlSecKeysMngrCreate(), xmlSecKeysMngrDestroy);
407 ThrowMsg(Exception::InternalError, "Failed to create keys manager.");
409 if (xmlSecCryptoAppDefaultKeysMngrInit(mngrPtr.get()) < 0)
410 ThrowMsg(Exception::InternalError, "Failed to initialize keys manager.");
412 context.referenceSet.clear();
414 if (!!context.certificatePtr)
415 loadDERCertificateMemory(context, mngrPtr.get());
417 if (!context.certificatePath.empty())
418 loadPEMCertificateFile(context, mngrPtr.get());
420 validateFile(context, mngrPtr.get());
423 void XmlSec::validate(XmlSecContext &context)
425 m_mode = ValidateMode::NORMAL;
426 validateInternal(context);
429 void XmlSec::validateNoHash(XmlSecContext &context)
431 m_mode = ValidateMode::NO_HASH;
432 validateInternal(context);
435 void XmlSec::validatePartialHash(XmlSecContext &context, const std::list<std::string> &targetUri)
437 m_mode = ValidateMode::PARTIAL_HASH;
438 m_pList = &targetUri;
439 validateInternal(context);
442 } // namespace ValidationCore