diff options
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 327 |
1 files changed, 294 insertions, 33 deletions
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index 6977c4d14037..cfd8b7ddfc4a 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -6006,6 +6006,9 @@ public: HASHContext *get() { return mpPtr; } }; +// ASN.1 used in the (much simpler) time stamp request. From RFC3161 +// and other sources. + /* AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, @@ -6042,19 +6045,6 @@ Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */ /* -Accuracy ::= SEQUENCE { - seconds INTEGER OPTIONAL, - millis [0] INTEGER (1..999) OPTIONAL, - micros [1] INTEGER (1..999) OPTIONAL } -*/ - -typedef struct { - SECItem seconds; - SECItem millis; - SECItem micros; -} Accuracy; - -/* TSAPolicyId ::= OBJECT IDENTIFIER TimeStampReq ::= SEQUENCE { @@ -6067,6 +6057,7 @@ TimeStampReq ::= SEQUENCE { certReq BOOLEAN DEFAULT FALSE, extensions [0] IMPLICIT Extensions OPTIONAL } */ + typedef struct { SECItem version; MessageImprint messageImprint; @@ -6076,9 +6067,167 @@ typedef struct { Extension *extensions; } TimeStampReq; +// (Partial) ASN.1 for the time stamp responce. Very complicated. Pulled +// together from varuous RFCs. + +/* +Accuracy ::= SEQUENCE { + seconds INTEGER OPTIONAL, + millis [0] INTEGER (1..999) OPTIONAL, + micros [1] INTEGER (1..999) OPTIONAL } + +PKIStatus ::= INTEGER { + granted (0), + -- when the PKIStatus contains the value zero a TimeStampToken, as requested, is present. + grantedWithMods (1), + -- when the PKIStatus contains the value one a TimeStampToken, with modifications, is present. + rejection (2), + waiting (3), + revocationWarning (4), + -- this message contains a warning that a revocation is + -- imminent + revocationNotification (5) + -- notification that a revocation has occurred +} + +PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String + -- text encoded as UTF-8 String [RFC3629] (note: each + -- UTF8String MAY include an [RFC3066] language tag + -- to indicate the language of the contained text + -- see [RFC2482] for details) + +PKIFailureInfo ::= BIT STRING { + badAlg (0), + -- unrecognized or unsupported Algorithm Identifier + badRequest (2), + -- transaction not permitted or supported + badDataFormat (5), + -- the data submitted has the wrong format + timeNotAvailable (14), + -- the TSA's time source is not available + unacceptedPolicy (15), + -- the requested TSA policy is not supported by the TSA. + unacceptedExtension (16), + -- the requested extension is not supported by the TSA. + addInfoNotAvailable (17), + -- the additional information requested could not be understood + -- or is not available + systemFailure (25) + -- the request cannot be handled due to system failure +} + +PKIStatusInfo ::= SEQUENCE { + status PKIStatus, + statusString PKIFreeText OPTIONAL, + failInfo PKIFailureInfo OPTIONAL } + +ContentType ::= OBJECT IDENTIFIER + +ContentInfo ::= SEQUENCE { + contentType ContentType, + content [0] EXPLICIT ANY DEFINED BY contentType } + +CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) } + +DigestAlgorithmIdentifier ::= AlgorithmIdentifier + +DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier + +ContentType ::= OBJECT IDENTIFIER + +EncapsulatedContentInfo ::= SEQUENCE { + eContentType ContentType, + eContent [0] EXPLICIT OCTET STRING OPTIONAL } + +OtherCertificateFormat ::= SEQUENCE { + otherCertFormat OBJECT IDENTIFIER, + otherCert ANY DEFINED BY otherCertFormat } + +CertificateChoices ::= CHOICE { + certificate Certificate, + extendedCertificate [0] IMPLICIT ExtendedCertificate, -- Obsolete + v1AttrCert [1] IMPLICIT AttributeCertificateV1, -- Obsolete + v2AttrCert [2] IMPLICIT AttributeCertificateV2, + other [3] IMPLICIT OtherCertificateFormat } + +CertificateSet ::= SET OF CertificateChoices + +CertificateList ::= SEQUENCE { + tbsCertList TBSCertList, + signatureAlgorithm AlgorithmIdentifier, + signatureValue BIT STRING } + +TBSCertList ::= SEQUENCE { + version Version OPTIONAL, + -- if present, MUST be v2 + signature AlgorithmIdentifier, + issuer Name, + thisUpdate Time, + nextUpdate Time OPTIONAL, + revokedCertificates SEQUENCE OF SEQUENCE { + userCertificate CertificateSerialNumber, + revocationDate Time, + crlEntryExtensions Extensions OPTIONAL + -- if present, version MUST be v2 + } OPTIONAL, + crlExtensions [0] EXPLICIT Extensions OPTIONAL + -- if present, version MUST be v2 + } + +OtherRevocationInfoFormat ::= SEQUENCE { + otherRevInfoFormat OBJECT IDENTIFIER, + otherRevInfo ANY DEFINED BY otherRevInfoFormat } + +RevocationInfoChoice ::= CHOICE { + crl CertificateList, + other [1] IMPLICIT OtherRevocationInfoFormat } + +RevocationInfoChoices ::= SET OF RevocationInfoChoice + +SignerInfos ::= SET OF SignerInfo + +SignedData ::= SEQUENCE { + version CMSVersion, + digestAlgorithms DigestAlgorithmIdentifiers, + encapContentInfo EncapsulatedContentInfo, + certificates [0] IMPLICIT CertificateSet OPTIONAL, + crls [1] IMPLICIT RevocationInfoChoices OPTIONAL, + signerInfos SignerInfos } + +TimeStampToken ::= ContentInfo + -- contentType is id-signedData as defined in [CMS] + -- content is SignedData as defined in([CMS]) + -- eContentType within SignedData is id-ct-TSTInfo + -- eContent within SignedData is TSTInfo + +TSTInfo ::= SEQUENCE { + version INTEGER { v1(1) }, + policy TSAPolicyId, + messageImprint MessageImprint, + -- MUST have the same value as the similar field in + -- TimeStampReq + serialNumber INTEGER, + -- Time-Stamping users MUST be ready to accommodate integers + -- up to 160 bits. + genTime GeneralizedTime, + accuracy Accuracy OPTIONAL, + ordering BOOLEAN DEFAULT FALSE, + nonce INTEGER OPTIONAL, + -- MUST be present if the similar field was present + -- in TimeStampReq. In that case it MUST have the same value. + tsa [0] GeneralName OPTIONAL, + extensions [1] IMPLICIT Extensions OPTIONAL } + +TimeStampResp ::= SEQUENCE { + status PKIStatusInfo, + timeStampToken TimeStampToken OPTIONAL } +*/ + SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) SEC_ASN1_MKSUB(MessageImprint_Template) SEC_ASN1_MKSUB(Extensions_Template) +SEC_ASN1_MKSUB(PKIStatusInfo_Template) +SEC_ASN1_MKSUB(Any_Template) const SEC_ASN1Template MessageImprint_Template[] = { @@ -6102,6 +6251,58 @@ const SEC_ASN1Template Extensions_Template[] = { SEC_ASN1_SEQUENCE_OF, 0, Extension_Template, 0 } }; +const SEC_ASN1Template TimeStampReq_Template[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(TimeStampReq) }, + { SEC_ASN1_INTEGER, offsetof(TimeStampReq, version), 0, 0 }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(TimeStampReq, messageImprint), SEC_ASN1_SUB(MessageImprint_Template), 0 }, + { SEC_ASN1_OBJECT_ID | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, reqPolicy), 0, 0 }, + { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, nonce), 0, 0 }, + { SEC_ASN1_BOOLEAN | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, certReq), 0, 0 }, + { SEC_ASN1_XTRN | SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(TimeStampReq, extensions), SEC_ASN1_SUB(Extensions_Template), 0 }, + { 0, 0, 0, 0 } +}; + +typedef struct { + SECItem status; + SECItem statusString; + SECItem failInfo; +} PKIStatusInfo; + +const SEC_ASN1Template PKIStatusInfo_Template[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(PKIStatusInfo) }, + { SEC_ASN1_INTEGER, offsetof(PKIStatusInfo, status), 0, 0 }, + { SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, statusString), 0, 0 }, + { SEC_ASN1_BIT_STRING | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, failInfo), 0, 0 }, + { 0, 0, 0, 0 } +}; + +const SEC_ASN1Template Any_Template[] = +{ + { SEC_ASN1_ANY, 0, NULL, sizeof(SECItem) } +}; + +typedef struct { + PKIStatusInfo status; + SECItem timeStampToken; +} TimeStampResp; + +const SEC_ASN1Template TimeStampResp_Template[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(TimeStampResp) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(TimeStampResp, status), SEC_ASN1_SUB(PKIStatusInfo_Template), 0 }, + { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, offsetof(TimeStampResp, timeStampToken), SEC_ASN1_SUB(Any_Template), 0 }, + { 0, 0, 0, 0 } +}; + +/* Will see if these are needed or not +typedef struct { + SECItem seconds; + SECItem millis; + SECItem micros; +} Accuracy; + const SEC_ASN1Template Integer_Template[] = { { SEC_ASN1_INTEGER, 0, NULL, sizeof(SECItem) } @@ -6115,18 +6316,7 @@ const SEC_ASN1Template Accuracy_Template[] = { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 1, offsetof(Accuracy, micros), Integer_Template, 0 }, { 0, 0, 0, 0 } }; - -const SEC_ASN1Template TimeStampReq_Template[] = -{ - { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(TimeStampReq) }, - { SEC_ASN1_INTEGER, offsetof(TimeStampReq, version), 0, 0 }, - { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(TimeStampReq, messageImprint), SEC_ASN1_SUB(MessageImprint_Template), 0 }, - { SEC_ASN1_OBJECT_ID | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, reqPolicy), 0, 0 }, - { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, nonce), 0, 0 }, - { SEC_ASN1_BOOLEAN | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, certReq), 0, 0 }, - { SEC_ASN1_XTRN | SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(TimeStampReq, extensions), SEC_ASN1_SUB(Extensions_Template), 0 }, - { 0, 0, 0, 0 } -}; +*/ size_t AppendToBuffer(char *ptr, size_t size, size_t nmemb, void *userdata) { @@ -6136,6 +6326,41 @@ size_t AppendToBuffer(char *ptr, size_t size, size_t nmemb, void *userdata) return size*nmemb; } +OUString PKIStatusToString(int n) +{ + switch (n) + { + case 0: return OUString("granted"); + case 1: return OUString("grantedWithMods"); + case 2: return OUString("rejection"); + case 3: return OUString("waiting"); + case 4: return OUString("revocationWarning"); + case 5: return OUString("revocationNotification"); + default: return "unknown (" + OUString::number(n) + ")"; + } +} + +OUString PKIStatusInfoToString(const PKIStatusInfo& rStatusInfo) +{ + OUString result; + + result += "{status="; + if (rStatusInfo.status.len == 1) + result += PKIStatusToString(rStatusInfo.status.data[0]); + else + result += "unknown (len=" + OUString::number(rStatusInfo.status.len); + if (rStatusInfo.statusString.data != NULL) + result += ",statusString='" + + OUString::fromUtf8(OString(reinterpret_cast<const sal_Char*>(rStatusInfo.statusString.data), rStatusInfo.statusString.len)) + + "'"; + + // FIXME: Worth it to decode failInfo to cleartext, probably not at least as long as this is only for a SAL_INFO + + result += "}"; + + return result; +} + #if 0 { #endif @@ -6333,7 +6558,7 @@ bool PDFWriterImpl::finalizeSignature() return false; } - SAL_INFO("vcl.pdfwriter", "request len=" << (timestamp_request ? timestamp_request->len : -1)); + SAL_INFO("vcl.pdfwriter", "request length=" << timestamp_request->len); #ifdef DBG_UTIL { @@ -6356,7 +6581,7 @@ bool PDFWriterImpl::finalizeSignature() return false; } - SAL_INFO("vcl.pdfwriter", "Setting curl to verbose: " << (curl_easy_setopt(curl, CURLOPT_VERBOSE, 1) == CURLE_OK ? "OK" : "FAIL")); + SAL_INFO("vcl.pdfwriter", "Setting curl to verbose: " << (curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L) == CURLE_OK ? "OK" : "FAIL")); if ((rc = curl_easy_setopt(curl, CURLOPT_URL, OUStringToOString(m_aContext.SignTSA, RTL_TEXTENCODING_UTF8).getStr())) != CURLE_OK) { @@ -6378,7 +6603,7 @@ bool PDFWriterImpl::finalizeSignature() return false; } - if ((rc = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, timestamp_request->len)) != CURLE_OK || + if ((rc = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<long>(timestamp_request->len))) != CURLE_OK || (rc = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, timestamp_request->data)) != CURLE_OK) { SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_setopt(CURLOPT_POSTFIELDSIZE or CURLOPT_POSTFIELDS) failed: " << curl_easy_strerror(rc)); @@ -6387,9 +6612,9 @@ bool PDFWriterImpl::finalizeSignature() return false; } - OStringBuffer reply_buffer; + OStringBuffer response_buffer; - if ((rc = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &reply_buffer)) != CURLE_OK || + if ((rc = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buffer)) != CURLE_OK || (rc = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, AppendToBuffer)) != CURLE_OK) { SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_setopt(CURLOPT_WRITEDATA or CURLOPT_WRITEFUNCTION) failed: " << curl_easy_strerror(rc)); @@ -6398,7 +6623,7 @@ bool PDFWriterImpl::finalizeSignature() return false; } - if ((rc = curl_easy_setopt(curl, CURLOPT_POST, 1)) != CURLE_OK) + if ((rc = curl_easy_setopt(curl, CURLOPT_POST, 1l)) != CURLE_OK) { SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_setopt(CURLOPT_POST) failed: " << curl_easy_strerror(rc)); curl_easy_cleanup(curl); @@ -6415,6 +6640,16 @@ bool PDFWriterImpl::finalizeSignature() return false; } + // Use a ten second timeout + if ((rc = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10l)) != CURLE_OK || + (rc = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10l)) != CURLE_OK) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_setopt(CURLOPT_TIMEOUT or CURLOPT_CONNECTTIMEOUT) failed: " << curl_easy_strerror(rc)); + curl_easy_cleanup(curl); + SECITEM_FreeItem(timestamp_request, PR_TRUE); + return false; + } + if (curl_easy_perform(curl) != CURLE_OK) { SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_perform failed: " << error_buffer); @@ -6423,18 +6658,44 @@ bool PDFWriterImpl::finalizeSignature() return false; } + SAL_INFO("vcl.pdfwriter", "PDF signing: got response, length=" << response_buffer.getLength()); + #ifdef DBG_UTIL { FILE *out = fopen("PDFWRITER.reply.data", "wb"); - fwrite(reply_buffer.getStr(), reply_buffer.getLength(), 1, out); + fwrite(response_buffer.getStr(), response_buffer.getLength(), 1, out); fclose(out); } #endif curl_slist_free_all(slist); curl_easy_cleanup(curl); - SECITEM_FreeItem(timestamp_request, PR_TRUE); + + TimeStampResp response; + memset(&response, 0, sizeof(response)); + + SECItem response_item; + response_item.type = siBuffer; + response_item.data = reinterpret_cast<unsigned char*>(const_cast<char*>(response_buffer.getStr())); + response_item.len = response_buffer.getLength(); + + if (SEC_ASN1DecodeItem(NULL, &response, TimeStampResp_Template, &response_item) != SECSuccess) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: SEC_ASN1DecodeItem failed"); + return false; + } + + SAL_INFO("vcl.pdfwriter", "TimeStampResp received and decoded, status=" << PKIStatusInfoToString(response.status)); +#if 0 + NSSCMSAttribute timestamp; + timestamp.type = ? + if (NSS_CMSSignerInfo_AddUnauthAttr(cms_signer, ) != SECSuccess) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: can't include cert chain."); + return false; + } +#endif } if (NSS_CMSSignerInfo_IncludeCerts(cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) |