Fix svace defects
[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 #include <memory>
27
28 #include <libxml/tree.h>
29 #include <libxml/xmlmemory.h>
30 #include <libxml/parser.h>
31
32 #ifndef XMLSEC_NO_XSLT
33 #include <libxslt/xslt.h>
34 #endif
35
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>
43
44 #include <dpl/assert.h>
45 #include <dpl/log/log.h>
46
47 #include <vcore/XmlsecAdapter.h>
48
49 #define VCORE_ERRORS_BUFFER_SIZE 1024
50
51 namespace {
52
53 struct FileWrapper {
54         FileWrapper(void *argFile, bool argReleased)
55                 : file(argFile)
56                 , released(argReleased)
57         {}
58         void *file;
59         bool released;
60 };
61
62 } // anonymous namespace
63
64 namespace ValidationCore {
65
66 static const std::string DIGEST_MD5 = "md5";
67
68 std::string XmlSec::s_prefixPath;
69
70 int XmlSec::fileMatchCallback(const char *filename)
71 {
72         std::string path = s_prefixPath + filename;
73         return xmlFileMatch(path.c_str());
74 }
75
76 void *XmlSec::fileOpenCallback(const char *filename)
77 {
78         std::string path = s_prefixPath + filename;
79         LogDebug("Xmlsec opening : " << path);
80         return new FileWrapper(xmlFileOpen(path.c_str()), false);
81 }
82
83 int XmlSec::fileReadCallback(void *context,
84                                                          char *buffer,
85                                                          int len)
86 {
87         FileWrapper *fw = static_cast<FileWrapper *>(context);
88
89         if (fw->released)
90                 return 0;
91
92         int output = xmlFileRead(fw->file, buffer, len);
93
94         if (output == 0) {
95                 fw->released = true;
96                 xmlFileClose(fw->file);
97         }
98
99         return output;
100 }
101
102 int XmlSec::fileCloseCallback(void *context)
103 {
104         FileWrapper *fw = static_cast<FileWrapper *>(context);
105         int output = 0;
106
107         if (!fw->released)
108                 output = xmlFileClose(fw->file);
109
110         delete fw;
111         return output;
112 }
113
114 void XmlSec::fileExtractPrefix(XmlSecContext &context)
115 {
116         if (!context.workingDirectory.empty()) {
117                 s_prefixPath = context.workingDirectory;
118                 return;
119         }
120
121         s_prefixPath = context.signatureFile;
122         size_t pos = s_prefixPath.rfind('/');
123
124         if (pos == std::string::npos)
125                 s_prefixPath.clear();
126         else
127                 s_prefixPath.erase(pos + 1, std::string::npos);
128 }
129
130 void LogDebugPrint(const char *file,
131                                    int line,
132                                    const char *func,
133                                    const char *errorObject,
134                                    const char *errorSubject,
135                                    int reason,
136                                    const char *msg)
137 {
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);
144                         break;
145                 }
146         }
147
148         // Make full error message.
149         char buff[VCORE_ERRORS_BUFFER_SIZE];
150         snprintf(buff,
151                         sizeof(buff),
152                         "[%s:%d] %s(): obj=%s, subj=%s, error=%d:%s:%s\n",
153                         file,
154                         line,
155                         func,
156                         (errorObject != NULL) ? errorObject : "",
157                         (errorSubject != NULL) ? errorSubject : "",
158                         reason,
159                         (errorMsg != NULL) ? errorMsg : "",
160                         (msg != NULL) ? msg : "");
161
162         if (reason == XMLSEC_ERRORS_MAX_NUMBER)
163                 LogError(buff);
164         else
165                 LogDebug(buff);
166 }
167
168 XmlSec::XmlSec() :
169         m_mode(ValidateMode::NORMAL),
170         m_pList(nullptr)
171 {
172         LIBXML_TEST_VERSION
173         xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
174         xmlSubstituteEntitiesDefault(1);
175 #ifndef XMLSEC_NO_XSLT
176         xmlIndentTreeOutput = 1;
177 #endif
178
179         if (xmlSecInit() < 0)
180                 ThrowMsg(Exception::InternalError, "Xmlsec initialization failed.");
181
182         if (xmlSecCheckVersion() != 1) {
183                 xmlSecShutdown();
184                 ThrowMsg(Exception::InternalError,
185                                  "Loaded xmlsec library version is not compatible.");
186         }
187
188 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
189
190         if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
191                 xmlSecShutdown();
192                 LogError(
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.");
198         }
199
200 #endif
201
202         if (xmlSecCryptoAppInit(nullptr) < 0) {
203                 xmlSecShutdown();
204                 ThrowMsg(Exception::InternalError, "Crypto initialization failed.");
205         }
206
207         if (xmlSecCryptoInit() < 0) {
208                 xmlSecCryptoAppShutdown();
209                 xmlSecShutdown();
210                 ThrowMsg(Exception::InternalError,
211                                  "Xmlsec-crypto initialization failed.");
212         }
213 }
214
215 XmlSec::~XmlSec()
216 {
217         xmlSecCryptoShutdown();
218         xmlSecCryptoAppShutdown();
219         xmlSecShutdown();
220 #ifndef XMLSEC_NO_XSLT
221         xsltCleanupGlobals();
222 #endif
223         s_prefixPath.clear();
224 }
225
226 void XmlSec::validateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
227 {
228         fileExtractPrefix(context);
229         LogDebug("Prefix path : " << s_prefixPath);
230         xmlSecIOCleanupCallbacks();
231         xmlSecIORegisterCallbacks(
232                 fileMatchCallback,
233                 fileOpenCallback,
234                 fileReadCallback,
235                 fileCloseCallback);
236         std::unique_ptr<xmlDoc, std::function<void(xmlDocPtr)>> docPtr(
237                 xmlParseFile(context.signatureFile.c_str()), xmlFreeDoc);
238
239         if (!docPtr || xmlDocGetRootElement(docPtr.get()) == nullptr)
240                 ThrowMsg(Exception::InvalidFormat,
241                                  "Unable to parse sig xml file: " << context.signatureFile);
242
243         xmlNodePtr node = xmlSecFindNode(
244                                                   xmlDocGetRootElement(docPtr.get()),
245                                                   xmlSecNodeSignature,
246                                                   xmlSecDSigNs);
247
248         if (node == nullptr)
249                 ThrowMsg(Exception::InvalidFormat,
250                                  "Start node not found in " << context.signatureFile);
251
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);
258         });
259
260         if (!dsigCtx)
261                 ThrowMsg(Exception::OutOfMemory, "Failed to create signature context.");
262
263         if (context.allowBrokenChain)
264                 dsigCtx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN;
265
266         if (context.validationTime) {
267                 LogDebug("Setting validation time.");
268                 dsigCtx->keyInfoReadCtx.certsVerificationTime = context.validationTime;
269         }
270
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"))
276                                 continue;
277
278                         if(xmlSecProxyCtxAdd(&(dsigCtx.get()->skipReferences),
279                                                                  reinterpret_cast<const xmlChar *>(data.c_str())))
280                                 ThrowMsg(Exception::InternalError, "Failed to add proxy data.");
281                         else
282                                 LogDebug("Add [" << data << "] to proxy.");
283
284                 }
285         }
286
287         int res;
288
289         switch (m_mode) {
290         case ValidateMode::NORMAL: {
291                 if (context.isProxyMode)
292                         dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_SKIP_PROXY;
293
294                 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
295                 break;
296         }
297
298         case ValidateMode::NO_HASH:
299                 dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_IGNORE_REFERENCES;
300                 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
301                 break;
302
303         case ValidateMode::PARTIAL_HASH: {
304                 if (context.isProxyMode)
305                         dsigCtx.get()->flags |= XMLSEC_DSIG_FLAGS_SKIP_PROXY;
306
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.");
312                 }
313                 res = xmlSecDSigCtxVerify(dsigCtx.get(), node);
314                 break;
315         }
316
317         default:
318                 ThrowMsg(Exception::InternalError, "ValidateMode is invalid");
319         }
320
321         if (res != 0)
322                 ThrowMsg(Exception::InvalidSig, "Signature verify error.");
323
324         if (dsigCtx->keyInfoReadCtx.flags2 & XMLSEC_KEYINFO_ERROR_FLAGS_BROKEN_CHAIN) {
325                 LogWarning("Signature contains broken chain!");
326                 context.errorBrokenChain = true;
327         }
328
329         if (dsigCtx->status != xmlSecDSigStatusSucceeded)
330                 ThrowMsg(Exception::InvalidSig, "Signature status is not succedded.");
331
332         // Set references for reverse reference check by ReferenceValidator
333         if (context.isProxyMode)
334                 for (auto &proxy : context.proxySet)
335                         context.referenceSet.insert(proxy);
336
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));
341
342                 if (!dsigRefCtx || !dsigRefCtx->uri)
343                         continue;
344
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);
350
351                         if (DIGEST_MD5.compare(digest) == 0)
352                                 ThrowMsg(Exception::InvalidFormat,
353                                                  "MD5 digest method used! Please use sha");
354                 }
355
356                 context.referenceSet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
357                 if (context.isProxyMode)
358                         context.proxySet.emplace(reinterpret_cast<char *>(dsigRefCtx->uri));
359         }
360 }
361
362 void XmlSec::loadDERCertificateMemory(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
363 {
364         std::string derCert;
365
366         try {
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());
371         }
372
373         if (xmlSecCryptoAppKeysMngrCertLoadMemory(
374                                 mngrPtr,
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.");
380 }
381
382 void XmlSec::loadPEMCertificateFile(XmlSecContext &context, xmlSecKeysMngrPtr mngrPtr)
383 {
384         if (xmlSecCryptoAppKeysMngrCertLoad(
385                                 mngrPtr,
386                                 context.certificatePath.c_str(),
387                                 xmlSecKeyDataFormatPem,
388                                 xmlSecKeyDataTypeTrusted) < 0)
389                 ThrowMsg(Exception::InternalError, "Failed to load PEM cert from file.");
390 }
391
392 void XmlSec::validateInternal(XmlSecContext &context)
393 {
394         LogDebug("Start to validate.");
395         Assert(!context.signatureFile.empty());
396         Assert(!!context.certificatePtr || !context.certificatePath.empty());
397         xmlSecErrorsSetCallback(LogDebugPrint);
398
399         std::unique_ptr<xmlSecKeysMngr, std::function<void(xmlSecKeysMngrPtr)>>
400                 mngrPtr(xmlSecKeysMngrCreate(), xmlSecKeysMngrDestroy);
401
402         if (!mngrPtr)
403                 ThrowMsg(Exception::InternalError, "Failed to create keys manager.");
404
405         if (xmlSecCryptoAppDefaultKeysMngrInit(mngrPtr.get()) < 0)
406                 ThrowMsg(Exception::InternalError, "Failed to initialize keys manager.");
407
408         context.referenceSet.clear();
409
410         if (!!context.certificatePtr)
411                 loadDERCertificateMemory(context, mngrPtr.get());
412
413         if (!context.certificatePath.empty())
414                 loadPEMCertificateFile(context, mngrPtr.get());
415
416         validateFile(context, mngrPtr.get());
417 }
418
419 void XmlSec::validate(XmlSecContext &context)
420 {
421         m_mode = ValidateMode::NORMAL;
422         validateInternal(context);
423 }
424
425 void XmlSec::validateNoHash(XmlSecContext &context)
426 {
427         m_mode = ValidateMode::NO_HASH;
428         validateInternal(context);
429 }
430
431 void XmlSec::validatePartialHash(XmlSecContext &context, const std::list<std::string> &targetUri)
432 {
433         m_mode = ValidateMode::PARTIAL_HASH;
434         m_pList = &targetUri;
435         validateInternal(context);
436 }
437
438 } // namespace ValidationCore