Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/nss/lib/pkcs7/secmime.c
Line
Count
Source (jump to first uncovered line)
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
0
{
75
0
    int i;
76
0
77
0
    for (i = 0; i < smime_symmetric_count; i++) {
78
0
        if (smime_cipher_maps[i].cipher == cipher)
79
0
            break;
80
0
    }
81
0
82
0
    if (i == smime_symmetric_count)
83
0
        return -1;
84
0
85
0
    return i;
86
0
}
87
88
/*
89
 * this function locally records the user's preference
90
 */
91
SECStatus
92
SECMIME_EnableCipher(long which, int on)
93
0
{
94
0
    unsigned long mask;
95
0
96
0
    if (smime_newprefs == NULL || smime_prefs_complete) {
97
0
        /*
98
0
         * This is either the very first time, or we are starting over.
99
0
         */
100
0
        smime_newprefs = (unsigned long *)PORT_ZAlloc(smime_symmetric_count * sizeof(*smime_newprefs));
101
0
        if (smime_newprefs == NULL)
102
0
            return SECFailure;
103
0
        smime_current_pref_index = 0;
104
0
        smime_prefs_complete = PR_FALSE;
105
0
    }
106
0
107
0
    mask = which & CIPHER_FAMILYID_MASK;
108
0
    if (mask == CIPHER_FAMILYID_MASK) {
109
0
        /*
110
0
         * This call signifies that all preferences have been set.
111
0
         * Move "newprefs" over, after checking first whether or
112
0
         * not the new ones are different from the old ones.
113
0
         */
114
0
        if (smime_prefs != NULL) {
115
0
            if (PORT_Memcmp(smime_prefs, smime_newprefs,
116
0
                            smime_symmetric_count * sizeof(*smime_prefs)) == 0)
117
0
                smime_prefs_changed = PR_FALSE;
118
0
            else
119
0
                smime_prefs_changed = PR_TRUE;
120
0
            PORT_Free(smime_prefs);
121
0
        }
122
0
123
0
        smime_prefs = smime_newprefs;
124
0
        smime_prefs_complete = PR_TRUE;
125
0
        return SECSuccess;
126
0
    }
127
0
128
0
    PORT_Assert(mask == CIPHER_FAMILYID_SMIME);
129
0
    if (mask != CIPHER_FAMILYID_SMIME) {
130
0
        /* XXX set an error! */
131
0
        return SECFailure;
132
0
    }
133
0
134
0
    if (on) {
135
0
        PORT_Assert(smime_current_pref_index < smime_symmetric_count);
136
0
        if (smime_current_pref_index >= smime_symmetric_count) {
137
0
            /* XXX set an error! */
138
0
            return SECFailure;
139
0
        }
140
0
141
0
        smime_newprefs[smime_current_pref_index++] = which;
142
0
    }
143
0
144
0
    return SECSuccess;
145
0
}
146
147
/*
148
 * this function locally records the export policy
149
 */
150
SECStatus
151
SECMIME_SetPolicy(long which, int on)
152
0
{
153
0
    unsigned long mask;
154
0
155
0
    PORT_Assert((which & CIPHER_FAMILYID_MASK) == CIPHER_FAMILYID_SMIME);
156
0
    if ((which & CIPHER_FAMILYID_MASK) != CIPHER_FAMILYID_SMIME) {
157
0
        /* XXX set an error! */
158
0
        return SECFailure;
159
0
    }
160
0
161
0
    which &= ~CIPHER_FAMILYID_MASK;
162
0
163
0
    PORT_Assert(which < 32); /* bits in the long */
164
0
    if (which >= 32) {
165
0
        /* XXX set an error! */
166
0
        return SECFailure;
167
0
    }
168
0
169
0
    mask = 1UL << which;
170
0
171
0
    if (on) {
172
0
        smime_policy_bits |= mask;
173
0
    } else {
174
0
        smime_policy_bits &= ~mask;
175
0
    }
176
0
177
0
    return SECSuccess;
178
0
}
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
0
{
189
0
    SECOidTag algtag;
190
0
191
0
    algtag = SECOID_GetAlgorithmTag(algid);
192
0
    switch (algtag) {
193
0
        case SEC_OID_RC2_CBC: {
194
0
            unsigned int keylen_bits;
195
0
196
0
            keylen_bits = PK11_GetKeyStrength(key, algid);
197
0
            switch (keylen_bits) {
198
0
                case 40:
199
0
                    return SMIME_RC2_CBC_40;
200
0
                case 64:
201
0
                    return SMIME_RC2_CBC_64;
202
0
                case 128:
203
0
                    return SMIME_RC2_CBC_128;
204
0
                default:
205
0
                    break;
206
0
            }
207
0
        } break;
208
0
        case SEC_OID_DES_CBC:
209
0
            return SMIME_DES_CBC_56;
210
0
        case SEC_OID_DES_EDE3_CBC:
211
0
            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
0
        default:
218
0
            break;
219
0
    }
220
0
221
0
    return -1;
222
0
}
223
224
static PRBool
225
smime_cipher_allowed(unsigned long which)
226
0
{
227
0
    unsigned long mask;
228
0
229
0
    which &= ~CIPHER_FAMILYID_MASK;
230
0
    PORT_Assert(which < 32); /* bits per long (min) */
231
0
    if (which >= 32)
232
0
        return PR_FALSE;
233
0
234
0
    mask = 1UL << which;
235
0
    if ((mask & smime_policy_bits) == 0)
236
0
        return PR_FALSE;
237
0
238
0
    return PR_TRUE;
239
0
}
240
241
PRBool
242
SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
243
0
{
244
0
    long which;
245
0
246
0
    which = smime_policy_algorithm(algid, key);
247
0
    if (which < 0)
248
0
        return PR_FALSE;
249
0
250
0
    return smime_cipher_allowed((unsigned long)which);
251
0
}
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
0
{
274
0
    if (smime_policy_bits != 0)
275
0
        return PR_TRUE;
276
0
277
0
    return PR_FALSE;
278
0
}
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
0
{
309
0
    unsigned long cipher;
310
0
    SECOidTag algtag;
311
0
    int i;
312
0
313
0
    algtag = SECOID_FindOIDTag(&(cap->capabilityID));
314
0
315
0
    for (i = 0; i < smime_symmetric_count; i++) {
316
0
        if (smime_cipher_maps[i].algtag != algtag)
317
0
            continue;
318
0
        /*
319
0
         * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
320
0
         * 2 NULLs as equal and NULL and non-NULL as not equal), we could
321
0
         * use that here instead of all of the following comparison code.
322
0
         */
323
0
        if (cap->parameters.data != NULL) {
324
0
            if (smime_cipher_maps[i].parms == NULL)
325
0
                continue;
326
0
            if (cap->parameters.len != smime_cipher_maps[i].parms->len)
327
0
                continue;
328
0
            if (PORT_Memcmp(cap->parameters.data,
329
0
                            smime_cipher_maps[i].parms->data,
330
0
                            cap->parameters.len) == 0)
331
0
                break;
332
0
        } else if (smime_cipher_maps[i].parms == NULL) {
333
0
            break;
334
0
        }
335
0
    }
336
0
337
0
    if (i == smime_symmetric_count)
338
0
        cipher = 0;
339
0
    else
340
0
        cipher = smime_cipher_maps[i].cipher;
341
0
342
0
    cap->cipher = cipher;
343
0
    cap->capIDTag = algtag;
344
0
}
345
346
static long
347
smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
348
0
{
349
0
    PLArenaPool *poolp;
350
0
    long chosen_cipher;
351
0
    int *cipher_abilities;
352
0
    int *cipher_votes;
353
0
    int strong_mapi;
354
0
    int rcount, mapi, max;
355
0
356
0
    if (smime_policy_bits == 0) {
357
0
        PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
358
0
        return -1;
359
0
    }
360
0
361
0
    chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */
362
0
363
0
    poolp = PORT_NewArena(1024); /* XXX what is right value? */
364
0
    if (poolp == NULL)
365
0
        goto done;
366
0
367
0
    cipher_abilities = (int *)PORT_ArenaZAlloc(poolp,
368
0
                                               smime_symmetric_count * sizeof(int));
369
0
    if (cipher_abilities == NULL)
370
0
        goto done;
371
0
372
0
    cipher_votes = (int *)PORT_ArenaZAlloc(poolp,
373
0
                                           smime_symmetric_count * sizeof(int));
374
0
    if (cipher_votes == NULL)
375
0
        goto done;
376
0
377
0
    /*
378
0
     * XXX Should have a #define somewhere which specifies default
379
0
     * strong cipher.  (Or better, a way to configure.)
380
0
     */
381
0
382
0
    /* Make triple-DES the strong cipher. */
383
0
    strong_mapi = smime_mapi_by_cipher(SMIME_DES_EDE3_168);
384
0
385
0
    PORT_Assert(strong_mapi >= 0);
386
0
387
0
    for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
388
0
        SECItem *profile;
389
0
        smime_capability **caps;
390
0
        int capi, pref;
391
0
        SECStatus dstat;
392
0
393
0
        pref = smime_symmetric_count;
394
0
        profile = CERT_FindSMimeProfile(rcerts[rcount]);
395
0
        if (profile != NULL && profile->data != NULL && profile->len > 0) {
396
0
            caps = NULL;
397
0
            dstat = SEC_QuickDERDecodeItem(poolp, &caps,
398
0
                                           smime_capabilities_template,
399
0
                                           profile);
400
0
            if (dstat == SECSuccess && caps != NULL) {
401
0
                for (capi = 0; caps[capi] != NULL; capi++) {
402
0
                    smime_fill_capability(caps[capi]);
403
0
                    mapi = smime_mapi_by_cipher(caps[capi]->cipher);
404
0
                    if (mapi >= 0) {
405
0
                        cipher_abilities[mapi]++;
406
0
                        cipher_votes[mapi] += pref;
407
0
                        --pref;
408
0
                    }
409
0
                }
410
0
            }
411
0
        } else {
412
0
            SECKEYPublicKey *key;
413
0
            unsigned int pklen_bits;
414
0
415
0
            /*
416
0
             * XXX This is probably only good for RSA keys.  What I would
417
0
             * really like is a function to just say;  Is the public key in
418
0
             * this cert an export-length key?  Then I would not have to
419
0
             * know things like the value 512, or the kind of key, or what
420
0
             * a subjectPublicKeyInfo is, etc.
421
0
             */
422
0
            key = CERT_ExtractPublicKey(rcerts[rcount]);
423
0
            if (key != NULL) {
424
0
                pklen_bits = SECKEY_PublicKeyStrength(key) * 8;
425
0
                SECKEY_DestroyPublicKey(key);
426
0
427
0
                if (pklen_bits > 512) {
428
0
                    cipher_abilities[strong_mapi]++;
429
0
                    cipher_votes[strong_mapi] += pref;
430
0
                }
431
0
            }
432
0
        }
433
0
        if (profile != NULL)
434
0
            SECITEM_FreeItem(profile, PR_TRUE);
435
0
    }
436
0
437
0
    max = 0;
438
0
    for (mapi = 0; mapi < smime_symmetric_count; mapi++) {
439
0
        if (cipher_abilities[mapi] != rcount)
440
0
            continue;
441
0
        if (!smime_cipher_allowed(smime_cipher_maps[mapi].cipher))
442
0
            continue;
443
0
        if (cipher_votes[mapi] > max) {
444
0
            chosen_cipher = smime_cipher_maps[mapi].cipher;
445
0
            max = cipher_votes[mapi];
446
0
        } /* XXX else if a tie, let scert break it? */
447
0
    }
448
0
449
0
done:
450
0
    if (poolp != NULL)
451
0
        PORT_FreeArena(poolp, PR_FALSE);
452
0
453
0
    return chosen_cipher;
454
0
}
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
0
{
464
0
    int keysize;
465
0
466
0
    switch (which) {
467
0
        case SMIME_RC2_CBC_40:
468
0
            keysize = 40;
469
0
            break;
470
0
        case SMIME_RC2_CBC_64:
471
0
            keysize = 64;
472
0
            break;
473
0
        case SMIME_RC2_CBC_128:
474
0
            keysize = 128;
475
0
            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
0
        case SMIME_DES_CBC_56:
487
0
        case SMIME_DES_EDE3_168:
488
0
            /*
489
0
             * These are special; since the key size is fixed, we actually
490
0
             * want to *avoid* specifying a key size.
491
0
             */
492
0
            keysize = 0;
493
0
            break;
494
0
        default:
495
0
            keysize = -1;
496
0
            break;
497
0
    }
498
0
499
0
    return keysize;
500
0
}
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
0
{
526
0
    SEC_PKCS7ContentInfo *cinfo;
527
0
    long cipher;
528
0
    SECOidTag encalg;
529
0
    int keysize;
530
0
    int mapi, rci;
531
0
532
0
    cipher = smime_choose_cipher(scert, rcerts);
533
0
    if (cipher < 0)
534
0
        return NULL;
535
0
536
0
    mapi = smime_mapi_by_cipher(cipher);
537
0
    if (mapi < 0)
538
0
        return NULL;
539
0
540
0
    /*
541
0
     * XXX This is stretching it -- CreateEnvelopedData should probably
542
0
     * take a cipher itself of some sort, because we cannot know what the
543
0
     * future will bring in terms of parameters for each type of algorithm.
544
0
     * For example, just an algorithm and keysize is *not* sufficient to
545
0
     * fully specify the usage of RC5 (which also needs to know rounds and
546
0
     * block size).  Work this out into a better API!
547
0
     */
548
0
    encalg = smime_cipher_maps[mapi].algtag;
549
0
    keysize = smime_keysize_by_cipher(cipher);
550
0
    if (keysize < 0)
551
0
        return NULL;
552
0
553
0
    cinfo = SEC_PKCS7CreateEnvelopedData(scert, certUsageEmailRecipient,
554
0
                                         certdb, encalg, keysize,
555
0
                                         pwfn, pwfn_arg);
556
0
    if (cinfo == NULL)
557
0
        return NULL;
558
0
559
0
    for (rci = 0; rcerts[rci] != NULL; rci++) {
560
0
        if (rcerts[rci] == scert)
561
0
            continue;
562
0
        if (SEC_PKCS7AddRecipient(cinfo, rcerts[rci], certUsageEmailRecipient,
563
0
                                  NULL) != SECSuccess) {
564
0
            SEC_PKCS7DestroyContentInfo(cinfo);
565
0
            return NULL;
566
0
        }
567
0
    }
568
0
569
0
    return cinfo;
570
0
}
571
572
static smime_capability **smime_capabilities;
573
static SECItem *smime_encoded_caps;
574
575
static SECStatus
576
smime_init_caps(void)
577
0
{
578
0
    smime_capability *cap;
579
0
    smime_cipher_map *map;
580
0
    SECOidData *oiddata;
581
0
    SECStatus rv;
582
0
    int i;
583
0
584
0
    if (smime_encoded_caps != NULL && (!smime_prefs_changed))
585
0
        return SECSuccess;
586
0
587
0
    if (smime_encoded_caps != NULL) {
588
0
        SECITEM_FreeItem(smime_encoded_caps, PR_TRUE);
589
0
        smime_encoded_caps = NULL;
590
0
    }
591
0
592
0
    if (smime_capabilities == NULL) {
593
0
        smime_capabilities = (smime_capability **)PORT_ZAlloc(
594
0
            (smime_symmetric_count + 1) * sizeof(smime_capability *));
595
0
        if (smime_capabilities == NULL)
596
0
            return SECFailure;
597
0
    }
598
0
599
0
    rv = SECFailure;
600
0
601
0
    /*
602
0
       The process of creating the encoded PKCS7 cipher capability list
603
0
       involves two basic steps:
604
0
605
0
       (a) Convert our internal representation of cipher preferences
606
0
           (smime_prefs) into an array containing cipher OIDs and
607
0
           parameter data (smime_capabilities). This step is
608
0
           performed here.
609
0
610
0
       (b) Encode, using ASN.1, the cipher information in
611
0
           smime_capabilities, leaving the encoded result in
612
0
           smime_encoded_caps.
613
0
614
0
       (In the process of performing (a), Lisa put in some optimizations
615
0
       which allow us to avoid needlessly re-populating elements in
616
0
       smime_capabilities as we walk through smime_prefs.)
617
0
    */
618
0
    for (i = 0; i < smime_current_pref_index; i++) {
619
0
        int mapi;
620
0
621
0
        /* Get the next cipher preference in smime_prefs. */
622
0
        mapi = smime_mapi_by_cipher(smime_prefs[i]);
623
0
        if (mapi < 0)
624
0
            break;
625
0
626
0
        /* Find the corresponding entry in the cipher map. */
627
0
        PORT_Assert(mapi < smime_symmetric_count);
628
0
        map = &(smime_cipher_maps[mapi]);
629
0
630
0
        /*
631
0
         * Convert the next preference found in smime_prefs into an
632
0
         * smime_capability.
633
0
         */
634
0
635
0
        cap = smime_capabilities[i];
636
0
        if (cap == NULL) {
637
0
            cap = (smime_capability *)PORT_ZAlloc(sizeof(smime_capability));
638
0
            if (cap == NULL)
639
0
                break;
640
0
            smime_capabilities[i] = cap;
641
0
        } else if (cap->cipher == smime_prefs[i]) {
642
0
            continue; /* no change to this one */
643
0
        }
644
0
645
0
        cap->capIDTag = map->algtag;
646
0
        oiddata = SECOID_FindOIDByTag(map->algtag);
647
0
        if (oiddata == NULL)
648
0
            break;
649
0
650
0
        if (cap->capabilityID.data != NULL) {
651
0
            SECITEM_FreeItem(&(cap->capabilityID), PR_FALSE);
652
0
            cap->capabilityID.data = NULL;
653
0
            cap->capabilityID.len = 0;
654
0
        }
655
0
656
0
        rv = SECITEM_CopyItem(NULL, &(cap->capabilityID), &(oiddata->oid));
657
0
        if (rv != SECSuccess)
658
0
            break;
659
0
660
0
        if (map->parms == NULL) {
661
0
            cap->parameters.data = NULL;
662
0
            cap->parameters.len = 0;
663
0
        } else {
664
0
            cap->parameters.data = map->parms->data;
665
0
            cap->parameters.len = map->parms->len;
666
0
        }
667
0
668
0
        cap->cipher = smime_prefs[i];
669
0
    }
670
0
671
0
    if (i != smime_current_pref_index)
672
0
        return rv;
673
0
674
0
    while (i < smime_symmetric_count) {
675
0
        cap = smime_capabilities[i];
676
0
        if (cap != NULL) {
677
0
            SECITEM_FreeItem(&(cap->capabilityID), PR_FALSE);
678
0
            PORT_Free(cap);
679
0
        }
680
0
        smime_capabilities[i] = NULL;
681
0
        i++;
682
0
    }
683
0
    smime_capabilities[i] = NULL;
684
0
685
0
    smime_encoded_caps = SEC_ASN1EncodeItem(NULL, NULL, &smime_capabilities,
686
0
                                            smime_capabilities_template);
687
0
    if (smime_encoded_caps == NULL)
688
0
        return SECFailure;
689
0
690
0
    return SECSuccess;
691
0
}
692
693
static SECStatus
694
smime_add_profile(CERTCertificate *cert, SEC_PKCS7ContentInfo *cinfo)
695
0
{
696
0
    PORT_Assert(smime_prefs_complete);
697
0
    if (!smime_prefs_complete)
698
0
        return SECFailure;
699
0
700
0
    /* For that matter, if capabilities haven't been initialized yet,
701
0
       do so now. */
702
0
    if (smime_encoded_caps == NULL || smime_prefs_changed) {
703
0
        SECStatus rv;
704
0
705
0
        rv = smime_init_caps();
706
0
        if (rv != SECSuccess)
707
0
            return rv;
708
0
709
0
        PORT_Assert(smime_encoded_caps != NULL);
710
0
    }
711
0
712
0
    return SEC_PKCS7AddSignedAttribute(cinfo, SEC_OID_PKCS9_SMIME_CAPABILITIES,
713
0
                                       smime_encoded_caps);
714
0
}
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
0
{
755
0
    SEC_PKCS7ContentInfo *cinfo;
756
0
    SECStatus rv;
757
0
758
0
    /* See note in header comment above about digestalg. */
759
0
    /* Doesn't explain this. PORT_Assert (digestalg == SEC_OID_SHA1); */
760
0
761
0
    cinfo = SEC_PKCS7CreateSignedData(scert, certUsageEmailSigner,
762
0
                                      certdb, digestalg, digest,
763
0
                                      pwfn, pwfn_arg);
764
0
    if (cinfo == NULL)
765
0
        return NULL;
766
0
767
0
    if (SEC_PKCS7IncludeCertChain(cinfo, NULL) != SECSuccess) {
768
0
        SEC_PKCS7DestroyContentInfo(cinfo);
769
0
        return NULL;
770
0
    }
771
0
772
0
    /* if the encryption cert and the signing cert differ, then include
773
0
     * the encryption cert too.
774
0
     */
775
0
    /* it is ok to compare the pointers since we ref count, and the same
776
0
     * cert will always have the same pointer
777
0
     */
778
0
    if ((ecert != NULL) && (ecert != scert)) {
779
0
        rv = SEC_PKCS7AddCertificate(cinfo, ecert);
780
0
        if (rv != SECSuccess) {
781
0
            SEC_PKCS7DestroyContentInfo(cinfo);
782
0
            return NULL;
783
0
        }
784
0
    }
785
0
    /*
786
0
     * Add the signing time.  But if it fails for some reason,
787
0
     * may as well not give up altogether -- just assert.
788
0
     */
789
0
    rv = SEC_PKCS7AddSigningTime(cinfo);
790
0
    PORT_Assert(rv == SECSuccess);
791
0
792
0
    /*
793
0
     * Add the email profile.  Again, if it fails for some reason,
794
0
     * may as well not give up altogether -- just assert.
795
0
     */
796
0
    rv = smime_add_profile(ecert, cinfo);
797
0
    PORT_Assert(rv == SECSuccess);
798
0
799
0
    return cinfo;
800
0
}