michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsXPIDLString.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsISupports.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "nsCRT.h" michael@0: michael@0: #include "nsICMSSecureMessage.h" michael@0: michael@0: #include "nsCMSSecureMessage.h" michael@0: #include "nsNSSCertificate.h" michael@0: #include "nsNSSHelper.h" michael@0: #include "nsNSSShutDown.h" michael@0: michael@0: #include michael@0: #include "plbase64.h" michael@0: #include "cert.h" michael@0: #include "cms.h" michael@0: michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIPrefBranch.h" michael@0: michael@0: #include "prlog.h" michael@0: #ifdef PR_LOGGING michael@0: extern PRLogModuleInfo* gPIPNSSLog; michael@0: #endif michael@0: michael@0: // Standard ISupports implementation michael@0: // NOTE: Should these be the thread-safe versions? michael@0: michael@0: /***** michael@0: * nsCMSSecureMessage michael@0: *****/ michael@0: michael@0: // Standard ISupports implementation michael@0: NS_IMPL_ISUPPORTS(nsCMSSecureMessage, nsICMSSecureMessage) michael@0: michael@0: // nsCMSSecureMessage constructor michael@0: nsCMSSecureMessage::nsCMSSecureMessage() michael@0: { michael@0: // initialize superclass michael@0: } michael@0: michael@0: // nsCMSMessage destructor michael@0: nsCMSSecureMessage::~nsCMSSecureMessage() michael@0: { michael@0: } michael@0: michael@0: /* string getCertByPrefID (in string certID); */ michael@0: NS_IMETHODIMP nsCMSSecureMessage:: michael@0: GetCertByPrefID(const char *certID, char **_retval) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::GetCertByPrefID\n")); michael@0: nsresult rv = NS_OK; michael@0: CERTCertificate *cert = 0; michael@0: nsXPIDLCString nickname; michael@0: nsCOMPtr ctx = new PipUIContext(); michael@0: michael@0: *_retval = 0; michael@0: michael@0: nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) { michael@0: goto done; michael@0: } michael@0: michael@0: rv = prefs->GetCharPref(certID, michael@0: getter_Copies(nickname)); michael@0: if (NS_FAILED(rv)) goto done; michael@0: michael@0: /* Find a good cert in the user's database */ michael@0: cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), const_cast(nickname.get()), michael@0: certUsageEmailRecipient, true, ctx); michael@0: michael@0: if (!cert) { michael@0: /* Success, but no value */ michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::GetCertByPrefID - can't find user cert\n")); michael@0: goto done; michael@0: } michael@0: michael@0: /* Convert the DER to a BASE64 String */ michael@0: encode(cert->derCert.data, cert->derCert.len, _retval); michael@0: michael@0: done: michael@0: if (cert) CERT_DestroyCertificate(cert); michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: // nsCMSSecureMessage::DecodeCert michael@0: nsresult nsCMSSecureMessage:: michael@0: DecodeCert(const char *value, nsIX509Cert ** _retval) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::DecodeCert\n")); michael@0: nsresult rv = NS_OK; michael@0: int32_t length; michael@0: unsigned char *data = 0; michael@0: michael@0: *_retval = 0; michael@0: michael@0: if (!value) { return NS_ERROR_FAILURE; } michael@0: michael@0: rv = decode(value, &data, &length); michael@0: if (NS_FAILED(rv)) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::DecodeCert - can't decode cert\n")); michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr cert = nsNSSCertificate::ConstructFromDER((char *)data, length); michael@0: michael@0: if (cert) { michael@0: *_retval = cert; michael@0: NS_ADDREF(*_retval); michael@0: } michael@0: else { michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: free((char*)data); michael@0: return rv; michael@0: } michael@0: michael@0: // nsCMSSecureMessage::SendMessage michael@0: nsresult nsCMSSecureMessage:: michael@0: SendMessage(const char *msg, const char *base64Cert, char ** _retval) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage\n")); michael@0: nsresult rv = NS_OK; michael@0: CERTCertificate *cert = 0; michael@0: NSSCMSMessage *cmsMsg = 0; michael@0: unsigned char *certDER = 0; michael@0: int32_t derLen; michael@0: NSSCMSEnvelopedData *env; michael@0: NSSCMSContentInfo *cinfo; michael@0: NSSCMSRecipientInfo *rcpt; michael@0: SECItem output; michael@0: PLArenaPool *arena = PORT_NewArena(1024); michael@0: SECStatus s; michael@0: nsCOMPtr ctx = new PipUIContext(); michael@0: michael@0: /* Step 0. Create a CMS Message */ michael@0: cmsMsg = NSS_CMSMessage_Create(nullptr); michael@0: if (!cmsMsg) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create NSSCMSMessage\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: /* Step 1. Import the certificate into NSS */ michael@0: rv = decode(base64Cert, &certDER, &derLen); michael@0: if (NS_FAILED(rv)) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't decode / import cert into NSS\n")); michael@0: goto done; michael@0: } michael@0: michael@0: cert = CERT_DecodeCertFromPackage((char *)certDER, derLen); michael@0: if (!cert) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't decode cert from package\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: /* Step 2. Get a signature cert */ michael@0: michael@0: /* Step 3. Build inner (signature) content */ michael@0: michael@0: /* Step 4. Build outer (enveloped) content */ michael@0: env = NSS_CMSEnvelopedData_Create(cmsMsg, SEC_OID_DES_EDE3_CBC, 0); michael@0: if (!env) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create envelope data\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: cinfo = NSS_CMSEnvelopedData_GetContentInfo(env); michael@0: s = NSS_CMSContentInfo_SetContent_Data(cmsMsg, cinfo, 0, false); michael@0: if (s != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't set content data\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: rcpt = NSS_CMSRecipientInfo_Create(cmsMsg, cert); michael@0: if (!rcpt) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create recipient info\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: s = NSS_CMSEnvelopedData_AddRecipient(env, rcpt); michael@0: if (s != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't add recipient\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: /* Step 5. Add content to message */ michael@0: cinfo = NSS_CMSMessage_GetContentInfo(cmsMsg); michael@0: s = NSS_CMSContentInfo_SetContent_EnvelopedData(cmsMsg, cinfo, env); michael@0: if (s != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't set content enveloped data\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: /* Step 6. Encode */ michael@0: NSSCMSEncoderContext *ecx; michael@0: michael@0: output.data = 0; output.len = 0; michael@0: ecx = NSS_CMSEncoder_Start(cmsMsg, 0, 0, &output, arena, michael@0: 0, ctx, 0, 0, 0, 0); michael@0: if (!ecx) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't start cms encoder\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: s = NSS_CMSEncoder_Update(ecx, msg, strlen(msg)); michael@0: if (s != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't update encoder\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: s = NSS_CMSEncoder_Finish(ecx); michael@0: if (s != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't finish encoder\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: /* Step 7. Base64 encode and return the result */ michael@0: rv = encode(output.data, output.len, _retval); michael@0: michael@0: done: michael@0: if (certDER) free((char *)certDER); michael@0: if (cert) CERT_DestroyCertificate(cert); michael@0: if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg); michael@0: if (arena) PORT_FreeArena(arena, false); /* false? */ michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * nsCMSSecureMessage::ReceiveMessage michael@0: */ michael@0: nsresult nsCMSSecureMessage:: michael@0: ReceiveMessage(const char *msg, char **_retval) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage\n")); michael@0: nsresult rv = NS_OK; michael@0: NSSCMSDecoderContext *dcx; michael@0: unsigned char *der = 0; michael@0: int32_t derLen; michael@0: NSSCMSMessage *cmsMsg = 0; michael@0: SECItem *content; michael@0: nsCOMPtr ctx = new PipUIContext(); michael@0: michael@0: /* Step 1. Decode the base64 wrapper */ michael@0: rv = decode(msg, &der, &derLen); michael@0: if (NS_FAILED(rv)) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't base64 decode\n")); michael@0: goto done; michael@0: } michael@0: michael@0: dcx = NSS_CMSDecoder_Start(0, 0, 0, /* pw */ 0, ctx, /* key */ 0, 0); michael@0: if (!dcx) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't start decoder\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: (void)NSS_CMSDecoder_Update(dcx, (char *)der, derLen); michael@0: cmsMsg = NSS_CMSDecoder_Finish(dcx); michael@0: if (!cmsMsg) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't finish decoder\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: /* Memory leak on dcx?? */ michael@0: goto done; michael@0: } michael@0: michael@0: content = NSS_CMSMessage_GetContent(cmsMsg); michael@0: if (!content) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't get content\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: /* Copy the data */ michael@0: *_retval = (char*)malloc(content->len+1); michael@0: memcpy(*_retval, content->data, content->len); michael@0: (*_retval)[content->len] = 0; michael@0: michael@0: done: michael@0: if (der) free(der); michael@0: if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult nsCMSSecureMessage:: michael@0: encode(const unsigned char *data, int32_t dataLen, char **_retval) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: *_retval = PL_Base64Encode((const char *)data, dataLen, nullptr); michael@0: if (!*_retval) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } michael@0: michael@0: loser: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult nsCMSSecureMessage:: michael@0: decode(const char *data, unsigned char **result, int32_t * _retval) michael@0: { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::decode\n")); michael@0: nsresult rv = NS_OK; michael@0: uint32_t len = strlen(data); michael@0: int adjust = 0; michael@0: michael@0: /* Compute length adjustment */ michael@0: if (data[len-1] == '=') { michael@0: adjust++; michael@0: if (data[len-2] == '=') adjust++; michael@0: } michael@0: michael@0: *result = (unsigned char *)PL_Base64Decode(data, len, nullptr); michael@0: if (!*result) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::decode - error decoding base64\n")); michael@0: rv = NS_ERROR_ILLEGAL_VALUE; michael@0: goto loser; michael@0: } michael@0: michael@0: *_retval = (len*3)/4 - adjust; michael@0: michael@0: loser: michael@0: return rv; michael@0: }