]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/ceph_crypto_cms.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / common / ceph_crypto_cms.cc
CommitLineData
7c673cae
FG
1/* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is the Netscape security libraries.
15 *
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Contributor(s):
22 *
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
34 *
35 * ***** END LICENSE BLOCK ***** */
36
37
38#include "common/config.h"
31f18b77 39#include "common/debug.h"
7c673cae
FG
40
41#ifdef USE_NSS
7c673cae
FG
42#include <nspr.h>
43#include <cert.h>
44#include <nss.h>
45#include <smime.h>
7c673cae
FG
46#endif
47
7c673cae
FG
48#define dout_subsys ceph_subsys_crypto
49
7c673cae 50#ifndef USE_NSS
7c673cae
FG
51int ceph_decode_cms(CephContext *cct, bufferlist& cms_bl, bufferlist& decoded_bl)
52{
53 return -ENOTSUP;
54}
55
56#else
57
7c673cae
FG
58static int cms_verbose = 0;
59
60static SECStatus
61DigestFile(PLArenaPool *poolp, SECItem ***digests, SECItem *input,
62 SECAlgorithmID **algids)
63{
64 NSSCMSDigestContext *digcx;
7c673cae
FG
65
66 digcx = NSS_CMSDigestContext_StartMultiple(algids);
67 if (digcx == NULL)
68 return SECFailure;
69
70 NSS_CMSDigestContext_Update(digcx, input->data, input->len);
71
11fdf7f2 72 return NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests);
7c673cae
FG
73}
74
75
76struct optionsStr {
77 SECCertUsage certUsage;
78 CERTCertDBHandle *certHandle;
79};
80
81struct decodeOptionsStr {
82 struct optionsStr *options;
83 SECItem content;
84 int headerLevel;
85 PRBool suppressContent;
86 NSSCMSGetDecryptKeyCallback dkcb;
87 PK11SymKey *bulkkey;
88 PRBool keepCerts;
89};
90
91static NSSCMSMessage *
92decode(CephContext *cct, SECItem *input, const struct decodeOptionsStr *decodeOptions, bufferlist& out)
93{
94 NSSCMSDecoderContext *dcx;
95 SECStatus rv;
96 NSSCMSMessage *cmsg;
97 int nlevels, i;
98 SECItem sitem;
99 bufferptr bp;
100 SECItem *item;
101
102 memset(&sitem, 0, sizeof(sitem));
103
104 PORT_SetError(0);
105 dcx = NSS_CMSDecoder_Start(NULL,
106 NULL, NULL, /* content callback */
107 NULL, NULL, /* password callback */
108 decodeOptions->dkcb, /* decrypt key callback */
109 decodeOptions->bulkkey);
110 if (dcx == NULL) {
111 ldout(cct, 0) << "ERROR: failed to set up message decoder" << dendl;
112 return NULL;
113 }
114 rv = NSS_CMSDecoder_Update(dcx, (char *)input->data, input->len);
115 if (rv != SECSuccess) {
116 ldout(cct, 0) << "ERROR: failed to decode message" << dendl;
117 NSS_CMSDecoder_Cancel(dcx);
118 return NULL;
119 }
120 cmsg = NSS_CMSDecoder_Finish(dcx);
121 if (cmsg == NULL) {
122 ldout(cct, 0) << "ERROR: failed to decode message" << dendl;
123 return NULL;
124 }
125
126 if (decodeOptions->headerLevel >= 0) {
127 ldout(cct, 20) << "SMIME: " << dendl;
128 }
129
130 nlevels = NSS_CMSMessage_ContentLevelCount(cmsg);
131 for (i = 0; i < nlevels; i++) {
132 NSSCMSContentInfo *cinfo;
133 SECOidTag typetag;
134
135 cinfo = NSS_CMSMessage_ContentLevel(cmsg, i);
136 typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
137
138 ldout(cct, 20) << "level=" << decodeOptions->headerLevel << "." << nlevels - i << dendl;
139
140 switch (typetag) {
141 case SEC_OID_PKCS7_SIGNED_DATA:
142 {
143 NSSCMSSignedData *sigd = NULL;
144 SECItem **digests;
145 int nsigners;
146 int j;
147
148 if (decodeOptions->headerLevel >= 0)
149 ldout(cct, 20) << "type=signedData; " << dendl;
150 sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
151 if (sigd == NULL) {
152 ldout(cct, 0) << "ERROR: signedData component missing" << dendl;
153 goto loser;
154 }
155
156 /* if we have a content file, but no digests for this signedData */
157 if (decodeOptions->content.data != NULL &&
158 !NSS_CMSSignedData_HasDigests(sigd)) {
159 PLArenaPool *poolp;
160 SECAlgorithmID **digestalgs;
161
162 /* detached content: grab content file */
163 sitem = decodeOptions->content;
164
165 if ((poolp = PORT_NewArena(1024)) == NULL) {
166 ldout(cct, 0) << "ERROR: Out of memory" << dendl;
167 goto loser;
168 }
169 digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
170 if (DigestFile (poolp, &digests, &sitem, digestalgs)
171 != SECSuccess) {
172 ldout(cct, 0) << "ERROR: problem computing message digest" << dendl;
173 PORT_FreeArena(poolp, PR_FALSE);
174 goto loser;
175 }
176 if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests)
177 != SECSuccess) {
178 ldout(cct, 0) << "ERROR: problem setting message digests" << dendl;
179 PORT_FreeArena(poolp, PR_FALSE);
180 goto loser;
181 }
182 PORT_FreeArena(poolp, PR_FALSE);
183 }
184
185 /* import the certificates */
186 if (NSS_CMSSignedData_ImportCerts(sigd,
187 decodeOptions->options->certHandle,
188 decodeOptions->options->certUsage,
189 decodeOptions->keepCerts)
190 != SECSuccess) {
191 ldout(cct, 0) << "ERROR: cert import failed" << dendl;
192 goto loser;
193 }
194
195 /* find out about signers */
196 nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
197 if (decodeOptions->headerLevel >= 0)
198 ldout(cct, 20) << "nsigners=" << nsigners << dendl;
199 if (nsigners == 0) {
200 /* Might be a cert transport message
201 ** or might be an invalid message, such as a QA test message
202 ** or a message from an attacker.
203 */
204 SECStatus rv;
205 rv = NSS_CMSSignedData_VerifyCertsOnly(sigd,
206 decodeOptions->options->certHandle,
207 decodeOptions->options->certUsage);
208 if (rv != SECSuccess) {
209 ldout(cct, 0) << "ERROR: Verify certs-only failed!" << dendl;
210 goto loser;
211 }
212 return cmsg;
213 }
214
215 /* still no digests? */
216 if (!NSS_CMSSignedData_HasDigests(sigd)) {
217 ldout(cct, 0) << "ERROR: no message digests" << dendl;
218 goto loser;
219 }
220
221 for (j = 0; j < nsigners; j++) {
222 const char * svs;
223 NSSCMSSignerInfo *si;
224 NSSCMSVerificationStatus vs;
225 SECStatus bad;
226
227 si = NSS_CMSSignedData_GetSignerInfo(sigd, j);
228 if (decodeOptions->headerLevel >= 0) {
229 char *signercn;
230 static char empty[] = { "" };
231
232 signercn = NSS_CMSSignerInfo_GetSignerCommonName(si);
233 if (signercn == NULL)
234 signercn = empty;
235 ldout(cct, 20) << "\t\tsigner" << j << ".id=" << signercn << dendl;
236 if (signercn != empty)
237 PORT_Free(signercn);
238 }
239 bad = NSS_CMSSignedData_VerifySignerInfo(sigd, j,
240 decodeOptions->options->certHandle,
241 decodeOptions->options->certUsage);
242 vs = NSS_CMSSignerInfo_GetVerificationStatus(si);
243 svs = NSS_CMSUtil_VerificationStatusToString(vs);
244 if (decodeOptions->headerLevel >= 0) {
245 ldout(cct, 20) << "signer" << j << "status=" << svs << dendl;
246 /* goto loser ? */
247 } else if (bad) {
248 ldout(cct, 0) << "ERROR: signer " << j << " status = " << svs << dendl;
249 goto loser;
250 }
251 }
252 }
253 break;
254 case SEC_OID_PKCS7_ENVELOPED_DATA:
255 {
256 NSSCMSEnvelopedData *envd;
257 if (decodeOptions->headerLevel >= 0)
258 ldout(cct, 20) << "type=envelopedData; " << dendl;
259 envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo);
260 if (envd == NULL) {
261 ldout(cct, 0) << "ERROR: envelopedData component missing" << dendl;
262 goto loser;
263 }
264 }
265 break;
266 case SEC_OID_PKCS7_ENCRYPTED_DATA:
267 {
268 NSSCMSEncryptedData *encd;
269 if (decodeOptions->headerLevel >= 0)
270 ldout(cct, 20) << "type=encryptedData; " << dendl;
271 encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo);
272 if (encd == NULL) {
273 ldout(cct, 0) << "ERROR: encryptedData component missing" << dendl;
274 goto loser;
275 }
276 }
277 break;
278 case SEC_OID_PKCS7_DATA:
279 if (decodeOptions->headerLevel >= 0)
280 ldout(cct, 20) << "type=data; " << dendl;
281 break;
282 default:
283 break;
284 }
285 }
286
287 item = (sitem.data ? &sitem : NSS_CMSMessage_GetContent(cmsg));
288 out.append((char *)item->data, item->len);
289 return cmsg;
290
291loser:
292 if (cmsg)
293 NSS_CMSMessage_Destroy(cmsg);
294 return NULL;
295}
296
297int ceph_decode_cms(CephContext *cct, bufferlist& cms_bl, bufferlist& decoded_bl)
298{
299 NSSCMSMessage *cmsg = NULL;
300 struct decodeOptionsStr decodeOptions = { };
301 struct optionsStr options;
302 SECItem input;
303
304 memset(&options, 0, sizeof(options));
305 memset(&input, 0, sizeof(input));
306
307 input.data = (unsigned char *)cms_bl.c_str();
308 input.len = cms_bl.length();
309
310 decodeOptions.content.data = NULL;
311 decodeOptions.content.len = 0;
312 decodeOptions.suppressContent = PR_FALSE;
313 decodeOptions.headerLevel = -1;
314 decodeOptions.keepCerts = PR_FALSE;
315 options.certUsage = certUsageEmailSigner;
316
317 options.certHandle = CERT_GetDefaultCertDB();
318 if (!options.certHandle) {
319 ldout(cct, 0) << "ERROR: No default cert DB" << dendl;
320 return -EIO;
321 }
322 if (cms_verbose) {
323 fprintf(stderr, "Got default certdb\n");
324 }
325
326 decodeOptions.options = &options;
327
328 int ret = 0;
329
330 cmsg = decode(cct, &input, &decodeOptions, decoded_bl);
331 if (!cmsg) {
332 ldout(cct, 0) << "ERROR: problem decoding" << dendl;
333 ret = -EINVAL;
334 }
335
336 if (cmsg)
337 NSS_CMSMessage_Destroy(cmsg);
338
339 SECITEM_FreeItem(&decodeOptions.content, PR_FALSE);
340
341 return ret;
342}
7c673cae 343#endif