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, ¬Before1, ¬After1) != SECSuccess)
1096 return FALSE;
1097
1098 if (CERT_GetCertTimes (cert2, ¬Before2, ¬After2) != 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