diff options
author | Milan Crha <mcrha@redhat.com> | 2023-05-02 18:46:32 +0200 |
---|---|---|
committer | Milan Crha <mcrha@redhat.com> | 2023-05-02 18:46:32 +0200 |
commit | 75e83ac75252aa8df2f4500ad5e6ed1bcffedfe6 (patch) | |
tree | f9377af8391bf72f82302637f7245d281b327405 | |
parent | 0892c27bf4a91fb5c072f9e5a1fc082857e32c5b (diff) | |
download | evolution-data-server-75e83ac75252aa8df2f4500ad5e6ed1bcffedfe6.tar.gz |
I#474 - Camel: Set proper S/MIME signature verification status ][
Follow up fix, verify the signature when the certificate is not trusted, to
properly claim whether the signature is or is not valid.
Related to https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/474
-rw-r--r-- | src/camel/camel-smime-context.c | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/src/camel/camel-smime-context.c b/src/camel/camel-smime-context.c index e0eb82fa4..5444e42e5 100644 --- a/src/camel/camel-smime-context.c +++ b/src/camel/camel-smime-context.c @@ -537,6 +537,125 @@ sm_status_description (NSSCMSVerificationStatus status, } } +/* Below camel_smime_NSS_...() functions are copy&pasted from NSS, + because they are available in the header files, but not exported + in the libraries... */ +static SECOidTag +camel_smime_NSS_CMSUtil_MapSignAlgs (SECOidTag signAlg) +{ + switch (signAlg) { + case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: + return SEC_OID_MD2; + case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: + return SEC_OID_MD5; + case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: + case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: + case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST: + return SEC_OID_SHA1; + case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: + case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE: + return SEC_OID_SHA256; + case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: + case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: + return SEC_OID_SHA384; + case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: + case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: + return SEC_OID_SHA512; + default: + break; + } + /* not one of the algtags incorrectly sent to us*/ + return signAlg; +} + +static SECOidData * +camel_smime_NSS_CMSSignerInfo_GetDigestAlg (NSSCMSSignerInfo *signerinfo) +{ + SECOidData *algdata; + SECOidTag algtag; + + algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm)); + if (algdata == NULL) { + return algdata; + } + /* Windows may have given us a signer algorithm oid instead of a digest + * algorithm oid. This call will map to a signer oid to a digest one, + * otherwise it leaves the oid alone and let the chips fall as they may + * if it's not a digest oid. + */ + algtag = camel_smime_NSS_CMSUtil_MapSignAlgs (algdata->offset); + if (algtag != algdata->offset) { + /* if the tags don't match, then we must have received a signer + * algorithID. Now we need to get the oid data for the digest + * oid, which the rest of the code is expecting */ + algdata = SECOID_FindOIDByTag (algtag); + } + + return algdata; +} + +static gint +camel_smime_NSS_CMSAlgArray_GetIndexByAlgTag (SECAlgorithmID **algorithmArray, + SECOidTag algtag) +{ + SECOidData *algid; + int i = -1; + + if (algorithmArray == NULL || algorithmArray[0] == NULL) + return i; + + algid = SECOID_FindOIDByTag (algtag); + if (!algid) + return i; + for (i = 0; algorithmArray[i] != NULL; i++) { + if (SECITEM_ItemsAreEqual (&algorithmArray[i]->algorithm, &algid->oid)) + break; /* bingo */ + } + + if (algorithmArray[i] == NULL) + return -1; /* not found */ + + return i; +} + +static SECItem * +camel_smime_NSS_CMSSignedData_GetDigestValue (NSSCMSSignedData *sigd, SECOidTag digestalgtag) +{ + int n; + + if (!sigd) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + if (sigd->digestAlgorithms == NULL || sigd->digests == NULL) { + PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND); + return NULL; + } + + n = camel_smime_NSS_CMSAlgArray_GetIndexByAlgTag (sigd->digestAlgorithms, digestalgtag); + + return (n < 0) ? NULL : sigd->digests[n]; +} + +static SECItem * +camel_smime_NSS_CMSContentInfo_GetContentTypeOID (NSSCMSContentInfo *cinfo) +{ + if (cinfo == NULL) { + return NULL; + } + + if (cinfo->contentTypeTag == NULL) { + cinfo->contentTypeTag = SECOID_FindOID (&(cinfo->contentType)); + } + + if (cinfo->contentTypeTag == NULL) { + return NULL; + } + + return &(cinfo->contentTypeTag->oid); +} + static CamelCipherValidity * sm_verify_cmsg (CamelCipherContext *context, NSSCMSMessage *cmsg, @@ -667,6 +786,32 @@ sm_verify_cmsg (CamelCipherContext *context, status = NSS_CMSSignerInfo_GetVerificationStatus (si); status_description = sm_status_description (status, &sign_status); + /* certificate trust is verified first; check the signature itself, + because CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN means "valid signature, + but not trusted certificate" */ + if (status == NSSCMSVS_SigningCertNotTrusted) { + NSSCMSContentInfo *cinfo; + SECOidData *algiddata; + SECItem *contentType, *digest; + SECOidTag oidTag; + + cinfo = &(sigd->contentInfo); + + /* find digest and contentType for signerinfo */ + algiddata = camel_smime_NSS_CMSSignerInfo_GetDigestAlg (si); + oidTag = algiddata ? algiddata->offset : SEC_OID_UNKNOWN; + digest = camel_smime_NSS_CMSSignedData_GetDigestValue (sigd, oidTag); + /* NULL digest is acceptable. */ + contentType = camel_smime_NSS_CMSContentInfo_GetContentTypeOID (cinfo); + /* NULL contentType is acceptable. */ + + /* now verify signature */ + if (NSS_CMSSignerInfo_Verify (si, digest, contentType) != SECSuccess || + NSS_CMSSignerInfo_GetVerificationStatus (si) != NSSCMSVS_GoodSignature) { + sign_status = CAMEL_CIPHER_VALIDITY_SIGN_BAD; + } + } + #if defined (NSS_VMAJOR) && defined (NSS_VMINOR) && (NSS_VMAJOR > 3 || (NSS_VMAJOR == 3 && NSS_VMINOR >= 89)) if (status == NSSCMSVS_BadSignature) { if (PORT_GetError () == SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED) { |