1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
4 * Michael Zucchi <notzed@ximian.com>
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
11 * Copyright 2003 Ximian, Inc. (www.ximian.com)
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.
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.
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.
47 #include <glib/gi18n-lib.h>
49 #include "camel-data-wrapper.h"
50 #include "camel-exception.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-smime-context.h"
57 #include "camel-stream-filter.h"
58 #include "camel-stream-fs.h"
59 #include "camel-stream-mem.h"
63 struct _CamelSMIMEContextPrivate {
64 CERTCertDBHandle *certdb;
67 camel_smime_sign_t sign_mode;
70 unsigned int send_encrypt_key_prefs:1;
73 static CamelCipherContextClass *parent_class = NULL;
75 /* used for decode content callback, for streaming decode */
77 sm_write_stream(void *arg, const char *buf, unsigned long len)
79 camel_stream_write((CamelStream *)arg, buf, len);
83 sm_decrypt_key(void *arg, SECAlgorithmID *algid)
85 printf("Decrypt key called\n");
86 return (PK11SymKey *)arg;
90 * camel_smime_context_new:
93 * Creates a new sm cipher context object.
95 * Returns a new sm cipher context object.
98 camel_smime_context_new(CamelSession *session)
100 CamelCipherContext *cipher;
101 CamelSMIMEContext *ctx;
103 g_return_val_if_fail(CAMEL_IS_SESSION(session), NULL);
105 ctx =(CamelSMIMEContext *) camel_object_new(camel_smime_context_get_type());
107 cipher =(CamelCipherContext *) ctx;
108 cipher->session = session;
109 camel_object_ref(session);
115 camel_smime_context_set_encrypt_key(CamelSMIMEContext *context, gboolean use, const char *key)
117 context->priv->send_encrypt_key_prefs = use;
118 g_free(context->priv->encrypt_key);
119 context->priv->encrypt_key = g_strdup(key);
122 /* set signing mode, clearsigned multipart/signed or enveloped */
124 camel_smime_context_set_sign_mode(CamelSMIMEContext *context, camel_smime_sign_t type)
126 context->priv->sign_mode = type;
129 /* TODO: This is suboptimal, but the only other solution is to pass around NSSCMSMessages */
131 camel_smime_context_describe_part(CamelSMIMEContext *context, CamelMimePart *part)
134 CamelContentType *ct;
140 ct = camel_mime_part_get_content_type(part);
142 if (camel_content_type_is(ct, "multipart", "signed")) {
143 tmp = camel_content_type_param(ct, "protocol");
145 (g_ascii_strcasecmp(tmp, ((CamelCipherContext *)context)->sign_protocol) == 0
146 || g_ascii_strcasecmp(tmp, "application/pkcs7-signature") == 0))
147 flags = CAMEL_SMIME_SIGNED;
148 } else if (camel_content_type_is(ct, "application", "x-pkcs7-mime")) {
149 CamelStreamMem *istream;
151 NSSCMSDecoderContext *dec;
153 /* FIXME: stream this to the decoder incrementally */
154 istream = (CamelStreamMem *)camel_stream_mem_new();
155 camel_data_wrapper_decode_to_stream(camel_medium_get_content_object((CamelMedium *)part), (CamelStream *)istream);
156 camel_stream_reset((CamelStream *)istream);
158 dec = NSS_CMSDecoder_Start(NULL,
160 NULL, NULL, /* password callback */
161 NULL, NULL); /* decrypt key callback */
163 NSS_CMSDecoder_Update(dec, istream->buffer->data, istream->buffer->len);
164 camel_object_unref(istream);
166 cmsg = NSS_CMSDecoder_Finish(dec);
168 if (NSS_CMSMessage_IsSigned(cmsg)) {
169 printf("message is signed\n");
170 flags |= CAMEL_SMIME_SIGNED;
173 if (NSS_CMSMessage_IsEncrypted(cmsg)) {
174 printf("message is encrypted\n");
175 flags |= CAMEL_SMIME_ENCRYPTED;
178 if (NSS_CMSMessage_ContainsCertsOrCrls(cmsg)) {
179 printf("message contains certs or crls\n");
180 flags |= CAMEL_SMIME_CERTS;
183 NSS_CMSMessage_Destroy(cmsg);
185 printf("Message could not be parsed\n");
193 sm_hash_to_id(CamelCipherContext *context, CamelCipherHash hash)
196 case CAMEL_CIPHER_HASH_MD5:
198 case CAMEL_CIPHER_HASH_SHA1:
199 case CAMEL_CIPHER_HASH_DEFAULT:
206 static CamelCipherHash
207 sm_id_to_hash(CamelCipherContext *context, const char *id)
210 if (!strcmp(id, "md5"))
211 return CAMEL_CIPHER_HASH_MD5;
212 else if (!strcmp(id, "sha1"))
213 return CAMEL_CIPHER_HASH_SHA1;
216 return CAMEL_CIPHER_HASH_DEFAULT;
219 static NSSCMSMessage *
220 sm_signing_cmsmessage(CamelSMIMEContext *context, const char *nick, SECOidTag hash, int detached, CamelException *ex)
222 struct _CamelSMIMEContextPrivate *p = context->priv;
223 NSSCMSMessage *cmsg = NULL;
224 NSSCMSContentInfo *cinfo;
225 NSSCMSSignedData *sigd;
226 NSSCMSSignerInfo *signerinfo;
227 CERTCertificate *cert= NULL, *ekpcert = NULL;
229 if ((cert = CERT_FindUserCertByUsage(p->certdb,
231 certUsageEmailSigner,
234 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot find certificate for '%s'"), nick);
238 cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
240 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS message"));
244 if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) {
245 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS signed data"));
249 cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
250 if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) != SECSuccess) {
251 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach CMS signed data"));
255 /* if !detatched, the contentinfo will alloc a data item for us */
256 cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
257 if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, detached) != SECSuccess) {
258 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach CMS data"));
262 signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, hash);
263 if (signerinfo == NULL) {
264 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS Signer information"));
268 /* we want the cert chain included for this one */
269 if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) {
270 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot find certificate chain"));
274 /* SMIME RFC says signing time should always be added */
275 if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) {
276 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot add CMS Signing time"));
281 /* this can but needn't be added. not sure what general usage is */
282 if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) {
283 fprintf(stderr, "ERROR: cannot add SMIMECaps attribute.\n");
288 /* Check if we need to send along our return encrypt cert, rfc2633 2.5.3 */
289 if (p->send_encrypt_key_prefs) {
290 CERTCertificate *enccert = NULL;
292 if (p->encrypt_key) {
293 /* encrypt key has its own nick */
294 if ((ekpcert = CERT_FindUserCertByUsage(
297 certUsageEmailRecipient, PR_FALSE, NULL)) == NULL) {
298 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Encryption certificate for '%s' does not exist"), p->encrypt_key);
302 } else if (CERT_CheckCertUsage(cert, certUsageEmailRecipient) == SECSuccess) {
303 /* encrypt key is signing key */
306 /* encrypt key uses same nick */
307 if ((ekpcert = CERT_FindUserCertByUsage(
308 p->certdb, (char *)nick,
309 certUsageEmailRecipient, PR_FALSE, NULL)) == NULL) {
310 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Encryption certificate for '%s' does not exist"), nick);
316 if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, enccert, p->certdb) != SECSuccess) {
317 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot add SMIMEEncKeyPrefs attribute"));
321 if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, enccert, p->certdb) != SECSuccess) {
322 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot add MS SMIMEEncKeyPrefs attribute"));
326 if (ekpcert != NULL && NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) {
327 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot add encryption certificate"));
332 if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
333 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot add CMS Signer information"));
338 CERT_DestroyCertificate(ekpcert);
341 CERT_DestroyCertificate(cert);
346 CERT_DestroyCertificate(ekpcert);
349 CERT_DestroyCertificate(cert);
351 NSS_CMSMessage_Destroy(cmsg);
357 sm_sign(CamelCipherContext *context, const char *userid, CamelCipherHash hash, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex)
361 CamelStream *ostream, *istream;
363 NSSCMSEncoderContext *enc;
364 CamelDataWrapper *dw;
365 CamelContentType *ct;
368 case CAMEL_CIPHER_HASH_SHA1:
369 case CAMEL_CIPHER_HASH_DEFAULT:
371 sechash = SEC_OID_SHA1;
373 case CAMEL_CIPHER_HASH_MD5:
374 sechash = SEC_OID_MD5;
378 cmsg = sm_signing_cmsmessage((CamelSMIMEContext *)context, userid, sechash,
379 ((CamelSMIMEContext *)context)->priv->sign_mode == CAMEL_SMIME_SIGN_CLEARSIGN, ex);
383 ostream = camel_stream_mem_new();
385 /* FIXME: stream this, we stream output at least */
386 istream = camel_stream_mem_new();
387 if (camel_cipher_canonical_to_stream(ipart,
388 CAMEL_MIME_FILTER_CANON_STRIP
389 |CAMEL_MIME_FILTER_CANON_CRLF
390 |CAMEL_MIME_FILTER_CANON_FROM, istream) == -1) {
391 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
392 _("Could not generate signing data: %s"), g_strerror(errno));
396 enc = NSS_CMSEncoder_Start(cmsg,
397 sm_write_stream, ostream, /* DER output callback */
398 NULL, NULL, /* destination storage */
399 NULL, NULL, /* password callback */
400 NULL, NULL, /* decrypt key callback */
401 NULL, NULL ); /* detached digests */
403 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create encoder context"));
407 if (NSS_CMSEncoder_Update(enc, ((CamelStreamMem *)istream)->buffer->data, ((CamelStreamMem *)istream)->buffer->len) != SECSuccess) {
408 NSS_CMSEncoder_Cancel(enc);
409 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to add data to CMS encoder"));
413 if (NSS_CMSEncoder_Finish(enc) != SECSuccess) {
414 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to encode data"));
420 dw = camel_data_wrapper_new();
421 camel_stream_reset(ostream);
422 camel_data_wrapper_construct_from_stream(dw, ostream);
423 dw->encoding = CAMEL_TRANSFER_ENCODING_BINARY;
425 if (((CamelSMIMEContext *)context)->priv->sign_mode == CAMEL_SMIME_SIGN_CLEARSIGN) {
426 CamelMultipartSigned *mps;
427 CamelMimePart *sigpart;
429 sigpart = camel_mime_part_new();
430 ct = camel_content_type_new("application", "x-pkcs7-signature");
431 camel_content_type_set_param(ct, "name", "smime.p7s");
432 camel_data_wrapper_set_mime_type_field(dw, ct);
433 camel_content_type_unref(ct);
435 camel_medium_set_content_object((CamelMedium *)sigpart, dw);
437 camel_mime_part_set_filename(sigpart, "smime.p7s");
438 camel_mime_part_set_disposition(sigpart, "attachment");
439 camel_mime_part_set_encoding(sigpart, CAMEL_TRANSFER_ENCODING_BASE64);
441 mps = camel_multipart_signed_new();
442 ct = camel_content_type_new("multipart", "signed");
443 camel_content_type_set_param(ct, "micalg", camel_cipher_hash_to_id(context, hash));
444 camel_content_type_set_param(ct, "protocol", context->sign_protocol);
445 camel_data_wrapper_set_mime_type_field((CamelDataWrapper *)mps, ct);
446 camel_content_type_unref(ct);
447 camel_multipart_set_boundary((CamelMultipart *)mps, NULL);
449 mps->signature = sigpart;
450 mps->contentraw = istream;
451 camel_stream_reset(istream);
452 camel_object_ref(istream);
454 camel_medium_set_content_object((CamelMedium *)opart, (CamelDataWrapper *)mps);
456 ct = camel_content_type_new("application", "x-pkcs7-mime");
457 camel_content_type_set_param(ct, "name", "smime.p7m");
458 camel_content_type_set_param(ct, "smime-type", "signed-data");
459 camel_data_wrapper_set_mime_type_field(dw, ct);
460 camel_content_type_unref(ct);
462 camel_medium_set_content_object((CamelMedium *)opart, dw);
464 camel_mime_part_set_filename(opart, "smime.p7m");
465 camel_mime_part_set_description(opart, "S/MIME Signed Message");
466 camel_mime_part_set_disposition(opart, "attachment");
467 camel_mime_part_set_encoding(opart, CAMEL_TRANSFER_ENCODING_BASE64);
470 camel_object_unref(dw);
472 camel_object_unref(ostream);
473 camel_object_unref(istream);
479 sm_status_description(NSSCMSVerificationStatus status)
481 /* could use this but then we can't control i18n? */
482 /*NSS_CMSUtil_VerificationStatusToString(status));*/
485 case NSSCMSVS_Unverified:
487 return _("Unverified");
488 case NSSCMSVS_GoodSignature:
489 return _("Good signature");
490 case NSSCMSVS_BadSignature:
491 return _("Bad signature");
492 case NSSCMSVS_DigestMismatch:
493 return _("Content tampered with or altered in transit");
494 case NSSCMSVS_SigningCertNotFound:
495 return _("Signing certificate not found");
496 case NSSCMSVS_SigningCertNotTrusted:
497 return _("Signing certificate not trusted");
498 case NSSCMSVS_SignatureAlgorithmUnknown:
499 return _("Signature algorithm unknown");
500 case NSSCMSVS_SignatureAlgorithmUnsupported:
501 return _("Signature algorithm unsupported");
502 case NSSCMSVS_MalformedSignature:
503 return _("Malformed signature");
504 case NSSCMSVS_ProcessingError:
505 return _("Processing error");
509 static CamelCipherValidity *
510 sm_verify_cmsg(CamelCipherContext *context, NSSCMSMessage *cmsg, CamelStream *extstream, CamelException *ex)
512 struct _CamelSMIMEContextPrivate *p = ((CamelSMIMEContext *)context)->priv;
513 NSSCMSSignedData *sigd = NULL;
514 NSSCMSEnvelopedData *envd;
515 NSSCMSEncryptedData *encd;
516 SECAlgorithmID **digestalgs;
517 NSSCMSDigestContext *digcx;
518 int count, i, nsigners, j;
520 PLArenaPool *poolp = NULL;
522 NSSCMSVerificationStatus status;
523 CamelCipherValidity *valid;
524 GString *description;
526 description = g_string_new("");
527 valid = camel_cipher_validity_new();
528 camel_cipher_validity_set_valid(valid, TRUE);
529 status = NSSCMSVS_Unverified;
531 /* NB: this probably needs to go into a decoding routine that can be used for processing
532 enveloped data too */
533 count = NSS_CMSMessage_ContentLevelCount(cmsg);
534 for (i = 0; i < count; i++) {
535 NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(cmsg, i);
536 SECOidTag typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
539 case SEC_OID_PKCS7_SIGNED_DATA:
540 sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
542 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("No signed data in signature"));
546 /* need to build digests of the content */
547 if (!NSS_CMSSignedData_HasDigests(sigd)) {
548 if (extstream == NULL) {
549 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Digests missing from enveloped data"));
553 if ((poolp = PORT_NewArena(1024)) == NULL) {
554 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, g_strerror (ENOMEM));
558 digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
560 digcx = NSS_CMSDigestContext_StartMultiple(digestalgs);
562 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot calculate digests"));
566 mem = (CamelStreamMem *)camel_stream_mem_new();
567 camel_stream_write_to_stream(extstream, (CamelStream *)mem);
568 NSS_CMSDigestContext_Update(digcx, mem->buffer->data, mem->buffer->len);
569 camel_object_unref(mem);
571 if (NSS_CMSDigestContext_FinishMultiple(digcx, poolp, &digests) != SECSuccess) {
572 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot calculate digests"));
576 if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) != SECSuccess) {
577 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot set message digests"));
581 PORT_FreeArena(poolp, PR_FALSE);
585 /* import all certificates present */
586 if (NSS_CMSSignedData_ImportCerts(sigd, p->certdb, certUsageEmailSigner, PR_TRUE) != SECSuccess) {
587 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Certificate import failed"));
591 if (NSS_CMSSignedData_ImportCerts(sigd, p->certdb, certUsageEmailRecipient, PR_TRUE) != SECSuccess) {
592 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Certificate import failed"));
596 /* check for certs-only message */
597 nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
600 /* already imported certs above, not sure what usage we should use here or if this isn't handled above */
601 if (NSS_CMSSignedData_VerifyCertsOnly(sigd, p->certdb, certUsageEmailSigner) != SECSuccess) {
602 g_string_printf(description, _("Certificate is the only message, cannot verify certificates"));
604 status = NSSCMSVS_GoodSignature;
605 g_string_printf(description, _("Certificate is the only message, certificates imported and verified"));
608 if (!NSS_CMSSignedData_HasDigests(sigd)) {
609 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot find signature digests"));
613 for (j = 0; j < nsigners; j++) {
614 NSSCMSSignerInfo *si;
617 si = NSS_CMSSignedData_GetSignerInfo(sigd, j);
618 NSS_CMSSignedData_VerifySignerInfo(sigd, j, p->certdb, certUsageEmailSigner);
620 status = NSS_CMSSignerInfo_GetVerificationStatus(si);
622 cn = NSS_CMSSignerInfo_GetSignerCommonName(si);
623 em = NSS_CMSSignerInfo_GetSignerEmailAddress(si);
625 g_string_append_printf(description, _("Signer: %s <%s>: %s\n"),
626 cn?cn:"<unknown>", em?em:"<unknown>",
627 sm_status_description(status));
629 camel_cipher_validity_add_certinfo(valid, CAMEL_CIPHER_VALIDITY_SIGN, cn, em);
636 if (status != NSSCMSVS_GoodSignature)
637 camel_cipher_validity_set_valid(valid, FALSE);
641 case SEC_OID_PKCS7_ENVELOPED_DATA:
642 envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo);
644 case SEC_OID_PKCS7_ENCRYPTED_DATA:
645 encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo);
647 case SEC_OID_PKCS7_DATA:
654 camel_cipher_validity_set_valid(valid, status == NSSCMSVS_GoodSignature);
655 camel_cipher_validity_set_description(valid, description->str);
656 g_string_free(description, TRUE);
661 camel_cipher_validity_free(valid);
662 g_string_free(description, TRUE);
667 static CamelCipherValidity *
668 sm_verify(CamelCipherContext *context, CamelMimePart *ipart, CamelException *ex)
670 NSSCMSDecoderContext *dec;
673 CamelStream *constream = NULL;
674 CamelCipherValidity *valid = NULL;
675 CamelContentType *ct;
677 CamelMimePart *sigpart;
678 CamelDataWrapper *dw;
680 dw = camel_medium_get_content_object((CamelMedium *)ipart);
683 /* FIXME: we should stream this to the decoder */
684 mem = (CamelStreamMem *)camel_stream_mem_new();
686 if (camel_content_type_is(ct, "multipart", "signed")) {
687 CamelMultipart *mps = (CamelMultipart *)dw;
689 tmp = camel_content_type_param(ct, "protocol");
690 if (!CAMEL_IS_MULTIPART_SIGNED(mps)
692 || (g_ascii_strcasecmp(tmp, context->sign_protocol) != 0
693 && g_ascii_strcasecmp(tmp, "application/pkcs7-signature") != 0)) {
694 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
695 _("Cannot verify message signature: Incorrect message format"));
699 constream = camel_multipart_signed_get_content_stream((CamelMultipartSigned *)mps, ex);
700 if (constream == NULL)
703 sigpart = camel_multipart_get_part(mps, CAMEL_MULTIPART_SIGNED_SIGNATURE);
704 if (sigpart == NULL) {
705 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
706 _("Cannot verify message signature: Incorrect message format"));
709 } else if (camel_content_type_is(ct, "application", "x-pkcs7-mime")) {
712 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
713 _("Cannot verify message signature: Incorrect message format"));
717 dec = NSS_CMSDecoder_Start(NULL,
718 NULL, NULL, /* content callback */
719 NULL, NULL, /* password callback */
720 NULL, NULL); /* decrypt key callback */
722 camel_data_wrapper_decode_to_stream(camel_medium_get_content_object((CamelMedium *)sigpart), (CamelStream *)mem);
723 (void)NSS_CMSDecoder_Update(dec, mem->buffer->data, mem->buffer->len);
724 cmsg = NSS_CMSDecoder_Finish(dec);
726 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Decoder failed"));
730 valid = sm_verify_cmsg(context, cmsg, constream, ex);
732 NSS_CMSMessage_Destroy(cmsg);
734 camel_object_unref(mem);
736 camel_object_unref(constream);
742 sm_encrypt(CamelCipherContext *context, const char *userid, GPtrArray *recipients, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex)
744 struct _CamelSMIMEContextPrivate *p = ((CamelSMIMEContext *)context)->priv;
745 /*NSSCMSRecipientInfo **recipient_infos;*/
746 CERTCertificate **recipient_certs = NULL;
747 NSSCMSContentInfo *cinfo;
748 PK11SymKey *bulkkey = NULL;
749 SECOidTag bulkalgtag;
751 CK_MECHANISM_TYPE type;
754 NSSCMSMessage *cmsg = NULL;
755 NSSCMSEnvelopedData *envd;
756 NSSCMSEncoderContext *enc = NULL;
758 CamelStream *ostream = NULL;
759 CamelDataWrapper *dw;
760 CamelContentType *ct;
762 poolp = PORT_NewArena(1024);
764 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, g_strerror (ENOMEM));
768 /* Lookup all recipients certs, for later working */
769 recipient_certs = (CERTCertificate **)PORT_ArenaZAlloc(poolp, sizeof(*recipient_certs[0])*(recipients->len + 1));
770 if (recipient_certs == NULL) {
771 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, g_strerror (ENOMEM));
775 for (i=0;i<recipients->len;i++) {
776 recipient_certs[i] = CERT_FindCertByNicknameOrEmailAddr(p->certdb, recipients->pdata[i]);
777 if (recipient_certs[i] == NULL) {
778 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot find certificate for `%s'"), recipients->pdata[i]);
783 /* Find a common algorithm, probably 3DES anyway ... */
784 if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipient_certs, &bulkalgtag, &bulkkeysize) != SECSuccess) {
785 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot find common bulk encryption algorithm"));
789 /* Generate a new bulk key based on the common algorithm - expensive */
790 type = PK11_AlgtagToMechanism(bulkalgtag);
791 slot = PK11_GetBestSlot(type, context);
793 /* PORT_GetError(); ?? */
794 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot allocate slot for encryption bulk key"));
798 bulkkey = PK11_KeyGen(slot, type, NULL, bulkkeysize/8, context);
801 /* Now we can start building the message */
802 /* msg->envelopedData->data */
803 cmsg = NSS_CMSMessage_Create(NULL);
805 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS Message"));
809 envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, bulkkeysize);
811 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS Enveloped data"));
815 cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
816 if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd) != SECSuccess) {
817 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach CMS Enveloped data"));
821 cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd);
822 if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) != SECSuccess) {
823 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach CMS data object"));
827 /* add recipient certs */
828 for (i=0;recipient_certs[i];i++) {
829 NSSCMSRecipientInfo *ri = NSS_CMSRecipientInfo_Create(cmsg, recipient_certs[i]);
832 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS Recipient information"));
836 if (NSS_CMSEnvelopedData_AddRecipient(envd, ri) != SECSuccess) {
837 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot add CMS Recipient information"));
843 ostream = camel_stream_mem_new();
844 enc = NSS_CMSEncoder_Start(cmsg,
845 sm_write_stream, ostream,
848 sm_decrypt_key, bulkkey,
851 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create encoder context"));
855 /* FIXME: Stream the input */
856 /* FIXME: Canonicalise the input? */
857 mem = (CamelStreamMem *)camel_stream_mem_new();
858 camel_data_wrapper_write_to_stream((CamelDataWrapper *)ipart, (CamelStream *)mem);
859 if (NSS_CMSEncoder_Update(enc, mem->buffer->data, mem->buffer->len) != SECSuccess) {
860 NSS_CMSEncoder_Cancel(enc);
861 camel_object_unref(mem);
862 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to add data to encoder"));
865 camel_object_unref(mem);
867 if (NSS_CMSEncoder_Finish(enc) != SECSuccess) {
868 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to encode data"));
872 PK11_FreeSymKey(bulkkey);
873 NSS_CMSMessage_Destroy(cmsg);
874 for (i=0;recipient_certs[i];i++)
875 CERT_DestroyCertificate(recipient_certs[i]);
876 PORT_FreeArena(poolp, PR_FALSE);
878 dw = camel_data_wrapper_new();
879 camel_data_wrapper_construct_from_stream(dw, ostream);
880 camel_object_unref(ostream);
881 dw->encoding = CAMEL_TRANSFER_ENCODING_BINARY;
883 ct = camel_content_type_new("application", "x-pkcs7-mime");
884 camel_content_type_set_param(ct, "name", "smime.p7m");
885 camel_content_type_set_param(ct, "smime-type", "enveloped-data");
886 camel_data_wrapper_set_mime_type_field(dw, ct);
887 camel_content_type_unref(ct);
889 camel_medium_set_content_object((CamelMedium *)opart, dw);
890 camel_object_unref(dw);
892 camel_mime_part_set_disposition(opart, "attachment");
893 camel_mime_part_set_filename(opart, "smime.p7m");
894 camel_mime_part_set_description(opart, "S/MIME Encrypted Message");
895 camel_mime_part_set_encoding(opart, CAMEL_TRANSFER_ENCODING_BASE64);
901 camel_object_unref(ostream);
903 NSS_CMSMessage_Destroy(cmsg);
905 PK11_FreeSymKey(bulkkey);
907 if (recipient_certs) {
908 for (i=0;recipient_certs[i];i++)
909 CERT_DestroyCertificate(recipient_certs[i]);
912 PORT_FreeArena(poolp, PR_FALSE);
917 static CamelCipherValidity *
918 sm_decrypt(CamelCipherContext *context, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex)
920 NSSCMSDecoderContext *dec;
922 CamelStreamMem *istream;
923 CamelStream *ostream;
924 CamelCipherValidity *valid = NULL;
926 /* FIXME: This assumes the content is only encrypted. Perhaps its ok for
927 this api to do this ... */
929 ostream = camel_stream_mem_new();
930 camel_stream_mem_set_secure((CamelStreamMem *)ostream);
932 /* FIXME: stream this to the decoder incrementally */
933 istream = (CamelStreamMem *)camel_stream_mem_new();
934 camel_data_wrapper_decode_to_stream(camel_medium_get_content_object((CamelMedium *)ipart), (CamelStream *)istream);
935 camel_stream_reset((CamelStream *)istream);
937 dec = NSS_CMSDecoder_Start(NULL,
938 sm_write_stream, ostream, /* content callback */
940 NULL, NULL); /* decrypt key callback */
942 if (NSS_CMSDecoder_Update(dec, istream->buffer->data, istream->buffer->len) != SECSuccess) {
943 printf("decoder update failed\n");
945 camel_object_unref(istream);
947 cmsg = NSS_CMSDecoder_Finish(dec);
949 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Decoder failed, error %d"), PORT_GetError());
954 /* not sure if we really care about this? */
955 if (!NSS_CMSMessage_IsEncrypted(cmsg)) {
956 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("S/MIME Decrypt: No encrypted content found"));
957 NSS_CMSMessage_Destroy(cmsg);
962 camel_stream_reset(ostream);
963 camel_data_wrapper_construct_from_stream((CamelDataWrapper *)opart, ostream);
965 if (NSS_CMSMessage_IsSigned(cmsg)) {
966 valid = sm_verify_cmsg(context, cmsg, NULL, ex);
968 valid = camel_cipher_validity_new();
969 valid->encrypt.description = g_strdup(_("Encrypted content"));
970 valid->encrypt.status = CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED;
973 NSS_CMSMessage_Destroy(cmsg);
975 camel_object_unref(ostream);
981 sm_import_keys(CamelCipherContext *context, CamelStream *istream, CamelException *ex)
983 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("import keys: unimplemented"));
989 sm_export_keys(CamelCipherContext *context, GPtrArray *keys, CamelStream *ostream, CamelException *ex)
991 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("export keys: unimplemented"));
996 /* ********************************************************************** */
999 camel_smime_context_class_init(CamelSMIMEContextClass *klass)
1001 CamelCipherContextClass *cipher_class = CAMEL_CIPHER_CONTEXT_CLASS(klass);
1003 parent_class = CAMEL_CIPHER_CONTEXT_CLASS(camel_type_get_global_classfuncs(camel_cipher_context_get_type()));
1005 cipher_class->hash_to_id = sm_hash_to_id;
1006 cipher_class->id_to_hash = sm_id_to_hash;
1007 cipher_class->sign = sm_sign;
1008 cipher_class->verify = sm_verify;
1009 cipher_class->encrypt = sm_encrypt;
1010 cipher_class->decrypt = sm_decrypt;
1011 cipher_class->import_keys = sm_import_keys;
1012 cipher_class->export_keys = sm_export_keys;
1016 camel_smime_context_init(CamelSMIMEContext *context)
1018 CamelCipherContext *cipher =(CamelCipherContext *) context;
1020 cipher->sign_protocol = "application/x-pkcs7-signature";
1021 cipher->encrypt_protocol = "application/x-pkcs7-mime";
1022 cipher->key_protocol = "application/x-pkcs7-signature";
1024 context->priv = g_malloc0(sizeof(*context->priv));
1025 context->priv->certdb = CERT_GetDefaultCertDB();
1026 context->priv->sign_mode = CAMEL_SMIME_SIGN_CLEARSIGN;
1027 context->priv->password_tries = 0;
1031 camel_smime_context_finalise(CamelObject *object)
1033 CamelSMIMEContext *context = (CamelSMIMEContext *)object;
1035 /* FIXME: do we have to free the certdb? */
1037 g_free(context->priv);
1041 camel_smime_context_get_type(void)
1043 static CamelType type = CAMEL_INVALID_TYPE;
1045 if (type == CAMEL_INVALID_TYPE) {
1046 type = camel_type_register(camel_cipher_context_get_type(),
1047 "CamelSMIMEContext",
1048 sizeof(CamelSMIMEContext),
1049 sizeof(CamelSMIMEContextClass),
1050 (CamelObjectClassInitFunc) camel_smime_context_class_init,
1052 (CamelObjectInitFunc) camel_smime_context_init,
1053 (CamelObjectFinalizeFunc) camel_smime_context_finalise);
1059 #endif /* ENABLE_SMIME */