Fix checkList bug on partial validate
[platform/core/security/cert-svc.git] / src / vcore / XmlsecAdapter.cpp
1 /*
2  * Copyright (c) 2015 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         int res;
305
306         switch (m_mode) {
307         case ValidateMode::NORMAL: {
308                 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
309                 break;
310         }
311
312         case ValidateMode::NO_HASH:
313                 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_IGNORE_REFERENCES;
314                 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
315                 break;
316
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.");
323                 }
324                 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
325                 xmlSecProxyCtxDestroy(dsigCtx.get()->proxyCtxPtr);
326                 break;
327         }
328
329         default:
330                 ThrowMsg(Exception::InternalError, "ValidateMode is invalid");
331         }
332
333         if (res != 0)
334                 ThrowMsg(Exception::InvalidSig, "Signature verify error.");
335
336         if (dsigCtx->keyInfoReadCtx.flags2 & XMLSEC_KEYINFO_ERROR_FLAGS_BROKEN_CHAIN) {
337                 LogWarning("Signature contains broken chain!");
338                 context.errorBrokenChain = true;
339         }
340
341         if (dsigCtx->status != xmlSecDSigStatusSucceeded)
342                 ThrowMsg(Exception::InvalidSig, "Signature status is not succedded.");
343
344         xmlSecSize refSize = xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences));
345
346         for (xmlSecSize i = 0; i < refSize; ++i) {
347                 xmlSecDSigReferenceCtxPtr dsigRefCtx = static_cast<xmlSecDSigReferenceCtxPtr>(
348                                 xmlSecPtrListGetItem(&(dsigCtx->signedInfoReferences), i));
349
350                 if (!dsigRefCtx || !dsigRefCtx->uri)
351                         continue;
352
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);
358
359                         if (DIGEST_MD5.compare(digest) == 0)
360                                 ThrowMsg(Exception::InvalidFormat,
361                                                  "MD5 digest method used! Please use sha");
362                 }
363
364                 context.referenceSet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
365         }
366 }
367
368 void XmlSec::loadDERCertificateMemory(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
369 {
370         std::string derCert;
371
372         try {
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());
377         }
378
379         if (xmlSecCryptoAppKeysMngrCertLoadMemory(
380                                 mngrPtr,
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.");
386 }
387
388 void XmlSec::loadPEMCertificateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
389 {
390         if (xmlSecCryptoAppKeysMngrCertLoad(
391                                 mngrPtr,
392                                 context.certificatePath.c_str(),
393                                 xmlSecKeyDataFormatPem,
394                                 xmlSecKeyDataTypeTrusted) < 0)
395                 ThrowMsg(Exception::InternalError, "Failed to load PEM cert from file.");
396 }
397
398 void XmlSec::validateInternal(XmlSecContext &context)
399 {
400         LogDebug("Start to validate.");
401         Assert(!context.signatureFile.empty());
402         Assert(!!context.certificatePtr || !context.certificatePath.empty());
403         xmlSecErrorsSetCallback(LogDebugPrint);
404
405         if (!m_initialized)
406                 ThrowMsg(Exception::InternalError, "XmlSec is not initialized");
407
408         CustomPtr<xmlSecKeysMngrPtr> mngrPtr(xmlSecKeysMngrCreate(), xmlSecKeysMngrDestroy);
409
410         if (!mngrPtr)
411                 ThrowMsg(Exception::InternalError, "Failed to create keys manager.");
412
413         if (xmlSecCryptoAppDefaultKeysMngrInit(mngrPtr.get()) < 0)
414                 ThrowMsg(Exception::InternalError, "Failed to initialize keys manager.");
415
416         context.referenceSet.clear();
417
418         if (!!context.certificatePtr)
419                 loadDERCertificateMemory(context, mngrPtr.get());
420
421         if (!context.certificatePath.empty())
422                 loadPEMCertificateFile(context, mngrPtr.get());
423
424         validateFile(context, mngrPtr.get());
425 }
426
427 void XmlSec::validate(XmlSecContext &context)
428 {
429         m_mode = ValidateMode::NORMAL;
430         validateInternal(context);
431 }
432
433 void XmlSec::validateNoHash(XmlSecContext &context)
434 {
435         m_mode = ValidateMode::NO_HASH;
436         validateInternal(context);
437 }
438
439 void XmlSec::validatePartialHash(XmlSecContext &context, const std::list<std::string> &targetUri)
440 {
441         m_mode = ValidateMode::PARTIAL_HASH;
442         m_pList = &targetUri;
443         validateInternal(context);
444 }
445
446 } // namespace ValidationCore