1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /*
6  * CMS message methods.
7  */
8 
9 #include "cmslocal.h"
10 
11 #include "cert.h"
12 #include "secasn1.h"
13 #include "secitem.h"
14 #include "secoid.h"
15 #include "pk11func.h"
16 #include "secerr.h"
17 
18 /*
19  * NSS_CMSMessage_Create - create a CMS message object
20  *
21  * "poolp" - arena to allocate memory from, or NULL if new arena should be created
22  */
23 NSSCMSMessage *
NSS_CMSMessage_Create(PLArenaPool * poolp)24 NSS_CMSMessage_Create(PLArenaPool *poolp)
25 {
26     void *mark = NULL;
27     NSSCMSMessage *cmsg;
28     PRBool poolp_is_ours = PR_FALSE;
29 
30     if (poolp == NULL) {
31         poolp = PORT_NewArena(1024); /* XXX what is right value? */
32         if (poolp == NULL) {
33             return NULL;
34         }
35         poolp_is_ours = PR_TRUE;
36     }
37 
38     if (!poolp_is_ours)
39         mark = PORT_ArenaMark(poolp);
40 
41     cmsg = (NSSCMSMessage *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSMessage));
42     if (cmsg == NULL ||
43         NSS_CMSContentInfo_Private_Init(&(cmsg->contentInfo)) != SECSuccess) {
44         if (!poolp_is_ours) {
45             if (mark) {
46                 PORT_ArenaRelease(poolp, mark);
47             }
48         } else {
49             PORT_FreeArena(poolp, PR_FALSE);
50         }
51         return NULL;
52     }
53 
54     cmsg->poolp = poolp;
55     cmsg->poolp_is_ours = poolp_is_ours;
56     cmsg->refCount = 1;
57 
58     if (mark) {
59         PORT_ArenaUnmark(poolp, mark);
60     }
61 
62     return cmsg;
63 }
64 
65 /*
66  * NSS_CMSMessage_SetEncodingParams - set up a CMS message object for encoding or decoding
67  *
68  * "cmsg" - message object
69  * "pwfn", pwfn_arg" - callback function for getting token password
70  * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
71  * "detached_digestalgs", "detached_digests" - digests from detached content
72  */
73 void
NSS_CMSMessage_SetEncodingParams(NSSCMSMessage * cmsg,PK11PasswordFunc pwfn,void * pwfn_arg,NSSCMSGetDecryptKeyCallback decrypt_key_cb,void * decrypt_key_cb_arg,SECAlgorithmID ** detached_digestalgs,SECItem ** detached_digests)74 NSS_CMSMessage_SetEncodingParams(NSSCMSMessage *cmsg,
75                                  PK11PasswordFunc pwfn, void *pwfn_arg,
76                                  NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
77                                  SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
78 {
79     if (cmsg == NULL) {
80         return;
81     }
82     if (pwfn) {
83         PK11_SetPasswordFunc(pwfn);
84     }
85 
86     cmsg->pwfn_arg = pwfn_arg;
87     cmsg->decrypt_key_cb = decrypt_key_cb;
88     cmsg->decrypt_key_cb_arg = decrypt_key_cb_arg;
89     cmsg->detached_digestalgs = detached_digestalgs;
90     cmsg->detached_digests = detached_digests;
91 }
92 
93 /*
94  * NSS_CMSMessage_Destroy - destroy a CMS message and all of its sub-pieces.
95  */
96 void
NSS_CMSMessage_Destroy(NSSCMSMessage * cmsg)97 NSS_CMSMessage_Destroy(NSSCMSMessage *cmsg)
98 {
99     if (cmsg == NULL)
100         return;
101 
102     PORT_Assert(cmsg->refCount > 0);
103     if (cmsg->refCount <= 0) { /* oops */
104         return;
105     }
106 
107     cmsg->refCount--; /* thread safety? */
108     if (cmsg->refCount > 0) {
109         return;
110     }
111 
112     NSS_CMSContentInfo_Destroy(&(cmsg->contentInfo));
113 
114     /* if poolp is not NULL, cmsg is the owner of its arena */
115     if (cmsg->poolp_is_ours) {
116         PORT_FreeArena(cmsg->poolp, PR_FALSE); /* XXX clear it? */
117     }
118 }
119 
120 /*
121  * NSS_CMSMessage_Copy - return a copy of the given message.
122  *
123  * The copy may be virtual or may be real -- either way, the result needs
124  * to be passed to NSS_CMSMessage_Destroy later (as does the original).
125  */
126 NSSCMSMessage *
NSS_CMSMessage_Copy(NSSCMSMessage * cmsg)127 NSS_CMSMessage_Copy(NSSCMSMessage *cmsg)
128 {
129     if (cmsg == NULL) {
130         return NULL;
131     }
132 
133     PORT_Assert(cmsg->refCount > 0);
134 
135     cmsg->refCount++; /* XXX chrisk thread safety? */
136     return cmsg;
137 }
138 
139 /*
140  * NSS_CMSMessage_GetArena - return a pointer to the message's arena pool
141  */
142 PLArenaPool *
NSS_CMSMessage_GetArena(NSSCMSMessage * cmsg)143 NSS_CMSMessage_GetArena(NSSCMSMessage *cmsg)
144 {
145     if (cmsg == NULL) {
146         return NULL;
147     }
148 
149     return cmsg->poolp;
150 }
151 
152 /*
153  * NSS_CMSMessage_GetContentInfo - return a pointer to the top level contentInfo
154  */
155 NSSCMSContentInfo *
NSS_CMSMessage_GetContentInfo(NSSCMSMessage * cmsg)156 NSS_CMSMessage_GetContentInfo(NSSCMSMessage *cmsg)
157 {
158     if (cmsg == NULL) {
159         return NULL;
160     }
161 
162     return &(cmsg->contentInfo);
163 }
164 
165 /*
166  * Return a pointer to the actual content.
167  * In the case of those types which are encrypted, this returns the *plain* content.
168  * In case of nested contentInfos, this descends and retrieves the innermost content.
169  */
170 SECItem *
NSS_CMSMessage_GetContent(NSSCMSMessage * cmsg)171 NSS_CMSMessage_GetContent(NSSCMSMessage *cmsg)
172 {
173     if (cmsg == NULL) {
174         return NULL;
175     }
176 
177     /* this is a shortcut */
178     NSSCMSContentInfo *cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
179     SECItem *pItem = NSS_CMSContentInfo_GetInnerContent(cinfo);
180     return pItem;
181 }
182 
183 /*
184  * NSS_CMSMessage_ContentLevelCount - count number of levels of CMS content objects in this message
185  *
186  * CMS data content objects do not count.
187  */
188 int
NSS_CMSMessage_ContentLevelCount(NSSCMSMessage * cmsg)189 NSS_CMSMessage_ContentLevelCount(NSSCMSMessage *cmsg)
190 {
191     int count = 0;
192     NSSCMSContentInfo *cinfo;
193 
194     if (cmsg == NULL) {
195         return 0;
196     }
197 
198     /* walk down the chain of contentinfos */
199     for (cinfo = &(cmsg->contentInfo); cinfo != NULL;) {
200         count++;
201         cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo);
202     }
203     return count;
204 }
205 
206 /*
207  * NSS_CMSMessage_ContentLevel - find content level #n
208  *
209  * CMS data content objects do not count.
210  */
211 NSSCMSContentInfo *
NSS_CMSMessage_ContentLevel(NSSCMSMessage * cmsg,int n)212 NSS_CMSMessage_ContentLevel(NSSCMSMessage *cmsg, int n)
213 {
214     int count = 0;
215     NSSCMSContentInfo *cinfo;
216 
217     if (cmsg == NULL) {
218         return NULL;
219     }
220 
221     /* walk down the chain of contentinfos */
222     for (cinfo = &(cmsg->contentInfo); cinfo != NULL && count < n;
223          cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
224         count++;
225     }
226 
227     return cinfo;
228 }
229 
230 /*
231  * NSS_CMSMessage_ContainsCertsOrCrls - see if message contains certs along the way
232  */
233 PRBool
NSS_CMSMessage_ContainsCertsOrCrls(NSSCMSMessage * cmsg)234 NSS_CMSMessage_ContainsCertsOrCrls(NSSCMSMessage *cmsg)
235 {
236     NSSCMSContentInfo *cinfo;
237 
238     if (cmsg == NULL) {
239         return PR_FALSE;
240     }
241 
242     /* descend into CMS message */
243     for (cinfo = &(cmsg->contentInfo); cinfo != NULL;
244          cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
245         if (!NSS_CMSType_IsData(NSS_CMSContentInfo_GetContentTypeTag(cinfo)))
246             continue; /* next level */
247 
248         if (NSS_CMSSignedData_ContainsCertsOrCrls(cinfo->content.signedData))
249             return PR_TRUE;
250         /* callback here for generic wrappers? */
251     }
252     return PR_FALSE;
253 }
254 
255 /*
256  * NSS_CMSMessage_IsEncrypted - see if message contains a encrypted submessage
257  */
258 PRBool
NSS_CMSMessage_IsEncrypted(NSSCMSMessage * cmsg)259 NSS_CMSMessage_IsEncrypted(NSSCMSMessage *cmsg)
260 {
261     NSSCMSContentInfo *cinfo;
262 
263     if (cmsg == NULL) {
264         return PR_FALSE;
265     }
266 
267     /* walk down the chain of contentinfos */
268     for (cinfo = &(cmsg->contentInfo); cinfo != NULL;
269          cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
270         switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
271             case SEC_OID_PKCS7_ENVELOPED_DATA:
272             case SEC_OID_PKCS7_ENCRYPTED_DATA:
273                 return PR_TRUE;
274             default:
275                 /* callback here for generic wrappers? */
276                 break;
277         }
278     }
279     return PR_FALSE;
280 }
281 
282 /*
283  * NSS_CMSMessage_IsSigned - see if message contains a signed submessage
284  *
285  * If the CMS message has a SignedData with a signature (not just a SignedData)
286  * return true; false otherwise.  This can/should be called before calling
287  * VerifySignature, which will always indicate failure if no signature is
288  * present, but that does not mean there even was a signature!
289  * Note that the content itself can be empty (detached content was sent
290  * another way); it is the presence of the signature that matters.
291  */
292 PRBool
NSS_CMSMessage_IsSigned(NSSCMSMessage * cmsg)293 NSS_CMSMessage_IsSigned(NSSCMSMessage *cmsg)
294 {
295     NSSCMSContentInfo *cinfo;
296 
297     if (cmsg == NULL) {
298         return PR_FALSE;
299     }
300 
301     /* walk down the chain of contentinfos */
302     for (cinfo = &(cmsg->contentInfo); cinfo != NULL;
303          cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
304         switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
305             case SEC_OID_PKCS7_SIGNED_DATA:
306                 if (cinfo->content.signedData == NULL) {
307                     return PR_FALSE;
308                 }
309                 if (!NSS_CMSArray_IsEmpty((void **)cinfo->content.signedData->signerInfos)) {
310                     return PR_TRUE;
311                 }
312                 break;
313             default:
314                 /* callback here for generic wrappers? */
315                 break;
316         }
317     }
318     return PR_FALSE;
319 }
320 
321 /*
322  * NSS_CMSMessage_IsContentEmpty - see if content is empty
323  *
324  * returns PR_TRUE is innermost content length is < minLen
325  * XXX need the encrypted content length (why?)
326  */
327 PRBool
NSS_CMSMessage_IsContentEmpty(NSSCMSMessage * cmsg,unsigned int minLen)328 NSS_CMSMessage_IsContentEmpty(NSSCMSMessage *cmsg, unsigned int minLen)
329 {
330     SECItem *item = NULL;
331 
332     if (cmsg == NULL) {
333         return PR_TRUE;
334     }
335 
336     item = NSS_CMSContentInfo_GetContent(NSS_CMSMessage_GetContentInfo(cmsg));
337 
338     if (!item) {
339         return PR_TRUE;
340     } else if (item->len <= minLen) {
341         return PR_TRUE;
342     }
343 
344     return PR_FALSE;
345 }
346