security/nss/lib/pkcs7/certread.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial