security/nss/lib/pkcs7/p7decode.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/pkcs7/p7decode.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1932 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/*
     1.9 + * PKCS7 decoding, verification.
    1.10 + */
    1.11 +
    1.12 +#include "p7local.h"
    1.13 +
    1.14 +#include "cert.h"
    1.15 +				/* XXX do not want to have to include */
    1.16 +#include "certdb.h"		/* certdb.h -- the trust stuff needed by */
    1.17 +     				/* the add certificate code needs to get */
    1.18 +                           	/* rewritten/abstracted and then this */
    1.19 +      				/* include should be removed! */
    1.20 +/*#include "cdbhdl.h" */
    1.21 +#include "cryptohi.h"
    1.22 +#include "key.h"
    1.23 +#include "secasn1.h"
    1.24 +#include "secitem.h"
    1.25 +#include "secoid.h"
    1.26 +#include "pk11func.h"
    1.27 +#include "prtime.h"
    1.28 +#include "secerr.h"
    1.29 +#include "sechash.h"	/* for HASH_GetHashObject() */
    1.30 +#include "secder.h"
    1.31 +#include "secpkcs5.h"
    1.32 +
    1.33 +struct sec_pkcs7_decoder_worker {
    1.34 +    int depth;
    1.35 +    int digcnt;
    1.36 +    void **digcxs;
    1.37 +    const SECHashObject **digobjs;
    1.38 +    sec_PKCS7CipherObject *decryptobj;
    1.39 +    PRBool saw_contents;
    1.40 +};
    1.41 +
    1.42 +struct SEC_PKCS7DecoderContextStr {
    1.43 +    SEC_ASN1DecoderContext *dcx;
    1.44 +    SEC_PKCS7ContentInfo *cinfo;
    1.45 +    SEC_PKCS7DecoderContentCallback cb;
    1.46 +    void *cb_arg;
    1.47 +    SECKEYGetPasswordKey pwfn;
    1.48 +    void *pwfn_arg;
    1.49 +    struct sec_pkcs7_decoder_worker worker;
    1.50 +    PLArenaPool *tmp_poolp;
    1.51 +    int error;
    1.52 +    SEC_PKCS7GetDecryptKeyCallback dkcb;
    1.53 +    void *dkcb_arg;
    1.54 +    SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb;
    1.55 +};
    1.56 +
    1.57 +/*
    1.58 + * Handle one worker, decrypting and digesting the data as necessary.
    1.59 + *
    1.60 + * XXX If/when we support nested contents, this probably needs to be
    1.61 + * revised somewhat to get passed the content-info (which unfortunately
    1.62 + * can be two different types depending on whether it is encrypted or not)
    1.63 + * corresponding to the given worker.
    1.64 + */
    1.65 +static void
    1.66 +sec_pkcs7_decoder_work_data (SEC_PKCS7DecoderContext *p7dcx,
    1.67 +			     struct sec_pkcs7_decoder_worker *worker,
    1.68 +			     const unsigned char *data, unsigned long len,
    1.69 +			     PRBool final)
    1.70 +{
    1.71 +    unsigned char *buf = NULL;
    1.72 +    SECStatus rv;
    1.73 +    int i;
    1.74 +
    1.75 +    /*
    1.76 +     * We should really have data to process, or we should be trying
    1.77 +     * to finish/flush the last block.  (This is an overly paranoid
    1.78 +     * check since all callers are in this file and simple inspection
    1.79 +     * proves they do it right.  But it could find a bug in future
    1.80 +     * modifications/development, that is why it is here.)
    1.81 +     */
    1.82 +    PORT_Assert ((data != NULL && len) || final);
    1.83 +
    1.84 +    /*
    1.85 +     * Decrypt this chunk.
    1.86 +     *
    1.87 +     * XXX If we get an error, we do not want to do the digest or callback,
    1.88 +     * but we want to keep decoding.  Or maybe we want to stop decoding
    1.89 +     * altogether if there is a callback, because obviously we are not
    1.90 +     * sending the data back and they want to know that.
    1.91 +     */
    1.92 +    if (worker->decryptobj != NULL) {
    1.93 +	/* XXX the following lengths should all be longs? */
    1.94 +	unsigned int inlen;	/* length of data being decrypted */
    1.95 +	unsigned int outlen;	/* length of decrypted data */
    1.96 +	unsigned int buflen;	/* length available for decrypted data */
    1.97 +	SECItem *plain;
    1.98 +
    1.99 +	inlen = len;
   1.100 +	buflen = sec_PKCS7DecryptLength (worker->decryptobj, inlen, final);
   1.101 +	if (buflen == 0) {
   1.102 +	    if (inlen == 0)	/* no input and no output */
   1.103 +		return;
   1.104 +	    /*
   1.105 +	     * No output is expected, but the input data may be buffered
   1.106 +	     * so we still have to call Decrypt.
   1.107 +	     */
   1.108 +	    rv = sec_PKCS7Decrypt (worker->decryptobj, NULL, NULL, 0,
   1.109 +				   data, inlen, final);
   1.110 +	    if (rv != SECSuccess) {
   1.111 +		p7dcx->error = PORT_GetError();
   1.112 +		return;		/* XXX indicate error? */
   1.113 +	    }
   1.114 +	    return;
   1.115 +	}
   1.116 +
   1.117 +	if (p7dcx->cb != NULL) {
   1.118 +	    buf = (unsigned char *) PORT_Alloc (buflen);
   1.119 +	    plain = NULL;
   1.120 +	} else {
   1.121 +	    unsigned long oldlen;
   1.122 +
   1.123 +	    /*
   1.124 +	     * XXX This assumes one level of content only.
   1.125 +	     * See comment above about nested content types.
   1.126 +	     * XXX Also, it should work for signedAndEnvelopedData, too!
   1.127 +	     */
   1.128 +	    plain = &(p7dcx->cinfo->
   1.129 +			content.envelopedData->encContentInfo.plainContent);
   1.130 +
   1.131 +	    oldlen = plain->len;
   1.132 +	    if (oldlen == 0) {
   1.133 +		buf = (unsigned char*)PORT_ArenaAlloc (p7dcx->cinfo->poolp, 
   1.134 +						       buflen);
   1.135 +	    } else {
   1.136 +		buf = (unsigned char*)PORT_ArenaGrow (p7dcx->cinfo->poolp, 
   1.137 +				      plain->data,
   1.138 +				      oldlen, oldlen + buflen);
   1.139 +		if (buf != NULL)
   1.140 +		    buf += oldlen;
   1.141 +	    }
   1.142 +	    plain->data = buf;
   1.143 +	}
   1.144 +	if (buf == NULL) {
   1.145 +	    p7dcx->error = SEC_ERROR_NO_MEMORY;
   1.146 +	    return;		/* XXX indicate error? */
   1.147 +	}
   1.148 +	rv = sec_PKCS7Decrypt (worker->decryptobj, buf, &outlen, buflen,
   1.149 +			       data, inlen, final);
   1.150 +	if (rv != SECSuccess) {
   1.151 +	    p7dcx->error = PORT_GetError();
   1.152 +	    return;		/* XXX indicate error? */
   1.153 +	}
   1.154 +	if (plain != NULL) {
   1.155 +	    PORT_Assert (final || outlen == buflen);
   1.156 +	    plain->len += outlen;
   1.157 +	}
   1.158 +	data = buf;
   1.159 +	len = outlen;
   1.160 +    }
   1.161 +
   1.162 +    /*
   1.163 +     * Update the running digests.
   1.164 +     */
   1.165 +    if (len) {
   1.166 +	for (i = 0; i < worker->digcnt; i++) {
   1.167 +	    (* worker->digobjs[i]->update) (worker->digcxs[i], data, len);
   1.168 +	}
   1.169 +    }
   1.170 +
   1.171 +    /*
   1.172 +     * Pass back the contents bytes, and free the temporary buffer.
   1.173 +     */
   1.174 +    if (p7dcx->cb != NULL) {
   1.175 +	if (len)
   1.176 +	    (* p7dcx->cb) (p7dcx->cb_arg, (const char *)data, len);
   1.177 +	if (worker->decryptobj != NULL) {
   1.178 +	    PORT_Assert (buf != NULL);
   1.179 +	    PORT_Free (buf);
   1.180 +	}
   1.181 +    }
   1.182 +}
   1.183 +
   1.184 +static void
   1.185 +sec_pkcs7_decoder_filter (void *arg, const char *data, unsigned long len,
   1.186 +			  int depth, SEC_ASN1EncodingPart data_kind)
   1.187 +{
   1.188 +    SEC_PKCS7DecoderContext *p7dcx;
   1.189 +    struct sec_pkcs7_decoder_worker *worker;
   1.190 +
   1.191 +    /*
   1.192 +     * Since we do not handle any nested contents, the only bytes we
   1.193 +     * are really interested in are the actual contents bytes (not
   1.194 +     * the identifier, length, or end-of-contents bytes).  If we were
   1.195 +     * handling nested types we would probably need to do something
   1.196 +     * smarter based on depth and data_kind.
   1.197 +     */
   1.198 +    if (data_kind != SEC_ASN1_Contents)
   1.199 +	return;
   1.200 +
   1.201 +    /*
   1.202 +     * The ASN.1 decoder should not even call us with a length of 0.
   1.203 +     * Just being paranoid.
   1.204 +     */
   1.205 +    PORT_Assert (len);
   1.206 +    if (len == 0)
   1.207 +	return;
   1.208 +
   1.209 +    p7dcx = (SEC_PKCS7DecoderContext*)arg;
   1.210 +
   1.211 +    /*
   1.212 +     * Handling nested contents would mean that there is a chain
   1.213 +     * of workers -- one per each level of content.  The following
   1.214 +     * would start with the first worker and loop over them.
   1.215 +     */
   1.216 +    worker = &(p7dcx->worker);
   1.217 +
   1.218 +    worker->saw_contents = PR_TRUE;
   1.219 +
   1.220 +    sec_pkcs7_decoder_work_data (p7dcx, worker,
   1.221 +				 (const unsigned char *) data, len, PR_FALSE);
   1.222 +}
   1.223 +
   1.224 +
   1.225 +/*
   1.226 + * Create digest contexts for each algorithm in "digestalgs".
   1.227 + * No algorithms is not an error, we just do not do anything.
   1.228 + * An error (like trouble allocating memory), marks the error
   1.229 + * in "p7dcx" and returns SECFailure, which means that our caller
   1.230 + * should just give up altogether.
   1.231 + */
   1.232 +static SECStatus
   1.233 +sec_pkcs7_decoder_start_digests (SEC_PKCS7DecoderContext *p7dcx, int depth,
   1.234 +				 SECAlgorithmID **digestalgs)
   1.235 +{
   1.236 +    int i, digcnt;
   1.237 +
   1.238 +    if (digestalgs == NULL)
   1.239 +	return SECSuccess;
   1.240 +
   1.241 +    /*
   1.242 +     * Count the algorithms.
   1.243 +     */
   1.244 +    digcnt = 0;
   1.245 +    while (digestalgs[digcnt] != NULL)
   1.246 +	digcnt++;
   1.247 +
   1.248 +    /*
   1.249 +     * No algorithms means no work to do.
   1.250 +     * Just act as if there were no algorithms specified.
   1.251 +     */
   1.252 +    if (digcnt == 0)
   1.253 +	return SECSuccess;
   1.254 +
   1.255 +    p7dcx->worker.digcxs = (void**)PORT_ArenaAlloc (p7dcx->tmp_poolp,
   1.256 +					    digcnt * sizeof (void *));
   1.257 +    p7dcx->worker.digobjs = (const SECHashObject**)PORT_ArenaAlloc (p7dcx->tmp_poolp,
   1.258 +					     digcnt * sizeof (SECHashObject *));
   1.259 +    if (p7dcx->worker.digcxs == NULL || p7dcx->worker.digobjs == NULL) {
   1.260 +	p7dcx->error = SEC_ERROR_NO_MEMORY;
   1.261 +	return SECFailure;
   1.262 +    }
   1.263 +
   1.264 +    p7dcx->worker.depth = depth;
   1.265 +    p7dcx->worker.digcnt = 0;
   1.266 +
   1.267 +    /*
   1.268 +     * Create a digest context for each algorithm.
   1.269 +     */
   1.270 +    for (i = 0; i < digcnt; i++) {
   1.271 +	SECAlgorithmID *     algid  = digestalgs[i];
   1.272 +	SECOidTag            oidTag = SECOID_FindOIDTag(&(algid->algorithm));
   1.273 +	const SECHashObject *digobj = HASH_GetHashObjectByOidTag(oidTag);
   1.274 +	void *digcx;
   1.275 +
   1.276 +	/*
   1.277 +	 * Skip any algorithm we do not even recognize; obviously,
   1.278 +	 * this could be a problem, but if it is critical then the
   1.279 +	 * result will just be that the signature does not verify.
   1.280 +	 * We do not necessarily want to error out here, because
   1.281 +	 * the particular algorithm may not actually be important,
   1.282 +	 * but we cannot know that until later.
   1.283 +	 */
   1.284 +	if (digobj == NULL) {
   1.285 +	    p7dcx->worker.digcnt--;
   1.286 +	    continue;
   1.287 +	}
   1.288 +
   1.289 +	digcx = (* digobj->create)();
   1.290 +	if (digcx != NULL) {
   1.291 +	    (* digobj->begin) (digcx);
   1.292 +	    p7dcx->worker.digobjs[p7dcx->worker.digcnt] = digobj;
   1.293 +	    p7dcx->worker.digcxs[p7dcx->worker.digcnt] = digcx;
   1.294 +	    p7dcx->worker.digcnt++;
   1.295 +	}
   1.296 +    }
   1.297 +
   1.298 +    if (p7dcx->worker.digcnt != 0)
   1.299 +	SEC_ASN1DecoderSetFilterProc (p7dcx->dcx,
   1.300 +				      sec_pkcs7_decoder_filter,
   1.301 +				      p7dcx,
   1.302 +				      (PRBool)(p7dcx->cb != NULL));
   1.303 +    return SECSuccess;
   1.304 +}
   1.305 +
   1.306 +
   1.307 +/*
   1.308 + * Close out all of the digest contexts, storing the results in "digestsp".
   1.309 + */
   1.310 +static SECStatus
   1.311 +sec_pkcs7_decoder_finish_digests (SEC_PKCS7DecoderContext *p7dcx,
   1.312 +				  PLArenaPool *poolp,
   1.313 +				  SECItem ***digestsp)
   1.314 +{
   1.315 +    struct sec_pkcs7_decoder_worker *worker;
   1.316 +    const SECHashObject *digobj;
   1.317 +    void *digcx;
   1.318 +    SECItem **digests, *digest;
   1.319 +    int i;
   1.320 +    void *mark;
   1.321 +
   1.322 +    /*
   1.323 +     * XXX Handling nested contents would mean that there is a chain
   1.324 +     * of workers -- one per each level of content.  The following
   1.325 +     * would want to find the last worker in the chain.
   1.326 +     */
   1.327 +    worker = &(p7dcx->worker);
   1.328 +
   1.329 +    /*
   1.330 +     * If no digests, then we have nothing to do.
   1.331 +     */
   1.332 +    if (worker->digcnt == 0)
   1.333 +	return SECSuccess;
   1.334 +
   1.335 +    /*
   1.336 +     * No matter what happens after this, we want to stop filtering.
   1.337 +     * XXX If we handle nested contents, we only want to stop filtering
   1.338 +     * if we are finishing off the *last* worker.
   1.339 +     */
   1.340 +    SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
   1.341 +
   1.342 +    /*
   1.343 +     * If we ended up with no contents, just destroy each
   1.344 +     * digest context -- they are meaningless and potentially
   1.345 +     * confusing, because their presence would imply some content
   1.346 +     * was digested.
   1.347 +     */
   1.348 +    if (! worker->saw_contents) {
   1.349 +	for (i = 0; i < worker->digcnt; i++) {
   1.350 +	    digcx = worker->digcxs[i];
   1.351 +	    digobj = worker->digobjs[i];
   1.352 +	    (* digobj->destroy) (digcx, PR_TRUE);
   1.353 +	}
   1.354 +	return SECSuccess;
   1.355 +    }
   1.356 +
   1.357 +    mark = PORT_ArenaMark (poolp);
   1.358 +
   1.359 +    /*
   1.360 +     * Close out each digest context, saving digest away.
   1.361 +     */
   1.362 +    digests = 
   1.363 +      (SECItem**)PORT_ArenaAlloc (poolp,(worker->digcnt+1)*sizeof(SECItem *));
   1.364 +    digest = (SECItem*)PORT_ArenaAlloc (poolp, worker->digcnt*sizeof(SECItem));
   1.365 +    if (digests == NULL || digest == NULL) {
   1.366 +	p7dcx->error = PORT_GetError();
   1.367 +	PORT_ArenaRelease (poolp, mark);
   1.368 +	return SECFailure;
   1.369 +    }
   1.370 +
   1.371 +    for (i = 0; i < worker->digcnt; i++, digest++) {
   1.372 +	digcx = worker->digcxs[i];
   1.373 +	digobj = worker->digobjs[i];
   1.374 +
   1.375 +	digest->data = (unsigned char*)PORT_ArenaAlloc (poolp, digobj->length);
   1.376 +	if (digest->data == NULL) {
   1.377 +	    p7dcx->error = PORT_GetError();
   1.378 +	    PORT_ArenaRelease (poolp, mark);
   1.379 +	    return SECFailure;
   1.380 +	}
   1.381 +
   1.382 +	digest->len = digobj->length;
   1.383 +	(* digobj->end) (digcx, digest->data, &(digest->len), digest->len);
   1.384 +	(* digobj->destroy) (digcx, PR_TRUE);
   1.385 +
   1.386 +	digests[i] = digest;
   1.387 +    }
   1.388 +    digests[i] = NULL;
   1.389 +    *digestsp = digests;
   1.390 +
   1.391 +    PORT_ArenaUnmark (poolp, mark);
   1.392 +    return SECSuccess;
   1.393 +}
   1.394 +
   1.395 +/*
   1.396 + * XXX Need comment explaining following helper function (which is used
   1.397 + * by sec_pkcs7_decoder_start_decrypt).
   1.398 + */
   1.399 +
   1.400 +static PK11SymKey *
   1.401 +sec_pkcs7_decoder_get_recipient_key (SEC_PKCS7DecoderContext *p7dcx,
   1.402 +				     SEC_PKCS7RecipientInfo **recipientinfos,
   1.403 +				     SEC_PKCS7EncryptedContentInfo *enccinfo)
   1.404 +{
   1.405 +    SEC_PKCS7RecipientInfo *ri;
   1.406 +    CERTCertificate *cert = NULL;
   1.407 +    SECKEYPrivateKey *privkey = NULL;
   1.408 +    PK11SymKey *bulkkey = NULL;
   1.409 +    SECOidTag keyalgtag, bulkalgtag, encalgtag;
   1.410 +    PK11SlotInfo *slot = NULL;
   1.411 +
   1.412 +    if (recipientinfos == NULL || recipientinfos[0] == NULL) {
   1.413 +	p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT;
   1.414 +	goto no_key_found;
   1.415 +    }
   1.416 +
   1.417 +    cert = PK11_FindCertAndKeyByRecipientList(&slot,recipientinfos,&ri,
   1.418 +						&privkey, p7dcx->pwfn_arg);
   1.419 +    if (cert == NULL) {
   1.420 +	p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT;
   1.421 +	goto no_key_found;
   1.422 +    }
   1.423 +
   1.424 +    ri->cert = cert;		/* so we can find it later */
   1.425 +    PORT_Assert(privkey != NULL);
   1.426 +
   1.427 +    keyalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
   1.428 +    encalgtag = SECOID_GetAlgorithmTag (&(ri->keyEncAlg));
   1.429 +    if (keyalgtag != encalgtag) {
   1.430 +	p7dcx->error = SEC_ERROR_PKCS7_KEYALG_MISMATCH;
   1.431 +	goto no_key_found;
   1.432 +    }
   1.433 +    bulkalgtag = SECOID_GetAlgorithmTag (&(enccinfo->contentEncAlg));
   1.434 +
   1.435 +    switch (encalgtag) {
   1.436 +      case SEC_OID_PKCS1_RSA_ENCRYPTION:
   1.437 +	bulkkey = PK11_PubUnwrapSymKey (privkey, &ri->encKey,
   1.438 +					PK11_AlgtagToMechanism (bulkalgtag),
   1.439 +					CKA_DECRYPT, 0);
   1.440 +	if (bulkkey == NULL) {
   1.441 +	    p7dcx->error = PORT_GetError();
   1.442 +	    PORT_SetError(0);
   1.443 +	    goto no_key_found;
   1.444 +	}
   1.445 +	break;
   1.446 +      default:
   1.447 +	p7dcx->error = SEC_ERROR_UNSUPPORTED_KEYALG;
   1.448 +	break;
   1.449 +    }
   1.450 +
   1.451 +no_key_found:
   1.452 +    if (privkey != NULL)
   1.453 +	SECKEY_DestroyPrivateKey (privkey);
   1.454 +    if (slot != NULL)
   1.455 +	PK11_FreeSlot(slot);
   1.456 +
   1.457 +    return bulkkey;
   1.458 +}
   1.459 + 
   1.460 +/*
   1.461 + * XXX The following comment is old -- the function used to only handle
   1.462 + * EnvelopedData or SignedAndEnvelopedData but now handles EncryptedData
   1.463 + * as well (and it had all of the code of the helper function above
   1.464 + * built into it), though the comment was left as is.  Fix it...
   1.465 + *
   1.466 + * We are just about to decode the content of an EnvelopedData.
   1.467 + * Set up a decryption context so we can decrypt as we go.
   1.468 + * Presumably we are one of the recipients listed in "recipientinfos".
   1.469 + * (XXX And if we are not, or if we have trouble, what should we do?
   1.470 + *  It would be nice to let the decoding still work.  Maybe it should
   1.471 + *  be an error if there is a content callback, but not an error otherwise?)
   1.472 + * The encryption key and related information can be found in "enccinfo".
   1.473 + */
   1.474 +static SECStatus
   1.475 +sec_pkcs7_decoder_start_decrypt (SEC_PKCS7DecoderContext *p7dcx, int depth,
   1.476 +				 SEC_PKCS7RecipientInfo **recipientinfos,
   1.477 +				 SEC_PKCS7EncryptedContentInfo *enccinfo,
   1.478 +				 PK11SymKey **copy_key_for_signature)
   1.479 +{
   1.480 +    PK11SymKey *bulkkey = NULL;
   1.481 +    sec_PKCS7CipherObject *decryptobj;
   1.482 +
   1.483 +    /*
   1.484 +     * If a callback is supplied to retrieve the encryption key, 
   1.485 +     * for instance, for Encrypted Content infos, then retrieve
   1.486 +     * the bulkkey from the callback.  Otherwise, assume that
   1.487 +     * we are processing Enveloped or SignedAndEnveloped data
   1.488 +     * content infos.
   1.489 +     *
   1.490 +     * XXX Put an assert here?
   1.491 +     */
   1.492 +    if (SEC_PKCS7ContentType(p7dcx->cinfo) == SEC_OID_PKCS7_ENCRYPTED_DATA) {
   1.493 +	if (p7dcx->dkcb != NULL) {
   1.494 +	    bulkkey = (*p7dcx->dkcb)(p7dcx->dkcb_arg, 
   1.495 +				     &(enccinfo->contentEncAlg));
   1.496 +	}
   1.497 +	enccinfo->keysize = 0;
   1.498 +    } else {
   1.499 +	bulkkey = sec_pkcs7_decoder_get_recipient_key (p7dcx, recipientinfos, 
   1.500 +						       enccinfo);
   1.501 +	if (bulkkey == NULL) goto no_decryption;
   1.502 +	enccinfo->keysize = PK11_GetKeyStrength(bulkkey, 
   1.503 +						&(enccinfo->contentEncAlg));
   1.504 +
   1.505 +    }
   1.506 +
   1.507 +    /*
   1.508 +     * XXX I think following should set error in p7dcx and clear set error
   1.509 +     * (as used to be done here, or as is done in get_receipient_key above.
   1.510 +     */
   1.511 +    if(bulkkey == NULL) {
   1.512 +	goto no_decryption;
   1.513 +    }
   1.514 +    
   1.515 +    /* 
   1.516 +     * We want to make sure decryption is allowed.  This is done via
   1.517 +     * a callback specified in SEC_PKCS7DecoderStart().
   1.518 +     */
   1.519 +    if (p7dcx->decrypt_allowed_cb) {
   1.520 +	if ((*p7dcx->decrypt_allowed_cb) (&(enccinfo->contentEncAlg), 
   1.521 +					  bulkkey) == PR_FALSE) {
   1.522 +	    p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
   1.523 +	    goto no_decryption;
   1.524 +	}
   1.525 +    } else {
   1.526 +	    p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
   1.527 +	    goto no_decryption;
   1.528 +    }
   1.529 +
   1.530 +    /*
   1.531 +     * When decrypting a signedAndEnvelopedData, the signature also has
   1.532 +     * to be decrypted with the bulk encryption key; to avoid having to
   1.533 +     * get it all over again later (and do another potentially expensive
   1.534 +     * RSA operation), copy it for later signature verification to use.
   1.535 +     */
   1.536 +    if (copy_key_for_signature != NULL)
   1.537 +	*copy_key_for_signature = PK11_ReferenceSymKey (bulkkey);
   1.538 +
   1.539 +    /*
   1.540 +     * Now we have the bulk encryption key (in bulkkey) and the
   1.541 +     * the algorithm (in enccinfo->contentEncAlg).  Using those,
   1.542 +     * create a decryption context.
   1.543 +     */
   1.544 +    decryptobj = sec_PKCS7CreateDecryptObject (bulkkey,
   1.545 +					       &(enccinfo->contentEncAlg));
   1.546 +
   1.547 +    /*
   1.548 +     * We are done with (this) bulkkey now.
   1.549 +     */
   1.550 +    PK11_FreeSymKey (bulkkey);
   1.551 +
   1.552 +    if (decryptobj == NULL) {
   1.553 +	p7dcx->error = PORT_GetError();
   1.554 +	PORT_SetError(0);
   1.555 +	goto no_decryption;
   1.556 +    }
   1.557 +
   1.558 +    SEC_ASN1DecoderSetFilterProc (p7dcx->dcx,
   1.559 +				  sec_pkcs7_decoder_filter,
   1.560 +				  p7dcx,
   1.561 +				  (PRBool)(p7dcx->cb != NULL));
   1.562 +
   1.563 +    p7dcx->worker.depth = depth;
   1.564 +    p7dcx->worker.decryptobj = decryptobj;
   1.565 +
   1.566 +    return SECSuccess;
   1.567 +
   1.568 +no_decryption:
   1.569 +    /*
   1.570 +     * For some reason (error set already, if appropriate), we cannot
   1.571 +     * decrypt the content.  I am not sure what exactly is the right
   1.572 +     * thing to do here; in some cases we want to just stop, and in
   1.573 +     * others we want to let the decoding finish even though we cannot
   1.574 +     * decrypt the content.  My current thinking is that if the caller
   1.575 +     * set up a content callback, then they are really interested in
   1.576 +     * getting (decrypted) content, and if they cannot they will want
   1.577 +     * to know about it.  However, if no callback was specified, then
   1.578 +     * maybe it is not important that the decryption failed.
   1.579 +     */
   1.580 +    if (p7dcx->cb != NULL)
   1.581 +	return SECFailure;
   1.582 +    else
   1.583 +	return SECSuccess;	/* Let the decoding continue. */
   1.584 +}
   1.585 +
   1.586 +
   1.587 +static SECStatus
   1.588 +sec_pkcs7_decoder_finish_decrypt (SEC_PKCS7DecoderContext *p7dcx,
   1.589 +				  PLArenaPool *poolp,
   1.590 +				  SEC_PKCS7EncryptedContentInfo *enccinfo)
   1.591 +{
   1.592 +    struct sec_pkcs7_decoder_worker *worker;
   1.593 +
   1.594 +    /*
   1.595 +     * XXX Handling nested contents would mean that there is a chain
   1.596 +     * of workers -- one per each level of content.  The following
   1.597 +     * would want to find the last worker in the chain.
   1.598 +     */
   1.599 +    worker = &(p7dcx->worker);
   1.600 +
   1.601 +    /*
   1.602 +     * If no decryption context, then we have nothing to do.
   1.603 +     */
   1.604 +    if (worker->decryptobj == NULL)
   1.605 +	return SECSuccess;
   1.606 +
   1.607 +    /*
   1.608 +     * No matter what happens after this, we want to stop filtering.
   1.609 +     * XXX If we handle nested contents, we only want to stop filtering
   1.610 +     * if we are finishing off the *last* worker.
   1.611 +     */
   1.612 +    SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
   1.613 +
   1.614 +    /*
   1.615 +     * Handle the last block.
   1.616 +     */
   1.617 +    sec_pkcs7_decoder_work_data (p7dcx, worker, NULL, 0, PR_TRUE);
   1.618 +
   1.619 +    /*
   1.620 +     * All done, destroy it.
   1.621 +     */
   1.622 +    sec_PKCS7DestroyDecryptObject (worker->decryptobj);
   1.623 +    worker->decryptobj = NULL;
   1.624 +
   1.625 +    return SECSuccess;
   1.626 +}
   1.627 +
   1.628 +
   1.629 +static void
   1.630 +sec_pkcs7_decoder_notify (void *arg, PRBool before, void *dest, int depth)
   1.631 +{
   1.632 +    SEC_PKCS7DecoderContext *p7dcx;
   1.633 +    SEC_PKCS7ContentInfo *cinfo;
   1.634 +    SEC_PKCS7SignedData *sigd;
   1.635 +    SEC_PKCS7EnvelopedData *envd;
   1.636 +    SEC_PKCS7SignedAndEnvelopedData *saed;
   1.637 +    SEC_PKCS7EncryptedData *encd;
   1.638 +    SEC_PKCS7DigestedData *digd;
   1.639 +    PRBool after;
   1.640 +    SECStatus rv;
   1.641 +
   1.642 +    /*
   1.643 +     * Just to make the code easier to read, create an "after" variable
   1.644 +     * that is equivalent to "not before".
   1.645 +     * (This used to be just the statement "after = !before", but that
   1.646 +     * causes a warning on the mac; to avoid that, we do it the long way.)
   1.647 +     */
   1.648 +    if (before)
   1.649 +	after = PR_FALSE;
   1.650 +    else
   1.651 +	after = PR_TRUE;
   1.652 +
   1.653 +    p7dcx = (SEC_PKCS7DecoderContext*)arg;
   1.654 +    cinfo = p7dcx->cinfo;
   1.655 +
   1.656 +    if (cinfo->contentTypeTag == NULL) {
   1.657 +	if (after && dest == &(cinfo->contentType))
   1.658 +	    cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
   1.659 +	return;
   1.660 +    }
   1.661 +
   1.662 +    switch (cinfo->contentTypeTag->offset) {
   1.663 +      case SEC_OID_PKCS7_SIGNED_DATA:
   1.664 +	sigd = cinfo->content.signedData;
   1.665 +	if (sigd == NULL)
   1.666 +	    break;
   1.667 +
   1.668 +	if (sigd->contentInfo.contentTypeTag == NULL) {
   1.669 +	    if (after && dest == &(sigd->contentInfo.contentType))
   1.670 +		sigd->contentInfo.contentTypeTag =
   1.671 +			SECOID_FindOID(&(sigd->contentInfo.contentType));
   1.672 +	    break;
   1.673 +	}
   1.674 +
   1.675 +	/*
   1.676 +	 * We only set up a filtering digest if the content is
   1.677 +	 * plain DATA; anything else needs more work because a
   1.678 +	 * second pass is required to produce a DER encoding from
   1.679 +	 * an input that can be BER encoded.  (This is a requirement
   1.680 +	 * of PKCS7 that is unfortunate, but there you have it.)
   1.681 +	 *
   1.682 +	 * XXX Also, since we stop here if this is not DATA, the
   1.683 +	 * inner content is not getting processed at all.  Someday
   1.684 +	 * we may want to fix that.
   1.685 +	 */
   1.686 +	if (sigd->contentInfo.contentTypeTag->offset != SEC_OID_PKCS7_DATA) {
   1.687 +	    /* XXX Set an error in p7dcx->error */
   1.688 +	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
   1.689 +	    break;
   1.690 +	}
   1.691 +
   1.692 +	/*
   1.693 +	 * Just before the content, we want to set up a digest context
   1.694 +	 * for each digest algorithm listed, and start a filter which
   1.695 +	 * will run all of the contents bytes through that digest.
   1.696 +	 */
   1.697 +	if (before && dest == &(sigd->contentInfo.content)) {
   1.698 +	    rv = sec_pkcs7_decoder_start_digests (p7dcx, depth,
   1.699 +						  sigd->digestAlgorithms);
   1.700 +	    if (rv != SECSuccess)
   1.701 +		SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
   1.702 +
   1.703 +	    break;
   1.704 +	}
   1.705 +
   1.706 +	/*
   1.707 +	 * XXX To handle nested types, here is where we would want
   1.708 +	 * to check for inner boundaries that need handling.
   1.709 +	 */
   1.710 +
   1.711 +	/*
   1.712 +	 * Are we done?
   1.713 +	 */
   1.714 +	if (after && dest == &(sigd->contentInfo.content)) {
   1.715 +	    /*
   1.716 +	     * Close out the digest contexts.  We ignore any error
   1.717 +	     * because we are stopping anyway; the error status left
   1.718 +	     * behind in p7dcx will be seen by outer functions.
   1.719 +	     */
   1.720 +	    (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp,
   1.721 +						     &(sigd->digests));
   1.722 +
   1.723 +	    /*
   1.724 +	     * XXX To handle nested contents, we would need to remove
   1.725 +	     * the worker from the chain (and free it).
   1.726 +	     */
   1.727 +
   1.728 +	    /*
   1.729 +	     * Stop notify.
   1.730 +	     */
   1.731 +	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
   1.732 +	}
   1.733 +	break;
   1.734 +
   1.735 +      case SEC_OID_PKCS7_ENVELOPED_DATA:
   1.736 +	envd = cinfo->content.envelopedData;
   1.737 +	if (envd == NULL)
   1.738 +	    break;
   1.739 +
   1.740 +	if (envd->encContentInfo.contentTypeTag == NULL) {
   1.741 +	    if (after && dest == &(envd->encContentInfo.contentType))
   1.742 +		envd->encContentInfo.contentTypeTag =
   1.743 +			SECOID_FindOID(&(envd->encContentInfo.contentType));
   1.744 +	    break;
   1.745 +	}
   1.746 +
   1.747 +	/*
   1.748 +	 * Just before the content, we want to set up a decryption
   1.749 +	 * context, and start a filter which will run all of the
   1.750 +	 * contents bytes through it to determine the plain content.
   1.751 +	 */
   1.752 +	if (before && dest == &(envd->encContentInfo.encContent)) {
   1.753 +	    rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth,
   1.754 +						  envd->recipientInfos,
   1.755 +						  &(envd->encContentInfo),
   1.756 +						  NULL);
   1.757 +	    if (rv != SECSuccess)
   1.758 +		SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
   1.759 +
   1.760 +	    break;
   1.761 +	}
   1.762 +
   1.763 +	/*
   1.764 +	 * Are we done?
   1.765 +	 */
   1.766 +	if (after && dest == &(envd->encContentInfo.encContent)) {
   1.767 +	    /*
   1.768 +	     * Close out the decryption context.  We ignore any error
   1.769 +	     * because we are stopping anyway; the error status left
   1.770 +	     * behind in p7dcx will be seen by outer functions.
   1.771 +	     */
   1.772 +	    (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp,
   1.773 +						     &(envd->encContentInfo));
   1.774 +
   1.775 +	    /*
   1.776 +	     * XXX To handle nested contents, we would need to remove
   1.777 +	     * the worker from the chain (and free it).
   1.778 +	     */
   1.779 +
   1.780 +	    /*
   1.781 +	     * Stop notify.
   1.782 +	     */
   1.783 +	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
   1.784 +	}
   1.785 +	break;
   1.786 +
   1.787 +      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   1.788 +	saed = cinfo->content.signedAndEnvelopedData;
   1.789 +	if (saed == NULL)
   1.790 +	    break;
   1.791 +
   1.792 +	if (saed->encContentInfo.contentTypeTag == NULL) {
   1.793 +	    if (after && dest == &(saed->encContentInfo.contentType))
   1.794 +		saed->encContentInfo.contentTypeTag =
   1.795 +			SECOID_FindOID(&(saed->encContentInfo.contentType));
   1.796 +	    break;
   1.797 +	}
   1.798 +
   1.799 +	/*
   1.800 +	 * Just before the content, we want to set up a decryption
   1.801 +	 * context *and* digest contexts, and start a filter which
   1.802 +	 * will run all of the contents bytes through both.
   1.803 +	 */
   1.804 +	if (before && dest == &(saed->encContentInfo.encContent)) {
   1.805 +	    rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth,
   1.806 +						  saed->recipientInfos,
   1.807 +						  &(saed->encContentInfo),
   1.808 +						  &(saed->sigKey));
   1.809 +	    if (rv == SECSuccess)
   1.810 +		rv = sec_pkcs7_decoder_start_digests (p7dcx, depth,
   1.811 +						      saed->digestAlgorithms);
   1.812 +	    if (rv != SECSuccess)
   1.813 +		SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
   1.814 +
   1.815 +	    break;
   1.816 +	}
   1.817 +
   1.818 +	/*
   1.819 +	 * Are we done?
   1.820 +	 */
   1.821 +	if (after && dest == &(saed->encContentInfo.encContent)) {
   1.822 +	    /*
   1.823 +	     * Close out the decryption and digests contexts.
   1.824 +	     * We ignore any errors because we are stopping anyway;
   1.825 +	     * the error status left behind in p7dcx will be seen by
   1.826 +	     * outer functions.
   1.827 +	     *
   1.828 +	     * Note that the decrypt stuff must be called first;
   1.829 +	     * it may have a last buffer to do which in turn has
   1.830 +	     * to be added to the digest.
   1.831 +	     */
   1.832 +	    (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp,
   1.833 +						     &(saed->encContentInfo));
   1.834 +	    (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp,
   1.835 +						     &(saed->digests));
   1.836 +
   1.837 +	    /*
   1.838 +	     * XXX To handle nested contents, we would need to remove
   1.839 +	     * the worker from the chain (and free it).
   1.840 +	     */
   1.841 +
   1.842 +	    /*
   1.843 +	     * Stop notify.
   1.844 +	     */
   1.845 +	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
   1.846 +	}
   1.847 +	break;
   1.848 +
   1.849 +      case SEC_OID_PKCS7_DIGESTED_DATA:
   1.850 +	digd = cinfo->content.digestedData;
   1.851 +	
   1.852 +	/* 
   1.853 +	 * XXX Want to do the digest or not?  Maybe future enhancement...
   1.854 +	 */
   1.855 +	if (before && dest == &(digd->contentInfo.content.data)) {
   1.856 +	    SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, sec_pkcs7_decoder_filter,
   1.857 +					  p7dcx,
   1.858 +					  (PRBool)(p7dcx->cb != NULL));
   1.859 +	    break;
   1.860 +	}
   1.861 +
   1.862 +	/*
   1.863 +	 * Are we done?
   1.864 +	 */
   1.865 +	if (after && dest == &(digd->contentInfo.content.data)) {
   1.866 +	    SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
   1.867 +	}
   1.868 +	break;
   1.869 +
   1.870 +      case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1.871 +	encd = cinfo->content.encryptedData;
   1.872 +
   1.873 +	/*
   1.874 +	 * XXX If the decryption key callback is set, we want to start
   1.875 +	 * the decryption.  If the callback is not set, we will treat the
   1.876 +	 * content as plain data, since we do not have the key.
   1.877 +	 *
   1.878 +	 * Is this the proper thing to do?
   1.879 +	 */
   1.880 +	if (before && dest == &(encd->encContentInfo.encContent)) {
   1.881 +	    /*
   1.882 +	     * Start the encryption process if the decryption key callback
   1.883 +	     * is present.  Otherwise, treat the content like plain data.
   1.884 +	     */
   1.885 +	    rv = SECSuccess;
   1.886 +	    if (p7dcx->dkcb != NULL) {
   1.887 +		rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, NULL,
   1.888 +						      &(encd->encContentInfo),
   1.889 +						      NULL);
   1.890 +	    }
   1.891 +
   1.892 +	    if (rv != SECSuccess)
   1.893 +		SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
   1.894 +		
   1.895 +	    break;
   1.896 +	}
   1.897 +
   1.898 +	/*
   1.899 +	 * Are we done?
   1.900 +	 */
   1.901 +	if (after && dest == &(encd->encContentInfo.encContent)) {
   1.902 +	    /*
   1.903 +	     * Close out the decryption context.  We ignore any error
   1.904 +	     * because we are stopping anyway; the error status left
   1.905 +	     * behind in p7dcx will be seen by outer functions.
   1.906 +	     */
   1.907 +	    (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp,
   1.908 +						     &(encd->encContentInfo));
   1.909 +
   1.910 +	    /*
   1.911 +	     * Stop notify.
   1.912 +	     */
   1.913 +	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
   1.914 +	}
   1.915 +	break;
   1.916 +
   1.917 +      case SEC_OID_PKCS7_DATA:
   1.918 +	/*
   1.919 +	 * If a output callback has been specified, we want to set the filter
   1.920 +	 * to call the callback.  This is taken care of in 
   1.921 +	 * sec_pkcs7_decoder_start_decrypt() or 
   1.922 +	 * sec_pkcs7_decoder_start_digests() for the other content types.
   1.923 +	 */ 
   1.924 +	
   1.925 +	if (before && dest == &(cinfo->content.data)) {
   1.926 +
   1.927 +	    /* 
   1.928 +	     * Set the filter proc up.
   1.929 +	     */
   1.930 +	    SEC_ASN1DecoderSetFilterProc (p7dcx->dcx,
   1.931 +					  sec_pkcs7_decoder_filter,
   1.932 +					  p7dcx,
   1.933 +					  (PRBool)(p7dcx->cb != NULL));
   1.934 +	    break;
   1.935 +	}
   1.936 +
   1.937 +	if (after && dest == &(cinfo->content.data)) {
   1.938 +	    /*
   1.939 +	     * Time to clean up after ourself, stop the Notify and Filter
   1.940 +	     * procedures.
   1.941 +	     */
   1.942 +	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
   1.943 +	    SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
   1.944 +	}
   1.945 +	break;
   1.946 +
   1.947 +      default:
   1.948 +	SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
   1.949 +	break;
   1.950 +    }
   1.951 +}
   1.952 +
   1.953 +
   1.954 +SEC_PKCS7DecoderContext *
   1.955 +SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback cb, void *cb_arg,
   1.956 +		      SECKEYGetPasswordKey pwfn, void *pwfn_arg,
   1.957 +		      SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, 
   1.958 +		      void *decrypt_key_cb_arg,
   1.959 +		      SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb)
   1.960 +{
   1.961 +    SEC_PKCS7DecoderContext *p7dcx;
   1.962 +    SEC_ASN1DecoderContext *dcx;
   1.963 +    SEC_PKCS7ContentInfo *cinfo;
   1.964 +    PLArenaPool *poolp;
   1.965 +
   1.966 +    poolp = PORT_NewArena (1024);		/* XXX what is right value? */
   1.967 +    if (poolp == NULL)
   1.968 +	return NULL;
   1.969 +
   1.970 +    cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo));
   1.971 +    if (cinfo == NULL) {
   1.972 +	PORT_FreeArena (poolp, PR_FALSE);
   1.973 +	return NULL;
   1.974 +    }
   1.975 +
   1.976 +    cinfo->poolp = poolp;
   1.977 +    cinfo->pwfn = pwfn;
   1.978 +    cinfo->pwfn_arg = pwfn_arg;
   1.979 +    cinfo->created = PR_FALSE;
   1.980 +    cinfo->refCount = 1;
   1.981 +
   1.982 +    p7dcx = 
   1.983 +      (SEC_PKCS7DecoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7DecoderContext));
   1.984 +    if (p7dcx == NULL) {
   1.985 +	PORT_FreeArena (poolp, PR_FALSE);
   1.986 +	return NULL;
   1.987 +    }
   1.988 +
   1.989 +    p7dcx->tmp_poolp = PORT_NewArena (1024);	/* XXX what is right value? */
   1.990 +    if (p7dcx->tmp_poolp == NULL) {
   1.991 +	PORT_Free (p7dcx);
   1.992 +	PORT_FreeArena (poolp, PR_FALSE);
   1.993 +	return NULL;
   1.994 +    }
   1.995 +
   1.996 +    dcx = SEC_ASN1DecoderStart (poolp, cinfo, sec_PKCS7ContentInfoTemplate);
   1.997 +    if (dcx == NULL) {
   1.998 +	PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE);
   1.999 +	PORT_Free (p7dcx);
  1.1000 +	PORT_FreeArena (poolp, PR_FALSE);
  1.1001 +	return NULL;
  1.1002 +    }
  1.1003 +
  1.1004 +    SEC_ASN1DecoderSetNotifyProc (dcx, sec_pkcs7_decoder_notify, p7dcx);
  1.1005 +
  1.1006 +    p7dcx->dcx = dcx;
  1.1007 +    p7dcx->cinfo = cinfo;
  1.1008 +    p7dcx->cb = cb;
  1.1009 +    p7dcx->cb_arg = cb_arg;
  1.1010 +    p7dcx->pwfn = pwfn;
  1.1011 +    p7dcx->pwfn_arg = pwfn_arg;
  1.1012 +    p7dcx->dkcb = decrypt_key_cb;
  1.1013 +    p7dcx->dkcb_arg = decrypt_key_cb_arg;
  1.1014 +    p7dcx->decrypt_allowed_cb = decrypt_allowed_cb;
  1.1015 +
  1.1016 +    return p7dcx;
  1.1017 +}
  1.1018 +
  1.1019 +
  1.1020 +/*
  1.1021 + * Do the next chunk of PKCS7 decoding.  If there is a problem, set
  1.1022 + * an error and return a failure status.  Note that in the case of
  1.1023 + * an error, this routine is still prepared to be called again and
  1.1024 + * again in case that is the easiest route for our caller to take.
  1.1025 + * We simply detect it and do not do anything except keep setting
  1.1026 + * that error in case our caller has not noticed it yet...
  1.1027 + */
  1.1028 +SECStatus
  1.1029 +SEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx,
  1.1030 +		       const char *buf, unsigned long len)
  1.1031 +{
  1.1032 +    if (p7dcx->cinfo != NULL && p7dcx->dcx != NULL) { 
  1.1033 +	PORT_Assert (p7dcx->error == 0);
  1.1034 +	if (p7dcx->error == 0) {
  1.1035 +	    if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) {
  1.1036 +		p7dcx->error = PORT_GetError();
  1.1037 +		PORT_Assert (p7dcx->error);
  1.1038 +		if (p7dcx->error == 0)
  1.1039 +		    p7dcx->error = -1;
  1.1040 +	    }
  1.1041 +	}
  1.1042 +    }
  1.1043 +
  1.1044 +    if (p7dcx->error) {
  1.1045 +	if (p7dcx->dcx != NULL) {
  1.1046 +	    (void) SEC_ASN1DecoderFinish (p7dcx->dcx);
  1.1047 +	    p7dcx->dcx = NULL;
  1.1048 +	}
  1.1049 +	if (p7dcx->cinfo != NULL) {
  1.1050 +	    SEC_PKCS7DestroyContentInfo (p7dcx->cinfo);
  1.1051 +	    p7dcx->cinfo = NULL;
  1.1052 +	}
  1.1053 +	PORT_SetError (p7dcx->error);
  1.1054 +	return SECFailure;
  1.1055 +    }
  1.1056 +
  1.1057 +    return SECSuccess;
  1.1058 +}
  1.1059 +
  1.1060 +
  1.1061 +SEC_PKCS7ContentInfo *
  1.1062 +SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx)
  1.1063 +{
  1.1064 +    SEC_PKCS7ContentInfo *cinfo;
  1.1065 +
  1.1066 +    cinfo = p7dcx->cinfo;
  1.1067 +    if (p7dcx->dcx != NULL) {
  1.1068 +	if (SEC_ASN1DecoderFinish (p7dcx->dcx) != SECSuccess) {
  1.1069 +	    SEC_PKCS7DestroyContentInfo (cinfo);
  1.1070 +	    cinfo = NULL;
  1.1071 +	}
  1.1072 +    }
  1.1073 +    /* free any NSS data structures */
  1.1074 +    if (p7dcx->worker.decryptobj) {
  1.1075 +        sec_PKCS7DestroyDecryptObject (p7dcx->worker.decryptobj);
  1.1076 +    }
  1.1077 +    PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE);
  1.1078 +    PORT_Free (p7dcx);
  1.1079 +    return cinfo;
  1.1080 +}
  1.1081 +
  1.1082 +
  1.1083 +SEC_PKCS7ContentInfo *
  1.1084 +SEC_PKCS7DecodeItem(SECItem *p7item,
  1.1085 +		    SEC_PKCS7DecoderContentCallback cb, void *cb_arg,
  1.1086 +		    SECKEYGetPasswordKey pwfn, void *pwfn_arg,
  1.1087 +		    SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, 
  1.1088 +		    void *decrypt_key_cb_arg,
  1.1089 +		    SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb)
  1.1090 +{
  1.1091 +    SEC_PKCS7DecoderContext *p7dcx;
  1.1092 +
  1.1093 +    p7dcx = SEC_PKCS7DecoderStart(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb,
  1.1094 +				  decrypt_key_cb_arg, decrypt_allowed_cb);
  1.1095 +    if (!p7dcx) {
  1.1096 +        /* error code is set */
  1.1097 +        return NULL;
  1.1098 +    }
  1.1099 +    (void) SEC_PKCS7DecoderUpdate(p7dcx, (char *) p7item->data, p7item->len);
  1.1100 +    return SEC_PKCS7DecoderFinish(p7dcx);
  1.1101 +}
  1.1102 +
  1.1103 +/*
  1.1104 + * Abort the ASN.1 stream. Used by pkcs 12
  1.1105 + */
  1.1106 +void
  1.1107 +SEC_PKCS7DecoderAbort(SEC_PKCS7DecoderContext *p7dcx, int error)
  1.1108 +{
  1.1109 +    PORT_Assert(p7dcx);
  1.1110 +    SEC_ASN1DecoderAbort(p7dcx->dcx, error);
  1.1111 +}
  1.1112 +
  1.1113 +
  1.1114 +/*
  1.1115 + * If the thing contains any certs or crls return true; false otherwise.
  1.1116 + */
  1.1117 +PRBool
  1.1118 +SEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo)
  1.1119 +{
  1.1120 +    SECOidTag kind;
  1.1121 +    SECItem **certs;
  1.1122 +    CERTSignedCrl **crls;
  1.1123 +
  1.1124 +    kind = SEC_PKCS7ContentType (cinfo);
  1.1125 +    switch (kind) {
  1.1126 +      default:
  1.1127 +      case SEC_OID_PKCS7_DATA:
  1.1128 +      case SEC_OID_PKCS7_DIGESTED_DATA:
  1.1129 +      case SEC_OID_PKCS7_ENVELOPED_DATA:
  1.1130 +      case SEC_OID_PKCS7_ENCRYPTED_DATA:
  1.1131 +	return PR_FALSE;
  1.1132 +      case SEC_OID_PKCS7_SIGNED_DATA:
  1.1133 +	certs = cinfo->content.signedData->rawCerts;
  1.1134 +	crls = cinfo->content.signedData->crls;
  1.1135 +	break;
  1.1136 +      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
  1.1137 +	certs = cinfo->content.signedAndEnvelopedData->rawCerts;
  1.1138 +	crls = cinfo->content.signedAndEnvelopedData->crls;
  1.1139 +	break;
  1.1140 +    }
  1.1141 +
  1.1142 +    /*
  1.1143 +     * I know this could be collapsed, but I was in a mood to be explicit.
  1.1144 +     */
  1.1145 +    if (certs != NULL && certs[0] != NULL)
  1.1146 +	return PR_TRUE;
  1.1147 +    else if (crls != NULL && crls[0] != NULL)
  1.1148 +	return PR_TRUE;
  1.1149 +    else
  1.1150 +	return PR_FALSE;
  1.1151 +}
  1.1152 +
  1.1153 +/* return the content length...could use GetContent, however we
  1.1154 + * need the encrypted content length 
  1.1155 + */
  1.1156 +PRBool
  1.1157 +SEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen)
  1.1158 +{
  1.1159 +    SECItem *item = NULL;
  1.1160 +
  1.1161 +    if(cinfo == NULL) {
  1.1162 +	return PR_TRUE;
  1.1163 +    }
  1.1164 +
  1.1165 +    switch(SEC_PKCS7ContentType(cinfo)) 
  1.1166 +    {
  1.1167 +	case SEC_OID_PKCS7_DATA:
  1.1168 +	    item = cinfo->content.data;
  1.1169 +	    break;
  1.1170 +	case SEC_OID_PKCS7_ENCRYPTED_DATA:
  1.1171 +	    item = &cinfo->content.encryptedData->encContentInfo.encContent;
  1.1172 +	    break;
  1.1173 +	default:
  1.1174 +	    /* add other types */
  1.1175 +	    return PR_FALSE;
  1.1176 +    }
  1.1177 +
  1.1178 +    if(!item) {
  1.1179 +	return PR_TRUE;
  1.1180 +    } else if(item->len <= minLen) {
  1.1181 +	return PR_TRUE;
  1.1182 +    }
  1.1183 +
  1.1184 +    return PR_FALSE;
  1.1185 +}
  1.1186 +
  1.1187 +
  1.1188 +PRBool
  1.1189 +SEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo)
  1.1190 +{
  1.1191 +    SECOidTag kind;
  1.1192 +
  1.1193 +    kind = SEC_PKCS7ContentType (cinfo);
  1.1194 +    switch (kind) {
  1.1195 +      default:
  1.1196 +      case SEC_OID_PKCS7_DATA:
  1.1197 +      case SEC_OID_PKCS7_DIGESTED_DATA:
  1.1198 +      case SEC_OID_PKCS7_SIGNED_DATA:
  1.1199 +	return PR_FALSE;
  1.1200 +      case SEC_OID_PKCS7_ENCRYPTED_DATA:
  1.1201 +      case SEC_OID_PKCS7_ENVELOPED_DATA:
  1.1202 +      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
  1.1203 +	return PR_TRUE;
  1.1204 +    }
  1.1205 +}
  1.1206 +
  1.1207 +
  1.1208 +/*
  1.1209 + * If the PKCS7 content has a signature (not just *could* have a signature)
  1.1210 + * return true; false otherwise.  This can/should be called before calling
  1.1211 + * VerifySignature, which will always indicate failure if no signature is
  1.1212 + * present, but that does not mean there even was a signature!
  1.1213 + * Note that the content itself can be empty (detached content was sent
  1.1214 + * another way); it is the presence of the signature that matters.
  1.1215 + */
  1.1216 +PRBool
  1.1217 +SEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo)
  1.1218 +{
  1.1219 +    SECOidTag kind;
  1.1220 +    SEC_PKCS7SignerInfo **signerinfos;
  1.1221 +
  1.1222 +    kind = SEC_PKCS7ContentType (cinfo);
  1.1223 +    switch (kind) {
  1.1224 +      default:
  1.1225 +      case SEC_OID_PKCS7_DATA:
  1.1226 +      case SEC_OID_PKCS7_DIGESTED_DATA:
  1.1227 +      case SEC_OID_PKCS7_ENVELOPED_DATA:
  1.1228 +      case SEC_OID_PKCS7_ENCRYPTED_DATA:
  1.1229 +	return PR_FALSE;
  1.1230 +      case SEC_OID_PKCS7_SIGNED_DATA:
  1.1231 +	signerinfos = cinfo->content.signedData->signerInfos;
  1.1232 +	break;
  1.1233 +      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
  1.1234 +	signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
  1.1235 +	break;
  1.1236 +    }
  1.1237 +
  1.1238 +    /*
  1.1239 +     * I know this could be collapsed; but I kind of think it will get
  1.1240 +     * more complicated before I am finished, so...
  1.1241 +     */
  1.1242 +    if (signerinfos != NULL && signerinfos[0] != NULL)
  1.1243 +	return PR_TRUE;
  1.1244 +    else
  1.1245 +	return PR_FALSE;
  1.1246 +}
  1.1247 +
  1.1248 +
  1.1249 +/*
  1.1250 + * sec_pkcs7_verify_signature
  1.1251 + *
  1.1252 + *	Look at a PKCS7 contentInfo and check if the signature is good.
  1.1253 + *	The digest was either calculated earlier (and is stored in the
  1.1254 + *	contentInfo itself) or is passed in via "detached_digest".
  1.1255 + *
  1.1256 + *	The verification checks that the signing cert is valid and trusted
  1.1257 + *	for the purpose specified by "certusage" at
  1.1258 + * 	- "*atTime" if "atTime" is not null, or
  1.1259 + * 	- the signing time if the signing time is available in "cinfo", or
  1.1260 + *	- the current time (as returned by PR_Now).
  1.1261 + *
  1.1262 + *	In addition, if "keepcerts" is true, add any new certificates found
  1.1263 + *	into our local database.
  1.1264 + *
  1.1265 + * XXX Each place which returns PR_FALSE should be sure to have a good
  1.1266 + * error set for inspection by the caller.  Alternatively, we could create
  1.1267 + * an enumeration of success and each type of failure and return that
  1.1268 + * instead of a boolean.  For now, the default in a bad situation is to
  1.1269 + * set the error to SEC_ERROR_PKCS7_BAD_SIGNATURE.  But this should be
  1.1270 + * reviewed; better (more specific) errors should be possible (to distinguish
  1.1271 + * a signature failure from a badly-formed pkcs7 signedData, for example).
  1.1272 + * Some of the errors should probably just be SEC_ERROR_BAD_SIGNATURE,
  1.1273 + * but that has a less helpful error string associated with it right now;
  1.1274 + * if/when that changes, review and change these as needed.
  1.1275 + *
  1.1276 + * XXX This is broken wrt signedAndEnvelopedData.  In that case, the
  1.1277 + * message digest is doubly encrypted -- first encrypted with the signer
  1.1278 + * private key but then again encrypted with the bulk encryption key used
  1.1279 + * to encrypt the content.  So before we can pass the digest to VerifyDigest,
  1.1280 + * we need to decrypt it with the bulk encryption key.  Also, in this case,
  1.1281 + * there should be NO authenticatedAttributes (signerinfo->authAttr should
  1.1282 + * be NULL).
  1.1283 + */
  1.1284 +static PRBool
  1.1285 +sec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo,
  1.1286 +			   SECCertUsage certusage,
  1.1287 +			   const SECItem *detached_digest,
  1.1288 +			   HASH_HashType digest_type,
  1.1289 +			   PRBool keepcerts,
  1.1290 +			   const PRTime *atTime)
  1.1291 +{
  1.1292 +    SECAlgorithmID **digestalgs, *bulkid;
  1.1293 +    const SECItem *digest;
  1.1294 +    SECItem **digests;
  1.1295 +    SECItem **rawcerts;
  1.1296 +    CERTSignedCrl **crls;
  1.1297 +    SEC_PKCS7SignerInfo **signerinfos, *signerinfo;
  1.1298 +    CERTCertificate *cert, **certs;
  1.1299 +    PRBool goodsig;
  1.1300 +    CERTCertDBHandle *certdb, *defaultdb; 
  1.1301 +    SECOidTag encTag,digestTag;
  1.1302 +    HASH_HashType found_type;
  1.1303 +    int i, certcount;
  1.1304 +    SECKEYPublicKey *publickey;
  1.1305 +    SECItem *content_type;
  1.1306 +    PK11SymKey *sigkey;
  1.1307 +    SECItem *encoded_stime;
  1.1308 +    PRTime stime;
  1.1309 +    PRTime verificationTime;
  1.1310 +    SECStatus rv;
  1.1311 +
  1.1312 +    /*
  1.1313 +     * Everything needed in order to "goto done" safely.
  1.1314 +     */
  1.1315 +    goodsig = PR_FALSE;
  1.1316 +    certcount = 0;
  1.1317 +    cert = NULL;
  1.1318 +    certs = NULL;
  1.1319 +    certdb = NULL;
  1.1320 +    defaultdb = CERT_GetDefaultCertDB();
  1.1321 +    publickey = NULL;
  1.1322 +
  1.1323 +    if (! SEC_PKCS7ContentIsSigned(cinfo)) {
  1.1324 +	PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1325 +	goto done;
  1.1326 +    }
  1.1327 +
  1.1328 +    PORT_Assert (cinfo->contentTypeTag != NULL);
  1.1329 +
  1.1330 +    switch (cinfo->contentTypeTag->offset) {
  1.1331 +      default:
  1.1332 +      case SEC_OID_PKCS7_DATA:
  1.1333 +      case SEC_OID_PKCS7_DIGESTED_DATA:
  1.1334 +      case SEC_OID_PKCS7_ENVELOPED_DATA:
  1.1335 +      case SEC_OID_PKCS7_ENCRYPTED_DATA:
  1.1336 +	/* Could only get here if SEC_PKCS7ContentIsSigned is broken. */
  1.1337 +	PORT_Assert (0);
  1.1338 +      case SEC_OID_PKCS7_SIGNED_DATA:
  1.1339 +	{
  1.1340 +	    SEC_PKCS7SignedData *sdp;
  1.1341 +
  1.1342 +	    sdp = cinfo->content.signedData;
  1.1343 +	    digestalgs = sdp->digestAlgorithms;
  1.1344 +	    digests = sdp->digests;
  1.1345 +	    rawcerts = sdp->rawCerts;
  1.1346 +	    crls = sdp->crls;
  1.1347 +	    signerinfos = sdp->signerInfos;
  1.1348 +	    content_type = &(sdp->contentInfo.contentType);
  1.1349 +	    sigkey = NULL;
  1.1350 +	    bulkid = NULL;
  1.1351 +	}
  1.1352 +	break;
  1.1353 +      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
  1.1354 +	{
  1.1355 +	    SEC_PKCS7SignedAndEnvelopedData *saedp;
  1.1356 +
  1.1357 +	    saedp = cinfo->content.signedAndEnvelopedData;
  1.1358 +	    digestalgs = saedp->digestAlgorithms;
  1.1359 +	    digests = saedp->digests;
  1.1360 +	    rawcerts = saedp->rawCerts;
  1.1361 +	    crls = saedp->crls;
  1.1362 +	    signerinfos = saedp->signerInfos;
  1.1363 +	    content_type = &(saedp->encContentInfo.contentType);
  1.1364 +	    sigkey = saedp->sigKey;
  1.1365 +	    bulkid = &(saedp->encContentInfo.contentEncAlg);
  1.1366 +	}
  1.1367 +	break;
  1.1368 +    }
  1.1369 +
  1.1370 +    if ((signerinfos == NULL) || (signerinfos[0] == NULL)) {
  1.1371 +	PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1372 +	goto done;
  1.1373 +    }
  1.1374 +
  1.1375 +    /*
  1.1376 +     * XXX Need to handle multiple signatures; checking them is easy,
  1.1377 +     * but what should be the semantics here (like, return value)?
  1.1378 +     */
  1.1379 +    if (signerinfos[1] != NULL) {
  1.1380 +	PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1381 +	goto done;
  1.1382 +    }
  1.1383 +
  1.1384 +    signerinfo = signerinfos[0];
  1.1385 +
  1.1386 +    /*
  1.1387 +     * XXX I would like to just pass the issuerAndSN, along with the rawcerts
  1.1388 +     * and crls, to some function that did all of this certificate stuff
  1.1389 +     * (open/close the database if necessary, verifying the certs, etc.)
  1.1390 +     * and gave me back a cert pointer if all was good.
  1.1391 +     */
  1.1392 +    certdb = defaultdb;
  1.1393 +    if (certdb == NULL) {
  1.1394 +	goto done;
  1.1395 +    }
  1.1396 +
  1.1397 +    certcount = 0;
  1.1398 +    if (rawcerts != NULL) {
  1.1399 +	for (; rawcerts[certcount] != NULL; certcount++) {
  1.1400 +	    /* just counting */
  1.1401 +	}
  1.1402 +    }
  1.1403 +
  1.1404 +    /*
  1.1405 +     * Note that the result of this is that each cert in "certs"
  1.1406 +     * needs to be destroyed.
  1.1407 +     */
  1.1408 +    rv = CERT_ImportCerts(certdb, certusage, certcount, rawcerts, &certs,
  1.1409 +			  keepcerts, PR_FALSE, NULL);
  1.1410 +    if ( rv != SECSuccess ) {
  1.1411 +	goto done;
  1.1412 +    }
  1.1413 +
  1.1414 +    /*
  1.1415 +     * This cert will also need to be freed, but since we save it
  1.1416 +     * in signerinfo for later, we do not want to destroy it when
  1.1417 +     * we leave this function -- we let the clean-up of the entire
  1.1418 +     * cinfo structure later do the destroy of this cert.
  1.1419 +     */
  1.1420 +    cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->issuerAndSN);
  1.1421 +    if (cert == NULL) {
  1.1422 +	goto done;
  1.1423 +    }
  1.1424 +
  1.1425 +    signerinfo->cert = cert;
  1.1426 +
  1.1427 +    /*
  1.1428 +     * Get and convert the signing time; if available, it will be used
  1.1429 +     * both on the cert verification and for importing the sender
  1.1430 +     * email profile.
  1.1431 +     */
  1.1432 +    encoded_stime = SEC_PKCS7GetSigningTime (cinfo);
  1.1433 +    if (encoded_stime != NULL) {
  1.1434 +	if (DER_DecodeTimeChoice (&stime, encoded_stime) != SECSuccess)
  1.1435 +	    encoded_stime = NULL;	/* conversion failed, so pretend none */
  1.1436 +    }
  1.1437 +
  1.1438 +    /*
  1.1439 +     * XXX  This uses the signing time, if available.  Additionally, we
  1.1440 +     * might want to, if there is no signing time, get the message time
  1.1441 +     * from the mail header itself, and use that.  That would require
  1.1442 +     * a change to our interface though, and for S/MIME callers to pass
  1.1443 +     * in a time (and for non-S/MIME callers to pass in nothing, or
  1.1444 +     * maybe make them pass in the current time, always?).
  1.1445 +     */
  1.1446 +    if (atTime) {
  1.1447 +	verificationTime = *atTime;
  1.1448 +    } else if (encoded_stime != NULL) {
  1.1449 +	verificationTime = stime;
  1.1450 +    } else {
  1.1451 +	verificationTime = PR_Now();
  1.1452 +    }
  1.1453 +    if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, verificationTime,
  1.1454 +			 cinfo->pwfn_arg, NULL) != SECSuccess)
  1.1455 +	{
  1.1456 +	/*
  1.1457 +	 * XXX Give the user an option to check the signature anyway?
  1.1458 +	 * If we want to do this, need to give a way to leave and display
  1.1459 +	 * some dialog and get the answer and come back through (or do
  1.1460 +	 * the rest of what we do below elsewhere, maybe by putting it
  1.1461 +	 * in a function that we call below and could call from a dialog
  1.1462 +	 * finish handler).
  1.1463 +	 */
  1.1464 +	goto savecert;
  1.1465 +    }
  1.1466 +
  1.1467 +    publickey = CERT_ExtractPublicKey (cert);
  1.1468 +    if (publickey == NULL)
  1.1469 +	goto done;
  1.1470 +
  1.1471 +    /*
  1.1472 +     * XXX No!  If digests is empty, see if we can create it now by
  1.1473 +     * digesting the contents.  This is necessary if we want to allow
  1.1474 +     * somebody to do a simple decode (without filtering, etc.) and
  1.1475 +     * then later call us here to do the verification.
  1.1476 +     * OR, we can just specify that the interface to this routine
  1.1477 +     * *requires* that the digest(s) be done before calling and either
  1.1478 +     * stashed in the struct itself or passed in explicitly (as would
  1.1479 +     * be done for detached contents).
  1.1480 +     */
  1.1481 +    if ((digests == NULL || digests[0] == NULL)
  1.1482 +	&& (detached_digest == NULL || detached_digest->data == NULL))
  1.1483 +	goto done;
  1.1484 +
  1.1485 +    /*
  1.1486 +     * Find and confirm digest algorithm.
  1.1487 +     */
  1.1488 +    digestTag = SECOID_FindOIDTag(&(signerinfo->digestAlg.algorithm));
  1.1489 +
  1.1490 +    /* make sure we understand the digest type first */
  1.1491 +    found_type = HASH_GetHashTypeByOidTag(digestTag);
  1.1492 +    if ((digestTag == SEC_OID_UNKNOWN) || (found_type == HASH_AlgNULL)) {
  1.1493 +	PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1494 +	goto done;
  1.1495 +    }
  1.1496 +
  1.1497 +    if (detached_digest != NULL) {
  1.1498 +	unsigned int hashLen     = HASH_ResultLen(found_type);
  1.1499 +
  1.1500 +	if (digest_type != found_type || 
  1.1501 +	    detached_digest->len != hashLen) {
  1.1502 +	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1503 +	    goto done;
  1.1504 +	}
  1.1505 +	digest = detached_digest;
  1.1506 +    } else {
  1.1507 +	PORT_Assert (digestalgs != NULL && digestalgs[0] != NULL);
  1.1508 +	if (digestalgs == NULL || digestalgs[0] == NULL) {
  1.1509 +	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1510 +	    goto done;
  1.1511 +	}
  1.1512 +
  1.1513 +	/*
  1.1514 +	 * pick digest matching signerinfo->digestAlg from digests
  1.1515 +	 */
  1.1516 +	for (i = 0; digestalgs[i] != NULL; i++) {
  1.1517 +	    if (SECOID_FindOIDTag(&(digestalgs[i]->algorithm)) == digestTag)
  1.1518 +		break;
  1.1519 +	}
  1.1520 +	if (digestalgs[i] == NULL) {
  1.1521 +	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1522 +	    goto done;
  1.1523 +	}
  1.1524 +
  1.1525 +	digest = digests[i];
  1.1526 +    }
  1.1527 +
  1.1528 +    encTag = SECOID_FindOIDTag(&(signerinfo->digestEncAlg.algorithm));
  1.1529 +    if (encTag == SEC_OID_UNKNOWN) {
  1.1530 +	PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1531 +	goto done;
  1.1532 +    }
  1.1533 +
  1.1534 +    if (signerinfo->authAttr != NULL) {
  1.1535 +	SEC_PKCS7Attribute *attr;
  1.1536 +	SECItem *value;
  1.1537 +	SECItem encoded_attrs;
  1.1538 +
  1.1539 +	/*
  1.1540 +	 * We have a sigkey only for signedAndEnvelopedData, which is
  1.1541 +	 * not supposed to have any authenticated attributes.
  1.1542 +	 */
  1.1543 +	if (sigkey != NULL) {
  1.1544 +	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1545 +	    goto done;
  1.1546 +	}
  1.1547 +
  1.1548 +	/*
  1.1549 +	 * PKCS #7 says that if there are any authenticated attributes,
  1.1550 +	 * then there must be one for content type which matches the
  1.1551 +	 * content type of the content being signed, and there must
  1.1552 +	 * be one for message digest which matches our message digest.
  1.1553 +	 * So check these things first.
  1.1554 +	 * XXX Might be nice to have a compare-attribute-value function
  1.1555 +	 * which could collapse the following nicely.
  1.1556 +	 */
  1.1557 +	attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
  1.1558 +				       SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
  1.1559 +	value = sec_PKCS7AttributeValue (attr);
  1.1560 +	if (value == NULL || value->len != content_type->len) {
  1.1561 +	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1562 +	    goto done;
  1.1563 +	}
  1.1564 +	if (PORT_Memcmp (value->data, content_type->data, value->len) != 0) {
  1.1565 +	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1566 +	    goto done;
  1.1567 +	}
  1.1568 +
  1.1569 +	attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
  1.1570 +				       SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
  1.1571 +	value = sec_PKCS7AttributeValue (attr);
  1.1572 +	if (value == NULL || value->len != digest->len) {
  1.1573 +	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1574 +	    goto done;
  1.1575 +	}
  1.1576 +	if (PORT_Memcmp (value->data, digest->data, value->len) != 0) {
  1.1577 +	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1578 +	    goto done;
  1.1579 +	}
  1.1580 +
  1.1581 +	/*
  1.1582 +	 * Okay, we met the constraints of the basic attributes.
  1.1583 +	 * Now check the signature, which is based on a digest of
  1.1584 +	 * the DER-encoded authenticated attributes.  So, first we
  1.1585 +	 * encode and then we digest/verify.
  1.1586 +	 */
  1.1587 +	encoded_attrs.data = NULL;
  1.1588 +	encoded_attrs.len = 0;
  1.1589 +	if (sec_PKCS7EncodeAttributes (NULL, &encoded_attrs,
  1.1590 +				       &(signerinfo->authAttr)) == NULL)
  1.1591 +	    goto done;
  1.1592 +
  1.1593 +	if (encoded_attrs.data == NULL || encoded_attrs.len == 0) {
  1.1594 +	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1595 +	    goto done;
  1.1596 +	}
  1.1597 +
  1.1598 +	goodsig = (PRBool)(VFY_VerifyDataDirect(encoded_attrs.data, 
  1.1599 +				   encoded_attrs.len,
  1.1600 +				   publickey, &(signerinfo->encDigest),
  1.1601 +				   encTag, digestTag, NULL,
  1.1602 +				   cinfo->pwfn_arg) == SECSuccess);
  1.1603 +	PORT_Free (encoded_attrs.data);
  1.1604 +    } else {
  1.1605 +	SECItem *sig;
  1.1606 +	SECItem holder;
  1.1607 +	SECStatus rv;
  1.1608 +
  1.1609 +	/*
  1.1610 +	 * No authenticated attributes.
  1.1611 +	 * The signature is based on the plain message digest.
  1.1612 +	 */
  1.1613 +
  1.1614 +	sig = &(signerinfo->encDigest);
  1.1615 +	if (sig->len == 0) {		/* bad signature */
  1.1616 +	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1617 +	    goto done;
  1.1618 +	}
  1.1619 +
  1.1620 +	if (sigkey != NULL) {
  1.1621 +	    sec_PKCS7CipherObject *decryptobj;
  1.1622 +	    unsigned int buflen;
  1.1623 +
  1.1624 +	    /*
  1.1625 +	     * For signedAndEnvelopedData, we first must decrypt the encrypted
  1.1626 +	     * digest with the bulk encryption key.  The result is the normal
  1.1627 +	     * encrypted digest (aka the signature).
  1.1628 +	     */
  1.1629 +	    decryptobj = sec_PKCS7CreateDecryptObject (sigkey, bulkid);
  1.1630 +	    if (decryptobj == NULL)
  1.1631 +		goto done;
  1.1632 +
  1.1633 +	    buflen = sec_PKCS7DecryptLength (decryptobj, sig->len, PR_TRUE);
  1.1634 +	    PORT_Assert (buflen);
  1.1635 +	    if (buflen == 0) {		/* something is wrong */
  1.1636 +		sec_PKCS7DestroyDecryptObject (decryptobj);
  1.1637 +		goto done;
  1.1638 +	    }
  1.1639 +
  1.1640 +	    holder.data = (unsigned char*)PORT_Alloc (buflen);
  1.1641 +	    if (holder.data == NULL) {
  1.1642 +		sec_PKCS7DestroyDecryptObject (decryptobj);
  1.1643 +		goto done;
  1.1644 +	    }
  1.1645 +
  1.1646 +	    rv = sec_PKCS7Decrypt (decryptobj, holder.data, &holder.len, buflen,
  1.1647 +				   sig->data, sig->len, PR_TRUE);
  1.1648 +	    sec_PKCS7DestroyDecryptObject (decryptobj);
  1.1649 +	    if (rv != SECSuccess) {
  1.1650 +		goto done;
  1.1651 +	    }
  1.1652 +
  1.1653 +	    sig = &holder;
  1.1654 +	}
  1.1655 +
  1.1656 +	goodsig = (PRBool)(VFY_VerifyDigestDirect(digest, publickey, sig,
  1.1657 +				     encTag, digestTag, cinfo->pwfn_arg)
  1.1658 +                            == SECSuccess);
  1.1659 +
  1.1660 +	if (sigkey != NULL) {
  1.1661 +	    PORT_Assert (sig == &holder);
  1.1662 +	    PORT_ZFree (holder.data, holder.len);
  1.1663 +	}
  1.1664 +    }
  1.1665 +
  1.1666 +    if (! goodsig) {
  1.1667 +	/*
  1.1668 +	 * XXX Change the generic error into our specific one, because
  1.1669 +	 * in that case we get a better explanation out of the Security
  1.1670 +	 * Advisor.  This is really a bug in our error strings (the
  1.1671 +	 * "generic" error has a lousy/wrong message associated with it
  1.1672 +	 * which assumes the signature verification was done for the
  1.1673 +	 * purposes of checking the issuer signature on a certificate)
  1.1674 +	 * but this is at least an easy workaround and/or in the
  1.1675 +	 * Security Advisor, which specifically checks for the error
  1.1676 +	 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
  1.1677 +	 * in that case but does not similarly check for
  1.1678 +	 * SEC_ERROR_BAD_SIGNATURE.  It probably should, but then would
  1.1679 +	 * probably say the wrong thing in the case that it *was* the
  1.1680 +	 * certificate signature check that failed during the cert
  1.1681 +	 * verification done above.  Our error handling is really a mess.
  1.1682 +	 */
  1.1683 +	if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
  1.1684 +	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  1.1685 +    }
  1.1686 +
  1.1687 +savecert:
  1.1688 +    /*
  1.1689 +     * Only save the smime profile if we are checking an email message and
  1.1690 +     * the cert has an email address in it.
  1.1691 +     */
  1.1692 +    if ( cert->emailAddr && cert->emailAddr[0] &&
  1.1693 +	( ( certusage == certUsageEmailSigner ) ||
  1.1694 +	 ( certusage == certUsageEmailRecipient ) ) ) {
  1.1695 +	SECItem *profile = NULL;
  1.1696 +	int save_error;
  1.1697 +
  1.1698 +	/*
  1.1699 +	 * Remember the current error set because we do not care about
  1.1700 +	 * anything set by the functions we are about to call.
  1.1701 +	 */
  1.1702 +	save_error = PORT_GetError();
  1.1703 +
  1.1704 +	if (goodsig && (signerinfo->authAttr != NULL)) {
  1.1705 +	    /*
  1.1706 +	     * If the signature is good, then we can save the S/MIME profile,
  1.1707 +	     * if we have one.
  1.1708 +	     */
  1.1709 +	    SEC_PKCS7Attribute *attr;
  1.1710 +
  1.1711 +	    attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
  1.1712 +					   SEC_OID_PKCS9_SMIME_CAPABILITIES,
  1.1713 +					   PR_TRUE);
  1.1714 +	    profile = sec_PKCS7AttributeValue (attr);
  1.1715 +	}
  1.1716 +
  1.1717 +	rv = CERT_SaveSMimeProfile (cert, profile, encoded_stime);
  1.1718 +
  1.1719 +	/*
  1.1720 +	 * Restore the saved error in case the calls above set a new
  1.1721 +	 * one that we do not actually care about.
  1.1722 +	 */
  1.1723 +	PORT_SetError (save_error);
  1.1724 +
  1.1725 +	/*
  1.1726 +	 * XXX Failure is not indicated anywhere -- the signature
  1.1727 +	 * verification itself is unaffected by whether or not the
  1.1728 +	 * profile was successfully saved.
  1.1729 +	 */
  1.1730 +    }
  1.1731 +	
  1.1732 +
  1.1733 +done:
  1.1734 +
  1.1735 +    /*
  1.1736 +     * See comment above about why we do not want to destroy cert
  1.1737 +     * itself here.
  1.1738 +     */
  1.1739 +
  1.1740 +    if (certs != NULL)
  1.1741 +	CERT_DestroyCertArray (certs, certcount);
  1.1742 +
  1.1743 +    if (publickey != NULL)
  1.1744 +	SECKEY_DestroyPublicKey (publickey);
  1.1745 +
  1.1746 +    return goodsig;
  1.1747 +}
  1.1748 +
  1.1749 +/*
  1.1750 + * SEC_PKCS7VerifySignature
  1.1751 + *	Look at a PKCS7 contentInfo and check if the signature is good.
  1.1752 + *	The verification checks that the signing cert is valid and trusted
  1.1753 + *	for the purpose specified by "certusage".
  1.1754 + *
  1.1755 + *	In addition, if "keepcerts" is true, add any new certificates found
  1.1756 + *	into our local database.
  1.1757 + */
  1.1758 +PRBool
  1.1759 +SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo,
  1.1760 +			 SECCertUsage certusage,
  1.1761 +			 PRBool keepcerts)
  1.1762 +{
  1.1763 +    return sec_pkcs7_verify_signature (cinfo, certusage,
  1.1764 +				       NULL, HASH_AlgNULL, keepcerts, NULL);
  1.1765 +}
  1.1766 +
  1.1767 +/*
  1.1768 + * SEC_PKCS7VerifyDetachedSignature
  1.1769 + *	Look at a PKCS7 contentInfo and check if the signature matches
  1.1770 + *	a passed-in digest (calculated, supposedly, from detached contents).
  1.1771 + *	The verification checks that the signing cert is valid and trusted
  1.1772 + *	for the purpose specified by "certusage".
  1.1773 + *
  1.1774 + *	In addition, if "keepcerts" is true, add any new certificates found
  1.1775 + *	into our local database.
  1.1776 + */
  1.1777 +PRBool
  1.1778 +SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo,
  1.1779 +				 SECCertUsage certusage,
  1.1780 +				 const SECItem *detached_digest,
  1.1781 +				 HASH_HashType digest_type,
  1.1782 +				 PRBool keepcerts)
  1.1783 +{
  1.1784 +    return sec_pkcs7_verify_signature (cinfo, certusage,
  1.1785 +				       detached_digest, digest_type,
  1.1786 +				       keepcerts, NULL);
  1.1787 +}
  1.1788 +
  1.1789 +/*
  1.1790 + * SEC_PKCS7VerifyDetachedSignatureAtTime
  1.1791 + *      Look at a PKCS7 contentInfo and check if the signature matches
  1.1792 + *      a passed-in digest (calculated, supposedly, from detached contents).
  1.1793 + *      The verification checks that the signing cert is valid and trusted
  1.1794 + *      for the purpose specified by "certusage" at time "atTime".
  1.1795 + *
  1.1796 + *	In addition, if "keepcerts" is true, add any new certificates found
  1.1797 + *	into our local database.
  1.1798 + */
  1.1799 +PRBool
  1.1800 +SEC_PKCS7VerifyDetachedSignatureAtTime(SEC_PKCS7ContentInfo *cinfo,
  1.1801 +				       SECCertUsage certusage,
  1.1802 +				       const SECItem *detached_digest,
  1.1803 +				       HASH_HashType digest_type,
  1.1804 +				       PRBool keepcerts,
  1.1805 +				       PRTime atTime)
  1.1806 +{
  1.1807 +    return sec_pkcs7_verify_signature (cinfo, certusage,
  1.1808 +				       detached_digest, digest_type,
  1.1809 +				       keepcerts, &atTime);
  1.1810 +}
  1.1811 +
  1.1812 +/*
  1.1813 + * Return the asked-for portion of the name of the signer of a PKCS7
  1.1814 + * signed object.
  1.1815 + *
  1.1816 + * Returns a pointer to allocated memory, which must be freed.
  1.1817 + * A NULL return value is an error.
  1.1818 + */
  1.1819 +
  1.1820 +#define sec_common_name 1
  1.1821 +#define sec_email_address 2
  1.1822 +
  1.1823 +static char *
  1.1824 +sec_pkcs7_get_signer_cert_info(SEC_PKCS7ContentInfo *cinfo, int selector)
  1.1825 +{
  1.1826 +    SECOidTag kind;
  1.1827 +    SEC_PKCS7SignerInfo **signerinfos;
  1.1828 +    CERTCertificate *signercert;
  1.1829 +    char *container;
  1.1830 +
  1.1831 +    kind = SEC_PKCS7ContentType (cinfo);
  1.1832 +    switch (kind) {
  1.1833 +      default:
  1.1834 +      case SEC_OID_PKCS7_DATA:
  1.1835 +      case SEC_OID_PKCS7_DIGESTED_DATA:
  1.1836 +      case SEC_OID_PKCS7_ENVELOPED_DATA:
  1.1837 +      case SEC_OID_PKCS7_ENCRYPTED_DATA:
  1.1838 +	PORT_Assert (0);
  1.1839 +	return NULL;
  1.1840 +      case SEC_OID_PKCS7_SIGNED_DATA:
  1.1841 +	{
  1.1842 +	    SEC_PKCS7SignedData *sdp;
  1.1843 +
  1.1844 +	    sdp = cinfo->content.signedData;
  1.1845 +	    signerinfos = sdp->signerInfos;
  1.1846 +	}
  1.1847 +	break;
  1.1848 +      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
  1.1849 +	{
  1.1850 +	    SEC_PKCS7SignedAndEnvelopedData *saedp;
  1.1851 +
  1.1852 +	    saedp = cinfo->content.signedAndEnvelopedData;
  1.1853 +	    signerinfos = saedp->signerInfos;
  1.1854 +	}
  1.1855 +	break;
  1.1856 +    }
  1.1857 +
  1.1858 +    if (signerinfos == NULL || signerinfos[0] == NULL)
  1.1859 +	return NULL;
  1.1860 +
  1.1861 +    signercert = signerinfos[0]->cert;
  1.1862 +
  1.1863 +    /*
  1.1864 +     * No cert there; see if we can find one by calling verify ourselves.
  1.1865 +     */
  1.1866 +    if (signercert == NULL) {
  1.1867 +	/*
  1.1868 +	 * The cert usage does not matter in this case, because we do not
  1.1869 +	 * actually care about the verification itself, but we have to pick
  1.1870 +	 * some valid usage to pass in.
  1.1871 +	 */
  1.1872 +	(void) sec_pkcs7_verify_signature (cinfo, certUsageEmailSigner,
  1.1873 +					   NULL, HASH_AlgNULL, PR_FALSE, NULL);
  1.1874 +	signercert = signerinfos[0]->cert;
  1.1875 +	if (signercert == NULL)
  1.1876 +	    return NULL;
  1.1877 +    }
  1.1878 +
  1.1879 +    switch (selector) {
  1.1880 +      case sec_common_name:
  1.1881 +	container = CERT_GetCommonName (&signercert->subject);
  1.1882 +	break;
  1.1883 +      case sec_email_address:
  1.1884 +	if(signercert->emailAddr && signercert->emailAddr[0]) {
  1.1885 +	    container = PORT_Strdup(signercert->emailAddr);
  1.1886 +	} else {
  1.1887 +	    container = NULL;
  1.1888 +	}
  1.1889 +	break;
  1.1890 +      default:
  1.1891 +	PORT_Assert (0);
  1.1892 +	container = NULL;
  1.1893 +	break;
  1.1894 +    }
  1.1895 +
  1.1896 +    return container;
  1.1897 +}
  1.1898 +
  1.1899 +char *
  1.1900 +SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo)
  1.1901 +{
  1.1902 +    return sec_pkcs7_get_signer_cert_info(cinfo, sec_common_name);
  1.1903 +}
  1.1904 +
  1.1905 +char *
  1.1906 +SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo)
  1.1907 +{
  1.1908 +    return sec_pkcs7_get_signer_cert_info(cinfo, sec_email_address);
  1.1909 +}
  1.1910 +
  1.1911 +
  1.1912 +/*
  1.1913 + * Return the signing time, in UTCTime format, of a PKCS7 contentInfo.
  1.1914 + */
  1.1915 +SECItem *
  1.1916 +SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo)
  1.1917 +{
  1.1918 +    SEC_PKCS7SignerInfo **signerinfos;
  1.1919 +    SEC_PKCS7Attribute *attr;
  1.1920 +
  1.1921 +    if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
  1.1922 +	return NULL;
  1.1923 +
  1.1924 +    signerinfos = cinfo->content.signedData->signerInfos;
  1.1925 +
  1.1926 +    /*
  1.1927 +     * No signature, or more than one, means no deal.
  1.1928 +     */
  1.1929 +    if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
  1.1930 +	return NULL;
  1.1931 +
  1.1932 +    attr = sec_PKCS7FindAttribute (signerinfos[0]->authAttr,
  1.1933 +				   SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
  1.1934 +    return sec_PKCS7AttributeValue (attr);
  1.1935 +}

mercurial