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