comphelper/source/misc/docpasswordhelper.cxx |   40 ++++++++++++++++++++-------
 oox/source/token/properties.txt              |    1 
 sc/qa/uitest/calc_tests9/tdf115933.py        |   32 +++++++++++++++++++++
 sc/qa/uitest/data/tdf115933.xlsx             |binary
 sc/source/filter/oox/workbooksettings.cxx    |   15 ++++++++++
 5 files changed, 79 insertions(+), 9 deletions(-)

New commits:
commit 40f38fd16dad4374543d4a7a109b3264837ce8d1
Author:     Tünde Tóth <toth.tu...@nisz.hu>
AuthorDate: Wed Sep 1 15:47:40 2021 +0200
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Thu Sep 16 15:54:57 2021 +0200

    tdf#115933 XLSX import: fix permission for editing
    
    The passwords for editing in XLSX documents
    created with Excel weren't asked and verified.
    
    Note: LibreOffice supports only a subset of the hashing
    algorithms specified in MS-OE376, according to
    DocPasswordHelper::GetOoxHashAsVector() and
    
https://docs.microsoft.com/en-us/openspecs/office_standards/ms-oe376/f70a4140-340b-4e94-a604-dff25b9846b1.
    Also the documents encrypted with unsupported algorithms
    got edit protection now, but it's not possible to add
    permission to edit them (copy of these documents are still
    editable).
    
    Change-Id: Iabc90f6bba4ed071dd2c60e9dea905481816964b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121497
    Tested-by: László Németh <nem...@numbertext.org>
    Reviewed-by: László Németh <nem...@numbertext.org>

diff --git a/comphelper/source/misc/docpasswordhelper.cxx 
b/comphelper/source/misc/docpasswordhelper.cxx
index cd7090944dc0..980faff14698 100644
--- a/comphelper/source/misc/docpasswordhelper.cxx
+++ b/comphelper/source/misc/docpasswordhelper.cxx
@@ -116,8 +116,7 @@ bool DocPasswordHelper::IsModifyPasswordCorrect( 
std::u16string_view aPassword,
     if ( !aPassword.empty() && aInfo.hasElements() )
     {
         OUString sAlgorithm;
-        uno::Sequence< sal_Int8 > aSalt;
-        uno::Sequence< sal_Int8 > aHash;
+        uno::Any aSalt, aHash;
         sal_Int32 nCount = 0;
 
         for ( const auto & prop : aInfo )
@@ -125,20 +124,43 @@ bool DocPasswordHelper::IsModifyPasswordCorrect( 
std::u16string_view aPassword,
             if ( prop.Name == "algorithm-name" )
                 prop.Value >>= sAlgorithm;
             else if ( prop.Name == "salt" )
-                prop.Value >>= aSalt;
+                aSalt = prop.Value;
             else if ( prop.Name == "iteration-count" )
                 prop.Value >>= nCount;
             else if ( prop.Name == "hash" )
-                prop.Value >>= aHash;
+                aHash = prop.Value;
         }
 
-        if ( sAlgorithm == "PBKDF2" && aSalt.hasElements() && nCount > 0 && 
aHash.hasElements() )
+        if ( sAlgorithm == "PBKDF2" )
         {
-            uno::Sequence< sal_Int8 > aNewHash = GeneratePBKDF2Hash( 
aPassword, aSalt, nCount, aHash.getLength() );
-            for ( sal_Int32 nInd = 0; nInd < aNewHash.getLength() && nInd < 
aHash.getLength() && aNewHash[nInd] == aHash[nInd]; nInd ++ )
+            uno::Sequence<sal_Int8> aIntSalt, aIntHash;
+            aSalt >>= aIntSalt;
+            aHash >>= aIntHash;
+            if (aIntSalt.hasElements() && nCount > 0 && aIntHash.hasElements())
             {
-                if ( nInd == aNewHash.getLength() - 1 && nInd == 
aHash.getLength() - 1 )
-                    bResult = true;
+                uno::Sequence<sal_Int8> aNewHash
+                    = GeneratePBKDF2Hash(aPassword, aIntSalt, nCount, 
aIntHash.getLength());
+                for (sal_Int32 nInd = 0; nInd < aNewHash.getLength() && nInd < 
aIntHash.getLength()
+                                         && aNewHash[nInd] == aIntHash[nInd];
+                     nInd++)
+                {
+                    if (nInd == aNewHash.getLength() - 1 && nInd == 
aIntHash.getLength() - 1)
+                        bResult = true;
+                }
+            }
+        }
+        else if (nCount > 0)
+        {
+            OUString sSalt, sHash;
+            aSalt >>= sSalt;
+            aHash >>= sHash;
+            if (!sSalt.isEmpty() && !sHash.isEmpty())
+            {
+                const OUString 
aNewHash(GetOoxHashAsBase64(OUString(aPassword), sSalt, nCount,
+                                                           
comphelper::Hash::IterCount::APPEND,
+                                                           sAlgorithm));
+                if (!aNewHash.isEmpty())
+                    bResult = aNewHash == sHash;
             }
         }
     }
diff --git a/oox/source/token/properties.txt b/oox/source/token/properties.txt
index 808df85533ff..2c56d0f46804 100644
--- a/oox/source/token/properties.txt
+++ b/oox/source/token/properties.txt
@@ -337,6 +337,7 @@ MirroredY
 MissingValueTreatment
 Model
 ModifyPasswordHash
+ModifyPasswordInfo
 MoveProtect
 MovingAveragePeriod
 MultiLine
diff --git a/sc/qa/uitest/calc_tests9/tdf115933.py 
b/sc/qa/uitest/calc_tests9/tdf115933.py
new file mode 100644
index 000000000000..be7719487958
--- /dev/null
+++ b/sc/qa/uitest/calc_tests9/tdf115933.py
@@ -0,0 +1,32 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+from uitest.framework import UITestCase
+from uitest.uihelper.common import get_url_for_data_file
+from libreoffice.uno.propertyvalue import mkPropertyValues
+
+#Bug 115933 - XLSX <fileSharing> password protected with algorithmName, 
hashValue, saltValue and spinCount
+
+class tdf115933(UITestCase):
+
+    def test_tdf115933(self):
+        with self.ui_test.load_file(get_url_for_data_file("tdf115933.xlsx")):
+            #The document was created in Excel.
+            calcDoc = self.xUITest.getTopFocusWindow()
+            gridwin = calcDoc.getChild("grid_window")
+
+            document = self.ui_test.get_component()
+
+            self.assertTrue(document.isReadonly())
+
+            #Without the fix in place, this dialog wouldn't have been displayed
+            with self.ui_test.execute_dialog_through_action(gridwin, "TYPE", 
mkPropertyValues({"KEYCODE": "CTRL+SHIFT+M"})) as xDialog:
+                xPassword = xDialog.getChild("newpassEntry")
+                xPassword.executeAction("TYPE", mkPropertyValues({"TEXT": 
"a"}))
+
+            self.assertFalse(document.isReadonly())
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sc/qa/uitest/data/tdf115933.xlsx b/sc/qa/uitest/data/tdf115933.xlsx
new file mode 100644
index 000000000000..a6073a76e506
Binary files /dev/null and b/sc/qa/uitest/data/tdf115933.xlsx differ
diff --git a/sc/source/filter/oox/workbooksettings.cxx 
b/sc/source/filter/oox/workbooksettings.cxx
index a7ab887bf967..ae2aa82e1a31 100644
--- a/sc/source/filter/oox/workbooksettings.cxx
+++ b/sc/source/filter/oox/workbooksettings.cxx
@@ -210,6 +210,21 @@ void WorkbookSettings::finalizeImport()
         if (maFileSharing.mbRecommendReadOnly || 
!maFileSharing.maHashValue.isEmpty())
             aSettingsProp.setProperty( PROP_LoadReadonly, true );
 
+        if (!maFileSharing.maHashValue.isEmpty())
+        {
+            Sequence<PropertyValue> aResult;
+            aResult.realloc(4);
+            aResult[0].Name = "algorithm-name";
+            aResult[0].Value <<= maFileSharing.maAlgorithmName;
+            aResult[1].Name = "salt";
+            aResult[1].Value <<= maFileSharing.maSaltValue;
+            aResult[2].Name = "iteration-count";
+            aResult[2].Value <<= maFileSharing.mnSpinCount;
+            aResult[3].Name = "hash";
+            aResult[3].Value <<= maFileSharing.maHashValue;
+            aSettingsProp.setProperty(PROP_ModifyPasswordInfo, aResult);
+        }
+
         if( maFileSharing.mnPasswordHash != 0 )
             aSettingsProp.setProperty( PROP_ModifyPasswordHash, static_cast< 
sal_Int32 >( maFileSharing.mnPasswordHash ) );
     }

Reply via email to