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