1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <config_gpgme.h>
21 
22 #include <com/sun/star/embed/ElementModes.hpp>
23 #include <com/sun/star/embed/XEncryptionProtectedStorage.hpp>
24 #include <com/sun/star/embed/XStorage.hpp>
25 #include <com/sun/star/embed/XTransactedObject.hpp>
26 #include <com/sun/star/embed/StorageFactory.hpp>
27 #include <com/sun/star/embed/FileSystemStorageFactory.hpp>
28 #include <com/sun/star/io/IOException.hpp>
29 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
30 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
31 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/beans/PropertyValue.hpp>
34 #include <com/sun/star/beans/NamedValue.hpp>
35 #include <com/sun/star/beans/IllegalTypeException.hpp>
36 #include <com/sun/star/xml/crypto/NSSInitializer.hpp>
37 #include <com/sun/star/xml/crypto/XDigestContext.hpp>
38 #include <com/sun/star/xml/crypto/DigestID.hpp>
39 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
40 #include <com/sun/star/security/XCertificate.hpp>
41 
42 #include <vector>
43 
44 #include <rtl/digest.h>
45 #include <rtl/random.h>
46 #include <osl/diagnose.h>
47 #include <sal/log.hxx>
48 
49 #include <ucbhelper/content.hxx>
50 
51 #include <comphelper/fileformat.h>
52 #include <comphelper/hash.hxx>
53 #include <comphelper/processfactory.hxx>
54 #include <comphelper/documentconstants.hxx>
55 #include <comphelper/storagehelper.hxx>
56 #include <comphelper/sequence.hxx>
57 #include <cppuhelper/exc_hlp.hxx>
58 
59 #if HAVE_FEATURE_GPGME
60 # include <context.h>
61 # include <encryptionresult.h>
62 # include <key.h>
63 # include <data.h>
64 #endif
65 
66 using namespace ::com::sun::star;
67 
68 namespace comphelper {
69 
70 
GetStorageFactory(const uno::Reference<uno::XComponentContext> & rxContext)71 uno::Reference< lang::XSingleServiceFactory > OStorageHelper::GetStorageFactory(
72                             const uno::Reference< uno::XComponentContext >& rxContext )
73 {
74     uno::Reference< uno::XComponentContext> xContext = rxContext.is() ? rxContext : ::comphelper::getProcessComponentContext();
75 
76     return embed::StorageFactory::create( xContext );
77 }
78 
79 
GetFileSystemStorageFactory(const uno::Reference<uno::XComponentContext> & rxContext)80 uno::Reference< lang::XSingleServiceFactory > OStorageHelper::GetFileSystemStorageFactory(
81                             const uno::Reference< uno::XComponentContext >& rxContext )
82 {
83     return embed::FileSystemStorageFactory::create(rxContext);
84 }
85 
86 
GetTemporaryStorage(const uno::Reference<uno::XComponentContext> & rxContext)87 uno::Reference< embed::XStorage > OStorageHelper::GetTemporaryStorage(
88             const uno::Reference< uno::XComponentContext >& rxContext )
89 {
90     uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstance(),
91                                                     uno::UNO_QUERY_THROW );
92     return xTempStorage;
93 }
94 
95 
GetStorageFromURL(const OUString & aURL,sal_Int32 nStorageMode,const uno::Reference<uno::XComponentContext> & rxContext)96 uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromURL(
97             const OUString& aURL,
98             sal_Int32 nStorageMode,
99             const uno::Reference< uno::XComponentContext >& rxContext )
100 {
101     uno::Sequence< uno::Any > aArgs( 2 );
102     aArgs[0] <<= aURL;
103     aArgs[1] <<= nStorageMode;
104 
105     uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
106                                                     uno::UNO_QUERY_THROW );
107     return xTempStorage;
108 }
109 
110 
GetStorageFromURL2(const OUString & aURL,sal_Int32 nStorageMode,const uno::Reference<uno::XComponentContext> & rxContext)111 uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromURL2(
112             const OUString& aURL,
113             sal_Int32 nStorageMode,
114             const uno::Reference< uno::XComponentContext >& rxContext )
115 {
116     uno::Sequence< uno::Any > aArgs( 2 );
117     aArgs[0] <<= aURL;
118     aArgs[1] <<= nStorageMode;
119 
120     uno::Reference< lang::XSingleServiceFactory > xFact;
121     css::uno::Any anyEx;
122     try {
123         ::ucbhelper::Content aCntnt( aURL,
124             uno::Reference< css::ucb::XCommandEnvironment > (),
125             getProcessComponentContext() );
126         if (aCntnt.isDocument()) {
127             xFact = GetStorageFactory( rxContext );
128         } else {
129             xFact = GetFileSystemStorageFactory( rxContext );
130         }
131     } catch (uno::Exception &)
132     {
133         anyEx = cppu::getCaughtException();
134     }
135 
136     if (!xFact.is())
137     {
138         if (anyEx.hasValue())
139             throw css::lang::WrappedTargetRuntimeException( "", nullptr, anyEx );
140         else
141             throw uno::RuntimeException();
142     }
143 
144     uno::Reference< embed::XStorage > xTempStorage(
145         xFact->createInstanceWithArguments( aArgs ), uno::UNO_QUERY_THROW );
146     return xTempStorage;
147 }
148 
149 
GetStorageFromInputStream(const uno::Reference<io::XInputStream> & xStream,const uno::Reference<uno::XComponentContext> & rxContext)150 uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromInputStream(
151             const uno::Reference < io::XInputStream >& xStream,
152             const uno::Reference< uno::XComponentContext >& rxContext )
153 {
154     uno::Sequence< uno::Any > aArgs( 2 );
155     aArgs[0] <<= xStream;
156     aArgs[1] <<= embed::ElementModes::READ;
157 
158     uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
159                                                     uno::UNO_QUERY_THROW );
160     return xTempStorage;
161 }
162 
163 
GetStorageFromStream(const uno::Reference<io::XStream> & xStream,sal_Int32 nStorageMode,const uno::Reference<uno::XComponentContext> & rxContext)164 uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromStream(
165             const uno::Reference < io::XStream >& xStream,
166             sal_Int32 nStorageMode,
167             const uno::Reference< uno::XComponentContext >& rxContext )
168 {
169     uno::Sequence< uno::Any > aArgs( 2 );
170     aArgs[0] <<= xStream;
171     aArgs[1] <<= nStorageMode;
172 
173     uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
174                                                     uno::UNO_QUERY_THROW );
175     return xTempStorage;
176 }
177 
178 
CopyInputToOutput(const uno::Reference<io::XInputStream> & xInput,const uno::Reference<io::XOutputStream> & xOutput)179 void OStorageHelper::CopyInputToOutput(
180             const uno::Reference< io::XInputStream >& xInput,
181             const uno::Reference< io::XOutputStream >& xOutput )
182 {
183     static const sal_Int32 nConstBufferSize = 32000;
184 
185     sal_Int32 nRead;
186     uno::Sequence < sal_Int8 > aSequence ( nConstBufferSize );
187 
188     do
189     {
190         nRead = xInput->readBytes ( aSequence, nConstBufferSize );
191         if ( nRead < nConstBufferSize )
192         {
193             uno::Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead );
194             xOutput->writeBytes ( aTempBuf );
195         }
196         else
197             xOutput->writeBytes ( aSequence );
198     }
199     while ( nRead == nConstBufferSize );
200 }
201 
202 
GetInputStreamFromURL(const OUString & aURL,const uno::Reference<uno::XComponentContext> & context)203 uno::Reference< io::XInputStream > OStorageHelper::GetInputStreamFromURL(
204             const OUString& aURL,
205             const uno::Reference< uno::XComponentContext >& context )
206 {
207     uno::Reference< io::XInputStream > xInputStream = ucb::SimpleFileAccess::create(context)->openFileRead( aURL );
208     if ( !xInputStream.is() )
209         throw uno::RuntimeException();
210 
211     return xInputStream;
212 }
213 
214 
SetCommonStorageEncryptionData(const uno::Reference<embed::XStorage> & xStorage,const uno::Sequence<beans::NamedValue> & aEncryptionData)215 void OStorageHelper::SetCommonStorageEncryptionData(
216             const uno::Reference< embed::XStorage >& xStorage,
217             const uno::Sequence< beans::NamedValue >& aEncryptionData )
218 {
219     uno::Reference< embed::XEncryptionProtectedStorage > xEncrSet( xStorage, uno::UNO_QUERY );
220     if ( !xEncrSet.is() )
221         throw io::IOException("no XEncryptionProtectedStorage"); // TODO
222 
223     if ( aEncryptionData.getLength() == 2 &&
224          aEncryptionData[0].Name == "GpgInfos" &&
225          aEncryptionData[1].Name == "EncryptionKey" )
226     {
227         xEncrSet->setGpgProperties(
228             aEncryptionData[0].Value.get< uno::Sequence< uno::Sequence< beans::NamedValue > > >() );
229         xEncrSet->setEncryptionData(
230             aEncryptionData[1].Value.get< uno::Sequence< beans::NamedValue > >() );
231     }
232     else
233         xEncrSet->setEncryptionData( aEncryptionData );
234 }
235 
236 
GetXStorageFormat(const uno::Reference<embed::XStorage> & xStorage)237 sal_Int32 OStorageHelper::GetXStorageFormat(
238             const uno::Reference< embed::XStorage >& xStorage )
239 {
240     uno::Reference< beans::XPropertySet > xStorProps( xStorage, uno::UNO_QUERY_THROW );
241 
242     OUString aMediaType;
243     xStorProps->getPropertyValue("MediaType") >>= aMediaType;
244 
245     sal_Int32 nResult = 0;
246 
247     // TODO/LATER: the filter configuration could be used to detect it later, or better a special service
248     if (
249         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER_ASCII       ) ||
250         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER_WEB_ASCII   ) ||
251         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER_GLOBAL_ASCII) ||
252         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_DRAW_ASCII         ) ||
253         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_IMPRESS_ASCII      ) ||
254         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_CALC_ASCII         ) ||
255         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_CHART_ASCII        ) ||
256         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_MATH_ASCII         )
257        )
258     {
259         nResult = SOFFICE_FILEFORMAT_60;
260     }
261     else if (
262         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_ASCII        ) ||
263         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_WEB_ASCII    ) ||
264         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_ASCII ) ||
265         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_ASCII     ) ||
266         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII) ||
267         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_ASCII ) ||
268         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_CHART_ASCII       ) ||
269         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_ASCII     ) ||
270         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII    ) ||
271         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_REPORT_ASCII    ) ||
272         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_REPORT_CHART_ASCII    ) ||
273         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_TEMPLATE_ASCII        ) ||
274         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_TEMPLATE_ASCII ) ||
275         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_TEMPLATE_ASCII     ) ||
276         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE_ASCII) ||
277         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_TEMPLATE_ASCII ) ||
278         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_CHART_TEMPLATE_ASCII       ) ||
279         aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_TEMPLATE_ASCII     )
280        )
281     {
282         nResult = SOFFICE_FILEFORMAT_8;
283     }
284     else
285     {
286         // the mediatype is not known
287         OUString aMsg = __func__
288                       + OUString::Concat(u":")
289                       + OUString::number(__LINE__)
290                       + ": unknown media type '"
291                       + aMediaType
292                       + "'";
293         throw beans::IllegalTypeException(aMsg);
294     }
295 
296     return nResult;
297 }
298 
299 
GetStorageOfFormatFromURL(const OUString & aFormat,const OUString & aURL,sal_Int32 nStorageMode,const uno::Reference<uno::XComponentContext> & rxContext)300 uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromURL(
301             const OUString& aFormat,
302             const OUString& aURL,
303             sal_Int32 nStorageMode,
304             const uno::Reference< uno::XComponentContext >& rxContext )
305 {
306     uno::Sequence< beans::PropertyValue > aProps( 1 );
307     aProps[0].Name = "StorageFormat";
308     aProps[0].Value <<= aFormat;
309 
310     uno::Sequence< uno::Any > aArgs( 3 );
311     aArgs[0] <<= aURL;
312     aArgs[1] <<= nStorageMode;
313     aArgs[2] <<= aProps;
314 
315     uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
316                                                     uno::UNO_QUERY_THROW );
317     return xTempStorage;
318 }
319 
320 
GetStorageOfFormatFromInputStream(const OUString & aFormat,const uno::Reference<io::XInputStream> & xStream,const uno::Reference<uno::XComponentContext> & rxContext,bool bRepairStorage)321 uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromInputStream(
322             const OUString& aFormat,
323             const uno::Reference < io::XInputStream >& xStream,
324             const uno::Reference< uno::XComponentContext >& rxContext,
325             bool bRepairStorage )
326 {
327     uno::Sequence< beans::PropertyValue > aProps( 1 );
328     sal_Int32 nPos = 0;
329     aProps[nPos].Name = "StorageFormat";
330     aProps[nPos].Value <<= aFormat;
331     ++nPos;
332     if ( bRepairStorage )
333     {
334         aProps.realloc(nPos+1);
335         aProps[nPos].Name = "RepairPackage";
336         aProps[nPos].Value <<= bRepairStorage;
337         ++nPos;
338     }
339 
340     uno::Sequence< uno::Any > aArgs( 3 );
341     aArgs[0] <<= xStream;
342     aArgs[1] <<= embed::ElementModes::READ;
343     aArgs[2] <<= aProps;
344 
345     uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
346                                                     uno::UNO_QUERY_THROW );
347     return xTempStorage;
348 }
349 
350 
GetStorageOfFormatFromStream(const OUString & aFormat,const uno::Reference<io::XStream> & xStream,sal_Int32 nStorageMode,const uno::Reference<uno::XComponentContext> & rxContext,bool bRepairStorage)351 uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromStream(
352             const OUString& aFormat,
353             const uno::Reference < io::XStream >& xStream,
354             sal_Int32 nStorageMode,
355             const uno::Reference< uno::XComponentContext >& rxContext,
356             bool bRepairStorage )
357 {
358     uno::Sequence< beans::PropertyValue > aProps( 1 );
359     sal_Int32 nPos = 0;
360     aProps[nPos].Name = "StorageFormat";
361     aProps[nPos].Value <<= aFormat;
362     ++nPos;
363     if ( bRepairStorage )
364     {
365         aProps.realloc(nPos+1);
366         aProps[nPos].Name = "RepairPackage";
367         aProps[nPos].Value <<= bRepairStorage;
368         ++nPos;
369     }
370 
371     uno::Sequence< uno::Any > aArgs( 3 );
372     aArgs[0] <<= xStream;
373     aArgs[1] <<= nStorageMode;
374     aArgs[2] <<= aProps;
375 
376     uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
377                                                     uno::UNO_QUERY_THROW );
378     return xTempStorage;
379 }
380 
381 
CreatePackageEncryptionData(std::u16string_view aPassword)382 uno::Sequence< beans::NamedValue > OStorageHelper::CreatePackageEncryptionData( std::u16string_view aPassword )
383 {
384     // TODO/LATER: Should not the method be part of DocPasswordHelper?
385     uno::Sequence< beans::NamedValue > aEncryptionData;
386     if ( !aPassword.empty() )
387     {
388         sal_Int32 nSha1Ind = 0;
389         // generate SHA256 start key
390         try
391         {
392             uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
393 
394             uno::Reference< css::xml::crypto::XNSSInitializer > xDigestContextSupplier = css::xml::crypto::NSSInitializer::create(xContext);
395             uno::Reference< css::xml::crypto::XDigestContext > xDigestContext( xDigestContextSupplier->getDigestContext( css::xml::crypto::DigestID::SHA256, uno::Sequence< beans::NamedValue >() ), uno::UNO_SET_THROW );
396 
397             OString aUTF8Password( OUStringToOString( aPassword, RTL_TEXTENCODING_UTF8 ) );
398             xDigestContext->updateDigest( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( aUTF8Password.getStr() ), aUTF8Password.getLength() ) );
399             uno::Sequence< sal_Int8 > aDigest = xDigestContext->finalizeDigestAndDispose();
400 
401             aEncryptionData.realloc( ++nSha1Ind );
402             aEncryptionData[0].Name = PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
403             aEncryptionData[0].Value <<= aDigest;
404         }
405         catch ( uno::Exception& )
406         {
407             OSL_ENSURE( false, "Can not create SHA256 digest!" );
408         }
409 
410         // MS_1252 encoding was used for SO60 document format password encoding,
411         // this encoding supports only a minor subset of nonascii characters,
412         // but for compatibility reasons it has to be used for old document formats
413         aEncryptionData.realloc( nSha1Ind + 3 );
414         // these are StarOffice not-quite-SHA1
415         aEncryptionData[nSha1Ind].Name = PACKAGE_ENCRYPTIONDATA_SHA1UTF8;
416         aEncryptionData[nSha1Ind + 1].Name = PACKAGE_ENCRYPTIONDATA_SHA1MS1252;
417 
418         rtl_TextEncoding const pEncoding[2] = { RTL_TEXTENCODING_UTF8, RTL_TEXTENCODING_MS_1252 };
419 
420         for ( sal_Int32 nInd = 0; nInd < 2; nInd++ )
421         {
422             OString aByteStrPass = OUStringToOString( aPassword, pEncoding[nInd] );
423 
424             sal_uInt8 pBuffer[RTL_DIGEST_LENGTH_SHA1];
425             rtlDigestError nError = rtl_digest_SHA1( aByteStrPass.getStr(),
426                                                     aByteStrPass.getLength(),
427                                                     pBuffer,
428                                                     RTL_DIGEST_LENGTH_SHA1 );
429 
430             if ( nError != rtl_Digest_E_None )
431             {
432                 aEncryptionData.realloc( nSha1Ind );
433                 return aEncryptionData;
434             }
435 
436             // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
437             aEncryptionData[nSha1Ind+nInd].Value <<= uno::Sequence< sal_Int8 >( reinterpret_cast<sal_Int8*>(pBuffer), RTL_DIGEST_LENGTH_SHA1 );
438         }
439 
440         // actual SHA1
441         aEncryptionData[nSha1Ind + 2].Name = PACKAGE_ENCRYPTIONDATA_SHA1CORRECT;
442         OString aByteStrPass = OUStringToOString(aPassword, RTL_TEXTENCODING_UTF8);
443         std::vector<unsigned char> const sha1(::comphelper::Hash::calculateHash(
444                 reinterpret_cast<unsigned char const*>(aByteStrPass.getStr()), aByteStrPass.getLength(),
445                 ::comphelper::HashType::SHA1));
446         aEncryptionData[nSha1Ind + 2].Value <<= uno::Sequence<sal_Int8>(
447                 reinterpret_cast<sal_Int8 const*>(sha1.data()), sha1.size());
448     }
449 
450     return aEncryptionData;
451 }
452 
CreateGpgPackageEncryptionData()453 uno::Sequence< beans::NamedValue > OStorageHelper::CreateGpgPackageEncryptionData()
454 {
455 #if HAVE_FEATURE_GPGME
456     // generate session key
457     // --------------------
458 
459     rtlRandomPool aRandomPool = rtl_random_createPool();
460 
461     // get 32 random chars out of it
462     uno::Sequence < sal_Int8 > aVector(32);
463     rtl_random_getBytes( aRandomPool, aVector.getArray(), aVector.getLength() );
464 
465     rtl_random_destroyPool(aRandomPool);
466 
467     uno::Sequence< beans::NamedValue > aContainer(2);
468     std::vector< uno::Sequence< beans::NamedValue > > aGpgEncryptions;
469     uno::Sequence< beans::NamedValue > aGpgEncryptionEntry(3);
470     uno::Sequence< beans::NamedValue > aEncryptionData(1);
471 
472     uno::Reference< security::XDocumentDigitalSignatures > xSigner(
473         // here none of the version-dependent methods are called
474         security::DocumentDigitalSignatures::createDefault(
475             comphelper::getProcessComponentContext()));
476 
477     // fire up certificate chooser dialog - user can multi-select!
478     const uno::Sequence< uno::Reference< security::XCertificate > > xSignCertificates=
479         xSigner->chooseEncryptionCertificate();
480 
481     if (!xSignCertificates.hasElements())
482         return uno::Sequence< beans::NamedValue >(); // user cancelled
483 
484     // generate one encrypted key entry for each recipient
485     // ---------------------------------------------------
486 
487     std::unique_ptr<GpgME::Context> ctx;
488     GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP);
489     if (err)
490         throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
491 
492     ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) );
493     if (ctx == nullptr)
494         throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
495     ctx->setArmor(false);
496 
497     for (const auto & cert : xSignCertificates)
498     {
499         uno::Sequence < sal_Int8 > aKeyID;
500         if (cert.is())
501             aKeyID = cert->getSHA1Thumbprint();
502 
503         std::vector<GpgME::Key> keys;
504         keys.push_back(
505             ctx->key(
506                 reinterpret_cast<const char*>(aKeyID.getConstArray()),
507                 err, false));
508 
509         // ctx is setup now, let's encrypt the lot!
510         GpgME::Data plain(
511             reinterpret_cast<const char*>(aVector.getConstArray()),
512             size_t(aVector.getLength()), false);
513         GpgME::Data cipher;
514 
515         GpgME::EncryptionResult crypt_res = ctx->encrypt(
516             keys, plain,
517             cipher, GpgME::Context::NoCompress);
518 
519         off_t result = cipher.seek(0,SEEK_SET);
520         (void) result;
521         assert(result == 0);
522         int len=0, curr=0; char buf;
523         while( (curr=cipher.read(&buf, 1)) )
524             len += curr;
525 
526         if(crypt_res.error() || !len)
527             throw lang::IllegalArgumentException(
528                 "Not a suitable key, or failed to encrypt.",
529                 css::uno::Reference<css::uno::XInterface>(), -1);
530 
531         uno::Sequence < sal_Int8 > aCipherValue(len);
532         result = cipher.seek(0,SEEK_SET);
533         assert(result == 0);
534         if( cipher.read(aCipherValue.getArray(), len) != len )
535             throw uno::RuntimeException("The GpgME library failed to read the encrypted value.");
536 
537         SAL_INFO("comphelper.crypto", "Generated gpg crypto of length: " << len);
538 
539         aGpgEncryptionEntry[0].Name = "KeyId";
540         aGpgEncryptionEntry[0].Value <<= aKeyID;
541         aGpgEncryptionEntry[1].Name = "KeyPacket";
542         aGpgEncryptionEntry[1].Value <<= aKeyID;
543         aGpgEncryptionEntry[2].Name = "CipherValue";
544         aGpgEncryptionEntry[2].Value <<= aCipherValue;
545 
546         aGpgEncryptions.push_back(aGpgEncryptionEntry);
547     }
548 
549     aEncryptionData[0].Name = PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
550     aEncryptionData[0].Value <<= aVector;
551 
552     aContainer[0].Name = "GpgInfos";
553     aContainer[0].Value <<= comphelper::containerToSequence(aGpgEncryptions);
554     aContainer[1].Name = "EncryptionKey";
555     aContainer[1].Value <<= aEncryptionData;
556 
557     return aContainer;
558 #else
559     return uno::Sequence< beans::NamedValue >();
560 #endif
561 }
562 
IsValidZipEntryFileName(const OUString & aName,bool bSlashAllowed)563 bool OStorageHelper::IsValidZipEntryFileName( const OUString& aName, bool bSlashAllowed )
564 {
565     return IsValidZipEntryFileName( aName.getStr(), aName.getLength(), bSlashAllowed );
566 }
567 
568 
IsValidZipEntryFileName(const sal_Unicode * pChar,sal_Int32 nLength,bool bSlashAllowed)569 bool OStorageHelper::IsValidZipEntryFileName(
570     const sal_Unicode *pChar, sal_Int32 nLength, bool bSlashAllowed )
571 {
572     for ( sal_Int32 i = 0; i < nLength; i++ )
573     {
574         switch ( pChar[i] )
575         {
576             case '\\':
577             case '?':
578             case '<':
579             case '>':
580             case '\"':
581             case '|':
582             case ':':
583                 return false;
584             case '/':
585                 if ( !bSlashAllowed )
586                     return false;
587                 break;
588             default:
589                 if ( pChar[i] < 32  || (pChar[i] >= 0xD800 && pChar[i] <= 0xDFFF) )
590                     return false;
591         }
592     }
593     return true;
594 }
595 
596 
PathHasSegment(const OUString & aPath,const OUString & aSegment)597 bool OStorageHelper::PathHasSegment( const OUString& aPath, const OUString& aSegment )
598 {
599     bool bResult = false;
600     const sal_Int32 nPathLen = aPath.getLength();
601     const sal_Int32 nSegLen = aSegment.getLength();
602 
603     if ( !aSegment.isEmpty() && nPathLen >= nSegLen )
604     {
605         OUString aEndSegment = "/" + aSegment;
606         OUString aInternalSegment = aEndSegment + "/";
607 
608         if ( aPath.indexOf( aInternalSegment ) >= 0 )
609             bResult = true;
610 
611         if ( !bResult && aPath.startsWith( aSegment ) )
612         {
613             if ( nPathLen == nSegLen || aPath[nSegLen] == '/' )
614                 bResult = true;
615         }
616 
617         if ( !bResult && nPathLen > nSegLen && aPath.subView( nPathLen - nSegLen - 1, nSegLen + 1 ) == aEndSegment )
618             bResult = true;
619     }
620 
621     return bResult;
622 }
623 
624 class LifecycleProxy::Impl
625     : public std::vector< uno::Reference< embed::XStorage > > {};
LifecycleProxy()626 LifecycleProxy::LifecycleProxy()
627     : m_xBadness( new Impl ) { }
~LifecycleProxy()628 LifecycleProxy::~LifecycleProxy() { }
629 
commitStorages()630 void LifecycleProxy::commitStorages()
631 {
632     std::for_each(m_xBadness->rbegin(), m_xBadness->rend(), // reverse order (outwards)
633         [](Impl::reference rxItem) {
634             uno::Reference<embed::XTransactedObject> const xTransaction(rxItem, uno::UNO_QUERY);
635             if (xTransaction.is())
636             {
637                 xTransaction->commit();
638             }
639         });
640 }
641 
splitPath(std::vector<OUString> & rElems,const OUString & rPath)642 static void splitPath( std::vector<OUString> &rElems,
643                        const OUString& rPath )
644 {
645     for (sal_Int32 i = 0; i >= 0;)
646         rElems.push_back( rPath.getToken( 0, '/', i ) );
647 }
648 
LookupStorageAtPath(const uno::Reference<embed::XStorage> & xParentStorage,std::vector<OUString> & rElems,sal_uInt32 nOpenMode,LifecycleProxy const & rNastiness)649 static uno::Reference< embed::XStorage > LookupStorageAtPath(
650         const uno::Reference< embed::XStorage > &xParentStorage,
651         std::vector<OUString> &rElems, sal_uInt32 nOpenMode,
652         LifecycleProxy const &rNastiness )
653 {
654     uno::Reference< embed::XStorage > xStorage( xParentStorage );
655     rNastiness.m_xBadness->push_back( xStorage );
656     for( size_t i = 0; i < rElems.size() && xStorage.is(); i++ )
657     {
658         xStorage = xStorage->openStorageElement( rElems[i], nOpenMode );
659         rNastiness.m_xBadness->push_back( xStorage );
660     }
661     return xStorage;
662 }
663 
GetStorageAtPath(const uno::Reference<embed::XStorage> & xStorage,const OUString & rPath,sal_uInt32 nOpenMode,LifecycleProxy const & rNastiness)664 uno::Reference< embed::XStorage > OStorageHelper::GetStorageAtPath(
665         const uno::Reference< embed::XStorage > &xStorage,
666         const OUString& rPath, sal_uInt32 nOpenMode,
667         LifecycleProxy const &rNastiness )
668 {
669     std::vector<OUString> aElems;
670     splitPath( aElems, rPath );
671     return LookupStorageAtPath( xStorage, aElems, nOpenMode, rNastiness );
672 }
673 
GetStreamAtPath(const uno::Reference<embed::XStorage> & xParentStorage,const OUString & rPath,sal_uInt32 nOpenMode,LifecycleProxy const & rNastiness)674 uno::Reference< io::XStream > OStorageHelper::GetStreamAtPath(
675         const uno::Reference< embed::XStorage > &xParentStorage,
676         const OUString& rPath, sal_uInt32 nOpenMode,
677         LifecycleProxy const &rNastiness )
678 {
679     std::vector<OUString> aElems;
680     splitPath( aElems, rPath );
681     OUString aName( aElems.back() );
682     aElems.pop_back();
683     sal_uInt32 nStorageMode = nOpenMode & ~embed::ElementModes::TRUNCATE;
684     uno::Reference< embed::XStorage > xStorage(
685         LookupStorageAtPath( xParentStorage, aElems, nStorageMode, rNastiness ),
686         uno::UNO_SET_THROW );
687     return xStorage->openStreamElement( aName, nOpenMode );
688 }
689 
GetStreamAtPackageURL(uno::Reference<embed::XStorage> const & xParentStorage,const OUString & rURL,sal_uInt32 const nOpenMode,LifecycleProxy const & rNastiness)690 uno::Reference< io::XStream > OStorageHelper::GetStreamAtPackageURL(
691         uno::Reference< embed::XStorage > const& xParentStorage,
692         const OUString& rURL, sal_uInt32 const nOpenMode,
693         LifecycleProxy const & rNastiness)
694 {
695     OUString path;
696     if (rURL.startsWithIgnoreAsciiCase("vnd.sun.star.Package:", &path))
697     {
698         return GetStreamAtPath(xParentStorage, path, nOpenMode, rNastiness);
699     }
700     return nullptr;
701 }
702 
GetODFVersionFromStorage(const uno::Reference<embed::XStorage> & xStorage)703 OUString OStorageHelper::GetODFVersionFromStorage(const uno::Reference<embed::XStorage>& xStorage)
704 {
705     OUString aODFVersion;
706     try
707     {
708         uno::Reference<beans::XPropertySet> xPropSet(xStorage, uno::UNO_QUERY_THROW);
709         xPropSet->getPropertyValue("Version") >>= aODFVersion;
710     }
711     catch (uno::Exception&)
712     {
713     }
714     return aODFVersion;
715 }
716 }
717 
718 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
719