1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * The Initial Developer of the Original Code is Netscape
4  * Communications Corporation.  Portions created by Netscape are
5  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
6  * Rights Reserved.
7  *
8  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
9  *
10  * This library is free software: you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
17  * for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this library. If not, see <http://www.gnu.org/licenses/>.
21  *
22  * Authors: Jeffrey Stedfast <fejj@ximian.com>
23  *          Michael Zucchi <notzed@ximian.com>
24  */
25 
26 #include "evolution-data-server-config.h"
27 
28 #ifdef ENABLE_SMIME
29 
30 #include "nss.h"
31 #include <cms.h>
32 #include <cert.h>
33 #include <certdb.h>
34 #include <pkcs11.h>
35 #include <smime.h>
36 #include <secerr.h>
37 #include <pkcs11t.h>
38 #include <pk11func.h>
39 #include <secoid.h>
40 
41 #include <errno.h>
42 
43 #include <glib/gi18n-lib.h>
44 
45 #include "camel-data-wrapper.h"
46 #include "camel-mime-filter-basic.h"
47 #include "camel-mime-filter-canon.h"
48 #include "camel-mime-part.h"
49 #include "camel-multipart-signed.h"
50 #include "camel-operation.h"
51 #include "camel-session.h"
52 #include "camel-smime-context.h"
53 #include "camel-stream-filter.h"
54 #include "camel-stream-fs.h"
55 #include "camel-stream-mem.h"
56 #include "camel-string-utils.h"
57 
58 #define d(x)
59 
60 struct _CamelSMIMEContextPrivate {
61 	CERTCertDBHandle *certdb;
62 
63 	gchar *encrypt_key;
64 	CamelSMIMESign sign_mode;
65 
66 	gint password_tries;
67 	guint send_encrypt_key_prefs : 1;
68 };
69 
G_DEFINE_TYPE_WITH_PRIVATE(CamelSMIMEContext,camel_smime_context,CAMEL_TYPE_CIPHER_CONTEXT)70 G_DEFINE_TYPE_WITH_PRIVATE (CamelSMIMEContext, camel_smime_context, CAMEL_TYPE_CIPHER_CONTEXT)
71 
72 static void
73 smime_cert_data_free (gpointer cert_data)
74 {
75 	g_return_if_fail (cert_data != NULL);
76 
77 	CERT_DestroyCertificate (cert_data);
78 }
79 
80 static gpointer
smime_cert_data_clone(gpointer cert_data)81 smime_cert_data_clone (gpointer cert_data)
82 {
83 	g_return_val_if_fail (cert_data != NULL, NULL);
84 
85 	return CERT_DupCertificate (cert_data);
86 }
87 
88 /* used for decode content callback, for streaming decode */
89 static void
sm_write_stream(gpointer arg,const gchar * buf,gulong len)90 sm_write_stream (gpointer arg,
91                  const gchar *buf,
92                  gulong len)
93 {
94 	camel_stream_write ((CamelStream *) arg, buf, len, NULL, NULL);
95 }
96 
97 static PK11SymKey *
sm_decrypt_key(gpointer arg,SECAlgorithmID * algid)98 sm_decrypt_key (gpointer arg,
99                 SECAlgorithmID *algid)
100 {
101 	printf ("Decrypt key called\n");
102 	return (PK11SymKey *) arg;
103 }
104 
105 static const gchar *
nss_error_to_string(glong errorcode)106 nss_error_to_string (glong errorcode)
107 {
108 #define cs(a,b) case a: return b;
109 
110 	switch (errorcode) {
111 	cs (SEC_ERROR_IO, "An I/O error occurred during security authorization.")
112 	cs (SEC_ERROR_LIBRARY_FAILURE, "security library failure.")
113 	cs (SEC_ERROR_BAD_DATA, "security library: received bad data.")
114 	cs (SEC_ERROR_OUTPUT_LEN, "security library: output length error.")
115 	cs (SEC_ERROR_INPUT_LEN, "security library has experienced an input length error.")
116 	cs (SEC_ERROR_INVALID_ARGS, "security library: invalid arguments.")
117 	cs (SEC_ERROR_INVALID_ALGORITHM, "security library: invalid algorithm.")
118 	cs (SEC_ERROR_INVALID_AVA, "security library: invalid AVA.")
119 	cs (SEC_ERROR_INVALID_TIME, "Improperly formatted time string.")
120 	cs (SEC_ERROR_BAD_DER, "security library: improperly formatted DER-encoded message.")
121 	cs (SEC_ERROR_BAD_SIGNATURE, "Peer's certificate has an invalid signature.")
122 	cs (SEC_ERROR_EXPIRED_CERTIFICATE, "Peer's Certificate has expired.")
123 	cs (SEC_ERROR_REVOKED_CERTIFICATE, "Peer's Certificate has been revoked.")
124 	cs (SEC_ERROR_UNKNOWN_ISSUER, "Peer's Certificate issuer is not recognized.")
125 	cs (SEC_ERROR_BAD_KEY, "Peer's public key is invalid.")
126 	cs (SEC_ERROR_BAD_PASSWORD, "The security password entered is incorrect.")
127 	cs (SEC_ERROR_RETRY_PASSWORD, "New password entered incorrectly.  Please try again.")
128 	cs (SEC_ERROR_NO_NODELOCK, "security library: no nodelock.")
129 	cs (SEC_ERROR_BAD_DATABASE, "security library: bad database.")
130 	cs (SEC_ERROR_NO_MEMORY, "security library: memory allocation failure.")
131 	cs (SEC_ERROR_UNTRUSTED_ISSUER, "Peer's certificate issuer has been marked as not trusted by the user.")
132 	cs (SEC_ERROR_UNTRUSTED_CERT, "Peer's certificate has been marked as not trusted by the user.")
133 	cs (SEC_ERROR_DUPLICATE_CERT, "Certificate already exists in your database.")
134 	cs (SEC_ERROR_DUPLICATE_CERT_NAME, "Downloaded certificate's name duplicates one already in your database.")
135 	cs (SEC_ERROR_ADDING_CERT, "Error adding certificate to database.")
136 	cs (SEC_ERROR_FILING_KEY, "Error refiling the key for this certificate.")
137 	cs (SEC_ERROR_NO_KEY, "The private key for this certificate cannot be found in key database")
138 	cs (SEC_ERROR_CERT_VALID, "This certificate is valid.")
139 	cs (SEC_ERROR_CERT_NOT_VALID, "This certificate is not valid.")
140 	cs (SEC_ERROR_CERT_NO_RESPONSE, "Cert Library: No Response")
141 	cs (SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE, "The certificate issuer's certificate has expired.  Check your system date and time.")
142 	cs (SEC_ERROR_CRL_EXPIRED, "The CRL for the certificate's issuer has expired.  Update it or check your system date and time.")
143 	cs (SEC_ERROR_CRL_BAD_SIGNATURE, "The CRL for the certificate's issuer has an invalid signature.")
144 	cs (SEC_ERROR_CRL_INVALID, "New CRL has an invalid format.")
145 	cs (SEC_ERROR_EXTENSION_VALUE_INVALID, "Certificate extension value is invalid.")
146 	cs (SEC_ERROR_EXTENSION_NOT_FOUND, "Certificate extension not found.")
147 	cs (SEC_ERROR_CA_CERT_INVALID, "Issuer certificate is invalid.")
148 	cs (SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID, "Certificate path length constraint is invalid.")
149 	cs (SEC_ERROR_CERT_USAGES_INVALID, "Certificate usages field is invalid.")
150 	cs (SEC_INTERNAL_ONLY, "**Internal ONLY module**")
151 	cs (SEC_ERROR_INVALID_KEY, "The key does not support the requested operation.")
152 	cs (SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION, "Certificate contains unknown critical extension.")
153 	cs (SEC_ERROR_OLD_CRL, "New CRL is not later than the current one.")
154 	cs (SEC_ERROR_NO_EMAIL_CERT, "Not encrypted or signed: you do not yet have an email certificate.")
155 	cs (SEC_ERROR_NO_RECIPIENT_CERTS_QUERY, "Not encrypted: you do not have certificates for each of the recipients.")
156 	cs (SEC_ERROR_NOT_A_RECIPIENT, "Cannot decrypt: you are not a recipient, or matching certificate and private key not found.")
157 	cs (SEC_ERROR_PKCS7_KEYALG_MISMATCH, "Cannot decrypt: key encryption algorithm does not match your certificate.")
158 	cs (SEC_ERROR_PKCS7_BAD_SIGNATURE, "Signature verification failed: no signer found, too many signers found, or improper or corrupted data.")
159 	cs (SEC_ERROR_UNSUPPORTED_KEYALG, "Unsupported or unknown key algorithm.")
160 	cs (SEC_ERROR_DECRYPTION_DISALLOWED, "Cannot decrypt: encrypted using a disallowed algorithm or key size.")
161 	cs (XP_SEC_FORTEZZA_BAD_CARD, "Fortezza card has not been properly initialized.  Please remove it and return it to your issuer.")
162 	cs (XP_SEC_FORTEZZA_NO_CARD, "No Fortezza cards Found")
163 	cs (XP_SEC_FORTEZZA_NONE_SELECTED, "No Fortezza card selected")
164 	cs (XP_SEC_FORTEZZA_MORE_INFO, "Please select a personality to get more info on")
165 	cs (XP_SEC_FORTEZZA_PERSON_NOT_FOUND, "Personality not found")
166 	cs (XP_SEC_FORTEZZA_NO_MORE_INFO, "No more information on that Personality")
167 	cs (XP_SEC_FORTEZZA_BAD_PIN, "Invalid Pin")
168 	cs (XP_SEC_FORTEZZA_PERSON_ERROR, "Couldn't initialize Fortezza personalities.")
169 	cs (SEC_ERROR_NO_KRL, "No KRL for this site's certificate has been found.")
170 	cs (SEC_ERROR_KRL_EXPIRED, "The KRL for this site's certificate has expired.")
171 	cs (SEC_ERROR_KRL_BAD_SIGNATURE, "The KRL for this site's certificate has an invalid signature.")
172 	cs (SEC_ERROR_REVOKED_KEY, "The key for this site's certificate has been revoked.")
173 	cs (SEC_ERROR_KRL_INVALID, "New KRL has an invalid format.")
174 	cs (SEC_ERROR_NEED_RANDOM, "security library: need random data.")
175 	cs (SEC_ERROR_NO_MODULE, "security library: no security module can perform the requested operation.")
176 	cs (SEC_ERROR_NO_TOKEN, "The security card or token does not exist, needs to be initialized, or has been removed.")
177 	cs (SEC_ERROR_READ_ONLY, "security library: read-only database.")
178 	cs (SEC_ERROR_NO_SLOT_SELECTED, "No slot or token was selected.")
179 	cs (SEC_ERROR_CERT_NICKNAME_COLLISION, "A certificate with the same nickname already exists.")
180 	cs (SEC_ERROR_KEY_NICKNAME_COLLISION, "A key with the same nickname already exists.")
181 	cs (SEC_ERROR_SAFE_NOT_CREATED, "error while creating safe object")
182 	cs (SEC_ERROR_BAGGAGE_NOT_CREATED, "error while creating baggage object")
183 	cs (XP_JAVA_REMOVE_PRINCIPAL_ERROR, "Couldn't remove the principal")
184 	cs (XP_JAVA_DELETE_PRIVILEGE_ERROR, "Couldn't delete the privilege")
185 	cs (XP_JAVA_CERT_NOT_EXISTS_ERROR, "This principal doesn't have a certificate")
186 	cs (SEC_ERROR_BAD_EXPORT_ALGORITHM, "Required algorithm is not allowed.")
187 	cs (SEC_ERROR_EXPORTING_CERTIFICATES, "Error attempting to export certificates.")
188 	cs (SEC_ERROR_IMPORTING_CERTIFICATES, "Error attempting to import certificates.")
189 	cs (SEC_ERROR_PKCS12_DECODING_PFX, "Unable to import.  Decoding error.  File not valid.")
190 	cs (SEC_ERROR_PKCS12_INVALID_MAC, "Unable to import.  Invalid MAC.  Incorrect password or corrupt file.")
191 	cs (SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM, "Unable to import.  MAC algorithm not supported.")
192 	cs (SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE, "Unable to import.  Only password integrity and privacy modes supported.")
193 	cs (SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE, "Unable to import.  File structure is corrupt.")
194 	cs (SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM, "Unable to import.  Encryption algorithm not supported.")
195 	cs (SEC_ERROR_PKCS12_UNSUPPORTED_VERSION, "Unable to import.  File version not supported.")
196 	cs (SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT, "Unable to import.  Incorrect privacy password.")
197 	cs (SEC_ERROR_PKCS12_CERT_COLLISION, "Unable to import.  Same nickname already exists in database.")
198 	cs (SEC_ERROR_USER_CANCELLED, "The user pressed cancel.")
199 	cs (SEC_ERROR_PKCS12_DUPLICATE_DATA, "Not imported, already in database.")
200 	cs (SEC_ERROR_MESSAGE_SEND_ABORTED, "Message not sent.")
201 	cs (SEC_ERROR_INADEQUATE_KEY_USAGE, "Certificate key usage inadequate for attempted operation.")
202 	cs (SEC_ERROR_INADEQUATE_CERT_TYPE, "Certificate type not approved for application.")
203 	cs (SEC_ERROR_CERT_ADDR_MISMATCH, "Address in signing certificate does not match address in message headers.")
204 	cs (SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY, "Unable to import.  Error attempting to import private key.")
205 	cs (SEC_ERROR_PKCS12_IMPORTING_CERT_CHAIN, "Unable to import.  Error attempting to import certificate chain.")
206 	cs (SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME, "Unable to export.  Unable to locate certificate or key by nickname.")
207 	cs (SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY, "Unable to export.  Private Key could not be located and exported.")
208 	cs (SEC_ERROR_PKCS12_UNABLE_TO_WRITE, "Unable to export.  Unable to write the export file.")
209 	cs (SEC_ERROR_PKCS12_UNABLE_TO_READ, "Unable to import.  Unable to read the import file.")
210 	cs (SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED, "Unable to export.  Key database corrupt or deleted.")
211 	cs (SEC_ERROR_KEYGEN_FAIL, "Unable to generate public/private key pair.")
212 	cs (SEC_ERROR_INVALID_PASSWORD, "Password entered is invalid.  Please pick a different one.")
213 	cs (SEC_ERROR_RETRY_OLD_PASSWORD, "Old password entered incorrectly.  Please try again.")
214 	cs (SEC_ERROR_BAD_NICKNAME, "Certificate nickname already in use.")
215 	cs (SEC_ERROR_NOT_FORTEZZA_ISSUER, "Peer FORTEZZA chain has a non-FORTEZZA Certificate.")
216 	cs (SEC_ERROR_CANNOT_MOVE_SENSITIVE_KEY, "A sensitive key cannot be moved to the slot where it is needed.")
217 	cs (SEC_ERROR_JS_INVALID_MODULE_NAME, "Invalid module name.")
218 	cs (SEC_ERROR_JS_INVALID_DLL, "Invalid module path/filename")
219 	cs (SEC_ERROR_JS_ADD_MOD_FAILURE, "Unable to add module")
220 	cs (SEC_ERROR_JS_DEL_MOD_FAILURE, "Unable to delete module")
221 	cs (SEC_ERROR_OLD_KRL, "New KRL is not later than the current one.")
222 	cs (SEC_ERROR_CKL_CONFLICT, "New CKL has different issuer than current CKL.  Delete current CKL.")
223 	cs (SEC_ERROR_CERT_NOT_IN_NAME_SPACE, "The Certifying Authority for this certificate is not permitted to issue a certificate with this name.")
224 	cs (SEC_ERROR_KRL_NOT_YET_VALID, "The key revocation list for this certificate is not yet valid.")
225 	cs (SEC_ERROR_CRL_NOT_YET_VALID, "The certificate revocation list for this certificate is not yet valid.")
226 	cs (SEC_ERROR_UNKNOWN_CERT, "The requested certificate could not be found.")
227 	cs (SEC_ERROR_UNKNOWN_SIGNER, "The signer's certificate could not be found.")
228 	cs (SEC_ERROR_CERT_BAD_ACCESS_LOCATION,	 "The location for the certificate status server has invalid format.")
229 	cs (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE, "The OCSP response cannot be fully decoded; it is of an unknown type.")
230 	cs (SEC_ERROR_OCSP_BAD_HTTP_RESPONSE, "The OCSP server returned unexpected/invalid HTTP data.")
231 	cs (SEC_ERROR_OCSP_MALFORMED_REQUEST, "The OCSP server found the request to be corrupted or improperly formed.")
232 	cs (SEC_ERROR_OCSP_SERVER_ERROR, "The OCSP server experienced an internal error.")
233 	cs (SEC_ERROR_OCSP_TRY_SERVER_LATER, "The OCSP server suggests trying again later.")
234 	cs (SEC_ERROR_OCSP_REQUEST_NEEDS_SIG, "The OCSP server requires a signature on this request.")
235 	cs (SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST, "The OCSP server has refused this request as unauthorized.")
236 	cs (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, "The OCSP server returned an unrecognizable status.")
237 	cs (SEC_ERROR_OCSP_UNKNOWN_CERT, "The OCSP server has no status for the certificate.")
238 	cs (SEC_ERROR_OCSP_NOT_ENABLED, "You must enable OCSP before performing this operation.")
239 	cs (SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER, "You must set the OCSP default responder before performing this operation.")
240 	cs (SEC_ERROR_OCSP_MALFORMED_RESPONSE, "The response from the OCSP server was corrupted or improperly formed.")
241 	cs (SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE, "The signer of the OCSP response is not authorized to give status for this certificate.")
242 	cs (SEC_ERROR_OCSP_FUTURE_RESPONSE, "The OCSP response is not yet valid (contains a date in the future).")
243 	cs (SEC_ERROR_OCSP_OLD_RESPONSE, "The OCSP response contains out-of-date information.")
244 	cs (SEC_ERROR_DIGEST_NOT_FOUND, "The CMS or PKCS #7 Digest was not found in signed message.")
245 	cs (SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE, "The CMS or PKCS #7 Message type is unsupported.")
246 	cs (SEC_ERROR_MODULE_STUCK, "PKCS #11 module could not be removed because it is still in use.")
247 	cs (SEC_ERROR_BAD_TEMPLATE, "Could not decode ASN.1 data. Specified template was invalid.")
248 	cs (SEC_ERROR_CRL_NOT_FOUND, "No matching CRL was found.")
249 	cs (SEC_ERROR_REUSED_ISSUER_AND_SERIAL, "You are attempting to import a cert with the same issuer/serial as an existing cert, but that is not the same cert.")
250 	cs (SEC_ERROR_BUSY, "NSS could not shutdown. Objects are still in use.")
251 	cs (SEC_ERROR_EXTRA_INPUT, "DER-encoded message contained extra unused data.")
252 	cs (SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE, "Unsupported elliptic curve.")
253 	cs (SEC_ERROR_UNSUPPORTED_EC_POINT_FORM, "Unsupported elliptic curve point form.")
254 	cs (SEC_ERROR_UNRECOGNIZED_OID, "Unrecognized Object Identifier.")
255 	cs (SEC_ERROR_OCSP_INVALID_SIGNING_CERT, "Invalid OCSP signing certificate in OCSP response.")
256 	cs (SEC_ERROR_REVOKED_CERTIFICATE_CRL, "Certificate is revoked in issuer's certificate revocation list.")
257 	cs (SEC_ERROR_REVOKED_CERTIFICATE_OCSP, "Issuer's OCSP responder reports certificate is revoked.")
258 	cs (SEC_ERROR_CRL_INVALID_VERSION, "Issuer's Certificate Revocation List has an unknown version number.")
259 	cs (SEC_ERROR_CRL_V1_CRITICAL_EXTENSION, "Issuer's V1 Certificate Revocation List has a critical extension.")
260 	cs (SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION, "Issuer's V2 Certificate Revocation List has an unknown critical extension.")
261 	cs (SEC_ERROR_UNKNOWN_OBJECT_TYPE, "Unknown object type specified.")
262 	cs (SEC_ERROR_INCOMPATIBLE_PKCS11, "PKCS #11 driver violates the spec in an incompatible way.")
263 	cs (SEC_ERROR_NO_EVENT, "No new slot event is available at this time.")
264 	cs (SEC_ERROR_CRL_ALREADY_EXISTS, "CRL already exists.")
265 	cs (SEC_ERROR_NOT_INITIALIZED, "NSS is not initialized.")
266 	cs (SEC_ERROR_TOKEN_NOT_LOGGED_IN, "The operation failed because the PKCS#11 token is not logged in.")
267 	cs (SEC_ERROR_OCSP_RESPONDER_CERT_INVALID, "Configured OCSP responder's certificate is invalid.")
268 	cs (SEC_ERROR_OCSP_BAD_SIGNATURE, "OCSP response has an invalid signature.")
269 
270 	#if defined (NSS_VMAJOR) && defined (NSS_VMINOR) && defined (NSS_VPATCH) && (NSS_VMAJOR > 3 || (NSS_VMAJOR == 3 && NSS_VMINOR > 12) || (NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH >= 2))
271 	cs (SEC_ERROR_OUT_OF_SEARCH_LIMITS, "Cert validation search is out of search limits")
272 	cs (SEC_ERROR_INVALID_POLICY_MAPPING, "Policy mapping contains anypolicy")
273 	cs (SEC_ERROR_POLICY_VALIDATION_FAILED, "Cert chain fails policy validation")
274 	cs (SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE, "Unknown location type in cert AIA extension")
275 	cs (SEC_ERROR_BAD_HTTP_RESPONSE, "Server returned bad HTTP response")
276 	cs (SEC_ERROR_BAD_LDAP_RESPONSE, "Server returned bad LDAP response")
277 	cs (SEC_ERROR_FAILED_TO_ENCODE_DATA, "Failed to encode data with ASN1 encoder")
278 	cs (SEC_ERROR_BAD_INFO_ACCESS_LOCATION, "Bad information access location in cert extension")
279 	cs (SEC_ERROR_LIBPKIX_INTERNAL, "Libpkix internal error occurred during cert validation.")
280 	cs (SEC_ERROR_PKCS11_GENERAL_ERROR, "A PKCS #11 module returned CKR_GENERAL_ERROR, indicating that an unrecoverable error has occurred.")
281 	cs (SEC_ERROR_PKCS11_FUNCTION_FAILED, "A PKCS #11 module returned CKR_FUNCTION_FAILED, indicating that the requested function could not be performed.  Trying the same operation again might succeed.")
282 	cs (SEC_ERROR_PKCS11_DEVICE_ERROR, "A PKCS #11 module returned CKR_DEVICE_ERROR, indicating that a problem has occurred with the token or slot.")
283 	#endif
284 	}
285 
286 	#undef cs
287 
288 	return NULL;
289 }
290 
291 static void
set_nss_error(GError ** error,const gchar * def_error)292 set_nss_error (GError **error,
293                const gchar *def_error)
294 {
295 	glong err_code;
296 
297 	g_return_if_fail (def_error != NULL);
298 
299 	err_code = PORT_GetError ();
300 
301 	if (!err_code) {
302 		g_set_error (
303 			error, CAMEL_SERVICE_ERROR,
304 			CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
305 			"%s", def_error);
306 	} else {
307 		const gchar *err_str;
308 
309 		err_str = nss_error_to_string (err_code);
310 		if (!err_str)
311 			err_str = "Uknown error.";
312 
313 		g_set_error (
314 			error, CAMEL_SERVICE_ERROR,
315 			CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
316 			"%s (%d) - %s", err_str, (gint) err_code, def_error);
317 	}
318 }
319 
320 static NSSCMSMessage *
sm_signing_cmsmessage(CamelSMIMEContext * context,const gchar * nick,SECOidTag * hash,gint detached,GError ** error)321 sm_signing_cmsmessage (CamelSMIMEContext *context,
322                        const gchar *nick,
323                        SECOidTag *hash,
324                        gint detached,
325                        GError **error)
326 {
327 	CamelSMIMEContextPrivate *p = context->priv;
328 	NSSCMSMessage *cmsg = NULL;
329 	NSSCMSContentInfo *cinfo;
330 	NSSCMSSignedData *sigd;
331 	NSSCMSSignerInfo *signerinfo;
332 	CERTCertificate *cert= NULL, *ekpcert = NULL;
333 
334 	g_return_val_if_fail (hash != NULL, NULL);
335 
336 	if ((cert = CERT_FindUserCertByUsage (p->certdb,
337 					     (gchar *) nick,
338 					     certUsageEmailSigner,
339 					     PR_TRUE,
340 					     NULL)) == NULL) {
341 		g_set_error (
342 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
343 			_("Cannot find certificate for “%s”"), nick);
344 		return NULL;
345 	}
346 
347 	if (*hash == SEC_OID_UNKNOWN) {
348 		/* use signature algorithm from the certificate */
349 		switch (SECOID_GetAlgorithmTag (&cert->signature)) {
350 		default:
351 		case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
352 			*hash = SEC_OID_SHA256;
353 			break;
354 		case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
355 			*hash = SEC_OID_SHA384;
356 			break;
357 		case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
358 			*hash = SEC_OID_SHA512;
359 			break;
360 		case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
361 			*hash = SEC_OID_MD5;
362 			break;
363 		case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
364 			*hash = SEC_OID_SHA1;
365 			break;
366 		}
367 	}
368 
369 	cmsg = NSS_CMSMessage_Create (NULL); /* create a message on its own pool */
370 	if (cmsg == NULL) {
371 		set_nss_error (error, _("Cannot create CMS message"));
372 		goto fail;
373 	}
374 
375 	if ((sigd = NSS_CMSSignedData_Create (cmsg)) == NULL) {
376 		set_nss_error (error, _("Cannot create CMS signed data"));
377 		goto fail;
378 	}
379 
380 	cinfo = NSS_CMSMessage_GetContentInfo (cmsg);
381 	if (NSS_CMSContentInfo_SetContent_SignedData (cmsg, cinfo, sigd) != SECSuccess) {
382 		set_nss_error (error, _("Cannot attach CMS signed data"));
383 		goto fail;
384 	}
385 
386 	/* if !detatched, the contentinfo will alloc a data item for us */
387 	cinfo = NSS_CMSSignedData_GetContentInfo (sigd);
388 	if (NSS_CMSContentInfo_SetContent_Data (cmsg, cinfo, NULL, detached) != SECSuccess) {
389 		set_nss_error (error, _("Cannot attach CMS data"));
390 		goto fail;
391 	}
392 
393 	signerinfo = NSS_CMSSignerInfo_Create (cmsg, cert, *hash);
394 	if (signerinfo == NULL) {
395 		set_nss_error (error, _("Cannot create CMS Signer information"));
396 		goto fail;
397 	}
398 
399 	/* we want the cert chain included for this one */
400 	if (NSS_CMSSignerInfo_IncludeCerts (signerinfo, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) {
401 		set_nss_error (error, _("Cannot find certificate chain"));
402 		goto fail;
403 	}
404 
405 	/* SMIME RFC says signing time should always be added */
406 	if (NSS_CMSSignerInfo_AddSigningTime (signerinfo, PR_Now ()) != SECSuccess) {
407 		set_nss_error (error, _("Cannot add CMS Signing time"));
408 		goto fail;
409 	}
410 
411 #if 0
412 	/* this can but needn't be added.  not sure what general usage is */
413 	if (NSS_CMSSignerInfo_AddSMIMECaps (signerinfo) != SECSuccess) {
414 		fprintf (stderr, "ERROR: cannot add SMIMECaps attribute.\n");
415 		goto loser;
416 	}
417 #endif
418 
419 	/* Check if we need to send along our return encrypt cert, rfc2633 2.5.3 */
420 	if (p->send_encrypt_key_prefs) {
421 		CERTCertificate *enccert = NULL;
422 
423 		if (p->encrypt_key) {
424 			/* encrypt key has its own nick */
425 			if ((ekpcert = CERT_FindUserCertByUsage (
426 				     p->certdb,
427 				     p->encrypt_key,
428 				     certUsageEmailRecipient, PR_TRUE, NULL)) == NULL) {
429 				g_set_error (
430 					error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
431 					_("Encryption certificate for “%s” does not exist"),
432 					p->encrypt_key);
433 				goto fail;
434 			}
435 			enccert = ekpcert;
436 		} else if (CERT_CheckCertUsage (cert, certUsageEmailRecipient) == SECSuccess) {
437 			/* encrypt key is signing key */
438 			enccert = cert;
439 		} else {
440 			/* encrypt key uses same nick */
441 			if ((ekpcert = CERT_FindUserCertByUsage (
442 				     p->certdb, (gchar *) nick,
443 				     certUsageEmailRecipient, PR_TRUE, NULL)) == NULL) {
444 				g_set_error (
445 					error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
446 					_("Encryption certificate for “%s” does not exist"), nick);
447 				goto fail;
448 			}
449 			enccert = ekpcert;
450 		}
451 
452 		if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs (signerinfo, enccert, p->certdb) != SECSuccess) {
453 			set_nss_error (error, _("Cannot add SMIMEEncKeyPrefs attribute"));
454 			goto fail;
455 		}
456 
457 		if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs (signerinfo, enccert, p->certdb) != SECSuccess) {
458 			set_nss_error (error, _("Cannot add MS SMIMEEncKeyPrefs attribute"));
459 			goto fail;
460 		}
461 
462 		if (ekpcert != NULL && NSS_CMSSignedData_AddCertificate (sigd, ekpcert) != SECSuccess) {
463 			set_nss_error (error, _("Cannot add encryption certificate"));
464 			goto fail;
465 		}
466 	}
467 
468 	if (NSS_CMSSignedData_AddSignerInfo (sigd, signerinfo) != SECSuccess) {
469 		set_nss_error (error, _("Cannot add CMS Signer information"));
470 		goto fail;
471 	}
472 
473 	if (ekpcert)
474 		CERT_DestroyCertificate (ekpcert);
475 
476 	if (cert)
477 		CERT_DestroyCertificate (cert);
478 
479 	return cmsg;
480 fail:
481 	if (ekpcert)
482 		CERT_DestroyCertificate (ekpcert);
483 
484 	if (cert)
485 		CERT_DestroyCertificate (cert);
486 
487 	NSS_CMSMessage_Destroy (cmsg);
488 
489 	return NULL;
490 }
491 
492 static const gchar *
sm_status_description(NSSCMSVerificationStatus status)493 sm_status_description (NSSCMSVerificationStatus status)
494 {
495 	/* could use this but then we can't control i18n? */
496 	/*NSS_CMSUtil_VerificationStatusToString (status));*/
497 
498 	switch (status) {
499 	case NSSCMSVS_Unverified:
500 	default:
501 		/* Translators: A fallback message when couldn't verify an SMIME signature */
502 		return _("Unverified");
503 	case NSSCMSVS_GoodSignature:
504 		return _("Good signature");
505 	case NSSCMSVS_BadSignature:
506 		return _("Bad signature");
507 	case NSSCMSVS_DigestMismatch:
508 		return _("Content tampered with or altered in transit");
509 	case NSSCMSVS_SigningCertNotFound:
510 		return _("Signing certificate not found");
511 	case NSSCMSVS_SigningCertNotTrusted:
512 		return _("Signing certificate not trusted");
513 	case NSSCMSVS_SignatureAlgorithmUnknown:
514 		return _("Signature algorithm unknown");
515 	case NSSCMSVS_SignatureAlgorithmUnsupported:
516 		return _("Signature algorithm unsupported");
517 	case NSSCMSVS_MalformedSignature:
518 		return _("Malformed signature");
519 	case NSSCMSVS_ProcessingError:
520 		return _("Processing error");
521 	}
522 }
523 
524 static CamelCipherValidity *
sm_verify_cmsg(CamelCipherContext * context,NSSCMSMessage * cmsg,CamelStream * extstream,GCancellable * cancellable,GError ** error)525 sm_verify_cmsg (CamelCipherContext *context,
526                 NSSCMSMessage *cmsg,
527                 CamelStream *extstream,
528                 GCancellable *cancellable,
529                 GError **error)
530 {
531 	CamelSMIMEContextPrivate *p = ((CamelSMIMEContext *) context)->priv;
532 	NSSCMSSignedData *sigd = NULL;
533 #if 0
534 	NSSCMSEnvelopedData *envd;
535 	NSSCMSEncryptedData *encd;
536 #endif
537 	SECAlgorithmID **digestalgs;
538 	NSSCMSDigestContext *digcx;
539 	gint count, i, nsigners, j;
540 	SECItem **digests;
541 	PLArenaPool *poolp = NULL;
542 	CamelStream *mem;
543 	NSSCMSVerificationStatus status;
544 	CamelCipherValidity *valid;
545 	GString *description;
546 
547 	description = g_string_new ("");
548 	valid = camel_cipher_validity_new ();
549 	camel_cipher_validity_set_valid (valid, TRUE);
550 	status = NSSCMSVS_Unverified;
551 
552 	/* NB: this probably needs to go into a decoding routine that can be used for processing
553 	 * enveloped data too */
554 	count = NSS_CMSMessage_ContentLevelCount (cmsg);
555 	for (i = 0; i < count; i++) {
556 		NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel (cmsg, i);
557 		SECOidTag typetag = NSS_CMSContentInfo_GetContentTypeTag (cinfo);
558 		GByteArray *buffer;
559 		gint which_digest;
560 
561 		switch (typetag) {
562 		case SEC_OID_PKCS7_SIGNED_DATA:
563 			sigd = (NSSCMSSignedData *) NSS_CMSContentInfo_GetContent (cinfo);
564 			if (sigd == NULL) {
565 				set_nss_error (error, _("No signed data in signature"));
566 				goto fail;
567 			}
568 
569 			if (extstream == NULL) {
570 				set_nss_error (error, _("Digests missing from enveloped data"));
571 				goto fail;
572 			}
573 
574 			if ((poolp = PORT_NewArena (1024)) == NULL) {
575 				set_nss_error (error, g_strerror (ENOMEM));
576 				goto fail;
577 			}
578 
579 			digestalgs = NSS_CMSSignedData_GetDigestAlgs (sigd);
580 
581 			digcx = NSS_CMSDigestContext_StartMultiple (digestalgs);
582 			if (digcx == NULL) {
583 				set_nss_error (error, _("Cannot calculate digests"));
584 				goto fail;
585 			}
586 
587 			buffer = g_byte_array_new ();
588 			mem = camel_stream_mem_new_with_byte_array (buffer);
589 			camel_stream_write_to_stream (extstream, mem, cancellable, NULL);
590 			NSS_CMSDigestContext_Update (digcx, buffer->data, buffer->len);
591 			g_object_unref (mem);
592 
593 			if (NSS_CMSDigestContext_FinishMultiple (digcx, poolp, &digests) != SECSuccess) {
594 				set_nss_error (error, _("Cannot calculate digests"));
595 				goto fail;
596 			}
597 
598 			for (which_digest = 0; digests[which_digest] != NULL; which_digest++) {
599 				SECOidData *digest_alg = SECOID_FindOID (&digestalgs[which_digest]->algorithm);
600 				if (digest_alg == NULL) {
601 					set_nss_error (error, _("Cannot set message digests"));
602 					goto fail;
603 				}
604 				if (NSS_CMSSignedData_SetDigestValue (sigd, digest_alg->offset, digests[which_digest]) != SECSuccess) {
605 					set_nss_error (error, _("Cannot set message digests"));
606 					goto fail;
607 				}
608 			}
609 
610 			PORT_FreeArena (poolp, PR_FALSE);
611 			poolp = NULL;
612 
613 			/* import all certificates present */
614 			if (NSS_CMSSignedData_ImportCerts (sigd, p->certdb, certUsageEmailSigner, PR_TRUE) != SECSuccess) {
615 				set_nss_error (error, _("Certificate import failed"));
616 				goto fail;
617 			}
618 
619 			if (NSS_CMSSignedData_ImportCerts (sigd, p->certdb, certUsageEmailRecipient, PR_TRUE) != SECSuccess) {
620 				set_nss_error (error, _("Certificate import failed"));
621 				goto fail;
622 			}
623 
624 			/* check for certs-only message */
625 			nsigners = NSS_CMSSignedData_SignerInfoCount (sigd);
626 			if (nsigners == 0) {
627 
628 				/* already imported certs above, not sure what usage we should use here or if this isn't handled above */
629 				if (NSS_CMSSignedData_VerifyCertsOnly (sigd, p->certdb, certUsageEmailSigner) != SECSuccess) {
630 					g_string_printf (description, _("Certificate is the only message, cannot verify certificates"));
631 				} else {
632 					status = NSSCMSVS_GoodSignature;
633 					g_string_printf (description, _("Certificate is the only message, certificates imported and verified"));
634 				}
635 			} else {
636 				if (!NSS_CMSSignedData_HasDigests (sigd)) {
637 					set_nss_error (error, _("Cannot find signature digests"));
638 					goto fail;
639 				}
640 
641 				for (j = 0; j < nsigners; j++) {
642 					CERTCertificate *cert;
643 					NSSCMSSignerInfo *si;
644 					gchar *cn, *em;
645 					gint idx;
646 
647 					si = NSS_CMSSignedData_GetSignerInfo (sigd, j);
648 					NSS_CMSSignedData_VerifySignerInfo (sigd, j, p->certdb, certUsageEmailSigner);
649 
650 					status = NSS_CMSSignerInfo_GetVerificationStatus (si);
651 
652 					cn = NSS_CMSSignerInfo_GetSignerCommonName (si);
653 					em = NSS_CMSSignerInfo_GetSignerEmailAddress (si);
654 
655 					g_string_append_printf (
656 						description, _("Signer: %s <%s>: %s\n"),
657 						cn ? cn:"<unknown>", em ? em:"<unknown>",
658 						sm_status_description (status));
659 
660 					cert = NSS_CMSSignerInfo_GetSigningCertificate (si, p->certdb);
661 
662 					idx = camel_cipher_validity_add_certinfo_ex (
663 						valid, CAMEL_CIPHER_VALIDITY_SIGN, cn, em,
664 						cert ? smime_cert_data_clone (cert) : NULL,
665 						cert ? smime_cert_data_free : NULL, cert ? smime_cert_data_clone : NULL);
666 
667 					if (cert && idx >= 0) {
668 						CamelInternetAddress *addrs = NULL;
669 						const gchar *cert_email;
670 
671 						for (cert_email = CERT_GetFirstEmailAddress (cert);
672 						     cert_email;
673 						     cert_email = CERT_GetNextEmailAddress (cert, cert_email)) {
674 							if (!*cert_email)
675 								continue;
676 
677 							if (!addrs)
678 								addrs = camel_internet_address_new ();
679 
680 							camel_internet_address_add (addrs, NULL, cert_email);
681 						}
682 
683 						if (addrs) {
684 							gchar *addresses = camel_address_format (CAMEL_ADDRESS (addrs));
685 
686 							camel_cipher_validity_set_certinfo_property (valid, CAMEL_CIPHER_VALIDITY_SIGN, idx,
687 								CAMEL_CIPHER_CERT_INFO_PROPERTY_SIGNERS_ALT_EMAILS, addresses,
688 								g_free, (CamelCipherCloneFunc) g_strdup);
689 
690 							g_object_unref (addrs);
691 						}
692 					}
693 
694 					if (cn)
695 						PORT_Free (cn);
696 					if (em)
697 						PORT_Free (em);
698 
699 					if (status != NSSCMSVS_GoodSignature)
700 						camel_cipher_validity_set_valid (valid, FALSE);
701 				}
702 			}
703 			break;
704 		case SEC_OID_PKCS7_ENVELOPED_DATA:
705 			/* FIXME Do something with this? */
706 			/*envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent (cinfo);*/
707 			break;
708 		case SEC_OID_PKCS7_ENCRYPTED_DATA:
709 			/* FIXME Do something with this? */
710 			/*encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent (cinfo);*/
711 			break;
712 		case SEC_OID_PKCS7_DATA:
713 			break;
714 		default:
715 			break;
716 		}
717 	}
718 
719 	camel_cipher_validity_set_valid (valid, camel_cipher_validity_get_valid (valid) && status == NSSCMSVS_GoodSignature);
720 	camel_cipher_validity_set_description (valid, description->str);
721 	g_string_free (description, TRUE);
722 
723 	return valid;
724 
725 fail:
726 	camel_cipher_validity_free (valid);
727 	g_string_free (description, TRUE);
728 
729 	return NULL;
730 }
731 
732 static const gchar *
smime_context_hash_to_id(CamelCipherContext * context,CamelCipherHash hash)733 smime_context_hash_to_id (CamelCipherContext *context,
734                           CamelCipherHash hash)
735 {
736 	switch (hash) {
737 		/* Support registered IANA hash function textual names.
738 		 * http://www.iana.org/assignments/hash-function-text-names */
739 		case CAMEL_CIPHER_HASH_MD5:
740 			return "md5";
741 		case CAMEL_CIPHER_HASH_SHA1:
742 			return "sha-1";
743 		case CAMEL_CIPHER_HASH_DEFAULT:
744 		case CAMEL_CIPHER_HASH_SHA256:
745 			return "sha-256";
746 		case CAMEL_CIPHER_HASH_SHA384:
747 			return "sha-384";
748 		case CAMEL_CIPHER_HASH_SHA512:
749 			return "sha-512";
750 		default:
751 			return NULL;
752 	}
753 }
754 
755 static CamelCipherHash
smime_context_id_to_hash(CamelCipherContext * context,const gchar * id)756 smime_context_id_to_hash (CamelCipherContext *context,
757                           const gchar *id)
758 {
759 	if (id != NULL) {
760 		/* Support registered IANA hash function textual names.
761 		 * http://www.iana.org/assignments/hash-function-text-names */
762 		if (g_str_equal (id, "md5"))
763 			return CAMEL_CIPHER_HASH_MD5;
764 		if (g_str_equal (id, "sha-1"))
765 			return CAMEL_CIPHER_HASH_SHA1;
766 		if (g_str_equal (id, "sha-256"))
767 			return CAMEL_CIPHER_HASH_SHA256;
768 		if (g_str_equal (id, "sha-384"))
769 			return CAMEL_CIPHER_HASH_SHA384;
770 		if (g_str_equal (id, "sha-512"))
771 			return CAMEL_CIPHER_HASH_SHA512;
772 
773 		/* Non-standard names. */
774 		if (g_str_equal (id, "sha1"))
775 			return CAMEL_CIPHER_HASH_SHA1;
776 		if (g_str_equal (id, "sha256"))
777 			return CAMEL_CIPHER_HASH_SHA256;
778 		if (g_str_equal (id, "sha384"))
779 			return CAMEL_CIPHER_HASH_SHA384;
780 		if (g_str_equal (id, "sha512"))
781 			return CAMEL_CIPHER_HASH_SHA512;
782 	}
783 
784 	return CAMEL_CIPHER_HASH_DEFAULT;
785 }
786 
787 static CamelCipherHash
get_hash_from_oid(SECOidTag oidTag)788 get_hash_from_oid (SECOidTag oidTag)
789 {
790 	switch (oidTag) {
791 	case SEC_OID_SHA1:
792 		return CAMEL_CIPHER_HASH_SHA1;
793 	case SEC_OID_SHA256:
794 		return CAMEL_CIPHER_HASH_SHA256;
795 	case SEC_OID_SHA384:
796 		return CAMEL_CIPHER_HASH_SHA384;
797 	case SEC_OID_SHA512:
798 		return CAMEL_CIPHER_HASH_SHA512;
799 	case SEC_OID_MD5:
800 		return CAMEL_CIPHER_HASH_MD5;
801 	default:
802 		break;
803 	}
804 
805 	return CAMEL_CIPHER_HASH_DEFAULT;
806 }
807 
808 static gboolean
smime_context_sign_sync(CamelCipherContext * context,const gchar * userid,CamelCipherHash hash,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)809 smime_context_sign_sync (CamelCipherContext *context,
810                          const gchar *userid,
811                          CamelCipherHash hash,
812                          CamelMimePart *ipart,
813                          CamelMimePart *opart,
814                          GCancellable *cancellable,
815                          GError **error)
816 {
817 	CamelCipherContextClass *class;
818 	NSSCMSMessage *cmsg;
819 	CamelStream *ostream, *istream;
820 	GByteArray *buffer;
821 	SECOidTag sechash;
822 	NSSCMSEncoderContext *enc;
823 	CamelDataWrapper *dw;
824 	CamelContentType *ct;
825 	gboolean success = FALSE;
826 
827 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
828 
829 	switch (hash) {
830 	case CAMEL_CIPHER_HASH_DEFAULT:
831 	default:
832 		sechash = SEC_OID_UNKNOWN;
833 		break;
834 	case CAMEL_CIPHER_HASH_SHA1:
835 		sechash = SEC_OID_SHA1;
836 		break;
837 	case CAMEL_CIPHER_HASH_SHA256:
838 		sechash = SEC_OID_SHA256;
839 		break;
840 	case CAMEL_CIPHER_HASH_SHA384:
841 		sechash = SEC_OID_SHA384;
842 		break;
843 	case CAMEL_CIPHER_HASH_SHA512:
844 		sechash = SEC_OID_SHA512;
845 		break;
846 	case CAMEL_CIPHER_HASH_MD5:
847 		sechash = SEC_OID_MD5;
848 		break;
849 	}
850 
851 	cmsg = sm_signing_cmsmessage (
852 		(CamelSMIMEContext *) context, userid, &sechash,
853 		((CamelSMIMEContext *) context)->priv->sign_mode == CAMEL_SMIME_SIGN_CLEARSIGN, error);
854 	if (cmsg == NULL)
855 		return FALSE;
856 
857 	ostream = camel_stream_mem_new ();
858 
859 	/* FIXME: stream this, we stream output at least */
860 	buffer = g_byte_array_new ();
861 	istream = camel_stream_mem_new_with_byte_array (buffer);
862 
863 	if (camel_cipher_canonical_to_stream (
864 		ipart, CAMEL_MIME_FILTER_CANON_STRIP |
865 		CAMEL_MIME_FILTER_CANON_CRLF |
866 		CAMEL_MIME_FILTER_CANON_FROM,
867 		istream, cancellable, error) == -1) {
868 		g_prefix_error (
869 			error, _("Could not generate signing data: "));
870 		goto fail;
871 	}
872 
873 	enc = NSS_CMSEncoder_Start (
874 		cmsg,
875 		sm_write_stream, ostream, /* DER output callback  */
876 		NULL, NULL,     /* destination storage  */
877 		NULL, NULL,	   /* password callback    */
878 		NULL, NULL,     /* decrypt key callback */
879 		NULL, NULL );   /* detached digests    */
880 	if (!enc) {
881 		set_nss_error (error, _("Cannot create encoder context"));
882 		goto fail;
883 	}
884 
885 	if (NSS_CMSEncoder_Update (enc, (gchar *) buffer->data, buffer->len) != SECSuccess) {
886 		NSS_CMSEncoder_Cancel (enc);
887 		set_nss_error (error, _("Failed to add data to CMS encoder"));
888 		goto fail;
889 	}
890 
891 	if (NSS_CMSEncoder_Finish (enc) != SECSuccess) {
892 		set_nss_error (error, _("Failed to encode data"));
893 		goto fail;
894 	}
895 
896 	success = TRUE;
897 
898 	dw = camel_data_wrapper_new ();
899 	g_seekable_seek (G_SEEKABLE (ostream), 0, G_SEEK_SET, NULL, NULL);
900 	camel_data_wrapper_construct_from_stream_sync (
901 		dw, ostream, cancellable, NULL);
902 	camel_data_wrapper_set_encoding (dw, CAMEL_TRANSFER_ENCODING_BINARY);
903 
904 	if (((CamelSMIMEContext *) context)->priv->sign_mode == CAMEL_SMIME_SIGN_CLEARSIGN) {
905 		CamelMultipartSigned *mps;
906 		CamelMimePart *sigpart;
907 
908 		sigpart = camel_mime_part_new ();
909 		ct = camel_content_type_new ("application", "pkcs7-signature");
910 		camel_content_type_set_param (ct, "name", "smime.p7s");
911 		camel_data_wrapper_set_mime_type_field (dw, ct);
912 		camel_content_type_unref (ct);
913 
914 		camel_medium_set_content ((CamelMedium *) sigpart, dw);
915 
916 		camel_mime_part_set_filename (sigpart, "smime.p7s");
917 		camel_mime_part_set_disposition (sigpart, "attachment");
918 		camel_mime_part_set_encoding (sigpart, CAMEL_TRANSFER_ENCODING_BASE64);
919 
920 		mps = camel_multipart_signed_new ();
921 		ct = camel_content_type_new ("multipart", "signed");
922 		camel_content_type_set_param (ct, "micalg", camel_cipher_context_hash_to_id (context, get_hash_from_oid (sechash)));
923 		camel_content_type_set_param (ct, "protocol", class->sign_protocol);
924 		camel_data_wrapper_set_mime_type_field ((CamelDataWrapper *) mps, ct);
925 		camel_content_type_unref (ct);
926 		camel_multipart_set_boundary ((CamelMultipart *) mps, NULL);
927 
928 		camel_multipart_signed_set_signature (mps, sigpart);
929 		camel_multipart_signed_set_content_stream (mps, istream);
930 
931 		g_object_unref (sigpart);
932 
933 		g_seekable_seek (
934 			G_SEEKABLE (istream), 0,
935 			G_SEEK_SET, NULL, NULL);
936 
937 		camel_medium_set_content ((CamelMedium *) opart, (CamelDataWrapper *) mps);
938 	} else {
939 		ct = camel_content_type_new ("application", "pkcs7-mime");
940 		camel_content_type_set_param (ct, "name", "smime.p7m");
941 		camel_content_type_set_param (ct, "smime-type", "signed-data");
942 		camel_data_wrapper_set_mime_type_field (dw, ct);
943 		camel_content_type_unref (ct);
944 
945 		camel_medium_set_content ((CamelMedium *) opart, dw);
946 
947 		camel_mime_part_set_filename (opart, "smime.p7m");
948 		camel_mime_part_set_description (opart, "S/MIME Signed Message");
949 		camel_mime_part_set_disposition (opart, "attachment");
950 		camel_mime_part_set_encoding (opart, CAMEL_TRANSFER_ENCODING_BASE64);
951 	}
952 
953 	g_object_unref (dw);
954 fail:
955 	g_object_unref (ostream);
956 	g_object_unref (istream);
957 
958 	return success;
959 }
960 
961 static CamelCipherValidity *
smime_context_verify_sync(CamelCipherContext * context,CamelMimePart * ipart,GCancellable * cancellable,GError ** error)962 smime_context_verify_sync (CamelCipherContext *context,
963                            CamelMimePart *ipart,
964                            GCancellable *cancellable,
965                            GError **error)
966 {
967 	CamelCipherContextClass *class;
968 	NSSCMSDecoderContext *dec;
969 	NSSCMSMessage *cmsg;
970 	CamelStream *mem;
971 	CamelStream *constream = NULL;
972 	CamelCipherValidity *valid = NULL;
973 	CamelContentType *ct;
974 	const gchar *tmp;
975 	CamelMimePart *sigpart;
976 	CamelDataWrapper *dw;
977 	GByteArray *buffer;
978 
979 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
980 
981 	dw = camel_medium_get_content ((CamelMedium *) ipart);
982 	ct = camel_data_wrapper_get_mime_type_field (dw);
983 
984 	/* FIXME: we should stream this to the decoder */
985 	buffer = g_byte_array_new ();
986 	mem = camel_stream_mem_new_with_byte_array (buffer);
987 
988 	if (camel_content_type_is (ct, "multipart", "signed")) {
989 		CamelMultipart *mps = (CamelMultipart *) dw;
990 
991 		tmp = camel_content_type_param (ct, "protocol");
992 		if (!CAMEL_IS_MULTIPART_SIGNED (mps)
993 		    || tmp == NULL
994 		    || (g_ascii_strcasecmp (tmp, class->sign_protocol) != 0
995 			&& g_ascii_strcasecmp (tmp, "application/xpkcs7signature") != 0
996 			&& g_ascii_strcasecmp (tmp, "application/xpkcs7-signature") != 0
997 			&& g_ascii_strcasecmp (tmp, "application/x-pkcs7-signature") != 0)) {
998 			g_set_error (
999 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1000 				_("Cannot verify message signature: "
1001 				"Incorrect message format"));
1002 			goto fail;
1003 		}
1004 
1005 		constream = camel_multipart_signed_get_content_stream (
1006 			(CamelMultipartSigned *) mps, error);
1007 		if (constream == NULL)
1008 			goto fail;
1009 
1010 		sigpart = camel_multipart_get_part (mps, CAMEL_MULTIPART_SIGNED_SIGNATURE);
1011 		if (sigpart == NULL) {
1012 			g_set_error (
1013 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1014 				_("Cannot verify message signature: "
1015 				"Incorrect message format"));
1016 			goto fail;
1017 		}
1018 	} else if (camel_content_type_is (ct, "application", "pkcs7-mime")
1019 		   || camel_content_type_is (ct, "application", "xpkcs7mime")
1020 		   || camel_content_type_is (ct, "application", "xpkcs7-mime")
1021 		   || camel_content_type_is (ct, "application", "x-pkcs7-mime")) {
1022 		sigpart = ipart;
1023 	} else {
1024 		g_set_error (
1025 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1026 			_("Cannot verify message signature: "
1027 			"Incorrect message format"));
1028 		goto fail;
1029 	}
1030 
1031 	dec = NSS_CMSDecoder_Start (
1032 		NULL,
1033 		NULL, NULL, /* content callback     */
1034 		NULL, NULL,	/* password callback    */
1035 		NULL, NULL); /* decrypt key callback */
1036 
1037 	camel_data_wrapper_decode_to_stream_sync (
1038 		camel_medium_get_content (
1039 			CAMEL_MEDIUM (sigpart)), mem, cancellable, NULL);
1040 	if (NSS_CMSDecoder_Update (dec, (gchar *) buffer->data, buffer->len) != SECSuccess) {
1041 		g_warning ("%s: Failed to call NSS_CMSDecoder_Update", G_STRFUNC);
1042 	}
1043 	cmsg = NSS_CMSDecoder_Finish (dec);
1044 	if (cmsg == NULL) {
1045 		set_nss_error (error, _("Decoder failed"));
1046 		goto fail;
1047 	}
1048 
1049 	valid = sm_verify_cmsg (context, cmsg, constream, cancellable, error);
1050 
1051 	NSS_CMSMessage_Destroy (cmsg);
1052 fail:
1053 	g_object_unref (mem);
1054 	if (constream)
1055 		g_object_unref (constream);
1056 
1057 	return valid;
1058 }
1059 
1060 static gboolean
camel_smime_first_certificate_is_better(CERTCertificate * cert1,CERTCertificate * cert2,PRTime now)1061 camel_smime_first_certificate_is_better (CERTCertificate *cert1,
1062 					 CERTCertificate *cert2,
1063 					 PRTime now)
1064 {
1065 	PRTime notBefore1, notAfter1, notBefore2, notAfter2;
1066 	CERTCertTrust trust;
1067 	gboolean cert1_trusted, cert2_trusted;
1068 
1069 	if (!cert1)
1070 		return FALSE;
1071 
1072 	if (!cert2)
1073 		return cert1 != NULL;
1074 
1075 	/* Both certificates are valid at the time, it's ensured with CERT_CheckCertValidTimes()
1076 	   in camel_smime_find_recipients_certs() */
1077 
1078 	if (SECSuccess == CERT_GetCertTrust (cert1, &trust))
1079 		cert1_trusted = (trust.emailFlags & CERTDB_TRUSTED) != 0;
1080 	else
1081 		cert1_trusted = FALSE;
1082 
1083 	if (SECSuccess == CERT_GetCertTrust (cert2, &trust))
1084 		cert2_trusted = (trust.emailFlags & CERTDB_TRUSTED) != 0;
1085 	else
1086 		cert2_trusted = FALSE;
1087 
1088 	if (cert1_trusted && !cert2_trusted)
1089 		return TRUE;
1090 
1091 	if (!cert1_trusted && cert2_trusted)
1092 		return FALSE;
1093 
1094 	/* Both are trusted or untrusted, then get the newer */
1095 	if (CERT_GetCertTimes (cert1, &notBefore1, &notAfter1) != SECSuccess)
1096 		return FALSE;
1097 
1098 	if (CERT_GetCertTimes (cert2, &notBefore2, &notAfter2) != SECSuccess)
1099 		return TRUE;
1100 
1101 	/* cert1 is valid after cert2, thus it is newer */
1102 	return notBefore1 > notBefore2;
1103 }
1104 
1105 typedef struct FindRecipientsData {
1106 	GHashTable *recipients_table;
1107 	guint certs_missing;
1108 	PRTime now;
1109 } FindRecipientsData;
1110 
1111 static SECStatus
camel_smime_find_recipients_certs(CERTCertificate * cert,SECItem * item,gpointer user_data)1112 camel_smime_find_recipients_certs (CERTCertificate *cert,
1113 				   SECItem *item,
1114 				   gpointer user_data)
1115 {
1116 	FindRecipientsData *frd = user_data;
1117 	const gchar *cert_email = NULL;
1118 	CERTCertificate **hash_value = NULL;
1119 
1120 	/* Cannot short-circuit when frd->certs_missing is 0, because there can be better certificates */
1121 	if (!frd->recipients_table ||
1122 	    CERT_CheckCertValidTimes (cert, frd->now, PR_FALSE) != secCertTimeValid) {
1123 		return SECFailure;
1124 	}
1125 
1126 	/* Loop over all cert's email addresses */
1127 	for (cert_email = CERT_GetFirstEmailAddress (cert);
1128 	     cert_email;
1129 	     cert_email = CERT_GetNextEmailAddress (cert, cert_email)) {
1130 		hash_value = g_hash_table_lookup (frd->recipients_table, cert_email);
1131 
1132 		if (hash_value && !*hash_value) {
1133 			/* Cannot break now, because there can be multiple addresses for this certificate */
1134 			*hash_value = CERT_DupCertificate (cert);
1135 			frd->certs_missing--;
1136 		} else if (hash_value && !camel_smime_first_certificate_is_better (*hash_value, cert, frd->now)) {
1137 			CERT_DestroyCertificate (*hash_value);
1138 			*hash_value = CERT_DupCertificate (cert);
1139 		}
1140 	}
1141 
1142 	/* Is the sender referenced by nickname rather than its email address? */
1143 	if (cert->nickname) {
1144 		hash_value = g_hash_table_lookup (frd->recipients_table, cert->nickname);
1145 
1146 		if (hash_value && !*hash_value) {
1147 			*hash_value = CERT_DupCertificate (cert);
1148 			frd->certs_missing--;
1149 		} else if (hash_value && !camel_smime_first_certificate_is_better (*hash_value, cert, frd->now)) {
1150 			CERT_DestroyCertificate (*hash_value);
1151 			*hash_value = CERT_DupCertificate (cert);
1152 		}
1153 	}
1154 
1155 	return SECFailure;
1156 }
1157 
1158 static guint
camel_smime_cert_hash(gconstpointer ptr)1159 camel_smime_cert_hash (gconstpointer ptr)
1160 {
1161 	const CERTCertificate *cert = ptr;
1162 	guint hashval = 0, ii;
1163 
1164 	if (!cert)
1165 		return 0;
1166 
1167 	if (cert->serialNumber.len && cert->serialNumber.data) {
1168 		for (ii = 0; ii < cert->serialNumber.len; ii += 4) {
1169 			guint num = cert->serialNumber.data[ii];
1170 
1171 			if (ii + 1 < cert->serialNumber.len)
1172 				num = num | (cert->serialNumber.data[ii + 1] << 8);
1173 			if (ii + 2 < cert->serialNumber.len)
1174 				num = num | (cert->serialNumber.data[ii + 2] << 16);
1175 			if (ii + 3 < cert->serialNumber.len)
1176 				num = num | (cert->serialNumber.data[ii + 3] << 24);
1177 
1178 			hashval = hashval ^ num;
1179 		}
1180 	}
1181 
1182 	return hashval;
1183 }
1184 
1185 static gboolean
camel_smime_cert_equal(gconstpointer ptr1,gconstpointer ptr2)1186 camel_smime_cert_equal (gconstpointer ptr1,
1187 			gconstpointer ptr2)
1188 {
1189 	const CERTCertificate *cert1 = ptr1, *cert2 = ptr2;
1190 
1191 	if (!cert1 || !cert2)
1192 		return cert1 == cert2;
1193 
1194 	if (cert1 == cert2)
1195 		return TRUE;
1196 
1197 	if (cert1->derCert.len != cert2->derCert.len ||
1198 	    !cert1->derCert.data || !cert2->derCert.data) {
1199 		return FALSE;
1200 	}
1201 
1202 	return memcmp (cert1->derCert.data, cert2->derCert.data, cert1->derCert.len) == 0;
1203 }
1204 
1205 static gboolean
smime_context_encrypt_sync(CamelCipherContext * context,const gchar * userid,GPtrArray * recipients,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)1206 smime_context_encrypt_sync (CamelCipherContext *context,
1207                             const gchar *userid,
1208                             GPtrArray *recipients,
1209                             CamelMimePart *ipart,
1210                             CamelMimePart *opart,
1211                             GCancellable *cancellable,
1212                             GError **error)
1213 {
1214 	/*NSSCMSRecipientInfo **recipient_infos;*/
1215 	CERTCertificate **recipient_certs = NULL;
1216 	FindRecipientsData frd;
1217 	NSSCMSContentInfo *cinfo;
1218 	PK11SymKey *bulkkey = NULL;
1219 	SECOidTag bulkalgtag;
1220 	gint bulkkeysize, i;
1221 	CK_MECHANISM_TYPE type;
1222 	PK11SlotInfo *slot;
1223 	PLArenaPool *poolp;
1224 	NSSCMSMessage *cmsg = NULL;
1225 	NSSCMSEnvelopedData *envd;
1226 	NSSCMSEncoderContext *enc = NULL;
1227 	CamelStream *mem;
1228 	CamelStream *ostream = NULL;
1229 	CamelDataWrapper *dw;
1230 	CamelContentType *ct;
1231 	GByteArray *buffer;
1232 	GSList *gathered_certificates = NULL, *link;
1233 
1234 	if (!camel_session_get_recipient_certificates_sync (camel_cipher_context_get_session (context),
1235 		CAMEL_RECIPIENT_CERTIFICATE_SMIME, recipients, &gathered_certificates, cancellable, error))
1236 		return FALSE;
1237 
1238 	poolp = PORT_NewArena (1024);
1239 	if (poolp == NULL) {
1240 		set_nss_error (error, g_strerror (ENOMEM));
1241 		g_slist_free_full (gathered_certificates, g_free);
1242 		return FALSE;
1243 	}
1244 
1245 	/* Lookup all recipients certs, for later working */
1246 	recipient_certs = (CERTCertificate **) PORT_ArenaZAlloc (poolp, sizeof (recipient_certs[0]) * (recipients->len + 1));
1247 	if (recipient_certs == NULL) {
1248 		set_nss_error (error, g_strerror (ENOMEM));
1249 		g_slist_free_full (gathered_certificates, g_free);
1250 		goto fail;
1251 	}
1252 
1253 	frd.recipients_table = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
1254 	for (i = 0; i < recipients->len; i++) {
1255 		g_hash_table_insert (
1256 				frd.recipients_table,
1257 				recipients->pdata[i],
1258 				&recipient_certs[i]);
1259 	}
1260 	frd.certs_missing = g_hash_table_size (frd.recipients_table);
1261 	frd.now = PR_Now();
1262 
1263 	for (link = gathered_certificates; link; link = g_slist_next (link)) {
1264 		const gchar *certstr = link->data;
1265 
1266 		if (certstr && *certstr) {
1267 			CERTCertificate *cert = NULL;
1268 			gsize len = 0;
1269 			guchar *data;
1270 
1271 			data = g_base64_decode (certstr, &len);
1272 
1273 			if (data && len)
1274 				cert = CERT_DecodeCertFromPackage ((gchar *) data, len);
1275 
1276 			g_free (data);
1277 
1278 			if (cert) {
1279 				camel_smime_find_recipients_certs (cert, NULL, &frd);
1280 				CERT_DestroyCertificate (cert);
1281 			}
1282 		}
1283 	}
1284 
1285 	g_slist_free_full (gathered_certificates, g_free);
1286 
1287 	/* Just ignore the return value */
1288 	(void) PK11_TraverseSlotCerts (camel_smime_find_recipients_certs, &frd, NULL);
1289 
1290 	if (frd.certs_missing) {
1291 		i = 0;
1292 		while (i < recipients->len) {
1293 			CERTCertificate **hash_value = g_hash_table_lookup (frd.recipients_table, recipients->pdata[i]);
1294 			if (!hash_value || !*hash_value) {
1295 				g_set_error (
1296 					error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1297 					_("No valid or appropriate certificate for “%s” was found"),
1298 					(gchar *) recipients->pdata[i]);
1299 				g_hash_table_destroy (frd.recipients_table);
1300 				goto fail;
1301 			}
1302 			i++;
1303 		}
1304 	} else {
1305 		/* Addresses and certificates can be duplicated, thus update the recipient_certs array */
1306 		GHashTable *final_certs;
1307 		GHashTableIter iter;
1308 		gpointer key;
1309 
1310 		final_certs = g_hash_table_new_full (camel_smime_cert_hash, camel_smime_cert_equal,
1311 			(GDestroyNotify) CERT_DestroyCertificate, NULL);
1312 
1313 		for (i = 0; i < recipients->len; i++) {
1314 			if (recipient_certs[i]) {
1315 				/* Passes ownership to final_certs */
1316 				g_hash_table_insert (final_certs, recipient_certs[i], NULL);
1317 				recipient_certs[i] = NULL;
1318 			}
1319 		}
1320 
1321 		i = 0;
1322 		g_hash_table_iter_init (&iter, final_certs);
1323 		while (g_hash_table_iter_next (&iter, &key, NULL)) {
1324 			CERTCertificate *cert = key;
1325 
1326 			recipient_certs[i] = CERT_DupCertificate (cert);
1327 			i++;
1328 		}
1329 
1330 		g_hash_table_destroy (final_certs);
1331 	}
1332 
1333 	g_hash_table_destroy (frd.recipients_table);
1334 
1335 	/* Find a common algorithm, probably 3DES anyway ... */
1336 	if (NSS_SMIMEUtil_FindBulkAlgForRecipients (recipient_certs, &bulkalgtag, &bulkkeysize) != SECSuccess) {
1337 		set_nss_error (error, _("Cannot find common bulk encryption algorithm"));
1338 		goto fail;
1339 	}
1340 
1341 	/* Generate a new bulk key based on the common algorithm - expensive */
1342 	type = PK11_AlgtagToMechanism (bulkalgtag);
1343 	slot = PK11_GetBestSlot (type, context);
1344 	if (slot == NULL) {
1345 		set_nss_error (error, _("Cannot allocate slot for encryption bulk key"));
1346 		goto fail;
1347 	}
1348 
1349 	bulkkey = PK11_KeyGen (slot, type, NULL, bulkkeysize / 8, context);
1350 	PK11_FreeSlot (slot);
1351 
1352 	/* Now we can start building the message */
1353 	/* msg->envelopedData->data */
1354 	cmsg = NSS_CMSMessage_Create (NULL);
1355 	if (cmsg == NULL) {
1356 		set_nss_error (error, _("Cannot create CMS Message"));
1357 		goto fail;
1358 	}
1359 
1360 	envd = NSS_CMSEnvelopedData_Create (cmsg, bulkalgtag, bulkkeysize);
1361 	if (envd == NULL) {
1362 		set_nss_error (error, _("Cannot create CMS Enveloped data"));
1363 		goto fail;
1364 	}
1365 
1366 	cinfo = NSS_CMSMessage_GetContentInfo (cmsg);
1367 	if (NSS_CMSContentInfo_SetContent_EnvelopedData (cmsg, cinfo, envd) != SECSuccess) {
1368 		set_nss_error (error, _("Cannot attach CMS Enveloped data"));
1369 		goto fail;
1370 	}
1371 
1372 	cinfo = NSS_CMSEnvelopedData_GetContentInfo (envd);
1373 	if (NSS_CMSContentInfo_SetContent_Data (cmsg, cinfo, NULL, PR_FALSE) != SECSuccess) {
1374 		set_nss_error (error, _("Cannot attach CMS data object"));
1375 		goto fail;
1376 	}
1377 
1378 	/* add recipient certs */
1379 	for (i = 0; recipient_certs[i]; i++) {
1380 		NSSCMSRecipientInfo *ri = NSS_CMSRecipientInfo_Create (cmsg, recipient_certs[i]);
1381 
1382 		if (ri == NULL) {
1383 			set_nss_error (error, _("Cannot create CMS Recipient information"));
1384 			goto fail;
1385 		}
1386 
1387 		if (NSS_CMSEnvelopedData_AddRecipient (envd, ri) != SECSuccess) {
1388 			set_nss_error (error, _("Cannot add CMS Recipient information"));
1389 			goto fail;
1390 		}
1391 	}
1392 
1393 	/* dump it out */
1394 	ostream = camel_stream_mem_new ();
1395 	enc = NSS_CMSEncoder_Start (
1396 		cmsg,
1397 		sm_write_stream, ostream,
1398 		NULL, NULL,
1399 		NULL, NULL,
1400 		sm_decrypt_key, bulkkey,
1401 		NULL, NULL);
1402 	if (enc == NULL) {
1403 		set_nss_error (error, _("Cannot create encoder context"));
1404 		goto fail;
1405 	}
1406 
1407 	/* FIXME: Stream the input */
1408 	buffer = g_byte_array_new ();
1409 	mem = camel_stream_mem_new_with_byte_array (buffer);
1410 	camel_cipher_canonical_to_stream (ipart, CAMEL_MIME_FILTER_CANON_CRLF, mem, NULL, NULL);
1411 	if (NSS_CMSEncoder_Update (enc, (gchar *) buffer->data, buffer->len) != SECSuccess) {
1412 		NSS_CMSEncoder_Cancel (enc);
1413 		g_object_unref (mem);
1414 		set_nss_error (error, _("Failed to add data to encoder"));
1415 		goto fail;
1416 	}
1417 	g_object_unref (mem);
1418 
1419 	if (NSS_CMSEncoder_Finish (enc) != SECSuccess) {
1420 		set_nss_error (error, _("Failed to encode data"));
1421 		goto fail;
1422 	}
1423 
1424 	PK11_FreeSymKey (bulkkey);
1425 	NSS_CMSMessage_Destroy (cmsg);
1426 	for (i = 0; i < recipients->len; i++) {
1427 		if (recipient_certs[i])
1428 			CERT_DestroyCertificate (recipient_certs[i]);
1429 	}
1430 	PORT_FreeArena (poolp, PR_FALSE);
1431 
1432 	dw = camel_data_wrapper_new ();
1433 	camel_data_wrapper_construct_from_stream_sync (
1434 		dw, ostream, NULL, NULL);
1435 	g_object_unref (ostream);
1436 	camel_data_wrapper_set_encoding (dw, CAMEL_TRANSFER_ENCODING_BINARY);
1437 
1438 	ct = camel_content_type_new ("application", "pkcs7-mime");
1439 	camel_content_type_set_param (ct, "name", "smime.p7m");
1440 	camel_content_type_set_param (ct, "smime-type", "enveloped-data");
1441 	camel_data_wrapper_set_mime_type_field (dw, ct);
1442 	camel_content_type_unref (ct);
1443 
1444 	camel_medium_set_content ((CamelMedium *) opart, dw);
1445 	g_object_unref (dw);
1446 
1447 	camel_mime_part_set_disposition (opart, "attachment");
1448 	camel_mime_part_set_filename (opart, "smime.p7m");
1449 	camel_mime_part_set_description (opart, "S/MIME Encrypted Message");
1450 	camel_mime_part_set_encoding (opart, CAMEL_TRANSFER_ENCODING_BASE64);
1451 
1452 	return TRUE;
1453 
1454 fail:
1455 	if (ostream)
1456 		g_object_unref (ostream);
1457 	if (cmsg)
1458 		NSS_CMSMessage_Destroy (cmsg);
1459 	if (bulkkey)
1460 		PK11_FreeSymKey (bulkkey);
1461 
1462 	if (recipient_certs) {
1463 		for (i = 0; i < recipients->len; i++) {
1464 			if (recipient_certs[i])
1465 				CERT_DestroyCertificate (recipient_certs[i]);
1466 		}
1467 	}
1468 
1469 	PORT_FreeArena (poolp, PR_FALSE);
1470 
1471 	return FALSE;
1472 }
1473 
1474 static CamelCipherValidity *
smime_context_decrypt_sync(CamelCipherContext * context,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)1475 smime_context_decrypt_sync (CamelCipherContext *context,
1476                             CamelMimePart *ipart,
1477                             CamelMimePart *opart,
1478                             GCancellable *cancellable,
1479                             GError **error)
1480 {
1481 	NSSCMSDecoderContext *dec;
1482 	NSSCMSMessage *cmsg;
1483 	CamelStream *istream;
1484 	CamelStream *ostream;
1485 	CamelCipherValidity *valid = NULL;
1486 	GByteArray *buffer;
1487 
1488 	/* FIXME: This assumes the content is only encrypted.  Perhaps its ok for
1489 	 * this api to do this ... */
1490 
1491 	ostream = camel_stream_mem_new ();
1492 	camel_stream_mem_set_secure (CAMEL_STREAM_MEM (ostream));
1493 
1494 	/* FIXME: stream this to the decoder incrementally */
1495 	buffer = g_byte_array_new ();
1496 	istream = camel_stream_mem_new_with_byte_array (buffer);
1497 	if (!camel_data_wrapper_decode_to_stream_sync (
1498 		camel_medium_get_content (CAMEL_MEDIUM (ipart)),
1499 		istream, cancellable, error)) {
1500 		g_object_unref (istream);
1501 		goto fail;
1502 	}
1503 
1504 	g_seekable_seek (G_SEEKABLE (istream), 0, G_SEEK_SET, NULL, NULL);
1505 
1506 	dec = NSS_CMSDecoder_Start (
1507 		NULL,
1508 		sm_write_stream, ostream, /* content callback     */
1509 		NULL, NULL,
1510 		NULL, NULL); /* decrypt key callback */
1511 
1512 	if (NSS_CMSDecoder_Update (dec, (gchar *) buffer->data, buffer->len) != SECSuccess) {
1513 		cmsg = NULL;
1514 	} else {
1515 		cmsg = NSS_CMSDecoder_Finish (dec);
1516 	}
1517 
1518 	g_object_unref (istream);
1519 
1520 	if (cmsg == NULL) {
1521 		set_nss_error (error, _("Decoder failed"));
1522 		goto fail;
1523 	}
1524 
1525 #if 0
1526 	/* not sure if we really care about this? */
1527 	if (!NSS_CMSMessage_IsEncrypted (cmsg)) {
1528 		set_nss_error (ex, _("S/MIME Decrypt: No encrypted content found"));
1529 		NSS_CMSMessage_Destroy (cmsg);
1530 		goto fail;
1531 	}
1532 #endif
1533 
1534 	g_seekable_seek (G_SEEKABLE (ostream), 0, G_SEEK_SET, NULL, NULL);
1535 
1536 	camel_data_wrapper_construct_from_stream_sync (
1537 		CAMEL_DATA_WRAPPER (opart), ostream, NULL, NULL);
1538 
1539 	if (NSS_CMSMessage_IsSigned (cmsg)) {
1540 		g_seekable_seek (
1541 			G_SEEKABLE (ostream), 0, G_SEEK_SET, NULL, NULL);
1542 		valid = sm_verify_cmsg (
1543 			context, cmsg, ostream, cancellable, error);
1544 	} else {
1545 		valid = camel_cipher_validity_new ();
1546 		valid->encrypt.description = g_strdup (_("Encrypted content"));
1547 		valid->encrypt.status = CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED;
1548 	}
1549 
1550 	NSS_CMSMessage_Destroy (cmsg);
1551 fail:
1552 	g_object_unref (ostream);
1553 
1554 	return valid;
1555 }
1556 
1557 static void
camel_smime_context_finalize(GObject * object)1558 camel_smime_context_finalize (GObject *object)
1559 {
1560 	CamelSMIMEContext *smime = CAMEL_SMIME_CONTEXT (object);
1561 
1562 	g_free (smime->priv->encrypt_key);
1563 
1564 	/* Chain up to parent's method. */
1565 	G_OBJECT_CLASS (camel_smime_context_parent_class)->finalize (object);
1566 }
1567 
1568 static void
camel_smime_context_class_init(CamelSMIMEContextClass * class)1569 camel_smime_context_class_init (CamelSMIMEContextClass *class)
1570 {
1571 	CamelCipherContextClass *cipher_context_class;
1572 	GObjectClass *object_class;
1573 
1574 	cipher_context_class = CAMEL_CIPHER_CONTEXT_CLASS (class);
1575 	cipher_context_class->sign_protocol = "application/pkcs7-signature";
1576 	cipher_context_class->encrypt_protocol = "application/pkcs7-mime";
1577 	cipher_context_class->key_protocol = "application/pkcs7-signature";
1578 	cipher_context_class->hash_to_id = smime_context_hash_to_id;
1579 	cipher_context_class->id_to_hash = smime_context_id_to_hash;
1580 	cipher_context_class->sign_sync = smime_context_sign_sync;
1581 	cipher_context_class->verify_sync = smime_context_verify_sync;
1582 	cipher_context_class->encrypt_sync = smime_context_encrypt_sync;
1583 	cipher_context_class->decrypt_sync = smime_context_decrypt_sync;
1584 
1585 	object_class = G_OBJECT_CLASS (class);
1586 	object_class->finalize = camel_smime_context_finalize;
1587 }
1588 
1589 static void
camel_smime_context_init(CamelSMIMEContext * smime_context)1590 camel_smime_context_init (CamelSMIMEContext *smime_context)
1591 {
1592 	smime_context->priv = camel_smime_context_get_instance_private (smime_context);
1593 	smime_context->priv->certdb = CERT_GetDefaultCertDB ();
1594 	smime_context->priv->sign_mode = CAMEL_SMIME_SIGN_CLEARSIGN;
1595 	smime_context->priv->password_tries = 0;
1596 }
1597 
1598 /**
1599  * camel_smime_context_new:
1600  * @session: session
1601  *
1602  * Creates a new sm cipher context object.
1603  *
1604  * Returns: a new sm cipher context object.
1605  **/
1606 CamelCipherContext *
camel_smime_context_new(CamelSession * session)1607 camel_smime_context_new (CamelSession *session)
1608 {
1609 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1610 
1611 	return g_object_new (
1612 		CAMEL_TYPE_SMIME_CONTEXT,
1613 		"session", session, NULL);
1614 }
1615 
1616 void
camel_smime_context_set_encrypt_key(CamelSMIMEContext * context,gboolean use,const gchar * key)1617 camel_smime_context_set_encrypt_key (CamelSMIMEContext *context,
1618                                      gboolean use,
1619                                      const gchar *key)
1620 {
1621 	context->priv->send_encrypt_key_prefs = use;
1622 	g_free (context->priv->encrypt_key);
1623 	context->priv->encrypt_key = g_strdup (key);
1624 }
1625 
1626 /* set signing mode, clearsigned multipart/signed or enveloped */
1627 void
camel_smime_context_set_sign_mode(CamelSMIMEContext * context,CamelSMIMESign type)1628 camel_smime_context_set_sign_mode (CamelSMIMEContext *context,
1629                                    CamelSMIMESign type)
1630 {
1631 	context->priv->sign_mode = type;
1632 }
1633 
1634 /* TODO: This is suboptimal, but the only other solution is to pass around NSSCMSMessages */
1635 guint32
camel_smime_context_describe_part(CamelSMIMEContext * context,CamelMimePart * part)1636 camel_smime_context_describe_part (CamelSMIMEContext *context,
1637                                    CamelMimePart *part)
1638 {
1639 	CamelCipherContextClass *class;
1640 	guint32 flags = 0;
1641 	CamelContentType *ct;
1642 	const gchar *tmp;
1643 
1644 	if (!part)
1645 		return flags;
1646 
1647 	class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
1648 
1649 	ct = camel_mime_part_get_content_type (part);
1650 
1651 	if (camel_content_type_is (ct, "multipart", "signed")) {
1652 		tmp = camel_content_type_param (ct, "protocol");
1653 		if (tmp &&
1654 		    (g_ascii_strcasecmp (tmp, class->sign_protocol) == 0
1655 		     || g_ascii_strcasecmp (tmp, "application/xpkcs7signature") == 0
1656 		     || g_ascii_strcasecmp (tmp, "application/xpkcs7-signature") == 0
1657 		     || g_ascii_strcasecmp (tmp, "application/x-pkcs7-signature") == 0))
1658 			flags = CAMEL_SMIME_SIGNED;
1659 	} else if (camel_content_type_is (ct, "application", "pkcs7-mime")
1660 		   || camel_content_type_is (ct, "application", "xpkcs7mime")
1661 		   || camel_content_type_is (ct, "application", "xpkcs7-mime")
1662 		   || camel_content_type_is (ct, "application", "x-pkcs7-mime")) {
1663 		CamelStream *istream;
1664 		NSSCMSMessage *cmsg;
1665 		NSSCMSDecoderContext *dec;
1666 		GByteArray *buffer;
1667 
1668 		/* FIXME: stream this to the decoder incrementally */
1669 		buffer = g_byte_array_new ();
1670 		istream = camel_stream_mem_new_with_byte_array (buffer);
1671 
1672 		/* FIXME Pass a GCancellable and GError here. */
1673 		camel_data_wrapper_decode_to_stream_sync (
1674 			camel_medium_get_content ((CamelMedium *) part),
1675 			istream, NULL, NULL);
1676 
1677 		g_seekable_seek (
1678 			G_SEEKABLE (istream), 0, G_SEEK_SET, NULL, NULL);
1679 
1680 		dec = NSS_CMSDecoder_Start (
1681 			NULL,
1682 			NULL, NULL,
1683 			NULL, NULL,	/* password callback    */
1684 			NULL, NULL); /* decrypt key callback */
1685 
1686 		NSS_CMSDecoder_Update (dec, (gchar *) buffer->data, buffer->len);
1687 		g_object_unref (istream);
1688 
1689 		cmsg = NSS_CMSDecoder_Finish (dec);
1690 		if (cmsg) {
1691 			if (NSS_CMSMessage_IsSigned (cmsg)) {
1692 				printf ("message is signed\n");
1693 				flags |= CAMEL_SMIME_SIGNED;
1694 			}
1695 
1696 			if (NSS_CMSMessage_IsEncrypted (cmsg)) {
1697 				printf ("message is encrypted\n");
1698 				flags |= CAMEL_SMIME_ENCRYPTED;
1699 			}
1700 #if 0
1701 			if (NSS_CMSMessage_ContainsCertsOrCrls (cmsg)) {
1702 				printf ("message contains certs or crls\n");
1703 				flags |= CAMEL_SMIME_CERTS;
1704 			}
1705 #endif
1706 			NSS_CMSMessage_Destroy (cmsg);
1707 		} else {
1708 			printf ("Message could not be parsed\n");
1709 		}
1710 	}
1711 
1712 	return flags;
1713 }
1714 
1715 #endif /* ENABLE_SMIME */
1716