Coverage Report

Created: 2025-07-18 07:17

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