tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cmsdecode.c (26724B)


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