|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsXPIDLString.h" |
|
7 #include "nsCOMPtr.h" |
|
8 #include "nsISupports.h" |
|
9 #include "nsIInterfaceRequestor.h" |
|
10 #include "nsCRT.h" |
|
11 |
|
12 #include "nsICMSSecureMessage.h" |
|
13 |
|
14 #include "nsCMSSecureMessage.h" |
|
15 #include "nsNSSCertificate.h" |
|
16 #include "nsNSSHelper.h" |
|
17 #include "nsNSSShutDown.h" |
|
18 |
|
19 #include <string.h> |
|
20 #include "plbase64.h" |
|
21 #include "cert.h" |
|
22 #include "cms.h" |
|
23 |
|
24 #include "nsIServiceManager.h" |
|
25 #include "nsIPrefService.h" |
|
26 #include "nsIPrefBranch.h" |
|
27 |
|
28 #include "prlog.h" |
|
29 #ifdef PR_LOGGING |
|
30 extern PRLogModuleInfo* gPIPNSSLog; |
|
31 #endif |
|
32 |
|
33 // Standard ISupports implementation |
|
34 // NOTE: Should these be the thread-safe versions? |
|
35 |
|
36 /***** |
|
37 * nsCMSSecureMessage |
|
38 *****/ |
|
39 |
|
40 // Standard ISupports implementation |
|
41 NS_IMPL_ISUPPORTS(nsCMSSecureMessage, nsICMSSecureMessage) |
|
42 |
|
43 // nsCMSSecureMessage constructor |
|
44 nsCMSSecureMessage::nsCMSSecureMessage() |
|
45 { |
|
46 // initialize superclass |
|
47 } |
|
48 |
|
49 // nsCMSMessage destructor |
|
50 nsCMSSecureMessage::~nsCMSSecureMessage() |
|
51 { |
|
52 } |
|
53 |
|
54 /* string getCertByPrefID (in string certID); */ |
|
55 NS_IMETHODIMP nsCMSSecureMessage:: |
|
56 GetCertByPrefID(const char *certID, char **_retval) |
|
57 { |
|
58 nsNSSShutDownPreventionLock locker; |
|
59 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::GetCertByPrefID\n")); |
|
60 nsresult rv = NS_OK; |
|
61 CERTCertificate *cert = 0; |
|
62 nsXPIDLCString nickname; |
|
63 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext(); |
|
64 |
|
65 *_retval = 0; |
|
66 |
|
67 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); |
|
68 if (NS_FAILED(rv)) { |
|
69 goto done; |
|
70 } |
|
71 |
|
72 rv = prefs->GetCharPref(certID, |
|
73 getter_Copies(nickname)); |
|
74 if (NS_FAILED(rv)) goto done; |
|
75 |
|
76 /* Find a good cert in the user's database */ |
|
77 cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), const_cast<char*>(nickname.get()), |
|
78 certUsageEmailRecipient, true, ctx); |
|
79 |
|
80 if (!cert) { |
|
81 /* Success, but no value */ |
|
82 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::GetCertByPrefID - can't find user cert\n")); |
|
83 goto done; |
|
84 } |
|
85 |
|
86 /* Convert the DER to a BASE64 String */ |
|
87 encode(cert->derCert.data, cert->derCert.len, _retval); |
|
88 |
|
89 done: |
|
90 if (cert) CERT_DestroyCertificate(cert); |
|
91 return rv; |
|
92 } |
|
93 |
|
94 |
|
95 // nsCMSSecureMessage::DecodeCert |
|
96 nsresult nsCMSSecureMessage:: |
|
97 DecodeCert(const char *value, nsIX509Cert ** _retval) |
|
98 { |
|
99 nsNSSShutDownPreventionLock locker; |
|
100 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::DecodeCert\n")); |
|
101 nsresult rv = NS_OK; |
|
102 int32_t length; |
|
103 unsigned char *data = 0; |
|
104 |
|
105 *_retval = 0; |
|
106 |
|
107 if (!value) { return NS_ERROR_FAILURE; } |
|
108 |
|
109 rv = decode(value, &data, &length); |
|
110 if (NS_FAILED(rv)) { |
|
111 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::DecodeCert - can't decode cert\n")); |
|
112 return rv; |
|
113 } |
|
114 |
|
115 nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::ConstructFromDER((char *)data, length); |
|
116 |
|
117 if (cert) { |
|
118 *_retval = cert; |
|
119 NS_ADDREF(*_retval); |
|
120 } |
|
121 else { |
|
122 rv = NS_ERROR_FAILURE; |
|
123 } |
|
124 |
|
125 free((char*)data); |
|
126 return rv; |
|
127 } |
|
128 |
|
129 // nsCMSSecureMessage::SendMessage |
|
130 nsresult nsCMSSecureMessage:: |
|
131 SendMessage(const char *msg, const char *base64Cert, char ** _retval) |
|
132 { |
|
133 nsNSSShutDownPreventionLock locker; |
|
134 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage\n")); |
|
135 nsresult rv = NS_OK; |
|
136 CERTCertificate *cert = 0; |
|
137 NSSCMSMessage *cmsMsg = 0; |
|
138 unsigned char *certDER = 0; |
|
139 int32_t derLen; |
|
140 NSSCMSEnvelopedData *env; |
|
141 NSSCMSContentInfo *cinfo; |
|
142 NSSCMSRecipientInfo *rcpt; |
|
143 SECItem output; |
|
144 PLArenaPool *arena = PORT_NewArena(1024); |
|
145 SECStatus s; |
|
146 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext(); |
|
147 |
|
148 /* Step 0. Create a CMS Message */ |
|
149 cmsMsg = NSS_CMSMessage_Create(nullptr); |
|
150 if (!cmsMsg) { |
|
151 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create NSSCMSMessage\n")); |
|
152 rv = NS_ERROR_FAILURE; |
|
153 goto done; |
|
154 } |
|
155 |
|
156 /* Step 1. Import the certificate into NSS */ |
|
157 rv = decode(base64Cert, &certDER, &derLen); |
|
158 if (NS_FAILED(rv)) { |
|
159 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't decode / import cert into NSS\n")); |
|
160 goto done; |
|
161 } |
|
162 |
|
163 cert = CERT_DecodeCertFromPackage((char *)certDER, derLen); |
|
164 if (!cert) { |
|
165 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't decode cert from package\n")); |
|
166 rv = NS_ERROR_FAILURE; |
|
167 goto done; |
|
168 } |
|
169 |
|
170 /* Step 2. Get a signature cert */ |
|
171 |
|
172 /* Step 3. Build inner (signature) content */ |
|
173 |
|
174 /* Step 4. Build outer (enveloped) content */ |
|
175 env = NSS_CMSEnvelopedData_Create(cmsMsg, SEC_OID_DES_EDE3_CBC, 0); |
|
176 if (!env) { |
|
177 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create envelope data\n")); |
|
178 rv = NS_ERROR_FAILURE; |
|
179 goto done; |
|
180 } |
|
181 |
|
182 cinfo = NSS_CMSEnvelopedData_GetContentInfo(env); |
|
183 s = NSS_CMSContentInfo_SetContent_Data(cmsMsg, cinfo, 0, false); |
|
184 if (s != SECSuccess) { |
|
185 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't set content data\n")); |
|
186 rv = NS_ERROR_FAILURE; |
|
187 goto done; |
|
188 } |
|
189 |
|
190 rcpt = NSS_CMSRecipientInfo_Create(cmsMsg, cert); |
|
191 if (!rcpt) { |
|
192 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create recipient info\n")); |
|
193 rv = NS_ERROR_FAILURE; |
|
194 goto done; |
|
195 } |
|
196 |
|
197 s = NSS_CMSEnvelopedData_AddRecipient(env, rcpt); |
|
198 if (s != SECSuccess) { |
|
199 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't add recipient\n")); |
|
200 rv = NS_ERROR_FAILURE; |
|
201 goto done; |
|
202 } |
|
203 |
|
204 /* Step 5. Add content to message */ |
|
205 cinfo = NSS_CMSMessage_GetContentInfo(cmsMsg); |
|
206 s = NSS_CMSContentInfo_SetContent_EnvelopedData(cmsMsg, cinfo, env); |
|
207 if (s != SECSuccess) { |
|
208 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't set content enveloped data\n")); |
|
209 rv = NS_ERROR_FAILURE; |
|
210 goto done; |
|
211 } |
|
212 |
|
213 /* Step 6. Encode */ |
|
214 NSSCMSEncoderContext *ecx; |
|
215 |
|
216 output.data = 0; output.len = 0; |
|
217 ecx = NSS_CMSEncoder_Start(cmsMsg, 0, 0, &output, arena, |
|
218 0, ctx, 0, 0, 0, 0); |
|
219 if (!ecx) { |
|
220 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't start cms encoder\n")); |
|
221 rv = NS_ERROR_FAILURE; |
|
222 goto done; |
|
223 } |
|
224 |
|
225 s = NSS_CMSEncoder_Update(ecx, msg, strlen(msg)); |
|
226 if (s != SECSuccess) { |
|
227 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't update encoder\n")); |
|
228 rv = NS_ERROR_FAILURE; |
|
229 goto done; |
|
230 } |
|
231 |
|
232 s = NSS_CMSEncoder_Finish(ecx); |
|
233 if (s != SECSuccess) { |
|
234 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't finish encoder\n")); |
|
235 rv = NS_ERROR_FAILURE; |
|
236 goto done; |
|
237 } |
|
238 |
|
239 /* Step 7. Base64 encode and return the result */ |
|
240 rv = encode(output.data, output.len, _retval); |
|
241 |
|
242 done: |
|
243 if (certDER) free((char *)certDER); |
|
244 if (cert) CERT_DestroyCertificate(cert); |
|
245 if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg); |
|
246 if (arena) PORT_FreeArena(arena, false); /* false? */ |
|
247 |
|
248 return rv; |
|
249 } |
|
250 |
|
251 /* |
|
252 * nsCMSSecureMessage::ReceiveMessage |
|
253 */ |
|
254 nsresult nsCMSSecureMessage:: |
|
255 ReceiveMessage(const char *msg, char **_retval) |
|
256 { |
|
257 nsNSSShutDownPreventionLock locker; |
|
258 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage\n")); |
|
259 nsresult rv = NS_OK; |
|
260 NSSCMSDecoderContext *dcx; |
|
261 unsigned char *der = 0; |
|
262 int32_t derLen; |
|
263 NSSCMSMessage *cmsMsg = 0; |
|
264 SECItem *content; |
|
265 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext(); |
|
266 |
|
267 /* Step 1. Decode the base64 wrapper */ |
|
268 rv = decode(msg, &der, &derLen); |
|
269 if (NS_FAILED(rv)) { |
|
270 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't base64 decode\n")); |
|
271 goto done; |
|
272 } |
|
273 |
|
274 dcx = NSS_CMSDecoder_Start(0, 0, 0, /* pw */ 0, ctx, /* key */ 0, 0); |
|
275 if (!dcx) { |
|
276 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't start decoder\n")); |
|
277 rv = NS_ERROR_FAILURE; |
|
278 goto done; |
|
279 } |
|
280 |
|
281 (void)NSS_CMSDecoder_Update(dcx, (char *)der, derLen); |
|
282 cmsMsg = NSS_CMSDecoder_Finish(dcx); |
|
283 if (!cmsMsg) { |
|
284 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't finish decoder\n")); |
|
285 rv = NS_ERROR_FAILURE; |
|
286 /* Memory leak on dcx?? */ |
|
287 goto done; |
|
288 } |
|
289 |
|
290 content = NSS_CMSMessage_GetContent(cmsMsg); |
|
291 if (!content) { |
|
292 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't get content\n")); |
|
293 rv = NS_ERROR_FAILURE; |
|
294 goto done; |
|
295 } |
|
296 |
|
297 /* Copy the data */ |
|
298 *_retval = (char*)malloc(content->len+1); |
|
299 memcpy(*_retval, content->data, content->len); |
|
300 (*_retval)[content->len] = 0; |
|
301 |
|
302 done: |
|
303 if (der) free(der); |
|
304 if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg); |
|
305 |
|
306 return rv; |
|
307 } |
|
308 |
|
309 nsresult nsCMSSecureMessage:: |
|
310 encode(const unsigned char *data, int32_t dataLen, char **_retval) |
|
311 { |
|
312 nsresult rv = NS_OK; |
|
313 |
|
314 *_retval = PL_Base64Encode((const char *)data, dataLen, nullptr); |
|
315 if (!*_retval) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } |
|
316 |
|
317 loser: |
|
318 return rv; |
|
319 } |
|
320 |
|
321 nsresult nsCMSSecureMessage:: |
|
322 decode(const char *data, unsigned char **result, int32_t * _retval) |
|
323 { |
|
324 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::decode\n")); |
|
325 nsresult rv = NS_OK; |
|
326 uint32_t len = strlen(data); |
|
327 int adjust = 0; |
|
328 |
|
329 /* Compute length adjustment */ |
|
330 if (data[len-1] == '=') { |
|
331 adjust++; |
|
332 if (data[len-2] == '=') adjust++; |
|
333 } |
|
334 |
|
335 *result = (unsigned char *)PL_Base64Decode(data, len, nullptr); |
|
336 if (!*result) { |
|
337 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::decode - error decoding base64\n")); |
|
338 rv = NS_ERROR_ILLEGAL_VALUE; |
|
339 goto loser; |
|
340 } |
|
341 |
|
342 *_retval = (len*3)/4 - adjust; |
|
343 |
|
344 loser: |
|
345 return rv; |
|
346 } |