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