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     rv = SECSuccess;
143 
144     certs = contentInfo.content.signedData->certificates;
145     if (certs) {
146         count = 0;
147 
148         while (*certs) {
149             count++;
150             certs++;
151         }
152         rv = (*f)(arg, contentInfo.content.signedData->certificates, count);
153     }
154 
155 done:
156     if (arena) {
157         PORT_FreeArena(arena, PR_FALSE);
158     }
159 
160     return rv;
161 }
162 
163 const SEC_ASN1Template SEC_CertSequenceTemplate[] = {
164     { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) }
165 };
166 
167 static SECStatus
SEC_ReadCertSequence(SECItem * certsItem,CERTImportCertificateFunc f,void * arg)168 SEC_ReadCertSequence(SECItem *certsItem, CERTImportCertificateFunc f, void *arg)
169 {
170     SECStatus rv = SECFailure;
171     SECItem **certs;
172     int count;
173     SECItem **rawCerts = NULL;
174     PLArenaPool *arena;
175     ContentInfo contentInfo;
176 
177     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
178     if (arena == NULL) {
179         return rv;
180     }
181 
182     PORT_Memset(&contentInfo, 0, sizeof(contentInfo));
183     if (SEC_ASN1DecodeItem(arena, &contentInfo, ContentInfoTemplate,
184                            certsItem) != SECSuccess) {
185         goto done;
186     }
187 
188     if (GetContentTypeTag(&contentInfo) != SEC_OID_NS_TYPE_CERT_SEQUENCE) {
189         goto done;
190     }
191 
192     if (SEC_QuickDERDecodeItem(arena, &rawCerts, SEC_CertSequenceTemplate,
193                                contentInfo.content.data) != SECSuccess) {
194         goto done;
195     }
196 
197     rv = SECSuccess;
198 
199     certs = rawCerts;
200     if (certs) {
201         count = 0;
202 
203         while (*certs) {
204             count++;
205             certs++;
206         }
207         rv = (*f)(arg, rawCerts, count);
208     }
209 
210 done:
211     if (arena) {
212         PORT_FreeArena(arena, PR_FALSE);
213     }
214 
215     return rv;
216 }
217 
218 CERTCertificate *
CERT_ConvertAndDecodeCertificate(char * certstr)219 CERT_ConvertAndDecodeCertificate(char *certstr)
220 {
221     CERTCertificate *cert;
222     SECStatus rv;
223     SECItem der;
224 
225     rv = ATOB_ConvertAsciiToItem(&der, certstr);
226     if (rv != SECSuccess)
227         return NULL;
228 
229     cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
230                                    &der, NULL, PR_FALSE, PR_TRUE);
231 
232     PORT_Free(der.data);
233     return cert;
234 }
235 
236 static const char NS_CERT_HEADER[] = "-----BEGIN CERTIFICATE-----";
237 static const char NS_CERT_TRAILER[] = "-----END CERTIFICATE-----";
238 #define NS_CERT_HEADER_LEN ((sizeof NS_CERT_HEADER) - 1)
239 #define NS_CERT_TRAILER_LEN ((sizeof NS_CERT_TRAILER) - 1)
240 
241 /*
242  * read an old style ascii or binary certificate chain
243  */
244 SECStatus
CERT_DecodeCertPackage(char * certbuf,int certlen,CERTImportCertificateFunc f,void * arg)245 CERT_DecodeCertPackage(char *certbuf,
246                        int certlen,
247                        CERTImportCertificateFunc f,
248                        void *arg)
249 {
250     unsigned char *cp;
251     unsigned char *bincert = NULL;
252     char *ascCert = NULL;
253     SECStatus rv;
254 
255     if (certbuf == NULL) {
256         PORT_SetError(SEC_ERROR_INVALID_ARGS);
257         return (SECFailure);
258     }
259     /*
260      * Make sure certlen is long enough to handle the longest possible
261      * reference in the code below:
262      * 0x30 0x84 l1 l2 l3 l4  +
263      *                       tag 9 o1 o2 o3 o4 o5 o6 o7 o8 o9
264      * where 9 is the longest length of the expected oids we are testing.
265      *   6 + 11 = 17. 17 bytes is clearly too small to code any kind of
266      *  certificate (a 128 bit ECC certificate contains at least an 8 byte
267      * key and a 16 byte signature, plus coding overhead). Typically a cert
268      * is much larger. So it's safe to require certlen to be at least 17
269      * bytes.
270      */
271     if (certlen < 17) {
272         PORT_SetError(SEC_ERROR_INPUT_LEN);
273         return (SECFailure);
274     }
275 
276     cp = (unsigned char *)certbuf;
277 
278     /* is a DER encoded certificate of some type? */
279     if ((*cp & 0x1f) == SEC_ASN1_SEQUENCE) {
280         SECItem certitem;
281         SECItem *pcertitem = &certitem;
282         PRUint64 seqLen, seqLenLen;
283 
284         cp++;
285 
286         if (*cp & 0x80) {
287             /* Multibyte length */
288             seqLenLen = cp[0] & 0x7f;
289 
290             switch (seqLenLen) {
291                 case 4:
292                     seqLen = ((unsigned long)cp[1] << 24) |
293                              ((unsigned long)cp[2] << 16) | (cp[3] << 8) | cp[4];
294                     break;
295                 case 3:
296                     seqLen = ((unsigned long)cp[1] << 16) | (cp[2] << 8) | cp[3];
297                     break;
298                 case 2:
299                     seqLen = (cp[1] << 8) | cp[2];
300                     break;
301                 case 1:
302                     seqLen = cp[1];
303                     break;
304                 case 0:
305                     /* indefinite length */
306                     seqLen = 0;
307                     break;
308                 default:
309                     goto notder;
310             }
311             cp += (seqLenLen + 1);
312 
313         } else {
314             seqLenLen = 0;
315             seqLen = *cp;
316             cp++;
317         }
318 
319         /* check entire length if definite length */
320         if (seqLen || seqLenLen) {
321             if (certlen != (seqLen + seqLenLen + 2L)) {
322                 if (certlen > (seqLen + seqLenLen + 2L))
323                     PORT_SetError(SEC_ERROR_EXTRA_INPUT);
324                 else
325                     PORT_SetError(SEC_ERROR_INPUT_LEN);
326                 goto notder;
327             }
328         }
329 
330         /* check the type oid */
331         if (cp[0] == SEC_ASN1_OBJECT_ID) {
332             SECOidData *oiddata;
333             SECItem oiditem;
334             /* XXX - assume DER encoding of OID len!! */
335             oiditem.len = cp[1];
336             /* if we add an oid below that is longer than 9 bytes, then we
337              * need to change the certlen check at the top of the function
338              * to prevent a buffer overflow
339              */
340             if (oiditem.len > 9) {
341                 PORT_SetError(SEC_ERROR_UNRECOGNIZED_OID);
342                 return (SECFailure);
343             }
344             oiditem.data = (unsigned char *)&cp[2];
345             oiddata = SECOID_FindOID(&oiditem);
346             if (oiddata == NULL) {
347                 return (SECFailure);
348             }
349 
350             certitem.data = (unsigned char *)certbuf;
351             certitem.len = certlen;
352 
353             switch (oiddata->offset) {
354                 case SEC_OID_PKCS7_SIGNED_DATA:
355                     /* oid: 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 */
356                     return (SEC_ReadPKCS7Certs(&certitem, f, arg));
357                     break;
358                 case SEC_OID_NS_TYPE_CERT_SEQUENCE:
359                     /* oid: 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x02, 0x05 */
360                     return (SEC_ReadCertSequence(&certitem, f, arg));
361                     break;
362                 default:
363                     break;
364             }
365 
366         } else {
367             /* it had better be a certificate by now!! */
368             certitem.data = (unsigned char *)certbuf;
369             certitem.len = certlen;
370 
371             rv = (*f)(arg, &pcertitem, 1);
372             return (rv);
373         }
374     }
375 
376 /* now look for a netscape base64 ascii encoded cert */
377 notder : {
378     unsigned char *certbegin = NULL;
379     unsigned char *certend = NULL;
380     char *pc;
381     int cl;
382 
383     /* Convert the ASCII data into a nul-terminated string */
384     ascCert = (char *)PORT_Alloc(certlen + 1);
385     if (!ascCert) {
386         rv = SECFailure;
387         goto loser;
388     }
389 
390     PORT_Memcpy(ascCert, certbuf, certlen);
391     ascCert[certlen] = '\0';
392 
393     pc = PORT_Strchr(ascCert, '\n'); /* find an EOL */
394     if (!pc) {                       /* maybe this is a MAC file */
395         pc = ascCert;
396         while (*pc && NULL != (pc = PORT_Strchr(pc, '\r'))) {
397             *pc++ = '\n';
398         }
399     }
400 
401     cp = (unsigned char *)ascCert;
402     cl = certlen;
403 
404     /* find the beginning marker */
405     while (cl > NS_CERT_HEADER_LEN) {
406         int found = 0;
407         if (!PORT_Strncasecmp((char *)cp, NS_CERT_HEADER,
408                               NS_CERT_HEADER_LEN)) {
409             cl -= NS_CERT_HEADER_LEN;
410             cp += NS_CERT_HEADER_LEN;
411             found = 1;
412         }
413 
414         /* skip to next eol */
415         while (cl && (*cp != '\n')) {
416             cp++;
417             cl--;
418         }
419 
420         /* skip all blank lines */
421         while (cl && (*cp == '\n' || *cp == '\r')) {
422             cp++;
423             cl--;
424         }
425         if (cl && found) {
426             certbegin = cp;
427             break;
428         }
429     }
430 
431     if (certbegin) {
432         /* find the ending marker */
433         while (cl >= NS_CERT_TRAILER_LEN) {
434             if (!PORT_Strncasecmp((char *)cp, NS_CERT_TRAILER,
435                                   NS_CERT_TRAILER_LEN)) {
436                 certend = cp;
437                 break;
438             }
439 
440             /* skip to next eol */
441             while (cl && (*cp != '\n')) {
442                 cp++;
443                 cl--;
444             }
445 
446             /* skip all blank lines */
447             while (cl && (*cp == '\n' || *cp == '\r')) {
448                 cp++;
449                 cl--;
450             }
451         }
452     }
453 
454     if (certbegin && certend) {
455         unsigned int binLen;
456 
457         *certend = 0;
458         /* convert to binary */
459         bincert = ATOB_AsciiToData((char *)certbegin, &binLen);
460         if (!bincert) {
461             rv = SECFailure;
462             goto loser;
463         }
464 
465         /* now recurse to decode the binary */
466         rv = CERT_DecodeCertPackage((char *)bincert, binLen, f, arg);
467 
468     } else {
469         PORT_SetError(SEC_ERROR_BAD_DER);
470         rv = SECFailure;
471     }
472 }
473 
474 loser:
475 
476     if (bincert) {
477         PORT_Free(bincert);
478     }
479 
480     if (ascCert) {
481         PORT_Free(ascCert);
482     }
483 
484     return (rv);
485 }
486 
487 typedef struct {
488     PLArenaPool *arena;
489     SECItem cert;
490 } collect_args;
491 
492 static SECStatus
collect_certs(void * arg,SECItem ** certs,int numcerts)493 collect_certs(void *arg, SECItem **certs, int numcerts)
494 {
495     SECStatus rv;
496     collect_args *collectArgs;
497 
498     collectArgs = (collect_args *)arg;
499 
500     rv = SECITEM_CopyItem(collectArgs->arena, &collectArgs->cert, *certs);
501 
502     return (rv);
503 }
504 
505 /*
506  * read an old style ascii or binary certificate
507  */
508 CERTCertificate *
CERT_DecodeCertFromPackage(char * certbuf,int certlen)509 CERT_DecodeCertFromPackage(char *certbuf, int certlen)
510 {
511     collect_args collectArgs;
512     SECStatus rv;
513     CERTCertificate *cert = NULL;
514 
515     collectArgs.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
516 
517     rv = CERT_DecodeCertPackage(certbuf, certlen, collect_certs,
518                                 (void *)&collectArgs);
519     if (rv == SECSuccess) {
520         cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
521                                        &collectArgs.cert, NULL,
522                                        PR_FALSE, PR_TRUE);
523     }
524 
525     PORT_FreeArena(collectArgs.arena, PR_FALSE);
526 
527     return (cert);
528 }
529