1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/manager/ssl/src/nsCMSSecureMessage.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,346 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsXPIDLString.h" 1.10 +#include "nsCOMPtr.h" 1.11 +#include "nsISupports.h" 1.12 +#include "nsIInterfaceRequestor.h" 1.13 +#include "nsCRT.h" 1.14 + 1.15 +#include "nsICMSSecureMessage.h" 1.16 + 1.17 +#include "nsCMSSecureMessage.h" 1.18 +#include "nsNSSCertificate.h" 1.19 +#include "nsNSSHelper.h" 1.20 +#include "nsNSSShutDown.h" 1.21 + 1.22 +#include <string.h> 1.23 +#include "plbase64.h" 1.24 +#include "cert.h" 1.25 +#include "cms.h" 1.26 + 1.27 +#include "nsIServiceManager.h" 1.28 +#include "nsIPrefService.h" 1.29 +#include "nsIPrefBranch.h" 1.30 + 1.31 +#include "prlog.h" 1.32 +#ifdef PR_LOGGING 1.33 +extern PRLogModuleInfo* gPIPNSSLog; 1.34 +#endif 1.35 + 1.36 +// Standard ISupports implementation 1.37 +// NOTE: Should these be the thread-safe versions? 1.38 + 1.39 +/***** 1.40 + * nsCMSSecureMessage 1.41 + *****/ 1.42 + 1.43 +// Standard ISupports implementation 1.44 +NS_IMPL_ISUPPORTS(nsCMSSecureMessage, nsICMSSecureMessage) 1.45 + 1.46 +// nsCMSSecureMessage constructor 1.47 +nsCMSSecureMessage::nsCMSSecureMessage() 1.48 +{ 1.49 + // initialize superclass 1.50 +} 1.51 + 1.52 +// nsCMSMessage destructor 1.53 +nsCMSSecureMessage::~nsCMSSecureMessage() 1.54 +{ 1.55 +} 1.56 + 1.57 +/* string getCertByPrefID (in string certID); */ 1.58 +NS_IMETHODIMP nsCMSSecureMessage:: 1.59 +GetCertByPrefID(const char *certID, char **_retval) 1.60 +{ 1.61 + nsNSSShutDownPreventionLock locker; 1.62 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::GetCertByPrefID\n")); 1.63 + nsresult rv = NS_OK; 1.64 + CERTCertificate *cert = 0; 1.65 + nsXPIDLCString nickname; 1.66 + nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext(); 1.67 + 1.68 + *_retval = 0; 1.69 + 1.70 + nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); 1.71 + if (NS_FAILED(rv)) { 1.72 + goto done; 1.73 + } 1.74 + 1.75 + rv = prefs->GetCharPref(certID, 1.76 + getter_Copies(nickname)); 1.77 + if (NS_FAILED(rv)) goto done; 1.78 + 1.79 + /* Find a good cert in the user's database */ 1.80 + cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), const_cast<char*>(nickname.get()), 1.81 + certUsageEmailRecipient, true, ctx); 1.82 + 1.83 + if (!cert) { 1.84 + /* Success, but no value */ 1.85 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::GetCertByPrefID - can't find user cert\n")); 1.86 + goto done; 1.87 + } 1.88 + 1.89 + /* Convert the DER to a BASE64 String */ 1.90 + encode(cert->derCert.data, cert->derCert.len, _retval); 1.91 + 1.92 +done: 1.93 + if (cert) CERT_DestroyCertificate(cert); 1.94 + return rv; 1.95 +} 1.96 + 1.97 + 1.98 +// nsCMSSecureMessage::DecodeCert 1.99 +nsresult nsCMSSecureMessage:: 1.100 +DecodeCert(const char *value, nsIX509Cert ** _retval) 1.101 +{ 1.102 + nsNSSShutDownPreventionLock locker; 1.103 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::DecodeCert\n")); 1.104 + nsresult rv = NS_OK; 1.105 + int32_t length; 1.106 + unsigned char *data = 0; 1.107 + 1.108 + *_retval = 0; 1.109 + 1.110 + if (!value) { return NS_ERROR_FAILURE; } 1.111 + 1.112 + rv = decode(value, &data, &length); 1.113 + if (NS_FAILED(rv)) { 1.114 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::DecodeCert - can't decode cert\n")); 1.115 + return rv; 1.116 + } 1.117 + 1.118 + nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::ConstructFromDER((char *)data, length); 1.119 + 1.120 + if (cert) { 1.121 + *_retval = cert; 1.122 + NS_ADDREF(*_retval); 1.123 + } 1.124 + else { 1.125 + rv = NS_ERROR_FAILURE; 1.126 + } 1.127 + 1.128 + free((char*)data); 1.129 + return rv; 1.130 +} 1.131 + 1.132 +// nsCMSSecureMessage::SendMessage 1.133 +nsresult nsCMSSecureMessage:: 1.134 +SendMessage(const char *msg, const char *base64Cert, char ** _retval) 1.135 +{ 1.136 + nsNSSShutDownPreventionLock locker; 1.137 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage\n")); 1.138 + nsresult rv = NS_OK; 1.139 + CERTCertificate *cert = 0; 1.140 + NSSCMSMessage *cmsMsg = 0; 1.141 + unsigned char *certDER = 0; 1.142 + int32_t derLen; 1.143 + NSSCMSEnvelopedData *env; 1.144 + NSSCMSContentInfo *cinfo; 1.145 + NSSCMSRecipientInfo *rcpt; 1.146 + SECItem output; 1.147 + PLArenaPool *arena = PORT_NewArena(1024); 1.148 + SECStatus s; 1.149 + nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext(); 1.150 + 1.151 + /* Step 0. Create a CMS Message */ 1.152 + cmsMsg = NSS_CMSMessage_Create(nullptr); 1.153 + if (!cmsMsg) { 1.154 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create NSSCMSMessage\n")); 1.155 + rv = NS_ERROR_FAILURE; 1.156 + goto done; 1.157 + } 1.158 + 1.159 + /* Step 1. Import the certificate into NSS */ 1.160 + rv = decode(base64Cert, &certDER, &derLen); 1.161 + if (NS_FAILED(rv)) { 1.162 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't decode / import cert into NSS\n")); 1.163 + goto done; 1.164 + } 1.165 + 1.166 + cert = CERT_DecodeCertFromPackage((char *)certDER, derLen); 1.167 + if (!cert) { 1.168 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't decode cert from package\n")); 1.169 + rv = NS_ERROR_FAILURE; 1.170 + goto done; 1.171 + } 1.172 + 1.173 + /* Step 2. Get a signature cert */ 1.174 + 1.175 + /* Step 3. Build inner (signature) content */ 1.176 + 1.177 + /* Step 4. Build outer (enveloped) content */ 1.178 + env = NSS_CMSEnvelopedData_Create(cmsMsg, SEC_OID_DES_EDE3_CBC, 0); 1.179 + if (!env) { 1.180 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create envelope data\n")); 1.181 + rv = NS_ERROR_FAILURE; 1.182 + goto done; 1.183 + } 1.184 + 1.185 + cinfo = NSS_CMSEnvelopedData_GetContentInfo(env); 1.186 + s = NSS_CMSContentInfo_SetContent_Data(cmsMsg, cinfo, 0, false); 1.187 + if (s != SECSuccess) { 1.188 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't set content data\n")); 1.189 + rv = NS_ERROR_FAILURE; 1.190 + goto done; 1.191 + } 1.192 + 1.193 + rcpt = NSS_CMSRecipientInfo_Create(cmsMsg, cert); 1.194 + if (!rcpt) { 1.195 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create recipient info\n")); 1.196 + rv = NS_ERROR_FAILURE; 1.197 + goto done; 1.198 + } 1.199 + 1.200 + s = NSS_CMSEnvelopedData_AddRecipient(env, rcpt); 1.201 + if (s != SECSuccess) { 1.202 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't add recipient\n")); 1.203 + rv = NS_ERROR_FAILURE; 1.204 + goto done; 1.205 + } 1.206 + 1.207 + /* Step 5. Add content to message */ 1.208 + cinfo = NSS_CMSMessage_GetContentInfo(cmsMsg); 1.209 + s = NSS_CMSContentInfo_SetContent_EnvelopedData(cmsMsg, cinfo, env); 1.210 + if (s != SECSuccess) { 1.211 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't set content enveloped data\n")); 1.212 + rv = NS_ERROR_FAILURE; 1.213 + goto done; 1.214 + } 1.215 + 1.216 + /* Step 6. Encode */ 1.217 + NSSCMSEncoderContext *ecx; 1.218 + 1.219 + output.data = 0; output.len = 0; 1.220 + ecx = NSS_CMSEncoder_Start(cmsMsg, 0, 0, &output, arena, 1.221 + 0, ctx, 0, 0, 0, 0); 1.222 + if (!ecx) { 1.223 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't start cms encoder\n")); 1.224 + rv = NS_ERROR_FAILURE; 1.225 + goto done; 1.226 + } 1.227 + 1.228 + s = NSS_CMSEncoder_Update(ecx, msg, strlen(msg)); 1.229 + if (s != SECSuccess) { 1.230 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't update encoder\n")); 1.231 + rv = NS_ERROR_FAILURE; 1.232 + goto done; 1.233 + } 1.234 + 1.235 + s = NSS_CMSEncoder_Finish(ecx); 1.236 + if (s != SECSuccess) { 1.237 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't finish encoder\n")); 1.238 + rv = NS_ERROR_FAILURE; 1.239 + goto done; 1.240 + } 1.241 + 1.242 + /* Step 7. Base64 encode and return the result */ 1.243 + rv = encode(output.data, output.len, _retval); 1.244 + 1.245 +done: 1.246 + if (certDER) free((char *)certDER); 1.247 + if (cert) CERT_DestroyCertificate(cert); 1.248 + if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg); 1.249 + if (arena) PORT_FreeArena(arena, false); /* false? */ 1.250 + 1.251 + return rv; 1.252 +} 1.253 + 1.254 +/* 1.255 + * nsCMSSecureMessage::ReceiveMessage 1.256 + */ 1.257 +nsresult nsCMSSecureMessage:: 1.258 +ReceiveMessage(const char *msg, char **_retval) 1.259 +{ 1.260 + nsNSSShutDownPreventionLock locker; 1.261 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage\n")); 1.262 + nsresult rv = NS_OK; 1.263 + NSSCMSDecoderContext *dcx; 1.264 + unsigned char *der = 0; 1.265 + int32_t derLen; 1.266 + NSSCMSMessage *cmsMsg = 0; 1.267 + SECItem *content; 1.268 + nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext(); 1.269 + 1.270 + /* Step 1. Decode the base64 wrapper */ 1.271 + rv = decode(msg, &der, &derLen); 1.272 + if (NS_FAILED(rv)) { 1.273 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't base64 decode\n")); 1.274 + goto done; 1.275 + } 1.276 + 1.277 + dcx = NSS_CMSDecoder_Start(0, 0, 0, /* pw */ 0, ctx, /* key */ 0, 0); 1.278 + if (!dcx) { 1.279 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't start decoder\n")); 1.280 + rv = NS_ERROR_FAILURE; 1.281 + goto done; 1.282 + } 1.283 + 1.284 + (void)NSS_CMSDecoder_Update(dcx, (char *)der, derLen); 1.285 + cmsMsg = NSS_CMSDecoder_Finish(dcx); 1.286 + if (!cmsMsg) { 1.287 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't finish decoder\n")); 1.288 + rv = NS_ERROR_FAILURE; 1.289 + /* Memory leak on dcx?? */ 1.290 + goto done; 1.291 + } 1.292 + 1.293 + content = NSS_CMSMessage_GetContent(cmsMsg); 1.294 + if (!content) { 1.295 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't get content\n")); 1.296 + rv = NS_ERROR_FAILURE; 1.297 + goto done; 1.298 + } 1.299 + 1.300 + /* Copy the data */ 1.301 + *_retval = (char*)malloc(content->len+1); 1.302 + memcpy(*_retval, content->data, content->len); 1.303 + (*_retval)[content->len] = 0; 1.304 + 1.305 +done: 1.306 + if (der) free(der); 1.307 + if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg); 1.308 + 1.309 + return rv; 1.310 +} 1.311 + 1.312 +nsresult nsCMSSecureMessage:: 1.313 +encode(const unsigned char *data, int32_t dataLen, char **_retval) 1.314 +{ 1.315 + nsresult rv = NS_OK; 1.316 + 1.317 + *_retval = PL_Base64Encode((const char *)data, dataLen, nullptr); 1.318 + if (!*_retval) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } 1.319 + 1.320 +loser: 1.321 + return rv; 1.322 +} 1.323 + 1.324 +nsresult nsCMSSecureMessage:: 1.325 +decode(const char *data, unsigned char **result, int32_t * _retval) 1.326 +{ 1.327 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::decode\n")); 1.328 + nsresult rv = NS_OK; 1.329 + uint32_t len = strlen(data); 1.330 + int adjust = 0; 1.331 + 1.332 + /* Compute length adjustment */ 1.333 + if (data[len-1] == '=') { 1.334 + adjust++; 1.335 + if (data[len-2] == '=') adjust++; 1.336 + } 1.337 + 1.338 + *result = (unsigned char *)PL_Base64Decode(data, len, nullptr); 1.339 + if (!*result) { 1.340 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::decode - error decoding base64\n")); 1.341 + rv = NS_ERROR_ILLEGAL_VALUE; 1.342 + goto loser; 1.343 + } 1.344 + 1.345 + *_retval = (len*3)/4 - adjust; 1.346 + 1.347 +loser: 1.348 + return rv; 1.349 +}