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