1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: tabprotection.cxx,v $
10 * $Revision: 1.1.4.7 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
34 // INCLUDE ---------------------------------------------------------------
36 #include "tabprotection.hxx"
37 #include "tools/debug.hxx"
38 #include "svtools/PasswordHelper.hxx"
39 #include "document.hxx"
43 #define DEBUG_TAB_PROTECTION 0
45 #define URI_SHA1 "http://www.w3.org/2000/09/xmldsig#sha1"
46 #define URI_XLS_LEGACY "http://docs.oasis-open.org/office/ns/table/legacy-hash-excel"
48 using namespace ::com::sun::star
;
49 using ::com::sun::star::uno::Sequence
;
50 using ::rtl::OUString
;
51 using ::rtl::OUStringBuffer
;
54 // ============================================================================
56 bool ScPassHashHelper::needsPassHashRegen(const ScDocument
& rDoc
, ScPasswordHash eHash1
, ScPasswordHash eHash2
)
58 if (rDoc
.IsDocProtected())
60 const ScDocProtection
* p
= rDoc
.GetDocProtection();
61 if (!p
->isPasswordEmpty() && !p
->hasPasswordHash(eHash1
, eHash2
))
65 SCTAB nTabCount
= rDoc
.GetTableCount();
66 for (SCTAB i
= 0; i
< nTabCount
; ++i
)
68 const ScTableProtection
* p
= rDoc
.GetTabProtection(i
);
69 if (!p
|| !p
->isProtected())
70 // Sheet not protected. Skip it.
73 if (!p
->isPasswordEmpty() && !p
->hasPasswordHash(eHash1
, eHash2
))
80 OUString
ScPassHashHelper::getHashURI(ScPasswordHash eHash
)
85 return OUString::createFromAscii(URI_SHA1
);
87 return OUString::createFromAscii(URI_XLS_LEGACY
);
88 case PASSHASH_UNSPECIFIED
:
95 ScPasswordHash
ScPassHashHelper::getHashTypeFromURI(const OUString
& rURI
)
97 if (rURI
.equalsAscii(URI_SHA1
))
99 else if (rURI
.equalsAscii(URI_XLS_LEGACY
))
101 return PASSHASH_UNSPECIFIED
;
104 // ============================================================================
106 ScPassHashProtectable::~ScPassHashProtectable()
110 // ============================================================================
112 static sal_uInt16
lcl_getXLHashFromChar(const sal_Char
* szPassword
)
114 sal_uInt16 cchPassword
= static_cast< sal_uInt16
>( strlen(szPassword
) );
115 sal_uInt16 wPasswordHash
= 0;
117 return wPasswordHash
;
119 const char* pch
= &szPassword
[cchPassword
];
120 while (pch
-- != szPassword
)
122 wPasswordHash
= ((wPasswordHash
>> 14) & 0x01) |
123 ((wPasswordHash
<< 1) & 0x7fff);
124 wPasswordHash
^= *pch
;
127 wPasswordHash
= ((wPasswordHash
>> 14) & 0x01) |
128 ((wPasswordHash
<< 1) & 0x7fff);
130 wPasswordHash
^= (0x8000 | ('N' << 8) | 'K');
131 wPasswordHash
^= cchPassword
;
133 return wPasswordHash
;
136 static Sequence
<sal_Int8
> lcl_getXLHash(const String
& aPassText
)
138 const sal_Char
* szBuf
= OUStringToOString(OUString(aPassText
), RTL_TEXTENCODING_UTF8
).getStr();
139 sal_uInt16 nHash
= lcl_getXLHashFromChar(szBuf
);
140 Sequence
<sal_Int8
> aHash(2);
141 aHash
[0] = (nHash
>> 8) & 0xFF;
142 aHash
[1] = nHash
& 0xFF;
146 class ScTableProtectionImpl
149 static Sequence
<sal_Int8
> hashPassword(const String
& aPassText
, ScPasswordHash eHash
= PASSHASH_SHA1
);
150 static Sequence
<sal_Int8
> hashPassword(const Sequence
<sal_Int8
>& rPassHash
, ScPasswordHash eHash
= PASSHASH_SHA1
);
152 explicit ScTableProtectionImpl(SCSIZE nOptSize
);
153 explicit ScTableProtectionImpl(const ScTableProtectionImpl
& r
);
155 bool isProtected() const;
156 bool isProtectedWithPass() const;
157 void setProtected(bool bProtected
);
159 bool isPasswordEmpty() const;
160 bool hasPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
= PASSHASH_UNSPECIFIED
) const;
161 void setPassword(const String
& aPassText
);
162 ::com::sun::star::uno::Sequence
<sal_Int8
> getPasswordHash(
163 ScPasswordHash eHash
, ScPasswordHash eHash2
= PASSHASH_UNSPECIFIED
) const;
164 void setPasswordHash(
165 const ::com::sun::star::uno::Sequence
<sal_Int8
>& aPassword
,
166 ScPasswordHash eHash
= PASSHASH_SHA1
, ScPasswordHash eHash2
= PASSHASH_UNSPECIFIED
);
167 bool verifyPassword(const String
& aPassText
) const;
169 bool isOptionEnabled(SCSIZE nOptId
) const;
170 void setOption(SCSIZE nOptId
, bool bEnabled
);
174 ::com::sun::star::uno::Sequence
<sal_Int8
> maPassHash
;
175 ::std::vector
<bool> maOptions
;
178 ScPasswordHash meHash1
;
179 ScPasswordHash meHash2
;
182 Sequence
<sal_Int8
> ScTableProtectionImpl::hashPassword(const String
& aPassText
, ScPasswordHash eHash
)
184 Sequence
<sal_Int8
> aHash
;
188 aHash
= lcl_getXLHash(aPassText
);
191 SvPasswordHelper::GetHashPassword(aHash
, aPassText
);
199 Sequence
<sal_Int8
> ScTableProtectionImpl::hashPassword(
200 const Sequence
<sal_Int8
>& rPassHash
, ScPasswordHash eHash
)
202 if (!rPassHash
.getLength() || eHash
== PASSHASH_UNSPECIFIED
)
205 // TODO: Right now, we only support double-hash by SHA1.
206 if (eHash
== PASSHASH_SHA1
)
208 vector
<sal_Char
> aChars
;
209 sal_Int32 n
= rPassHash
.getLength();
211 for (sal_Int32 i
= 0; i
< n
; ++i
)
212 aChars
.push_back(static_cast<sal_Char
>(rPassHash
[i
]));
214 Sequence
<sal_Int8
> aNewHash
;
215 SvPasswordHelper::GetHashPassword(aNewHash
, &aChars
[0], aChars
.size());
222 ScTableProtectionImpl::ScTableProtectionImpl(SCSIZE nOptSize
) :
226 meHash1(PASSHASH_SHA1
),
227 meHash2(PASSHASH_UNSPECIFIED
)
231 ScTableProtectionImpl::ScTableProtectionImpl(const ScTableProtectionImpl
& r
) :
232 maPassText(r
.maPassText
),
233 maPassHash(r
.maPassHash
),
234 maOptions(r
.maOptions
),
235 mbEmptyPass(r
.mbEmptyPass
),
236 mbProtected(r
.mbProtected
),
242 bool ScTableProtectionImpl::isProtected() const
247 bool ScTableProtectionImpl::isProtectedWithPass() const
252 return maPassText
.Len() || maPassHash
.getLength();
255 void ScTableProtectionImpl::setProtected(bool bProtected
)
257 mbProtected
= bProtected
;
258 // We need to keep the old password even when the protection is off. So,
259 // don't erase the password data here.
262 void ScTableProtectionImpl::setPassword(const String
& aPassText
)
264 // We can't hash it here because we don't know whether this document will
265 // get saved to Excel or ODF, depending on which we will need to use a
266 // different hashing algorithm. One alternative is to hash it using all
267 // hash algorithms that we support, and store them all.
269 maPassText
= aPassText
;
270 mbEmptyPass
= aPassText
.Len() == 0;
273 maPassHash
= Sequence
<sal_Int8
>();
277 bool ScTableProtectionImpl::isPasswordEmpty() const
282 bool ScTableProtectionImpl::hasPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
) const
287 if (maPassText
.Len())
290 if (meHash1
== eHash
)
292 if (meHash2
== PASSHASH_UNSPECIFIED
)
296 return meHash2
== eHash2
;
302 Sequence
<sal_Int8
> ScTableProtectionImpl::getPasswordHash(
303 ScPasswordHash eHash
, ScPasswordHash eHash2
) const
305 Sequence
<sal_Int8
> aPassHash
;
311 if (maPassText
.Len())
313 // Cleartext password exists. Hash it.
314 aPassHash
= hashPassword(maPassText
, eHash
);
315 if (eHash2
!= PASSHASH_UNSPECIFIED
)
317 aPassHash
= hashPassword(aPassHash
, eHash2
);
323 // No clear text password. Check if we have a hash value of the right hash type.
324 if (meHash1
== eHash
)
326 aPassHash
= maPassHash
;
328 if (meHash2
== eHash2
)
329 // Matching double-hash requested.
331 else if (meHash2
== PASSHASH_UNSPECIFIED
)
332 // primary hashing type match. Double hash it by the requested
334 return hashPassword(aPassHash
, eHash2
);
339 return Sequence
<sal_Int8
>();
342 void ScTableProtectionImpl::setPasswordHash(
343 const uno::Sequence
<sal_Int8
>& aPassword
, ScPasswordHash eHash
, ScPasswordHash eHash2
)
345 sal_Int32 nLen
= aPassword
.getLength();
346 mbEmptyPass
= nLen
<= 0 ? true : false;
349 maPassHash
= aPassword
;
351 #if DEBUG_TAB_PROTECTION
352 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
353 printf("%2.2X ", static_cast<sal_uInt8
>(aPassword
[i
]));
358 bool ScTableProtectionImpl::verifyPassword(const String
& aPassText
) const
360 #if DEBUG_TAB_PROTECTION
361 fprintf(stdout
, "ScTableProtectionImpl::verifyPassword: input = '%s'\n",
362 OUStringToOString(rtl::OUString(aPassText
), RTL_TEXTENCODING_UTF8
).getStr());
366 return aPassText
.Len() == 0;
368 if (maPassText
.Len())
369 // Clear text password exists, and this one takes precedence.
370 return aPassText
.Equals(maPassText
);
372 Sequence
<sal_Int8
> aHash
= hashPassword(aPassText
, meHash1
);
373 aHash
= hashPassword(aHash
, meHash2
);
375 #if DEBUG_TAB_PROTECTION
376 fprintf(stdout
, "ScTableProtectionImpl::verifyPassword: hash = ");
377 for (sal_Int32 i
= 0; i
< aHash
.getLength(); ++i
)
378 printf("%2.2X ", static_cast<sal_uInt8
>(aHash
[i
]));
382 return aHash
== maPassHash
;
385 bool ScTableProtectionImpl::isOptionEnabled(SCSIZE nOptId
) const
387 if ( maOptions
.size() <= static_cast<size_t>(nOptId
) )
389 DBG_ERROR("ScTableProtectionImpl::isOptionEnabled: wrong size");
393 return maOptions
[nOptId
];
396 void ScTableProtectionImpl::setOption(SCSIZE nOptId
, bool bEnabled
)
398 if ( maOptions
.size() <= static_cast<size_t>(nOptId
) )
400 DBG_ERROR("ScTableProtectionImpl::setOption: wrong size");
404 maOptions
[nOptId
] = bEnabled
;
407 // ============================================================================
409 ScDocProtection::ScDocProtection() :
410 mpImpl(new ScTableProtectionImpl(static_cast<SCSIZE
>(ScDocProtection::NONE
)))
414 ScDocProtection::ScDocProtection(const ScDocProtection
& r
) :
415 ScPassHashProtectable(),
416 mpImpl(new ScTableProtectionImpl(*r
.mpImpl
))
420 ScDocProtection::~ScDocProtection()
424 bool ScDocProtection::isProtected() const
426 return mpImpl
->isProtected();
429 bool ScDocProtection::isProtectedWithPass() const
431 return mpImpl
->isProtectedWithPass();
434 void ScDocProtection::setProtected(bool bProtected
)
436 mpImpl
->setProtected(bProtected
);
438 // Currently Calc doesn't support document protection options. So, let's
439 // assume that when the document is protected, its structure is protected.
440 // We need to do this for Excel export.
441 mpImpl
->setOption(ScDocProtection::STRUCTURE
, bProtected
);
444 bool ScDocProtection::isPasswordEmpty() const
446 return mpImpl
->isPasswordEmpty();
449 bool ScDocProtection::hasPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
) const
451 return mpImpl
->hasPasswordHash(eHash
, eHash2
);
454 void ScDocProtection::setPassword(const String
& aPassText
)
456 mpImpl
->setPassword(aPassText
);
459 uno::Sequence
<sal_Int8
> ScDocProtection::getPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
) const
461 return mpImpl
->getPasswordHash(eHash
, eHash2
);
464 void ScDocProtection::setPasswordHash(
465 const uno::Sequence
<sal_Int8
>& aPassword
, ScPasswordHash eHash
, ScPasswordHash eHash2
)
467 mpImpl
->setPasswordHash(aPassword
, eHash
, eHash2
);
470 bool ScDocProtection::verifyPassword(const String
& aPassText
) const
472 return mpImpl
->verifyPassword(aPassText
);
475 bool ScDocProtection::isOptionEnabled(Option eOption
) const
477 return mpImpl
->isOptionEnabled(eOption
);
480 void ScDocProtection::setOption(Option eOption
, bool bEnabled
)
482 mpImpl
->setOption(eOption
, bEnabled
);
485 // ============================================================================
487 ScTableProtection::ScTableProtection() :
488 mpImpl(new ScTableProtectionImpl(static_cast<SCSIZE
>(ScTableProtection::NONE
)))
490 // Set default values for the options.
491 mpImpl
->setOption(SELECT_LOCKED_CELLS
, true);
492 mpImpl
->setOption(SELECT_UNLOCKED_CELLS
, true);
495 ScTableProtection::ScTableProtection(const ScTableProtection
& r
) :
496 ScPassHashProtectable(),
497 mpImpl(new ScTableProtectionImpl(*r
.mpImpl
))
501 ScTableProtection::~ScTableProtection()
505 bool ScTableProtection::isProtected() const
507 return mpImpl
->isProtected();
510 bool ScTableProtection::isProtectedWithPass() const
512 return mpImpl
->isProtectedWithPass();
515 void ScTableProtection::setProtected(bool bProtected
)
517 mpImpl
->setProtected(bProtected
);
520 bool ScTableProtection::isPasswordEmpty() const
522 return mpImpl
->isPasswordEmpty();
525 bool ScTableProtection::hasPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
) const
527 return mpImpl
->hasPasswordHash(eHash
, eHash2
);
530 void ScTableProtection::setPassword(const String
& aPassText
)
532 mpImpl
->setPassword(aPassText
);
535 Sequence
<sal_Int8
> ScTableProtection::getPasswordHash(ScPasswordHash eHash
, ScPasswordHash eHash2
) const
537 return mpImpl
->getPasswordHash(eHash
, eHash2
);
540 void ScTableProtection::setPasswordHash(
541 const uno::Sequence
<sal_Int8
>& aPassword
, ScPasswordHash eHash
, ScPasswordHash eHash2
)
543 mpImpl
->setPasswordHash(aPassword
, eHash
, eHash2
);
546 bool ScTableProtection::verifyPassword(const String
& aPassText
) const
548 return mpImpl
->verifyPassword(aPassText
);
551 bool ScTableProtection::isOptionEnabled(Option eOption
) const
553 return mpImpl
->isOptionEnabled(eOption
);
556 void ScTableProtection::setOption(Option eOption
, bool bEnabled
)
558 mpImpl
->setOption(eOption
, bEnabled
);