Coverage Report

Created: 2025-11-04 07:14

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