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 #include "cert.h"
6 #include "base64.h"
7 #include "secitem.h"
8 #include "secder.h"
9 #include "secasn1.h"
10 #include "secoid.h"
11 #include "secerr.h"
12 
13 SEC_ASN1_MKSUB(SEC_AnyTemplate)
14 SEC_ASN1_MKSUB(SEC_SetOfAnyTemplate)
15 
16 typedef struct ContentInfoStr ContentInfo;
17 typedef struct DegenerateSignedDataStr DegenerateSignedData;
18 
19 struct ContentInfoStr {
20     SECOidTag contentTypeTag; /* local; not part of encoding */
21     SECItem contentType;
22     union {
23         SECItem *data;
24         DegenerateSignedData *signedData;
25     } content;
26 };
27 
28 struct DegenerateSignedDataStr {
29     SECItem version;
30     SECItem **digestAlgorithms;
31     ContentInfo contentInfo;
32     SECItem **certificates;
33     SECItem **crls;
34     SECItem **signerInfos;
35 };
36 
37 static const SEC_ASN1Template *
38 choose_content_template(void *src_or_dest, PRBool encoding);
39 
40 static const SEC_ASN1TemplateChooserPtr template_chooser = choose_content_template;
41 
42 static const SEC_ASN1Template ContentInfoTemplate[] = {
43     { SEC_ASN1_SEQUENCE,
44       0, NULL, sizeof(ContentInfo) },
45     { SEC_ASN1_OBJECT_ID,
46       offsetof(ContentInfo, contentType) },
47     { SEC_ASN1_OPTIONAL | SEC_ASN1_DYNAMIC |
48           SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
49       offsetof(ContentInfo, content),
50       &template_chooser },
51     { 0 }
52 };
53 
54 static const SEC_ASN1Template DegenerateSignedDataTemplate[] = {
55     { SEC_ASN1_SEQUENCE,
56       0, NULL, sizeof(DegenerateSignedData) },
57     { SEC_ASN1_INTEGER,
58       offsetof(DegenerateSignedData, version) },
59     { SEC_ASN1_SET_OF | SEC_ASN1_XTRN,
60       offsetof(DegenerateSignedData, digestAlgorithms),
61       SEC_ASN1_SUB(SEC_AnyTemplate) },
62     { SEC_ASN1_INLINE,
63       offsetof(DegenerateSignedData, contentInfo),
64       ContentInfoTemplate },
65     { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
66           SEC_ASN1_XTRN | 0,
67       offsetof(DegenerateSignedData, certificates),
68       SEC_ASN1_SUB(SEC_SetOfAnyTemplate) },
69     { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
70           SEC_ASN1_XTRN | 1,
71       offsetof(DegenerateSignedData, crls),
72       SEC_ASN1_SUB(SEC_SetOfAnyTemplate) },
73     { SEC_ASN1_SET_OF | SEC_ASN1_XTRN,
74       offsetof(DegenerateSignedData, signerInfos),
75       SEC_ASN1_SUB(SEC_AnyTemplate) },
76     { 0 }
77 };
78 
79 static const SEC_ASN1Template PointerToDegenerateSignedDataTemplate[] = {
80     { SEC_ASN1_POINTER, 0, DegenerateSignedDataTemplate }
81 };
82 
83 static SECOidTag
GetContentTypeTag(ContentInfo * cinfo)84 GetContentTypeTag(ContentInfo *cinfo)
85 {
86     if (cinfo->contentTypeTag == SEC_OID_UNKNOWN)
87         cinfo->contentTypeTag = SECOID_FindOIDTag(&cinfo->contentType);
88     return cinfo->contentTypeTag;
89 }
90 
91 static const SEC_ASN1Template *
choose_content_template(void * src_or_dest,PRBool encoding)92 choose_content_template(void *src_or_dest, PRBool encoding)
93 {
94     const SEC_ASN1Template *theTemplate;
95     ContentInfo *cinfo;
96     SECOidTag kind;
97 
98     PORT_Assert(src_or_dest != NULL);
99     if (src_or_dest == NULL)
100         return NULL;
101 
102     cinfo = (ContentInfo *)src_or_dest;
103     kind = GetContentTypeTag(cinfo);
104     switch (kind) {
105         default:
106             theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate);
107             break;
108         case SEC_OID_PKCS7_DATA:
109             theTemplate = SEC_ASN1_GET(SEC_PointerToOctetStringTemplate);
110             break;
111         case SEC_OID_PKCS7_SIGNED_DATA:
112             theTemplate = PointerToDegenerateSignedDataTemplate;
113             break;
114     }
115     return theTemplate;
116 }
117 
118 static SECStatus
SEC_ReadPKCS7Certs(SECItem * pkcs7Item,CERTImportCertificateFunc f,void * arg)119 SEC_ReadPKCS7Certs(SECItem *pkcs7Item, CERTImportCertificateFunc f, void *arg)
120 {
121     ContentInfo contentInfo;
122     SECStatus rv = SECFailure;
123     SECItem **certs;
124     int count;
125     PLArenaPool *arena;
126 
127     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
128     if (arena == NULL) {
129         return rv;
130     }
131 
132     PORT_Memset(&contentInfo, 0, sizeof(contentInfo));
133     if (SEC_ASN1DecodeItem(arena, &contentInfo, ContentInfoTemplate,
134                            pkcs7Item) != SECSuccess) {
135         goto done;
136     }
137 
138     if (GetContentTypeTag(&contentInfo) != SEC_OID_PKCS7_SIGNED_DATA) {
139         goto done;
140     }
141 
142     if (contentInfo.content.signedData == NULL) {
143         PORT_SetError(SEC_ERROR_BAD_DER);
144         goto done;
145     }
146 
147     rv = SECSuccess;
148 
149     certs = contentInfo.content.signedData->certificates;
150     if (certs) {
151         count = 0;
152 
153         while (*certs) {
154             count++;
155             certs++;
156         }
157         rv = (*f)(arg, contentInfo.content.signedData->certificates, count);
158     }
159 
160 done:
161     if (arena) {
162         PORT_FreeArena(arena, PR_FALSE);
163     }
164 
165     return rv;
166 }
167 
168 const SEC_ASN1Template SEC_CertSequenceTemplate[] = {
169     { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) }
170 };
171 
172 static SECStatus
SEC_ReadCertSequence(SECItem * certsItem,CERTImportCertificateFunc f,void * arg)173 SEC_ReadCertSequence(SECItem *certsItem, CERTImportCertificateFunc f, void *arg)
174 {
175     SECStatus rv = SECFailure;
176     SECItem **certs;
177     int count;
178     SECItem **rawCerts = NULL;
179     PLArenaPool *arena;
180     ContentInfo contentInfo;
181 
182     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
183     if (arena == NULL) {
184         return rv;
185     }
186 
187     PORT_Memset(&contentInfo, 0, sizeof(contentInfo));
188     if (SEC_ASN1DecodeItem(arena, &contentInfo, ContentInfoTemplate,
189                            certsItem) != SECSuccess) {
190         goto done;
191     }
192 
193     if (GetContentTypeTag(&contentInfo) != SEC_OID_NS_TYPE_CERT_SEQUENCE) {
194         goto done;
195     }
196 
197     if (SEC_QuickDERDecodeItem(arena, &rawCerts, SEC_CertSequenceTemplate,
198                                contentInfo.content.data) != SECSuccess) {
199         goto done;
200     }
201 
202     rv = SECSuccess;
203 
204     certs = rawCerts;
205     if (certs) {
206         count = 0;
207 
208         while (*certs) {
209             count++;
210             certs++;
211         }
212         rv = (*f)(arg, rawCerts, count);
213     }
214 
215 done:
216     if (arena) {
217         PORT_FreeArena(arena, PR_FALSE);
218     }
219 
220     return rv;
221 }
222 
223 CERTCertificate *
CERT_ConvertAndDecodeCertificate(char * certstr)224 CERT_ConvertAndDecodeCertificate(char *certstr)
225 {
226     CERTCertificate *cert;
227     SECStatus rv;
228     SECItem der;
229 
230     rv = ATOB_ConvertAsciiToItem(&der, certstr);
231     if (rv != SECSuccess)
232         return NULL;
233 
234     cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
235                                    &der, NULL, PR_FALSE, PR_TRUE);
236 
237     PORT_Free(der.data);
238     return cert;
239 }
240 
241 static const char NS_CERT_HEADER[] = "-----BEGIN CERTIFICATE-----";
242 static const char NS_CERT_TRAILER[] = "-----END CERTIFICATE-----";
243 #define NS_CERT_HEADER_LEN ((sizeof NS_CERT_HEADER) - 1)
244 #define NS_CERT_TRAILER_LEN ((sizeof NS_CERT_TRAILER) - 1)
245 
246 /*
247  * read an old style ascii or binary certificate chain
248  */
249 SECStatus
CERT_DecodeCertPackage(char * certbuf,int certlen,CERTImportCertificateFunc f,void * arg)250 CERT_DecodeCertPackage(char *certbuf,
251                        int certlen,
252                        CERTImportCertificateFunc f,
253                        void *arg)
254 {
255     unsigned char *cp;
256     unsigned char *bincert = NULL;
257     char *ascCert = NULL;
258     SECStatus rv;
259 
260     if (certbuf == NULL) {
261         PORT_SetError(SEC_ERROR_INVALID_ARGS);
262         return (SECFailure);
263     }
264     /*
265      * Make sure certlen is long enough to handle the longest possible
266      * reference in the code below:
267      * 0x30 0x84 l1 l2 l3 l4  +
268      *                       tag 9 o1 o2 o3 o4 o5 o6 o7 o8 o9
269      * where 9 is the longest length of the expected oids we are testing.
270      *   6 + 11 = 17. 17 bytes is clearly too small to code any kind of
271      *  certificate (a 128 bit ECC certificate contains at least an 8 byte
272      * key and a 16 byte signature, plus coding overhead). Typically a cert
273      * is much larger. So it's safe to require certlen to be at least 17
274      * bytes.
275      */
276     if (certlen < 17) {
277         PORT_SetError(SEC_ERROR_INPUT_LEN);
278         return (SECFailure);
279     }
280 
281     cp = (unsigned char *)certbuf;
282 
283     /* is a DER encoded certificate of some type? */
284     if ((*cp & 0x1f) == SEC_ASN1_SEQUENCE) {
285         SECItem certitem;
286         SECItem *pcertitem = &certitem;
287         PRUint64 seqLen, seqLenLen;
288 
289         cp++;
290 
291         if (*cp & 0x80) {
292             /* Multibyte length */
293             seqLenLen = cp[0] & 0x7f;
294 
295             switch (seqLenLen) {
296                 case 4:
297                     seqLen = ((unsigned long)cp[1] << 24) |
298                              ((unsigned long)cp[2] << 16) | (cp[3] << 8) | cp[4];
299                     break;
300                 case 3:
301                     seqLen = ((unsigned long)cp[1] << 16) | (cp[2] << 8) | cp[3];
302                     break;
303                 case 2:
304                     seqLen = (cp[1] << 8) | cp[2];
305                     break;
306                 case 1:
307                     seqLen = cp[1];
308                     break;
309                 case 0:
310                     /* indefinite length */
311                     seqLen = 0;
312                     break;
313                 default:
314                     goto notder;
315             }
316             cp += (seqLenLen + 1);
317 
318         } else {
319             seqLenLen = 0;
320             seqLen = *cp;
321             cp++;
322         }
323 
324         /* check entire length if definite length */
325         if (seqLen || seqLenLen) {
326             if (certlen != (seqLen + seqLenLen + 2L)) {
327                 if (certlen > (seqLen + seqLenLen + 2L))
328                     PORT_SetError(SEC_ERROR_EXTRA_INPUT);
329                 else
330                     PORT_SetError(SEC_ERROR_INPUT_LEN);
331                 goto notder;
332             }
333         }
334 
335         /* check the type oid */
336         if (cp[0] == SEC_ASN1_OBJECT_ID) {
337             SECOidData *oiddata;
338             SECItem oiditem;
339             /* XXX - assume DER encoding of OID len!! */
340             oiditem.len = cp[1];
341             /* if we add an oid below that is longer than 9 bytes, then we
342              * need to change the certlen check at the top of the function
343              * to prevent a buffer overflow
344              */
345             if (oiditem.len > 9) {
346                 PORT_SetError(SEC_ERROR_UNRECOGNIZED_OID);
347                 return (SECFailure);
348             }
349             oiditem.data = (unsigned char *)&cp[2];
350             oiddata = SECOID_FindOID(&oiditem);
351             if (oiddata == NULL) {
352                 return (SECFailure);
353             }
354 
355             certitem.data = (unsigned char *)certbuf;
356             certitem.len = certlen;
357 
358             switch (oiddata->offset) {
359                 case SEC_OID_PKCS7_SIGNED_DATA:
360                     /* oid: 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 */
361                     return (SEC_ReadPKCS7Certs(&certitem, f, arg));
362                     break;
363                 case SEC_OID_NS_TYPE_CERT_SEQUENCE:
364                     /* oid: 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x02, 0x05 */
365                     return (SEC_ReadCertSequence(&certitem, f, arg));
366                     break;
367                 default:
368                     break;
369             }
370 
371         } else {
372             /* it had better be a certificate by now!! */
373             certitem.data = (unsigned char *)certbuf;
374             certitem.len = certlen;
375 
376             rv = (*f)(arg, &pcertitem, 1);
377             return (rv);
378         }
379     }
380 
381 /* now look for a netscape base64 ascii encoded cert */
382 notder : {
383     unsigned char *certbegin = NULL;
384     unsigned char *certend = NULL;
385     char *pc;
386     int cl;
387 
388     /* Convert the ASCII data into a nul-terminated string */
389     ascCert = (char *)PORT_Alloc(certlen + 1);
390     if (!ascCert) {
391         rv = SECFailure;
392         goto loser;
393     }
394 
395     PORT_Memcpy(ascCert, certbuf, certlen);
396     ascCert[certlen] = '\0';
397 
398     pc = PORT_Strchr(ascCert, '\n'); /* find an EOL */
399     if (!pc) {                       /* maybe this is a MAC file */
400         pc = ascCert;
401         while (*pc && NULL != (pc = PORT_Strchr(pc, '\r'))) {
402             *pc++ = '\n';
403         }
404     }
405 
406     cp = (unsigned char *)ascCert;
407     cl = certlen;
408 
409     /* find the beginning marker */
410     while (cl > NS_CERT_HEADER_LEN) {
411         int found = 0;
412         if (!PORT_Strncasecmp((char *)cp, NS_CERT_HEADER,
413                               NS_CERT_HEADER_LEN)) {
414             cl -= NS_CERT_HEADER_LEN;
415             cp += NS_CERT_HEADER_LEN;
416             found = 1;
417         }
418 
419         /* skip to next eol */
420         while (cl && (*cp != '\n')) {
421             cp++;
422             cl--;
423         }
424 
425         /* skip all blank lines */
426         while (cl && (*cp == '\n' || *cp == '\r')) {
427             cp++;
428             cl--;
429         }
430         if (cl && found) {
431             certbegin = cp;
432             break;
433         }
434     }
435 
436     if (certbegin) {
437         /* find the ending marker */
438         while (cl >= NS_CERT_TRAILER_LEN) {
439             if (!PORT_Strncasecmp((char *)cp, NS_CERT_TRAILER,
440                                   NS_CERT_TRAILER_LEN)) {
441                 certend = cp;
442                 break;
443             }
444 
445             /* skip to next eol */
446             while (cl && (*cp != '\n')) {
447                 cp++;
448                 cl--;
449             }
450 
451             /* skip all blank lines */
452             while (cl && (*cp == '\n' || *cp == '\r')) {
453                 cp++;
454                 cl--;
455             }
456         }
457     }
458 
459     if (certbegin && certend) {
460         unsigned int binLen;
461 
462         *certend = 0;
463         /* convert to binary */
464         bincert = ATOB_AsciiToData((char *)certbegin, &binLen);
465         if (!bincert) {
466             rv = SECFailure;
467             goto loser;
468         }
469 
470         /* now recurse to decode the binary */
471         rv = CERT_DecodeCertPackage((char *)bincert, binLen, f, arg);
472 
473     } else {
474         PORT_SetError(SEC_ERROR_BAD_DER);
475         rv = SECFailure;
476     }
477 }
478 
479 loser:
480 
481     if (bincert) {
482         PORT_Free(bincert);
483     }
484 
485     if (ascCert) {
486         PORT_Free(ascCert);
487     }
488 
489     return (rv);
490 }
491 
492 typedef struct {
493     PLArenaPool *arena;
494     SECItem cert;
495 } collect_args;
496 
497 static SECStatus
collect_certs(void * arg,SECItem ** certs,int numcerts)498 collect_certs(void *arg, SECItem **certs, int numcerts)
499 {
500     collect_args *collectArgs = (collect_args *)arg;
501     if (!collectArgs || !collectArgs->arena) {
502         PORT_SetError(SEC_ERROR_INVALID_ARGS);
503         return SECFailure;
504     }
505     if (numcerts < 1 || !certs || !*certs) {
506         PORT_SetError(SEC_ERROR_BAD_DER);
507         return SECFailure;
508     }
509     return SECITEM_CopyItem(collectArgs->arena, &collectArgs->cert, *certs);
510 }
511 
512 /*
513  * read an old style ascii or binary certificate
514  */
515 CERTCertificate *
CERT_DecodeCertFromPackage(char * certbuf,int certlen)516 CERT_DecodeCertFromPackage(char *certbuf, int certlen)
517 {
518     collect_args collectArgs;
519     SECStatus rv;
520     CERTCertificate *cert = NULL;
521 
522     collectArgs.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
523 
524     rv = CERT_DecodeCertPackage(certbuf, certlen, collect_certs,
525                                 (void *)&collectArgs);
526     if (rv == SECSuccess) {
527         cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
528                                        &collectArgs.cert, NULL,
529                                        PR_FALSE, PR_TRUE);
530     }
531 
532     PORT_FreeArena(collectArgs.arena, PR_FALSE);
533 
534     return (cert);
535 }
536