1 | //======================================================================== |
2 | // |
3 | // SecurityHandler.cc |
4 | // |
5 | // Copyright 2004 Glyph & Cog, LLC |
6 | // |
7 | //======================================================================== |
8 | |
9 | //======================================================================== |
10 | // |
11 | // Modified under the Poppler project - http://poppler.freedesktop.org |
12 | // |
13 | // All changes made under the Poppler project to this file are licensed |
14 | // under GPL version 2 or later |
15 | // |
16 | // Copyright (C) 2010, 2012, 2015, 2017, 2018, 2020-2022 Albert Astals Cid <aacid@kde.org> |
17 | // Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com> |
18 | // Copyright (C) 2014 Fabio D'Urso <fabiodurso@hotmail.it> |
19 | // Copyright (C) 2016 Alok Anand <alok4nand@gmail.com> |
20 | // |
21 | // To see a description of the changes please see the Changelog file that |
22 | // came with your tarball or type make ChangeLog if you are building from git |
23 | // |
24 | //======================================================================== |
25 | |
26 | #include <config.h> |
27 | |
28 | #include "GooString.h" |
29 | #include "PDFDoc.h" |
30 | #include "Decrypt.h" |
31 | #include "Error.h" |
32 | #include "GlobalParams.h" |
33 | #include "SecurityHandler.h" |
34 | |
35 | #include <climits> |
36 | |
37 | //------------------------------------------------------------------------ |
38 | // SecurityHandler |
39 | //------------------------------------------------------------------------ |
40 | |
41 | SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) |
42 | { |
43 | SecurityHandler *secHdlr; |
44 | |
45 | Object filterObj = encryptDictA->dictLookup(key: "Filter" ); |
46 | if (filterObj.isName(nameA: "Standard" )) { |
47 | secHdlr = new StandardSecurityHandler(docA, encryptDictA); |
48 | } else if (filterObj.isName()) { |
49 | error(category: errSyntaxError, pos: -1, msg: "Couldn't find the '{0:s}' security handler" , filterObj.getName()); |
50 | secHdlr = nullptr; |
51 | } else { |
52 | error(category: errSyntaxError, pos: -1, msg: "Missing or invalid 'Filter' entry in encryption dictionary" ); |
53 | secHdlr = nullptr; |
54 | } |
55 | return secHdlr; |
56 | } |
57 | |
58 | SecurityHandler::SecurityHandler(PDFDoc *docA) |
59 | { |
60 | doc = docA; |
61 | } |
62 | |
63 | SecurityHandler::~SecurityHandler() { } |
64 | |
65 | bool SecurityHandler::checkEncryption(const std::optional<GooString> &ownerPassword, const std::optional<GooString> &userPassword) |
66 | { |
67 | void *authData; |
68 | |
69 | if (ownerPassword || userPassword) { |
70 | authData = makeAuthData(ownerPassword, userPassword); |
71 | } else { |
72 | authData = nullptr; |
73 | } |
74 | const bool ok = authorize(authData); |
75 | if (authData) { |
76 | freeAuthData(authData); |
77 | } |
78 | if (!ok) { |
79 | if (!ownerPassword && !userPassword) { |
80 | return checkEncryption(ownerPassword: GooString(), userPassword: GooString()); |
81 | } else { |
82 | error(category: errCommandLine, pos: -1, msg: "Incorrect password" ); |
83 | } |
84 | } |
85 | return ok; |
86 | } |
87 | |
88 | //------------------------------------------------------------------------ |
89 | // StandardSecurityHandler |
90 | //------------------------------------------------------------------------ |
91 | |
92 | class StandardAuthData |
93 | { |
94 | public: |
95 | StandardAuthData(GooString *ownerPasswordA, GooString *userPasswordA) |
96 | { |
97 | ownerPassword = ownerPasswordA; |
98 | userPassword = userPasswordA; |
99 | } |
100 | |
101 | ~StandardAuthData() |
102 | { |
103 | if (ownerPassword) { |
104 | delete ownerPassword; |
105 | } |
106 | if (userPassword) { |
107 | delete userPassword; |
108 | } |
109 | } |
110 | |
111 | StandardAuthData(const StandardAuthData &) = delete; |
112 | StandardAuthData &operator=(const StandardAuthData &) = delete; |
113 | |
114 | GooString *ownerPassword; |
115 | GooString *userPassword; |
116 | }; |
117 | |
118 | StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA) : SecurityHandler(docA) |
119 | { |
120 | ok = false; |
121 | fileID = nullptr; |
122 | ownerKey = nullptr; |
123 | userKey = nullptr; |
124 | ownerEnc = nullptr; |
125 | userEnc = nullptr; |
126 | fileKeyLength = 0; |
127 | encAlgorithm = cryptNone; |
128 | |
129 | Object versionObj = encryptDictA->dictLookup(key: "V" ); |
130 | Object revisionObj = encryptDictA->dictLookup(key: "R" ); |
131 | Object lengthObj = encryptDictA->dictLookup(key: "Length" ); |
132 | Object ownerKeyObj = encryptDictA->dictLookup(key: "O" ); |
133 | Object userKeyObj = encryptDictA->dictLookup(key: "U" ); |
134 | Object ownerEncObj = encryptDictA->dictLookup(key: "OE" ); |
135 | Object userEncObj = encryptDictA->dictLookup(key: "UE" ); |
136 | Object permObj = encryptDictA->dictLookup(key: "P" ); |
137 | if (permObj.isInt64()) { |
138 | unsigned int permUint = static_cast<unsigned int>(permObj.getInt64()); |
139 | int perms = permUint - UINT_MAX - 1; |
140 | permObj = Object(perms); |
141 | } |
142 | Object fileIDObj = doc->getXRef()->getTrailerDict()->dictLookup(key: "ID" ); |
143 | if (versionObj.isInt() && revisionObj.isInt() && permObj.isInt() && ownerKeyObj.isString() && userKeyObj.isString()) { |
144 | encVersion = versionObj.getInt(); |
145 | encRevision = revisionObj.getInt(); |
146 | if ((encRevision <= 4 && ownerKeyObj.getString()->getLength() >= 1 && userKeyObj.getString()->getLength() >= 1) |
147 | || ((encRevision == 5 || encRevision == 6) && |
148 | // the spec says 48 bytes, but Acrobat pads them out longer |
149 | ownerKeyObj.getString()->getLength() >= 48 && userKeyObj.getString()->getLength() >= 48 && ownerEncObj.isString() && ownerEncObj.getString()->getLength() == 32 && userEncObj.isString() |
150 | && userEncObj.getString()->getLength() == 32)) { |
151 | encAlgorithm = cryptRC4; |
152 | // revision 2 forces a 40-bit key - some buggy PDF generators |
153 | // set the Length value incorrectly |
154 | if (encRevision == 2 || !lengthObj.isInt()) { |
155 | fileKeyLength = 5; |
156 | } else { |
157 | fileKeyLength = lengthObj.getInt() / 8; |
158 | } |
159 | encryptMetadata = true; |
160 | //~ this currently only handles a subset of crypt filter functionality |
161 | //~ (in particular, it ignores the EFF entry in encryptDictA, and |
162 | //~ doesn't handle the case where StmF, StrF, and EFF are not all the |
163 | //~ same) |
164 | if ((encVersion == 4 || encVersion == 5) && (encRevision == 4 || encRevision == 5 || encRevision == 6)) { |
165 | Object cryptFiltersObj = encryptDictA->dictLookup(key: "CF" ); |
166 | Object streamFilterObj = encryptDictA->dictLookup(key: "StmF" ); |
167 | Object stringFilterObj = encryptDictA->dictLookup(key: "StrF" ); |
168 | if (cryptFiltersObj.isDict() && streamFilterObj.isName() && stringFilterObj.isName() && !strcmp(s1: streamFilterObj.getName(), s2: stringFilterObj.getName())) { |
169 | if (!strcmp(s1: streamFilterObj.getName(), s2: "Identity" )) { |
170 | // no encryption on streams or strings |
171 | encVersion = encRevision = -1; |
172 | } else { |
173 | Object cryptFilterObj = cryptFiltersObj.dictLookup(key: streamFilterObj.getName()); |
174 | if (cryptFilterObj.isDict()) { |
175 | Object cfmObj = cryptFilterObj.dictLookup(key: "CFM" ); |
176 | if (cfmObj.isName(nameA: "V2" )) { |
177 | encVersion = 2; |
178 | encRevision = 3; |
179 | Object cfLengthObj = cryptFilterObj.dictLookup(key: "Length" ); |
180 | if (cfLengthObj.isInt()) { |
181 | //~ according to the spec, this should be cfLengthObj / 8 |
182 | fileKeyLength = cfLengthObj.getInt(); |
183 | } |
184 | } else if (cfmObj.isName(nameA: "AESV2" )) { |
185 | encVersion = 2; |
186 | encRevision = 3; |
187 | encAlgorithm = cryptAES; |
188 | Object cfLengthObj = cryptFilterObj.dictLookup(key: "Length" ); |
189 | if (cfLengthObj.isInt()) { |
190 | //~ according to the spec, this should be cfLengthObj / 8 |
191 | fileKeyLength = cfLengthObj.getInt(); |
192 | } |
193 | } else if (cfmObj.isName(nameA: "AESV3" )) { |
194 | encVersion = 5; |
195 | // let encRevision be 5 or 6 |
196 | encAlgorithm = cryptAES256; |
197 | Object cfLengthObj = cryptFilterObj.dictLookup(key: "Length" ); |
198 | if (cfLengthObj.isInt()) { |
199 | //~ according to the spec, this should be cfLengthObj / 8 |
200 | fileKeyLength = cfLengthObj.getInt(); |
201 | } |
202 | } |
203 | } |
204 | } |
205 | } |
206 | Object encryptMetadataObj = encryptDictA->dictLookup(key: "EncryptMetadata" ); |
207 | if (encryptMetadataObj.isBool()) { |
208 | encryptMetadata = encryptMetadataObj.getBool(); |
209 | } |
210 | } |
211 | permFlags = permObj.getInt(); |
212 | ownerKey = ownerKeyObj.getString()->copy(); |
213 | userKey = userKeyObj.getString()->copy(); |
214 | if (encVersion >= 1 && encVersion <= 2 && encRevision >= 2 && encRevision <= 3) { |
215 | if (fileIDObj.isArray()) { |
216 | Object fileIDObj1 = fileIDObj.arrayGet(i: 0); |
217 | if (fileIDObj1.isString()) { |
218 | fileID = fileIDObj1.getString()->copy(); |
219 | } else { |
220 | fileID = new GooString(); |
221 | } |
222 | } else { |
223 | fileID = new GooString(); |
224 | } |
225 | if (fileKeyLength > 16 || fileKeyLength < 0) { |
226 | fileKeyLength = 16; |
227 | } |
228 | ok = true; |
229 | } else if (encVersion == 5 && (encRevision == 5 || encRevision == 6)) { |
230 | fileID = new GooString(); // unused for V=R=5 |
231 | if (ownerEncObj.isString() && userEncObj.isString()) { |
232 | ownerEnc = ownerEncObj.getString()->copy(); |
233 | userEnc = userEncObj.getString()->copy(); |
234 | if (fileKeyLength > 32 || fileKeyLength < 0) { |
235 | fileKeyLength = 32; |
236 | } |
237 | ok = true; |
238 | } else { |
239 | error(category: errSyntaxError, pos: -1, msg: "Weird encryption owner/user info" ); |
240 | } |
241 | } else if (!(encVersion == -1 && encRevision == -1)) { |
242 | error(category: errUnimplemented, pos: -1, msg: "Unsupported version/revision ({0:d}/{1:d}) of Standard security handler" , encVersion, encRevision); |
243 | } |
244 | |
245 | if (encRevision <= 4) { |
246 | // Adobe apparently zero-pads the U value (and maybe the O value?) |
247 | // if it's short |
248 | while (ownerKey->getLength() < 32) { |
249 | ownerKey->append(c: (char)0x00); |
250 | } |
251 | while (userKey->getLength() < 32) { |
252 | userKey->append(c: (char)0x00); |
253 | } |
254 | } |
255 | } else { |
256 | error(category: errSyntaxError, pos: -1, |
257 | msg: "Invalid encryption key length. version: {0:d} - revision: {1:d} - ownerKeyLength: {2:d} - userKeyLength: {3:d} - ownerEncIsString: {4:d} - ownerEncLength: {5:d} - userEncIsString: {6:d} - userEncLength: {7:d}" , |
258 | encVersion, encRevision, ownerKeyObj.getString()->getLength(), userKeyObj.getString()->getLength(), ownerEncObj.isString(), ownerEncObj.isString() ? ownerEncObj.getString()->getLength() : -1, userEncObj.isString(), |
259 | userEncObj.isString() ? userEncObj.getString()->getLength() : -1); |
260 | } |
261 | } else { |
262 | error(category: errSyntaxError, pos: -1, msg: "Weird encryption info" ); |
263 | } |
264 | } |
265 | |
266 | StandardSecurityHandler::~StandardSecurityHandler() |
267 | { |
268 | if (fileID) { |
269 | delete fileID; |
270 | } |
271 | if (ownerKey) { |
272 | delete ownerKey; |
273 | } |
274 | if (userKey) { |
275 | delete userKey; |
276 | } |
277 | if (ownerEnc) { |
278 | delete ownerEnc; |
279 | } |
280 | if (userEnc) { |
281 | delete userEnc; |
282 | } |
283 | } |
284 | |
285 | bool StandardSecurityHandler::isUnencrypted() const |
286 | { |
287 | if (!ok) { |
288 | return true; |
289 | } |
290 | return encVersion == -1 && encRevision == -1; |
291 | } |
292 | |
293 | void *StandardSecurityHandler::makeAuthData(const std::optional<GooString> &ownerPassword, const std::optional<GooString> &userPassword) |
294 | { |
295 | return new StandardAuthData(ownerPassword ? ownerPassword->copy() : nullptr, userPassword ? userPassword->copy() : nullptr); |
296 | } |
297 | |
298 | void StandardSecurityHandler::freeAuthData(void *authData) |
299 | { |
300 | delete (StandardAuthData *)authData; |
301 | } |
302 | |
303 | bool StandardSecurityHandler::authorize(void *authData) |
304 | { |
305 | GooString *ownerPassword, *userPassword; |
306 | |
307 | if (!ok) { |
308 | return false; |
309 | } |
310 | if (authData) { |
311 | ownerPassword = ((StandardAuthData *)authData)->ownerPassword; |
312 | userPassword = ((StandardAuthData *)authData)->userPassword; |
313 | } else { |
314 | ownerPassword = nullptr; |
315 | userPassword = nullptr; |
316 | } |
317 | if (!Decrypt::makeFileKey(encVersion, encRevision, keyLength: fileKeyLength, ownerKey, userKey, ownerEnc, userEnc, permissions: permFlags, fileID, ownerPassword, userPassword, fileKey, encryptMetadata, ownerPasswordOk: &ownerPasswordOk)) { |
318 | return false; |
319 | } |
320 | return true; |
321 | } |
322 | |