tor-browser

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

secmime.c (24893B)


      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 * Stuff specific to S/MIME policy and interoperability.
      7 * Depends on PKCS7, but there should be no dependency the other way around.
      8 */
      9 
     10 #include "secmime.h"
     11 #include "secoid.h"
     12 #include "pk11func.h"
     13 #include "ciferfam.h" /* for CIPHER_FAMILY symbols */
     14 #include "secasn1.h"
     15 #include "secitem.h"
     16 #include "cert.h"
     17 #include "keyhi.h"
     18 #include "secerr.h"
     19 
     20 typedef struct smime_cipher_map_struct {
     21    unsigned long cipher;
     22    SECOidTag algtag;
     23    SECItem *parms;
     24 } smime_cipher_map;
     25 
     26 /*
     27 * These are macros because I think some subsequent parameters,
     28 * like those for RC5, will want to use them, too, separately.
     29 */
     30 #define SMIME_DER_INTVAL_16 SEC_ASN1_INTEGER, 0x01, 0x10
     31 #define SMIME_DER_INTVAL_40 SEC_ASN1_INTEGER, 0x01, 0x28
     32 #define SMIME_DER_INTVAL_64 SEC_ASN1_INTEGER, 0x01, 0x40
     33 #define SMIME_DER_INTVAL_128 SEC_ASN1_INTEGER, 0x02, 0x00, 0x80
     34 
     35 #ifdef SMIME_DOES_RC5 /* will be needed; quiet unused warning for now */
     36 static unsigned char smime_int16[] = { SMIME_DER_INTVAL_16 };
     37 #endif
     38 static unsigned char smime_int40[] = { SMIME_DER_INTVAL_40 };
     39 static unsigned char smime_int64[] = { SMIME_DER_INTVAL_64 };
     40 static unsigned char smime_int128[] = { SMIME_DER_INTVAL_128 };
     41 
     42 static SECItem smime_rc2p40 = { siBuffer, smime_int40, sizeof(smime_int40) };
     43 static SECItem smime_rc2p64 = { siBuffer, smime_int64, sizeof(smime_int64) };
     44 static SECItem smime_rc2p128 = { siBuffer, smime_int128, sizeof(smime_int128) };
     45 
     46 static smime_cipher_map smime_cipher_maps[] = {
     47    { SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &smime_rc2p40 },
     48    { SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &smime_rc2p64 },
     49    { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &smime_rc2p128 },
     50 #ifdef SMIME_DOES_RC5
     51    { SMIME_RC5PAD_64_16_40, SEC_OID_RC5_CBC_PAD, &smime_rc5p40 },
     52    { SMIME_RC5PAD_64_16_64, SEC_OID_RC5_CBC_PAD, &smime_rc5p64 },
     53    { SMIME_RC5PAD_64_16_128, SEC_OID_RC5_CBC_PAD, &smime_rc5p128 },
     54 #endif
     55    { SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL },
     56    { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL }
     57 };
     58 
     59 /*
     60 * Note, the following value really just needs to be an upper bound
     61 * on the ciphers.
     62 */
     63 static const int smime_symmetric_count = sizeof(smime_cipher_maps) / sizeof(smime_cipher_map);
     64 
     65 static unsigned long *smime_prefs, *smime_newprefs;
     66 static int smime_current_pref_index = 0;
     67 static PRBool smime_prefs_complete = PR_FALSE;
     68 static PRBool smime_prefs_changed = PR_TRUE;
     69 
     70 static unsigned long smime_policy_bits = 0;
     71 
     72 static int
     73 smime_mapi_by_cipher(unsigned long cipher)
     74 {
     75    int i;
     76 
     77    for (i = 0; i < smime_symmetric_count; i++) {
     78        if (smime_cipher_maps[i].cipher == cipher)
     79            break;
     80    }
     81 
     82    if (i == smime_symmetric_count)
     83        return -1;
     84 
     85    return i;
     86 }
     87 
     88 /*
     89 * this function locally records the user's preference
     90 */
     91 SECStatus
     92 SECMIME_EnableCipher(long which, int on)
     93 {
     94    unsigned long mask;
     95 
     96    if (smime_newprefs == NULL || smime_prefs_complete) {
     97        /*
     98         * This is either the very first time, or we are starting over.
     99         */
    100        smime_newprefs = (unsigned long *)PORT_ZAlloc(smime_symmetric_count * sizeof(*smime_newprefs));
    101        if (smime_newprefs == NULL)
    102            return SECFailure;
    103        smime_current_pref_index = 0;
    104        smime_prefs_complete = PR_FALSE;
    105    }
    106 
    107    mask = which & CIPHER_FAMILYID_MASK;
    108    if (mask == CIPHER_FAMILYID_MASK) {
    109        /*
    110         * This call signifies that all preferences have been set.
    111         * Move "newprefs" over, after checking first whether or
    112         * not the new ones are different from the old ones.
    113         */
    114        if (smime_prefs != NULL) {
    115            if (PORT_Memcmp(smime_prefs, smime_newprefs,
    116                            smime_symmetric_count * sizeof(*smime_prefs)) == 0)
    117                smime_prefs_changed = PR_FALSE;
    118            else
    119                smime_prefs_changed = PR_TRUE;
    120            PORT_Free(smime_prefs);
    121        }
    122 
    123        smime_prefs = smime_newprefs;
    124        smime_prefs_complete = PR_TRUE;
    125        return SECSuccess;
    126    }
    127 
    128    PORT_Assert(mask == CIPHER_FAMILYID_SMIME);
    129    if (mask != CIPHER_FAMILYID_SMIME) {
    130        /* XXX set an error! */
    131        return SECFailure;
    132    }
    133 
    134    if (on) {
    135        PORT_Assert(smime_current_pref_index < smime_symmetric_count);
    136        if (smime_current_pref_index >= smime_symmetric_count) {
    137            /* XXX set an error! */
    138            return SECFailure;
    139        }
    140 
    141        smime_newprefs[smime_current_pref_index++] = which;
    142    }
    143 
    144    return SECSuccess;
    145 }
    146 
    147 /*
    148 * this function locally records the export policy
    149 */
    150 SECStatus
    151 SECMIME_SetPolicy(long which, int on)
    152 {
    153    unsigned long mask;
    154 
    155    PORT_Assert((which & CIPHER_FAMILYID_MASK) == CIPHER_FAMILYID_SMIME);
    156    if ((which & CIPHER_FAMILYID_MASK) != CIPHER_FAMILYID_SMIME) {
    157        /* XXX set an error! */
    158        return SECFailure;
    159    }
    160 
    161    which &= ~CIPHER_FAMILYID_MASK;
    162 
    163    PORT_Assert(which < 32); /* bits in the long */
    164    if (which >= 32) {
    165        /* XXX set an error! */
    166        return SECFailure;
    167    }
    168 
    169    mask = 1UL << which;
    170 
    171    if (on) {
    172        smime_policy_bits |= mask;
    173    } else {
    174        smime_policy_bits &= ~mask;
    175    }
    176 
    177    return SECSuccess;
    178 }
    179 
    180 /*
    181 * Based on the given algorithm (including its parameters, in some cases!)
    182 * and the given key (may or may not be inspected, depending on the
    183 * algorithm), find the appropriate policy algorithm specification
    184 * and return it.  If no match can be made, -1 is returned.
    185 */
    186 static long
    187 smime_policy_algorithm(SECAlgorithmID *algid, PK11SymKey *key)
    188 {
    189    SECOidTag algtag;
    190 
    191    algtag = SECOID_GetAlgorithmTag(algid);
    192    switch (algtag) {
    193        case SEC_OID_RC2_CBC: {
    194            unsigned int keylen_bits;
    195 
    196            keylen_bits = PK11_GetKeyStrength(key, algid);
    197            switch (keylen_bits) {
    198                case 40:
    199                    return SMIME_RC2_CBC_40;
    200                case 64:
    201                    return SMIME_RC2_CBC_64;
    202                case 128:
    203                    return SMIME_RC2_CBC_128;
    204                default:
    205                    break;
    206            }
    207        } break;
    208        case SEC_OID_DES_CBC:
    209            return SMIME_DES_CBC_56;
    210        case SEC_OID_DES_EDE3_CBC:
    211            return SMIME_DES_EDE3_168;
    212 #ifdef SMIME_DOES_RC5
    213        case SEC_OID_RC5_CBC_PAD:
    214            PORT_Assert(0); /* XXX need to pull out parameters and match */
    215            break;
    216 #endif
    217        default:
    218            break;
    219    }
    220 
    221    return -1;
    222 }
    223 
    224 static PRBool
    225 smime_cipher_allowed(unsigned long which)
    226 {
    227    unsigned long mask;
    228 
    229    which &= ~CIPHER_FAMILYID_MASK;
    230    PORT_Assert(which < 32); /* bits per long (min) */
    231    if (which >= 32)
    232        return PR_FALSE;
    233 
    234    mask = 1UL << which;
    235    if ((mask & smime_policy_bits) == 0)
    236        return PR_FALSE;
    237 
    238    return PR_TRUE;
    239 }
    240 
    241 PRBool
    242 SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
    243 {
    244    long which;
    245 
    246    which = smime_policy_algorithm(algid, key);
    247    if (which < 0)
    248        return PR_FALSE;
    249 
    250    return smime_cipher_allowed((unsigned long)which);
    251 }
    252 
    253 /*
    254 * Does the current policy allow *any* S/MIME encryption (or decryption)?
    255 *
    256 * This tells whether or not *any* S/MIME encryption can be done,
    257 * according to policy.  Callers may use this to do nicer user interface
    258 * (say, greying out a checkbox so a user does not even try to encrypt
    259 * a message when they are not allowed to) or for any reason they want
    260 * to check whether S/MIME encryption (or decryption, for that matter)
    261 * may be done.
    262 *
    263 * It takes no arguments.  The return value is a simple boolean:
    264 *   PR_TRUE means encryption (or decryption) is *possible*
    265 *      (but may still fail due to other reasons, like because we cannot
    266 *      find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
    267 *   PR_FALSE means encryption (or decryption) is not permitted
    268 *
    269 * There are no errors from this routine.
    270 */
    271 PRBool
    272 SECMIME_EncryptionPossible(void)
    273 {
    274    if (smime_policy_bits != 0)
    275        return PR_TRUE;
    276 
    277    return PR_FALSE;
    278 }
    279 
    280 /*
    281 * XXX Would like the "parameters" field to be a SECItem *, but the
    282 * encoder is having trouble with optional pointers to an ANY.  Maybe
    283 * once that is fixed, can change this back...
    284 */
    285 typedef struct smime_capability_struct {
    286    unsigned long cipher; /* local; not part of encoding */
    287    SECOidTag capIDTag;   /* local; not part of encoding */
    288    SECItem capabilityID;
    289    SECItem parameters;
    290 } smime_capability;
    291 
    292 static const SEC_ASN1Template smime_capability_template[] = {
    293    { SEC_ASN1_SEQUENCE,
    294      0, NULL, sizeof(smime_capability) },
    295    { SEC_ASN1_OBJECT_ID,
    296      offsetof(smime_capability, capabilityID) },
    297    { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
    298      offsetof(smime_capability, parameters) },
    299    { 0 }
    300 };
    301 
    302 static const SEC_ASN1Template smime_capabilities_template[] = {
    303    { SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template }
    304 };
    305 
    306 static void
    307 smime_fill_capability(smime_capability *cap)
    308 {
    309    unsigned long cipher;
    310    SECOidTag algtag;
    311    int i;
    312 
    313    algtag = SECOID_FindOIDTag(&(cap->capabilityID));
    314 
    315    for (i = 0; i < smime_symmetric_count; i++) {
    316        if (smime_cipher_maps[i].algtag != algtag)
    317            continue;
    318        /*
    319         * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
    320         * 2 NULLs as equal and NULL and non-NULL as not equal), we could
    321         * use that here instead of all of the following comparison code.
    322         */
    323        if (cap->parameters.data != NULL) {
    324            if (smime_cipher_maps[i].parms == NULL)
    325                continue;
    326            if (cap->parameters.len != smime_cipher_maps[i].parms->len)
    327                continue;
    328            if (PORT_Memcmp(cap->parameters.data,
    329                            smime_cipher_maps[i].parms->data,
    330                            cap->parameters.len) == 0)
    331                break;
    332        } else if (smime_cipher_maps[i].parms == NULL) {
    333            break;
    334        }
    335    }
    336 
    337    if (i == smime_symmetric_count)
    338        cipher = 0;
    339    else
    340        cipher = smime_cipher_maps[i].cipher;
    341 
    342    cap->cipher = cipher;
    343    cap->capIDTag = algtag;
    344 }
    345 
    346 static long
    347 smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
    348 {
    349    PLArenaPool *poolp;
    350    long chosen_cipher;
    351    int *cipher_abilities;
    352    int *cipher_votes;
    353    int strong_mapi;
    354    int rcount, mapi, max;
    355 
    356    if (smime_policy_bits == 0) {
    357        PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
    358        return -1;
    359    }
    360 
    361    chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */
    362 
    363    poolp = PORT_NewArena(1024); /* XXX what is right value? */
    364    if (poolp == NULL)
    365        goto done;
    366 
    367    cipher_abilities = (int *)PORT_ArenaZAlloc(poolp,
    368                                               smime_symmetric_count * sizeof(int));
    369    if (cipher_abilities == NULL)
    370        goto done;
    371 
    372    cipher_votes = (int *)PORT_ArenaZAlloc(poolp,
    373                                           smime_symmetric_count * sizeof(int));
    374    if (cipher_votes == NULL)
    375        goto done;
    376 
    377    /*
    378     * XXX Should have a #define somewhere which specifies default
    379     * strong cipher.  (Or better, a way to configure.)
    380     */
    381 
    382    /* Make triple-DES the strong cipher. */
    383    strong_mapi = smime_mapi_by_cipher(SMIME_DES_EDE3_168);
    384 
    385    PORT_Assert(strong_mapi >= 0);
    386 
    387    for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
    388        SECItem *profile;
    389        smime_capability **caps;
    390        int capi, pref;
    391        SECStatus dstat;
    392 
    393        pref = smime_symmetric_count;
    394        profile = CERT_FindSMimeProfile(rcerts[rcount]);
    395        if (profile != NULL && profile->data != NULL && profile->len > 0) {
    396            caps = NULL;
    397            dstat = SEC_QuickDERDecodeItem(poolp, &caps,
    398                                           smime_capabilities_template,
    399                                           profile);
    400            if (dstat == SECSuccess && caps != NULL) {
    401                for (capi = 0; caps[capi] != NULL; capi++) {
    402                    smime_fill_capability(caps[capi]);
    403                    mapi = smime_mapi_by_cipher(caps[capi]->cipher);
    404                    if (mapi >= 0) {
    405                        cipher_abilities[mapi]++;
    406                        cipher_votes[mapi] += pref;
    407                        --pref;
    408                    }
    409                }
    410            }
    411        } else {
    412            SECKEYPublicKey *key;
    413            unsigned int pklen_bits;
    414 
    415            /*
    416             * XXX This is probably only good for RSA keys.  What I would
    417             * really like is a function to just say;  Is the public key in
    418             * this cert an export-length key?  Then I would not have to
    419             * know things like the value 512, or the kind of key, or what
    420             * a subjectPublicKeyInfo is, etc.
    421             */
    422            key = CERT_ExtractPublicKey(rcerts[rcount]);
    423            if (key != NULL) {
    424                pklen_bits = SECKEY_PublicKeyStrength(key) * 8;
    425                SECKEY_DestroyPublicKey(key);
    426 
    427                if (pklen_bits > 512) {
    428                    cipher_abilities[strong_mapi]++;
    429                    cipher_votes[strong_mapi] += pref;
    430                }
    431            }
    432        }
    433        if (profile != NULL)
    434            SECITEM_FreeItem(profile, PR_TRUE);
    435    }
    436 
    437    max = 0;
    438    for (mapi = 0; mapi < smime_symmetric_count; mapi++) {
    439        if (cipher_abilities[mapi] != rcount)
    440            continue;
    441        if (!smime_cipher_allowed(smime_cipher_maps[mapi].cipher))
    442            continue;
    443        if (cipher_votes[mapi] > max) {
    444            chosen_cipher = smime_cipher_maps[mapi].cipher;
    445            max = cipher_votes[mapi];
    446        } /* XXX else if a tie, let scert break it? */
    447    }
    448 
    449 done:
    450    if (poolp != NULL)
    451        PORT_FreeArena(poolp, PR_FALSE);
    452 
    453    return chosen_cipher;
    454 }
    455 
    456 /*
    457 * XXX This is a hack for now to satisfy our current interface.
    458 * Eventually, with more parameters needing to be specified, just
    459 * looking up the keysize is not going to be sufficient.
    460 */
    461 static int
    462 smime_keysize_by_cipher(unsigned long which)
    463 {
    464    int keysize;
    465 
    466    switch (which) {
    467        case SMIME_RC2_CBC_40:
    468            keysize = 40;
    469            break;
    470        case SMIME_RC2_CBC_64:
    471            keysize = 64;
    472            break;
    473        case SMIME_RC2_CBC_128:
    474            keysize = 128;
    475            break;
    476 #ifdef SMIME_DOES_RC5
    477        case SMIME_RC5PAD_64_16_40:
    478        case SMIME_RC5PAD_64_16_64:
    479        case SMIME_RC5PAD_64_16_128:
    480            /* XXX See comment above; keysize is not enough... */
    481            PORT_Assert(0);
    482            PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    483            keysize = -1;
    484            break;
    485 #endif
    486        case SMIME_DES_CBC_56:
    487        case SMIME_DES_EDE3_168:
    488            /*
    489             * These are special; since the key size is fixed, we actually
    490             * want to *avoid* specifying a key size.
    491             */
    492            keysize = 0;
    493            break;
    494        default:
    495            keysize = -1;
    496            break;
    497    }
    498 
    499    return keysize;
    500 }
    501 
    502 /*
    503 * Start an S/MIME encrypting context.
    504 *
    505 * "scert" is the cert for the sender.  It will be checked for validity.
    506 * "rcerts" are the certs for the recipients.  They will also be checked.
    507 *
    508 * "certdb" is the cert database to use for verifying the certs.
    509 * It can be NULL if a default database is available (like in the client).
    510 *
    511 * This function already does all of the stuff specific to S/MIME protocol
    512 * and local policy; the return value just needs to be passed to
    513 * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
    514 * and finally to SEC_PKCS7DestroyContentInfo().
    515 *
    516 * An error results in a return value of NULL and an error set.
    517 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
    518 */
    519 SEC_PKCS7ContentInfo *
    520 SECMIME_CreateEncrypted(CERTCertificate *scert,
    521                        CERTCertificate **rcerts,
    522                        CERTCertDBHandle *certdb,
    523                        SECKEYGetPasswordKey pwfn,
    524                        void *pwfn_arg)
    525 {
    526    SEC_PKCS7ContentInfo *cinfo;
    527    long cipher;
    528    SECOidTag encalg;
    529    int keysize;
    530    int mapi, rci;
    531 
    532    cipher = smime_choose_cipher(scert, rcerts);
    533    if (cipher < 0)
    534        return NULL;
    535 
    536    mapi = smime_mapi_by_cipher(cipher);
    537    if (mapi < 0)
    538        return NULL;
    539 
    540    /*
    541     * XXX This is stretching it -- CreateEnvelopedData should probably
    542     * take a cipher itself of some sort, because we cannot know what the
    543     * future will bring in terms of parameters for each type of algorithm.
    544     * For example, just an algorithm and keysize is *not* sufficient to
    545     * fully specify the usage of RC5 (which also needs to know rounds and
    546     * block size).  Work this out into a better API!
    547     */
    548    encalg = smime_cipher_maps[mapi].algtag;
    549    keysize = smime_keysize_by_cipher(cipher);
    550    if (keysize < 0)
    551        return NULL;
    552 
    553    cinfo = SEC_PKCS7CreateEnvelopedData(scert, certUsageEmailRecipient,
    554                                         certdb, encalg, keysize,
    555                                         pwfn, pwfn_arg);
    556    if (cinfo == NULL)
    557        return NULL;
    558 
    559    for (rci = 0; rcerts[rci] != NULL; rci++) {
    560        if (rcerts[rci] == scert)
    561            continue;
    562        if (SEC_PKCS7AddRecipient(cinfo, rcerts[rci], certUsageEmailRecipient,
    563                                  NULL) != SECSuccess) {
    564            SEC_PKCS7DestroyContentInfo(cinfo);
    565            return NULL;
    566        }
    567    }
    568 
    569    return cinfo;
    570 }
    571 
    572 static smime_capability **smime_capabilities;
    573 static SECItem *smime_encoded_caps;
    574 
    575 static SECStatus
    576 smime_init_caps(void)
    577 {
    578    smime_capability *cap;
    579    smime_cipher_map *map;
    580    SECOidData *oiddata;
    581    SECStatus rv;
    582    int i;
    583 
    584    if (smime_encoded_caps != NULL && (!smime_prefs_changed))
    585        return SECSuccess;
    586 
    587    if (smime_encoded_caps != NULL) {
    588        SECITEM_FreeItem(smime_encoded_caps, PR_TRUE);
    589        smime_encoded_caps = NULL;
    590    }
    591 
    592    if (smime_capabilities == NULL) {
    593        smime_capabilities = (smime_capability **)PORT_ZAlloc(
    594            (smime_symmetric_count + 1) * sizeof(smime_capability *));
    595        if (smime_capabilities == NULL)
    596            return SECFailure;
    597    }
    598 
    599    rv = SECFailure;
    600 
    601    /*
    602       The process of creating the encoded PKCS7 cipher capability list
    603       involves two basic steps:
    604 
    605       (a) Convert our internal representation of cipher preferences
    606           (smime_prefs) into an array containing cipher OIDs and
    607           parameter data (smime_capabilities). This step is
    608           performed here.
    609 
    610       (b) Encode, using ASN.1, the cipher information in
    611           smime_capabilities, leaving the encoded result in
    612           smime_encoded_caps.
    613 
    614       (In the process of performing (a), Lisa put in some optimizations
    615       which allow us to avoid needlessly re-populating elements in
    616       smime_capabilities as we walk through smime_prefs.)
    617    */
    618    for (i = 0; i < smime_current_pref_index; i++) {
    619        int mapi;
    620 
    621        /* Get the next cipher preference in smime_prefs. */
    622        mapi = smime_mapi_by_cipher(smime_prefs[i]);
    623        if (mapi < 0)
    624            break;
    625 
    626        /* Find the corresponding entry in the cipher map. */
    627        PORT_Assert(mapi < smime_symmetric_count);
    628        map = &(smime_cipher_maps[mapi]);
    629 
    630        /*
    631         * Convert the next preference found in smime_prefs into an
    632         * smime_capability.
    633         */
    634 
    635        cap = smime_capabilities[i];
    636        if (cap == NULL) {
    637            cap = (smime_capability *)PORT_ZAlloc(sizeof(smime_capability));
    638            if (cap == NULL)
    639                break;
    640            smime_capabilities[i] = cap;
    641        } else if (cap->cipher == smime_prefs[i]) {
    642            continue; /* no change to this one */
    643        }
    644 
    645        cap->capIDTag = map->algtag;
    646        oiddata = SECOID_FindOIDByTag(map->algtag);
    647        if (oiddata == NULL)
    648            break;
    649 
    650        if (cap->capabilityID.data != NULL) {
    651            SECITEM_FreeItem(&(cap->capabilityID), PR_FALSE);
    652            cap->capabilityID.data = NULL;
    653            cap->capabilityID.len = 0;
    654        }
    655 
    656        rv = SECITEM_CopyItem(NULL, &(cap->capabilityID), &(oiddata->oid));
    657        if (rv != SECSuccess)
    658            break;
    659 
    660        if (map->parms == NULL) {
    661            cap->parameters.data = NULL;
    662            cap->parameters.len = 0;
    663        } else {
    664            cap->parameters.data = map->parms->data;
    665            cap->parameters.len = map->parms->len;
    666        }
    667 
    668        cap->cipher = smime_prefs[i];
    669    }
    670 
    671    if (i != smime_current_pref_index)
    672        return rv;
    673 
    674    while (i < smime_symmetric_count) {
    675        cap = smime_capabilities[i];
    676        if (cap != NULL) {
    677            SECITEM_FreeItem(&(cap->capabilityID), PR_FALSE);
    678            PORT_Free(cap);
    679        }
    680        smime_capabilities[i] = NULL;
    681        i++;
    682    }
    683    smime_capabilities[i] = NULL;
    684 
    685    smime_encoded_caps = SEC_ASN1EncodeItem(NULL, NULL, &smime_capabilities,
    686                                            smime_capabilities_template);
    687    if (smime_encoded_caps == NULL)
    688        return SECFailure;
    689 
    690    return SECSuccess;
    691 }
    692 
    693 static SECStatus
    694 smime_add_profile(CERTCertificate *cert, SEC_PKCS7ContentInfo *cinfo)
    695 {
    696    PORT_Assert(smime_prefs_complete);
    697    if (!smime_prefs_complete)
    698        return SECFailure;
    699 
    700    /* For that matter, if capabilities haven't been initialized yet,
    701       do so now. */
    702    if (smime_encoded_caps == NULL || smime_prefs_changed) {
    703        SECStatus rv;
    704 
    705        rv = smime_init_caps();
    706        if (rv != SECSuccess)
    707            return rv;
    708 
    709        PORT_Assert(smime_encoded_caps != NULL);
    710    }
    711 
    712    return SEC_PKCS7AddSignedAttribute(cinfo, SEC_OID_PKCS9_SMIME_CAPABILITIES,
    713                                       smime_encoded_caps);
    714 }
    715 
    716 /*
    717 * Start an S/MIME signing context.
    718 *
    719 * "scert" is the cert that will be used to sign the data.  It will be
    720 * checked for validity.
    721 *
    722 * "ecert" is the signer's encryption cert.  If it is different from
    723 * scert, then it will be included in the signed message so that the
    724 * recipient can save it for future encryptions.
    725 *
    726 * "certdb" is the cert database to use for verifying the cert.
    727 * It can be NULL if a default database is available (like in the client).
    728 *
    729 * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
    730 * XXX There should be SECMIME functions for hashing, or the hashing should
    731 * be built into this interface, which we would like because we would
    732 * support more smartcards that way, and then this argument should go away.)
    733 *
    734 * "digest" is the actual digest of the data.  It must be provided in
    735 * the case of detached data or NULL if the content will be included.
    736 *
    737 * This function already does all of the stuff specific to S/MIME protocol
    738 * and local policy; the return value just needs to be passed to
    739 * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
    740 * and finally to SEC_PKCS7DestroyContentInfo().
    741 *
    742 * An error results in a return value of NULL and an error set.
    743 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
    744 */
    745 
    746 SEC_PKCS7ContentInfo *
    747 SECMIME_CreateSigned(CERTCertificate *scert,
    748                     CERTCertificate *ecert,
    749                     CERTCertDBHandle *certdb,
    750                     SECOidTag digestalg,
    751                     SECItem *digest,
    752                     SECKEYGetPasswordKey pwfn,
    753                     void *pwfn_arg)
    754 {
    755    SEC_PKCS7ContentInfo *cinfo;
    756    SECStatus rv;
    757 
    758    /* See note in header comment above about digestalg. */
    759    /* Doesn't explain this. PORT_Assert (digestalg == SEC_OID_SHA1); */
    760 
    761    cinfo = SEC_PKCS7CreateSignedData(scert, certUsageEmailSigner,
    762                                      certdb, digestalg, digest,
    763                                      pwfn, pwfn_arg);
    764    if (cinfo == NULL)
    765        return NULL;
    766 
    767    if (SEC_PKCS7IncludeCertChain(cinfo, NULL) != SECSuccess) {
    768        SEC_PKCS7DestroyContentInfo(cinfo);
    769        return NULL;
    770    }
    771 
    772    /* if the encryption cert and the signing cert differ, then include
    773     * the encryption cert too.
    774     */
    775    /* it is ok to compare the pointers since we ref count, and the same
    776     * cert will always have the same pointer
    777     */
    778    if ((ecert != NULL) && (ecert != scert)) {
    779        rv = SEC_PKCS7AddCertificate(cinfo, ecert);
    780        if (rv != SECSuccess) {
    781            SEC_PKCS7DestroyContentInfo(cinfo);
    782            return NULL;
    783        }
    784    }
    785    /*
    786     * Add the signing time.  But if it fails for some reason,
    787     * may as well not give up altogether -- just assert.
    788     */
    789    rv = SEC_PKCS7AddSigningTime(cinfo);
    790    PORT_Assert(rv == SECSuccess);
    791 
    792    /*
    793     * Add the email profile.  Again, if it fails for some reason,
    794     * may as well not give up altogether -- just assert.
    795     */
    796    rv = smime_add_profile(ecert, cinfo);
    797    PORT_Assert(rv == SECSuccess);
    798 
    799    return cinfo;
    800 }