1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sc.hxx" 30 31 // INCLUDE --------------------------------------------------------------- 32 33 #include "tabprotection.hxx" 34 #include "tools/debug.hxx" 35 #include "svl/PasswordHelper.hxx" 36 #include <comphelper/docpasswordhelper.hxx> 37 #include "document.hxx" 38 39 #define DEBUG_TAB_PROTECTION 0 40 41 using namespace ::com::sun::star; 42 using ::com::sun::star::uno::Sequence; 43 using ::rtl::OUString; 44 45 // ============================================================================ 46 47 bool ScPassHashHelper::needsPassHashRegen(const ScDocument& rDoc, ScPasswordHash eHash) 48 { 49 if (rDoc.IsDocProtected()) 50 { 51 const ScDocProtection* p = rDoc.GetDocProtection(); 52 if (!p->isPasswordEmpty() && !p->hasPasswordHash(eHash)) 53 return true; 54 } 55 56 SCTAB nTabCount = rDoc.GetTableCount(); 57 for (SCTAB i = 0; i < nTabCount; ++i) 58 { 59 const ScTableProtection* p = rDoc.GetTabProtection(i); 60 if (!p || !p->isProtected()) 61 // Sheet not protected. Skip it. 62 continue; 63 64 if (!p->isPasswordEmpty() && !p->hasPasswordHash(eHash)) 65 return true; 66 } 67 68 return false; 69 } 70 71 // ============================================================================ 72 73 ScPassHashProtectable::~ScPassHashProtectable() 74 { 75 } 76 77 // ============================================================================ 78 79 class ScTableProtectionImpl 80 { 81 public: 82 static ::com::sun::star::uno::Sequence<sal_Int8> hashPassword(const String& aPassText, ScPasswordHash eHash = PASSHASH_OOO); 83 84 explicit ScTableProtectionImpl(SCSIZE nOptSize); 85 explicit ScTableProtectionImpl(const ScTableProtectionImpl& r); 86 87 bool isProtected() const; 88 bool isProtectedWithPass() const; 89 void setProtected(bool bProtected); 90 91 bool isPasswordEmpty() const; 92 bool hasPasswordHash(ScPasswordHash eHash) const; 93 void setPassword(const String& aPassText); 94 ::com::sun::star::uno::Sequence<sal_Int8> getPasswordHash(ScPasswordHash eHash) const; 95 void setPasswordHash(const ::com::sun::star::uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash = PASSHASH_OOO); 96 bool verifyPassword(const String& aPassText) const; 97 98 bool isOptionEnabled(SCSIZE nOptId) const; 99 void setOption(SCSIZE nOptId, bool bEnabled); 100 101 private: 102 String maPassText; 103 ::com::sun::star::uno::Sequence<sal_Int8> maPassHash; 104 ::std::vector<bool> maOptions; 105 bool mbEmptyPass; 106 bool mbProtected; 107 ScPasswordHash meHash; 108 }; 109 110 Sequence<sal_Int8> ScTableProtectionImpl::hashPassword(const String& aPassText, ScPasswordHash eHash) 111 { 112 Sequence<sal_Int8> aHash; 113 switch (eHash) 114 { 115 case PASSHASH_XL: 116 aHash = ::comphelper::DocPasswordHelper::GetXLHashAsSequence( aPassText, RTL_TEXTENCODING_UTF8 ); 117 break; 118 case PASSHASH_OOO: 119 default: 120 SvPasswordHelper::GetHashPassword(aHash, aPassText); 121 break; 122 } 123 return aHash; 124 } 125 126 ScTableProtectionImpl::ScTableProtectionImpl(SCSIZE nOptSize) : 127 maOptions(nOptSize), 128 mbEmptyPass(true), 129 mbProtected(false), 130 meHash(PASSHASH_OOO) 131 { 132 } 133 134 ScTableProtectionImpl::ScTableProtectionImpl(const ScTableProtectionImpl& r) : 135 maPassText(r.maPassText), 136 maPassHash(r.maPassHash), 137 maOptions(r.maOptions), 138 mbEmptyPass(r.mbEmptyPass), 139 mbProtected(r.mbProtected), 140 meHash(r.meHash) 141 { 142 } 143 144 bool ScTableProtectionImpl::isProtected() const 145 { 146 return mbProtected; 147 } 148 149 bool ScTableProtectionImpl::isProtectedWithPass() const 150 { 151 if (!mbProtected) 152 return false; 153 154 return maPassText.Len() || maPassHash.getLength(); 155 } 156 157 void ScTableProtectionImpl::setProtected(bool bProtected) 158 { 159 mbProtected = bProtected; 160 // We need to keep the old password even when the protection is off. So, 161 // don't erase the password data here. 162 } 163 164 void ScTableProtectionImpl::setPassword(const String& aPassText) 165 { 166 // We can't hash it here because we don't know whether this document will 167 // get saved to Excel or ODF, depending on which we will need to use a 168 // different hashing algorithm. One alternative is to hash it using all 169 // hash algorithms that we support, and store them all. 170 171 maPassText = aPassText; 172 mbEmptyPass = aPassText.Len() == 0; 173 if (mbEmptyPass) 174 { 175 maPassHash = Sequence<sal_Int8>(); 176 } 177 } 178 179 bool ScTableProtectionImpl::isPasswordEmpty() const 180 { 181 return mbEmptyPass; 182 } 183 184 bool ScTableProtectionImpl::hasPasswordHash(ScPasswordHash eHash) const 185 { 186 if (mbEmptyPass) 187 return true; 188 189 if (maPassText.Len()) 190 return true; 191 192 if (meHash == eHash) 193 return true; 194 195 return false; 196 } 197 198 Sequence<sal_Int8> ScTableProtectionImpl::getPasswordHash(ScPasswordHash eHash) const 199 { 200 if (mbEmptyPass) 201 // Flaged as empty. 202 return Sequence<sal_Int8>(); 203 204 if (maPassText.Len()) 205 // Cleartext password exists. Hash it. 206 return hashPassword(maPassText, eHash); 207 208 if (meHash == eHash) 209 // Stored hash exists. 210 return maPassHash; 211 212 // Failed to find a matching hash. 213 return Sequence<sal_Int8>(); 214 } 215 216 void ScTableProtectionImpl::setPasswordHash(const uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash) 217 { 218 sal_Int32 nLen = aPassword.getLength(); 219 mbEmptyPass = nLen <= 0 ? true : false; 220 meHash = eHash; 221 maPassHash = aPassword; 222 223 #if DEBUG_TAB_PROTECTION 224 for (sal_Int32 i = 0; i < nLen; ++i) 225 printf("%2.2X ", static_cast<sal_uInt8>(aPassword[i])); 226 printf("\n"); 227 #endif 228 } 229 230 bool ScTableProtectionImpl::verifyPassword(const String& aPassText) const 231 { 232 #if DEBUG_TAB_PROTECTION 233 fprintf(stdout, "ScTableProtectionImpl::verifyPassword: input = '%s'\n", 234 OUStringToOString(rtl::OUString(aPassText), RTL_TEXTENCODING_UTF8).getStr()); 235 #endif 236 237 if (mbEmptyPass) 238 return aPassText.Len() == 0; 239 240 if (maPassText.Len()) 241 // Clear text password exists, and this one takes precedence. 242 return aPassText.Equals(maPassText); 243 244 Sequence<sal_Int8> aHash = hashPassword(aPassText, meHash); 245 246 #if DEBUG_TAB_PROTECTION 247 fprintf(stdout, "ScTableProtectionImpl::verifyPassword: hash = "); 248 for (sal_Int32 i = 0; i < aHash.getLength(); ++i) 249 printf("%2.2X ", static_cast<sal_uInt8>(aHash[i])); 250 printf("\n"); 251 #endif 252 253 return aHash == maPassHash; 254 } 255 256 bool ScTableProtectionImpl::isOptionEnabled(SCSIZE nOptId) const 257 { 258 if ( maOptions.size() <= static_cast<size_t>(nOptId) ) 259 { 260 DBG_ERROR("ScTableProtectionImpl::isOptionEnabled: wrong size"); 261 return false; 262 } 263 264 return maOptions[nOptId]; 265 } 266 267 void ScTableProtectionImpl::setOption(SCSIZE nOptId, bool bEnabled) 268 { 269 if ( maOptions.size() <= static_cast<size_t>(nOptId) ) 270 { 271 DBG_ERROR("ScTableProtectionImpl::setOption: wrong size"); 272 return; 273 } 274 275 maOptions[nOptId] = bEnabled; 276 } 277 278 // ============================================================================ 279 280 ScDocProtection::ScDocProtection() : 281 mpImpl(new ScTableProtectionImpl(static_cast<SCSIZE>(ScDocProtection::NONE))) 282 { 283 } 284 285 ScDocProtection::ScDocProtection(const ScDocProtection& r) : 286 ScPassHashProtectable(), 287 mpImpl(new ScTableProtectionImpl(*r.mpImpl)) 288 { 289 } 290 291 ScDocProtection::~ScDocProtection() 292 { 293 } 294 295 bool ScDocProtection::isProtected() const 296 { 297 return mpImpl->isProtected(); 298 } 299 300 bool ScDocProtection::isProtectedWithPass() const 301 { 302 return mpImpl->isProtectedWithPass(); 303 } 304 305 void ScDocProtection::setProtected(bool bProtected) 306 { 307 mpImpl->setProtected(bProtected); 308 309 // Currently Calc doesn't support document protection options. So, let's 310 // assume that when the document is protected, its structure is protected. 311 // We need to do this for Excel export. 312 mpImpl->setOption(ScDocProtection::STRUCTURE, bProtected); 313 } 314 315 bool ScDocProtection::isPasswordEmpty() const 316 { 317 return mpImpl->isPasswordEmpty(); 318 } 319 320 bool ScDocProtection::hasPasswordHash(ScPasswordHash eHash) const 321 { 322 return mpImpl->hasPasswordHash(eHash); 323 } 324 325 void ScDocProtection::setPassword(const String& aPassText) 326 { 327 mpImpl->setPassword(aPassText); 328 } 329 330 uno::Sequence<sal_Int8> ScDocProtection::getPasswordHash(ScPasswordHash eHash) const 331 { 332 return mpImpl->getPasswordHash(eHash); 333 } 334 335 void ScDocProtection::setPasswordHash(const uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash) 336 { 337 mpImpl->setPasswordHash(aPassword, eHash); 338 } 339 340 bool ScDocProtection::verifyPassword(const String& aPassText) const 341 { 342 return mpImpl->verifyPassword(aPassText); 343 } 344 345 bool ScDocProtection::isOptionEnabled(Option eOption) const 346 { 347 return mpImpl->isOptionEnabled(eOption); 348 } 349 350 void ScDocProtection::setOption(Option eOption, bool bEnabled) 351 { 352 mpImpl->setOption(eOption, bEnabled); 353 } 354 355 // ============================================================================ 356 357 ScTableProtection::ScTableProtection() : 358 mpImpl(new ScTableProtectionImpl(static_cast<SCSIZE>(ScTableProtection::NONE))) 359 { 360 // Set default values for the options. 361 mpImpl->setOption(SELECT_LOCKED_CELLS, true); 362 mpImpl->setOption(SELECT_UNLOCKED_CELLS, true); 363 } 364 365 ScTableProtection::ScTableProtection(const ScTableProtection& r) : 366 ScPassHashProtectable(), 367 mpImpl(new ScTableProtectionImpl(*r.mpImpl)) 368 { 369 } 370 371 ScTableProtection::~ScTableProtection() 372 { 373 } 374 375 bool ScTableProtection::isProtected() const 376 { 377 return mpImpl->isProtected(); 378 } 379 380 bool ScTableProtection::isProtectedWithPass() const 381 { 382 return mpImpl->isProtectedWithPass(); 383 } 384 385 void ScTableProtection::setProtected(bool bProtected) 386 { 387 mpImpl->setProtected(bProtected); 388 } 389 390 bool ScTableProtection::isPasswordEmpty() const 391 { 392 return mpImpl->isPasswordEmpty(); 393 } 394 395 bool ScTableProtection::hasPasswordHash(ScPasswordHash eHash) const 396 { 397 return mpImpl->hasPasswordHash(eHash); 398 } 399 400 void ScTableProtection::setPassword(const String& aPassText) 401 { 402 mpImpl->setPassword(aPassText); 403 } 404 405 Sequence<sal_Int8> ScTableProtection::getPasswordHash(ScPasswordHash eHash) const 406 { 407 return mpImpl->getPasswordHash(eHash); 408 } 409 410 void ScTableProtection::setPasswordHash(const uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash) 411 { 412 mpImpl->setPasswordHash(aPassword, eHash); 413 } 414 415 bool ScTableProtection::verifyPassword(const String& aPassText) const 416 { 417 return mpImpl->verifyPassword(aPassText); 418 } 419 420 bool ScTableProtection::isOptionEnabled(Option eOption) const 421 { 422 return mpImpl->isOptionEnabled(eOption); 423 } 424 425 void ScTableProtection::setOption(Option eOption, bool bEnabled) 426 { 427 mpImpl->setOption(eOption, bEnabled); 428 } 429 430