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
41SecurityHandler *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
58SecurityHandler::SecurityHandler(PDFDoc *docA)
59{
60 doc = docA;
61}
62
63SecurityHandler::~SecurityHandler() { }
64
65bool 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
92class StandardAuthData
93{
94public:
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
118StandardSecurityHandler::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
266StandardSecurityHandler::~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
285bool StandardSecurityHandler::isUnencrypted() const
286{
287 if (!ok) {
288 return true;
289 }
290 return encVersion == -1 && encRevision == -1;
291}
292
293void *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
298void StandardSecurityHandler::freeAuthData(void *authData)
299{
300 delete (StandardAuthData *)authData;
301}
302
303bool 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

source code of poppler/poppler/SecurityHandler.cc