Back to index

lightning-sunbird  0.9+nobinonly
p7decode.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  * PKCS7 decoding, verification.
00040  *
00041  * $Id: p7decode.c,v 1.20.2.1 2006/04/28 03:35:30 rrelyea%redhat.com Exp $
00042  */
00043 
00044 #include "nssrenam.h"
00045 
00046 #include "p7local.h"
00047 
00048 #include "cert.h"
00049                             /* XXX do not want to have to include */
00050 #include "certdb.h"         /* certdb.h -- the trust stuff needed by */
00051                             /* the add certificate code needs to get */
00052                             /* rewritten/abstracted and then this */
00053                             /* include should be removed! */
00054 /*#include "cdbhdl.h" */
00055 #include "cryptohi.h"
00056 #include "key.h"
00057 #include "secasn1.h"
00058 #include "secitem.h"
00059 #include "secoid.h"
00060 #include "pk11func.h"
00061 #include "prtime.h"
00062 #include "secerr.h"
00063 #include "sechash.h" /* for HASH_GetHashObject() */
00064 #include "secder.h"
00065 #include "secpkcs5.h"
00066 
00067 struct sec_pkcs7_decoder_worker {
00068     int depth;
00069     int digcnt;
00070     void **digcxs;
00071     const SECHashObject **digobjs;
00072     sec_PKCS7CipherObject *decryptobj;
00073     PRBool saw_contents;
00074 };
00075 
00076 struct SEC_PKCS7DecoderContextStr {
00077     SEC_ASN1DecoderContext *dcx;
00078     SEC_PKCS7ContentInfo *cinfo;
00079     SEC_PKCS7DecoderContentCallback cb;
00080     void *cb_arg;
00081     SECKEYGetPasswordKey pwfn;
00082     void *pwfn_arg;
00083     struct sec_pkcs7_decoder_worker worker;
00084     PRArenaPool *tmp_poolp;
00085     int error;
00086     SEC_PKCS7GetDecryptKeyCallback dkcb;
00087     void *dkcb_arg;
00088     SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb;
00089 };
00090 
00091 /*
00092  * Handle one worker, decrypting and digesting the data as necessary.
00093  *
00094  * XXX If/when we support nested contents, this probably needs to be
00095  * revised somewhat to get passed the content-info (which unfortunately
00096  * can be two different types depending on whether it is encrypted or not)
00097  * corresponding to the given worker.
00098  */
00099 static void
00100 sec_pkcs7_decoder_work_data (SEC_PKCS7DecoderContext *p7dcx,
00101                           struct sec_pkcs7_decoder_worker *worker,
00102                           const unsigned char *data, unsigned long len,
00103                           PRBool final)
00104 {
00105     unsigned char *buf = NULL;
00106     SECStatus rv;
00107     int i;
00108 
00109     /*
00110      * We should really have data to process, or we should be trying
00111      * to finish/flush the last block.  (This is an overly paranoid
00112      * check since all callers are in this file and simple inspection
00113      * proves they do it right.  But it could find a bug in future
00114      * modifications/development, that is why it is here.)
00115      */
00116     PORT_Assert ((data != NULL && len) || final);
00117 
00118     /*
00119      * Decrypt this chunk.
00120      *
00121      * XXX If we get an error, we do not want to do the digest or callback,
00122      * but we want to keep decoding.  Or maybe we want to stop decoding
00123      * altogether if there is a callback, because obviously we are not
00124      * sending the data back and they want to know that.
00125      */
00126     if (worker->decryptobj != NULL) {
00127        /* XXX the following lengths should all be longs? */
00128        unsigned int inlen;  /* length of data being decrypted */
00129        unsigned int outlen; /* length of decrypted data */
00130        unsigned int buflen; /* length available for decrypted data */
00131        SECItem *plain;
00132 
00133        inlen = len;
00134        buflen = sec_PKCS7DecryptLength (worker->decryptobj, inlen, final);
00135        if (buflen == 0) {
00136            if (inlen == 0)  /* no input and no output */
00137               return;
00138            /*
00139             * No output is expected, but the input data may be buffered
00140             * so we still have to call Decrypt.
00141             */
00142            rv = sec_PKCS7Decrypt (worker->decryptobj, NULL, NULL, 0,
00143                                data, inlen, final);
00144            if (rv != SECSuccess) {
00145               p7dcx->error = PORT_GetError();
00146               return;              /* XXX indicate error? */
00147            }
00148            return;
00149        }
00150 
00151        if (p7dcx->cb != NULL) {
00152            buf = (unsigned char *) PORT_Alloc (buflen);
00153            plain = NULL;
00154        } else {
00155            unsigned long oldlen;
00156 
00157            /*
00158             * XXX This assumes one level of content only.
00159             * See comment above about nested content types.
00160             * XXX Also, it should work for signedAndEnvelopedData, too!
00161             */
00162            plain = &(p7dcx->cinfo->
00163                      content.envelopedData->encContentInfo.plainContent);
00164 
00165            oldlen = plain->len;
00166            if (oldlen == 0) {
00167               buf = (unsigned char*)PORT_ArenaAlloc (p7dcx->cinfo->poolp, 
00168                                                  buflen);
00169            } else {
00170               buf = (unsigned char*)PORT_ArenaGrow (p7dcx->cinfo->poolp, 
00171                                   plain->data,
00172                                   oldlen, oldlen + buflen);
00173               if (buf != NULL)
00174                   buf += oldlen;
00175            }
00176            plain->data = buf;
00177        }
00178        if (buf == NULL) {
00179            p7dcx->error = SEC_ERROR_NO_MEMORY;
00180            return;          /* XXX indicate error? */
00181        }
00182        rv = sec_PKCS7Decrypt (worker->decryptobj, buf, &outlen, buflen,
00183                             data, inlen, final);
00184        if (rv != SECSuccess) {
00185            p7dcx->error = PORT_GetError();
00186            return;          /* XXX indicate error? */
00187        }
00188        if (plain != NULL) {
00189            PORT_Assert (final || outlen == buflen);
00190            plain->len += outlen;
00191        }
00192        data = buf;
00193        len = outlen;
00194     }
00195 
00196     /*
00197      * Update the running digests.
00198      */
00199     if (len) {
00200        for (i = 0; i < worker->digcnt; i++) {
00201            (* worker->digobjs[i]->update) (worker->digcxs[i], data, len);
00202        }
00203     }
00204 
00205     /*
00206      * Pass back the contents bytes, and free the temporary buffer.
00207      */
00208     if (p7dcx->cb != NULL) {
00209        if (len)
00210            (* p7dcx->cb) (p7dcx->cb_arg, (const char *)data, len);
00211        if (worker->decryptobj != NULL) {
00212            PORT_Assert (buf != NULL);
00213            PORT_Free (buf);
00214        }
00215     }
00216 }
00217 
00218 static void
00219 sec_pkcs7_decoder_filter (void *arg, const char *data, unsigned long len,
00220                        int depth, SEC_ASN1EncodingPart data_kind)
00221 {
00222     SEC_PKCS7DecoderContext *p7dcx;
00223     struct sec_pkcs7_decoder_worker *worker;
00224 
00225     /*
00226      * Since we do not handle any nested contents, the only bytes we
00227      * are really interested in are the actual contents bytes (not
00228      * the identifier, length, or end-of-contents bytes).  If we were
00229      * handling nested types we would probably need to do something
00230      * smarter based on depth and data_kind.
00231      */
00232     if (data_kind != SEC_ASN1_Contents)
00233        return;
00234 
00235     /*
00236      * The ASN.1 decoder should not even call us with a length of 0.
00237      * Just being paranoid.
00238      */
00239     PORT_Assert (len);
00240     if (len == 0)
00241        return;
00242 
00243     p7dcx = (SEC_PKCS7DecoderContext*)arg;
00244 
00245     /*
00246      * Handling nested contents would mean that there is a chain
00247      * of workers -- one per each level of content.  The following
00248      * would start with the first worker and loop over them.
00249      */
00250     worker = &(p7dcx->worker);
00251 
00252     worker->saw_contents = PR_TRUE;
00253 
00254     sec_pkcs7_decoder_work_data (p7dcx, worker,
00255                              (const unsigned char *) data, len, PR_FALSE);
00256 }
00257 
00258 
00259 /*
00260  * Create digest contexts for each algorithm in "digestalgs".
00261  * No algorithms is not an error, we just do not do anything.
00262  * An error (like trouble allocating memory), marks the error
00263  * in "p7dcx" and returns SECFailure, which means that our caller
00264  * should just give up altogether.
00265  */
00266 static SECStatus
00267 sec_pkcs7_decoder_start_digests (SEC_PKCS7DecoderContext *p7dcx, int depth,
00268                              SECAlgorithmID **digestalgs)
00269 {
00270     int i, digcnt;
00271 
00272     if (digestalgs == NULL)
00273        return SECSuccess;
00274 
00275     /*
00276      * Count the algorithms.
00277      */
00278     digcnt = 0;
00279     while (digestalgs[digcnt] != NULL)
00280        digcnt++;
00281 
00282     /*
00283      * No algorithms means no work to do.
00284      * Just act as if there were no algorithms specified.
00285      */
00286     if (digcnt == 0)
00287        return SECSuccess;
00288 
00289     p7dcx->worker.digcxs = (void**)PORT_ArenaAlloc (p7dcx->tmp_poolp,
00290                                        digcnt * sizeof (void *));
00291     p7dcx->worker.digobjs = (const SECHashObject**)PORT_ArenaAlloc (p7dcx->tmp_poolp,
00292                                         digcnt * sizeof (SECHashObject *));
00293     if (p7dcx->worker.digcxs == NULL || p7dcx->worker.digobjs == NULL) {
00294        p7dcx->error = SEC_ERROR_NO_MEMORY;
00295        return SECFailure;
00296     }
00297 
00298     p7dcx->worker.depth = depth;
00299     p7dcx->worker.digcnt = 0;
00300 
00301     /*
00302      * Create a digest context for each algorithm.
00303      */
00304     for (i = 0; i < digcnt; i++) {
00305        SECAlgorithmID *     algid  = digestalgs[i];
00306        SECOidTag            oidTag = SECOID_FindOIDTag(&(algid->algorithm));
00307        const SECHashObject *digobj = HASH_GetHashObjectByOidTag(oidTag);
00308        void *digcx;
00309 
00310        /*
00311         * Skip any algorithm we do not even recognize; obviously,
00312         * this could be a problem, but if it is critical then the
00313         * result will just be that the signature does not verify.
00314         * We do not necessarily want to error out here, because
00315         * the particular algorithm may not actually be important,
00316         * but we cannot know that until later.
00317         */
00318        if (digobj == NULL) {
00319            p7dcx->worker.digcnt--;
00320            continue;
00321        }
00322 
00323        digcx = (* digobj->create)();
00324        if (digcx != NULL) {
00325            (* digobj->begin) (digcx);
00326            p7dcx->worker.digobjs[p7dcx->worker.digcnt] = digobj;
00327            p7dcx->worker.digcxs[p7dcx->worker.digcnt] = digcx;
00328            p7dcx->worker.digcnt++;
00329        }
00330     }
00331 
00332     if (p7dcx->worker.digcnt != 0)
00333        SEC_ASN1DecoderSetFilterProc (p7dcx->dcx,
00334                                   sec_pkcs7_decoder_filter,
00335                                   p7dcx,
00336                                   (PRBool)(p7dcx->cb != NULL));
00337     return SECSuccess;
00338 }
00339 
00340 
00341 /*
00342  * Close out all of the digest contexts, storing the results in "digestsp".
00343  */
00344 static SECStatus
00345 sec_pkcs7_decoder_finish_digests (SEC_PKCS7DecoderContext *p7dcx,
00346                               PRArenaPool *poolp,
00347                               SECItem ***digestsp)
00348 {
00349     struct sec_pkcs7_decoder_worker *worker;
00350     const SECHashObject *digobj;
00351     void *digcx;
00352     SECItem **digests, *digest;
00353     int i;
00354     void *mark;
00355 
00356     /*
00357      * XXX Handling nested contents would mean that there is a chain
00358      * of workers -- one per each level of content.  The following
00359      * would want to find the last worker in the chain.
00360      */
00361     worker = &(p7dcx->worker);
00362 
00363     /*
00364      * If no digests, then we have nothing to do.
00365      */
00366     if (worker->digcnt == 0)
00367        return SECSuccess;
00368 
00369     /*
00370      * No matter what happens after this, we want to stop filtering.
00371      * XXX If we handle nested contents, we only want to stop filtering
00372      * if we are finishing off the *last* worker.
00373      */
00374     SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
00375 
00376     /*
00377      * If we ended up with no contents, just destroy each
00378      * digest context -- they are meaningless and potentially
00379      * confusing, because their presence would imply some content
00380      * was digested.
00381      */
00382     if (! worker->saw_contents) {
00383        for (i = 0; i < worker->digcnt; i++) {
00384            digcx = worker->digcxs[i];
00385            digobj = worker->digobjs[i];
00386            (* digobj->destroy) (digcx, PR_TRUE);
00387        }
00388        return SECSuccess;
00389     }
00390 
00391     mark = PORT_ArenaMark (poolp);
00392 
00393     /*
00394      * Close out each digest context, saving digest away.
00395      */
00396     digests = 
00397       (SECItem**)PORT_ArenaAlloc (poolp,(worker->digcnt+1)*sizeof(SECItem *));
00398     digest = (SECItem*)PORT_ArenaAlloc (poolp, worker->digcnt*sizeof(SECItem));
00399     if (digests == NULL || digest == NULL) {
00400        p7dcx->error = PORT_GetError();
00401        PORT_ArenaRelease (poolp, mark);
00402        return SECFailure;
00403     }
00404 
00405     for (i = 0; i < worker->digcnt; i++, digest++) {
00406        digcx = worker->digcxs[i];
00407        digobj = worker->digobjs[i];
00408 
00409        digest->data = (unsigned char*)PORT_ArenaAlloc (poolp, digobj->length);
00410        if (digest->data == NULL) {
00411            p7dcx->error = PORT_GetError();
00412            PORT_ArenaRelease (poolp, mark);
00413            return SECFailure;
00414        }
00415 
00416        digest->len = digobj->length;
00417        (* digobj->end) (digcx, digest->data, &(digest->len), digest->len);
00418        (* digobj->destroy) (digcx, PR_TRUE);
00419 
00420        digests[i] = digest;
00421     }
00422     digests[i] = NULL;
00423     *digestsp = digests;
00424 
00425     PORT_ArenaUnmark (poolp, mark);
00426     return SECSuccess;
00427 }
00428 
00429 /*
00430  * XXX Need comment explaining following helper function (which is used
00431  * by sec_pkcs7_decoder_start_decrypt).
00432  */
00433 extern const SEC_ASN1Template SEC_SMIMEKEAParamTemplateAllParams[];
00434 
00435 static PK11SymKey *
00436 sec_pkcs7_decoder_get_recipient_key (SEC_PKCS7DecoderContext *p7dcx,
00437                                  SEC_PKCS7RecipientInfo **recipientinfos,
00438                                  SEC_PKCS7EncryptedContentInfo *enccinfo)
00439 {
00440     SEC_PKCS7RecipientInfo *ri;
00441     CERTCertificate *cert = NULL;
00442     SECKEYPrivateKey *privkey = NULL;
00443     PK11SymKey *bulkkey;
00444     SECOidTag keyalgtag, bulkalgtag, encalgtag;
00445     PK11SlotInfo *slot;
00446     int bulkLength = 0;
00447 
00448     if (recipientinfos == NULL || recipientinfos[0] == NULL) {
00449        p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT;
00450        goto no_key_found;
00451     }
00452 
00453     cert = PK11_FindCertAndKeyByRecipientList(&slot,recipientinfos,&ri,
00454                                           &privkey, p7dcx->pwfn_arg);
00455     if (cert == NULL) {
00456        p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT;
00457        goto no_key_found;
00458     }
00459 
00460     ri->cert = cert;        /* so we can find it later */
00461     PORT_Assert(privkey != NULL);
00462 
00463     keyalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
00464     encalgtag = SECOID_GetAlgorithmTag (&(ri->keyEncAlg));
00465     if ((encalgtag != SEC_OID_NETSCAPE_SMIME_KEA) && (keyalgtag != encalgtag)) {
00466        p7dcx->error = SEC_ERROR_PKCS7_KEYALG_MISMATCH;
00467        goto no_key_found;
00468     }
00469     bulkalgtag = SECOID_GetAlgorithmTag (&(enccinfo->contentEncAlg));
00470 
00471     switch (encalgtag) {
00472       case SEC_OID_PKCS1_RSA_ENCRYPTION:
00473        bulkkey = PK11_PubUnwrapSymKey (privkey, &ri->encKey,
00474                                    PK11_AlgtagToMechanism (bulkalgtag),
00475                                    CKA_DECRYPT, 0);
00476        if (bulkkey == NULL) {
00477            p7dcx->error = PORT_GetError();
00478            PORT_SetError(0);
00479            goto no_key_found;
00480        }
00481        break;
00482        /* ### mwelch -- KEA */ 
00483         case SEC_OID_NETSCAPE_SMIME_KEA:
00484          {
00485              SECStatus err;
00486              CK_MECHANISM_TYPE bulkType;
00487              PK11SymKey *tek;
00488              SECKEYPublicKey *senderPubKey;
00489              SEC_PKCS7SMIMEKEAParameters   keaParams;
00490 
00491              (void) memset(&keaParams, 0, sizeof(keaParams));
00492 
00493              /* Decode the KEA algorithm parameters. */
00494              err = SEC_ASN1DecodeItem(NULL,
00495                                    &keaParams,
00496                                    SEC_SMIMEKEAParamTemplateAllParams,
00497                                    &(ri->keyEncAlg.parameters));
00498              if (err != SECSuccess)
00499              {
00500                 p7dcx->error = err;
00501                 PORT_SetError(0);
00502                 goto no_key_found;
00503              }
00504          
00505 
00506              /* We just got key data, no key structure. So, we
00507                create one. */
00508             senderPubKey = 
00509                 PK11_MakeKEAPubKey(keaParams.originatorKEAKey.data,
00510                                  keaParams.originatorKEAKey.len);
00511             if (senderPubKey == NULL)
00512             {
00513                   p7dcx->error = PORT_GetError();
00514                   PORT_SetError(0);
00515                   goto no_key_found;
00516             }
00517              
00518             /* Generate the TEK (token exchange key) which we use
00519                 to unwrap the bulk encryption key. */
00520             tek = PK11_PubDerive(privkey, senderPubKey, 
00521                                PR_FALSE,
00522                                &keaParams.originatorRA,
00523                                NULL,
00524                                CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
00525                                CKA_WRAP, 0, p7dcx->pwfn_arg);
00526             SECKEY_DestroyPublicKey(senderPubKey);
00527              
00528             if (tek == NULL)
00529             {
00530                 p7dcx->error = PORT_GetError();
00531                 PORT_SetError(0);
00532                 goto no_key_found;
00533             }
00534              
00535              /* Now that we have the TEK, unwrap the bulk key
00536                 with which to decrypt the message. We have to
00537                do one of two different things depending on 
00538                whether Skipjack was used for bulk encryption 
00539                of the message. */
00540              bulkType = PK11_AlgtagToMechanism (bulkalgtag);
00541              switch(bulkType)
00542              {
00543              case CKM_SKIPJACK_CBC64:
00544              case CKM_SKIPJACK_ECB64:
00545              case CKM_SKIPJACK_OFB64:
00546              case CKM_SKIPJACK_CFB64:
00547              case CKM_SKIPJACK_CFB32:
00548              case CKM_SKIPJACK_CFB16:
00549              case CKM_SKIPJACK_CFB8:
00550                 /* Skipjack is being used as the bulk encryption algorithm.*/
00551                 /* Unwrap the bulk key. */
00552                 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP,
00553                                          NULL, &ri->encKey, 
00554                                          CKM_SKIPJACK_CBC64, 
00555                                          CKA_DECRYPT, 0);
00556                 break;
00557              default:
00558                 /* Skipjack was not used for bulk encryption of this
00559                    message. Use Skipjack CBC64, with the nonSkipjackIV
00560                    part of the KEA key parameters, to decrypt 
00561                    the bulk key. If we got a parameter indicating that the
00562                    bulk key size is different than the encrypted key size,
00563                    pass in the real key size. */
00564                 
00565                 /* Check for specified bulk key length (unspecified implies
00566                    that the bulk key length is the same as encrypted length) */
00567                 if (keaParams.bulkKeySize.len > 0)
00568                 {
00569                     p7dcx->error = SEC_ASN1DecodeItem(NULL, &bulkLength,
00570                                    SEC_ASN1_GET(SEC_IntegerTemplate),
00571                                    &keaParams.bulkKeySize);
00572                 }
00573                 
00574                 if (p7dcx->error != SECSuccess)
00575                     goto no_key_found;
00576                 
00577                 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_CBC64,
00578                                          &keaParams.nonSkipjackIV, 
00579                                          &ri->encKey,
00580                                          bulkType,
00581                                          CKA_DECRYPT, bulkLength);
00582              }
00583              
00584              
00585              if (bulkkey == NULL)
00586              {
00587                 p7dcx->error = PORT_GetError();
00588                 PORT_SetError(0);
00589                 goto no_key_found;
00590              }
00591              break;
00592          }
00593       default:
00594        p7dcx->error = SEC_ERROR_UNSUPPORTED_KEYALG;
00595        goto no_key_found;
00596     }
00597 
00598     return bulkkey;
00599 
00600 no_key_found:
00601     if (privkey != NULL)
00602        SECKEY_DestroyPrivateKey (privkey);
00603 
00604     return NULL;
00605 }
00606  
00607 /*
00608  * XXX The following comment is old -- the function used to only handle
00609  * EnvelopedData or SignedAndEnvelopedData but now handles EncryptedData
00610  * as well (and it had all of the code of the helper function above
00611  * built into it), though the comment was left as is.  Fix it...
00612  *
00613  * We are just about to decode the content of an EnvelopedData.
00614  * Set up a decryption context so we can decrypt as we go.
00615  * Presumably we are one of the recipients listed in "recipientinfos".
00616  * (XXX And if we are not, or if we have trouble, what should we do?
00617  *  It would be nice to let the decoding still work.  Maybe it should
00618  *  be an error if there is a content callback, but not an error otherwise?)
00619  * The encryption key and related information can be found in "enccinfo".
00620  */
00621 static SECStatus
00622 sec_pkcs7_decoder_start_decrypt (SEC_PKCS7DecoderContext *p7dcx, int depth,
00623                              SEC_PKCS7RecipientInfo **recipientinfos,
00624                              SEC_PKCS7EncryptedContentInfo *enccinfo,
00625                              PK11SymKey **copy_key_for_signature)
00626 {
00627     PK11SymKey *bulkkey = NULL;
00628     sec_PKCS7CipherObject *decryptobj;
00629 
00630     /*
00631      * If a callback is supplied to retrieve the encryption key, 
00632      * for instance, for Encrypted Content infos, then retrieve
00633      * the bulkkey from the callback.  Otherwise, assume that
00634      * we are processing Enveloped or SignedAndEnveloped data
00635      * content infos.
00636      *
00637      * XXX Put an assert here?
00638      */
00639     if (SEC_PKCS7ContentType(p7dcx->cinfo) == SEC_OID_PKCS7_ENCRYPTED_DATA) {
00640        if (p7dcx->dkcb != NULL) {
00641            bulkkey = (*p7dcx->dkcb)(p7dcx->dkcb_arg, 
00642                                  &(enccinfo->contentEncAlg));
00643        }
00644        enccinfo->keysize = 0;
00645     } else {
00646        bulkkey = sec_pkcs7_decoder_get_recipient_key (p7dcx, recipientinfos, 
00647                                                  enccinfo);
00648        if (bulkkey == NULL) goto no_decryption;
00649        enccinfo->keysize = PK11_GetKeyStrength(bulkkey, 
00650                                           &(enccinfo->contentEncAlg));
00651 
00652     }
00653 
00654     /*
00655      * XXX I think following should set error in p7dcx and clear set error
00656      * (as used to be done here, or as is done in get_receipient_key above.
00657      */
00658     if(bulkkey == NULL) {
00659        goto no_decryption;
00660     }
00661     
00662     /* 
00663      * We want to make sure decryption is allowed.  This is done via
00664      * a callback specified in SEC_PKCS7DecoderStart().
00665      */
00666     if (p7dcx->decrypt_allowed_cb) {
00667        if ((*p7dcx->decrypt_allowed_cb) (&(enccinfo->contentEncAlg), 
00668                                      bulkkey) == PR_FALSE) {
00669            p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
00670            goto no_decryption;
00671        }
00672     } else {
00673            p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
00674            goto no_decryption;
00675     }
00676 
00677     /*
00678      * When decrypting a signedAndEnvelopedData, the signature also has
00679      * to be decrypted with the bulk encryption key; to avoid having to
00680      * get it all over again later (and do another potentially expensive
00681      * RSA operation), copy it for later signature verification to use.
00682      */
00683     if (copy_key_for_signature != NULL)
00684        *copy_key_for_signature = PK11_ReferenceSymKey (bulkkey);
00685 
00686     /*
00687      * Now we have the bulk encryption key (in bulkkey) and the
00688      * the algorithm (in enccinfo->contentEncAlg).  Using those,
00689      * create a decryption context.
00690      */
00691     decryptobj = sec_PKCS7CreateDecryptObject (bulkkey,
00692                                           &(enccinfo->contentEncAlg));
00693 
00694     /*
00695      * We are done with (this) bulkkey now.
00696      */
00697     PK11_FreeSymKey (bulkkey);
00698 
00699     if (decryptobj == NULL) {
00700        p7dcx->error = PORT_GetError();
00701        PORT_SetError(0);
00702        goto no_decryption;
00703     }
00704 
00705     SEC_ASN1DecoderSetFilterProc (p7dcx->dcx,
00706                               sec_pkcs7_decoder_filter,
00707                               p7dcx,
00708                               (PRBool)(p7dcx->cb != NULL));
00709 
00710     p7dcx->worker.depth = depth;
00711     p7dcx->worker.decryptobj = decryptobj;
00712 
00713     return SECSuccess;
00714 
00715 no_decryption:
00716     /*
00717      * For some reason (error set already, if appropriate), we cannot
00718      * decrypt the content.  I am not sure what exactly is the right
00719      * thing to do here; in some cases we want to just stop, and in
00720      * others we want to let the decoding finish even though we cannot
00721      * decrypt the content.  My current thinking is that if the caller
00722      * set up a content callback, then they are really interested in
00723      * getting (decrypted) content, and if they cannot they will want
00724      * to know about it.  However, if no callback was specified, then
00725      * maybe it is not important that the decryption failed.
00726      */
00727     if (p7dcx->cb != NULL)
00728        return SECFailure;
00729     else
00730        return SECSuccess;   /* Let the decoding continue. */
00731 }
00732 
00733 
00734 static SECStatus
00735 sec_pkcs7_decoder_finish_decrypt (SEC_PKCS7DecoderContext *p7dcx,
00736                               PRArenaPool *poolp,
00737                               SEC_PKCS7EncryptedContentInfo *enccinfo)
00738 {
00739     struct sec_pkcs7_decoder_worker *worker;
00740 
00741     /*
00742      * XXX Handling nested contents would mean that there is a chain
00743      * of workers -- one per each level of content.  The following
00744      * would want to find the last worker in the chain.
00745      */
00746     worker = &(p7dcx->worker);
00747 
00748     /*
00749      * If no decryption context, then we have nothing to do.
00750      */
00751     if (worker->decryptobj == NULL)
00752        return SECSuccess;
00753 
00754     /*
00755      * No matter what happens after this, we want to stop filtering.
00756      * XXX If we handle nested contents, we only want to stop filtering
00757      * if we are finishing off the *last* worker.
00758      */
00759     SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
00760 
00761     /*
00762      * Handle the last block.
00763      */
00764     sec_pkcs7_decoder_work_data (p7dcx, worker, NULL, 0, PR_TRUE);
00765 
00766     /*
00767      * All done, destroy it.
00768      */
00769     sec_PKCS7DestroyDecryptObject (worker->decryptobj);
00770     worker->decryptobj = NULL;
00771 
00772     return SECSuccess;
00773 }
00774 
00775 
00776 static void
00777 sec_pkcs7_decoder_notify (void *arg, PRBool before, void *dest, int depth)
00778 {
00779     SEC_PKCS7DecoderContext *p7dcx;
00780     SEC_PKCS7ContentInfo *cinfo;
00781     SEC_PKCS7SignedData *sigd;
00782     SEC_PKCS7EnvelopedData *envd;
00783     SEC_PKCS7SignedAndEnvelopedData *saed;
00784     SEC_PKCS7EncryptedData *encd;
00785     SEC_PKCS7DigestedData *digd;
00786     PRBool after;
00787     SECStatus rv;
00788 
00789     /*
00790      * Just to make the code easier to read, create an "after" variable
00791      * that is equivalent to "not before".
00792      * (This used to be just the statement "after = !before", but that
00793      * causes a warning on the mac; to avoid that, we do it the long way.)
00794      */
00795     if (before)
00796        after = PR_FALSE;
00797     else
00798        after = PR_TRUE;
00799 
00800     p7dcx = (SEC_PKCS7DecoderContext*)arg;
00801     cinfo = p7dcx->cinfo;
00802 
00803     if (cinfo->contentTypeTag == NULL) {
00804        if (after && dest == &(cinfo->contentType))
00805            cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
00806        return;
00807     }
00808 
00809     switch (cinfo->contentTypeTag->offset) {
00810       case SEC_OID_PKCS7_SIGNED_DATA:
00811        sigd = cinfo->content.signedData;
00812        if (sigd == NULL)
00813            break;
00814 
00815        if (sigd->contentInfo.contentTypeTag == NULL) {
00816            if (after && dest == &(sigd->contentInfo.contentType))
00817               sigd->contentInfo.contentTypeTag =
00818                      SECOID_FindOID(&(sigd->contentInfo.contentType));
00819            break;
00820        }
00821 
00822        /*
00823         * We only set up a filtering digest if the content is
00824         * plain DATA; anything else needs more work because a
00825         * second pass is required to produce a DER encoding from
00826         * an input that can be BER encoded.  (This is a requirement
00827         * of PKCS7 that is unfortunate, but there you have it.)
00828         *
00829         * XXX Also, since we stop here if this is not DATA, the
00830         * inner content is not getting processed at all.  Someday
00831         * we may want to fix that.
00832         */
00833        if (sigd->contentInfo.contentTypeTag->offset != SEC_OID_PKCS7_DATA) {
00834            /* XXX Set an error in p7dcx->error */
00835            SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
00836            break;
00837        }
00838 
00839        /*
00840         * Just before the content, we want to set up a digest context
00841         * for each digest algorithm listed, and start a filter which
00842         * will run all of the contents bytes through that digest.
00843         */
00844        if (before && dest == &(sigd->contentInfo.content)) {
00845            rv = sec_pkcs7_decoder_start_digests (p7dcx, depth,
00846                                             sigd->digestAlgorithms);
00847            if (rv != SECSuccess)
00848               SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
00849 
00850            break;
00851        }
00852 
00853        /*
00854         * XXX To handle nested types, here is where we would want
00855         * to check for inner boundaries that need handling.
00856         */
00857 
00858        /*
00859         * Are we done?
00860         */
00861        if (after && dest == &(sigd->contentInfo.content)) {
00862            /*
00863             * Close out the digest contexts.  We ignore any error
00864             * because we are stopping anyway; the error status left
00865             * behind in p7dcx will be seen by outer functions.
00866             */
00867            (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp,
00868                                                &(sigd->digests));
00869 
00870            /*
00871             * XXX To handle nested contents, we would need to remove
00872             * the worker from the chain (and free it).
00873             */
00874 
00875            /*
00876             * Stop notify.
00877             */
00878            SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
00879        }
00880        break;
00881 
00882       case SEC_OID_PKCS7_ENVELOPED_DATA:
00883        envd = cinfo->content.envelopedData;
00884        if (envd == NULL)
00885            break;
00886 
00887        if (envd->encContentInfo.contentTypeTag == NULL) {
00888            if (after && dest == &(envd->encContentInfo.contentType))
00889               envd->encContentInfo.contentTypeTag =
00890                      SECOID_FindOID(&(envd->encContentInfo.contentType));
00891            break;
00892        }
00893 
00894        /*
00895         * Just before the content, we want to set up a decryption
00896         * context, and start a filter which will run all of the
00897         * contents bytes through it to determine the plain content.
00898         */
00899        if (before && dest == &(envd->encContentInfo.encContent)) {
00900            rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth,
00901                                             envd->recipientInfos,
00902                                             &(envd->encContentInfo),
00903                                             NULL);
00904            if (rv != SECSuccess)
00905               SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
00906 
00907            break;
00908        }
00909 
00910        /*
00911         * Are we done?
00912         */
00913        if (after && dest == &(envd->encContentInfo.encContent)) {
00914            /*
00915             * Close out the decryption context.  We ignore any error
00916             * because we are stopping anyway; the error status left
00917             * behind in p7dcx will be seen by outer functions.
00918             */
00919            (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp,
00920                                                &(envd->encContentInfo));
00921 
00922            /*
00923             * XXX To handle nested contents, we would need to remove
00924             * the worker from the chain (and free it).
00925             */
00926 
00927            /*
00928             * Stop notify.
00929             */
00930            SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
00931        }
00932        break;
00933 
00934       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
00935        saed = cinfo->content.signedAndEnvelopedData;
00936        if (saed == NULL)
00937            break;
00938 
00939        if (saed->encContentInfo.contentTypeTag == NULL) {
00940            if (after && dest == &(saed->encContentInfo.contentType))
00941               saed->encContentInfo.contentTypeTag =
00942                      SECOID_FindOID(&(saed->encContentInfo.contentType));
00943            break;
00944        }
00945 
00946        /*
00947         * Just before the content, we want to set up a decryption
00948         * context *and* digest contexts, and start a filter which
00949         * will run all of the contents bytes through both.
00950         */
00951        if (before && dest == &(saed->encContentInfo.encContent)) {
00952            rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth,
00953                                             saed->recipientInfos,
00954                                             &(saed->encContentInfo),
00955                                             &(saed->sigKey));
00956            if (rv == SECSuccess)
00957               rv = sec_pkcs7_decoder_start_digests (p7dcx, depth,
00958                                                 saed->digestAlgorithms);
00959            if (rv != SECSuccess)
00960               SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
00961 
00962            break;
00963        }
00964 
00965        /*
00966         * Are we done?
00967         */
00968        if (after && dest == &(saed->encContentInfo.encContent)) {
00969            /*
00970             * Close out the decryption and digests contexts.
00971             * We ignore any errors because we are stopping anyway;
00972             * the error status left behind in p7dcx will be seen by
00973             * outer functions.
00974             *
00975             * Note that the decrypt stuff must be called first;
00976             * it may have a last buffer to do which in turn has
00977             * to be added to the digest.
00978             */
00979            (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp,
00980                                                &(saed->encContentInfo));
00981            (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp,
00982                                                &(saed->digests));
00983 
00984            /*
00985             * XXX To handle nested contents, we would need to remove
00986             * the worker from the chain (and free it).
00987             */
00988 
00989            /*
00990             * Stop notify.
00991             */
00992            SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
00993        }
00994        break;
00995 
00996       case SEC_OID_PKCS7_DIGESTED_DATA:
00997        digd = cinfo->content.digestedData;
00998        
00999        /* 
01000         * XXX Want to do the digest or not?  Maybe future enhancement...
01001         */
01002        if (before && dest == &(digd->contentInfo.content.data)) {
01003            SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, sec_pkcs7_decoder_filter,
01004                                      p7dcx,
01005                                      (PRBool)(p7dcx->cb != NULL));
01006            break;
01007        }
01008 
01009        /*
01010         * Are we done?
01011         */
01012        if (after && dest == &(digd->contentInfo.content.data)) {
01013            SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
01014        }
01015        break;
01016 
01017       case SEC_OID_PKCS7_ENCRYPTED_DATA:
01018        encd = cinfo->content.encryptedData;
01019 
01020        /*
01021         * XXX If the decryption key callback is set, we want to start
01022         * the decryption.  If the callback is not set, we will treat the
01023         * content as plain data, since we do not have the key.
01024         *
01025         * Is this the proper thing to do?
01026         */
01027        if (before && dest == &(encd->encContentInfo.encContent)) {
01028            /*
01029             * Start the encryption process if the decryption key callback
01030             * is present.  Otherwise, treat the content like plain data.
01031             */
01032            rv = SECSuccess;
01033            if (p7dcx->dkcb != NULL) {
01034               rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, NULL,
01035                                                 &(encd->encContentInfo),
01036                                                 NULL);
01037            }
01038 
01039            if (rv != SECSuccess)
01040               SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
01041               
01042            break;
01043        }
01044 
01045        /*
01046         * Are we done?
01047         */
01048        if (after && dest == &(encd->encContentInfo.encContent)) {
01049            /*
01050             * Close out the decryption context.  We ignore any error
01051             * because we are stopping anyway; the error status left
01052             * behind in p7dcx will be seen by outer functions.
01053             */
01054            (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp,
01055                                                &(encd->encContentInfo));
01056 
01057            /*
01058             * Stop notify.
01059             */
01060            SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
01061        }
01062        break;
01063 
01064       case SEC_OID_PKCS7_DATA:
01065        /*
01066         * If a output callback has been specified, we want to set the filter
01067         * to call the callback.  This is taken care of in 
01068         * sec_pkcs7_decoder_start_decrypt() or 
01069         * sec_pkcs7_decoder_start_digests() for the other content types.
01070         */ 
01071        
01072        if (before && dest == &(cinfo->content.data)) {
01073 
01074            /* 
01075             * Set the filter proc up.
01076             */
01077            SEC_ASN1DecoderSetFilterProc (p7dcx->dcx,
01078                                      sec_pkcs7_decoder_filter,
01079                                      p7dcx,
01080                                      (PRBool)(p7dcx->cb != NULL));
01081            break;
01082        }
01083 
01084        if (after && dest == &(cinfo->content.data)) {
01085            /*
01086             * Time to clean up after ourself, stop the Notify and Filter
01087             * procedures.
01088             */
01089            SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
01090            SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
01091        }
01092        break;
01093 
01094       default:
01095        SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
01096        break;
01097     }
01098 }
01099 
01100 
01101 SEC_PKCS7DecoderContext *
01102 SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback cb, void *cb_arg,
01103                     SECKEYGetPasswordKey pwfn, void *pwfn_arg,
01104                     SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, 
01105                     void *decrypt_key_cb_arg,
01106                     SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb)
01107 {
01108     SEC_PKCS7DecoderContext *p7dcx;
01109     SEC_ASN1DecoderContext *dcx;
01110     SEC_PKCS7ContentInfo *cinfo;
01111     PRArenaPool *poolp;
01112 
01113     poolp = PORT_NewArena (1024);         /* XXX what is right value? */
01114     if (poolp == NULL)
01115        return NULL;
01116 
01117     cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo));
01118     if (cinfo == NULL) {
01119        PORT_FreeArena (poolp, PR_FALSE);
01120        return NULL;
01121     }
01122 
01123     cinfo->poolp = poolp;
01124     cinfo->pwfn = pwfn;
01125     cinfo->pwfn_arg = pwfn_arg;
01126     cinfo->created = PR_FALSE;
01127     cinfo->refCount = 1;
01128 
01129     p7dcx = 
01130       (SEC_PKCS7DecoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7DecoderContext));
01131     if (p7dcx == NULL) {
01132        PORT_FreeArena (poolp, PR_FALSE);
01133        return NULL;
01134     }
01135 
01136     p7dcx->tmp_poolp = PORT_NewArena (1024);     /* XXX what is right value? */
01137     if (p7dcx->tmp_poolp == NULL) {
01138        PORT_Free (p7dcx);
01139        PORT_FreeArena (poolp, PR_FALSE);
01140        return NULL;
01141     }
01142 
01143     dcx = SEC_ASN1DecoderStart (poolp, cinfo, sec_PKCS7ContentInfoTemplate);
01144     if (dcx == NULL) {
01145        PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE);
01146        PORT_Free (p7dcx);
01147        PORT_FreeArena (poolp, PR_FALSE);
01148        return NULL;
01149     }
01150 
01151     SEC_ASN1DecoderSetNotifyProc (dcx, sec_pkcs7_decoder_notify, p7dcx);
01152 
01153     p7dcx->dcx = dcx;
01154     p7dcx->cinfo = cinfo;
01155     p7dcx->cb = cb;
01156     p7dcx->cb_arg = cb_arg;
01157     p7dcx->pwfn = pwfn;
01158     p7dcx->pwfn_arg = pwfn_arg;
01159     p7dcx->dkcb = decrypt_key_cb;
01160     p7dcx->dkcb_arg = decrypt_key_cb_arg;
01161     p7dcx->decrypt_allowed_cb = decrypt_allowed_cb;
01162 
01163     return p7dcx;
01164 }
01165 
01166 
01167 /*
01168  * Do the next chunk of PKCS7 decoding.  If there is a problem, set
01169  * an error and return a failure status.  Note that in the case of
01170  * an error, this routine is still prepared to be called again and
01171  * again in case that is the easiest route for our caller to take.
01172  * We simply detect it and do not do anything except keep setting
01173  * that error in case our caller has not noticed it yet...
01174  */
01175 SECStatus
01176 SEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx,
01177                      const char *buf, unsigned long len)
01178 {
01179     if (p7dcx->cinfo != NULL && p7dcx->dcx != NULL) { 
01180        PORT_Assert (p7dcx->error == 0);
01181        if (p7dcx->error == 0) {
01182            if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) {
01183               p7dcx->error = PORT_GetError();
01184               PORT_Assert (p7dcx->error);
01185               if (p7dcx->error == 0)
01186                   p7dcx->error = -1;
01187            }
01188        }
01189     }
01190 
01191     if (p7dcx->error) {
01192        if (p7dcx->dcx != NULL) {
01193            (void) SEC_ASN1DecoderFinish (p7dcx->dcx);
01194            p7dcx->dcx = NULL;
01195        }
01196        if (p7dcx->cinfo != NULL) {
01197            SEC_PKCS7DestroyContentInfo (p7dcx->cinfo);
01198            p7dcx->cinfo = NULL;
01199        }
01200        PORT_SetError (p7dcx->error);
01201        return SECFailure;
01202     }
01203 
01204     return SECSuccess;
01205 }
01206 
01207 
01208 SEC_PKCS7ContentInfo *
01209 SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx)
01210 {
01211     SEC_PKCS7ContentInfo *cinfo;
01212 
01213     cinfo = p7dcx->cinfo;
01214     if (p7dcx->dcx != NULL) {
01215        if (SEC_ASN1DecoderFinish (p7dcx->dcx) != SECSuccess) {
01216            SEC_PKCS7DestroyContentInfo (cinfo);
01217            cinfo = NULL;
01218        }
01219     }
01220     /* free any NSS data structures */
01221     if (p7dcx->worker.decryptobj) {
01222         sec_PKCS7DestroyDecryptObject (p7dcx->worker.decryptobj);
01223     }
01224     PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE);
01225     PORT_Free (p7dcx);
01226     return cinfo;
01227 }
01228 
01229 
01230 SEC_PKCS7ContentInfo *
01231 SEC_PKCS7DecodeItem(SECItem *p7item,
01232                   SEC_PKCS7DecoderContentCallback cb, void *cb_arg,
01233                   SECKEYGetPasswordKey pwfn, void *pwfn_arg,
01234                   SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, 
01235                   void *decrypt_key_cb_arg,
01236                   SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb)
01237 {
01238     SEC_PKCS7DecoderContext *p7dcx;
01239 
01240     p7dcx = SEC_PKCS7DecoderStart(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb,
01241                               decrypt_key_cb_arg, decrypt_allowed_cb);
01242     (void) SEC_PKCS7DecoderUpdate(p7dcx, (char *) p7item->data, p7item->len);
01243     return SEC_PKCS7DecoderFinish(p7dcx);
01244 }
01245 
01246 /*
01247  * Abort the ASN.1 stream. Used by pkcs 12
01248  */
01249 void
01250 SEC_PKCS7DecoderAbort(SEC_PKCS7DecoderContext *p7dcx, int error)
01251 {
01252     PORT_Assert(p7dcx);
01253     SEC_ASN1DecoderAbort(p7dcx->dcx, error);
01254 }
01255 
01256 
01257 /*
01258  * If the thing contains any certs or crls return true; false otherwise.
01259  */
01260 PRBool
01261 SEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo)
01262 {
01263     SECOidTag kind;
01264     SECItem **certs;
01265     CERTSignedCrl **crls;
01266 
01267     kind = SEC_PKCS7ContentType (cinfo);
01268     switch (kind) {
01269       default:
01270       case SEC_OID_PKCS7_DATA:
01271       case SEC_OID_PKCS7_DIGESTED_DATA:
01272       case SEC_OID_PKCS7_ENVELOPED_DATA:
01273       case SEC_OID_PKCS7_ENCRYPTED_DATA:
01274        return PR_FALSE;
01275       case SEC_OID_PKCS7_SIGNED_DATA:
01276        certs = cinfo->content.signedData->rawCerts;
01277        crls = cinfo->content.signedData->crls;
01278        break;
01279       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
01280        certs = cinfo->content.signedAndEnvelopedData->rawCerts;
01281        crls = cinfo->content.signedAndEnvelopedData->crls;
01282        break;
01283     }
01284 
01285     /*
01286      * I know this could be collapsed, but I was in a mood to be explicit.
01287      */
01288     if (certs != NULL && certs[0] != NULL)
01289        return PR_TRUE;
01290     else if (crls != NULL && crls[0] != NULL)
01291        return PR_TRUE;
01292     else
01293        return PR_FALSE;
01294 }
01295 
01296 /* return the content length...could use GetContent, however we
01297  * need the encrypted content length 
01298  */
01299 PRBool
01300 SEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen)
01301 {
01302     SECItem *item = NULL;
01303 
01304     if(cinfo == NULL) {
01305        return PR_TRUE;
01306     }
01307 
01308     switch(SEC_PKCS7ContentType(cinfo)) 
01309     {
01310        case SEC_OID_PKCS7_DATA:
01311            item = cinfo->content.data;
01312            break;
01313        case SEC_OID_PKCS7_ENCRYPTED_DATA:
01314            item = &cinfo->content.encryptedData->encContentInfo.encContent;
01315            break;
01316        default:
01317            /* add other types */
01318            return PR_FALSE;
01319     }
01320 
01321     if(!item) {
01322        return PR_TRUE;
01323     } else if(item->len <= minLen) {
01324        return PR_TRUE;
01325     }
01326 
01327     return PR_FALSE;
01328 }
01329 
01330 
01331 PRBool
01332 SEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo)
01333 {
01334     SECOidTag kind;
01335 
01336     kind = SEC_PKCS7ContentType (cinfo);
01337     switch (kind) {
01338       default:
01339       case SEC_OID_PKCS7_DATA:
01340       case SEC_OID_PKCS7_DIGESTED_DATA:
01341       case SEC_OID_PKCS7_SIGNED_DATA:
01342        return PR_FALSE;
01343       case SEC_OID_PKCS7_ENCRYPTED_DATA:
01344       case SEC_OID_PKCS7_ENVELOPED_DATA:
01345       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
01346        return PR_TRUE;
01347     }
01348 }
01349 
01350 
01351 /*
01352  * If the PKCS7 content has a signature (not just *could* have a signature)
01353  * return true; false otherwise.  This can/should be called before calling
01354  * VerifySignature, which will always indicate failure if no signature is
01355  * present, but that does not mean there even was a signature!
01356  * Note that the content itself can be empty (detached content was sent
01357  * another way); it is the presence of the signature that matters.
01358  */
01359 PRBool
01360 SEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo)
01361 {
01362     SECOidTag kind;
01363     SEC_PKCS7SignerInfo **signerinfos;
01364 
01365     kind = SEC_PKCS7ContentType (cinfo);
01366     switch (kind) {
01367       default:
01368       case SEC_OID_PKCS7_DATA:
01369       case SEC_OID_PKCS7_DIGESTED_DATA:
01370       case SEC_OID_PKCS7_ENVELOPED_DATA:
01371       case SEC_OID_PKCS7_ENCRYPTED_DATA:
01372        return PR_FALSE;
01373       case SEC_OID_PKCS7_SIGNED_DATA:
01374        signerinfos = cinfo->content.signedData->signerInfos;
01375        break;
01376       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
01377        signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
01378        break;
01379     }
01380 
01381     /*
01382      * I know this could be collapsed; but I kind of think it will get
01383      * more complicated before I am finished, so...
01384      */
01385     if (signerinfos != NULL && signerinfos[0] != NULL)
01386        return PR_TRUE;
01387     else
01388        return PR_FALSE;
01389 }
01390 
01391 
01392 /*
01393  * SEC_PKCS7ContentVerifySignature
01394  *     Look at a PKCS7 contentInfo and check if the signature is good.
01395  *     The digest was either calculated earlier (and is stored in the
01396  *     contentInfo itself) or is passed in via "detached_digest".
01397  *
01398  *     The verification checks that the signing cert is valid and trusted
01399  *     for the purpose specified by "certusage".
01400  *
01401  *     In addition, if "keepcerts" is true, add any new certificates found
01402  *     into our local database.
01403  *
01404  * XXX Each place which returns PR_FALSE should be sure to have a good
01405  * error set for inspection by the caller.  Alternatively, we could create
01406  * an enumeration of success and each type of failure and return that
01407  * instead of a boolean.  For now, the default in a bad situation is to
01408  * set the error to SEC_ERROR_PKCS7_BAD_SIGNATURE.  But this should be
01409  * reviewed; better (more specific) errors should be possible (to distinguish
01410  * a signature failure from a badly-formed pkcs7 signedData, for example).
01411  * Some of the errors should probably just be SEC_ERROR_BAD_SIGNATURE,
01412  * but that has a less helpful error string associated with it right now;
01413  * if/when that changes, review and change these as needed.
01414  *
01415  * XXX This is broken wrt signedAndEnvelopedData.  In that case, the
01416  * message digest is doubly encrypted -- first encrypted with the signer
01417  * private key but then again encrypted with the bulk encryption key used
01418  * to encrypt the content.  So before we can pass the digest to VerifyDigest,
01419  * we need to decrypt it with the bulk encryption key.  Also, in this case,
01420  * there should be NO authenticatedAttributes (signerinfo->authAttr should
01421  * be NULL).
01422  */
01423 static PRBool
01424 sec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo,
01425                         SECCertUsage certusage,
01426                         SECItem *detached_digest,
01427                         HASH_HashType digest_type,
01428                         PRBool keepcerts)
01429 {
01430     SECAlgorithmID **digestalgs, *bulkid;
01431     SECItem *digest;
01432     SECItem **digests;
01433     SECItem **rawcerts;
01434     CERTSignedCrl **crls;
01435     SEC_PKCS7SignerInfo **signerinfos, *signerinfo;
01436     CERTCertificate *cert, **certs;
01437     PRBool goodsig;
01438     CERTCertDBHandle *certdb, *defaultdb; 
01439     SECOidData *algiddata;
01440     int i, certcount;
01441     SECKEYPublicKey *publickey;
01442     SECItem *content_type;
01443     PK11SymKey *sigkey;
01444     SECItem *encoded_stime;
01445     int64 stime;
01446     SECStatus rv;
01447 
01448     /*
01449      * Everything needed in order to "goto done" safely.
01450      */
01451     goodsig = PR_FALSE;
01452     certcount = 0;
01453     cert = NULL;
01454     certs = NULL;
01455     certdb = NULL;
01456     defaultdb = CERT_GetDefaultCertDB();
01457     publickey = NULL;
01458 
01459     if (! SEC_PKCS7ContentIsSigned(cinfo)) {
01460        PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01461        goto done;
01462     }
01463 
01464     PORT_Assert (cinfo->contentTypeTag != NULL);
01465 
01466     switch (cinfo->contentTypeTag->offset) {
01467       default:
01468       case SEC_OID_PKCS7_DATA:
01469       case SEC_OID_PKCS7_DIGESTED_DATA:
01470       case SEC_OID_PKCS7_ENVELOPED_DATA:
01471       case SEC_OID_PKCS7_ENCRYPTED_DATA:
01472        /* Could only get here if SEC_PKCS7ContentIsSigned is broken. */
01473        PORT_Assert (0);
01474       case SEC_OID_PKCS7_SIGNED_DATA:
01475        {
01476            SEC_PKCS7SignedData *sdp;
01477 
01478            sdp = cinfo->content.signedData;
01479            digestalgs = sdp->digestAlgorithms;
01480            digests = sdp->digests;
01481            rawcerts = sdp->rawCerts;
01482            crls = sdp->crls;
01483            signerinfos = sdp->signerInfos;
01484            content_type = &(sdp->contentInfo.contentType);
01485            sigkey = NULL;
01486            bulkid = NULL;
01487        }
01488        break;
01489       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
01490        {
01491            SEC_PKCS7SignedAndEnvelopedData *saedp;
01492 
01493            saedp = cinfo->content.signedAndEnvelopedData;
01494            digestalgs = saedp->digestAlgorithms;
01495            digests = saedp->digests;
01496            rawcerts = saedp->rawCerts;
01497            crls = saedp->crls;
01498            signerinfos = saedp->signerInfos;
01499            content_type = &(saedp->encContentInfo.contentType);
01500            sigkey = saedp->sigKey;
01501            bulkid = &(saedp->encContentInfo.contentEncAlg);
01502        }
01503        break;
01504     }
01505 
01506     if ((signerinfos == NULL) || (signerinfos[0] == NULL)) {
01507        PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01508        goto done;
01509     }
01510 
01511     /*
01512      * XXX Need to handle multiple signatures; checking them is easy,
01513      * but what should be the semantics here (like, return value)?
01514      */
01515     if (signerinfos[1] != NULL) {
01516        PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01517        goto done;
01518     }
01519 
01520     signerinfo = signerinfos[0];
01521 
01522     /*
01523      * XXX I would like to just pass the issuerAndSN, along with the rawcerts
01524      * and crls, to some function that did all of this certificate stuff
01525      * (open/close the database if necessary, verifying the certs, etc.)
01526      * and gave me back a cert pointer if all was good.
01527      */
01528     certdb = defaultdb;
01529     if (certdb == NULL) {
01530        goto done;
01531     }
01532 
01533     certcount = 0;
01534     if (rawcerts != NULL) {
01535        for (; rawcerts[certcount] != NULL; certcount++) {
01536            /* just counting */
01537        }
01538     }
01539 
01540     /*
01541      * Note that the result of this is that each cert in "certs"
01542      * needs to be destroyed.
01543      */
01544     rv = CERT_ImportCerts(certdb, certusage, certcount, rawcerts, &certs,
01545                        keepcerts, PR_FALSE, NULL);
01546     if ( rv != SECSuccess ) {
01547        goto done;
01548     }
01549 
01550     /*
01551      * This cert will also need to be freed, but since we save it
01552      * in signerinfo for later, we do not want to destroy it when
01553      * we leave this function -- we let the clean-up of the entire
01554      * cinfo structure later do the destroy of this cert.
01555      */
01556     cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->issuerAndSN);
01557     if (cert == NULL) {
01558        goto done;
01559     }
01560 
01561     signerinfo->cert = cert;
01562 
01563     /*
01564      * Get and convert the signing time; if available, it will be used
01565      * both on the cert verification and for importing the sender
01566      * email profile.
01567      */
01568     encoded_stime = SEC_PKCS7GetSigningTime (cinfo);
01569     if (encoded_stime != NULL) {
01570        if (DER_DecodeTimeChoice (&stime, encoded_stime) != SECSuccess)
01571            encoded_stime = NULL;   /* conversion failed, so pretend none */
01572     }
01573 
01574     /*
01575      * XXX  This uses the signing time, if available.  Additionally, we
01576      * might want to, if there is no signing time, get the message time
01577      * from the mail header itself, and use that.  That would require
01578      * a change to our interface though, and for S/MIME callers to pass
01579      * in a time (and for non-S/MIME callers to pass in nothing, or
01580      * maybe make them pass in the current time, always?).
01581      */
01582     if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage,
01583                       encoded_stime != NULL ? stime : PR_Now(),
01584                       cinfo->pwfn_arg, NULL) != SECSuccess)
01585        {
01586        /*
01587         * XXX Give the user an option to check the signature anyway?
01588         * If we want to do this, need to give a way to leave and display
01589         * some dialog and get the answer and come back through (or do
01590         * the rest of what we do below elsewhere, maybe by putting it
01591         * in a function that we call below and could call from a dialog
01592         * finish handler).
01593         */
01594        goto savecert;
01595     }
01596 
01597     publickey = CERT_ExtractPublicKey (cert);
01598     if (publickey == NULL)
01599        goto done;
01600 
01601     /*
01602      * XXX No!  If digests is empty, see if we can create it now by
01603      * digesting the contents.  This is necessary if we want to allow
01604      * somebody to do a simple decode (without filtering, etc.) and
01605      * then later call us here to do the verification.
01606      * OR, we can just specify that the interface to this routine
01607      * *requires* that the digest(s) be done before calling and either
01608      * stashed in the struct itself or passed in explicitly (as would
01609      * be done for detached contents).
01610      */
01611     if ((digests == NULL || digests[0] == NULL)
01612        && (detached_digest == NULL || detached_digest->data == NULL))
01613        goto done;
01614 
01615     /*
01616      * Find and confirm digest algorithm.
01617      */
01618     algiddata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
01619 
01620     if (detached_digest != NULL) {
01621        HASH_HashType found_type = HASH_GetHashTypeByOidTag(algiddata->offset);
01622        unsigned int hashLen     = HASH_ResultLen(digest_type);
01623 
01624        if (digest_type != found_type || 
01625            digest_type == HASH_AlgNULL || 
01626            detached_digest->len != hashLen) {
01627            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01628            goto done;
01629        }
01630        digest = detached_digest;
01631     } else {
01632        PORT_Assert (digestalgs != NULL && digestalgs[0] != NULL);
01633        if (digestalgs == NULL || digestalgs[0] == NULL) {
01634            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01635            goto done;
01636        }
01637 
01638        /*
01639         * pick digest matching signerinfo->digestAlg from digests
01640         */
01641        if (algiddata == NULL) {
01642            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01643            goto done;
01644        }
01645        for (i = 0; digestalgs[i] != NULL; i++) {
01646            if (SECOID_FindOID (&(digestalgs[i]->algorithm)) == algiddata)
01647               break;
01648        }
01649        if (digestalgs[i] == NULL) {
01650            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01651            goto done;
01652        }
01653 
01654        digest = digests[i];
01655     }
01656 
01657     /*
01658      * XXX This may not be the right set of algorithms to check.
01659      * I'd prefer to trust that just calling VFY_Verify{Data,Digest}
01660      * would do the right thing (and set an error if it could not);
01661      * then additional algorithms could be handled by that code
01662      * and we would Just Work.  So this check should just be removed,
01663      * but not until the VFY code is better at setting errors.
01664      */
01665     algiddata = SECOID_FindOID (&(signerinfo->digestEncAlg.algorithm));
01666     if (algiddata == NULL ||
01667        ((algiddata->offset != SEC_OID_PKCS1_RSA_ENCRYPTION) &&
01668 #ifdef NSS_ECC_MORE_THAN_SUITE_B
01669         (algiddata->offset != SEC_OID_ANSIX962_EC_PUBLIC_KEY) &&
01670 #endif
01671         (algiddata->offset != SEC_OID_ANSIX9_DSA_SIGNATURE))) {
01672        PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01673        goto done;
01674     }
01675 
01676     if (signerinfo->authAttr != NULL) {
01677        SEC_PKCS7Attribute *attr;
01678        SECItem *value;
01679        SECItem encoded_attrs;
01680 
01681        /*
01682         * We have a sigkey only for signedAndEnvelopedData, which is
01683         * not supposed to have any authenticated attributes.
01684         */
01685        if (sigkey != NULL) {
01686            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01687            goto done;
01688        }
01689 
01690        /*
01691         * PKCS #7 says that if there are any authenticated attributes,
01692         * then there must be one for content type which matches the
01693         * content type of the content being signed, and there must
01694         * be one for message digest which matches our message digest.
01695         * So check these things first.
01696         * XXX Might be nice to have a compare-attribute-value function
01697         * which could collapse the following nicely.
01698         */
01699        attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
01700                                    SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
01701        value = sec_PKCS7AttributeValue (attr);
01702        if (value == NULL || value->len != content_type->len) {
01703            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01704            goto done;
01705        }
01706        if (PORT_Memcmp (value->data, content_type->data, value->len) != 0) {
01707            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01708            goto done;
01709        }
01710 
01711        attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
01712                                    SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
01713        value = sec_PKCS7AttributeValue (attr);
01714        if (value == NULL || value->len != digest->len) {
01715            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01716            goto done;
01717        }
01718        if (PORT_Memcmp (value->data, digest->data, value->len) != 0) {
01719            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01720            goto done;
01721        }
01722 
01723        /*
01724         * Okay, we met the constraints of the basic attributes.
01725         * Now check the signature, which is based on a digest of
01726         * the DER-encoded authenticated attributes.  So, first we
01727         * encode and then we digest/verify.
01728         */
01729        encoded_attrs.data = NULL;
01730        encoded_attrs.len = 0;
01731        if (sec_PKCS7EncodeAttributes (NULL, &encoded_attrs,
01732                                    &(signerinfo->authAttr)) == NULL)
01733            goto done;
01734 
01735        if (encoded_attrs.data == NULL || encoded_attrs.len == 0) {
01736            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01737            goto done;
01738        }
01739 
01740        /*
01741         * XXX the 5th (algid) argument should be the signature algorithm.
01742         * See sec_pkcs7_pick_sign_alg in p7encode.c.
01743         */
01744        goodsig = (PRBool)(VFY_VerifyData (encoded_attrs.data, 
01745                                encoded_attrs.len,
01746                                publickey, &(signerinfo->encDigest),
01747                                SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)),
01748                                cinfo->pwfn_arg) == SECSuccess);
01749        PORT_Free (encoded_attrs.data);
01750     } else {
01751        SECItem *sig;
01752        SECItem holder;
01753        SECStatus rv;
01754 
01755        /*
01756         * No authenticated attributes.
01757         * The signature is based on the plain message digest.
01758         */
01759 
01760        sig = &(signerinfo->encDigest);
01761        if (sig->len == 0) {        /* bad signature */
01762            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01763            goto done;
01764        }
01765 
01766        if (sigkey != NULL) {
01767            sec_PKCS7CipherObject *decryptobj;
01768            unsigned int buflen;
01769 
01770            /*
01771             * For signedAndEnvelopedData, we first must decrypt the encrypted
01772             * digest with the bulk encryption key.  The result is the normal
01773             * encrypted digest (aka the signature).
01774             */
01775            decryptobj = sec_PKCS7CreateDecryptObject (sigkey, bulkid);
01776            if (decryptobj == NULL)
01777               goto done;
01778 
01779            buflen = sec_PKCS7DecryptLength (decryptobj, sig->len, PR_TRUE);
01780            PORT_Assert (buflen);
01781            if (buflen == 0) {             /* something is wrong */
01782               sec_PKCS7DestroyDecryptObject (decryptobj);
01783               goto done;
01784            }
01785 
01786            holder.data = (unsigned char*)PORT_Alloc (buflen);
01787            if (holder.data == NULL) {
01788               sec_PKCS7DestroyDecryptObject (decryptobj);
01789               goto done;
01790            }
01791 
01792            rv = sec_PKCS7Decrypt (decryptobj, holder.data, &holder.len, buflen,
01793                                sig->data, sig->len, PR_TRUE);
01794            sec_PKCS7DestroyDecryptObject (decryptobj);
01795            if (rv != SECSuccess) {
01796               goto done;
01797            }
01798 
01799            sig = &holder;
01800        }
01801 
01802        /*
01803         * XXX the 4th (algid) argument should be the signature algorithm.
01804         * See sec_pkcs7_pick_sign_alg in p7encode.c.
01805         */
01806        goodsig = (PRBool)(VFY_VerifyDigest (digest, publickey, sig,
01807                                  SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)),
01808                                  cinfo->pwfn_arg)
01809                  == SECSuccess);
01810 
01811        if (sigkey != NULL) {
01812            PORT_Assert (sig == &holder);
01813            PORT_ZFree (holder.data, holder.len);
01814        }
01815     }
01816 
01817     if (! goodsig) {
01818        /*
01819         * XXX Change the generic error into our specific one, because
01820         * in that case we get a better explanation out of the Security
01821         * Advisor.  This is really a bug in our error strings (the
01822         * "generic" error has a lousy/wrong message associated with it
01823         * which assumes the signature verification was done for the
01824         * purposes of checking the issuer signature on a certificate)
01825         * but this is at least an easy workaround and/or in the
01826         * Security Advisor, which specifically checks for the error
01827         * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
01828         * in that case but does not similarly check for
01829         * SEC_ERROR_BAD_SIGNATURE.  It probably should, but then would
01830         * probably say the wrong thing in the case that it *was* the
01831         * certificate signature check that failed during the cert
01832         * verification done above.  Our error handling is really a mess.
01833         */
01834        if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
01835            PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
01836     }
01837 
01838 savecert:
01839     /*
01840      * Only save the smime profile if we are checking an email message and
01841      * the cert has an email address in it.
01842      */
01843     if ( cert->emailAddr && cert->emailAddr[0] &&
01844        ( ( certusage == certUsageEmailSigner ) ||
01845         ( certusage == certUsageEmailRecipient ) ) ) {
01846        SECItem *profile = NULL;
01847        int save_error;
01848 
01849        /*
01850         * Remember the current error set because we do not care about
01851         * anything set by the functions we are about to call.
01852         */
01853        save_error = PORT_GetError();
01854 
01855        if (goodsig && (signerinfo->authAttr != NULL)) {
01856            /*
01857             * If the signature is good, then we can save the S/MIME profile,
01858             * if we have one.
01859             */
01860            SEC_PKCS7Attribute *attr;
01861 
01862            attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
01863                                       SEC_OID_PKCS9_SMIME_CAPABILITIES,
01864                                       PR_TRUE);
01865            profile = sec_PKCS7AttributeValue (attr);
01866        }
01867 
01868        rv = CERT_SaveSMimeProfile (cert, profile, encoded_stime);
01869 
01870        /*
01871         * Restore the saved error in case the calls above set a new
01872         * one that we do not actually care about.
01873         */
01874        PORT_SetError (save_error);
01875 
01876        /*
01877         * XXX Failure is not indicated anywhere -- the signature
01878         * verification itself is unaffected by whether or not the
01879         * profile was successfully saved.
01880         */
01881     }
01882        
01883 
01884 done:
01885 
01886     /*
01887      * See comment above about why we do not want to destroy cert
01888      * itself here.
01889      */
01890 
01891     if (certs != NULL)
01892        CERT_DestroyCertArray (certs, certcount);
01893 
01894     if (publickey != NULL)
01895        SECKEY_DestroyPublicKey (publickey);
01896 
01897     return goodsig;
01898 }
01899 
01900 /*
01901  * SEC_PKCS7VerifySignature
01902  *     Look at a PKCS7 contentInfo and check if the signature is good.
01903  *     The verification checks that the signing cert is valid and trusted
01904  *     for the purpose specified by "certusage".
01905  *
01906  *     In addition, if "keepcerts" is true, add any new certificates found
01907  *     into our local database.
01908  */
01909 PRBool
01910 SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo,
01911                       SECCertUsage certusage,
01912                       PRBool keepcerts)
01913 {
01914     return sec_pkcs7_verify_signature (cinfo, certusage,
01915                                    NULL, HASH_AlgNULL, keepcerts);
01916 }
01917 
01918 /*
01919  * SEC_PKCS7VerifyDetachedSignature
01920  *     Look at a PKCS7 contentInfo and check if the signature matches
01921  *     a passed-in digest (calculated, supposedly, from detached contents).
01922  *     The verification checks that the signing cert is valid and trusted
01923  *     for the purpose specified by "certusage".
01924  *
01925  *     In addition, if "keepcerts" is true, add any new certificates found
01926  *     into our local database.
01927  */
01928 PRBool
01929 SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo,
01930                              SECCertUsage certusage,
01931                              SECItem *detached_digest,
01932                              HASH_HashType digest_type,
01933                              PRBool keepcerts)
01934 {
01935     return sec_pkcs7_verify_signature (cinfo, certusage,
01936                                    detached_digest, digest_type,
01937                                    keepcerts);
01938 }
01939 
01940 
01941 /*
01942  * Return the asked-for portion of the name of the signer of a PKCS7
01943  * signed object.
01944  *
01945  * Returns a pointer to allocated memory, which must be freed.
01946  * A NULL return value is an error.
01947  */
01948 
01949 #define sec_common_name 1
01950 #define sec_email_address 2
01951 
01952 static char *
01953 sec_pkcs7_get_signer_cert_info(SEC_PKCS7ContentInfo *cinfo, int selector)
01954 {
01955     SECOidTag kind;
01956     SEC_PKCS7SignerInfo **signerinfos;
01957     CERTCertificate *signercert;
01958     char *container;
01959 
01960     kind = SEC_PKCS7ContentType (cinfo);
01961     switch (kind) {
01962       default:
01963       case SEC_OID_PKCS7_DATA:
01964       case SEC_OID_PKCS7_DIGESTED_DATA:
01965       case SEC_OID_PKCS7_ENVELOPED_DATA:
01966       case SEC_OID_PKCS7_ENCRYPTED_DATA:
01967        PORT_Assert (0);
01968        return NULL;
01969       case SEC_OID_PKCS7_SIGNED_DATA:
01970        {
01971            SEC_PKCS7SignedData *sdp;
01972 
01973            sdp = cinfo->content.signedData;
01974            signerinfos = sdp->signerInfos;
01975        }
01976        break;
01977       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
01978        {
01979            SEC_PKCS7SignedAndEnvelopedData *saedp;
01980 
01981            saedp = cinfo->content.signedAndEnvelopedData;
01982            signerinfos = saedp->signerInfos;
01983        }
01984        break;
01985     }
01986 
01987     if (signerinfos == NULL || signerinfos[0] == NULL)
01988        return NULL;
01989 
01990     signercert = signerinfos[0]->cert;
01991 
01992     /*
01993      * No cert there; see if we can find one by calling verify ourselves.
01994      */
01995     if (signercert == NULL) {
01996        /*
01997         * The cert usage does not matter in this case, because we do not
01998         * actually care about the verification itself, but we have to pick
01999         * some valid usage to pass in.
02000         */
02001        (void) sec_pkcs7_verify_signature (cinfo, certUsageEmailSigner,
02002                                       NULL, HASH_AlgNULL, PR_FALSE);
02003        signercert = signerinfos[0]->cert;
02004        if (signercert == NULL)
02005            return NULL;
02006     }
02007 
02008     switch (selector) {
02009       case sec_common_name:
02010        container = CERT_GetCommonName (&signercert->subject);
02011        break;
02012       case sec_email_address:
02013        if(signercert->emailAddr && signercert->emailAddr[0]) {
02014            container = PORT_Strdup(signercert->emailAddr);
02015        } else {
02016            container = NULL;
02017        }
02018        break;
02019       default:
02020        PORT_Assert (0);
02021        container = NULL;
02022        break;
02023     }
02024 
02025     return container;
02026 }
02027 
02028 char *
02029 SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo)
02030 {
02031     return sec_pkcs7_get_signer_cert_info(cinfo, sec_common_name);
02032 }
02033 
02034 char *
02035 SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo)
02036 {
02037     return sec_pkcs7_get_signer_cert_info(cinfo, sec_email_address);
02038 }
02039 
02040 
02041 /*
02042  * Return the signing time, in UTCTime format, of a PKCS7 contentInfo.
02043  */
02044 SECItem *
02045 SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo)
02046 {
02047     SEC_PKCS7SignerInfo **signerinfos;
02048     SEC_PKCS7Attribute *attr;
02049 
02050     if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
02051        return NULL;
02052 
02053     signerinfos = cinfo->content.signedData->signerInfos;
02054 
02055     /*
02056      * No signature, or more than one, means no deal.
02057      */
02058     if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
02059        return NULL;
02060 
02061     attr = sec_PKCS7FindAttribute (signerinfos[0]->authAttr,
02062                                SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
02063     return sec_PKCS7AttributeValue (attr);
02064 }