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