2eb1574ab199e1e8c85387bf7c50754b0f40d935
[platform/core/security/cert-svc.git] / src / vcore / XmlsecAdapter.cpp
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 /*
17  * @file        XmlsecAdapter.cpp
18  * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
19  * @author      Sangwan Kwon (sangwan.kwon@samsung.com)
20  * @version     2.1
21  * @brief
22  */
23 #include <cstdlib>
24 #include <cstring>
25 #include <functional>
26
27 #include <libxml/tree.h>
28 #include <libxml/xmlmemory.h>
29 #include <libxml/parser.h>
30
31 #ifndef XMLSEC_NO_XSLT
32 #include <libxslt/xslt.h>
33 #endif
34
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>
42
43 #include <dpl/assert.h>
44 #include <dpl/log/log.h>
45 #include <dpl/singleton_impl.h>
46
47 #include <vcore/XmlsecAdapter.h>
48
49 #define VCORE_ERRORS_BUFFER_SIZE 1024
50
51 IMPLEMENT_SINGLETON(ValidationCore::XmlSec)
52
53 namespace {
54
55 template <typename Type>
56 struct CustomPtr {
57         Type ptr;
58         std::function<void(Type)> deleter;
59
60         CustomPtr() = delete;
61
62         explicit CustomPtr(Type in, std::function<void(Type)> d)
63                 : ptr(in)
64                 , deleter(d) {}
65
66         ~CustomPtr()
67         {
68                 deleter(ptr);
69         }
70
71         inline Type get(void) const
72         {
73                 return ptr;
74         }
75
76         inline Type operator->() const
77         {
78                 return ptr;
79         }
80
81         inline bool operator!() const
82         {
83                 return (ptr == nullptr) ? true : false;
84         }
85 };
86
87 struct FileWrapper {
88         FileWrapper(void *argFile, bool argReleased)
89                 : file(argFile)
90                 , released(argReleased)
91         {}
92         void *file;
93         bool released;
94 };
95
96 } // anonymous namespace
97
98 namespace ValidationCore {
99
100 static const std::string DIGEST_MD5 = "md5";
101
102 std::string XmlSec::s_prefixPath;
103
104 int XmlSec::fileMatchCallback(const char *filename)
105 {
106         std::string path = s_prefixPath + filename;
107         return xmlFileMatch(path.c_str());
108 }
109
110 void *XmlSec::fileOpenCallback(const char *filename)
111 {
112         std::string path = s_prefixPath + filename;
113         LogDebug("Xmlsec opening : " << path);
114         return new FileWrapper(xmlFileOpen(path.c_str()), false);
115 }
116
117 int XmlSec::fileReadCallback(void *context,
118                                                          char *buffer,
119                                                          int len)
120 {
121         FileWrapper *fw = static_cast<FileWrapper *>(context);
122
123         if (fw->released)
124                 return 0;
125
126         int output = xmlFileRead(fw->file, buffer, len);
127
128         if (output == 0) {
129                 fw->released = true;
130                 xmlFileClose(fw->file);
131         }
132
133         return output;
134 }
135
136 int XmlSec::fileCloseCallback(void *context)
137 {
138         FileWrapper *fw = static_cast<FileWrapper *>(context);
139         int output = 0;
140
141         if (!fw->released)
142                 output = xmlFileClose(fw->file);
143
144         delete fw;
145         return output;
146 }
147
148 void XmlSec::fileExtractPrefix(XmlSecContext &context)
149 {
150         if (!context.workingDirectory.empty()) {
151                 s_prefixPath = context.workingDirectory;
152                 return;
153         }
154
155         s_prefixPath = context.signatureFile;
156         size_t pos = s_prefixPath.rfind('/');
157
158         if (pos == std::string::npos)
159                 s_prefixPath.clear();
160         else
161                 s_prefixPath.erase(pos + 1, std::string::npos);
162 }
163
164 void LogDebugPrint(const char *file,
165                                    int line,
166                                    const char *func,
167                                    const char *errorObject,
168                                    const char *errorSubject,
169                                    int reason,
170                                    const char *msg)
171 {
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);
178                         break;
179                 }
180         }
181
182         // Make full error message.
183         char buff[VCORE_ERRORS_BUFFER_SIZE];
184         snprintf(buff,
185                         sizeof(buff),
186                         "[%s:%d] %s(): obj=%s, subj=%s, error=%d:%s:%s\n",
187                         file,
188                         line,
189                         func,
190                         (errorObject != NULL) ? errorObject : "",
191                         (errorSubject != NULL) ? errorSubject : "",
192                         reason,
193                         (errorMsg != NULL) ? errorMsg : "",
194                         (msg != NULL) ? msg : "");
195
196         if (reason == XMLSEC_ERRORS_MAX_NUMBER)
197                 LogError(buff);
198         else
199                 LogDebug(buff);
200 }
201
202 XmlSec::XmlSec()
203         : m_initialized(false)
204         , m_pList(nullptr)
205 {
206         LIBXML_TEST_VERSION
207         xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
208         xmlSubstituteEntitiesDefault(1);
209 #ifndef XMLSEC_NO_XSLT
210         xmlIndentTreeOutput = 1;
211 #endif
212
213         if (xmlSecInit() < 0)
214                 ThrowMsg(Exception::InternalError, "Xmlsec initialization failed.");
215
216         if (xmlSecCheckVersion() != 1) {
217                 xmlSecShutdown();
218                 ThrowMsg(Exception::InternalError,
219                                  "Loaded xmlsec library version is not compatible.");
220         }
221
222 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
223
224         if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
225                 xmlSecShutdown();
226                 LogError(
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.");
232         }
233
234 #endif
235
236         if (xmlSecCryptoAppInit(nullptr) < 0) {
237                 xmlSecShutdown();
238                 ThrowMsg(Exception::InternalError, "Crypto initialization failed.");
239         }
240
241         if (xmlSecCryptoInit() < 0) {
242                 xmlSecCryptoAppShutdown();
243                 xmlSecShutdown();
244                 ThrowMsg(Exception::InternalError,
245                                  "Xmlsec-crypto initialization failed.");
246         }
247
248         m_initialized = true;
249 }
250
251 XmlSec::~XmlSec()
252 {
253         if (m_initialized)
254                 return;
255
256         xmlSecCryptoShutdown();
257         xmlSecCryptoAppShutdown();
258         xmlSecShutdown();
259 #ifndef XMLSEC_NO_XSLT
260         xsltCleanupGlobals();
261 #endif
262         s_prefixPath.clear();
263         m_initialized = false;
264 }
265
266 void XmlSec::validateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
267 {
268         fileExtractPrefix(context);
269         LogDebug("Prefix path : " << s_prefixPath);
270         xmlSecIOCleanupCallbacks();
271         xmlSecIORegisterCallbacks(
272                 fileMatchCallback,
273                 fileOpenCallback,
274                 fileReadCallback,
275                 fileCloseCallback);
276         CustomPtr<xmlDocPtr> docPtr(xmlParseFile(context.signatureFile.c_str()), xmlFreeDoc);
277
278         if (!docPtr || xmlDocGetRootElement(docPtr.get()) == nullptr)
279                 ThrowMsg(Exception::InvalidFormat,
280                                  "Unable to parse sig xml file: " << context.signatureFile);
281
282         xmlNodePtr node = xmlSecFindNode(
283                                                   xmlDocGetRootElement(docPtr.get()),
284                                                   xmlSecNodeSignature,
285                                                   xmlSecDSigNs);
286
287         if (node == nullptr)
288                 ThrowMsg(Exception::InvalidFormat,
289                                  "Start node not found in " << context.signatureFile);
290
291         CustomPtr<xmlSecDSigCtxPtr> dsigCtx(xmlSecDSigCtxCreate(mngrPtr), xmlSecDSigCtxDestroy);
292
293         if (!dsigCtx)
294                 ThrowMsg(Exception::OutOfMemory, "Failed to create signature context.");
295
296         if (context.allowBrokenChain)
297                 dsigCtx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN;
298
299         if (context.validationTime) {
300                 LogDebug("Setting validation time.");
301                 dsigCtx->keyInfoReadCtx.certsVerificationTime = context.validationTime;
302         }
303
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"))
309                                 continue;
310
311                         if(xmlSecProxyCtxAdd(&(dsigCtx.get()->proxyCtxPtr),
312                                                                  reinterpret_cast<const xmlChar *>(data.c_str())))
313                                 ThrowMsg(Exception::InternalError, "Failed to add proxy data.");
314
315                 }
316         }
317
318         int res;
319
320         switch (m_mode) {
321         case ValidateMode::NORMAL: {
322                 if (context.isProxyMode)
323                         dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_SKIP_PROXY;
324
325                 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
326                 break;
327         }
328
329         case ValidateMode::NO_HASH:
330                 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_IGNORE_REFERENCES;
331                 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
332                 break;
333
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.");
340                 }
341                 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
342                 xmlSecProxyCtxDestroy(dsigCtx.get()->proxyCtxPtr);
343                 break;
344         }
345
346         default:
347                 ThrowMsg(Exception::InternalError, "ValidateMode is invalid");
348         }
349
350         if (res != 0)
351                 ThrowMsg(Exception::InvalidSig, "Signature verify error.");
352
353         if (dsigCtx->keyInfoReadCtx.flags2 & XMLSEC_KEYINFO_ERROR_FLAGS_BROKEN_CHAIN) {
354                 LogWarning("Signature contains broken chain!");
355                 context.errorBrokenChain = true;
356         }
357
358         if (dsigCtx->status != xmlSecDSigStatusSucceeded)
359                 ThrowMsg(Exception::InvalidSig, "Signature status is not succedded.");
360
361         // Set references for reverse reference check by ReferenceValidator
362         if (context.isProxyMode)
363                 for (auto &proxy : context.proxySet)
364                         context.referenceSet.insert(proxy);
365
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));
370
371                 if (!dsigRefCtx || !dsigRefCtx->uri)
372                         continue;
373
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);
379
380                         if (DIGEST_MD5.compare(digest) == 0)
381                                 ThrowMsg(Exception::InvalidFormat,
382                                                  "MD5 digest method used! Please use sha");
383                 }
384
385                 context.referenceSet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
386                 if (context.isProxyMode)
387                         context.proxySet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
388         }
389 }
390
391 void XmlSec::loadDERCertificateMemory(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
392 {
393         std::string derCert;
394
395         try {
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());
400         }
401
402         if (xmlSecCryptoAppKeysMngrCertLoadMemory(
403                                 mngrPtr,
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.");
409 }
410
411 void XmlSec::loadPEMCertificateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
412 {
413         if (xmlSecCryptoAppKeysMngrCertLoad(
414                                 mngrPtr,
415                                 context.certificatePath.c_str(),
416                                 xmlSecKeyDataFormatPem,
417                                 xmlSecKeyDataTypeTrusted) < 0)
418                 ThrowMsg(Exception::InternalError, "Failed to load PEM cert from file.");
419 }
420
421 void XmlSec::validateInternal(XmlSecContext &context)
422 {
423         LogDebug("Start to validate.");
424         Assert(!context.signatureFile.empty());
425         Assert(!!context.certificatePtr || !context.certificatePath.empty());
426         xmlSecErrorsSetCallback(LogDebugPrint);
427
428         if (!m_initialized)
429                 ThrowMsg(Exception::InternalError, "XmlSec is not initialized");
430
431         CustomPtr<xmlSecKeysMngrPtr> mngrPtr(xmlSecKeysMngrCreate(), xmlSecKeysMngrDestroy);
432
433         if (!mngrPtr)
434                 ThrowMsg(Exception::InternalError, "Failed to create keys manager.");
435
436         if (xmlSecCryptoAppDefaultKeysMngrInit(mngrPtr.get()) < 0)
437                 ThrowMsg(Exception::InternalError, "Failed to initialize keys manager.");
438
439         context.referenceSet.clear();
440
441         if (!!context.certificatePtr)
442                 loadDERCertificateMemory(context, mngrPtr.get());
443
444         if (!context.certificatePath.empty())
445                 loadPEMCertificateFile(context, mngrPtr.get());
446
447         validateFile(context, mngrPtr.get());
448 }
449
450 void XmlSec::validate(XmlSecContext &context)
451 {
452         m_mode = ValidateMode::NORMAL;
453         validateInternal(context);
454 }
455
456 void XmlSec::validateNoHash(XmlSecContext &context)
457 {
458         m_mode = ValidateMode::NO_HASH;
459         validateInternal(context);
460 }
461
462 void XmlSec::validatePartialHash(XmlSecContext &context, const std::list<std::string> &targetUri)
463 {
464         m_mode = ValidateMode::PARTIAL_HASH;
465         m_pList = &targetUri;
466         validateInternal(context);
467 }
468
469 } // namespace ValidationCore