Coverage Report

Created: 2026-10-19 06:49

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