]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | //======================================================================== |
2 | // | |
3 | // SecurityHandler.cc | |
4 | // | |
5 | // Copyright 2004 Glyph & Cog, LLC | |
6 | // | |
7 | //======================================================================== | |
8 | ||
9 | #include <config.h> | |
10 | ||
11 | #ifdef USE_GCC_PRAGMAS | |
12 | #pragma implementation | |
13 | #endif | |
14 | ||
15 | #include "GString.h" | |
16 | #include "PDFDoc.h" | |
17 | #include "Decrypt.h" | |
18 | #include "Error.h" | |
19 | #include "GlobalParams.h" | |
20 | #if HAVE_XPDFCORE | |
21 | # include "XPDFCore.h" | |
22 | #elif HAVE_WINPDFCORE | |
23 | # include "WinPDFCore.h" | |
24 | #endif | |
25 | #ifdef ENABLE_PLUGINS | |
26 | # include "XpdfPluginAPI.h" | |
27 | #endif | |
28 | #include "SecurityHandler.h" | |
29 | ||
30 | //------------------------------------------------------------------------ | |
31 | // SecurityHandler | |
32 | //------------------------------------------------------------------------ | |
33 | ||
34 | SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) { | |
35 | Object filterObj; | |
36 | SecurityHandler *secHdlr; | |
37 | #ifdef ENABLE_PLUGINS | |
38 | XpdfSecurityHandler *xsh; | |
39 | #endif | |
40 | ||
41 | encryptDictA->dictLookup("Filter", &filterObj); | |
42 | if (filterObj.isName("Standard")) { | |
43 | secHdlr = new StandardSecurityHandler(docA, encryptDictA); | |
44 | } else if (filterObj.isName()) { | |
45 | #ifdef ENABLE_PLUGINS | |
46 | if ((xsh = globalParams->getSecurityHandler(filterObj.getName()))) { | |
47 | secHdlr = new ExternalSecurityHandler(docA, encryptDictA, xsh); | |
48 | } else { | |
49 | #endif | |
50 | error(-1, "Couldn't find the '%s' security handler", | |
51 | filterObj.getName()); | |
52 | secHdlr = NULL; | |
53 | #ifdef ENABLE_PLUGINS | |
54 | } | |
55 | #endif | |
56 | } else { | |
57 | error(-1, "Missing or invalid 'Filter' entry in encryption dictionary"); | |
58 | secHdlr = NULL; | |
59 | } | |
60 | filterObj.free(); | |
61 | return secHdlr; | |
62 | } | |
63 | ||
64 | SecurityHandler::SecurityHandler(PDFDoc *docA) { | |
65 | doc = docA; | |
66 | } | |
67 | ||
68 | SecurityHandler::~SecurityHandler() { | |
69 | } | |
70 | ||
71 | GBool SecurityHandler::checkEncryption(GString *ownerPassword, | |
72 | GString *userPassword) { | |
73 | void *authData; | |
74 | GBool ok; | |
75 | int i; | |
76 | ||
77 | if (ownerPassword || userPassword) { | |
78 | authData = makeAuthData(ownerPassword, userPassword); | |
79 | } else { | |
80 | authData = NULL; | |
81 | } | |
82 | ok = authorize(authData); | |
83 | if (authData) { | |
84 | freeAuthData(authData); | |
85 | } | |
86 | for (i = 0; !ok && i < 3; ++i) { | |
87 | if (!(authData = getAuthData())) { | |
88 | break; | |
89 | } | |
90 | ok = authorize(authData); | |
91 | if (authData) { | |
92 | freeAuthData(authData); | |
93 | } | |
94 | } | |
95 | if (!ok) { | |
96 | error(-1, "Incorrect password"); | |
97 | } | |
98 | return ok; | |
99 | } | |
100 | ||
101 | //------------------------------------------------------------------------ | |
102 | // StandardSecurityHandler | |
103 | //------------------------------------------------------------------------ | |
104 | ||
105 | class StandardAuthData { | |
106 | public: | |
107 | ||
108 | StandardAuthData(GString *ownerPasswordA, GString *userPasswordA) { | |
109 | ownerPassword = ownerPasswordA; | |
110 | userPassword = userPasswordA; | |
111 | } | |
112 | ||
113 | ~StandardAuthData() { | |
114 | if (ownerPassword) { | |
115 | delete ownerPassword; | |
116 | } | |
117 | if (userPassword) { | |
118 | delete userPassword; | |
119 | } | |
120 | } | |
121 | ||
122 | GString *ownerPassword; | |
123 | GString *userPassword; | |
124 | }; | |
125 | ||
126 | StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA, | |
127 | Object *encryptDictA): | |
128 | SecurityHandler(docA) | |
129 | { | |
130 | Object versionObj, revisionObj, lengthObj; | |
131 | Object ownerKeyObj, userKeyObj, permObj, fileIDObj; | |
132 | Object fileIDObj1; | |
133 | Object cryptFiltersObj, streamFilterObj, stringFilterObj; | |
134 | Object cryptFilterObj, cfmObj, cfLengthObj; | |
135 | Object encryptMetadataObj; | |
136 | ||
137 | ok = gFalse; | |
138 | fileID = NULL; | |
139 | ownerKey = NULL; | |
140 | userKey = NULL; | |
141 | ||
142 | encryptDictA->dictLookup("V", &versionObj); | |
143 | encryptDictA->dictLookup("R", &revisionObj); | |
144 | encryptDictA->dictLookup("Length", &lengthObj); | |
145 | encryptDictA->dictLookup("O", &ownerKeyObj); | |
146 | encryptDictA->dictLookup("U", &userKeyObj); | |
147 | encryptDictA->dictLookup("P", &permObj); | |
148 | doc->getXRef()->getTrailerDict()->dictLookup("ID", &fileIDObj); | |
149 | if (versionObj.isInt() && | |
150 | revisionObj.isInt() && | |
151 | ownerKeyObj.isString() && ownerKeyObj.getString()->getLength() == 32 && | |
152 | userKeyObj.isString() && userKeyObj.getString()->getLength() == 32 && | |
153 | permObj.isInt()) { | |
154 | encVersion = versionObj.getInt(); | |
155 | encRevision = revisionObj.getInt(); | |
156 | // revision 2 forces a 40-bit key - some buggy PDF generators | |
157 | // set the Length value incorrectly | |
158 | if (encRevision == 2 || !lengthObj.isInt()) { | |
159 | fileKeyLength = 5; | |
160 | } else { | |
161 | fileKeyLength = lengthObj.getInt() / 8; | |
162 | } | |
163 | encryptMetadata = gTrue; | |
164 | //~ this currently only handles a subset of crypt filter functionality | |
165 | if (encVersion == 4 && encRevision == 4) { | |
166 | encryptDictA->dictLookup("CF", &cryptFiltersObj); | |
167 | encryptDictA->dictLookup("StmF", &streamFilterObj); | |
168 | encryptDictA->dictLookup("StrF", &stringFilterObj); | |
169 | if (cryptFiltersObj.isDict() && | |
170 | streamFilterObj.isName() && | |
171 | stringFilterObj.isName() && | |
172 | !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) { | |
173 | if (cryptFiltersObj.dictLookup(streamFilterObj.getName(), | |
174 | &cryptFilterObj)->isDict()) { | |
175 | if (cryptFilterObj.dictLookup("CFM", &cfmObj)->isName("V2")) { | |
176 | encVersion = 2; | |
177 | encRevision = 3; | |
178 | if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) { | |
179 | //~ according to the spec, this should be cfLengthObj / 8 | |
180 | fileKeyLength = cfLengthObj.getInt(); | |
181 | } | |
182 | cfLengthObj.free(); | |
183 | } | |
184 | cfmObj.free(); | |
185 | } | |
186 | cryptFilterObj.free(); | |
187 | } | |
188 | stringFilterObj.free(); | |
189 | streamFilterObj.free(); | |
190 | cryptFiltersObj.free(); | |
191 | if (encryptDictA->dictLookup("EncryptMetadata", | |
192 | &encryptMetadataObj)->isBool()) { | |
193 | encryptMetadata = encryptMetadataObj.getBool(); | |
194 | } | |
195 | encryptMetadataObj.free(); | |
196 | } | |
197 | permFlags = permObj.getInt(); | |
198 | ownerKey = ownerKeyObj.getString()->copy(); | |
199 | userKey = userKeyObj.getString()->copy(); | |
200 | if (encVersion >= 1 && encVersion <= 2 && | |
201 | encRevision >= 2 && encRevision <= 3) { | |
202 | if (fileIDObj.isArray()) { | |
203 | if (fileIDObj.arrayGet(0, &fileIDObj1)->isString()) { | |
204 | fileID = fileIDObj1.getString()->copy(); | |
205 | } else { | |
206 | fileID = new GString(); | |
207 | } | |
208 | fileIDObj1.free(); | |
209 | } else { | |
210 | fileID = new GString(); | |
211 | } | |
212 | ok = gTrue; | |
213 | } else { | |
214 | error(-1, "Unsupported version/revision (%d/%d) of Standard security handler", | |
215 | encVersion, encRevision); | |
216 | } | |
217 | } else { | |
218 | error(-1, "Weird encryption info"); | |
219 | } | |
220 | if (fileKeyLength > 16) { | |
221 | fileKeyLength = 16; | |
222 | } | |
223 | fileIDObj.free(); | |
224 | permObj.free(); | |
225 | userKeyObj.free(); | |
226 | ownerKeyObj.free(); | |
227 | lengthObj.free(); | |
228 | revisionObj.free(); | |
229 | versionObj.free(); | |
230 | } | |
231 | ||
232 | StandardSecurityHandler::~StandardSecurityHandler() { | |
233 | if (fileID) { | |
234 | delete fileID; | |
235 | } | |
236 | if (ownerKey) { | |
237 | delete ownerKey; | |
238 | } | |
239 | if (userKey) { | |
240 | delete userKey; | |
241 | } | |
242 | } | |
243 | ||
244 | void *StandardSecurityHandler::makeAuthData(GString *ownerPassword, | |
245 | GString *userPassword) { | |
246 | return new StandardAuthData(ownerPassword ? ownerPassword->copy() | |
247 | : (GString *)NULL, | |
248 | userPassword ? userPassword->copy() | |
249 | : (GString *)NULL); | |
250 | } | |
251 | ||
252 | void *StandardSecurityHandler::getAuthData() { | |
253 | #if HAVE_XPDFCORE | |
254 | XPDFCore *core; | |
255 | GString *password; | |
256 | ||
257 | if (!(core = (XPDFCore *)doc->getGUIData()) || | |
258 | !(password = core->getPassword())) { | |
259 | return NULL; | |
260 | } | |
261 | return new StandardAuthData(password, password->copy()); | |
262 | #elif HAVE_WINPDFCORE | |
263 | WinPDFCore *core; | |
264 | GString *password; | |
265 | ||
266 | if (!(core = (WinPDFCore *)doc->getGUIData()) || | |
267 | !(password = core->getPassword())) { | |
268 | return NULL; | |
269 | } | |
270 | return new StandardAuthData(password, password->copy()); | |
271 | #else | |
272 | return NULL; | |
273 | #endif | |
274 | } | |
275 | ||
276 | void StandardSecurityHandler::freeAuthData(void *authData) { | |
277 | delete (StandardAuthData *)authData; | |
278 | } | |
279 | ||
280 | GBool StandardSecurityHandler::authorize(void *authData) { | |
281 | GString *ownerPassword, *userPassword; | |
282 | ||
283 | if (!ok) { | |
284 | return gFalse; | |
285 | } | |
286 | if (authData) { | |
287 | ownerPassword = ((StandardAuthData *)authData)->ownerPassword; | |
288 | userPassword = ((StandardAuthData *)authData)->userPassword; | |
289 | } else { | |
290 | ownerPassword = NULL; | |
291 | userPassword = NULL; | |
292 | } | |
293 | if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength, | |
294 | ownerKey, userKey, permFlags, fileID, | |
295 | ownerPassword, userPassword, fileKey, | |
296 | encryptMetadata, &ownerPasswordOk)) { | |
297 | return gFalse; | |
298 | } | |
299 | return gTrue; | |
300 | } | |
301 | ||
302 | #ifdef ENABLE_PLUGINS | |
303 | ||
304 | //------------------------------------------------------------------------ | |
305 | // ExternalSecurityHandler | |
306 | //------------------------------------------------------------------------ | |
307 | ||
308 | ExternalSecurityHandler::ExternalSecurityHandler(PDFDoc *docA, | |
309 | Object *encryptDictA, | |
310 | XpdfSecurityHandler *xshA): | |
311 | SecurityHandler(docA) | |
312 | { | |
313 | encryptDictA->copy(&encryptDict); | |
314 | xsh = xshA; | |
315 | ok = gFalse; | |
316 | ||
317 | if (!(*xsh->newDoc)(xsh->handlerData, (XpdfDoc)docA, | |
318 | (XpdfObject)encryptDictA, &docData)) { | |
319 | return; | |
320 | } | |
321 | ||
322 | ok = gTrue; | |
323 | } | |
324 | ||
325 | ExternalSecurityHandler::~ExternalSecurityHandler() { | |
326 | (*xsh->freeDoc)(xsh->handlerData, docData); | |
327 | encryptDict.free(); | |
328 | } | |
329 | ||
330 | void *ExternalSecurityHandler::makeAuthData(GString *ownerPassword, | |
331 | GString *userPassword) { | |
332 | char *opw, *upw; | |
333 | void *authData; | |
334 | ||
335 | opw = ownerPassword ? ownerPassword->getCString() : (char *)NULL; | |
336 | upw = userPassword ? userPassword->getCString() : (char *)NULL; | |
337 | if (!(*xsh->makeAuthData)(xsh->handlerData, docData, opw, upw, &authData)) { | |
338 | return NULL; | |
339 | } | |
340 | return authData; | |
341 | } | |
342 | ||
343 | void *ExternalSecurityHandler::getAuthData() { | |
344 | void *authData; | |
345 | ||
346 | if (!(*xsh->getAuthData)(xsh->handlerData, docData, &authData)) { | |
347 | return NULL; | |
348 | } | |
349 | return authData; | |
350 | } | |
351 | ||
352 | void ExternalSecurityHandler::freeAuthData(void *authData) { | |
353 | (*xsh->freeAuthData)(xsh->handlerData, docData, authData); | |
354 | } | |
355 | ||
356 | GBool ExternalSecurityHandler::authorize(void *authData) { | |
357 | char *key; | |
358 | int length; | |
359 | ||
360 | if (!ok) { | |
361 | return gFalse; | |
362 | } | |
363 | permFlags = (*xsh->authorize)(xsh->handlerData, docData, authData); | |
364 | if (!(permFlags & xpdfPermissionOpen)) { | |
365 | return gFalse; | |
366 | } | |
367 | if (!(*xsh->getKey)(xsh->handlerData, docData, &key, &length, &encVersion)) { | |
368 | return gFalse; | |
369 | } | |
370 | if ((fileKeyLength = length) > 16) { | |
371 | fileKeyLength = 16; | |
372 | } | |
373 | memcpy(fileKey, key, fileKeyLength); | |
374 | (*xsh->freeKey)(xsh->handlerData, docData, key, length); | |
375 | return gTrue; | |
376 | } | |
377 | ||
378 | #endif // ENABLE_PLUGINS |