summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--comphelper/source/misc/docpasswordhelper.cxx57
-rw-r--r--include/comphelper/docpasswordhelper.hxx17
-rw-r--r--include/sfx2/objsh.hxx3
-rw-r--r--sfx2/source/dialog/securitypage.cxx26
-rw-r--r--sfx2/source/doc/objserv.cxx31
-rw-r--r--sw/qa/uitest/data/tdf128744.docxbin0 -> 21228 bytes
-rw-r--r--sw/qa/uitest/writer_tests7/tdf128744.py71
-rw-r--r--sw/source/uibase/uiview/view2.cxx57
8 files changed, 237 insertions, 25 deletions
diff --git a/comphelper/source/misc/docpasswordhelper.cxx b/comphelper/source/misc/docpasswordhelper.cxx
index e894b0d77bb7..5edb3949c977 100644
--- a/comphelper/source/misc/docpasswordhelper.cxx
+++ b/comphelper/source/misc/docpasswordhelper.cxx
@@ -137,6 +137,63 @@ DocPasswordHelper::GenerateNewModifyPasswordInfoOOXML(std::u16string_view aPassw
}
+uno::Sequence< beans::PropertyValue > DocPasswordHelper::ConvertPasswordInfo( const uno::Sequence< beans::PropertyValue >& aInfo )
+{
+ uno::Sequence< beans::PropertyValue > aResult;
+ OUString sAlgorithm, sHash, sSalt, sCount;
+ sal_Int32 nAlgorithm = 0;
+
+ for ( const auto & prop : aInfo )
+ {
+ if ( prop.Name == "cryptAlgorithmSid" )
+ {
+ prop.Value >>= sAlgorithm;
+ nAlgorithm = sAlgorithm.toInt32();
+ }
+ else if ( prop.Name == "salt" )
+ prop.Value >>= sSalt;
+ else if ( prop.Name == "cryptSpinCount" )
+ prop.Value >>= sCount;
+ else if ( prop.Name == "hash" )
+ prop.Value >>= sHash;
+ }
+
+ if (nAlgorithm == 1)
+ sAlgorithm = "MD2";
+ else if (nAlgorithm == 2)
+ sAlgorithm = "MD4";
+ else if (nAlgorithm == 3)
+ sAlgorithm = "MD5";
+ else if (nAlgorithm == 4)
+ sAlgorithm = "SHA-1";
+ else if (nAlgorithm == 5)
+ sAlgorithm = "MAC";
+ else if (nAlgorithm == 6)
+ sAlgorithm = "RIPEMD";
+ else if (nAlgorithm == 7)
+ sAlgorithm = "RIPEMD-160";
+ else if (nAlgorithm == 9)
+ sAlgorithm = "HMAC";
+ else if (nAlgorithm == 12)
+ sAlgorithm = "SHA-256";
+ else if (nAlgorithm == 13)
+ sAlgorithm = "SHA-384";
+ else if (nAlgorithm == 14)
+ sAlgorithm = "SHA-512";
+
+ if ( !sCount.isEmpty() )
+ {
+ sal_Int32 nCount = sCount.toInt32();
+ aResult = { comphelper::makePropertyValue("algorithm-name", sAlgorithm),
+ comphelper::makePropertyValue("salt", sSalt),
+ comphelper::makePropertyValue("iteration-count", nCount),
+ comphelper::makePropertyValue("hash", sHash) };
+ }
+
+ return aResult;
+}
+
+
bool DocPasswordHelper::IsModifyPasswordCorrect( std::u16string_view aPassword, const uno::Sequence< beans::PropertyValue >& aInfo )
{
bool bResult = false;
diff --git a/include/comphelper/docpasswordhelper.hxx b/include/comphelper/docpasswordhelper.hxx
index 0b9e646bd2b8..64d7ba9782ec 100644
--- a/include/comphelper/docpasswordhelper.hxx
+++ b/include/comphelper/docpasswordhelper.hxx
@@ -113,6 +113,23 @@ public:
static css::uno::Sequence< css::beans::PropertyValue >
GenerateNewModifyPasswordInfo( std::u16string_view aPassword );
+ /** This helper function converts a grab-bagged password, e.g. the
+ trackChanges password which has no complete inner equivalent to
+ the inner format. The result sequence contains the hash and the
+ algorithm-related info to use e.g. in IsModifyPasswordCorrect().
+
+ @param aInfo
+ The sequence containing the hash and the algorithm-related info
+ according to the OOXML origin, used by grab-bagging.
+
+ @return
+ The sequence containing the hash and the algorithm-related info
+ in the inner format.
+ */
+
+ static css::uno::Sequence< css::beans::PropertyValue > ConvertPasswordInfo(
+ const css::uno::Sequence< css::beans::PropertyValue >& aInfo );
+
static css::uno::Sequence<css::beans::PropertyValue>
GenerateNewModifyPasswordInfoOOXML(std::u16string_view aPassword);
diff --git a/include/sfx2/objsh.hxx b/include/sfx2/objsh.hxx
index b7f9e1fa0688..428995bff493 100644
--- a/include/sfx2/objsh.hxx
+++ b/include/sfx2/objsh.hxx
@@ -781,6 +781,9 @@ public:
/// Gets the certificate that is already picked by the user but not yet used for signing.
css::uno::Reference<css::security::XCertificate> GetSignPDFCertificate() const;
+ /// Gets grab-bagged password info to unprotect change tracking with verification
+ css::uno::Sequence< css::beans::PropertyValue > GetDocumentProtectionFromGrabBag() const;
+
// Lock all unlocked views, and returns a guard object which unlocks those views when destructed
virtual std::unique_ptr<LockAllViewsGuard> LockAllViews()
{
diff --git a/sfx2/source/dialog/securitypage.cxx b/sfx2/source/dialog/securitypage.cxx
index 0ff73b44f6f9..39bdc7cb1eba 100644
--- a/sfx2/source/dialog/securitypage.cxx
+++ b/sfx2/source/dialog/securitypage.cxx
@@ -33,6 +33,7 @@
#include <svl/poolitem.hxx>
#include <svl/intitem.hxx>
#include <svl/PasswordHelper.hxx>
+#include <comphelper/docpasswordhelper.hxx>
#include <sfx2/strings.hrc>
@@ -114,12 +115,29 @@ static bool lcl_IsPasswordCorrect( std::u16string_view rPassword )
pCurDocShell->GetProtectionHash( aPasswordHash );
// check if supplied password was correct
- uno::Sequence< sal_Int8 > aNewPasswd( aPasswordHash );
- SvPasswordHelper::GetHashPassword( aNewPasswd, rPassword );
- if (SvPasswordHelper::CompareHashPassword( aPasswordHash, rPassword ))
- bRes = true; // password was correct
+ if (aPasswordHash.getLength() == 1 && aPasswordHash[0] == 1)
+ {
+ // dummy RedlinePassword from OOXML import: get real password info
+ // from the grab-bag to verify the password
+ const css::uno::Sequence< css::beans::PropertyValue > aDocumentProtection =
+ pCurDocShell->GetDocumentProtectionFromGrabBag();
+ bRes =
+ // password is ok, if there is no DocumentProtection in the GrabBag,
+ // i.e. the dummy RedlinePassword imported from an OpenDocument file
+ !aDocumentProtection.hasElements() ||
+ // verify password with the password info imported from OOXML
+ ::comphelper::DocPasswordHelper::IsModifyPasswordCorrect( rPassword,
+ ::comphelper::DocPasswordHelper::ConvertPasswordInfo ( aDocumentProtection ) );
+ }
else
{
+ uno::Sequence< sal_Int8 > aNewPasswd( aPasswordHash );
+ SvPasswordHelper::GetHashPassword( aNewPasswd, rPassword );
+ bRes = SvPasswordHelper::CompareHashPassword( aPasswordHash, rPassword );
+ }
+
+ if ( !bRes )
+ {
std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
VclMessageType::Info, VclButtonsType::Ok,
SfxResId(RID_SVXSTR_INCORRECT_PASSWORD)));
diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx
index 452d059d0c16..3a6a0ceea2a8 100644
--- a/sfx2/source/doc/objserv.cxx
+++ b/sfx2/source/doc/objserv.cxx
@@ -104,6 +104,7 @@
#include <unotools/ucbstreamhelper.hxx>
#include <unotools/streamwrap.hxx>
#include <comphelper/sequenceashashmap.hxx>
+#include <editeng/unoprnms.hxx>
#include <autoredactdialog.hxx>
@@ -2097,4 +2098,34 @@ const uno::Sequence<sal_Int8>& SfxObjectShell::getUnoTunnelId()
return theSfxObjectShellUnoTunnelId.getSeq();
}
+uno::Sequence< beans::PropertyValue > SfxObjectShell::GetDocumentProtectionFromGrabBag() const
+{
+ uno::Reference<frame::XModel> xModel = GetBaseModel();
+
+ if (!xModel.is())
+ {
+ return uno::Sequence< beans::PropertyValue>();
+ }
+
+ uno::Reference< beans::XPropertySet > xPropSet( xModel, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
+ const OUString aGrabBagName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
+ if ( xPropSetInfo->hasPropertyByName( aGrabBagName ) )
+ {
+ uno::Sequence< beans::PropertyValue > propList;
+ xPropSet->getPropertyValue( aGrabBagName ) >>= propList;
+ for( const auto& rProp : std::as_const(propList) )
+ {
+ if (rProp.Name == "DocumentProtection")
+ {
+ uno::Sequence< beans::PropertyValue > rAttributeList;
+ rProp.Value >>= rAttributeList;
+ return rAttributeList;
+ }
+ }
+ }
+
+ return uno::Sequence< beans::PropertyValue>();
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/uitest/data/tdf128744.docx b/sw/qa/uitest/data/tdf128744.docx
new file mode 100644
index 000000000000..b03bef21baa4
--- /dev/null
+++ b/sw/qa/uitest/data/tdf128744.docx
Binary files differ
diff --git a/sw/qa/uitest/writer_tests7/tdf128744.py b/sw/qa/uitest/writer_tests7/tdf128744.py
new file mode 100644
index 000000000000..34201e858675
--- /dev/null
+++ b/sw/qa/uitest/writer_tests7/tdf128744.py
@@ -0,0 +1,71 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# 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 libreoffice.uno.propertyvalue import mkPropertyValues
+from uitest.uihelper.common import get_state_as_dict
+from uitest.uihelper.common import select_pos
+from uitest.uihelper.common import get_url_for_data_file
+
+class tdf128744(UITestCase):
+
+ def test_tdf128744(self):
+ # load the sample file
+ with self.ui_test.load_file(get_url_for_data_file("tdf128744.docx")):
+
+ # first try to unprotect Record Changes with an invalid password
+
+ with self.ui_test.execute_dialog_through_command(".uno:SetDocumentProperties") as xDialog:
+ xRecordChangesCheckbox = xDialog.getChild("recordchanges")
+ self.assertEqual(get_state_as_dict(xRecordChangesCheckbox)["Selected"], "true")
+ xTabs = xDialog.getChild("tabcontrol")
+ select_pos(xTabs, "3") #tab Security
+ xProtectBtn = xDialog.getChild("protect")
+
+ # No close_button: click on the "Ok" inside to check the "Invalid password" infobox
+ with self.ui_test.execute_blocking_action(xProtectBtn.executeAction, args=('CLICK', ()), close_button="") as xPasswordDialog:
+ self.assertEqual(get_state_as_dict(xPasswordDialog)["DisplayText"], "Enter Password")
+ xPassword = xPasswordDialog.getChild("pass1ed")
+ xPassword.executeAction("TYPE", mkPropertyValues({"TEXT": "bad password"}))
+ print(xPasswordDialog.getChildren())
+ xOkBtn = xPasswordDialog.getChild("ok")
+ with self.ui_test.execute_blocking_action(xOkBtn.executeAction, args=('CLICK', ())) as xInfoBox:
+ # "Invalid password" infobox
+ self.assertEqual(get_state_as_dict(xInfoBox)["DisplayText"], 'Information')
+
+ # now open the dialog again and read the properties, Record Changes checkbox is still enabled
+ with self.ui_test.execute_dialog_through_command(".uno:SetDocumentProperties", close_button="cancel") as xDialog:
+ xRecordChangesCheckbox = xDialog.getChild("recordchanges")
+ self.assertEqual(get_state_as_dict(xRecordChangesCheckbox)["Selected"], "true")
+ xResetBtn = xDialog.getChild("reset")
+ xResetBtn.executeAction("CLICK", tuple())
+
+ # unprotect Record Changes with the valid password "test"
+
+ with self.ui_test.execute_dialog_through_command(".uno:SetDocumentProperties") as xDialog:
+ xRecordChangesCheckbox = xDialog.getChild("recordchanges")
+ self.assertEqual(get_state_as_dict(xRecordChangesCheckbox)["Selected"], "true")
+ xTabs = xDialog.getChild("tabcontrol")
+ select_pos(xTabs, "3") #tab Security
+ xProtectBtn = xDialog.getChild("protect")
+
+ with self.ui_test.execute_blocking_action(xProtectBtn.executeAction, args=('CLICK', ())) as xPasswordDialog:
+ self.assertEqual(get_state_as_dict(xPasswordDialog)["DisplayText"], "Enter Password")
+ xPassword = xPasswordDialog.getChild("pass1ed")
+ # give the correct password
+ xPassword.executeAction("TYPE", mkPropertyValues({"TEXT": "test"}))
+
+ # now open the dialog again and read the properties, Record Changes checkbox is disabled now
+ with self.ui_test.execute_dialog_through_command(".uno:SetDocumentProperties", close_button="cancel") as xDialog:
+ xRecordChangesCheckbox = xDialog.getChild("recordchanges")
+ self.assertEqual(get_state_as_dict(xRecordChangesCheckbox)["Selected"], "false")
+ xResetBtn = xDialog.getChild("reset")
+ xResetBtn.executeAction("CLICK", tuple())
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sw/source/uibase/uiview/view2.cxx b/sw/source/uibase/uiview/view2.cxx
index 2cb38325ba67..970064c6c164 100644
--- a/sw/source/uibase/uiview/view2.cxx
+++ b/sw/source/uibase/uiview/view2.cxx
@@ -117,6 +117,7 @@
#include <reffld.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/string.hxx>
+#include <comphelper/docpasswordhelper.hxx>
#include <PostItMgr.hxx>
@@ -682,34 +683,48 @@ void SwView::Execute(SfxRequest &rReq)
{
OSL_ENSURE( !static_cast<const SfxBoolItem*>(pItem)->GetValue(), "SwView::Execute(): password set and redlining off doesn't match!" );
- // dummy password from OOXML import: only confirmation dialog
+ // xmlsec05: new password dialog
+ SfxPasswordDialog aPasswdDlg(GetFrameWeld());
+ aPasswdDlg.SetMinLen(1);
+ //#i69751# the result of Execute() can be ignored
+ (void)aPasswdDlg.run();
+ OUString sNewPasswd(aPasswdDlg.GetPassword());
+
+ // password verification
+ bool bPasswordOk = false;
if (aPasswd.getLength() == 1 && aPasswd[0] == 1)
{
- std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(m_pWrtShell->GetView().GetFrameWeld(),
- VclMessageType::Warning, VclButtonsType::YesNo,
- SfxResId(RID_SVXSTR_END_REDLINING_WARNING)));
- xWarn->set_default_response(RET_NO);
- if (xWarn->run() == RET_YES)
- rIDRA.SetRedlinePassword(Sequence <sal_Int8> ());
- else
- break;
+ // dummy RedlinePassword from OOXML import: get real password info
+ // from the grab-bag to verify the password
+ const css::uno::Sequence< css::beans::PropertyValue > aDocumentProtection =
+ static_cast<SfxObjectShell*>(GetDocShell())->
+ GetDocumentProtectionFromGrabBag();
+
+ bPasswordOk =
+ // password is ok, if there is no DocumentProtection in the GrabBag,
+ // i.e. the dummy RedlinePassword imported from an OpenDocument file
+ !aDocumentProtection.hasElements() ||
+ // verify password with the password info imported from OOXML
+ ::comphelper::DocPasswordHelper::IsModifyPasswordCorrect(sNewPasswd,
+ ::comphelper::DocPasswordHelper::ConvertPasswordInfo ( aDocumentProtection ) );
}
else
{
- // xmlsec05: new password dialog
- SfxPasswordDialog aPasswdDlg(GetFrameWeld());
- aPasswdDlg.SetMinLen(1);
- //#i69751# the result of Execute() can be ignored
- (void)aPasswdDlg.run();
- OUString sNewPasswd(aPasswdDlg.GetPassword());
+ // the simplified RedlinePassword
Sequence <sal_Int8> aNewPasswd = rIDRA.GetRedlinePassword();
SvPasswordHelper::GetHashPassword( aNewPasswd, sNewPasswd );
- if(SvPasswordHelper::CompareHashPassword(aPasswd, sNewPasswd))
- rIDRA.SetRedlinePassword(Sequence <sal_Int8> ());
- else
- { // xmlsec05: message box for wrong password
- break;
- }
+ bPasswordOk = SvPasswordHelper::CompareHashPassword(aPasswd, sNewPasswd);
+ }
+
+ if (bPasswordOk)
+ rIDRA.SetRedlinePassword(Sequence <sal_Int8> ());
+ else
+ { // xmlsec05: message box for wrong password
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(RID_SVXSTR_INCORRECT_PASSWORD)));
+ xInfoBox->run();
+ break;
}
}