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