security/nss/lib/smime/cmsdecode.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 /*
     6  * CMS decoding.
     7  */
     9 #include "cmslocal.h"
    11 #include "cert.h"
    12 #include "key.h"
    13 #include "secasn1.h"
    14 #include "secitem.h"
    15 #include "secoid.h"
    16 #include "prtime.h"
    17 #include "secerr.h"
    19 struct NSSCMSDecoderContextStr {
    20     SEC_ASN1DecoderContext *	dcx;		/* ASN.1 decoder context */
    21     NSSCMSMessage *		cmsg;		/* backpointer to the root message */
    22     SECOidTag			type;		/* type of message */
    23     NSSCMSContent		content;	/* pointer to message */
    24     NSSCMSDecoderContext *	childp7dcx;	/* inner CMS decoder context */
    25     PRBool			saw_contents;
    26     int				error;
    27     NSSCMSContentCallback	cb;
    28     void *			cb_arg;
    29     PRBool			first_decoded;
    30     PRBool			need_indefinite_finish;
    31 };
    33 struct NSSCMSDecoderDataStr {
    34     SECItem data; 	/* must be first */
    35     unsigned int totalBufferSize;
    36 };
    38 typedef struct NSSCMSDecoderDataStr NSSCMSDecoderData;
    40 static void      nss_cms_decoder_update_filter (void *arg, const char *data, 
    41                  unsigned long len, int depth, SEC_ASN1EncodingPart data_kind);
    42 static SECStatus nss_cms_before_data(NSSCMSDecoderContext *p7dcx);
    43 static SECStatus nss_cms_after_data(NSSCMSDecoderContext *p7dcx);
    44 static SECStatus nss_cms_after_end(NSSCMSDecoderContext *p7dcx);
    45 static void      nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx, 
    46 		 const unsigned char *data, unsigned long len, PRBool final);
    47 static NSSCMSDecoderData *nss_cms_create_decoder_data(PLArenaPool *poolp);
    49 extern const SEC_ASN1Template NSSCMSMessageTemplate[];
    51 static NSSCMSDecoderData *
    52 nss_cms_create_decoder_data(PLArenaPool *poolp)
    53 {
    54     NSSCMSDecoderData *decoderData = NULL;
    56     decoderData = (NSSCMSDecoderData *)
    57 			PORT_ArenaAlloc(poolp,sizeof(NSSCMSDecoderData));
    58     if (!decoderData) {
    59 	return NULL;
    60     }
    61     decoderData->data.data = NULL;
    62     decoderData->data.len = 0;
    63     decoderData->totalBufferSize = 0;
    64     return decoderData;
    65 }
    67 /* 
    68  * nss_cms_decoder_notify -
    69  *  this is the driver of the decoding process. It gets called by the ASN.1
    70  *  decoder before and after an object is decoded.
    71  *  at various points in the decoding process, we intercept to set up and do
    72  *  further processing.
    73  */
    74 static void
    75 nss_cms_decoder_notify(void *arg, PRBool before, void *dest, int depth)
    76 {
    77     NSSCMSDecoderContext *p7dcx;
    78     NSSCMSContentInfo *rootcinfo, *cinfo;
    79     PRBool after = !before;
    81     p7dcx = (NSSCMSDecoderContext *)arg;
    82     rootcinfo = &(p7dcx->cmsg->contentInfo);
    84     /* XXX error handling: need to set p7dcx->error */
    86 #ifdef CMSDEBUG 
    87     fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
    88 #endif
    90     /* so what are we working on right now? */
    91     if (p7dcx->type == SEC_OID_UNKNOWN) {
    92 	/*
    93 	 * right now, we are still decoding the OUTER (root) cinfo
    94 	 * As soon as we know the inner content type, set up the info,
    95 	 * but NO inner decoder or filter. The root decoder handles the first
    96 	 * level children by itself - only for encapsulated contents (which
    97 	 * are encoded as DER inside of an OCTET STRING) we need to set up a
    98 	 * child decoder...
    99 	 */
   100 	if (after && dest == &(rootcinfo->contentType)) {
   101 	    p7dcx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
   102 	    p7dcx->content = rootcinfo->content;	
   103 	    /* is this ready already ? need to alloc? */
   104 	    /* XXX yes we need to alloc -- continue here */
   105 	}
   106     } else if (NSS_CMSType_IsData(p7dcx->type)) {
   107 	/* this can only happen if the outermost cinfo has DATA in it */
   108 	/* otherwise, we handle this type implicitely in the inner decoders */
   110 	if (before && dest == &(rootcinfo->content)) {
   111 	    /* cause the filter to put the data in the right place... 
   112 	    ** We want the ASN.1 decoder to deliver the decoded bytes to us 
   113 	    ** from now on 
   114 	    */
   115 	    SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
   116 					  nss_cms_decoder_update_filter,
   117 					  p7dcx,
   118 					  (PRBool)(p7dcx->cb != NULL));
   119 	} else if (after && dest == &(rootcinfo->content.data)) {
   120 	    /* remove the filter */
   121 	    SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
   122 	}
   123     } else if (NSS_CMSType_IsWrapper(p7dcx->type)) {
   124 	if (!before || dest != &(rootcinfo->content)) {
   126 	    if (p7dcx->content.pointer == NULL)
   127 		p7dcx->content = rootcinfo->content;
   129 	    /* get this data type's inner contentInfo */
   130 	    cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, 
   131 	                                      p7dcx->type);
   133 	    if (before && dest == &(cinfo->contentType)) {
   134 	        /* at this point, set up the &%$&$ back pointer */
   135 	        /* we cannot do it later, because the content itself 
   136 		 * is optional! */
   137 		switch (p7dcx->type) {
   138 		case SEC_OID_PKCS7_SIGNED_DATA:
   139 		    p7dcx->content.signedData->cmsg = p7dcx->cmsg;
   140 		    break;
   141 		case SEC_OID_PKCS7_DIGESTED_DATA:
   142 		    p7dcx->content.digestedData->cmsg = p7dcx->cmsg;
   143 		    break;
   144 		case SEC_OID_PKCS7_ENVELOPED_DATA:
   145 		    p7dcx->content.envelopedData->cmsg = p7dcx->cmsg;
   146 		    break;
   147 		case SEC_OID_PKCS7_ENCRYPTED_DATA:
   148 		    p7dcx->content.encryptedData->cmsg = p7dcx->cmsg;
   149 		    break;
   150 		default:
   151 		    p7dcx->content.genericData->cmsg = p7dcx->cmsg;
   152 		    break;
   153 		}
   154 	    }
   156 	    if (before && dest == &(cinfo->rawContent)) {
   157 		/* we want the ASN.1 decoder to deliver the decoded bytes to us 
   158 		 ** from now on 
   159 		 */
   160 		SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, 
   161 	                                 nss_cms_decoder_update_filter, 
   162 					 p7dcx, (PRBool)(p7dcx->cb != NULL));
   165 		/* we're right in front of the data */
   166 		if (nss_cms_before_data(p7dcx) != SECSuccess) {
   167 		    SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);	
   168 		    /* stop all processing */
   169 		    p7dcx->error = PORT_GetError();
   170 		}
   171 	    }
   172 	    if (after && dest == &(cinfo->rawContent)) {
   173 		/* we're right after of the data */
   174 		if (nss_cms_after_data(p7dcx) != SECSuccess)
   175 		    p7dcx->error = PORT_GetError();
   177 		/* we don't need to see the contents anymore */
   178 		SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
   179 	    }
   180 	}
   181     } else {
   182 	/* unsupported or unknown message type - fail  gracefully */
   183 	p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE;
   184     }
   185 }
   187 /*
   188  * nss_cms_before_data - set up the current encoder to receive data
   189  */
   190 static SECStatus
   191 nss_cms_before_data(NSSCMSDecoderContext *p7dcx)
   192 {
   193     SECStatus rv;
   194     SECOidTag childtype;
   195     PLArenaPool *poolp;
   196     NSSCMSDecoderContext *childp7dcx;
   197     NSSCMSContentInfo *cinfo;
   198     const SEC_ASN1Template *template;
   199     void *mark = NULL;
   200     size_t size;
   202     poolp = p7dcx->cmsg->poolp;
   204     /* call _Decode_BeforeData handlers */
   205     switch (p7dcx->type) {
   206     case SEC_OID_PKCS7_SIGNED_DATA:
   207 	/* we're decoding a signedData, so set up the digests */
   208 	rv = NSS_CMSSignedData_Decode_BeforeData(p7dcx->content.signedData);
   209 	break;
   210     case SEC_OID_PKCS7_DIGESTED_DATA:
   211 	/* we're encoding a digestedData, so set up the digest */
   212 	rv = NSS_CMSDigestedData_Decode_BeforeData(p7dcx->content.digestedData);
   213 	break;
   214     case SEC_OID_PKCS7_ENVELOPED_DATA:
   215 	rv = NSS_CMSEnvelopedData_Decode_BeforeData(
   216 	                             p7dcx->content.envelopedData);
   217 	break;
   218     case SEC_OID_PKCS7_ENCRYPTED_DATA:
   219 	rv = NSS_CMSEncryptedData_Decode_BeforeData(
   220 	                             p7dcx->content.encryptedData);
   221 	break;
   222     default:
   223 	rv = NSS_CMSGenericWrapperData_Decode_BeforeData(p7dcx->type,
   224 				p7dcx->content.genericData);
   225     }
   226     if (rv != SECSuccess)
   227 	return SECFailure;
   229     /* ok, now we have a pointer to cinfo */
   230     /* find out what kind of data is encapsulated */
   232     cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type);
   233     childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
   235     if (NSS_CMSType_IsData(childtype)) {
   236 	cinfo->content.pointer = (void *) nss_cms_create_decoder_data(poolp);
   237 	if (cinfo->content.pointer == NULL)
   238 	    /* set memory error */
   239 	    return SECFailure;
   241 	p7dcx->childp7dcx = NULL;
   242 	return SECSuccess;
   243     }
   245     /* set up inner decoder */
   247     if ((template = NSS_CMSUtil_GetTemplateByTypeTag(childtype)) == NULL)
   248 	return SECFailure;
   250     childp7dcx = PORT_ZNew(NSSCMSDecoderContext);
   251     if (childp7dcx == NULL)
   252 	return SECFailure;
   254     mark = PORT_ArenaMark(poolp);
   256     /* allocate space for the stuff we're creating */
   257     size = NSS_CMSUtil_GetSizeByTypeTag(childtype);
   258     childp7dcx->content.pointer = (void *)PORT_ArenaZAlloc(poolp, size);
   259     if (childp7dcx->content.pointer == NULL)
   260 	goto loser;
   262     /* give the parent a copy of the pointer so that it doesn't get lost */
   263     cinfo->content.pointer = childp7dcx->content.pointer;
   265     /* start the child decoder */
   266     childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, 
   267                                            template);
   268     if (childp7dcx->dcx == NULL)
   269 	goto loser;
   271     /* the new decoder needs to notify, too */
   272     SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify, 
   273                                  childp7dcx);
   275     /* tell the parent decoder that it needs to feed us the content data */
   276     p7dcx->childp7dcx = childp7dcx;
   278     childp7dcx->type = childtype;	/* our type */
   280     childp7dcx->cmsg = p7dcx->cmsg;	/* backpointer to root message */
   282     /* should the child decoder encounter real data, 
   283     ** it must give it to the caller 
   284     */
   285     childp7dcx->cb = p7dcx->cb;
   286     childp7dcx->cb_arg = p7dcx->cb_arg;
   287     childp7dcx->first_decoded = PR_FALSE;
   288     childp7dcx->need_indefinite_finish = PR_FALSE;
   289     if (childtype == SEC_OID_PKCS7_SIGNED_DATA) {
   290 	childp7dcx->first_decoded = PR_TRUE;
   291     }
   293     /* now set up the parent to hand decoded data to the next level decoder */
   294     p7dcx->cb = (NSSCMSContentCallback)NSS_CMSDecoder_Update;
   295     p7dcx->cb_arg = childp7dcx;
   297     PORT_ArenaUnmark(poolp, mark);
   299     return SECSuccess;
   301 loser:
   302     if (mark)
   303 	PORT_ArenaRelease(poolp, mark);
   304     if (childp7dcx)
   305 	PORT_Free(childp7dcx);
   306     p7dcx->childp7dcx = NULL;
   307     return SECFailure;
   308 }
   310 static SECStatus
   311 nss_cms_after_data(NSSCMSDecoderContext *p7dcx)
   312 {
   313     NSSCMSDecoderContext *childp7dcx;
   314     SECStatus rv = SECFailure;
   316     /* Handle last block. This is necessary to flush out the last bytes
   317      * of a possibly incomplete block */
   318     nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE);
   320     /* finish any "inner" decoders - there's no more data coming... */
   321     if (p7dcx->childp7dcx != NULL) {
   322 	childp7dcx = p7dcx->childp7dcx;
   323 	if (childp7dcx->dcx != NULL) {
   324 	    /* we started and indefinite sequence somewhere, not complete it */
   325 	    if (childp7dcx->need_indefinite_finish) {
   326 		static const char lbuf[2] = { 0, 0 };
   327 		NSS_CMSDecoder_Update(childp7dcx, lbuf, sizeof(lbuf));
   328 		childp7dcx->need_indefinite_finish = PR_FALSE;
   329 	    }
   331 	    if (SEC_ASN1DecoderFinish(childp7dcx->dcx) != SECSuccess) {
   332 		/* do what? free content? */
   333 		rv = SECFailure;
   334 	    } else {
   335 		rv = nss_cms_after_end(childp7dcx);
   336 	    }
   337 	    if (rv != SECSuccess)
   338 		goto done;
   339 	}
   340 	PORT_Free(p7dcx->childp7dcx);
   341 	p7dcx->childp7dcx = NULL;
   342     }
   344     switch (p7dcx->type) {
   345     case SEC_OID_PKCS7_SIGNED_DATA:
   346 	/* this will finish the digests and verify */
   347 	rv = NSS_CMSSignedData_Decode_AfterData(p7dcx->content.signedData);
   348 	break;
   349     case SEC_OID_PKCS7_ENVELOPED_DATA:
   350 	rv = NSS_CMSEnvelopedData_Decode_AfterData(
   351 	                            p7dcx->content.envelopedData);
   352 	break;
   353     case SEC_OID_PKCS7_DIGESTED_DATA:
   354 	rv = NSS_CMSDigestedData_Decode_AfterData(
   355 	                           p7dcx->content.digestedData);
   356 	break;
   357     case SEC_OID_PKCS7_ENCRYPTED_DATA:
   358 	rv = NSS_CMSEncryptedData_Decode_AfterData(
   359 	                            p7dcx->content.encryptedData);
   360 	break;
   361     case SEC_OID_PKCS7_DATA:
   362 	/* do nothing */
   363 	break;
   364     default:
   365 	rv = NSS_CMSGenericWrapperData_Decode_AfterData(p7dcx->type,
   366 	                            p7dcx->content.genericData);
   367 	break;
   368     }
   369 done:
   370     return rv;
   371 }
   373 static SECStatus
   374 nss_cms_after_end(NSSCMSDecoderContext *p7dcx)
   375 {
   376     SECStatus rv = SECSuccess;
   378     switch (p7dcx->type) {
   379     case SEC_OID_PKCS7_SIGNED_DATA:
   380 	if (p7dcx->content.signedData)
   381 	    rv = NSS_CMSSignedData_Decode_AfterEnd(p7dcx->content.signedData);
   382 	break;
   383     case SEC_OID_PKCS7_ENVELOPED_DATA:
   384 	if (p7dcx->content.envelopedData)
   385 	    rv = NSS_CMSEnvelopedData_Decode_AfterEnd(
   386 	                               p7dcx->content.envelopedData);
   387 	break;
   388     case SEC_OID_PKCS7_DIGESTED_DATA:
   389 	if (p7dcx->content.digestedData)
   390 	    rv = NSS_CMSDigestedData_Decode_AfterEnd(
   391 	                              p7dcx->content.digestedData);
   392 	break;
   393     case SEC_OID_PKCS7_ENCRYPTED_DATA:
   394 	if (p7dcx->content.encryptedData)
   395 	    rv = NSS_CMSEncryptedData_Decode_AfterEnd(
   396 	                               p7dcx->content.encryptedData);
   397 	break;
   398     case SEC_OID_PKCS7_DATA:
   399 	break;
   400     default:
   401 	rv = NSS_CMSGenericWrapperData_Decode_AfterEnd(p7dcx->type,
   402 	                               p7dcx->content.genericData);
   403 	break;
   404     }
   405     return rv;
   406 }
   408 /*
   409  * nss_cms_decoder_work_data - handle decoded data bytes.
   410  *
   411  * This function either decrypts the data if needed, and/or calculates digests
   412  * on it, then either stores it or passes it on to the next level decoder.
   413  */
   414 static void
   415 nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx, 
   416 			     const unsigned char *data, unsigned long len,
   417 			     PRBool final)
   418 {
   419     NSSCMSContentInfo *cinfo;
   420     unsigned char *buf = NULL;
   421     unsigned char *dest;
   422     unsigned int offset;
   423     SECStatus rv;
   425     /*
   426      * We should really have data to process, or we should be trying
   427      * to finish/flush the last block.  (This is an overly paranoid
   428      * check since all callers are in this file and simple inspection
   429      * proves they do it right.  But it could find a bug in future
   430      * modifications/development, that is why it is here.)
   431      */
   432     PORT_Assert ((data != NULL && len) || final);
   434     cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type);
   435     if (!cinfo) {
   436 	/* The original programmer didn't expect this to happen */
   437 	p7dcx->error = SEC_ERROR_LIBRARY_FAILURE;
   438 	goto loser;
   439     }
   441     if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
   442 	/*
   443 	 * we are decrypting.
   444 	 * 
   445 	 * XXX If we get an error, we do not want to do the digest or callback,
   446 	 * but we want to keep decoding.  Or maybe we want to stop decoding
   447 	 * altogether if there is a callback, because obviously we are not
   448 	 * sending the data back and they want to know that.
   449 	 */
   451 	unsigned int outlen = 0;	/* length of decrypted data */
   452 	unsigned int buflen;		/* length available for decrypted data */
   454 	/* find out about the length of decrypted data */
   455 	buflen = NSS_CMSCipherContext_DecryptLength(cinfo->privateInfo->ciphcx, len, final);
   457 	/*
   458 	 * it might happen that we did not provide enough data for a full
   459 	 * block (decryption unit), and that there is no output available
   460 	 */
   462 	/* no output available, AND no input? */
   463 	if (buflen == 0 && len == 0)
   464 	    goto loser;	/* bail out */
   466 	/*
   467 	 * have inner decoder: pass the data on (means inner content type is NOT data)
   468 	 * no inner decoder: we have DATA in here: either call callback or store
   469 	 */
   470 	if (buflen != 0) {
   471 	    /* there will be some output - need to make room for it */
   472 	    /* allocate buffer from the heap */
   473 	    buf = (unsigned char *)PORT_Alloc(buflen);
   474 	    if (buf == NULL) {
   475 		p7dcx->error = SEC_ERROR_NO_MEMORY;
   476 		goto loser;
   477 	    }
   478 	}
   480 	/*
   481 	 * decrypt incoming data
   482 	 * buf can still be NULL here (and buflen == 0) here if we don't expect
   483 	 * any output (see above), but we still need to call NSS_CMSCipherContext_Decrypt to
   484 	 * keep track of incoming data
   485 	 */
   486 	rv = NSS_CMSCipherContext_Decrypt(cinfo->privateInfo->ciphcx, buf, &outlen, buflen,
   487 			       data, len, final);
   488 	if (rv != SECSuccess) {
   489 	    p7dcx->error = PORT_GetError();
   490 	    goto loser;
   491 	}
   493 	PORT_Assert (final || outlen == buflen);
   495 	/* swap decrypted data in */
   496 	data = buf;
   497 	len = outlen;
   498     }
   500     if (len == 0)
   501 	goto done;		/* nothing more to do */
   503     /*
   504      * Update the running digests with plaintext bytes (if we need to).
   505      */
   506     if (cinfo->privateInfo && cinfo->privateInfo->digcx)
   507 	NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len);
   509     /* at this point, we have the plain decoded & decrypted data 
   510     ** which is either more encoded DER (which we need to hand to the child 
   511     ** decoder) or data we need to hand back to our caller 
   512     */
   514     /* pass the content back to our caller or */
   515     /* feed our freshly decrypted and decoded data into child decoder */
   516     if (p7dcx->cb != NULL) {
   517 	(*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
   518     }
   519 #if 1
   520     else
   521 #endif
   522     if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) {
   523 	/* store it in "inner" data item as well */
   524 	/* find the DATA item in the encapsulated cinfo and store it there */
   525 	NSSCMSDecoderData *decoderData = 
   526 				(NSSCMSDecoderData *)cinfo->content.pointer;
   527 	SECItem *dataItem = &decoderData->data;
   529 	offset = dataItem->len;
   530 	if (dataItem->len+len > decoderData->totalBufferSize) {
   531 	    int needLen = (dataItem->len+len) * 2;
   532 	    dest = (unsigned char *)
   533 				PORT_ArenaAlloc(p7dcx->cmsg->poolp, needLen);
   534 	    if (dest == NULL) {
   535 		p7dcx->error = SEC_ERROR_NO_MEMORY;
   536 		goto loser;
   537 	    }
   539 	    if (dataItem->len) {
   540 		PORT_Memcpy(dest, dataItem->data, dataItem->len);
   541 	    }
   542 	    decoderData->totalBufferSize = needLen;
   543 	    dataItem->data = dest;
   544 	}
   546 	/* copy it in */
   547 	PORT_Memcpy(dataItem->data + offset, data, len);
   548 	dataItem->len += len;
   549     }
   551 done:
   552 loser:
   553     if (buf)
   554 	PORT_Free (buf);
   555 }
   557 /*
   558  * nss_cms_decoder_update_filter - process ASN.1 data
   559  *
   560  * once we have set up a filter in nss_cms_decoder_notify(),
   561  * all data processed by the ASN.1 decoder is also passed through here.
   562  * we pass the content bytes (as opposed to length and tag bytes) on to
   563  * nss_cms_decoder_work_data().
   564  */
   565 static void
   566 nss_cms_decoder_update_filter (void *arg, const char *data, unsigned long len,
   567 			  int depth, SEC_ASN1EncodingPart data_kind)
   568 {
   569     NSSCMSDecoderContext *p7dcx;
   571     PORT_Assert (len);	/* paranoia */
   572     if (len == 0)
   573 	return;
   575     p7dcx = (NSSCMSDecoderContext*)arg;
   577     p7dcx->saw_contents = PR_TRUE;
   579     /* pass on the content bytes only */
   580     if (data_kind == SEC_ASN1_Contents)
   581 	nss_cms_decoder_work_data(p7dcx, (const unsigned char *) data, len, 
   582 	                          PR_FALSE);
   583 }
   585 /*
   586  * NSS_CMSDecoder_Start - set up decoding of a DER-encoded CMS message
   587  *
   588  * "poolp" - pointer to arena for message, or NULL if new pool should be created
   589  * "cb", "cb_arg" - callback function and argument for delivery of inner content
   590  * "pwfn", pwfn_arg" - callback function for getting token password
   591  * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
   592  */
   593 NSSCMSDecoderContext *
   594 NSS_CMSDecoder_Start(PLArenaPool *poolp,
   595 		      NSSCMSContentCallback cb, void *cb_arg,
   596 		      PK11PasswordFunc pwfn, void *pwfn_arg,
   597 		      NSSCMSGetDecryptKeyCallback decrypt_key_cb, 
   598 		      void *decrypt_key_cb_arg)
   599 {
   600     NSSCMSDecoderContext *p7dcx;
   601     NSSCMSMessage *cmsg;
   603     cmsg = NSS_CMSMessage_Create(poolp);
   604     if (cmsg == NULL)
   605 	return NULL;
   607     NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, 
   608                                      decrypt_key_cb_arg, NULL, NULL);
   610     p7dcx = PORT_ZNew(NSSCMSDecoderContext);
   611     if (p7dcx == NULL) {
   612 	NSS_CMSMessage_Destroy(cmsg);
   613 	return NULL;
   614     }
   616     p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, NSSCMSMessageTemplate);
   617     if (p7dcx->dcx == NULL) {
   618 	PORT_Free (p7dcx);
   619 	NSS_CMSMessage_Destroy(cmsg);
   620 	return NULL;
   621     }
   623     SEC_ASN1DecoderSetNotifyProc (p7dcx->dcx, nss_cms_decoder_notify, p7dcx);
   625     p7dcx->cmsg = cmsg;
   626     p7dcx->type = SEC_OID_UNKNOWN;
   628     p7dcx->cb = cb;
   629     p7dcx->cb_arg = cb_arg;
   630     p7dcx->first_decoded = PR_FALSE;
   631     p7dcx->need_indefinite_finish = PR_FALSE;
   632     return p7dcx;
   633 }
   635 /*
   636  * NSS_CMSDecoder_Update - feed DER-encoded data to decoder
   637  */
   638 SECStatus
   639 NSS_CMSDecoder_Update(NSSCMSDecoderContext *p7dcx, const char *buf, 
   640                       unsigned long len)
   641 {
   642     SECStatus rv = SECSuccess;
   643     if (p7dcx->dcx != NULL && p7dcx->error == 0) {	
   644     	/* if error is set already, don't bother */
   645 	if ((p7dcx->type == SEC_OID_PKCS7_SIGNED_DATA) 
   646 		&& (p7dcx->first_decoded==PR_TRUE)
   647 		&& (buf[0] == SEC_ASN1_INTEGER)) {
   648 	    /* Microsoft Windows 2008 left out the Sequence wrapping in some
   649 	     * of their kerberos replies. If we are here, we most likely are
   650 	     * dealing with one of those replies. Supply the Sequence wrap
   651 	     * as indefinite encoding (since we don't know the total length
   652 	     * yet) */
   653 	     static const char lbuf[2] = 
   654 		{ SEC_ASN1_SEQUENCE|SEC_ASN1_CONSTRUCTED, 0x80 };
   655 	     rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, lbuf, sizeof(lbuf));
   656 	     if (rv != SECSuccess) {
   657 		goto loser;
   658 	    }
   659 	    /* ok, we're going to need the indefinite finish when we are done */
   660 	    p7dcx->need_indefinite_finish = PR_TRUE;
   661 	}
   663 	rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, buf, len);
   664     }
   666 loser:
   667     p7dcx->first_decoded = PR_FALSE;
   668     if (rv != SECSuccess) {
   669 	p7dcx->error = PORT_GetError();
   670 	PORT_Assert (p7dcx->error);
   671 	if (p7dcx->error == 0)
   672 	    p7dcx->error = -1;
   673     }
   675     if (p7dcx->error == 0)
   676 	return SECSuccess;
   678     /* there has been a problem, let's finish the decoder */
   679     if (p7dcx->dcx != NULL) {
   680 	(void) SEC_ASN1DecoderFinish (p7dcx->dcx);
   681 	p7dcx->dcx = NULL;
   682     }
   683     PORT_SetError (p7dcx->error);
   685     return SECFailure;
   686 }
   688 /*
   689  * NSS_CMSDecoder_Cancel - stop decoding in case of error
   690  */
   691 void
   692 NSS_CMSDecoder_Cancel(NSSCMSDecoderContext *p7dcx)
   693 {
   694     if (p7dcx->dcx != NULL)
   695 	(void)SEC_ASN1DecoderFinish(p7dcx->dcx);
   696     NSS_CMSMessage_Destroy(p7dcx->cmsg);
   697     PORT_Free(p7dcx);
   698 }
   700 /*
   701  * NSS_CMSDecoder_Finish - mark the end of inner content and finish decoding
   702  */
   703 NSSCMSMessage *
   704 NSS_CMSDecoder_Finish(NSSCMSDecoderContext *p7dcx)
   705 {
   706     NSSCMSMessage *cmsg;
   708     cmsg = p7dcx->cmsg;
   710     if (p7dcx->dcx == NULL || 
   711         SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess ||
   712 	nss_cms_after_end(p7dcx) != SECSuccess)
   713     {
   714 	NSS_CMSMessage_Destroy(cmsg);	/* get rid of pool if it's ours */
   715 	cmsg = NULL;
   716     }
   718     PORT_Free(p7dcx);
   719     return cmsg;
   720 }
   722 NSSCMSMessage *
   723 NSS_CMSMessage_CreateFromDER(SECItem *DERmessage,
   724 		    NSSCMSContentCallback cb, void *cb_arg,
   725 		    PK11PasswordFunc pwfn, void *pwfn_arg,
   726 		    NSSCMSGetDecryptKeyCallback decrypt_key_cb, 
   727 		    void *decrypt_key_cb_arg)
   728 {
   729     NSSCMSDecoderContext *p7dcx;
   731     /* first arg(poolp) == NULL => create our own pool */
   732     p7dcx = NSS_CMSDecoder_Start(NULL, cb, cb_arg, pwfn, pwfn_arg, 
   733                                  decrypt_key_cb, decrypt_key_cb_arg);
   734     if (p7dcx == NULL)
   735 	return NULL;
   736     NSS_CMSDecoder_Update(p7dcx, (char *)DERmessage->data, DERmessage->len);
   737     return NSS_CMSDecoder_Finish(p7dcx);
   738 }

mercurial