Coverage Report

Created: 2025-09-04 08:08

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