Coverage Report

Created: 2026-10-16 07:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl/crypto/x509/x509_lu.c
Line
Count
Source
1
/*
2
 * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include <stdio.h>
11
#include "internal/cryptlib.h"
12
#include "internal/hashtable.h"
13
#include "internal/hashfunc.h"
14
#include "internal/refcount.h"
15
#include <openssl/x509.h>
16
#include "crypto/x509.h"
17
#include <openssl/x509v3.h>
18
#include "x509_local.h"
19
20
0
#define X509_OBJS_HT_BUCKETS 8
21
22
X509_LOOKUP *X509_LOOKUP_new(X509_LOOKUP_METHOD *method)
23
0
{
24
0
    X509_LOOKUP *ret = OPENSSL_zalloc(sizeof(*ret));
25
26
0
    if (ret == NULL)
27
0
        return NULL;
28
29
0
    ret->method = method;
30
0
    if (method->new_item != NULL && method->new_item(ret) == 0) {
31
0
        OPENSSL_free(ret);
32
0
        return NULL;
33
0
    }
34
0
    return ret;
35
0
}
36
37
void X509_LOOKUP_free(X509_LOOKUP *ctx)
38
0
{
39
0
    if (ctx == NULL)
40
0
        return;
41
0
    if ((ctx->method != NULL) && (ctx->method->free != NULL))
42
0
        (*ctx->method->free)(ctx);
43
0
    OPENSSL_free(ctx);
44
0
}
45
46
int X509_STORE_lock(X509_STORE *xs)
47
0
{
48
0
    return CRYPTO_THREAD_write_lock(xs->lock);
49
0
}
50
51
int ossl_x509_store_read_lock(X509_STORE *xs)
52
0
{
53
0
    return CRYPTO_THREAD_read_lock(xs->lock);
54
0
}
55
56
int X509_STORE_unlock(X509_STORE *xs)
57
0
{
58
0
    return CRYPTO_THREAD_unlock(xs->lock);
59
0
}
60
61
int X509_LOOKUP_init(X509_LOOKUP *ctx)
62
0
{
63
0
    if (ctx->method == NULL)
64
0
        return 0;
65
0
    if (ctx->method->init != NULL)
66
0
        return ctx->method->init(ctx);
67
0
    else
68
0
        return 1;
69
0
}
70
71
int X509_LOOKUP_shutdown(X509_LOOKUP *ctx)
72
0
{
73
0
    if (ctx->method == NULL)
74
0
        return 0;
75
0
    if (ctx->method->shutdown != NULL)
76
0
        return ctx->method->shutdown(ctx);
77
0
    else
78
0
        return 1;
79
0
}
80
81
int X509_LOOKUP_ctrl_ex(X509_LOOKUP *ctx, int cmd, const char *argc, long argl,
82
    char **ret, OSSL_LIB_CTX *libctx, const char *propq)
83
0
{
84
0
    if (ctx->method == NULL)
85
0
        return -1;
86
0
    if (ctx->method->ctrl_ex != NULL)
87
0
        return ctx->method->ctrl_ex(ctx, cmd, argc, argl, ret, libctx, propq);
88
0
    if (ctx->method->ctrl != NULL)
89
0
        return ctx->method->ctrl(ctx, cmd, argc, argl, ret);
90
0
    return 1;
91
0
}
92
93
int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl,
94
    char **ret)
95
0
{
96
0
    return X509_LOOKUP_ctrl_ex(ctx, cmd, argc, argl, ret, NULL, NULL);
97
0
}
98
99
int X509_LOOKUP_by_subject_ex(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type,
100
    const X509_NAME *name, X509_OBJECT *ret,
101
    OSSL_LIB_CTX *libctx, const char *propq)
102
0
{
103
0
    if (ctx->skip
104
0
        || ctx->method == NULL
105
0
        || (ctx->method->get_by_subject == NULL
106
0
            && ctx->method->get_by_subject_ex == NULL))
107
0
        return 0;
108
0
    if (ctx->method->get_by_subject_ex != NULL)
109
0
        return ctx->method->get_by_subject_ex(ctx, type, name, ret, libctx,
110
0
            propq);
111
0
    else
112
0
        return ctx->method->get_by_subject(ctx, type, name, ret);
113
0
}
114
115
int X509_LOOKUP_by_subject(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type,
116
    const X509_NAME *name, X509_OBJECT *ret)
117
0
{
118
0
    return X509_LOOKUP_by_subject_ex(ctx, type, name, ret, NULL, NULL);
119
0
}
120
121
int X509_LOOKUP_by_issuer_serial(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type,
122
    const X509_NAME *name,
123
    const ASN1_INTEGER *serial,
124
    X509_OBJECT *ret)
125
0
{
126
0
    if ((ctx->method == NULL) || (ctx->method->get_by_issuer_serial == NULL))
127
0
        return 0;
128
0
    return ctx->method->get_by_issuer_serial(ctx, type, name, serial, ret);
129
0
}
130
131
int X509_LOOKUP_by_fingerprint(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type,
132
    const unsigned char *bytes, int len,
133
    X509_OBJECT *ret)
134
0
{
135
0
    if ((ctx->method == NULL) || (ctx->method->get_by_fingerprint == NULL))
136
0
        return 0;
137
0
    return ctx->method->get_by_fingerprint(ctx, type, bytes, len, ret);
138
0
}
139
140
int X509_LOOKUP_by_alias(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type,
141
    const char *str, int len, X509_OBJECT *ret)
142
0
{
143
0
    if ((ctx->method == NULL) || (ctx->method->get_by_alias == NULL))
144
0
        return 0;
145
0
    return ctx->method->get_by_alias(ctx, type, str, len, ret);
146
0
}
147
148
int X509_LOOKUP_set_method_data(X509_LOOKUP *ctx, void *data)
149
0
{
150
0
    ctx->method_data = data;
151
0
    return 1;
152
0
}
153
154
void *X509_LOOKUP_get_method_data(const X509_LOOKUP *ctx)
155
0
{
156
0
    return ctx->method_data;
157
0
}
158
159
X509_STORE *X509_LOOKUP_get_store(const X509_LOOKUP *ctx)
160
0
{
161
0
    return ctx->store_ctx;
162
0
}
163
164
static int x509_object_cmp(const X509_OBJECT *const *a,
165
    const X509_OBJECT *const *b)
166
0
{
167
0
    int ret;
168
169
0
    ret = ((*a)->type - (*b)->type);
170
0
    if (ret)
171
0
        return ret;
172
0
    switch ((*a)->type) {
173
0
    case X509_LU_X509:
174
0
        ret = X509_subject_name_cmp((*a)->data.x509, (*b)->data.x509);
175
0
        break;
176
0
    case X509_LU_CRL:
177
0
        ret = X509_CRL_cmp((*a)->data.crl, (*b)->data.crl);
178
0
        break;
179
0
    case X509_LU_NONE:
180
        /* abort(); */
181
0
        return 0;
182
0
    }
183
0
    return ret;
184
0
}
185
186
static void objs_ht_free(HT_VALUE *v)
187
0
{
188
0
    STACK_OF(X509_OBJECT) *objs = v->value;
189
190
0
    sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
191
0
}
192
193
static uint64_t obj_ht_hash(HT_KEY *key)
194
0
{
195
0
    OBJS_KEY *k = (OBJS_KEY *)key;
196
197
0
    return ossl_fnv1a_hash(k->keyfields.xn_canon, k->keyfields.xn_canon_enclen);
198
0
}
199
200
X509_STORE *X509_STORE_new(void)
201
0
{
202
0
    X509_STORE *ret = OPENSSL_zalloc(sizeof(*ret));
203
0
    HT_CONFIG htconf = {
204
0
        .ht_free_fn = objs_ht_free,
205
0
        .ht_hash_fn = obj_ht_hash,
206
0
        .init_neighborhoods = X509_OBJS_HT_BUCKETS,
207
0
        .no_rcu = 1,
208
0
    };
209
210
0
    if (ret == NULL)
211
0
        return NULL;
212
0
    if ((ret->objs_ht = ossl_ht_new(&htconf)) == NULL) {
213
0
        ERR_raise(ERR_LIB_X509, ERR_R_CRYPTO_LIB);
214
0
        goto err;
215
0
    }
216
0
    if ((ret->objs = sk_X509_OBJECT_new(x509_object_cmp)) == NULL) {
217
0
        ERR_raise(ERR_LIB_X509, ERR_R_CRYPTO_LIB);
218
0
        goto err;
219
0
    }
220
0
    ret->cache = 1;
221
0
    if ((ret->get_cert_methods = sk_X509_LOOKUP_new_null()) == NULL) {
222
0
        ERR_raise(ERR_LIB_X509, ERR_R_CRYPTO_LIB);
223
0
        goto err;
224
0
    }
225
226
0
    if ((ret->param = X509_VERIFY_PARAM_new()) == NULL) {
227
0
        ERR_raise(ERR_LIB_X509, ERR_R_X509_LIB);
228
0
        goto err;
229
0
    }
230
0
    if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509_STORE, ret, &ret->ex_data)) {
231
0
        ERR_raise(ERR_LIB_X509, ERR_R_CRYPTO_LIB);
232
0
        goto err;
233
0
    }
234
235
0
    ret->lock = CRYPTO_THREAD_lock_new();
236
0
    if (ret->lock == NULL) {
237
0
        ERR_raise(ERR_LIB_X509, ERR_R_CRYPTO_LIB);
238
0
        goto err;
239
0
    }
240
241
0
    if (!CRYPTO_NEW_REF(&ret->references, 1))
242
0
        goto err;
243
0
    return ret;
244
245
0
err:
246
0
    X509_VERIFY_PARAM_free(ret->param);
247
0
    sk_X509_OBJECT_free(ret->objs);
248
0
    sk_X509_LOOKUP_free(ret->get_cert_methods);
249
0
    ossl_ht_free(ret->objs_ht);
250
0
    CRYPTO_THREAD_lock_free(ret->lock);
251
0
    OPENSSL_free(ret);
252
0
    return NULL;
253
0
}
254
255
void X509_STORE_free(X509_STORE *xs)
256
0
{
257
0
    int i;
258
0
    STACK_OF(X509_LOOKUP) *sk;
259
0
    X509_LOOKUP *lu;
260
261
0
    if (xs == NULL)
262
0
        return;
263
0
    CRYPTO_DOWN_REF(&xs->references, &i);
264
0
    REF_PRINT_COUNT("X509_STORE", i, xs);
265
0
    if (i > 0)
266
0
        return;
267
0
    REF_ASSERT_ISNT(i < 0);
268
269
0
    sk = xs->get_cert_methods;
270
0
    for (i = 0; i < sk_X509_LOOKUP_num(sk); i++) {
271
0
        lu = sk_X509_LOOKUP_value(sk, i);
272
0
        X509_LOOKUP_shutdown(lu);
273
0
        X509_LOOKUP_free(lu);
274
0
    }
275
0
    sk_X509_LOOKUP_free(sk);
276
0
    sk_X509_OBJECT_pop_free(xs->objs, X509_OBJECT_free);
277
278
0
    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_X509_STORE, xs, &xs->ex_data);
279
0
    X509_VERIFY_PARAM_free(xs->param);
280
0
    CRYPTO_THREAD_lock_free(xs->lock);
281
0
    CRYPTO_FREE_REF(&xs->references);
282
0
    ossl_ht_free(xs->objs_ht);
283
0
    OPENSSL_free(xs);
284
0
}
285
286
int X509_STORE_up_ref(X509_STORE *xs)
287
0
{
288
0
    int i;
289
290
0
    if (!CRYPTO_UP_REF(&xs->references, &i))
291
0
        return 0;
292
293
0
    REF_PRINT_COUNT("X509_STORE", i, xs);
294
0
    REF_ASSERT_ISNT(i < 2);
295
0
    return i > 1 ? 1 : 0;
296
0
}
297
298
X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *xs, X509_LOOKUP_METHOD *m)
299
0
{
300
0
    int i;
301
0
    STACK_OF(X509_LOOKUP) *sk;
302
0
    X509_LOOKUP *lu;
303
304
0
    sk = xs->get_cert_methods;
305
0
    for (i = 0; i < sk_X509_LOOKUP_num(sk); i++) {
306
0
        lu = sk_X509_LOOKUP_value(sk, i);
307
0
        if (m == lu->method) {
308
0
            return lu;
309
0
        }
310
0
    }
311
    /* a new one */
312
0
    lu = X509_LOOKUP_new(m);
313
0
    if (lu == NULL) {
314
0
        ERR_raise(ERR_LIB_X509, ERR_R_X509_LIB);
315
0
        return NULL;
316
0
    }
317
318
0
    lu->store_ctx = xs;
319
0
    if (sk_X509_LOOKUP_push(xs->get_cert_methods, lu))
320
0
        return lu;
321
    /* sk_X509_LOOKUP_push() failed */
322
0
    ERR_raise(ERR_LIB_X509, ERR_R_CRYPTO_LIB);
323
0
    X509_LOOKUP_free(lu);
324
0
    return NULL;
325
0
}
326
327
/* Also fill the cache (ctx->store->objs) with all matching certificates. */
328
X509_OBJECT *X509_STORE_CTX_get_obj_by_subject(X509_STORE_CTX *ctx,
329
    X509_LOOKUP_TYPE type,
330
    const X509_NAME *name)
331
0
{
332
0
    X509_OBJECT *ret = X509_OBJECT_new();
333
334
0
    if (ret == NULL)
335
0
        return NULL;
336
0
    if (!X509_STORE_CTX_get_by_subject(ctx, type, name, ret)) {
337
0
        X509_OBJECT_free(ret);
338
0
        return NULL;
339
0
    }
340
0
    return ret;
341
0
}
342
343
STACK_OF(X509_OBJECT) *ossl_x509_store_ht_get_by_name(const X509_STORE *store,
344
    const X509_NAME *xn)
345
0
{
346
0
    HT_VALUE *v;
347
0
    OBJS_KEY key;
348
0
    int ret;
349
350
0
    if (xn->canon_enc == NULL || xn->modified) {
351
0
        ret = i2d_X509_NAME((X509_NAME *)xn, NULL);
352
0
        if (ret < 0)
353
0
            return NULL;
354
0
    }
355
356
0
    HT_INIT_KEY(&key);
357
0
    HT_SET_KEY_FIELD(&key, xn_canon, xn->canon_enc);
358
0
    HT_SET_KEY_FIELD(&key, xn_canon_enclen, xn->canon_enclen);
359
0
    v = ossl_ht_get(store->objs_ht, TO_HT_KEY(&key));
360
0
    if (v == NULL)
361
0
        return NULL;
362
0
    v = ossl_rcu_deref(&v);
363
364
0
    return v->value;
365
0
}
366
367
static int x509_name_objs_ht_insert(const X509_STORE *store, const X509_NAME *xn,
368
    STACK_OF(X509_OBJECT) *objs, X509_OBJECT *obj)
369
0
{
370
0
    int ret = 0, added = 0;
371
0
    OBJS_KEY key;
372
0
    HT_VALUE val = { 0 };
373
374
0
    if (objs != NULL) {
375
0
        added = sk_X509_OBJECT_push(objs, obj) != 0;
376
0
        return added;
377
0
    }
378
379
0
    if (xn->canon_enc == NULL || xn->modified) {
380
0
        ret = i2d_X509_NAME((X509_NAME *)xn, NULL);
381
0
        if (ret < 0)
382
0
            return 0;
383
0
    }
384
385
0
    objs = sk_X509_OBJECT_new(x509_object_cmp);
386
0
    if (objs == NULL)
387
0
        return 0;
388
389
0
    added = sk_X509_OBJECT_push(objs, obj) != 0;
390
0
    if (added == 0) {
391
0
        sk_X509_OBJECT_free(objs);
392
0
        return 0;
393
0
    }
394
395
0
    HT_INIT_KEY(&key);
396
0
    HT_SET_KEY_FIELD(&key, xn_canon, xn->canon_enc);
397
0
    HT_SET_KEY_FIELD(&key, xn_canon_enclen, xn->canon_enclen);
398
0
    val.value = (void *)objs;
399
0
    ret = ossl_ht_insert(store->objs_ht, TO_HT_KEY(&key), &val, NULL);
400
0
    if (ret != 1) {
401
0
        sk_X509_OBJECT_free(objs);
402
0
        return 0;
403
0
    }
404
405
0
    return 1;
406
0
}
407
408
static int obj_ht_foreach_certs(HT_VALUE *v, void *arg)
409
0
{
410
0
    STACK_OF(X509) **sk = arg;
411
0
    STACK_OF(X509_OBJECT) *objs = v->value;
412
0
    int i, r;
413
414
0
    for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
415
0
        X509 *cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objs, i));
416
417
0
        if (cert == NULL)
418
0
            continue;
419
0
        r = X509_add_cert(*sk, cert, X509_ADD_FLAG_UP_REF);
420
0
        if (r == 0) {
421
0
            OSSL_STACK_OF_X509_free(*sk);
422
0
            *sk = NULL;
423
0
            return 0;
424
0
        }
425
0
    }
426
427
0
    return 1;
428
0
}
429
430
/*
431
 * Increment the reference count of the object contained in |a|, if any.
432
 * The X509_OBJECT itself is not reference counted.
433
 */
434
static int x509_object_up_ref_count(X509_OBJECT *a)
435
0
{
436
0
    switch (a->type) {
437
0
    case X509_LU_NONE:
438
0
        break;
439
0
    case X509_LU_X509:
440
0
        return X509_up_ref(a->data.x509);
441
0
    case X509_LU_CRL:
442
0
        return X509_CRL_up_ref(a->data.crl);
443
0
    }
444
0
    return 1;
445
0
}
446
447
/*
448
 * May be called with |ret| == NULL just for the side effect of
449
 * caching all certs matching the given subject DN in |ctx->store->objs|.
450
 * Returns 1 if successful,
451
 * 0 if not found or X509_LOOKUP_by_subject_ex() returns an error,
452
 * -1 on failure
453
 */
454
int ossl_x509_store_ctx_get_by_subject(const X509_STORE_CTX *ctx, X509_LOOKUP_TYPE type,
455
    const X509_NAME *name, X509_OBJECT *ret)
456
0
{
457
0
    X509_STORE *store = ctx->store;
458
0
    X509_LOOKUP *lu;
459
0
    X509_OBJECT stmp, *tmp = NULL;
460
0
    STACK_OF(X509_OBJECT) *objs;
461
0
    int i, j;
462
463
0
    if (store == NULL)
464
0
        return 0;
465
466
0
    stmp.type = X509_LU_NONE;
467
0
    stmp.data.x509 = NULL;
468
469
0
    if (!ossl_x509_store_read_lock(store))
470
0
        return 0;
471
0
    if (store->objs_ht != NULL) {
472
0
        objs = ossl_x509_store_ht_get_by_name(store, name);
473
0
        if (objs != NULL)
474
0
            tmp = X509_OBJECT_retrieve_by_subject(objs, type, name);
475
0
    } else {
476
0
        tmp = X509_OBJECT_retrieve_by_subject(store->objs, type, name);
477
0
    }
478
0
    X509_STORE_unlock(store);
479
480
0
    if (tmp == NULL || type == X509_LU_CRL) {
481
0
        for (i = 0; i < sk_X509_LOOKUP_num(store->get_cert_methods); i++) {
482
0
            lu = sk_X509_LOOKUP_value(store->get_cert_methods, i);
483
0
            if (lu->skip)
484
0
                continue;
485
0
            if (lu->method == NULL)
486
0
                return -1;
487
0
            j = X509_LOOKUP_by_subject_ex(lu, type, name, &stmp,
488
0
                ctx->libctx, ctx->propq);
489
0
            if (j != 0) { /* non-zero value is considered success here */
490
0
                tmp = &stmp;
491
0
                break;
492
0
            }
493
0
        }
494
0
        if (tmp == NULL)
495
0
            return 0;
496
0
    }
497
498
0
    if (ret != NULL) {
499
0
        if (!x509_object_up_ref_count(tmp))
500
0
            return -1;
501
0
        ret->type = tmp->type;
502
0
        ret->data = tmp->data;
503
0
    }
504
0
    return 1;
505
0
}
506
507
/* Also fill the cache |ctx->store->objs| with all matching certificates. */
508
int X509_STORE_CTX_get_by_subject(const X509_STORE_CTX *ctx,
509
    X509_LOOKUP_TYPE type,
510
    const X509_NAME *name, X509_OBJECT *ret)
511
0
{
512
0
    return ossl_x509_store_ctx_get_by_subject(ctx, type, name, ret) > 0;
513
0
}
514
static int x509_store_add_obj(X509_STORE *store, X509_OBJECT *obj)
515
0
{
516
0
    const X509_NAME *xn;
517
0
    STACK_OF(X509_OBJECT) *objs = NULL;
518
0
    int ret = 0, added = 0;
519
520
0
    if (obj->type == X509_LU_CRL)
521
0
        xn = obj->data.crl->crl.issuer;
522
0
    else
523
0
        xn = obj->data.x509->cert_info.subject;
524
525
0
    if (xn == NULL) {
526
0
        obj->type = X509_LU_NONE;
527
0
        X509_OBJECT_free(obj);
528
0
        return 0;
529
0
    }
530
531
0
    if (!x509_object_up_ref_count(obj)) {
532
0
        obj->type = X509_LU_NONE;
533
0
        X509_OBJECT_free(obj);
534
0
        return 0;
535
0
    }
536
537
0
    if (!X509_STORE_lock(store)) {
538
0
        X509_OBJECT_free(obj);
539
0
        return 0;
540
0
    }
541
542
0
    if (store->objs_ht != NULL) {
543
0
        objs = ossl_x509_store_ht_get_by_name(store, xn);
544
0
        if (objs != NULL && X509_OBJECT_retrieve_match(objs, obj)) {
545
0
            ret = 1;
546
0
        } else {
547
0
            added = x509_name_objs_ht_insert(store, xn, objs, obj);
548
0
            ret = added != 0;
549
0
        }
550
0
    } else {
551
0
        if (X509_OBJECT_retrieve_match(store->objs, obj)) {
552
0
            ret = 1;
553
0
        } else {
554
0
            added = sk_X509_OBJECT_push(store->objs, obj);
555
0
            ret = added != 0;
556
0
        }
557
0
    }
558
0
    X509_STORE_unlock(store);
559
560
0
    if (added == 0) /* obj not pushed */
561
0
        X509_OBJECT_free(obj);
562
563
0
    return ret;
564
0
}
565
566
static int x509_store_add_x509(X509_STORE *store, const X509 *x)
567
0
{
568
0
    X509_OBJECT *obj;
569
570
0
    if (x == NULL)
571
0
        return 0;
572
0
    obj = X509_OBJECT_new();
573
0
    if (obj == NULL)
574
0
        return 0;
575
576
0
    obj->type = X509_LU_X509;
577
    /*
578
     * XXX Casts away const, get rid of this once we can have the x509
579
     * member of OBJECT be const.
580
     */
581
0
    obj->data.x509 = (X509 *)x;
582
583
0
    return x509_store_add_obj(store, obj);
584
0
}
585
586
static int x509_store_add_crl(X509_STORE *store, X509_CRL *crl)
587
0
{
588
0
    X509_OBJECT *obj;
589
590
0
    if (crl == NULL)
591
0
        return 0;
592
0
    obj = X509_OBJECT_new();
593
0
    if (obj == NULL)
594
0
        return 0;
595
596
0
    obj->type = X509_LU_CRL;
597
0
    obj->data.crl = crl;
598
599
0
    return x509_store_add_obj(store, obj);
600
0
}
601
602
int X509_STORE_add_cert(X509_STORE *xs, const X509 *x)
603
0
{
604
0
    if (!x509_store_add_x509(xs, x)) {
605
0
        ERR_raise(ERR_LIB_X509, ERR_R_X509_LIB);
606
0
        return 0;
607
0
    }
608
0
    return 1;
609
0
}
610
611
int X509_STORE_add_crl(X509_STORE *xs, X509_CRL *x)
612
0
{
613
0
    if (!x509_store_add_crl(xs, x)) {
614
0
        ERR_raise(ERR_LIB_X509, ERR_R_X509_LIB);
615
0
        return 0;
616
0
    }
617
0
    return 1;
618
0
}
619
620
#ifndef OPENSSL_NO_DEPRECATED_4_2
621
int X509_OBJECT_up_ref_count(X509_OBJECT *a)
622
0
{
623
0
    return x509_object_up_ref_count(a);
624
0
}
625
#endif
626
627
X509 *X509_OBJECT_get0_X509(const X509_OBJECT *a)
628
0
{
629
0
    if (a == NULL || a->type != X509_LU_X509)
630
0
        return NULL;
631
0
    return a->data.x509;
632
0
}
633
634
X509_CRL *X509_OBJECT_get0_X509_CRL(const X509_OBJECT *a)
635
0
{
636
0
    if (a == NULL || a->type != X509_LU_CRL)
637
0
        return NULL;
638
0
    return a->data.crl;
639
0
}
640
641
X509_LOOKUP_TYPE X509_OBJECT_get_type(const X509_OBJECT *a)
642
0
{
643
0
    return a->type;
644
0
}
645
646
X509_OBJECT *X509_OBJECT_new(void)
647
0
{
648
0
    X509_OBJECT *ret = OPENSSL_zalloc(sizeof(*ret));
649
650
0
    if (ret == NULL)
651
0
        return NULL;
652
0
    ret->type = X509_LU_NONE;
653
0
    return ret;
654
0
}
655
656
static void x509_object_free_internal(X509_OBJECT *a)
657
0
{
658
0
    if (a == NULL)
659
0
        return;
660
0
    switch (a->type) {
661
0
    case X509_LU_NONE:
662
0
        break;
663
0
    case X509_LU_X509:
664
0
        X509_free(a->data.x509);
665
0
        break;
666
0
    case X509_LU_CRL:
667
0
        X509_CRL_free(a->data.crl);
668
0
        break;
669
0
    }
670
0
}
671
672
int X509_OBJECT_set1_X509(X509_OBJECT *a, X509 *obj)
673
0
{
674
0
    if (a == NULL || !X509_up_ref(obj))
675
0
        return 0;
676
677
0
    x509_object_free_internal(a);
678
0
    a->type = X509_LU_X509;
679
0
    a->data.x509 = obj;
680
0
    return 1;
681
0
}
682
683
int X509_OBJECT_set1_X509_CRL(X509_OBJECT *a, X509_CRL *obj)
684
0
{
685
0
    if (a == NULL || !X509_CRL_up_ref(obj))
686
0
        return 0;
687
688
0
    x509_object_free_internal(a);
689
0
    a->type = X509_LU_CRL;
690
0
    a->data.crl = obj;
691
0
    return 1;
692
0
}
693
694
void X509_OBJECT_free(X509_OBJECT *a)
695
0
{
696
0
    x509_object_free_internal(a);
697
0
    OPENSSL_free(a);
698
0
}
699
700
/* Returns -1 if not found, but also on error */
701
static int x509_object_idx_cnt(STACK_OF(X509_OBJECT) *h, X509_LOOKUP_TYPE type,
702
    const X509_NAME *name, int *pnmatch)
703
0
{
704
0
    X509_OBJECT stmp;
705
0
    X509 x509_s;
706
0
    X509_CRL crl_s;
707
708
0
    stmp.type = type;
709
0
    switch (type) {
710
0
    case X509_LU_X509:
711
0
        stmp.data.x509 = &x509_s;
712
0
        x509_s.cert_info.subject = (X509_NAME *)name; /* won't modify it */
713
0
        break;
714
0
    case X509_LU_CRL:
715
0
        stmp.data.crl = &crl_s;
716
0
        crl_s.crl.issuer = (X509_NAME *)name; /* won't modify it */
717
0
        break;
718
0
    case X509_LU_NONE:
719
0
    default:
720
        /* abort(); */
721
0
        return -1;
722
0
    }
723
724
    /* Assumes h is locked for read if applicable */
725
0
    return sk_X509_OBJECT_find_all(h, &stmp, pnmatch);
726
0
}
727
728
/* Assumes h is locked for read if applicable */
729
int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h, X509_LOOKUP_TYPE type,
730
    const X509_NAME *name)
731
0
{
732
0
    return x509_object_idx_cnt(h, type, name, NULL);
733
0
}
734
735
/* Assumes h is locked for read if applicable */
736
X509_OBJECT *X509_OBJECT_retrieve_by_subject(STACK_OF(X509_OBJECT) *h,
737
    X509_LOOKUP_TYPE type,
738
    const X509_NAME *name)
739
0
{
740
0
    int idx = X509_OBJECT_idx_by_subject(h, type, name);
741
742
0
    if (idx == -1)
743
0
        return NULL;
744
0
    return sk_X509_OBJECT_value(h, idx);
745
0
}
746
747
static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj)
748
0
{
749
0
    X509_OBJECT *ret = X509_OBJECT_new();
750
0
    if (ret == NULL)
751
0
        return NULL;
752
753
0
    ret->type = obj->type;
754
0
    ret->data = obj->data;
755
756
0
    if (!x509_object_up_ref_count(ret)) {
757
0
        OPENSSL_free(ret);
758
0
        return NULL;
759
0
    }
760
761
0
    return ret;
762
0
}
763
764
#ifndef OPENSSL_NO_DEPRECATED_4_0
765
static int obj_ht_to_stack(HT_VALUE *v, void *arg)
766
0
{
767
0
    STACK_OF(X509_OBJECT) **sk = arg;
768
0
    STACK_OF(X509_OBJECT) *objs = v->value;
769
0
    X509_OBJECT *obj;
770
0
    int i;
771
772
0
    for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
773
0
        obj = sk_X509_OBJECT_value(objs, i);
774
0
        if (sk_X509_OBJECT_push(*sk, obj) == 0)
775
0
            goto err;
776
0
    }
777
778
0
    return 1;
779
780
0
err:
781
0
    sk_X509_OBJECT_zero(*sk);
782
783
0
    return 0;
784
0
}
785
786
static int obj_ht_zero_stacks(HT_VALUE *v, void *arg)
787
0
{
788
0
    STACK_OF(X509_OBJECT) *objs = v->value;
789
790
0
    sk_X509_OBJECT_zero(objs);
791
792
0
    return 1;
793
0
}
794
795
STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(const X509_STORE *xs)
796
0
{
797
0
    X509_STORE *store = (X509_STORE *)xs;
798
799
0
    if (xs->objs_ht != NULL) {
800
0
        ossl_ht_foreach_until(xs->objs_ht, obj_ht_to_stack, &store->objs);
801
0
        if (sk_X509_OBJECT_num(store->objs) > 0
802
0
            || ossl_ht_count(xs->objs_ht) == 0) {
803
0
            ossl_ht_foreach_until(xs->objs_ht, obj_ht_zero_stacks, NULL);
804
0
            ossl_ht_free(xs->objs_ht);
805
0
            store->objs_ht = NULL;
806
0
        } else {
807
0
            return NULL;
808
0
        }
809
0
    }
810
811
0
    return xs->objs;
812
0
}
813
#endif
814
815
static int obj_ht_foreach_object(HT_VALUE *v, void *arg)
816
0
{
817
0
    STACK_OF(X509_OBJECT) **sk = arg;
818
0
    STACK_OF(X509_OBJECT) *objs = v->value;
819
0
    X509_OBJECT *dup, *obj;
820
0
    int i;
821
822
0
    for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
823
0
        obj = sk_X509_OBJECT_value(objs, i);
824
0
        dup = x509_object_dup(obj);
825
0
        if (dup == NULL)
826
0
            goto err;
827
0
        if (sk_X509_OBJECT_push(*sk, dup) == 0)
828
0
            goto err;
829
0
    }
830
831
0
    return 1;
832
833
0
err:
834
0
    X509_OBJECT_free(dup);
835
0
    sk_X509_OBJECT_pop_free(*sk, X509_OBJECT_free);
836
0
    *sk = NULL;
837
838
0
    return 0;
839
0
}
840
841
STACK_OF(X509_OBJECT) *X509_STORE_get1_objects(X509_STORE *store)
842
0
{
843
0
    STACK_OF(X509_OBJECT) *objs;
844
845
0
    if (store == NULL) {
846
0
        ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
847
0
        return NULL;
848
0
    }
849
850
0
    if (!ossl_x509_store_read_lock(store))
851
0
        return NULL;
852
853
0
    if (store->objs_ht != NULL) {
854
0
        if ((objs = sk_X509_OBJECT_new(x509_object_cmp)) == NULL) {
855
0
            X509_STORE_unlock(store);
856
0
            return NULL;
857
0
        }
858
0
        ossl_ht_foreach_until(store->objs_ht, obj_ht_foreach_object, &objs);
859
0
    } else {
860
0
        objs = sk_X509_OBJECT_deep_copy(store->objs, x509_object_dup,
861
0
            X509_OBJECT_free);
862
0
    }
863
0
    X509_STORE_unlock(store);
864
865
0
    return objs;
866
0
}
867
868
STACK_OF(X509) *X509_STORE_get1_all_certs(X509_STORE *store)
869
0
{
870
0
    STACK_OF(X509) *sk = NULL;
871
872
0
    if (store == NULL) {
873
0
        ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
874
0
        return NULL;
875
0
    }
876
0
    if ((sk = sk_X509_new_null()) == NULL)
877
0
        return NULL;
878
879
0
    if (!ossl_x509_store_read_lock(store))
880
0
        goto out_free;
881
882
0
    if (store->objs_ht != NULL) {
883
0
        ossl_ht_foreach_until(store->objs_ht, obj_ht_foreach_certs, &sk);
884
0
    } else {
885
0
        for (int i = 0; i < sk_X509_OBJECT_num(store->objs); i++) {
886
0
            X509 *cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(store->objs, i));
887
888
0
            if (cert != NULL
889
0
                && !X509_add_cert(sk, cert, X509_ADD_FLAG_UP_REF))
890
0
                goto err;
891
0
        }
892
0
    }
893
0
    X509_STORE_unlock(store);
894
895
0
    return sk;
896
897
0
err:
898
0
    X509_STORE_unlock(store);
899
0
out_free:
900
0
    OSSL_STACK_OF_X509_free(sk);
901
0
    return NULL;
902
0
}
903
904
/*-
905
 * Collect from |ctx->store| all certs with subject matching |nm|.
906
 * Returns NULL on internal/fatal error, empty stack if not found.
907
 */
908
STACK_OF(X509) *X509_STORE_CTX_get1_certs(const X509_STORE_CTX *ctx,
909
    const X509_NAME *nm)
910
0
{
911
0
    int i, idx = -1, cnt = 0;
912
0
    STACK_OF(X509) *sk = NULL;
913
0
    X509 *x;
914
0
    X509_OBJECT *obj;
915
0
    X509_STORE *store = ctx->store;
916
0
    STACK_OF(X509_OBJECT) *objs;
917
918
0
    if (store == NULL)
919
0
        return sk_X509_new_null();
920
921
0
    if (!ossl_x509_store_read_lock(store))
922
0
        return NULL;
923
924
0
    if (store->objs_ht != NULL) {
925
0
        objs = ossl_x509_store_ht_get_by_name(store, nm);
926
0
        if (objs != NULL)
927
0
            idx = x509_object_idx_cnt(objs, X509_LU_X509, nm, &cnt);
928
0
    } else {
929
0
        objs = store->objs;
930
0
        idx = x509_object_idx_cnt(objs, X509_LU_X509, nm, &cnt);
931
0
    }
932
0
    if (idx < 0 || objs == NULL) {
933
        /*
934
         * Nothing found in cache: do lookup to possibly add new objects to
935
         * cache
936
         */
937
0
        X509_STORE_unlock(store);
938
0
        i = ossl_x509_store_ctx_get_by_subject(ctx, X509_LU_X509, nm, NULL);
939
0
        if (i <= 0)
940
0
            return i < 0 ? NULL : sk_X509_new_null();
941
0
        if (!ossl_x509_store_read_lock(store))
942
0
            return NULL;
943
0
        if (store->objs_ht != NULL) {
944
0
            objs = ossl_x509_store_ht_get_by_name(store, nm);
945
0
            if (objs == NULL)
946
0
                goto end;
947
0
        } else {
948
0
            objs = store->objs;
949
0
        }
950
0
        idx = x509_object_idx_cnt(objs, X509_LU_X509, nm, &cnt);
951
0
    }
952
953
0
    sk = sk_X509_new_null();
954
0
    if (idx < 0 || sk == NULL)
955
0
        goto end;
956
0
    for (i = idx; i < sk_X509_OBJECT_num(objs); i++) {
957
0
        obj = sk_X509_OBJECT_value(objs, i);
958
0
        x = obj->data.x509;
959
0
        if (obj->type != X509_LU_X509)
960
0
            continue;
961
0
        if (X509_add_cert(sk, x, X509_ADD_FLAG_UP_REF) == 0) {
962
0
            X509_STORE_unlock(store);
963
0
            OSSL_STACK_OF_X509_free(sk);
964
0
            return NULL;
965
0
        }
966
0
    }
967
968
0
end:
969
0
    X509_STORE_unlock(store);
970
0
    return sk;
971
0
}
972
973
/* Returns NULL on internal/fatal error, empty stack if not found */
974
STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(const X509_STORE_CTX *ctx,
975
    const X509_NAME *nm)
976
0
{
977
0
    int i = 1, idx, cnt = 0;
978
0
    STACK_OF(X509_CRL) *sk;
979
0
    X509_CRL *x;
980
0
    X509_OBJECT *obj;
981
0
    X509_STORE *store = ctx->store;
982
0
    STACK_OF(X509_OBJECT) *objs;
983
984
    /* Always do lookup to possibly add new CRLs to cache */
985
0
    i = ossl_x509_store_ctx_get_by_subject(ctx, X509_LU_CRL, nm, NULL);
986
0
    if (i < 0)
987
0
        return NULL;
988
0
    sk = sk_X509_CRL_new_null();
989
0
    if (i == 0)
990
0
        return sk;
991
0
    if (!ossl_x509_store_read_lock(store)) {
992
0
        sk_X509_CRL_free(sk);
993
0
        return NULL;
994
0
    }
995
0
    if (store->objs_ht) {
996
0
        objs = ossl_x509_store_ht_get_by_name(store, nm);
997
0
        if (objs == NULL)
998
0
            goto end;
999
0
    } else {
1000
0
        objs = store->objs;
1001
0
    }
1002
0
    idx = x509_object_idx_cnt(objs, X509_LU_CRL, nm, &cnt);
1003
0
    if (idx < 0)
1004
0
        goto end;
1005
1006
0
    for (i = idx; i < sk_X509_OBJECT_num(objs); i++) {
1007
0
        obj = sk_X509_OBJECT_value(objs, i);
1008
0
        x = obj->data.crl;
1009
0
        if (obj->type != X509_LU_CRL)
1010
0
            continue;
1011
0
        if (!X509_CRL_up_ref(x)) {
1012
0
            X509_STORE_unlock(store);
1013
0
            sk_X509_CRL_pop_free(sk, X509_CRL_free);
1014
0
            return NULL;
1015
0
        }
1016
0
        if (!sk_X509_CRL_push(sk, x)) {
1017
0
            X509_STORE_unlock(store);
1018
0
            X509_CRL_free(x);
1019
0
            sk_X509_CRL_pop_free(sk, X509_CRL_free);
1020
0
            return NULL;
1021
0
        }
1022
0
    }
1023
0
end:
1024
0
    X509_STORE_unlock(store);
1025
0
    return sk;
1026
0
}
1027
1028
X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h,
1029
    X509_OBJECT *x)
1030
0
{
1031
0
    int idx, i, num;
1032
0
    X509_OBJECT *obj;
1033
1034
0
    idx = sk_X509_OBJECT_find(h, x);
1035
0
    if (idx < 0)
1036
0
        return NULL;
1037
0
    if ((x->type != X509_LU_X509) && (x->type != X509_LU_CRL))
1038
0
        return sk_X509_OBJECT_value(h, idx);
1039
0
    for (i = idx, num = sk_X509_OBJECT_num(h); i < num; i++) {
1040
0
        obj = sk_X509_OBJECT_value(h, i);
1041
0
        if (x509_object_cmp((const X509_OBJECT **)&obj,
1042
0
                (const X509_OBJECT **)&x))
1043
0
            return NULL;
1044
0
        if (x->type == X509_LU_X509) {
1045
0
            if (!X509_cmp(obj->data.x509, x->data.x509))
1046
0
                return obj;
1047
0
        } else if (x->type == X509_LU_CRL) {
1048
0
            if (X509_CRL_match(obj->data.crl, x->data.crl) == 0)
1049
0
                return obj;
1050
0
        } else {
1051
0
            return obj;
1052
0
        }
1053
0
    }
1054
0
    return NULL;
1055
0
}
1056
1057
int X509_STORE_set_flags(X509_STORE *xs, unsigned long flags)
1058
0
{
1059
0
    return X509_VERIFY_PARAM_set_flags(xs->param, flags);
1060
0
}
1061
1062
int X509_STORE_set_depth(X509_STORE *xs, int depth)
1063
0
{
1064
0
    X509_VERIFY_PARAM_set_depth(xs->param, depth);
1065
0
    return 1;
1066
0
}
1067
1068
int X509_STORE_set_purpose(X509_STORE *xs, int purpose)
1069
0
{
1070
0
    return X509_VERIFY_PARAM_set_purpose(xs->param, purpose);
1071
0
}
1072
1073
int X509_STORE_set_trust(X509_STORE *xs, int trust)
1074
0
{
1075
0
    return X509_VERIFY_PARAM_set_trust(xs->param, trust);
1076
0
}
1077
1078
int X509_STORE_set1_param(X509_STORE *xs, const X509_VERIFY_PARAM *param)
1079
0
{
1080
0
    return X509_VERIFY_PARAM_set1(xs->param, param);
1081
0
}
1082
1083
X509_VERIFY_PARAM *X509_STORE_get0_param(const X509_STORE *xs)
1084
0
{
1085
0
    return xs->param;
1086
0
}
1087
1088
void X509_STORE_set_verify(X509_STORE *xs, X509_STORE_CTX_verify_fn verify)
1089
0
{
1090
0
    xs->verify = verify;
1091
0
}
1092
1093
X509_STORE_CTX_verify_fn X509_STORE_get_verify(const X509_STORE *xs)
1094
0
{
1095
0
    return xs->verify;
1096
0
}
1097
1098
void X509_STORE_set_verify_cb(X509_STORE *xs,
1099
    X509_STORE_CTX_verify_cb verify_cb)
1100
0
{
1101
0
    xs->verify_cb = verify_cb;
1102
0
}
1103
1104
X509_STORE_CTX_verify_cb X509_STORE_get_verify_cb(const X509_STORE *xs)
1105
0
{
1106
0
    return xs->verify_cb;
1107
0
}
1108
1109
void X509_STORE_set_get_issuer(X509_STORE *xs,
1110
    X509_STORE_CTX_get_issuer_fn get_issuer)
1111
0
{
1112
0
    xs->get_issuer = get_issuer;
1113
0
}
1114
1115
X509_STORE_CTX_get_issuer_fn X509_STORE_get_get_issuer(const X509_STORE *xs)
1116
0
{
1117
0
    return xs->get_issuer;
1118
0
}
1119
1120
void X509_STORE_set_check_issued(X509_STORE *xs,
1121
    X509_STORE_CTX_check_issued_fn check_issued)
1122
0
{
1123
0
    xs->check_issued = check_issued;
1124
0
}
1125
1126
X509_STORE_CTX_check_issued_fn X509_STORE_get_check_issued(const X509_STORE *xs)
1127
0
{
1128
0
    return xs->check_issued;
1129
0
}
1130
1131
void X509_STORE_set_check_revocation(X509_STORE *xs,
1132
    X509_STORE_CTX_check_revocation_fn cb)
1133
0
{
1134
0
    xs->check_revocation = cb;
1135
0
}
1136
1137
X509_STORE_CTX_check_revocation_fn X509_STORE_get_check_revocation(const X509_STORE *xs)
1138
0
{
1139
0
    return xs->check_revocation;
1140
0
}
1141
1142
void X509_STORE_set_get_crl(X509_STORE *xs,
1143
    X509_STORE_CTX_get_crl_fn get_crl)
1144
0
{
1145
0
    xs->get_crl = get_crl;
1146
0
}
1147
1148
X509_STORE_CTX_get_crl_fn X509_STORE_get_get_crl(const X509_STORE *xs)
1149
0
{
1150
0
    return xs->get_crl;
1151
0
}
1152
1153
void X509_STORE_set_check_crl(X509_STORE *xs,
1154
    X509_STORE_CTX_check_crl_fn check_crl)
1155
0
{
1156
0
    xs->check_crl = check_crl;
1157
0
}
1158
1159
X509_STORE_CTX_check_crl_fn X509_STORE_get_check_crl(const X509_STORE *xs)
1160
0
{
1161
0
    return xs->check_crl;
1162
0
}
1163
1164
void X509_STORE_set_cert_crl(X509_STORE *xs,
1165
    X509_STORE_CTX_cert_crl_fn cert_crl)
1166
0
{
1167
0
    xs->cert_crl = cert_crl;
1168
0
}
1169
1170
X509_STORE_CTX_cert_crl_fn X509_STORE_get_cert_crl(const X509_STORE *xs)
1171
0
{
1172
0
    return xs->cert_crl;
1173
0
}
1174
1175
void X509_STORE_set_check_policy(X509_STORE *xs,
1176
    X509_STORE_CTX_check_policy_fn check_policy)
1177
0
{
1178
0
    xs->check_policy = check_policy;
1179
0
}
1180
1181
X509_STORE_CTX_check_policy_fn X509_STORE_get_check_policy(const X509_STORE *xs)
1182
0
{
1183
0
    return xs->check_policy;
1184
0
}
1185
1186
void X509_STORE_set_lookup_certs(X509_STORE *xs,
1187
    X509_STORE_CTX_lookup_certs_fn lookup_certs)
1188
0
{
1189
0
    xs->lookup_certs = lookup_certs;
1190
0
}
1191
1192
X509_STORE_CTX_lookup_certs_fn X509_STORE_get_lookup_certs(const X509_STORE *xs)
1193
0
{
1194
0
    return xs->lookup_certs;
1195
0
}
1196
1197
void X509_STORE_set_lookup_crls(X509_STORE *xs,
1198
    X509_STORE_CTX_lookup_crls_fn lookup_crls)
1199
0
{
1200
0
    xs->lookup_crls = lookup_crls;
1201
0
}
1202
1203
X509_STORE_CTX_lookup_crls_fn X509_STORE_get_lookup_crls(const X509_STORE *xs)
1204
0
{
1205
0
    return xs->lookup_crls;
1206
0
}
1207
1208
void X509_STORE_set_cleanup(X509_STORE *xs,
1209
    X509_STORE_CTX_cleanup_fn cleanup)
1210
0
{
1211
0
    xs->cleanup = cleanup;
1212
0
}
1213
1214
X509_STORE_CTX_cleanup_fn X509_STORE_get_cleanup(const X509_STORE *xs)
1215
0
{
1216
0
    return xs->cleanup;
1217
0
}
1218
1219
int X509_STORE_set_ex_data(X509_STORE *xs, int idx, void *data)
1220
0
{
1221
0
    return CRYPTO_set_ex_data(&xs->ex_data, idx, data);
1222
0
}
1223
1224
void *X509_STORE_get_ex_data(const X509_STORE *xs, int idx)
1225
0
{
1226
0
    return CRYPTO_get_ex_data(&xs->ex_data, idx);
1227
0
}
1228
1229
X509_STORE *X509_STORE_CTX_get0_store(const X509_STORE_CTX *ctx)
1230
0
{
1231
0
    return ctx->store;
1232
0
}