1 //========================================================================
2 //
3 // pdfsig.cc
4 //
5 // This file is licensed under the GPLv2 or later
6 //
7 // Copyright 2015 André Guerreiro <aguerreiro1985@gmail.com>
8 // Copyright 2015 André Esser <bepandre@hotmail.com>
9 // Copyright 2015, 2017-2021 Albert Astals Cid <aacid@kde.org>
10 // Copyright 2016 Markus Kilås <digital@markuspage.com>
11 // Copyright 2017, 2019 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
12 // Copyright 2017, 2019 Adrian Johnson <ajohnson@redneon.com>
13 // Copyright 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@protonmail.com>
14 // Copyright 2019 Alexey Pavlov <alexpux@gmail.com>
15 // Copyright 2019 Oliver Sander <oliver.sander@tu-dresden.de>
16 // Copyright 2019 Nelson Efrain A. Cruz <neac03@gmail.com>
17 // Copyright 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net.
18 // Copyright 2021 Theofilos Intzoglou <int.teo@gmail.com>
19 //
20 //========================================================================
21
22 #include "config.h"
23 #include <poppler-config.h>
24 #include <cstdio>
25 #include <cstdlib>
26 #include <cstddef>
27 #include <cstring>
28 #include <ctime>
29 #include <hasht.h>
30 #include <fstream>
31 #include <random>
32 #include "parseargs.h"
33 #include "Object.h"
34 #include "Array.h"
35 #include "goo/gbasename.h"
36 #include "Page.h"
37 #include "PDFDoc.h"
38 #include "PDFDocFactory.h"
39 #include "Error.h"
40 #include "GlobalParams.h"
41 #include "SignatureHandler.h"
42 #include "SignatureInfo.h"
43 #include "Win32Console.h"
44 #include "numberofcharacters.h"
45 #include "UTF.h"
46 #include <libgen.h>
47
getReadableSigState(SignatureValidationStatus sig_vs)48 static const char *getReadableSigState(SignatureValidationStatus sig_vs)
49 {
50 switch (sig_vs) {
51 case SIGNATURE_VALID:
52 return "Signature is Valid.";
53
54 case SIGNATURE_INVALID:
55 return "Signature is Invalid.";
56
57 case SIGNATURE_DIGEST_MISMATCH:
58 return "Digest Mismatch.";
59
60 case SIGNATURE_DECODING_ERROR:
61 return "Document isn't signed or corrupted data.";
62
63 case SIGNATURE_NOT_VERIFIED:
64 return "Signature has not yet been verified.";
65
66 default:
67 return "Unknown Validation Failure.";
68 }
69 }
70
getReadableCertState(CertificateValidationStatus cert_vs)71 static const char *getReadableCertState(CertificateValidationStatus cert_vs)
72 {
73 switch (cert_vs) {
74 case CERTIFICATE_TRUSTED:
75 return "Certificate is Trusted.";
76
77 case CERTIFICATE_UNTRUSTED_ISSUER:
78 return "Certificate issuer isn't Trusted.";
79
80 case CERTIFICATE_UNKNOWN_ISSUER:
81 return "Certificate issuer is unknown.";
82
83 case CERTIFICATE_REVOKED:
84 return "Certificate has been Revoked.";
85
86 case CERTIFICATE_EXPIRED:
87 return "Certificate has Expired";
88
89 case CERTIFICATE_NOT_VERIFIED:
90 return "Certificate has not yet been verified.";
91
92 default:
93 return "Unknown issue with Certificate or corrupted data.";
94 }
95 }
96
getReadableTime(time_t unix_time)97 static char *getReadableTime(time_t unix_time)
98 {
99 char *time_str = (char *)gmalloc(64);
100 strftime(time_str, 64, "%b %d %Y %H:%M:%S", localtime(&unix_time));
101 return time_str;
102 }
103
dumpSignature(int sig_num,int sigCount,FormFieldSignature * s,const char * filename)104 static bool dumpSignature(int sig_num, int sigCount, FormFieldSignature *s, const char *filename)
105 {
106 const GooString *signature = s->getSignature();
107 if (!signature) {
108 printf("Cannot dump signature #%d\n", sig_num);
109 return false;
110 }
111
112 const int sigCountLength = numberOfCharacters(sigCount);
113 // We want format to be {0:s}.sig{1:Xd} where X is sigCountLength
114 // since { is the magic character to replace things we need to put it twice where
115 // we don't want it to be replaced
116 GooString *format = GooString::format("{{0:s}}.sig{{1:{0:d}d}}", sigCountLength);
117 GooString *path = GooString::format(format->c_str(), gbasename(filename).c_str(), sig_num);
118 printf("Signature #%d (%u bytes) => %s\n", sig_num, signature->getLength(), path->c_str());
119 std::ofstream outfile(path->c_str(), std::ofstream::binary);
120 outfile.write(signature->c_str(), signature->getLength());
121 outfile.close();
122 delete format;
123 delete path;
124
125 return true;
126 }
127
128 static GooString nssDir;
129 static GooString nssPassword;
130 static bool printVersion = false;
131 static bool printHelp = false;
132 static bool dontVerifyCert = false;
133 static bool noOCSPRevocationCheck = false;
134 static bool dumpSignatures = false;
135 static bool etsiCAdESdetached = false;
136 static int signatureNumber = 0;
137 static char certNickname[256] = "";
138 static char password[256] = "";
139 static char digestName[256] = "SHA256";
140 static GooString reason;
141 static bool listNicknames = false;
142 static bool addNewSignature = false;
143 static bool useAIACertFetch = false;
144 static GooString newSignatureFieldName;
145
146 static const ArgDesc argDesc[] = { { "-nssdir", argGooString, &nssDir, 0, "path to directory of libnss3 database" },
147 { "-nss-pwd", argGooString, &nssPassword, 0, "password to access the NSS database (if any)" },
148 { "-nocert", argFlag, &dontVerifyCert, 0, "don't perform certificate validation" },
149 { "-no-ocsp", argFlag, &noOCSPRevocationCheck, 0, "don't perform online OCSP certificate revocation check" },
150 { "-aia", argFlag, &useAIACertFetch, 0, "use Authority Information Access (AIA) extension for certificate fetching" },
151 { "-dump", argFlag, &dumpSignatures, 0, "dump all signatures into current directory" },
152 { "-add-signature", argFlag, &addNewSignature, 0, "adds a new signature to the document" },
153 { "-new-signature-field-name", argGooString, &newSignatureFieldName, 0, "field name used for the newly added signature. A random ID will be used if empty" },
154 { "-sign", argInt, &signatureNumber, 0, "sign the document in the signature field with the given number" },
155 { "-etsi", argFlag, &etsiCAdESdetached, 0, "create a signature of type ETSI.CAdES.detached instead of adbe.pkcs7.detached" },
156 { "-nick", argString, &certNickname, 256, "use the certificate with the given nickname for signing" },
157 { "-kpw", argString, &password, 256, "password for the signing key (might be missing if the key isn't password protected)" },
158 { "-digest", argString, &digestName, 256, "name of the digest algorithm (default: SHA256)" },
159 { "-reason", argGooString, &reason, 0, "reason for signing (default: no reason given)" },
160 { "-list-nicks", argFlag, &listNicknames, 0, "list available nicknames in the NSS database" },
161 { "-v", argFlag, &printVersion, 0, "print copyright and version info" },
162 { "-h", argFlag, &printHelp, 0, "print usage information" },
163 { "-help", argFlag, &printHelp, 0, "print usage information" },
164 { "-?", argFlag, &printHelp, 0, "print usage information" },
165 {} };
166
print_version_usage(bool usage)167 static void print_version_usage(bool usage)
168 {
169 fprintf(stderr, "pdfsig version %s\n", PACKAGE_VERSION);
170 fprintf(stderr, "%s\n", popplerCopyright);
171 fprintf(stderr, "%s\n", xpdfCopyright);
172 if (usage) {
173 printUsage("pdfsig", "<PDF-file> [<output-file>]", argDesc);
174 }
175 }
176
getAvailableSigningCertificates(bool * error)177 static std::vector<std::unique_ptr<X509CertificateInfo>> getAvailableSigningCertificates(bool *error)
178 {
179 bool wrongPassword = false;
180 bool passwordNeeded = false;
181 auto passwordCallback = [&passwordNeeded, &wrongPassword](const char *) -> char * {
182 static bool firstTime = true;
183 if (!firstTime) {
184 wrongPassword = true;
185 return nullptr;
186 }
187 firstTime = false;
188 if (nssPassword.getLength() > 0) {
189 return strdup(nssPassword.c_str());
190 } else {
191 passwordNeeded = true;
192 return nullptr;
193 }
194 };
195 SignatureHandler::setNSSPasswordCallback(passwordCallback);
196 std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = SignatureHandler::getAvailableSigningCertificates();
197 SignatureHandler::setNSSPasswordCallback({});
198 if (passwordNeeded) {
199 *error = true;
200 printf("Password is needed to access the NSS database.\n");
201 printf("\tPlease provide one with -nss-pwd.\n");
202 return {};
203 }
204 if (wrongPassword) {
205 *error = true;
206 printf("Password was not accepted to open the NSS database.\n");
207 printf("\tPlease provide the correct one with -nss-pwd.\n");
208 return {};
209 }
210
211 *error = false;
212 return vCerts;
213 }
214
main(int argc,char * argv[])215 int main(int argc, char *argv[])
216 {
217 char *time_str = nullptr;
218 globalParams = std::make_unique<GlobalParams>();
219
220 Win32Console win32Console(&argc, &argv);
221
222 const bool ok = parseArgs(argDesc, &argc, argv);
223
224 if (!ok) {
225 print_version_usage(true);
226 return 99;
227 }
228
229 if (printVersion) {
230 print_version_usage(false);
231 return 0;
232 }
233
234 if (printHelp) {
235 print_version_usage(true);
236 return 0;
237 }
238
239 SignatureHandler::setNSSDir(nssDir);
240
241 if (listNicknames) {
242 bool getCertsError;
243 const std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = getAvailableSigningCertificates(&getCertsError);
244 if (getCertsError) {
245 return 2;
246 } else {
247 if (vCerts.empty()) {
248 printf("There are no certificates available.\n");
249 } else {
250 printf("Certificate nicknames available:\n");
251 for (auto &cert : vCerts) {
252 const GooString &nick = cert->getNickName();
253 printf("%s\n", nick.c_str());
254 }
255 }
256 }
257 return 0;
258 }
259
260 if (argc < 2) {
261 // no filename was given
262 print_version_usage(true);
263 return 99;
264 }
265
266 std::unique_ptr<GooString> fileName = std::make_unique<GooString>(argv[1]);
267
268 // open PDF file
269 std::unique_ptr<PDFDoc> doc(PDFDocFactory().createPDFDoc(*fileName, nullptr, nullptr));
270
271 if (!doc->isOk()) {
272 return 1;
273 }
274
275 if (addNewSignature && signatureNumber > 0) {
276 // incompatible options
277 print_version_usage(true);
278 return 99;
279 }
280
281 if (addNewSignature) {
282 if (argc == 2) {
283 fprintf(stderr, "An output filename for the signed document must be given\n");
284 return 2;
285 }
286
287 if (strlen(certNickname) == 0) {
288 printf("A nickname of the signing certificate must be given\n");
289 return 2;
290 }
291
292 if (etsiCAdESdetached) {
293 printf("-etsi is not supported yet with -add-signature\n");
294 printf("Please file a bug report if this is important for you\n");
295 return 2;
296 }
297
298 if (digestName != std::string("SHA256")) {
299 printf("Only digest SHA256 is supported at the moment with -add-signature\n");
300 printf("Please file a bug report if this is important for you\n");
301 return 2;
302 }
303
304 if (doc->getPage(1) == nullptr) {
305 printf("Error getting first page of the document.\n");
306 return 2;
307 }
308
309 bool getCertsError;
310 // We need to call this otherwise NSS spins forever
311 getAvailableSigningCertificates(&getCertsError);
312 if (getCertsError) {
313 return 2;
314 }
315
316 const char *pw = (strlen(password) == 0) ? nullptr : password;
317 const auto rs = std::unique_ptr<GooString>(reason.toStr().empty() ? nullptr : utf8ToUtf16WithBom(reason.toStr()));
318
319 if (newSignatureFieldName.getLength() == 0) {
320 // Create a random field name, it could be anything but 32 hex numbers should
321 // hopefully give us something that is not already in the document
322 std::random_device rd;
323 std::mt19937 gen(rd());
324 std::uniform_int_distribution<> distrib(1, 15);
325 for (int i = 0; i < 32; ++i) {
326 const int value = distrib(gen);
327 newSignatureFieldName.append(value < 10 ? 48 + value : 65 + (value - 10));
328 }
329 }
330
331 // We don't provide a way to customize the UI from pdfsig for now
332 const bool success = doc->sign(argv[2], certNickname, pw, newSignatureFieldName.copy(), /*page*/ 1,
333 /*rect */ { 0, 0, 0, 0 }, /*signatureText*/ {}, /*signatureTextLeft*/ {}, /*fontSize */ 0,
334 /*fontColor*/ {}, /*borderWidth*/ 0, /*borderColor*/ {}, /*backgroundColor*/ {}, rs.get());
335 return success ? 0 : 3;
336 }
337
338 const std::vector<FormFieldSignature *> signatures = doc->getSignatureFields();
339 const unsigned int sigCount = signatures.size();
340
341 if (signatureNumber > 0) {
342 // We are signing an existing signature field
343 if (argc == 2) {
344 fprintf(stderr, "An output filename for the signed document must be given\n");
345 return 2;
346 }
347
348 if (signatureNumber > static_cast<int>(sigCount)) {
349 printf("File '%s' does not contain a signature with number %d\n", fileName->c_str(), signatureNumber);
350 return 2;
351 }
352
353 if (strlen(certNickname) == 0) {
354 printf("A nickname of the signing certificate must be given\n");
355 return 2;
356 }
357 FormFieldSignature *ffs = signatures.at(signatureNumber - 1);
358 Goffset file_size = 0;
359 GooString *sig = ffs->getCheckedSignature(&file_size);
360 if (sig) {
361 delete sig;
362 printf("Signature number %d is already signed\n", signatureNumber);
363 return 2;
364 }
365 if (etsiCAdESdetached)
366 ffs->setSignatureType(ETSI_CAdES_detached);
367 const char *pw = (strlen(password) == 0) ? nullptr : password;
368 const auto rs = std::unique_ptr<GooString>(reason.toStr().empty() ? nullptr : utf8ToUtf16WithBom(reason.toStr()));
369 if (ffs->getNumWidgets() != 1) {
370 printf("Unexpected number of widgets for the signature: %d\n", ffs->getNumWidgets());
371 return 2;
372 }
373 FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(ffs->getWidget(0));
374 const bool success = fws->signDocument(argv[2], certNickname, digestName, pw, rs.get());
375 return success ? 0 : 3;
376 }
377
378 if (argc > 2) {
379 // We are not signing and more than 1 filename was given
380 print_version_usage(true);
381 return 99;
382 }
383
384 if (sigCount >= 1) {
385 if (dumpSignatures) {
386 printf("Dumping Signatures: %u\n", sigCount);
387 for (unsigned int i = 0; i < sigCount; i++) {
388 const bool dumpingOk = dumpSignature(i, sigCount, signatures.at(i), fileName->c_str());
389 if (!dumpingOk) {
390 return 3;
391 }
392 }
393 return 0;
394 } else {
395 printf("Digital Signature Info of: %s\n", fileName->c_str());
396 }
397 } else {
398 printf("File '%s' does not contain any signatures\n", fileName->c_str());
399 return 2;
400 }
401
402 for (unsigned int i = 0; i < sigCount; i++) {
403 const SignatureInfo *sig_info = signatures.at(i)->validateSignature(!dontVerifyCert, false, -1 /* now */, !noOCSPRevocationCheck, useAIACertFetch);
404 printf("Signature #%u:\n", i + 1);
405 printf(" - Signer Certificate Common Name: %s\n", sig_info->getSignerName());
406 printf(" - Signer full Distinguished Name: %s\n", sig_info->getSubjectDN());
407 printf(" - Signing Time: %s\n", time_str = getReadableTime(sig_info->getSigningTime()));
408 printf(" - Signing Hash Algorithm: ");
409 switch (sig_info->getHashAlgorithm()) {
410 case HASH_AlgMD2:
411 printf("MD2\n");
412 break;
413 case HASH_AlgMD5:
414 printf("MD5\n");
415 break;
416 case HASH_AlgSHA1:
417 printf("SHA1\n");
418 break;
419 case HASH_AlgSHA256:
420 printf("SHA-256\n");
421 break;
422 case HASH_AlgSHA384:
423 printf("SHA-384\n");
424 break;
425 case HASH_AlgSHA512:
426 printf("SHA-512\n");
427 break;
428 case HASH_AlgSHA224:
429 printf("SHA-224\n");
430 break;
431 default:
432 printf("unknown\n");
433 }
434 printf(" - Signature Type: ");
435 switch (signatures.at(i)->getSignatureType()) {
436 case adbe_pkcs7_sha1:
437 printf("adbe.pkcs7.sha1\n");
438 break;
439 case adbe_pkcs7_detached:
440 printf("adbe.pkcs7.detached\n");
441 break;
442 case ETSI_CAdES_detached:
443 printf("ETSI.CAdES.detached\n");
444 break;
445 default:
446 printf("unknown\n");
447 }
448 std::vector<Goffset> ranges = signatures.at(i)->getSignedRangeBounds();
449 if (ranges.size() == 4) {
450 printf(" - Signed Ranges: [%lld - %lld], [%lld - %lld]\n", ranges[0], ranges[1], ranges[2], ranges[3]);
451 Goffset checked_file_size;
452 GooString *signature = signatures.at(i)->getCheckedSignature(&checked_file_size);
453 if (signature && checked_file_size == ranges[3]) {
454 printf(" - Total document signed\n");
455 } else {
456 printf(" - Not total document signed\n");
457 }
458 delete signature;
459 }
460 printf(" - Signature Validation: %s\n", getReadableSigState(sig_info->getSignatureValStatus()));
461 gfree(time_str);
462 if (sig_info->getSignatureValStatus() != SIGNATURE_VALID || dontVerifyCert) {
463 continue;
464 }
465 printf(" - Certificate Validation: %s\n", getReadableCertState(sig_info->getCertificateValStatus()));
466 }
467
468 return 0;
469 }
470