Coverage Report

Created: 2026-10-13 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/smime/cmsudf.c
Line
Count
Source
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 User Define Types
7
 */
8
9
#include "cmslocal.h"
10
11
#include "prinit.h"
12
#include "pk11func.h"
13
#include "secitem.h"
14
#include "secoid.h"
15
#include "secerr.h"
16
#include "nss.h"
17
18
typedef struct nsscmstypeInfoStr nsscmstypeInfo;
19
struct nsscmstypeInfoStr {
20
    SECOidTag type;
21
    SEC_ASN1Template *template;
22
    size_t size;
23
    PRBool isData;
24
    NSSCMSGenericWrapperDataDestroy destroy;
25
    NSSCMSGenericWrapperDataCallback decode_before;
26
    NSSCMSGenericWrapperDataCallback decode_after;
27
    NSSCMSGenericWrapperDataCallback decode_end;
28
    NSSCMSGenericWrapperDataCallback encode_start;
29
    NSSCMSGenericWrapperDataCallback encode_before;
30
    NSSCMSGenericWrapperDataCallback encode_after;
31
};
32
33
/* make sure the global tables are only initialized once */
34
static PRCallOnceType nsscmstypeOnce;
35
static PRCallOnceType nsscmstypeClearOnce;
36
/* lock for adding a new entry */
37
static PRLock *nsscmstypeAddLock;
38
/* lock for the hash table */
39
static PRLock *nsscmstypeHashLock;
40
/* the hash table itself */
41
static PLHashTable *nsscmstypeHash;
42
/* arena to hold all the hash table data */
43
static PLArenaPool *nsscmstypeArena;
44
45
/*
46
 * clean up our global tables
47
 */
48
SECStatus
49
nss_cmstype_shutdown(void *appData, void *reserved)
50
0
{
51
0
    if (nsscmstypeHashLock) {
52
0
        PR_Lock(nsscmstypeHashLock);
53
0
    }
54
0
    if (nsscmstypeHash) {
55
0
        PL_HashTableDestroy(nsscmstypeHash);
56
0
        nsscmstypeHash = NULL;
57
0
    }
58
0
    if (nsscmstypeArena) {
59
0
        PORT_FreeArena(nsscmstypeArena, PR_FALSE);
60
0
        nsscmstypeArena = NULL;
61
0
    }
62
0
    if (nsscmstypeAddLock) {
63
0
        PR_DestroyLock(nsscmstypeAddLock);
64
0
    }
65
0
    if (nsscmstypeHashLock) {
66
0
        PRLock *oldLock = nsscmstypeHashLock;
67
0
        nsscmstypeHashLock = NULL;
68
0
        PR_Unlock(oldLock);
69
0
        PR_DestroyLock(oldLock);
70
0
    }
71
72
    /* don't clear out the PR_ONCE data if we failed our inital call */
73
0
    if (appData == NULL) {
74
0
        nsscmstypeOnce = nsscmstypeClearOnce;
75
0
    }
76
0
    return SECSuccess;
77
0
}
78
79
static PLHashNumber
80
nss_cmstype_hash_key(const void *key)
81
0
{
82
0
    return (PLHashNumber)((char *)key - (char *)NULL);
83
0
}
84
85
static PRIntn
86
nss_cmstype_compare_keys(const void *v1, const void *v2)
87
0
{
88
0
    PLHashNumber value1 = nss_cmstype_hash_key(v1);
89
0
    PLHashNumber value2 = nss_cmstype_hash_key(v2);
90
91
0
    return (value1 == value2);
92
0
}
93
94
/*
95
 * initialize our hash tables, called once on the first attemat to register
96
 * a new SMIME type.
97
 */
98
static PRStatus
99
nss_cmstype_init(void)
100
0
{
101
0
    SECStatus rv;
102
103
0
    nsscmstypeHashLock = PR_NewLock();
104
0
    if (nsscmstypeHashLock == NULL) {
105
0
        return PR_FAILURE;
106
0
    }
107
0
    nsscmstypeAddLock = PR_NewLock();
108
0
    if (nsscmstypeHashLock == NULL) {
109
0
        goto fail;
110
0
    }
111
0
    nsscmstypeHash = PL_NewHashTable(64, nss_cmstype_hash_key,
112
0
                                     nss_cmstype_compare_keys,
113
0
                                     PL_CompareValues, NULL, NULL);
114
0
    if (nsscmstypeHash == NULL) {
115
0
        goto fail;
116
0
    }
117
0
    nsscmstypeArena = PORT_NewArena(2048);
118
0
    if (nsscmstypeArena == NULL) {
119
0
        goto fail;
120
0
    }
121
0
    rv = NSS_RegisterShutdown(nss_cmstype_shutdown, NULL);
122
0
    if (rv != SECSuccess) {
123
0
        goto fail;
124
0
    }
125
0
    return PR_SUCCESS;
126
127
0
fail:
128
0
    nss_cmstype_shutdown(&nsscmstypeOnce, NULL);
129
0
    return PR_FAILURE;
130
0
}
131
132
/*
133
 * look up and registered SIME type
134
 */
135
static const nsscmstypeInfo *
136
nss_cmstype_lookup(SECOidTag type)
137
17.7M
{
138
17.7M
    nsscmstypeInfo *typeInfo = NULL;
139
17.7M
    ;
140
17.7M
    if (!nsscmstypeHash) {
141
17.7M
        return NULL;
142
17.7M
    }
143
0
    PR_Lock(nsscmstypeHashLock);
144
0
    if (nsscmstypeHash) {
145
0
        typeInfo = PL_HashTableLookupConst(nsscmstypeHash, (void *)type);
146
0
    }
147
0
    PR_Unlock(nsscmstypeHashLock);
148
0
    return typeInfo;
149
17.7M
}
150
151
/*
152
 * add a new type to the SMIME type table
153
 */
154
static SECStatus
155
nss_cmstype_add(SECOidTag type, nsscmstypeInfo *typeinfo)
156
0
{
157
0
    PLHashEntry *entry;
158
159
0
    if (!nsscmstypeHash) {
160
        /* assert? this shouldn't happen */
161
0
        return SECFailure;
162
0
    }
163
0
    PR_Lock(nsscmstypeHashLock);
164
    /* this is really paranoia. If we really are racing nsscmstypeHash, we'll
165
     * also be racing nsscmstypeHashLock... */
166
0
    if (!nsscmstypeHash) {
167
0
        PR_Unlock(nsscmstypeHashLock);
168
0
        return SECFailure;
169
0
    }
170
0
    entry = PL_HashTableAdd(nsscmstypeHash, (void *)type, typeinfo);
171
0
    PR_Unlock(nsscmstypeHashLock);
172
0
    return entry ? SECSuccess : SECFailure;
173
0
}
174
175
/* helper functions to manage new content types
176
 */
177
178
PRBool
179
NSS_CMSType_IsWrapper(SECOidTag type)
180
17.6M
{
181
17.6M
    const nsscmstypeInfo *typeInfo = NULL;
182
183
17.6M
    switch (type) {
184
16.9M
        case SEC_OID_PKCS7_SIGNED_DATA:
185
17.6M
        case SEC_OID_PKCS7_ENVELOPED_DATA:
186
17.6M
        case SEC_OID_CMS_AUTH_ENVELOPED_DATA:
187
17.6M
        case SEC_OID_PKCS7_DIGESTED_DATA:
188
17.6M
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
189
17.6M
            return PR_TRUE;
190
273
        default:
191
273
            typeInfo = nss_cmstype_lookup(type);
192
273
            if (typeInfo && !typeInfo->isData) {
193
0
                return PR_TRUE;
194
0
            }
195
17.6M
    }
196
273
    return PR_FALSE;
197
17.6M
}
198
199
PRBool
200
NSS_CMSType_IsData(SECOidTag type)
201
17.6M
{
202
17.6M
    const nsscmstypeInfo *typeInfo = NULL;
203
204
17.6M
    switch (type) {
205
854
        case SEC_OID_PKCS7_DATA:
206
854
            return PR_TRUE;
207
17.6M
        default:
208
17.6M
            typeInfo = nss_cmstype_lookup(type);
209
17.6M
            if (typeInfo && typeInfo->isData) {
210
0
                return PR_TRUE;
211
0
            }
212
17.6M
    }
213
17.6M
    return PR_FALSE;
214
17.6M
}
215
216
const SEC_ASN1Template *
217
NSS_CMSType_GetTemplate(SECOidTag type)
218
3.17k
{
219
3.17k
    const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type);
220
221
3.17k
    if (typeInfo && typeInfo->template) {
222
0
        return typeInfo->template;
223
0
    }
224
3.17k
    return SEC_ASN1_GET(SEC_PointerToOctetStringTemplate);
225
3.17k
}
226
227
size_t
228
NSS_CMSType_GetContentSize(SECOidTag type)
229
2.25k
{
230
2.25k
    const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type);
231
232
2.25k
    if (typeInfo) {
233
0
        return typeInfo->size;
234
0
    }
235
2.25k
    return sizeof(SECItem *);
236
2.25k
}
237
238
void
239
NSS_CMSGenericWrapperData_Destroy(SECOidTag type, NSSCMSGenericWrapperData *gd)
240
8.23k
{
241
8.23k
    const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type);
242
243
8.23k
    if (typeInfo && (typeInfo->destroy) && (gd != NULL)) {
244
0
        (*typeInfo->destroy)(gd);
245
0
    }
246
8.23k
}
247
248
SECStatus
249
NSS_CMSGenericWrapperData_Decode_BeforeData(SECOidTag type,
250
                                            NSSCMSGenericWrapperData *gd)
251
0
{
252
0
    const nsscmstypeInfo *typeInfo;
253
254
    /* short cut common case */
255
0
    if (type == SEC_OID_PKCS7_DATA) {
256
0
        return SECSuccess;
257
0
    }
258
259
0
    typeInfo = nss_cmstype_lookup(type);
260
0
    if (typeInfo) {
261
0
        if (typeInfo->decode_before) {
262
0
            return (*typeInfo->decode_before)(gd);
263
0
        }
264
        /* decoder ops optional for data tags */
265
0
        if (typeInfo->isData) {
266
0
            return SECSuccess;
267
0
        }
268
0
    }
269
    /* expected a function, but none existed */
270
0
    return SECFailure;
271
0
}
272
273
SECStatus
274
NSS_CMSGenericWrapperData_Decode_AfterData(SECOidTag type,
275
                                           NSSCMSGenericWrapperData *gd)
276
0
{
277
0
    const nsscmstypeInfo *typeInfo;
278
279
    /* short cut common case */
280
0
    if (type == SEC_OID_PKCS7_DATA) {
281
0
        return SECSuccess;
282
0
    }
283
284
0
    typeInfo = nss_cmstype_lookup(type);
285
0
    if (typeInfo) {
286
0
        if (typeInfo->decode_after) {
287
0
            return (*typeInfo->decode_after)(gd);
288
0
        }
289
        /* decoder ops optional for data tags */
290
0
        if (typeInfo->isData) {
291
0
            return SECSuccess;
292
0
        }
293
0
    }
294
    /* expected a function, but none existed */
295
0
    return SECFailure;
296
0
}
297
298
SECStatus
299
NSS_CMSGenericWrapperData_Decode_AfterEnd(SECOidTag type,
300
                                          NSSCMSGenericWrapperData *gd)
301
726
{
302
726
    const nsscmstypeInfo *typeInfo;
303
304
    /* short cut common case */
305
726
    if (type == SEC_OID_PKCS7_DATA) {
306
0
        return SECSuccess;
307
0
    }
308
309
726
    typeInfo = nss_cmstype_lookup(type);
310
726
    if (typeInfo) {
311
0
        if (typeInfo->decode_end) {
312
0
            return (*typeInfo->decode_end)(gd);
313
0
        }
314
        /* decoder ops optional for data tags */
315
0
        if (typeInfo->isData) {
316
0
            return SECSuccess;
317
0
        }
318
0
    }
319
    /* expected a function, but none existed */
320
726
    return SECFailure;
321
726
}
322
323
SECStatus
324
NSS_CMSGenericWrapperData_Encode_BeforeStart(SECOidTag type,
325
                                             NSSCMSGenericWrapperData *gd)
326
0
{
327
0
    const nsscmstypeInfo *typeInfo;
328
329
    /* short cut common case */
330
0
    if (type == SEC_OID_PKCS7_DATA) {
331
0
        return SECSuccess;
332
0
    }
333
334
0
    typeInfo = nss_cmstype_lookup(type);
335
0
    if (typeInfo) {
336
0
        if (typeInfo->encode_start) {
337
0
            return (*typeInfo->encode_start)(gd);
338
0
        }
339
        /* decoder ops optional for data tags */
340
0
        if (typeInfo->isData) {
341
0
            return SECSuccess;
342
0
        }
343
0
    }
344
    /* expected a function, but none existed */
345
0
    return SECFailure;
346
0
}
347
348
SECStatus
349
NSS_CMSGenericWrapperData_Encode_BeforeData(SECOidTag type,
350
                                            NSSCMSGenericWrapperData *gd)
351
0
{
352
0
    const nsscmstypeInfo *typeInfo;
353
354
    /* short cut common case */
355
0
    if (type == SEC_OID_PKCS7_DATA) {
356
0
        return SECSuccess;
357
0
    }
358
359
0
    typeInfo = nss_cmstype_lookup(type);
360
0
    if (typeInfo) {
361
0
        if (typeInfo->encode_before) {
362
0
            return (*typeInfo->encode_before)(gd);
363
0
        }
364
        /* decoder ops optional for data tags */
365
0
        if (typeInfo->isData) {
366
0
            return SECSuccess;
367
0
        }
368
0
    }
369
    /* expected a function, but none existed */
370
0
    return SECFailure;
371
0
}
372
373
SECStatus
374
NSS_CMSGenericWrapperData_Encode_AfterData(SECOidTag type,
375
                                           NSSCMSGenericWrapperData *gd)
376
0
{
377
0
    const nsscmstypeInfo *typeInfo;
378
379
    /* short cut common case */
380
0
    if (type == SEC_OID_PKCS7_DATA) {
381
0
        return SECSuccess;
382
0
    }
383
384
0
    typeInfo = nss_cmstype_lookup(type);
385
0
    if (typeInfo) {
386
0
        if (typeInfo->encode_after) {
387
0
            return (*typeInfo->encode_after)(gd);
388
0
        }
389
        /* decoder ops optional for data tags */
390
0
        if (typeInfo->isData) {
391
0
            return SECSuccess;
392
0
        }
393
0
    }
394
    /* expected a function, but none existed */
395
0
    return SECFailure;
396
0
}
397
398
SECStatus
399
NSS_CMSType_RegisterContentType(SECOidTag type,
400
                                SEC_ASN1Template *asn1Template, size_t size,
401
                                NSSCMSGenericWrapperDataDestroy destroy,
402
                                NSSCMSGenericWrapperDataCallback decode_before,
403
                                NSSCMSGenericWrapperDataCallback decode_after,
404
                                NSSCMSGenericWrapperDataCallback decode_end,
405
                                NSSCMSGenericWrapperDataCallback encode_start,
406
                                NSSCMSGenericWrapperDataCallback encode_before,
407
                                NSSCMSGenericWrapperDataCallback encode_after,
408
                                PRBool isData)
409
0
{
410
0
    PRStatus rc;
411
0
    SECStatus rv;
412
0
    nsscmstypeInfo *typeInfo;
413
0
    const nsscmstypeInfo *exists;
414
415
0
    rc = PR_CallOnce(&nsscmstypeOnce, nss_cmstype_init);
416
0
    if (rc == PR_FAILURE) {
417
0
        return SECFailure;
418
0
    }
419
0
    PR_Lock(nsscmstypeAddLock);
420
0
    exists = nss_cmstype_lookup(type);
421
0
    if (exists) {
422
0
        PR_Unlock(nsscmstypeAddLock);
423
        /* already added */
424
0
        return SECSuccess;
425
0
    }
426
0
    typeInfo = PORT_ArenaNew(nsscmstypeArena, nsscmstypeInfo);
427
0
    typeInfo->type = type;
428
0
    typeInfo->size = size;
429
0
    typeInfo->isData = isData;
430
0
    typeInfo->template = asn1Template;
431
0
    typeInfo->destroy = destroy;
432
0
    typeInfo->decode_before = decode_before;
433
0
    typeInfo->decode_after = decode_after;
434
0
    typeInfo->decode_end = decode_end;
435
0
    typeInfo->encode_start = encode_start;
436
0
    typeInfo->encode_before = encode_before;
437
0
    typeInfo->encode_after = encode_after;
438
0
    rv = nss_cmstype_add(type, typeInfo);
439
0
    PR_Unlock(nsscmstypeAddLock);
440
0
    return rv;
441
0
}