xref: /freebsd/crypto/openssl/crypto/x509/by_store.c (revision 88b8b7f0c4e9948667a2279e78e975a784049cba)
1 /*
2  * Copyright 2018-2025 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 <openssl/safestack.h>
11 #include <openssl/store.h>
12 #include "internal/cryptlib.h"
13 #include "crypto/x509.h"
14 #include "x509_local.h"
15 
16 typedef struct cached_store_st {
17     char *uri;
18     OSSL_LIB_CTX *libctx;
19     char *propq;
20 } CACHED_STORE;
21 
DEFINE_STACK_OF(CACHED_STORE)22 DEFINE_STACK_OF(CACHED_STORE)
23 
24 /* Generic object loader, given expected type and criterion */
25 static int cache_objects(X509_LOOKUP *lctx, CACHED_STORE *store,
26                          const OSSL_STORE_SEARCH *criterion, int depth)
27 {
28     int ok = 0;
29     OSSL_STORE_CTX *ctx;
30     X509_STORE *xstore = X509_LOOKUP_get_store(lctx);
31 
32     if ((ctx = OSSL_STORE_open_ex(store->uri, store->libctx, store->propq,
33                                   NULL, NULL, NULL, NULL, NULL)) == NULL)
34         return 0;
35 
36     /*
37      * We try to set the criterion, but don't care if it was valid or not.
38      * For an OSSL_STORE, it merely serves as an optimization, the expectation
39      * being that if the criterion couldn't be used, we will get *everything*
40      * from the container that the URI represents rather than the subset that
41      * the criterion indicates, so the biggest harm is that we cache more
42      * objects certs and CRLs than we may expect, but that's ok.
43      *
44      * Specifically for OpenSSL's own file: scheme, the only workable
45      * criterion is the BY_NAME one, which it can only apply on directories,
46      * but it's possible that the URI is a single file rather than a directory,
47      * and in that case, the BY_NAME criterion is pointless.
48      *
49      * We could very simply not apply any criterion at all here, and just let
50      * the code that selects certs and CRLs from the cached objects do its job,
51      * but it's a nice optimization when it can be applied (such as on an
52      * actual directory with a thousand CA certs).
53      */
54     if (criterion != NULL)
55         OSSL_STORE_find(ctx, criterion);
56 
57     for (;;) {
58         OSSL_STORE_INFO *info = OSSL_STORE_load(ctx);
59         int infotype;
60 
61         /* NULL means error or "end of file".  Either way, we break. */
62         if (info == NULL)
63             break;
64 
65         infotype = OSSL_STORE_INFO_get_type(info);
66         ok = 0;
67 
68         if (infotype == OSSL_STORE_INFO_NAME) {
69             /*
70              * This is an entry in the "directory" represented by the current
71              * uri.  if |depth| allows, dive into it.
72              */
73             if (depth > 0) {
74                 CACHED_STORE substore;
75 
76                 substore.uri = (char *)OSSL_STORE_INFO_get0_NAME(info);
77                 substore.libctx = store->libctx;
78                 substore.propq = store->propq;
79                 ok = cache_objects(lctx, &substore, criterion, depth - 1);
80             }
81         } else {
82             /*
83              * We know that X509_STORE_add_{cert|crl} increments the object's
84              * refcount, so we can safely use OSSL_STORE_INFO_get0_{cert,crl}
85              * to get them.
86              */
87             switch (infotype) {
88             case OSSL_STORE_INFO_CERT:
89                 ok = X509_STORE_add_cert(xstore,
90                                          OSSL_STORE_INFO_get0_CERT(info));
91                 break;
92             case OSSL_STORE_INFO_CRL:
93                 ok = X509_STORE_add_crl(xstore,
94                                         OSSL_STORE_INFO_get0_CRL(info));
95                 break;
96             }
97         }
98 
99         OSSL_STORE_INFO_free(info);
100         if (!ok)
101             break;
102     }
103     OSSL_STORE_close(ctx);
104 
105     return ok;
106 }
107 
108 
free_store(CACHED_STORE * store)109 static void free_store(CACHED_STORE *store)
110 {
111     if (store != NULL) {
112         OPENSSL_free(store->uri);
113         OPENSSL_free(store->propq);
114         OPENSSL_free(store);
115     }
116 }
117 
by_store_free(X509_LOOKUP * ctx)118 static void by_store_free(X509_LOOKUP *ctx)
119 {
120     STACK_OF(CACHED_STORE) *stores = X509_LOOKUP_get_method_data(ctx);
121     sk_CACHED_STORE_pop_free(stores, free_store);
122 }
123 
by_store_ctrl_ex(X509_LOOKUP * ctx,int cmd,const char * argp,long argl,char ** retp,OSSL_LIB_CTX * libctx,const char * propq)124 static int by_store_ctrl_ex(X509_LOOKUP *ctx, int cmd, const char *argp,
125                             long argl, char **retp, OSSL_LIB_CTX *libctx,
126                             const char *propq)
127 {
128     switch (cmd) {
129     case X509_L_ADD_STORE:
130         if (argp != NULL) {
131             STACK_OF(CACHED_STORE) *stores = X509_LOOKUP_get_method_data(ctx);
132             CACHED_STORE *store = OPENSSL_zalloc(sizeof(*store));
133             OSSL_STORE_CTX *sctx;
134 
135             if (store == NULL) {
136                 return 0;
137             }
138 
139             store->uri = OPENSSL_strdup(argp);
140             store->libctx = libctx;
141             if (propq != NULL)
142                 store->propq = OPENSSL_strdup(propq);
143             /*
144              * We open this to check for errors now - so we can report those
145              * errors early.
146              */
147             sctx = OSSL_STORE_open_ex(argp, libctx, propq, NULL, NULL,
148                                       NULL, NULL, NULL);
149             if (sctx == NULL
150                 || (propq != NULL && store->propq == NULL)
151                 || store->uri == NULL) {
152                 OSSL_STORE_close(sctx);
153                 free_store(store);
154                 return 0;
155             }
156             OSSL_STORE_close(sctx);
157 
158             if (stores == NULL) {
159                 stores = sk_CACHED_STORE_new_null();
160                 if (stores != NULL)
161                     X509_LOOKUP_set_method_data(ctx, stores);
162             }
163             if (stores == NULL || sk_CACHED_STORE_push(stores, store) <= 0) {
164                 free_store(store);
165                 return 0;
166             }
167             return 1;
168         }
169         /* NOP if no URI is given. */
170         return 1;
171     case X509_L_LOAD_STORE: {
172         /* This is a shortcut for quick loading of specific containers */
173         CACHED_STORE store;
174 
175         store.uri = (char *)argp;
176         store.libctx = libctx;
177         store.propq = (char *)propq;
178         return cache_objects(ctx, &store, NULL, 0);
179     }
180     default:
181         /* Unsupported command */
182         return 0;
183     }
184 }
185 
by_store_ctrl(X509_LOOKUP * ctx,int cmd,const char * argp,long argl,char ** retp)186 static int by_store_ctrl(X509_LOOKUP *ctx, int cmd,
187                          const char *argp, long argl, char **retp)
188 {
189     return by_store_ctrl_ex(ctx, cmd, argp, argl, retp, NULL, NULL);
190 }
191 
by_store(X509_LOOKUP * ctx,X509_LOOKUP_TYPE type,const OSSL_STORE_SEARCH * criterion,X509_OBJECT * ret)192 static int by_store(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type,
193                     const OSSL_STORE_SEARCH *criterion, X509_OBJECT *ret)
194 {
195     STACK_OF(CACHED_STORE) *stores = X509_LOOKUP_get_method_data(ctx);
196     int i;
197     int ok = 0;
198 
199     for (i = 0; i < sk_CACHED_STORE_num(stores); i++) {
200         ok = cache_objects(ctx, sk_CACHED_STORE_value(stores, i), criterion,
201                            1 /* depth */);
202 
203         if (ok)
204             break;
205     }
206     return ok;
207 }
208 
by_store_subject(X509_LOOKUP * ctx,X509_LOOKUP_TYPE type,const X509_NAME * name,X509_OBJECT * ret)209 static int by_store_subject(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type,
210                             const X509_NAME *name, X509_OBJECT *ret)
211 {
212     OSSL_STORE_SEARCH *criterion =
213         OSSL_STORE_SEARCH_by_name((X509_NAME *)name); /* won't modify it */
214     int ok = by_store(ctx, type, criterion, ret);
215     STACK_OF(X509_OBJECT) *store_objects =
216         X509_STORE_get0_objects(X509_LOOKUP_get_store(ctx));
217     X509_OBJECT *tmp = NULL;
218 
219     OSSL_STORE_SEARCH_free(criterion);
220 
221     if (ok) {
222         X509_STORE *store = X509_LOOKUP_get_store(ctx);
223 
224         if (!ossl_x509_store_read_lock(store))
225             return 0;
226         tmp = X509_OBJECT_retrieve_by_subject(store_objects, type, name);
227         X509_STORE_unlock(store);
228     }
229 
230     ok = 0;
231     if (tmp != NULL) {
232         /*
233          * This could also be done like this:
234          *
235          *     if (tmp != NULL) {
236          *         *ret = *tmp;
237          *         ok = 1;
238          *     }
239          *
240          * However, we want to exercise the documented API to the max, so
241          * we do it the hard way.
242          *
243          * To be noted is that X509_OBJECT_set1_* increment the refcount,
244          * but so does X509_STORE_CTX_get_by_subject upon return of this
245          * function, so we must ensure the refcount is decremented
246          * before we return, or we will get a refcount leak.  We cannot do
247          * this with X509_OBJECT_free(), though, as that will free a bit
248          * too much.
249          */
250         switch (type) {
251         case X509_LU_X509:
252             ok = X509_OBJECT_set1_X509(ret, tmp->data.x509);
253             if (ok)
254                 X509_free(tmp->data.x509);
255             break;
256         case X509_LU_CRL:
257             ok = X509_OBJECT_set1_X509_CRL(ret, tmp->data.crl);
258             if (ok)
259                 X509_CRL_free(tmp->data.crl);
260             break;
261         case X509_LU_NONE:
262             break;
263         }
264     }
265     return ok;
266 }
267 
268 /*
269  * We lack the implementations for get_by_issuer_serial, get_by_fingerprint
270  * and get_by_alias.  There's simply not enough support in the X509_LOOKUP
271  * or X509_STORE APIs.
272  */
273 
274 static X509_LOOKUP_METHOD x509_store_lookup = {
275     "Load certs from STORE URIs",
276     NULL,                        /* new_item */
277     by_store_free,               /* free */
278     NULL,                        /* init */
279     NULL,                        /* shutdown */
280     by_store_ctrl,               /* ctrl */
281     by_store_subject,            /* get_by_subject */
282     NULL,                        /* get_by_issuer_serial */
283     NULL,                        /* get_by_fingerprint */
284     NULL,                        /* get_by_alias */
285     NULL,                        /* get_by_subject_ex */
286     by_store_ctrl_ex
287 };
288 
X509_LOOKUP_store(void)289 X509_LOOKUP_METHOD *X509_LOOKUP_store(void)
290 {
291     return &x509_store_lookup;
292 }
293