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