1 //========================================================================
2 //
3 // SignatureHandler.cc
4 //
5 // This file is licensed under the GPLv2 or later
6 //
7 // Copyright 2015, 2016 André Guerreiro <aguerreiro1985@gmail.com>
8 // Copyright 2015 André Esser <bepandre@hotmail.com>
9 // Copyright 2015, 2016 Albert Astals Cid <aacid@kde.org>
10 // Copyright 2015 Markus Kilås <digital@markuspage.com>
11 //
12 //========================================================================
13 
14 #include <config.h>
15 
16 #include "SignatureHandler.h"
17 #include "goo/gmem.h"
18 #include <secmod.h>
19 
20 #include <dirent.h>
21 #include <Error.h>
22 
digestLength(SECOidTag digestAlgId)23 unsigned int SignatureHandler::digestLength(SECOidTag digestAlgId)
24 {
25   switch(digestAlgId){
26     case SEC_OID_SHA1:
27       return 20;
28     case SEC_OID_SHA256:
29       return 32;
30     case SEC_OID_SHA384:
31       return 48;
32     case SEC_OID_SHA512:
33       return 64;
34     default:
35       printf("ERROR: Unrecognized Hash ID\n");
36       return 0;
37   }
38 }
39 
getSignerName()40 char *SignatureHandler::getSignerName()
41 {
42   if (!CMSSignerInfo)
43       return NULL;
44 
45   CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB());
46   return CERT_GetCommonName(&cert->subject);
47 }
48 
getSigningTime()49 time_t SignatureHandler::getSigningTime()
50 {
51   PRTime sTime; // time in microseconds since the epoch
52 
53   if (NSS_CMSSignerInfo_GetSigningTime (CMSSignerInfo, &sTime) != SECSuccess)
54     return 0;
55 
56   return (time_t) sTime/1000000;
57 }
58 
59 
getDefaultFirefoxCertDB_Linux()60 GooString *SignatureHandler::getDefaultFirefoxCertDB_Linux()
61 {
62   GooString * finalPath = NULL;
63   DIR *toSearchIn;
64   struct dirent *subFolder;
65 
66   GooString * homePath = new GooString(getenv("HOME"));
67   homePath = homePath->append("/.mozilla/firefox/");
68 
69   if ((toSearchIn = opendir(homePath->getCString())) == NULL) {
70     error(errInternal, 0, "couldn't find default Firefox Folder");
71     delete homePath;
72     return NULL;
73   }
74   do {
75     if ((subFolder = readdir(toSearchIn)) != NULL) {
76       if (strstr(subFolder->d_name, "default") != NULL) {
77 	finalPath = homePath->append(subFolder->d_name);
78 	closedir(toSearchIn);
79 	return finalPath;
80       }
81     }
82   } while (subFolder != NULL);
83 
84   closedir(toSearchIn);
85   delete homePath;
86   return NULL;
87 }
88 
89 /**
90  * Initialise NSS
91  */
init_nss()92 void SignatureHandler::init_nss()
93 {
94   GooString *certDBPath = getDefaultFirefoxCertDB_Linux();
95   if (certDBPath == NULL) {
96     NSS_Init("sql:/etc/pki/nssdb");
97   } else {
98     NSS_Init(certDBPath->getCString());
99   }
100   //Make sure NSS root certificates module is loaded
101   SECMOD_AddNewModule("Root Certs", "libnssckbi.so", 0, 0);
102 
103   delete certDBPath;
104 }
105 
106 
SignatureHandler(unsigned char * p7,int p7_length)107 SignatureHandler::SignatureHandler(unsigned char *p7, int p7_length)
108  : CMSMessage(NULL),
109    CMSSignedData(NULL),
110    CMSSignerInfo(NULL),
111    temp_certs(NULL)
112 {
113   init_nss();
114   CMSitem.data = p7;
115   CMSitem.len = p7_length;
116   CMSMessage = CMS_MessageCreate(&CMSitem);
117   CMSSignedData = CMS_SignedDataCreate(CMSMessage);
118   CMSSignerInfo = CMS_SignerInfoCreate(CMSSignedData);
119   hash_context = initHashContext();
120 }
121 
initHashContext()122 HASHContext * SignatureHandler::initHashContext()
123 {
124 
125   SECItem usedAlgorithm = NSS_CMSSignedData_GetDigestAlgs(CMSSignedData)[0]->algorithm;
126   hash_length = digestLength(SECOID_FindOIDTag(&usedAlgorithm));
127   HASH_HashType hashType;
128   hashType    = HASH_GetHashTypeByOidTag(SECOID_FindOIDTag(&usedAlgorithm));
129   return HASH_Create(hashType);
130 }
131 
updateHash(unsigned char * data_block,int data_len)132 void SignatureHandler::updateHash(unsigned char * data_block, int data_len)
133 {
134   HASH_Update(hash_context, data_block, data_len);
135 }
136 
~SignatureHandler()137 SignatureHandler::~SignatureHandler()
138 {
139   SECITEM_FreeItem(&CMSitem, PR_FALSE);
140   if (CMSSignerInfo)
141     NSS_CMSSignerInfo_Destroy(CMSSignerInfo);
142   if (CMSSignedData)
143     NSS_CMSSignedData_Destroy(CMSSignedData);
144   if (CMSMessage)
145     NSS_CMSMessage_Destroy(CMSMessage);
146 
147   if (hash_context)
148     HASH_Destroy(hash_context);
149 
150   free(temp_certs);
151 
152   if (NSS_Shutdown()!=SECSuccess)
153     fprintf(stderr, "Detail: %s\n", PR_ErrorToString(PORT_GetError(), PR_LANGUAGE_I_DEFAULT));
154 }
155 
CMS_MessageCreate(SECItem * cms_item)156 NSSCMSMessage *SignatureHandler::CMS_MessageCreate(SECItem * cms_item)
157 {
158   if (cms_item->data){
159     return NSS_CMSMessage_CreateFromDER(cms_item, NULL, NULL /* Content callback */
160                         , NULL, NULL /*Password callback*/
161                         , NULL, NULL /*Decrypt callback*/);
162   } else {
163     return NULL;
164   }
165 }
166 
CMS_SignedDataCreate(NSSCMSMessage * cms_msg)167 NSSCMSSignedData *SignatureHandler::CMS_SignedDataCreate(NSSCMSMessage * cms_msg)
168 {
169   if (!NSS_CMSMessage_IsSigned(cms_msg)) {
170     error(errInternal, 0, "Input couldn't be parsed as a CMS signature");
171     return NULL;
172   }
173 
174   NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(cms_msg, 0);
175   if (!cinfo) {
176     error(errInternal, 0, "Error in NSS_CMSMessage_ContentLevel");
177     return NULL;
178   }
179 
180   NSSCMSSignedData *signedData = (NSSCMSSignedData*) NSS_CMSContentInfo_GetContent(cinfo);
181   if (!signedData) {
182     error(errInternal, 0, "CError in NSS_CMSContentInfo_GetContent()");
183     return NULL;
184   }
185 
186   if (signedData->rawCerts)
187   {
188     size_t i;
189     for (i = 0; signedData->rawCerts[i]; ++i) {} // just count the length of the certificate chain
190 
191     // tempCerts field needs to be filled for complete memory release by NSSCMSSignedData_Destroy
192     signedData->tempCerts = (CERTCertificate **) gmallocn( i+1, sizeof(CERTCertificate *));
193     memset(signedData->tempCerts, 0, (i+1) * sizeof(CERTCertificate *));
194     // store the adresses of these temporary certificates for future release
195     for (i = 0; signedData->rawCerts[i]; ++i)
196       signedData->tempCerts[i] = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), signedData->rawCerts[i], NULL, 0, 0);
197 
198     temp_certs = signedData->tempCerts;
199     return signedData;
200   } else {
201     return NULL;
202   }
203 }
204 
CMS_SignerInfoCreate(NSSCMSSignedData * cms_sig_data)205 NSSCMSSignerInfo *SignatureHandler::CMS_SignerInfoCreate(NSSCMSSignedData * cms_sig_data)
206 {
207   NSSCMSSignerInfo *signerInfo = NSS_CMSSignedData_GetSignerInfo(cms_sig_data, 0);
208   if (!signerInfo) {
209     printf("Error in NSS_CMSSignedData_GetSignerInfo()\n");
210     return NULL;
211   } else {
212     return signerInfo;
213   }
214 }
215 
validateSignature()216 NSSCMSVerificationStatus SignatureHandler::validateSignature()
217 {
218   unsigned char *digest_buffer = NULL;
219 
220   if (!CMSSignedData)
221     return NSSCMSVS_MalformedSignature;
222 
223   digest_buffer = (unsigned char *)PORT_Alloc(hash_length);
224   unsigned int result_len = 0;
225 
226   HASH_End(hash_context, digest_buffer, &result_len, hash_length);
227 
228   SECItem digest;
229   digest.data = digest_buffer;
230   digest.len = hash_length;
231 
232   if ((NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == NULL)
233     CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
234 
235   SECItem * content_info_data = CMSSignedData->contentInfo.content.data;
236   if (content_info_data != NULL && content_info_data->data != NULL)
237   {
238     /*
239       This means it's not a detached type signature
240       so the digest is contained in SignedData->contentInfo
241     */
242     if (memcmp(digest.data, content_info_data->data, hash_length) == 0
243         && digest.len == content_info_data->len)
244     {
245       PORT_Free(digest_buffer);
246       return NSSCMSVS_GoodSignature;
247     }
248     else
249     {
250       PORT_Free(digest_buffer);
251       return NSSCMSVS_DigestMismatch;
252     }
253 
254   }
255   else if (NSS_CMSSignerInfo_Verify(CMSSignerInfo, &digest, NULL) != SECSuccess)
256   {
257 
258     PORT_Free(digest_buffer);
259     return CMSSignerInfo->verificationStatus;
260   }
261   else
262   {
263     PORT_Free(digest_buffer);
264     return NSSCMSVS_GoodSignature;
265   }
266 }
267 
validateCertificate()268 SECErrorCodes SignatureHandler::validateCertificate()
269 {
270   SECErrorCodes retVal;
271   CERTCertificate *cert;
272 
273   if (!CMSSignerInfo)
274     return (SECErrorCodes) -1; //error code to avoid matching error codes defined in SECErrorCodes
275 
276   if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == NULL)
277     CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
278 
279   CERTValInParam inParams[2];
280   inParams[0].type = cert_pi_revocationFlags;
281   inParams[0].value.pointer.revocation = CERT_GetClassicOCSPEnabledSoftFailurePolicy();
282   inParams[1].type = cert_pi_end;
283 
284   CERT_PKIXVerifyCert(cert, certificateUsageEmailSigner, inParams, NULL,
285                 CMSSignerInfo->cmsg->pwfn_arg);
286 
287   retVal = (SECErrorCodes) PORT_GetError();
288 
289   if (cert)
290     CERT_DestroyCertificate(cert);
291 
292   return retVal;
293 }
294 
295 
NSS_SigTranslate(NSSCMSVerificationStatus nss_code)296 SignatureValidationStatus SignatureHandler::NSS_SigTranslate(NSSCMSVerificationStatus nss_code)
297 {
298   switch(nss_code)
299   {
300     case NSSCMSVS_GoodSignature:
301       return SIGNATURE_VALID;
302 
303     case NSSCMSVS_BadSignature:
304       return SIGNATURE_INVALID;
305 
306       case NSSCMSVS_DigestMismatch:
307       return SIGNATURE_DIGEST_MISMATCH;
308 
309     case NSSCMSVS_ProcessingError:
310       return SIGNATURE_DECODING_ERROR;
311 
312     default:
313       return SIGNATURE_GENERIC_ERROR;
314   }
315 }
316 
NSS_CertTranslate(SECErrorCodes nss_code)317 CertificateValidationStatus SignatureHandler::NSS_CertTranslate(SECErrorCodes nss_code)
318 {
319   // 0 not defined in SECErrorCodes, it means success for this purpose.
320   if (nss_code == (SECErrorCodes) 0)
321     return CERTIFICATE_TRUSTED;
322 
323   switch(nss_code)
324   {
325     case SEC_ERROR_UNKNOWN_ISSUER:
326       return CERTIFICATE_UNKNOWN_ISSUER;
327 
328     case SEC_ERROR_UNTRUSTED_ISSUER:
329       return CERTIFICATE_UNTRUSTED_ISSUER;
330 
331     case SEC_ERROR_REVOKED_CERTIFICATE:
332       return CERTIFICATE_REVOKED;
333 
334     case SEC_ERROR_EXPIRED_CERTIFICATE:
335       return CERTIFICATE_EXPIRED;
336 
337     default:
338       return CERTIFICATE_GENERIC_ERROR;
339   }
340 }
341