/src/xpdf-4.06/xpdf/SecurityHandler.cc
Line | Count | Source |
1 | | //======================================================================== |
2 | | // |
3 | | // SecurityHandler.cc |
4 | | // |
5 | | // Copyright 2004 Glyph & Cog, LLC |
6 | | // |
7 | | //======================================================================== |
8 | | |
9 | | #include <aconf.h> |
10 | | |
11 | | #include "gmempp.h" |
12 | | #include "GString.h" |
13 | | #include "PDFDoc.h" |
14 | | #include "Decrypt.h" |
15 | | #include "Error.h" |
16 | | #include "GlobalParams.h" |
17 | | #include "PDFCore.h" |
18 | | #include "SecurityHandler.h" |
19 | | |
20 | | //------------------------------------------------------------------------ |
21 | | // SecurityHandler |
22 | | //------------------------------------------------------------------------ |
23 | | |
24 | 665 | SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) { |
25 | 665 | Object filterObj; |
26 | 665 | SecurityHandler *secHdlr; |
27 | | |
28 | 665 | encryptDictA->dictLookup("Filter", &filterObj); |
29 | 665 | if (filterObj.isName("Standard")) { |
30 | 663 | secHdlr = new StandardSecurityHandler(docA, encryptDictA); |
31 | 663 | } else if (filterObj.isName()) { |
32 | 0 | error(errSyntaxError, -1, "Couldn't find the '{0:s}' security handler", |
33 | 0 | filterObj.getName()); |
34 | 0 | secHdlr = NULL; |
35 | 2 | } else { |
36 | 2 | error(errSyntaxError, -1, |
37 | 2 | "Missing or invalid 'Filter' entry in encryption dictionary"); |
38 | 2 | secHdlr = NULL; |
39 | 2 | } |
40 | 665 | filterObj.free(); |
41 | 665 | return secHdlr; |
42 | 665 | } |
43 | | |
44 | 663 | SecurityHandler::SecurityHandler(PDFDoc *docA) { |
45 | 663 | doc = docA; |
46 | 663 | } |
47 | | |
48 | 663 | SecurityHandler::~SecurityHandler() { |
49 | 663 | } |
50 | | |
51 | | GBool SecurityHandler::checkEncryption(GString *ownerPassword, |
52 | 657 | GString *userPassword) { |
53 | 657 | void *authData; |
54 | 657 | GBool ok; |
55 | 657 | int i; |
56 | | |
57 | 657 | if (ownerPassword || userPassword) { |
58 | 0 | authData = makeAuthData(ownerPassword, userPassword); |
59 | 657 | } else { |
60 | 657 | authData = NULL; |
61 | 657 | } |
62 | 657 | ok = authorize(authData); |
63 | 657 | if (authData) { |
64 | 0 | freeAuthData(authData); |
65 | 0 | } |
66 | 657 | for (i = 0; !ok && i < 3; ++i) { |
67 | 25 | if (!(authData = getAuthData())) { |
68 | 25 | break; |
69 | 25 | } |
70 | 0 | ok = authorize(authData); |
71 | 0 | if (authData) { |
72 | 0 | freeAuthData(authData); |
73 | 0 | } |
74 | 0 | } |
75 | 657 | if (!ok) { |
76 | 25 | error(errCommandLine, -1, "Incorrect password"); |
77 | 25 | } |
78 | 657 | return ok; |
79 | 657 | } |
80 | | |
81 | | //------------------------------------------------------------------------ |
82 | | // StandardSecurityHandler |
83 | | //------------------------------------------------------------------------ |
84 | | |
85 | | class StandardAuthData { |
86 | | public: |
87 | | |
88 | 0 | StandardAuthData(GString *ownerPasswordA, GString *userPasswordA) { |
89 | 0 | ownerPassword = ownerPasswordA; |
90 | 0 | userPassword = userPasswordA; |
91 | 0 | } |
92 | | |
93 | 0 | ~StandardAuthData() { |
94 | 0 | if (ownerPassword) { |
95 | 0 | delete ownerPassword; |
96 | 0 | } |
97 | 0 | if (userPassword) { |
98 | 0 | delete userPassword; |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | GString *ownerPassword; |
103 | | GString *userPassword; |
104 | | }; |
105 | | |
106 | | StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA, |
107 | | Object *encryptDictA): |
108 | 663 | SecurityHandler(docA) |
109 | 663 | { |
110 | 663 | Object versionObj, revisionObj, lengthObj; |
111 | 663 | Object ownerKeyObj, userKeyObj, ownerEncObj, userEncObj; |
112 | 663 | Object permObj, fileIDObj, fileIDObj1; |
113 | 663 | Object cryptFiltersObj, streamFilterObj, stringFilterObj; |
114 | 663 | Object cryptFilterObj, cfmObj, cfLengthObj; |
115 | 663 | Object encryptMetadataObj; |
116 | | |
117 | 663 | ok = gFalse; |
118 | 663 | fileID = NULL; |
119 | 663 | ownerKey = NULL; |
120 | 663 | userKey = NULL; |
121 | 663 | ownerEnc = NULL; |
122 | 663 | userEnc = NULL; |
123 | 663 | fileKeyLength = 0; |
124 | 663 | encVersion = -1; |
125 | 663 | encRevision = -1; |
126 | | |
127 | | //--- get the main parameters |
128 | 663 | encryptDictA->dictLookup("V", &versionObj); |
129 | 663 | encryptDictA->dictLookup("R", &revisionObj); |
130 | 663 | encryptDictA->dictLookup("Length", &lengthObj); |
131 | 663 | encryptDictA->dictLookup("O", &ownerKeyObj); |
132 | 663 | encryptDictA->dictLookup("U", &userKeyObj); |
133 | 663 | encryptDictA->dictLookup("OE", &ownerEncObj); |
134 | 663 | encryptDictA->dictLookup("UE", &userEncObj); |
135 | 663 | encryptDictA->dictLookup("P", &permObj); |
136 | 663 | doc->getXRef()->getTrailerDict()->dictLookup("ID", &fileIDObj); |
137 | 663 | if (!versionObj.isInt() || |
138 | 663 | !revisionObj.isInt() || |
139 | 662 | !permObj.isInt() || |
140 | 658 | !ownerKeyObj.isString() || |
141 | 657 | !userKeyObj.isString()) { |
142 | 6 | error(errSyntaxError, -1, "Invalid encryption parameters"); |
143 | 6 | goto done; |
144 | 6 | } |
145 | 657 | encVersion = versionObj.getInt(); |
146 | 657 | encRevision = revisionObj.getInt(); |
147 | 657 | encAlgorithm = cryptRC4; |
148 | | // revision 2 forces a 40-bit key - some buggy PDF generators |
149 | | // set the Length value incorrectly |
150 | 657 | if (encRevision == 2 || !lengthObj.isInt()) { |
151 | 589 | fileKeyLength = 5; |
152 | 589 | } else { |
153 | 68 | fileKeyLength = lengthObj.getInt() / 8; |
154 | 68 | } |
155 | 657 | encryptMetadata = gTrue; |
156 | | |
157 | | //--- check for a crypt filter (which can modify the parameters) |
158 | | //~ this currently only handles a subset of crypt filter functionality |
159 | | //~ (in particular, it ignores the EFF entry in encryptDictA, and |
160 | | //~ doesn't handle the case where StmF, StrF, and EFF are not all the |
161 | | //~ same) |
162 | 657 | if ((encVersion == 4 || encVersion == 5) && |
163 | 119 | (encRevision == 4 || encRevision == 5 || encRevision == 6)) { |
164 | 119 | encryptDictA->dictLookup("CF", &cryptFiltersObj); |
165 | 119 | encryptDictA->dictLookup("StmF", &streamFilterObj); |
166 | 119 | encryptDictA->dictLookup("StrF", &stringFilterObj); |
167 | 119 | if (cryptFiltersObj.isDict() && |
168 | 108 | streamFilterObj.isName() && |
169 | 108 | stringFilterObj.isName() && |
170 | 107 | !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) { |
171 | 100 | if (!strcmp(streamFilterObj.getName(), "Identity")) { |
172 | | // no encryption on streams or strings |
173 | 0 | stringFilterObj.free(); |
174 | 0 | streamFilterObj.free(); |
175 | 0 | cryptFiltersObj.free(); |
176 | 0 | goto done; |
177 | 0 | } |
178 | 100 | if (cryptFiltersObj.dictLookup(streamFilterObj.getName(), |
179 | 100 | &cryptFilterObj)->isDict()) { |
180 | 86 | cryptFilterObj.dictLookup("CFM", &cfmObj); |
181 | 86 | if (cfmObj.isName("V2")) { |
182 | 1 | if (cryptFilterObj.dictLookup("Length", |
183 | 1 | &cfLengthObj)->isInt()) { |
184 | 0 | fileKeyLength = cfLengthObj.getInt(); |
185 | 0 | } |
186 | 1 | cfLengthObj.free(); |
187 | 1 | encVersion = 2; |
188 | 1 | encRevision = 3; |
189 | 85 | } else if (cfmObj.isName("AESV2")) { |
190 | 45 | if (cryptFilterObj.dictLookup("Length", |
191 | 45 | &cfLengthObj)->isInt()) { |
192 | 19 | fileKeyLength = cfLengthObj.getInt(); |
193 | 19 | } |
194 | 45 | cfLengthObj.free(); |
195 | 45 | encVersion = 2; |
196 | 45 | encRevision = 3; |
197 | 45 | encAlgorithm = cryptAES; |
198 | 45 | } else if (cfmObj.isName("AESV3")) { |
199 | 34 | if (cryptFilterObj.dictLookup("Length", |
200 | 34 | &cfLengthObj)->isInt()) { |
201 | 8 | fileKeyLength = cfLengthObj.getInt(); |
202 | 8 | } |
203 | 34 | cfLengthObj.free(); |
204 | 34 | encVersion = 5; |
205 | 34 | if (encRevision != 5 && encRevision != 6) { |
206 | 0 | encRevision = 6; |
207 | 0 | } |
208 | 34 | encAlgorithm = cryptAES256; |
209 | | // The PDF 2.0 spec says Length and CF.Length are both deprecated. |
210 | | // Acrobat X honors Length and ignores CF.Length. |
211 | | // I think it's safest to ignore both. |
212 | 34 | fileKeyLength = 32; |
213 | 34 | } |
214 | 86 | cfmObj.free(); |
215 | 86 | } |
216 | 100 | cryptFilterObj.free(); |
217 | 100 | } |
218 | 119 | stringFilterObj.free(); |
219 | 119 | streamFilterObj.free(); |
220 | 119 | cryptFiltersObj.free(); |
221 | 119 | if (encryptDictA->dictLookup("EncryptMetadata", |
222 | 119 | &encryptMetadataObj)->isBool()) { |
223 | 0 | encryptMetadata = encryptMetadataObj.getBool(); |
224 | 0 | } |
225 | 119 | encryptMetadataObj.free(); |
226 | 119 | } |
227 | | |
228 | | //--- version-specific parameters |
229 | 657 | if (encRevision <= 4) { |
230 | 585 | if (ownerKeyObj.getString()->getLength() != 32 || |
231 | 565 | userKeyObj.getString()->getLength() != 32) { |
232 | 31 | error(errSyntaxError, -1, "Invalid encryption key length"); |
233 | | // this is non-fatal -- see below |
234 | 31 | } |
235 | 585 | } else if (encRevision <= 6) { |
236 | | // the spec says 48 bytes, but Acrobat pads them out longer |
237 | 72 | if (ownerKeyObj.getString()->getLength() < 48 || |
238 | 71 | userKeyObj.getString()->getLength() < 48 || |
239 | 71 | !ownerEncObj.isString() || |
240 | 71 | ownerEncObj.getString()->getLength() != 32 || |
241 | 71 | !userEncObj.isString() || |
242 | 71 | userEncObj.getString()->getLength() != 32) { |
243 | 1 | error(errSyntaxError, -1, "Invalid encryption key length"); |
244 | 1 | goto done; |
245 | 1 | } |
246 | 72 | } |
247 | 656 | permFlags = permObj.getInt(); |
248 | 656 | ownerKey = ownerKeyObj.getString()->copy(); |
249 | 656 | userKey = userKeyObj.getString()->copy(); |
250 | 656 | if (encRevision <= 4) { |
251 | | // Adobe apparently zero-pads the U value (and maybe the O value?) |
252 | | // if it's short |
253 | 654 | while (ownerKey->getLength() < 32) { |
254 | 69 | ownerKey->append((char)0x00); |
255 | 69 | } |
256 | 674 | while (userKey->getLength() < 32) { |
257 | 89 | userKey->append((char)0x00); |
258 | 89 | } |
259 | 585 | } |
260 | 656 | if (encVersion >= 1 && encVersion <= 2 && |
261 | 584 | encRevision >= 2 && encRevision <= 3) { |
262 | 584 | if (fileIDObj.isArray()) { |
263 | 584 | if (fileIDObj.arrayGet(0, &fileIDObj1)->isString()) { |
264 | 583 | fileID = fileIDObj1.getString()->copy(); |
265 | 583 | } else { |
266 | 1 | fileID = new GString(); |
267 | 1 | } |
268 | 584 | fileIDObj1.free(); |
269 | 584 | } else { |
270 | 0 | fileID = new GString(); |
271 | 0 | } |
272 | 584 | if (fileKeyLength > 16 || fileKeyLength <= 0) { |
273 | 17 | fileKeyLength = 16; |
274 | 17 | } |
275 | 584 | ok = gTrue; |
276 | 584 | } else if (encVersion == 5 && (encRevision == 5 || encRevision == 6)) { |
277 | 71 | fileID = new GString(); // unused for V=R=5 |
278 | 71 | ownerEnc = ownerEncObj.getString()->copy(); |
279 | 71 | userEnc = userEncObj.getString()->copy(); |
280 | 71 | if (fileKeyLength > 32 || fileKeyLength <= 0) { |
281 | 0 | fileKeyLength = 32; |
282 | 0 | } |
283 | 71 | ok = gTrue; |
284 | 71 | } else { |
285 | 1 | error(errUnimplemented, -1, |
286 | 1 | "Unsupported version/revision ({0:d}/{1:d}) of Standard security handler", |
287 | 1 | encVersion, encRevision); |
288 | 1 | } |
289 | | |
290 | 663 | done: |
291 | 663 | fileIDObj.free(); |
292 | 663 | permObj.free(); |
293 | 663 | userEncObj.free(); |
294 | 663 | ownerEncObj.free(); |
295 | 663 | userKeyObj.free(); |
296 | 663 | ownerKeyObj.free(); |
297 | 663 | lengthObj.free(); |
298 | 663 | revisionObj.free(); |
299 | 663 | versionObj.free(); |
300 | 663 | } |
301 | | |
302 | 663 | StandardSecurityHandler::~StandardSecurityHandler() { |
303 | 663 | if (fileID) { |
304 | 655 | delete fileID; |
305 | 655 | } |
306 | 663 | if (ownerKey) { |
307 | 656 | delete ownerKey; |
308 | 656 | } |
309 | 663 | if (userKey) { |
310 | 656 | delete userKey; |
311 | 656 | } |
312 | 663 | if (ownerEnc) { |
313 | 71 | delete ownerEnc; |
314 | 71 | } |
315 | 663 | if (userEnc) { |
316 | 71 | delete userEnc; |
317 | 71 | } |
318 | 663 | } |
319 | | |
320 | 663 | GBool StandardSecurityHandler::isUnencrypted() { |
321 | 663 | return encVersion == -1 && encRevision == -1; |
322 | 663 | } |
323 | | |
324 | | void *StandardSecurityHandler::makeAuthData(GString *ownerPassword, |
325 | 0 | GString *userPassword) { |
326 | 0 | return new StandardAuthData(ownerPassword ? ownerPassword->copy() |
327 | 0 | : (GString *)NULL, |
328 | 0 | userPassword ? userPassword->copy() |
329 | 0 | : (GString *)NULL); |
330 | 0 | } |
331 | | |
332 | 25 | void *StandardSecurityHandler::getAuthData() { |
333 | 25 | PDFCore *core; |
334 | 25 | GString *password; |
335 | | |
336 | 25 | if (!(core = doc->getCore()) || |
337 | 25 | !(password = core->getPassword())) { |
338 | 25 | return NULL; |
339 | 25 | } |
340 | 0 | return new StandardAuthData(password, password->copy()); |
341 | 25 | } |
342 | | |
343 | 0 | void StandardSecurityHandler::freeAuthData(void *authData) { |
344 | 0 | delete (StandardAuthData *)authData; |
345 | 0 | } |
346 | | |
347 | 657 | GBool StandardSecurityHandler::authorize(void *authData) { |
348 | 657 | GString *ownerPassword, *userPassword; |
349 | | |
350 | 657 | if (!ok) { |
351 | 2 | return gFalse; |
352 | 2 | } |
353 | 655 | if (authData) { |
354 | 0 | ownerPassword = ((StandardAuthData *)authData)->ownerPassword; |
355 | 0 | userPassword = ((StandardAuthData *)authData)->userPassword; |
356 | 655 | } else { |
357 | 655 | ownerPassword = NULL; |
358 | 655 | userPassword = NULL; |
359 | 655 | } |
360 | 655 | if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength, |
361 | 655 | ownerKey, userKey, ownerEnc, userEnc, |
362 | 655 | permFlags, fileID, |
363 | 655 | ownerPassword, userPassword, fileKey, |
364 | 655 | encryptMetadata, &ownerPasswordOk)) { |
365 | 23 | return gFalse; |
366 | 23 | } |
367 | 632 | return gTrue; |
368 | 655 | } |