Coverage Report

Created: 2025-07-01 06:25

/src/nss/lib/certdb/certxutl.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
 * Certificate Extensions handling code
7
 *
8
 */
9
10
#include "cert.h"
11
#include "secitem.h"
12
#include "secoid.h"
13
#include "secder.h"
14
#include "secasn1.h"
15
#include "certxutl.h"
16
#include "secerr.h"
17
18
#ifdef OLD
19
#include "ocspti.h" /* XXX a better extensions interface would not
20
       * require knowledge of data structures of callers */
21
#endif
22
23
static CERTCertExtension *
24
GetExtension(CERTCertExtension **extensions, SECItem *oid)
25
0
{
26
0
    CERTCertExtension **exts;
27
0
    CERTCertExtension *ext = NULL;
28
0
    SECComparison comp;
29
30
0
    exts = extensions;
31
32
0
    if (exts) {
33
0
        while (*exts) {
34
0
            ext = *exts;
35
0
            comp = SECITEM_CompareItem(oid, &ext->id);
36
0
            if (comp == SECEqual)
37
0
                break;
38
39
0
            exts++;
40
0
        }
41
0
        return (*exts ? ext : NULL);
42
0
    }
43
0
    return (NULL);
44
0
}
45
46
SECStatus
47
cert_FindExtensionByOID(CERTCertExtension **extensions, SECItem *oid,
48
                        SECItem *value)
49
0
{
50
0
    CERTCertExtension *ext;
51
0
    SECStatus rv = SECSuccess;
52
53
0
    ext = GetExtension(extensions, oid);
54
0
    if (ext == NULL) {
55
0
        PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
56
0
        return (SECFailure);
57
0
    }
58
0
    if (value)
59
0
        rv = SECITEM_CopyItem(NULL, value, &ext->value);
60
0
    return (rv);
61
0
}
62
63
SECStatus
64
CERT_GetExtenCriticality(CERTCertExtension **extensions, int tag,
65
                         PRBool *isCritical)
66
0
{
67
0
    CERTCertExtension *ext;
68
0
    SECOidData *oid;
69
70
0
    if (!isCritical)
71
0
        return (SECSuccess);
72
73
    /* find the extension in the extensions list */
74
0
    oid = SECOID_FindOIDByTag((SECOidTag)tag);
75
0
    if (!oid) {
76
0
        return (SECFailure);
77
0
    }
78
0
    ext = GetExtension(extensions, &oid->oid);
79
0
    if (ext == NULL) {
80
0
        PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
81
0
        return (SECFailure);
82
0
    }
83
84
    /* If the criticality is omitted, then it is false by default.
85
       ex->critical.data is NULL */
86
0
    if (ext->critical.data == NULL)
87
0
        *isCritical = PR_FALSE;
88
0
    else
89
0
        *isCritical = (ext->critical.data[0] == 0xff) ? PR_TRUE : PR_FALSE;
90
0
    return (SECSuccess);
91
0
}
92
93
SECStatus
94
cert_FindExtension(CERTCertExtension **extensions, int tag, SECItem *value)
95
0
{
96
0
    SECOidData *oid;
97
98
0
    oid = SECOID_FindOIDByTag((SECOidTag)tag);
99
0
    if (!oid) {
100
0
        return (SECFailure);
101
0
    }
102
103
0
    return (cert_FindExtensionByOID(extensions, &oid->oid, value));
104
0
}
105
106
typedef struct _extNode {
107
    struct _extNode *next;
108
    CERTCertExtension *ext;
109
} extNode;
110
111
typedef struct {
112
    void (*setExts)(void *object, CERTCertExtension **exts);
113
    void *object;
114
    PLArenaPool *ownerArena;
115
    PLArenaPool *arena;
116
    extNode *head;
117
    int count;
118
} extRec;
119
120
/*
121
 * cert_StartExtensions
122
 *
123
 * NOTE: This interface changed significantly to remove knowledge
124
 *   about callers data structures (owner objects)
125
 */
126
void *
127
cert_StartExtensions(void *owner, PLArenaPool *ownerArena,
128
                     void (*setExts)(void *object, CERTCertExtension **exts))
129
0
{
130
0
    PLArenaPool *arena;
131
0
    extRec *handle;
132
133
0
    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
134
0
    if (!arena) {
135
0
        return (0);
136
0
    }
137
138
0
    handle = (extRec *)PORT_ArenaAlloc(arena, sizeof(extRec));
139
0
    if (!handle) {
140
0
        PORT_FreeArena(arena, PR_FALSE);
141
0
        return (0);
142
0
    }
143
144
0
    handle->object = owner;
145
0
    handle->ownerArena = ownerArena;
146
0
    handle->setExts = setExts;
147
148
0
    handle->arena = arena;
149
0
    handle->head = 0;
150
0
    handle->count = 0;
151
152
0
    return (handle);
153
0
}
154
155
static unsigned char hextrue = 0xff;
156
157
/*
158
 * Note - assumes that data pointed to by oid->data will not move
159
 */
160
SECStatus
161
CERT_AddExtensionByOID(void *exthandle, SECItem *oid, SECItem *value,
162
                       PRBool critical, PRBool copyData)
163
0
{
164
0
    CERTCertExtension *ext;
165
0
    SECStatus rv;
166
0
    extNode *node;
167
0
    extRec *handle;
168
169
0
    handle = (extRec *)exthandle;
170
171
    /* allocate space for extension and list node */
172
0
    ext = (CERTCertExtension *)PORT_ArenaZAlloc(handle->ownerArena,
173
0
                                                sizeof(CERTCertExtension));
174
0
    if (!ext) {
175
0
        return (SECFailure);
176
0
    }
177
178
0
    node = (extNode *)PORT_ArenaAlloc(handle->arena, sizeof(extNode));
179
0
    if (!node) {
180
0
        return (SECFailure);
181
0
    }
182
183
    /* add to list */
184
0
    node->next = handle->head;
185
0
    handle->head = node;
186
187
    /* point to ext struct */
188
0
    node->ext = ext;
189
190
    /* set critical field */
191
0
    if (critical) {
192
0
        ext->critical.data = (unsigned char *)&hextrue;
193
0
        ext->critical.len = 1;
194
0
    }
195
196
    /* set object ID of the extension and its value */
197
0
    if (copyData) {
198
0
        rv = SECITEM_CopyItem(handle->ownerArena, &ext->id, oid);
199
0
        if (rv) {
200
0
            return (SECFailure);
201
0
        }
202
203
0
        rv = SECITEM_CopyItem(handle->ownerArena, &ext->value, value);
204
0
        if (rv) {
205
0
            return (SECFailure);
206
0
        }
207
0
    } else {
208
0
        ext->id = *oid;
209
0
        ext->value = *value;
210
0
    }
211
212
0
    handle->count++;
213
214
0
    return (SECSuccess);
215
0
}
216
217
SECStatus
218
CERT_AddExtension(void *exthandle, int idtag, SECItem *value, PRBool critical,
219
                  PRBool copyData)
220
0
{
221
0
    SECOidData *oid;
222
223
0
    oid = SECOID_FindOIDByTag((SECOidTag)idtag);
224
0
    if (!oid) {
225
0
        return (SECFailure);
226
0
    }
227
228
0
    return (CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical,
229
0
                                   copyData));
230
0
}
231
232
SECStatus
233
CERT_EncodeAndAddExtension(void *exthandle, int idtag, void *value,
234
                           PRBool critical, const SEC_ASN1Template *atemplate)
235
0
{
236
0
    extRec *handle;
237
0
    SECItem *encitem;
238
239
0
    handle = (extRec *)exthandle;
240
241
0
    encitem = SEC_ASN1EncodeItem(handle->ownerArena, NULL, value, atemplate);
242
0
    if (encitem == NULL) {
243
0
        return (SECFailure);
244
0
    }
245
246
0
    return CERT_AddExtension(exthandle, idtag, encitem, critical, PR_FALSE);
247
0
}
248
249
void
250
PrepareBitStringForEncoding(SECItem *bitsmap, SECItem *value)
251
0
{
252
0
    unsigned char onebyte;
253
0
    unsigned int i, len = 0;
254
255
    /* to prevent warning on some platform at compile time */
256
0
    onebyte = '\0';
257
    /* Get the position of the right-most turn-on bit */
258
0
    for (i = 0; i < (value->len) * 8; ++i) {
259
0
        if (i % 8 == 0)
260
0
            onebyte = value->data[i / 8];
261
0
        if (onebyte & 0x80)
262
0
            len = i;
263
0
        onebyte <<= 1;
264
0
    }
265
0
    bitsmap->data = value->data;
266
    /* Add one here since we work with base 1 */
267
0
    bitsmap->len = len + 1;
268
0
}
269
270
SECStatus
271
CERT_EncodeAndAddBitStrExtension(void *exthandle, int idtag, SECItem *value,
272
                                 PRBool critical)
273
0
{
274
0
    SECItem bitsmap;
275
276
0
    PrepareBitStringForEncoding(&bitsmap, value);
277
0
    return (CERT_EncodeAndAddExtension(exthandle, idtag, &bitsmap, critical,
278
0
                                       SEC_ASN1_GET(SEC_BitStringTemplate)));
279
0
}
280
281
SECStatus
282
CERT_FinishExtensions(void *exthandle)
283
0
{
284
0
    extRec *handle;
285
0
    extNode *node;
286
0
    CERTCertExtension **exts;
287
0
    SECStatus rv = SECFailure;
288
289
0
    handle = (extRec *)exthandle;
290
291
    /* allocate space for extensions array */
292
0
    exts = PORT_ArenaNewArray(handle->ownerArena, CERTCertExtension *,
293
0
                              handle->count + 1);
294
0
    if (exts == NULL) {
295
0
        goto loser;
296
0
    }
297
298
    /* put extensions in owner object and update its version number */
299
300
#ifdef OLD
301
    switch (handle->type) {
302
        case CertificateExtensions:
303
            handle->owner.cert->extensions = exts;
304
            DER_SetUInteger(ownerArena, &(handle->owner.cert->version),
305
                            SEC_CERTIFICATE_VERSION_3);
306
            break;
307
        case CrlExtensions:
308
            handle->owner.crl->extensions = exts;
309
            DER_SetUInteger(ownerArena, &(handle->owner.crl->version),
310
                            SEC_CRL_VERSION_2);
311
            break;
312
        case OCSPRequestExtensions:
313
            handle->owner.request->tbsRequest->requestExtensions = exts;
314
            break;
315
        case OCSPSingleRequestExtensions:
316
            handle->owner.singleRequest->singleRequestExtensions = exts;
317
            break;
318
        case OCSPResponseSingleExtensions:
319
            handle->owner.singleResponse->singleExtensions = exts;
320
            break;
321
    }
322
#endif
323
324
0
    handle->setExts(handle->object, exts);
325
326
    /* update the version number */
327
328
    /* copy each extension pointer */
329
0
    node = handle->head;
330
0
    while (node) {
331
0
        *exts = node->ext;
332
333
0
        node = node->next;
334
0
        exts++;
335
0
    }
336
337
    /* terminate the array of extensions */
338
0
    *exts = 0;
339
340
0
    rv = SECSuccess;
341
342
0
loser:
343
    /* free working arena */
344
0
    PORT_FreeArena(handle->arena, PR_FALSE);
345
0
    return rv;
346
0
}
347
348
SECStatus
349
CERT_MergeExtensions(void *exthandle, CERTCertExtension **extensions)
350
0
{
351
0
    CERTCertExtension *ext;
352
0
    SECStatus rv = SECSuccess;
353
0
    SECOidTag tag;
354
0
    extNode *node;
355
0
    extRec *handle = exthandle;
356
357
0
    if (!exthandle || !extensions) {
358
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
359
0
        return SECFailure;
360
0
    }
361
0
    while ((ext = *extensions++) != NULL) {
362
0
        tag = SECOID_FindOIDTag(&ext->id);
363
0
        for (node = handle->head; node != NULL; node = node->next) {
364
0
            if (tag == 0) {
365
0
                if (SECITEM_ItemsAreEqual(&ext->id, &node->ext->id))
366
0
                    break;
367
0
            } else {
368
0
                if (SECOID_FindOIDTag(&node->ext->id) == tag) {
369
0
                    break;
370
0
                }
371
0
            }
372
0
        }
373
0
        if (node == NULL) {
374
0
            PRBool critical = (ext->critical.len != 0 &&
375
0
                               ext->critical.data[ext->critical.len - 1] != 0);
376
0
            if (critical && tag == SEC_OID_UNKNOWN) {
377
0
                PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
378
0
                rv = SECFailure;
379
0
                break;
380
0
            }
381
            /* add to list */
382
0
            rv = CERT_AddExtensionByOID(exthandle, &ext->id, &ext->value,
383
0
                                        critical, PR_TRUE);
384
0
            if (rv != SECSuccess)
385
0
                break;
386
0
        }
387
0
    }
388
0
    return rv;
389
0
}
390
391
/*
392
 * get the value of the Netscape Certificate Type Extension
393
 */
394
SECStatus
395
CERT_FindBitStringExtension(CERTCertExtension **extensions, int tag,
396
                            SECItem *retItem)
397
0
{
398
0
    SECItem wrapperItem, tmpItem = { siBuffer, 0 };
399
0
    SECStatus rv;
400
0
    PORTCheapArenaPool tmpArena;
401
402
0
    wrapperItem.data = NULL;
403
0
    tmpItem.data = NULL;
404
405
0
    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
406
407
0
    rv = cert_FindExtension(extensions, tag, &wrapperItem);
408
0
    if (rv != SECSuccess) {
409
0
        goto loser;
410
0
    }
411
412
0
    rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &tmpItem,
413
0
                                SEC_ASN1_GET(SEC_BitStringTemplate),
414
0
                                &wrapperItem);
415
416
0
    if (rv != SECSuccess) {
417
0
        goto loser;
418
0
    }
419
420
0
    retItem->data = (unsigned char *)PORT_ZAlloc((tmpItem.len + 7) >> 3);
421
0
    if (retItem->data == NULL) {
422
0
        goto loser;
423
0
    }
424
425
0
    if (tmpItem.len > 0) {
426
0
        PORT_Memcpy(retItem->data, tmpItem.data, (tmpItem.len + 7) >> 3);
427
0
    }
428
429
0
    retItem->len = tmpItem.len;
430
431
0
    rv = SECSuccess;
432
0
    goto done;
433
434
0
loser:
435
0
    rv = SECFailure;
436
437
0
done:
438
0
    PORT_DestroyCheapArena(&tmpArena);
439
440
0
    if (wrapperItem.data) {
441
0
        PORT_Free(wrapperItem.data);
442
0
    }
443
444
0
    return (rv);
445
0
}
446
447
PRBool
448
cert_HasCriticalExtension(CERTCertExtension **extensions)
449
0
{
450
0
    CERTCertExtension **exts;
451
0
    CERTCertExtension *ext = NULL;
452
0
    PRBool hasCriticalExten = PR_FALSE;
453
454
0
    exts = extensions;
455
456
0
    if (exts) {
457
0
        while (*exts) {
458
0
            ext = *exts;
459
            /* If the criticality is omitted, it's non-critical */
460
0
            if (ext->critical.data && ext->critical.data[0] == 0xff) {
461
0
                hasCriticalExten = PR_TRUE;
462
0
                break;
463
0
            }
464
0
            exts++;
465
0
        }
466
0
    }
467
0
    return (hasCriticalExten);
468
0
}
469
470
PRBool
471
cert_HasUnknownCriticalExten(CERTCertExtension **extensions)
472
0
{
473
0
    CERTCertExtension **exts;
474
0
    CERTCertExtension *ext = NULL;
475
0
    PRBool hasUnknownCriticalExten = PR_FALSE;
476
477
0
    exts = extensions;
478
479
0
    if (exts) {
480
0
        while (*exts) {
481
0
            ext = *exts;
482
            /* If the criticality is omitted, it's non-critical.
483
               If an extension is critical, make sure that we know
484
               how to process the extension.
485
             */
486
0
            if (ext->critical.data && ext->critical.data[0] == 0xff) {
487
0
                if (SECOID_KnownCertExtenOID(&ext->id) == PR_FALSE) {
488
0
                    hasUnknownCriticalExten = PR_TRUE;
489
0
                    break;
490
0
                }
491
0
            }
492
0
            exts++;
493
0
        }
494
0
    }
495
0
    return (hasUnknownCriticalExten);
496
0
}