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
needsPassHashRegen(const ScDocument & rDoc,ScPasswordHash eHash)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
~ScPassHashProtectable()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
hashPassword(const String & aPassText,ScPasswordHash eHash)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
ScTableProtectionImpl(SCSIZE nOptSize)122 ScTableProtectionImpl::ScTableProtectionImpl(SCSIZE nOptSize) :
123 maOptions(nOptSize),
124 mbEmptyPass(true),
125 mbProtected(false),
126 meHash(PASSHASH_OOO)
127 {
128 }
129
ScTableProtectionImpl(const ScTableProtectionImpl & r)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
isProtected() const140 bool ScTableProtectionImpl::isProtected() const
141 {
142 return mbProtected;
143 }
144
isProtectedWithPass() const145 bool ScTableProtectionImpl::isProtectedWithPass() const
146 {
147 if (!mbProtected)
148 return false;
149
150 return maPassText.Len() || maPassHash.getLength();
151 }
152
setProtected(bool bProtected)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
setPassword(const String & aPassText)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
isPasswordEmpty() const175 bool ScTableProtectionImpl::isPasswordEmpty() const
176 {
177 return mbEmptyPass;
178 }
179
hasPasswordHash(ScPasswordHash eHash) const180 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
getPasswordHash(ScPasswordHash eHash) const194 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
setPasswordHash(const uno::Sequence<sal_Int8> & aPassword,ScPasswordHash eHash)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
verifyPassword(const String & aPassText) const226 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
isOptionEnabled(SCSIZE nOptId) const252 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
setOption(SCSIZE nOptId,bool bEnabled)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
ScDocProtection()276 ScDocProtection::ScDocProtection() :
277 mpImpl(new ScTableProtectionImpl(static_cast<SCSIZE>(ScDocProtection::NONE)))
278 {
279 }
280
ScDocProtection(const ScDocProtection & r)281 ScDocProtection::ScDocProtection(const ScDocProtection& r) :
282 ScPassHashProtectable(),
283 mpImpl(new ScTableProtectionImpl(*r.mpImpl))
284 {
285 }
286
~ScDocProtection()287 ScDocProtection::~ScDocProtection()
288 {
289 }
290
isProtected() const291 bool ScDocProtection::isProtected() const
292 {
293 return mpImpl->isProtected();
294 }
295
isProtectedWithPass() const296 bool ScDocProtection::isProtectedWithPass() const
297 {
298 return mpImpl->isProtectedWithPass();
299 }
300
setProtected(bool bProtected)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
isPasswordEmpty() const311 bool ScDocProtection::isPasswordEmpty() const
312 {
313 return mpImpl->isPasswordEmpty();
314 }
315
hasPasswordHash(ScPasswordHash eHash) const316 bool ScDocProtection::hasPasswordHash(ScPasswordHash eHash) const
317 {
318 return mpImpl->hasPasswordHash(eHash);
319 }
320
setPassword(const String & aPassText)321 void ScDocProtection::setPassword(const String& aPassText)
322 {
323 mpImpl->setPassword(aPassText);
324 }
325
getPasswordHash(ScPasswordHash eHash) const326 uno::Sequence<sal_Int8> ScDocProtection::getPasswordHash(ScPasswordHash eHash) const
327 {
328 return mpImpl->getPasswordHash(eHash);
329 }
330
setPasswordHash(const uno::Sequence<sal_Int8> & aPassword,ScPasswordHash eHash)331 void ScDocProtection::setPasswordHash(const uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash)
332 {
333 mpImpl->setPasswordHash(aPassword, eHash);
334 }
335
verifyPassword(const String & aPassText) const336 bool ScDocProtection::verifyPassword(const String& aPassText) const
337 {
338 return mpImpl->verifyPassword(aPassText);
339 }
340
isOptionEnabled(Option eOption) const341 bool ScDocProtection::isOptionEnabled(Option eOption) const
342 {
343 return mpImpl->isOptionEnabled(eOption);
344 }
345
setOption(Option eOption,bool bEnabled)346 void ScDocProtection::setOption(Option eOption, bool bEnabled)
347 {
348 mpImpl->setOption(eOption, bEnabled);
349 }
350
351 // ============================================================================
352
ScTableProtection()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
ScTableProtection(const ScTableProtection & r)361 ScTableProtection::ScTableProtection(const ScTableProtection& r) :
362 ScPassHashProtectable(),
363 mpImpl(new ScTableProtectionImpl(*r.mpImpl))
364 {
365 }
366
~ScTableProtection()367 ScTableProtection::~ScTableProtection()
368 {
369 }
370
isProtected() const371 bool ScTableProtection::isProtected() const
372 {
373 return mpImpl->isProtected();
374 }
375
isProtectedWithPass() const376 bool ScTableProtection::isProtectedWithPass() const
377 {
378 return mpImpl->isProtectedWithPass();
379 }
380
setProtected(bool bProtected)381 void ScTableProtection::setProtected(bool bProtected)
382 {
383 mpImpl->setProtected(bProtected);
384 }
385
isPasswordEmpty() const386 bool ScTableProtection::isPasswordEmpty() const
387 {
388 return mpImpl->isPasswordEmpty();
389 }
390
hasPasswordHash(ScPasswordHash eHash) const391 bool ScTableProtection::hasPasswordHash(ScPasswordHash eHash) const
392 {
393 return mpImpl->hasPasswordHash(eHash);
394 }
395
setPassword(const String & aPassText)396 void ScTableProtection::setPassword(const String& aPassText)
397 {
398 mpImpl->setPassword(aPassText);
399 }
400
getPasswordHash(ScPasswordHash eHash) const401 Sequence<sal_Int8> ScTableProtection::getPasswordHash(ScPasswordHash eHash) const
402 {
403 return mpImpl->getPasswordHash(eHash);
404 }
405
setPasswordHash(const uno::Sequence<sal_Int8> & aPassword,ScPasswordHash eHash)406 void ScTableProtection::setPasswordHash(const uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash)
407 {
408 mpImpl->setPasswordHash(aPassword, eHash);
409 }
410
verifyPassword(const String & aPassText) const411 bool ScTableProtection::verifyPassword(const String& aPassText) const
412 {
413 return mpImpl->verifyPassword(aPassText);
414 }
415
isOptionEnabled(Option eOption) const416 bool ScTableProtection::isOptionEnabled(Option eOption) const
417 {
418 return mpImpl->isOptionEnabled(eOption);
419 }
420
setOption(Option eOption,bool bEnabled)421 void ScTableProtection::setOption(Option eOption, bool bEnabled)
422 {
423 mpImpl->setOption(eOption, bEnabled);
424 }
425
426