xref: /unit/src/nxt_cert.c (revision 774)
1*774Svbart@nginx.com 
2*774Svbart@nginx.com /*
3*774Svbart@nginx.com  * Copyright (C) Valentin V. Bartenev
4*774Svbart@nginx.com  * Copyright (C) NGINX, Inc.
5*774Svbart@nginx.com  */
6*774Svbart@nginx.com 
7*774Svbart@nginx.com #include <nxt_main.h>
8*774Svbart@nginx.com #include <nxt_conf.h>
9*774Svbart@nginx.com #include <nxt_cert.h>
10*774Svbart@nginx.com 
11*774Svbart@nginx.com #include <dirent.h>
12*774Svbart@nginx.com 
13*774Svbart@nginx.com #include <openssl/bio.h>
14*774Svbart@nginx.com #include <openssl/pem.h>
15*774Svbart@nginx.com #include <openssl/evp.h>
16*774Svbart@nginx.com #include <openssl/x509.h>
17*774Svbart@nginx.com #include <openssl/x509v3.h>
18*774Svbart@nginx.com #include <openssl/rsa.h>
19*774Svbart@nginx.com #include <openssl/err.h>
20*774Svbart@nginx.com 
21*774Svbart@nginx.com 
22*774Svbart@nginx.com struct nxt_cert_s {
23*774Svbart@nginx.com     EVP_PKEY          *key;
24*774Svbart@nginx.com     nxt_uint_t        count;
25*774Svbart@nginx.com     X509              *chain[];
26*774Svbart@nginx.com };
27*774Svbart@nginx.com 
28*774Svbart@nginx.com 
29*774Svbart@nginx.com typedef struct {
30*774Svbart@nginx.com     nxt_str_t         name;
31*774Svbart@nginx.com     nxt_conf_value_t  *value;
32*774Svbart@nginx.com     nxt_mp_t          *mp;
33*774Svbart@nginx.com } nxt_cert_info_t;
34*774Svbart@nginx.com 
35*774Svbart@nginx.com 
36*774Svbart@nginx.com typedef struct {
37*774Svbart@nginx.com     nxt_str_t         name;
38*774Svbart@nginx.com     nxt_fd_t          fd;
39*774Svbart@nginx.com } nxt_cert_item_t;
40*774Svbart@nginx.com 
41*774Svbart@nginx.com 
42*774Svbart@nginx.com static nxt_cert_t *nxt_cert_fd(nxt_task_t *task, nxt_fd_t fd);
43*774Svbart@nginx.com static nxt_cert_t *nxt_cert_bio(nxt_task_t *task, BIO *bio);
44*774Svbart@nginx.com static int nxt_nxt_cert_pem_suffix(char *pem_str, const char *suffix);
45*774Svbart@nginx.com 
46*774Svbart@nginx.com static nxt_conf_value_t *nxt_cert_details(nxt_mp_t *mp, nxt_cert_t *cert);
47*774Svbart@nginx.com static nxt_conf_value_t *nxt_cert_name_details(nxt_mp_t *mp, X509 *x509,
48*774Svbart@nginx.com     nxt_bool_t issuer);
49*774Svbart@nginx.com 
50*774Svbart@nginx.com 
51*774Svbart@nginx.com static nxt_lvlhsh_t  nxt_cert_info;
52*774Svbart@nginx.com 
53*774Svbart@nginx.com 
54*774Svbart@nginx.com nxt_cert_t *
55*774Svbart@nginx.com nxt_cert_mem(nxt_task_t *task, nxt_buf_mem_t *mbuf)
56*774Svbart@nginx.com {
57*774Svbart@nginx.com     BIO         *bio;
58*774Svbart@nginx.com     nxt_cert_t  *cert;
59*774Svbart@nginx.com 
60*774Svbart@nginx.com     bio = BIO_new_mem_buf(mbuf->pos, nxt_buf_mem_used_size(mbuf));
61*774Svbart@nginx.com     if (nxt_slow_path(bio == NULL)) {
62*774Svbart@nginx.com         nxt_openssl_log_error(task, NXT_LOG_ALERT, "BIO_new_mem_buf() failed");
63*774Svbart@nginx.com         return NULL;
64*774Svbart@nginx.com     }
65*774Svbart@nginx.com 
66*774Svbart@nginx.com     cert = nxt_cert_bio(task, bio);
67*774Svbart@nginx.com 
68*774Svbart@nginx.com     BIO_free(bio);
69*774Svbart@nginx.com 
70*774Svbart@nginx.com     return cert;
71*774Svbart@nginx.com }
72*774Svbart@nginx.com 
73*774Svbart@nginx.com 
74*774Svbart@nginx.com static nxt_cert_t *
75*774Svbart@nginx.com nxt_cert_fd(nxt_task_t *task, nxt_fd_t fd)
76*774Svbart@nginx.com {
77*774Svbart@nginx.com     BIO         *bio;
78*774Svbart@nginx.com     nxt_cert_t  *cert;
79*774Svbart@nginx.com 
80*774Svbart@nginx.com     bio = BIO_new_fd(fd, 0);
81*774Svbart@nginx.com     if (nxt_slow_path(bio == NULL)) {
82*774Svbart@nginx.com         nxt_openssl_log_error(task, NXT_LOG_ALERT, "BIO_new_fd() failed");
83*774Svbart@nginx.com         return NULL;
84*774Svbart@nginx.com     }
85*774Svbart@nginx.com 
86*774Svbart@nginx.com     cert = nxt_cert_bio(task, bio);
87*774Svbart@nginx.com 
88*774Svbart@nginx.com     BIO_free(bio);
89*774Svbart@nginx.com 
90*774Svbart@nginx.com     return cert;
91*774Svbart@nginx.com }
92*774Svbart@nginx.com 
93*774Svbart@nginx.com 
94*774Svbart@nginx.com static nxt_cert_t *
95*774Svbart@nginx.com nxt_cert_bio(nxt_task_t *task, BIO *bio)
96*774Svbart@nginx.com {
97*774Svbart@nginx.com     int                         ret, suffix, key_id;
98*774Svbart@nginx.com     long                        length, reason;
99*774Svbart@nginx.com     char                        *type, *header;
100*774Svbart@nginx.com     X509                        *x509;
101*774Svbart@nginx.com     EVP_PKEY                    *key;
102*774Svbart@nginx.com     nxt_uint_t                  nalloc;
103*774Svbart@nginx.com     nxt_cert_t                  *cert, *new_cert;
104*774Svbart@nginx.com     u_char                      *data;
105*774Svbart@nginx.com     const u_char                *data_copy;
106*774Svbart@nginx.com     PKCS8_PRIV_KEY_INFO         *p8inf;
107*774Svbart@nginx.com     const EVP_PKEY_ASN1_METHOD  *ameth;
108*774Svbart@nginx.com 
109*774Svbart@nginx.com     nalloc = 4;
110*774Svbart@nginx.com 
111*774Svbart@nginx.com     cert = nxt_zalloc(sizeof(nxt_cert_t) + nalloc * sizeof(X509 *));
112*774Svbart@nginx.com     if (cert == NULL) {
113*774Svbart@nginx.com         return NULL;
114*774Svbart@nginx.com     }
115*774Svbart@nginx.com 
116*774Svbart@nginx.com     for ( ;; ) {
117*774Svbart@nginx.com         ret = PEM_read_bio(bio, &type, &header, &data, &length);
118*774Svbart@nginx.com 
119*774Svbart@nginx.com         if (ret == 0) {
120*774Svbart@nginx.com             reason = ERR_GET_REASON(ERR_peek_last_error());
121*774Svbart@nginx.com             if (reason != PEM_R_NO_START_LINE) {
122*774Svbart@nginx.com                 nxt_openssl_log_error(task, NXT_LOG_ALERT,
123*774Svbart@nginx.com                                       "PEM_read_bio() failed");
124*774Svbart@nginx.com                 goto fail;
125*774Svbart@nginx.com             }
126*774Svbart@nginx.com 
127*774Svbart@nginx.com             ERR_clear_error();
128*774Svbart@nginx.com             break;
129*774Svbart@nginx.com         }
130*774Svbart@nginx.com 
131*774Svbart@nginx.com         nxt_debug(task, "PEM type: \"%s\"", type);
132*774Svbart@nginx.com 
133*774Svbart@nginx.com         key = NULL;
134*774Svbart@nginx.com         x509 = NULL;
135*774Svbart@nginx.com /*
136*774Svbart@nginx.com         EVP_CIPHER_INFO  cipher;
137*774Svbart@nginx.com 
138*774Svbart@nginx.com         if (PEM_get_EVP_CIPHER_INFO(header, &cipher) != 0) {
139*774Svbart@nginx.com             nxt_alert(task, "encrypted PEM isn't supported");
140*774Svbart@nginx.com             goto done;
141*774Svbart@nginx.com         }
142*774Svbart@nginx.com */
143*774Svbart@nginx.com         if (nxt_strcmp(type, PEM_STRING_PKCS8) == 0) {
144*774Svbart@nginx.com             nxt_alert(task, "PEM PKCS8 isn't supported");
145*774Svbart@nginx.com             goto done;
146*774Svbart@nginx.com         }
147*774Svbart@nginx.com 
148*774Svbart@nginx.com         if (nxt_strcmp(type, PEM_STRING_PKCS8INF) == 0) {
149*774Svbart@nginx.com             data_copy = data;
150*774Svbart@nginx.com 
151*774Svbart@nginx.com             p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &data_copy, length);
152*774Svbart@nginx.com 
153*774Svbart@nginx.com             if (p8inf == NULL) {
154*774Svbart@nginx.com                 nxt_openssl_log_error(task, NXT_LOG_ALERT,
155*774Svbart@nginx.com                                       "d2i_PKCS8_PRIV_KEY_INFO() failed");
156*774Svbart@nginx.com                 goto done;
157*774Svbart@nginx.com             }
158*774Svbart@nginx.com 
159*774Svbart@nginx.com             key = EVP_PKCS82PKEY(p8inf);
160*774Svbart@nginx.com 
161*774Svbart@nginx.com             PKCS8_PRIV_KEY_INFO_free(p8inf);
162*774Svbart@nginx.com             goto done;
163*774Svbart@nginx.com         }
164*774Svbart@nginx.com 
165*774Svbart@nginx.com         suffix = nxt_nxt_cert_pem_suffix(type, PEM_STRING_PKCS8INF);
166*774Svbart@nginx.com 
167*774Svbart@nginx.com         if (suffix != 0) {
168*774Svbart@nginx.com 
169*774Svbart@nginx.com             ameth = EVP_PKEY_asn1_find_str(NULL, type, suffix);
170*774Svbart@nginx.com             if (ameth == NULL) {
171*774Svbart@nginx.com                 nxt_openssl_log_error(task, NXT_LOG_ALERT,
172*774Svbart@nginx.com                                       "EVP_PKEY_asn1_find_str() failed");
173*774Svbart@nginx.com                 goto done;
174*774Svbart@nginx.com             }
175*774Svbart@nginx.com 
176*774Svbart@nginx.com             EVP_PKEY_asn1_get0_info(&key_id, NULL, NULL, NULL, NULL, ameth);
177*774Svbart@nginx.com 
178*774Svbart@nginx.com             data_copy = data;
179*774Svbart@nginx.com 
180*774Svbart@nginx.com             key = d2i_PrivateKey(key_id, NULL, &data_copy, length);
181*774Svbart@nginx.com             goto done;
182*774Svbart@nginx.com         }
183*774Svbart@nginx.com 
184*774Svbart@nginx.com         if (nxt_strcmp(type, PEM_STRING_X509) == 0
185*774Svbart@nginx.com             || nxt_strcmp(type, PEM_STRING_X509_OLD) == 0)
186*774Svbart@nginx.com         {
187*774Svbart@nginx.com             data_copy = data;
188*774Svbart@nginx.com 
189*774Svbart@nginx.com             x509 = d2i_X509(NULL, &data_copy, length);
190*774Svbart@nginx.com             if (x509 == NULL) {
191*774Svbart@nginx.com                 nxt_openssl_log_error(task, NXT_LOG_ALERT,
192*774Svbart@nginx.com                                       "d2i_X509() failed");
193*774Svbart@nginx.com             }
194*774Svbart@nginx.com 
195*774Svbart@nginx.com             goto done;
196*774Svbart@nginx.com         }
197*774Svbart@nginx.com 
198*774Svbart@nginx.com         if (nxt_strcmp(type, PEM_STRING_X509_TRUSTED) == 0) {
199*774Svbart@nginx.com             data_copy = data;
200*774Svbart@nginx.com 
201*774Svbart@nginx.com             x509 = d2i_X509_AUX(NULL, &data_copy, length);
202*774Svbart@nginx.com             if (x509 == NULL) {
203*774Svbart@nginx.com                 nxt_openssl_log_error(task, NXT_LOG_ALERT,
204*774Svbart@nginx.com                                       "d2i_X509_AUX() failed");
205*774Svbart@nginx.com             }
206*774Svbart@nginx.com 
207*774Svbart@nginx.com             goto done;
208*774Svbart@nginx.com         }
209*774Svbart@nginx.com 
210*774Svbart@nginx.com         nxt_alert(task, "unsupported PEM type: \"%s\"", type);
211*774Svbart@nginx.com 
212*774Svbart@nginx.com     done:
213*774Svbart@nginx.com 
214*774Svbart@nginx.com         OPENSSL_free(data);
215*774Svbart@nginx.com         OPENSSL_free(header);
216*774Svbart@nginx.com         OPENSSL_free(type);
217*774Svbart@nginx.com 
218*774Svbart@nginx.com         if (key != NULL) {
219*774Svbart@nginx.com             if (cert->key != NULL) {
220*774Svbart@nginx.com                 EVP_PKEY_free(key);
221*774Svbart@nginx.com                 nxt_alert(task, "multiple private keys in PEM");
222*774Svbart@nginx.com                 goto fail;
223*774Svbart@nginx.com             }
224*774Svbart@nginx.com 
225*774Svbart@nginx.com             cert->key = key;
226*774Svbart@nginx.com             continue;
227*774Svbart@nginx.com         }
228*774Svbart@nginx.com 
229*774Svbart@nginx.com         if (x509 != NULL) {
230*774Svbart@nginx.com 
231*774Svbart@nginx.com             if (cert->count == nalloc) {
232*774Svbart@nginx.com                 nalloc += 4;
233*774Svbart@nginx.com 
234*774Svbart@nginx.com                 new_cert = nxt_realloc(cert, sizeof(nxt_cert_t)
235*774Svbart@nginx.com                                              + nalloc * sizeof(X509 *));
236*774Svbart@nginx.com                 if (new_cert == NULL) {
237*774Svbart@nginx.com                     X509_free(x509);
238*774Svbart@nginx.com                     goto fail;
239*774Svbart@nginx.com                 }
240*774Svbart@nginx.com 
241*774Svbart@nginx.com                 nxt_free(cert);
242*774Svbart@nginx.com                 cert = new_cert;
243*774Svbart@nginx.com             }
244*774Svbart@nginx.com 
245*774Svbart@nginx.com             cert->chain[cert->count++] = x509;
246*774Svbart@nginx.com             continue;
247*774Svbart@nginx.com         }
248*774Svbart@nginx.com 
249*774Svbart@nginx.com         goto fail;
250*774Svbart@nginx.com     }
251*774Svbart@nginx.com 
252*774Svbart@nginx.com     if (cert->key == NULL) {
253*774Svbart@nginx.com         nxt_alert(task, "no key found");
254*774Svbart@nginx.com         goto fail;
255*774Svbart@nginx.com     }
256*774Svbart@nginx.com 
257*774Svbart@nginx.com     if (cert->count == 0) {
258*774Svbart@nginx.com         nxt_alert(task, "no certificates found");
259*774Svbart@nginx.com         goto fail;
260*774Svbart@nginx.com     }
261*774Svbart@nginx.com 
262*774Svbart@nginx.com     return cert;
263*774Svbart@nginx.com 
264*774Svbart@nginx.com fail:
265*774Svbart@nginx.com 
266*774Svbart@nginx.com     nxt_cert_destroy(cert);
267*774Svbart@nginx.com 
268*774Svbart@nginx.com     return NULL;
269*774Svbart@nginx.com }
270*774Svbart@nginx.com 
271*774Svbart@nginx.com 
272*774Svbart@nginx.com static int
273*774Svbart@nginx.com nxt_nxt_cert_pem_suffix(char *pem_str, const char *suffix)
274*774Svbart@nginx.com {
275*774Svbart@nginx.com     char        *p;
276*774Svbart@nginx.com     nxt_uint_t  pem_len, suffix_len;
277*774Svbart@nginx.com 
278*774Svbart@nginx.com     pem_len = strlen(pem_str);
279*774Svbart@nginx.com     suffix_len = strlen(suffix);
280*774Svbart@nginx.com 
281*774Svbart@nginx.com     if (suffix_len + 1 >= pem_len) {
282*774Svbart@nginx.com         return 0;
283*774Svbart@nginx.com     }
284*774Svbart@nginx.com 
285*774Svbart@nginx.com     p = pem_str + pem_len - suffix_len;
286*774Svbart@nginx.com 
287*774Svbart@nginx.com     if (nxt_strcmp(p, suffix) != 0) {
288*774Svbart@nginx.com         return 0;
289*774Svbart@nginx.com     }
290*774Svbart@nginx.com 
291*774Svbart@nginx.com     p--;
292*774Svbart@nginx.com 
293*774Svbart@nginx.com     if (*p != ' ') {
294*774Svbart@nginx.com         return 0;
295*774Svbart@nginx.com     }
296*774Svbart@nginx.com 
297*774Svbart@nginx.com     return p - pem_str;
298*774Svbart@nginx.com }
299*774Svbart@nginx.com 
300*774Svbart@nginx.com 
301*774Svbart@nginx.com void
302*774Svbart@nginx.com nxt_cert_destroy(nxt_cert_t *cert)
303*774Svbart@nginx.com {
304*774Svbart@nginx.com     nxt_uint_t  i;
305*774Svbart@nginx.com 
306*774Svbart@nginx.com     EVP_PKEY_free(cert->key);
307*774Svbart@nginx.com 
308*774Svbart@nginx.com     for (i = 0; i != cert->count; i++) {
309*774Svbart@nginx.com         X509_free(cert->chain[i]);
310*774Svbart@nginx.com     }
311*774Svbart@nginx.com 
312*774Svbart@nginx.com     nxt_free(cert);
313*774Svbart@nginx.com }
314*774Svbart@nginx.com 
315*774Svbart@nginx.com 
316*774Svbart@nginx.com 
317*774Svbart@nginx.com static nxt_int_t
318*774Svbart@nginx.com nxt_cert_info_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
319*774Svbart@nginx.com {
320*774Svbart@nginx.com     nxt_cert_info_t  *info;
321*774Svbart@nginx.com 
322*774Svbart@nginx.com     info = data;
323*774Svbart@nginx.com 
324*774Svbart@nginx.com     if (nxt_strcasestr_eq(&lhq->key, &info->name)) {
325*774Svbart@nginx.com         return NXT_OK;
326*774Svbart@nginx.com     }
327*774Svbart@nginx.com 
328*774Svbart@nginx.com     return NXT_DECLINED;
329*774Svbart@nginx.com }
330*774Svbart@nginx.com 
331*774Svbart@nginx.com 
332*774Svbart@nginx.com static const nxt_lvlhsh_proto_t  nxt_cert_info_hash_proto
333*774Svbart@nginx.com     nxt_aligned(64) =
334*774Svbart@nginx.com {
335*774Svbart@nginx.com     NXT_LVLHSH_DEFAULT,
336*774Svbart@nginx.com     nxt_cert_info_hash_test,
337*774Svbart@nginx.com     nxt_lvlhsh_alloc,
338*774Svbart@nginx.com     nxt_lvlhsh_free,
339*774Svbart@nginx.com };
340*774Svbart@nginx.com 
341*774Svbart@nginx.com 
342*774Svbart@nginx.com void
343*774Svbart@nginx.com nxt_cert_info_init(nxt_task_t *task, nxt_array_t *certs)
344*774Svbart@nginx.com {
345*774Svbart@nginx.com     uint32_t         i;
346*774Svbart@nginx.com     nxt_cert_t       *cert;
347*774Svbart@nginx.com     nxt_cert_item_t  *items;
348*774Svbart@nginx.com 
349*774Svbart@nginx.com     for (items = certs->elts, i = 0; i < certs->nelts; i++) {
350*774Svbart@nginx.com         cert = nxt_cert_fd(task, items[i].fd);
351*774Svbart@nginx.com 
352*774Svbart@nginx.com         if (nxt_slow_path(cert == NULL)) {
353*774Svbart@nginx.com             continue;
354*774Svbart@nginx.com         }
355*774Svbart@nginx.com 
356*774Svbart@nginx.com         (void) nxt_cert_info_save(&items[i].name, cert);
357*774Svbart@nginx.com 
358*774Svbart@nginx.com         nxt_cert_destroy(cert);
359*774Svbart@nginx.com     }
360*774Svbart@nginx.com }
361*774Svbart@nginx.com 
362*774Svbart@nginx.com 
363*774Svbart@nginx.com nxt_int_t
364*774Svbart@nginx.com nxt_cert_info_save(nxt_str_t *name, nxt_cert_t *cert)
365*774Svbart@nginx.com {
366*774Svbart@nginx.com     nxt_mp_t            *mp;
367*774Svbart@nginx.com     nxt_int_t           ret;
368*774Svbart@nginx.com     nxt_cert_info_t     *info;
369*774Svbart@nginx.com     nxt_conf_value_t    *value;
370*774Svbart@nginx.com     nxt_lvlhsh_query_t  lhq;
371*774Svbart@nginx.com 
372*774Svbart@nginx.com     mp = nxt_mp_create(1024, 128, 256, 32);
373*774Svbart@nginx.com     if (nxt_slow_path(mp == NULL)) {
374*774Svbart@nginx.com         return NXT_ERROR;
375*774Svbart@nginx.com     }
376*774Svbart@nginx.com 
377*774Svbart@nginx.com     info = nxt_mp_get(mp, sizeof(nxt_cert_info_t));
378*774Svbart@nginx.com     if (nxt_slow_path(info == NULL)) {
379*774Svbart@nginx.com         goto fail;
380*774Svbart@nginx.com     }
381*774Svbart@nginx.com 
382*774Svbart@nginx.com     name = nxt_str_dup(mp, &info->name, name);
383*774Svbart@nginx.com     if (nxt_slow_path(name == NULL)) {
384*774Svbart@nginx.com         goto fail;
385*774Svbart@nginx.com     }
386*774Svbart@nginx.com 
387*774Svbart@nginx.com     value = nxt_cert_details(mp, cert);
388*774Svbart@nginx.com     if (nxt_slow_path(value == NULL)) {
389*774Svbart@nginx.com         goto fail;
390*774Svbart@nginx.com     }
391*774Svbart@nginx.com 
392*774Svbart@nginx.com     info->mp = mp;
393*774Svbart@nginx.com     info->value = value;
394*774Svbart@nginx.com 
395*774Svbart@nginx.com     lhq.key_hash = nxt_djb_hash(name->start, name->length);
396*774Svbart@nginx.com     lhq.replace = 1;
397*774Svbart@nginx.com     lhq.key = *name;
398*774Svbart@nginx.com     lhq.value = info;
399*774Svbart@nginx.com     lhq.proto = &nxt_cert_info_hash_proto;
400*774Svbart@nginx.com 
401*774Svbart@nginx.com     ret = nxt_lvlhsh_insert(&nxt_cert_info, &lhq);
402*774Svbart@nginx.com     if (nxt_slow_path(ret != NXT_OK)) {
403*774Svbart@nginx.com         goto fail;
404*774Svbart@nginx.com     }
405*774Svbart@nginx.com 
406*774Svbart@nginx.com     if (lhq.value != info) {
407*774Svbart@nginx.com         info = lhq.value;
408*774Svbart@nginx.com         nxt_mp_destroy(info->mp);
409*774Svbart@nginx.com     }
410*774Svbart@nginx.com 
411*774Svbart@nginx.com     return NXT_OK;
412*774Svbart@nginx.com 
413*774Svbart@nginx.com fail:
414*774Svbart@nginx.com 
415*774Svbart@nginx.com     nxt_mp_destroy(mp);
416*774Svbart@nginx.com     return NXT_ERROR;
417*774Svbart@nginx.com }
418*774Svbart@nginx.com 
419*774Svbart@nginx.com 
420*774Svbart@nginx.com nxt_conf_value_t *
421*774Svbart@nginx.com nxt_cert_info_get(nxt_str_t *name)
422*774Svbart@nginx.com {
423*774Svbart@nginx.com     nxt_int_t           ret;
424*774Svbart@nginx.com     nxt_cert_info_t     *info;
425*774Svbart@nginx.com     nxt_lvlhsh_query_t  lhq;
426*774Svbart@nginx.com 
427*774Svbart@nginx.com     lhq.key_hash = nxt_djb_hash(name->start, name->length);
428*774Svbart@nginx.com     lhq.key = *name;
429*774Svbart@nginx.com     lhq.proto = &nxt_cert_info_hash_proto;
430*774Svbart@nginx.com 
431*774Svbart@nginx.com     ret = nxt_lvlhsh_find(&nxt_cert_info, &lhq);
432*774Svbart@nginx.com     if (ret != NXT_OK) {
433*774Svbart@nginx.com         return NULL;
434*774Svbart@nginx.com     }
435*774Svbart@nginx.com 
436*774Svbart@nginx.com     info = lhq.value;
437*774Svbart@nginx.com 
438*774Svbart@nginx.com     return info->value;
439*774Svbart@nginx.com }
440*774Svbart@nginx.com 
441*774Svbart@nginx.com 
442*774Svbart@nginx.com nxt_conf_value_t *
443*774Svbart@nginx.com nxt_cert_info_get_all(nxt_mp_t *mp)
444*774Svbart@nginx.com {
445*774Svbart@nginx.com     uint32_t           i;
446*774Svbart@nginx.com     nxt_cert_info_t    *info;
447*774Svbart@nginx.com     nxt_conf_value_t   *all;
448*774Svbart@nginx.com     nxt_lvlhsh_each_t  lhe;
449*774Svbart@nginx.com 
450*774Svbart@nginx.com     nxt_lvlhsh_each_init(&lhe, &nxt_cert_info_hash_proto);
451*774Svbart@nginx.com 
452*774Svbart@nginx.com     i = 0;
453*774Svbart@nginx.com 
454*774Svbart@nginx.com     for ( ;; ) {
455*774Svbart@nginx.com         info = nxt_lvlhsh_each(&nxt_cert_info, &lhe);
456*774Svbart@nginx.com 
457*774Svbart@nginx.com         if (info == NULL) {
458*774Svbart@nginx.com             break;
459*774Svbart@nginx.com         }
460*774Svbart@nginx.com 
461*774Svbart@nginx.com         i++;
462*774Svbart@nginx.com     }
463*774Svbart@nginx.com 
464*774Svbart@nginx.com     all = nxt_conf_create_object(mp, i);
465*774Svbart@nginx.com     if (nxt_slow_path(all == NULL)) {
466*774Svbart@nginx.com         return NULL;
467*774Svbart@nginx.com     }
468*774Svbart@nginx.com 
469*774Svbart@nginx.com     nxt_lvlhsh_each_init(&lhe, &nxt_cert_info_hash_proto);
470*774Svbart@nginx.com 
471*774Svbart@nginx.com     i = 0;
472*774Svbart@nginx.com 
473*774Svbart@nginx.com     for ( ;; ) {
474*774Svbart@nginx.com         info = nxt_lvlhsh_each(&nxt_cert_info, &lhe);
475*774Svbart@nginx.com 
476*774Svbart@nginx.com         if (info == NULL) {
477*774Svbart@nginx.com             break;
478*774Svbart@nginx.com         }
479*774Svbart@nginx.com 
480*774Svbart@nginx.com         nxt_conf_set_member(all, &info->name, info->value, i);
481*774Svbart@nginx.com 
482*774Svbart@nginx.com         i++;
483*774Svbart@nginx.com     }
484*774Svbart@nginx.com 
485*774Svbart@nginx.com     return all;
486*774Svbart@nginx.com }
487*774Svbart@nginx.com 
488*774Svbart@nginx.com 
489*774Svbart@nginx.com static nxt_conf_value_t *
490*774Svbart@nginx.com nxt_cert_details(nxt_mp_t *mp, nxt_cert_t *cert)
491*774Svbart@nginx.com {
492*774Svbart@nginx.com     BIO               *bio;
493*774Svbart@nginx.com     X509              *x509;
494*774Svbart@nginx.com     u_char            *end;
495*774Svbart@nginx.com     EVP_PKEY          *key;
496*774Svbart@nginx.com     ASN1_TIME         *asn1_time;
497*774Svbart@nginx.com     nxt_str_t         str;
498*774Svbart@nginx.com     nxt_int_t         ret;
499*774Svbart@nginx.com     nxt_uint_t        i;
500*774Svbart@nginx.com     nxt_conf_value_t  *object, *chain, *element, *value;
501*774Svbart@nginx.com     u_char            buf[256];
502*774Svbart@nginx.com 
503*774Svbart@nginx.com     static nxt_str_t key_str = nxt_string("key");
504*774Svbart@nginx.com     static nxt_str_t chain_str = nxt_string("chain");
505*774Svbart@nginx.com     static nxt_str_t since_str = nxt_string("since");
506*774Svbart@nginx.com     static nxt_str_t until_str = nxt_string("until");
507*774Svbart@nginx.com     static nxt_str_t issuer_str = nxt_string("issuer");
508*774Svbart@nginx.com     static nxt_str_t subject_str = nxt_string("subject");
509*774Svbart@nginx.com     static nxt_str_t validity_str = nxt_string("validity");
510*774Svbart@nginx.com 
511*774Svbart@nginx.com     object = nxt_conf_create_object(mp, 2);
512*774Svbart@nginx.com     if (nxt_slow_path(object == NULL)) {
513*774Svbart@nginx.com         return NULL;
514*774Svbart@nginx.com     }
515*774Svbart@nginx.com 
516*774Svbart@nginx.com     if (cert->key != NULL) {
517*774Svbart@nginx.com         key = cert->key;
518*774Svbart@nginx.com 
519*774Svbart@nginx.com         switch (EVP_PKEY_base_id(key)) {
520*774Svbart@nginx.com         case EVP_PKEY_RSA:
521*774Svbart@nginx.com             end = nxt_sprintf(buf, buf + sizeof(buf), "RSA (%d bits)",
522*774Svbart@nginx.com                               EVP_PKEY_bits(key));
523*774Svbart@nginx.com 
524*774Svbart@nginx.com             str.length = end - buf;
525*774Svbart@nginx.com             str.start = buf;
526*774Svbart@nginx.com             break;
527*774Svbart@nginx.com 
528*774Svbart@nginx.com         case EVP_PKEY_DH:
529*774Svbart@nginx.com             end = nxt_sprintf(buf, buf + sizeof(buf), "DH (%d bits)",
530*774Svbart@nginx.com                               EVP_PKEY_bits(key));
531*774Svbart@nginx.com 
532*774Svbart@nginx.com             str.length = end - buf;
533*774Svbart@nginx.com             str.start = buf;
534*774Svbart@nginx.com             break;
535*774Svbart@nginx.com 
536*774Svbart@nginx.com         case EVP_PKEY_EC:
537*774Svbart@nginx.com             nxt_str_set(&str, "ECDH");
538*774Svbart@nginx.com             break;
539*774Svbart@nginx.com 
540*774Svbart@nginx.com         default:
541*774Svbart@nginx.com             nxt_str_set(&str, "unknown");
542*774Svbart@nginx.com         }
543*774Svbart@nginx.com 
544*774Svbart@nginx.com         ret = nxt_conf_set_member_string_dup(object, mp, &key_str, &str, 0);
545*774Svbart@nginx.com 
546*774Svbart@nginx.com         if (nxt_slow_path(ret != NXT_OK)) {
547*774Svbart@nginx.com             return NULL;
548*774Svbart@nginx.com         }
549*774Svbart@nginx.com 
550*774Svbart@nginx.com     } else {
551*774Svbart@nginx.com         nxt_conf_set_member_null(object, &key_str, 0);
552*774Svbart@nginx.com     }
553*774Svbart@nginx.com 
554*774Svbart@nginx.com     chain = nxt_conf_create_array(mp, cert->count);
555*774Svbart@nginx.com     if (nxt_slow_path(chain == NULL)) {
556*774Svbart@nginx.com         return NULL;
557*774Svbart@nginx.com     }
558*774Svbart@nginx.com 
559*774Svbart@nginx.com     for (i = 0; i < cert->count; i++) {
560*774Svbart@nginx.com         element = nxt_conf_create_object(mp, 3);
561*774Svbart@nginx.com         if (nxt_slow_path(element == NULL)) {
562*774Svbart@nginx.com             return NULL;
563*774Svbart@nginx.com         }
564*774Svbart@nginx.com 
565*774Svbart@nginx.com         x509 = cert->chain[i];
566*774Svbart@nginx.com 
567*774Svbart@nginx.com         value = nxt_cert_name_details(mp, x509, 0);
568*774Svbart@nginx.com         if (value == NULL) {
569*774Svbart@nginx.com             return NULL;
570*774Svbart@nginx.com         }
571*774Svbart@nginx.com 
572*774Svbart@nginx.com         nxt_conf_set_member(element, &subject_str, value, 0);
573*774Svbart@nginx.com 
574*774Svbart@nginx.com         value = nxt_cert_name_details(mp, x509, 1);
575*774Svbart@nginx.com         if (value == NULL) {
576*774Svbart@nginx.com             return NULL;
577*774Svbart@nginx.com         }
578*774Svbart@nginx.com 
579*774Svbart@nginx.com         nxt_conf_set_member(element, &issuer_str, value, 1);
580*774Svbart@nginx.com 
581*774Svbart@nginx.com         value = nxt_conf_create_object(mp, 2);
582*774Svbart@nginx.com         if (nxt_slow_path(value == NULL)) {
583*774Svbart@nginx.com             return NULL;
584*774Svbart@nginx.com         }
585*774Svbart@nginx.com 
586*774Svbart@nginx.com         bio = BIO_new(BIO_s_mem());
587*774Svbart@nginx.com         if (nxt_slow_path(bio == NULL)) {
588*774Svbart@nginx.com             return NULL;
589*774Svbart@nginx.com         }
590*774Svbart@nginx.com 
591*774Svbart@nginx.com         asn1_time = X509_get_notBefore(x509);
592*774Svbart@nginx.com 
593*774Svbart@nginx.com         ret = ASN1_TIME_print(bio, asn1_time);
594*774Svbart@nginx.com 
595*774Svbart@nginx.com         if (nxt_fast_path(ret == 1)) {
596*774Svbart@nginx.com             str.length = BIO_get_mem_data(bio, &str.start);
597*774Svbart@nginx.com             ret = nxt_conf_set_member_string_dup(value, mp, &since_str, &str,
598*774Svbart@nginx.com                                                  0);
599*774Svbart@nginx.com         } else {
600*774Svbart@nginx.com             ret = NXT_ERROR;
601*774Svbart@nginx.com         }
602*774Svbart@nginx.com 
603*774Svbart@nginx.com         BIO_free(bio);
604*774Svbart@nginx.com 
605*774Svbart@nginx.com         if (nxt_slow_path(ret != NXT_OK)) {
606*774Svbart@nginx.com             return NULL;
607*774Svbart@nginx.com         }
608*774Svbart@nginx.com 
609*774Svbart@nginx.com         bio = BIO_new(BIO_s_mem());
610*774Svbart@nginx.com         if (nxt_slow_path(bio == NULL)) {
611*774Svbart@nginx.com             return NULL;
612*774Svbart@nginx.com         }
613*774Svbart@nginx.com 
614*774Svbart@nginx.com         asn1_time = X509_get_notAfter(x509);
615*774Svbart@nginx.com 
616*774Svbart@nginx.com         ret = ASN1_TIME_print(bio, asn1_time);
617*774Svbart@nginx.com 
618*774Svbart@nginx.com         if (nxt_fast_path(ret == 1)) {
619*774Svbart@nginx.com             str.length = BIO_get_mem_data(bio, &str.start);
620*774Svbart@nginx.com             ret = nxt_conf_set_member_string_dup(value, mp, &until_str, &str,
621*774Svbart@nginx.com                                                  1);
622*774Svbart@nginx.com         } else {
623*774Svbart@nginx.com             ret = NXT_ERROR;
624*774Svbart@nginx.com         }
625*774Svbart@nginx.com 
626*774Svbart@nginx.com         BIO_free(bio);
627*774Svbart@nginx.com 
628*774Svbart@nginx.com         if (nxt_slow_path(ret != NXT_OK)) {
629*774Svbart@nginx.com             return NULL;
630*774Svbart@nginx.com         }
631*774Svbart@nginx.com 
632*774Svbart@nginx.com         nxt_conf_set_member(element, &validity_str, value, 2);
633*774Svbart@nginx.com 
634*774Svbart@nginx.com         nxt_conf_set_element(chain, i, element);
635*774Svbart@nginx.com     }
636*774Svbart@nginx.com 
637*774Svbart@nginx.com     nxt_conf_set_member(object, &chain_str, chain, 1);
638*774Svbart@nginx.com 
639*774Svbart@nginx.com     return object;
640*774Svbart@nginx.com }
641*774Svbart@nginx.com 
642*774Svbart@nginx.com 
643*774Svbart@nginx.com typedef struct {
644*774Svbart@nginx.com     int        nid;
645*774Svbart@nginx.com     nxt_str_t  name;
646*774Svbart@nginx.com } nxt_cert_nid_t;
647*774Svbart@nginx.com 
648*774Svbart@nginx.com 
649*774Svbart@nginx.com static nxt_conf_value_t *
650*774Svbart@nginx.com nxt_cert_name_details(nxt_mp_t *mp, X509 *x509, nxt_bool_t issuer)
651*774Svbart@nginx.com {
652*774Svbart@nginx.com     int                     len;
653*774Svbart@nginx.com     X509_NAME               *x509_name;
654*774Svbart@nginx.com     nxt_str_t               str;
655*774Svbart@nginx.com     nxt_int_t               ret;
656*774Svbart@nginx.com     nxt_uint_t              i, n, count;
657*774Svbart@nginx.com     GENERAL_NAME            *name;
658*774Svbart@nginx.com     nxt_conf_value_t        *object, *names;
659*774Svbart@nginx.com     STACK_OF(GENERAL_NAME)  *alt_names;
660*774Svbart@nginx.com     u_char                  buf[256];
661*774Svbart@nginx.com 
662*774Svbart@nginx.com     static nxt_cert_nid_t  nids[] = {
663*774Svbart@nginx.com         { NID_commonName, nxt_string("common_name") },
664*774Svbart@nginx.com         { NID_countryName, nxt_string("country") },
665*774Svbart@nginx.com         { NID_stateOrProvinceName, nxt_string("state_or_province") },
666*774Svbart@nginx.com         { NID_localityName, nxt_string("locality") },
667*774Svbart@nginx.com         { NID_organizationName, nxt_string("organization") },
668*774Svbart@nginx.com         { NID_organizationalUnitName, nxt_string("department") },
669*774Svbart@nginx.com     };
670*774Svbart@nginx.com 
671*774Svbart@nginx.com     static nxt_str_t alt_names_str = nxt_string("alt_names");
672*774Svbart@nginx.com 
673*774Svbart@nginx.com     count = 0;
674*774Svbart@nginx.com 
675*774Svbart@nginx.com     x509_name = issuer ? X509_get_issuer_name(x509)
676*774Svbart@nginx.com                        : X509_get_subject_name(x509);
677*774Svbart@nginx.com 
678*774Svbart@nginx.com     for (n = 0; n != nxt_nitems(nids); n++) {
679*774Svbart@nginx.com 
680*774Svbart@nginx.com         if (X509_NAME_get_index_by_NID(x509_name, nids[n].nid, -1) < 0) {
681*774Svbart@nginx.com             continue;
682*774Svbart@nginx.com         }
683*774Svbart@nginx.com 
684*774Svbart@nginx.com         count++;
685*774Svbart@nginx.com     }
686*774Svbart@nginx.com 
687*774Svbart@nginx.com     alt_names = X509_get_ext_d2i(x509, issuer ? NID_issuer_alt_name
688*774Svbart@nginx.com                                               : NID_subject_alt_name,
689*774Svbart@nginx.com                                  NULL, NULL);
690*774Svbart@nginx.com 
691*774Svbart@nginx.com     if (alt_names != NULL) {
692*774Svbart@nginx.com         count++;
693*774Svbart@nginx.com     }
694*774Svbart@nginx.com 
695*774Svbart@nginx.com     object = nxt_conf_create_object(mp, count);
696*774Svbart@nginx.com     if (nxt_slow_path(object == NULL)) {
697*774Svbart@nginx.com         goto fail;
698*774Svbart@nginx.com     }
699*774Svbart@nginx.com 
700*774Svbart@nginx.com     for (n = 0, i = 0; n != nxt_nitems(nids) && i != count; n++) {
701*774Svbart@nginx.com 
702*774Svbart@nginx.com         len = X509_NAME_get_text_by_NID(x509_name, nids[n].nid,
703*774Svbart@nginx.com                                         (char *) buf, sizeof(buf));
704*774Svbart@nginx.com 
705*774Svbart@nginx.com         if (len < 0) {
706*774Svbart@nginx.com             continue;
707*774Svbart@nginx.com         }
708*774Svbart@nginx.com 
709*774Svbart@nginx.com         if (i == 1 && alt_names != NULL) {
710*774Svbart@nginx.com             i++;
711*774Svbart@nginx.com         }
712*774Svbart@nginx.com 
713*774Svbart@nginx.com         str.length = len;
714*774Svbart@nginx.com         str.start = buf;
715*774Svbart@nginx.com 
716*774Svbart@nginx.com         ret = nxt_conf_set_member_string_dup(object, mp, &nids[n].name,
717*774Svbart@nginx.com                                              &str, i++);
718*774Svbart@nginx.com         if (nxt_slow_path(ret != NXT_OK)) {
719*774Svbart@nginx.com             goto fail;
720*774Svbart@nginx.com         }
721*774Svbart@nginx.com     }
722*774Svbart@nginx.com 
723*774Svbart@nginx.com     if (alt_names != NULL) {
724*774Svbart@nginx.com         count = sk_GENERAL_NAME_num(alt_names);
725*774Svbart@nginx.com 
726*774Svbart@nginx.com         for (n = 0; n != count; n++) {
727*774Svbart@nginx.com             name = sk_GENERAL_NAME_value(alt_names, n);
728*774Svbart@nginx.com 
729*774Svbart@nginx.com             if (name->type != GEN_DNS) {
730*774Svbart@nginx.com                 continue;
731*774Svbart@nginx.com             }
732*774Svbart@nginx.com         }
733*774Svbart@nginx.com 
734*774Svbart@nginx.com         names = nxt_conf_create_array(mp, n);
735*774Svbart@nginx.com         if (nxt_slow_path(names == NULL)) {
736*774Svbart@nginx.com             goto fail;
737*774Svbart@nginx.com         }
738*774Svbart@nginx.com 
739*774Svbart@nginx.com         for (n = 0, i = 0; n != count; n++) {
740*774Svbart@nginx.com             name = sk_GENERAL_NAME_value(alt_names, n);
741*774Svbart@nginx.com 
742*774Svbart@nginx.com             if (name->type != GEN_DNS) {
743*774Svbart@nginx.com                 continue;
744*774Svbart@nginx.com             }
745*774Svbart@nginx.com 
746*774Svbart@nginx.com             str.length = ASN1_STRING_length(name->d.dNSName);
747*774Svbart@nginx.com #if OPENSSL_VERSION_NUMBER > 0x10100000L
748*774Svbart@nginx.com             str.start = (u_char *) ASN1_STRING_get0_data(name->d.dNSName);
749*774Svbart@nginx.com #else
750*774Svbart@nginx.com             str.start = ASN1_STRING_data(name->d.dNSName);
751*774Svbart@nginx.com #endif
752*774Svbart@nginx.com 
753*774Svbart@nginx.com             ret = nxt_conf_set_element_string_dup(names, mp, i++, &str);
754*774Svbart@nginx.com             if (nxt_slow_path(ret != NXT_OK)) {
755*774Svbart@nginx.com                 goto fail;
756*774Svbart@nginx.com             }
757*774Svbart@nginx.com         }
758*774Svbart@nginx.com 
759*774Svbart@nginx.com         sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
760*774Svbart@nginx.com 
761*774Svbart@nginx.com         nxt_conf_set_member(object, &alt_names_str, names, 1);
762*774Svbart@nginx.com     }
763*774Svbart@nginx.com 
764*774Svbart@nginx.com     return object;
765*774Svbart@nginx.com 
766*774Svbart@nginx.com fail:
767*774Svbart@nginx.com 
768*774Svbart@nginx.com     if (alt_names != NULL) {
769*774Svbart@nginx.com         sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
770*774Svbart@nginx.com     }
771*774Svbart@nginx.com 
772*774Svbart@nginx.com     return NULL;
773*774Svbart@nginx.com }
774*774Svbart@nginx.com 
775*774Svbart@nginx.com 
776*774Svbart@nginx.com nxt_int_t
777*774Svbart@nginx.com nxt_cert_info_delete(nxt_str_t *name)
778*774Svbart@nginx.com {
779*774Svbart@nginx.com     nxt_int_t           ret;
780*774Svbart@nginx.com     nxt_cert_info_t     *info;
781*774Svbart@nginx.com     nxt_lvlhsh_query_t  lhq;
782*774Svbart@nginx.com 
783*774Svbart@nginx.com     lhq.key_hash = nxt_djb_hash(name->start, name->length);
784*774Svbart@nginx.com     lhq.key = *name;
785*774Svbart@nginx.com     lhq.proto = &nxt_cert_info_hash_proto;
786*774Svbart@nginx.com 
787*774Svbart@nginx.com     ret = nxt_lvlhsh_delete(&nxt_cert_info, &lhq);
788*774Svbart@nginx.com 
789*774Svbart@nginx.com     if (ret == NXT_OK) {
790*774Svbart@nginx.com         info = lhq.value;
791*774Svbart@nginx.com         nxt_mp_destroy(info->mp);
792*774Svbart@nginx.com     }
793*774Svbart@nginx.com 
794*774Svbart@nginx.com     return ret;
795*774Svbart@nginx.com }
796*774Svbart@nginx.com 
797*774Svbart@nginx.com 
798*774Svbart@nginx.com 
799*774Svbart@nginx.com nxt_array_t *
800*774Svbart@nginx.com nxt_cert_store_load(nxt_task_t *task)
801*774Svbart@nginx.com {
802*774Svbart@nginx.com     DIR              *dir;
803*774Svbart@nginx.com     size_t           size, alloc;
804*774Svbart@nginx.com     u_char           *buf, *p;
805*774Svbart@nginx.com     nxt_mp_t         *mp;
806*774Svbart@nginx.com     nxt_str_t        name;
807*774Svbart@nginx.com     nxt_int_t        ret;
808*774Svbart@nginx.com     nxt_file_t       file;
809*774Svbart@nginx.com     nxt_array_t      *certs;
810*774Svbart@nginx.com     nxt_runtime_t    *rt;
811*774Svbart@nginx.com     struct dirent    *de;
812*774Svbart@nginx.com     nxt_cert_item_t  *item;
813*774Svbart@nginx.com 
814*774Svbart@nginx.com     rt = task->thread->runtime;
815*774Svbart@nginx.com 
816*774Svbart@nginx.com     if (nxt_slow_path(rt->certs.start == NULL)) {
817*774Svbart@nginx.com         nxt_alert(task, "no certificates storage directory");
818*774Svbart@nginx.com         return NULL;
819*774Svbart@nginx.com     }
820*774Svbart@nginx.com 
821*774Svbart@nginx.com     mp = nxt_mp_create(1024, 128, 256, 32);
822*774Svbart@nginx.com     if (nxt_slow_path(mp == NULL)) {
823*774Svbart@nginx.com         return NULL;
824*774Svbart@nginx.com     }
825*774Svbart@nginx.com 
826*774Svbart@nginx.com     certs = nxt_array_create(mp, 16, sizeof(nxt_cert_item_t));
827*774Svbart@nginx.com     if (nxt_slow_path(certs == NULL)) {
828*774Svbart@nginx.com         nxt_mp_destroy(mp);
829*774Svbart@nginx.com         return NULL;
830*774Svbart@nginx.com     }
831*774Svbart@nginx.com 
832*774Svbart@nginx.com     buf = NULL;
833*774Svbart@nginx.com     alloc = 0;
834*774Svbart@nginx.com 
835*774Svbart@nginx.com     dir = opendir((char *) rt->certs.start);
836*774Svbart@nginx.com     if (nxt_slow_path(dir == NULL)) {
837*774Svbart@nginx.com         nxt_alert(task, "opendir(\"%s\") failed %E",
838*774Svbart@nginx.com                   rt->certs.start, nxt_errno);
839*774Svbart@nginx.com         goto fail;
840*774Svbart@nginx.com     }
841*774Svbart@nginx.com 
842*774Svbart@nginx.com     for ( ;; ) {
843*774Svbart@nginx.com         de = readdir(dir);
844*774Svbart@nginx.com         if (de == NULL) {
845*774Svbart@nginx.com             break;
846*774Svbart@nginx.com         }
847*774Svbart@nginx.com 
848*774Svbart@nginx.com         if (de->d_type != DT_REG) {
849*774Svbart@nginx.com             continue;
850*774Svbart@nginx.com         }
851*774Svbart@nginx.com 
852*774Svbart@nginx.com         item = nxt_array_add(certs);
853*774Svbart@nginx.com         if (nxt_slow_path(item == NULL)) {
854*774Svbart@nginx.com             goto fail;
855*774Svbart@nginx.com         }
856*774Svbart@nginx.com 
857*774Svbart@nginx.com         item->fd = -1;
858*774Svbart@nginx.com 
859*774Svbart@nginx.com         name.length = nxt_strlen(de->d_name);
860*774Svbart@nginx.com         name.start = (u_char *) de->d_name;
861*774Svbart@nginx.com 
862*774Svbart@nginx.com         size = rt->certs.length + name.length + 1;
863*774Svbart@nginx.com 
864*774Svbart@nginx.com         if (size > alloc) {
865*774Svbart@nginx.com             size += 32;
866*774Svbart@nginx.com 
867*774Svbart@nginx.com             p = nxt_realloc(buf, size);
868*774Svbart@nginx.com             if (p == NULL) {
869*774Svbart@nginx.com                 goto fail;
870*774Svbart@nginx.com             }
871*774Svbart@nginx.com 
872*774Svbart@nginx.com             alloc = size;
873*774Svbart@nginx.com             buf = p;
874*774Svbart@nginx.com         }
875*774Svbart@nginx.com 
876*774Svbart@nginx.com         p = nxt_cpymem(buf, rt->certs.start, rt->certs.length);
877*774Svbart@nginx.com         p = nxt_cpymem(p, name.start, name.length + 1);
878*774Svbart@nginx.com 
879*774Svbart@nginx.com         nxt_memzero(&file, sizeof(nxt_file_t));
880*774Svbart@nginx.com 
881*774Svbart@nginx.com         file.name = buf;
882*774Svbart@nginx.com 
883*774Svbart@nginx.com         ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN,
884*774Svbart@nginx.com                             NXT_FILE_OWNER_ACCESS);
885*774Svbart@nginx.com 
886*774Svbart@nginx.com 
887*774Svbart@nginx.com         if (nxt_slow_path(ret != NXT_OK)) {
888*774Svbart@nginx.com             nxt_array_remove_last(certs);
889*774Svbart@nginx.com             continue;
890*774Svbart@nginx.com         }
891*774Svbart@nginx.com 
892*774Svbart@nginx.com         item->fd = file.fd;
893*774Svbart@nginx.com 
894*774Svbart@nginx.com         if (nxt_slow_path(nxt_str_dup(mp, &item->name, &name) == NULL)) {
895*774Svbart@nginx.com             goto fail;
896*774Svbart@nginx.com         }
897*774Svbart@nginx.com     }
898*774Svbart@nginx.com 
899*774Svbart@nginx.com     if (buf != NULL) {
900*774Svbart@nginx.com         nxt_free(buf);
901*774Svbart@nginx.com     }
902*774Svbart@nginx.com 
903*774Svbart@nginx.com     (void) closedir(dir);
904*774Svbart@nginx.com 
905*774Svbart@nginx.com     return certs;
906*774Svbart@nginx.com 
907*774Svbart@nginx.com fail:
908*774Svbart@nginx.com 
909*774Svbart@nginx.com     if (buf != NULL) {
910*774Svbart@nginx.com         nxt_free(buf);
911*774Svbart@nginx.com     }
912*774Svbart@nginx.com 
913*774Svbart@nginx.com     if (dir != NULL) {
914*774Svbart@nginx.com         (void) closedir(dir);
915*774Svbart@nginx.com     }
916*774Svbart@nginx.com 
917*774Svbart@nginx.com     nxt_cert_store_release(certs);
918*774Svbart@nginx.com 
919*774Svbart@nginx.com     return NULL;
920*774Svbart@nginx.com }
921*774Svbart@nginx.com 
922*774Svbart@nginx.com 
923*774Svbart@nginx.com void
924*774Svbart@nginx.com nxt_cert_store_release(nxt_array_t *certs)
925*774Svbart@nginx.com {
926*774Svbart@nginx.com     uint32_t         i;
927*774Svbart@nginx.com     nxt_cert_item_t  *items;
928*774Svbart@nginx.com 
929*774Svbart@nginx.com     for (items = certs->elts, i = 0;
930*774Svbart@nginx.com          i < certs->nelts;
931*774Svbart@nginx.com          i++)
932*774Svbart@nginx.com     {
933*774Svbart@nginx.com         nxt_fd_close(items[i].fd);
934*774Svbart@nginx.com     }
935*774Svbart@nginx.com 
936*774Svbart@nginx.com     nxt_mp_destroy(certs->mem_pool);
937*774Svbart@nginx.com }
938*774Svbart@nginx.com 
939*774Svbart@nginx.com 
940*774Svbart@nginx.com #if 0
941*774Svbart@nginx.com 
942*774Svbart@nginx.com void
943*774Svbart@nginx.com nxt_cert_store_discovery_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
944*774Svbart@nginx.com {
945*774Svbart@nginx.com     DIR            *dir;
946*774Svbart@nginx.com     size_t         size;
947*774Svbart@nginx.com     nxt_buf_t      *b;
948*774Svbart@nginx.com     nxt_int_t      ret;
949*774Svbart@nginx.com     nxt_port_t     *port;
950*774Svbart@nginx.com     nxt_runtime_t  *rt;
951*774Svbart@nginx.com     struct dirent  *de;
952*774Svbart@nginx.com 
953*774Svbart@nginx.com     port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
954*774Svbart@nginx.com                                  msg->port_msg.reply_port);
955*774Svbart@nginx.com 
956*774Svbart@nginx.com     if (nxt_slow_path(port == NULL)) {
957*774Svbart@nginx.com         return;
958*774Svbart@nginx.com     }
959*774Svbart@nginx.com 
960*774Svbart@nginx.com     b = NULL;
961*774Svbart@nginx.com     dir = NULL;
962*774Svbart@nginx.com 
963*774Svbart@nginx.com     rt = task->thread->runtime;
964*774Svbart@nginx.com 
965*774Svbart@nginx.com     if (nxt_slow_path(rt->certs.start == NULL)) {
966*774Svbart@nginx.com         nxt_alert(task, "no certificates storage directory");
967*774Svbart@nginx.com         goto fail;
968*774Svbart@nginx.com     }
969*774Svbart@nginx.com 
970*774Svbart@nginx.com     dir = opendir((char *) rt->certs.start);
971*774Svbart@nginx.com     if (nxt_slow_path(dir == NULL)) {
972*774Svbart@nginx.com         nxt_alert(task, "opendir(\"%s\") failed %E",
973*774Svbart@nginx.com                   rt->certs.start, nxt_errno);
974*774Svbart@nginx.com         goto fail;
975*774Svbart@nginx.com     }
976*774Svbart@nginx.com 
977*774Svbart@nginx.com     size = 0;
978*774Svbart@nginx.com 
979*774Svbart@nginx.com     for ( ;; ) {
980*774Svbart@nginx.com         de = readdir(dir);
981*774Svbart@nginx.com         if (de == NULL) {
982*774Svbart@nginx.com             break;
983*774Svbart@nginx.com         }
984*774Svbart@nginx.com 
985*774Svbart@nginx.com         if (de->d_type != DT_REG) {
986*774Svbart@nginx.com             continue;
987*774Svbart@nginx.com         }
988*774Svbart@nginx.com 
989*774Svbart@nginx.com         size += nxt_strlen(de->d_name) + 1;
990*774Svbart@nginx.com     }
991*774Svbart@nginx.com 
992*774Svbart@nginx.com     b = nxt_port_mmap_get_buf(task, port, size);
993*774Svbart@nginx.com     if (nxt_slow_path(b == NULL)) {
994*774Svbart@nginx.com         goto fail;
995*774Svbart@nginx.com     }
996*774Svbart@nginx.com 
997*774Svbart@nginx.com     rewinddir(dir);
998*774Svbart@nginx.com 
999*774Svbart@nginx.com     for ( ;; ) {
1000*774Svbart@nginx.com         de = readdir(dir);
1001*774Svbart@nginx.com         if (de == NULL) {
1002*774Svbart@nginx.com             break;
1003*774Svbart@nginx.com         }
1004*774Svbart@nginx.com 
1005*774Svbart@nginx.com         if (de->d_type != DT_REG) {
1006*774Svbart@nginx.com             continue;
1007*774Svbart@nginx.com         }
1008*774Svbart@nginx.com 
1009*774Svbart@nginx.com         size = nxt_strlen(de->d_name) + 1;
1010*774Svbart@nginx.com 
1011*774Svbart@nginx.com         if (nxt_slow_path(size > (size_t) nxt_buf_mem_free_size(&b->mem))) {
1012*774Svbart@nginx.com             b->mem.free = b->mem.start;
1013*774Svbart@nginx.com             break;
1014*774Svbart@nginx.com         }
1015*774Svbart@nginx.com 
1016*774Svbart@nginx.com         b->mem.free = nxt_cpymem(b->mem.free, de->d_name, size);
1017*774Svbart@nginx.com     }
1018*774Svbart@nginx.com 
1019*774Svbart@nginx.com     (void) closedir(dir);
1020*774Svbart@nginx.com     dir = NULL;
1021*774Svbart@nginx.com 
1022*774Svbart@nginx.com     if (nxt_slow_path(nxt_buf_mem_free_size(&b->mem) != 0)) {
1023*774Svbart@nginx.com         nxt_alert(task, "certificates storage directory "
1024*774Svbart@nginx.com                   "has changed while reading it");
1025*774Svbart@nginx.com         goto fail;
1026*774Svbart@nginx.com     }
1027*774Svbart@nginx.com 
1028*774Svbart@nginx.com     ret = nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_READY_LAST, -1,
1029*774Svbart@nginx.com                                 msg->port_msg.stream, 0, b);
1030*774Svbart@nginx.com 
1031*774Svbart@nginx.com     if (nxt_fast_path(ret == NXT_OK)) {
1032*774Svbart@nginx.com         return;
1033*774Svbart@nginx.com     }
1034*774Svbart@nginx.com 
1035*774Svbart@nginx.com fail:
1036*774Svbart@nginx.com 
1037*774Svbart@nginx.com     if (dir != NULL) {
1038*774Svbart@nginx.com         (void) closedir(dir);
1039*774Svbart@nginx.com     }
1040*774Svbart@nginx.com 
1041*774Svbart@nginx.com     if (b != NULL) {
1042*774Svbart@nginx.com         b->completion_handler(task, b, b->parent);
1043*774Svbart@nginx.com     }
1044*774Svbart@nginx.com 
1045*774Svbart@nginx.com     (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, -1,
1046*774Svbart@nginx.com                                  msg->port_msg.stream, 0, NULL);
1047*774Svbart@nginx.com }
1048*774Svbart@nginx.com 
1049*774Svbart@nginx.com #endif
1050*774Svbart@nginx.com 
1051*774Svbart@nginx.com 
1052*774Svbart@nginx.com void
1053*774Svbart@nginx.com nxt_cert_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp,
1054*774Svbart@nginx.com     nxt_port_rpc_handler_t handler, void *ctx)
1055*774Svbart@nginx.com {
1056*774Svbart@nginx.com     uint32_t       stream;
1057*774Svbart@nginx.com     nxt_int_t      ret;
1058*774Svbart@nginx.com     nxt_buf_t      *b;
1059*774Svbart@nginx.com     nxt_port_t     *main_port, *recv_port;
1060*774Svbart@nginx.com     nxt_runtime_t  *rt;
1061*774Svbart@nginx.com 
1062*774Svbart@nginx.com     b = nxt_buf_mem_alloc(mp, name->length + 1, 0);
1063*774Svbart@nginx.com     if (nxt_slow_path(b == NULL)) {
1064*774Svbart@nginx.com         goto fail;
1065*774Svbart@nginx.com     }
1066*774Svbart@nginx.com 
1067*774Svbart@nginx.com     nxt_buf_cpystr(b, name);
1068*774Svbart@nginx.com     *b->mem.free++ = '\0';
1069*774Svbart@nginx.com 
1070*774Svbart@nginx.com     rt = task->thread->runtime;
1071*774Svbart@nginx.com     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
1072*774Svbart@nginx.com     recv_port = rt->port_by_type[rt->type];
1073*774Svbart@nginx.com 
1074*774Svbart@nginx.com     stream = nxt_port_rpc_register_handler(task, recv_port, handler, handler,
1075*774Svbart@nginx.com                                            -1, ctx);
1076*774Svbart@nginx.com     if (nxt_slow_path(stream == 0)) {
1077*774Svbart@nginx.com         goto fail;
1078*774Svbart@nginx.com     }
1079*774Svbart@nginx.com 
1080*774Svbart@nginx.com     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_CERT_GET, -1,
1081*774Svbart@nginx.com                                 stream, recv_port->id, b);
1082*774Svbart@nginx.com 
1083*774Svbart@nginx.com     if (nxt_slow_path(ret != NXT_OK)) {
1084*774Svbart@nginx.com         nxt_port_rpc_cancel(task, recv_port, stream);
1085*774Svbart@nginx.com         goto fail;
1086*774Svbart@nginx.com     }
1087*774Svbart@nginx.com 
1088*774Svbart@nginx.com     return;
1089*774Svbart@nginx.com 
1090*774Svbart@nginx.com fail:
1091*774Svbart@nginx.com 
1092*774Svbart@nginx.com     handler(task, NULL, ctx);
1093*774Svbart@nginx.com }
1094*774Svbart@nginx.com 
1095*774Svbart@nginx.com 
1096*774Svbart@nginx.com void
1097*774Svbart@nginx.com nxt_cert_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
1098*774Svbart@nginx.com {
1099*774Svbart@nginx.com     u_char               *p;
1100*774Svbart@nginx.com     nxt_int_t            ret;
1101*774Svbart@nginx.com     nxt_str_t            name;
1102*774Svbart@nginx.com     nxt_file_t           file;
1103*774Svbart@nginx.com     nxt_port_t           *port;
1104*774Svbart@nginx.com     nxt_runtime_t        *rt;
1105*774Svbart@nginx.com     nxt_port_msg_type_t  type;
1106*774Svbart@nginx.com 
1107*774Svbart@nginx.com     port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
1108*774Svbart@nginx.com                                  msg->port_msg.reply_port);
1109*774Svbart@nginx.com 
1110*774Svbart@nginx.com     if (port == NULL) {
1111*774Svbart@nginx.com         return;
1112*774Svbart@nginx.com     }
1113*774Svbart@nginx.com 
1114*774Svbart@nginx.com     nxt_memzero(&file, sizeof(nxt_file_t));
1115*774Svbart@nginx.com 
1116*774Svbart@nginx.com     file.fd = -1;
1117*774Svbart@nginx.com     type = NXT_PORT_MSG_RPC_ERROR;
1118*774Svbart@nginx.com 
1119*774Svbart@nginx.com     rt = task->thread->runtime;
1120*774Svbart@nginx.com 
1121*774Svbart@nginx.com     if (nxt_slow_path(rt->certs.start == NULL)) {
1122*774Svbart@nginx.com         nxt_alert(task, "no certificates storage directory");
1123*774Svbart@nginx.com         goto error;
1124*774Svbart@nginx.com     }
1125*774Svbart@nginx.com 
1126*774Svbart@nginx.com     name.start = msg->buf->mem.pos;
1127*774Svbart@nginx.com     name.length = nxt_strlen(name.start);
1128*774Svbart@nginx.com 
1129*774Svbart@nginx.com     file.name = nxt_malloc(rt->certs.length + name.length + 1);
1130*774Svbart@nginx.com 
1131*774Svbart@nginx.com     if (nxt_slow_path(file.name == NULL)) {
1132*774Svbart@nginx.com         goto error;
1133*774Svbart@nginx.com     }
1134*774Svbart@nginx.com 
1135*774Svbart@nginx.com     p = nxt_cpymem(file.name, rt->certs.start, rt->certs.length);
1136*774Svbart@nginx.com     p = nxt_cpymem(p, name.start, name.length + 1);
1137*774Svbart@nginx.com 
1138*774Svbart@nginx.com     ret = nxt_file_open(task, &file, NXT_FILE_RDWR, NXT_FILE_CREATE_OR_OPEN,
1139*774Svbart@nginx.com                         NXT_FILE_OWNER_ACCESS);
1140*774Svbart@nginx.com 
1141*774Svbart@nginx.com     nxt_free(file.name);
1142*774Svbart@nginx.com 
1143*774Svbart@nginx.com     if (nxt_fast_path(ret == NXT_OK)) {
1144*774Svbart@nginx.com         type = NXT_PORT_MSG_RPC_READY_LAST | NXT_PORT_MSG_CLOSE_FD;
1145*774Svbart@nginx.com     }
1146*774Svbart@nginx.com 
1147*774Svbart@nginx.com error:
1148*774Svbart@nginx.com 
1149*774Svbart@nginx.com     (void) nxt_port_socket_write(task, port, type, file.fd,
1150*774Svbart@nginx.com                                  msg->port_msg.stream, 0, NULL);
1151*774Svbart@nginx.com }
1152*774Svbart@nginx.com 
1153*774Svbart@nginx.com 
1154*774Svbart@nginx.com void
1155*774Svbart@nginx.com nxt_cert_store_delete(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp)
1156*774Svbart@nginx.com {
1157*774Svbart@nginx.com     nxt_buf_t      *b;
1158*774Svbart@nginx.com     nxt_port_t     *main_port;
1159*774Svbart@nginx.com     nxt_runtime_t  *rt;
1160*774Svbart@nginx.com 
1161*774Svbart@nginx.com     b = nxt_buf_mem_alloc(mp, name->length + 1, 0);
1162*774Svbart@nginx.com 
1163*774Svbart@nginx.com     if (nxt_fast_path(b != NULL)) {
1164*774Svbart@nginx.com         nxt_buf_cpystr(b, name);
1165*774Svbart@nginx.com         *b->mem.free++ = '\0';
1166*774Svbart@nginx.com 
1167*774Svbart@nginx.com         rt = task->thread->runtime;
1168*774Svbart@nginx.com         main_port = rt->port_by_type[NXT_PROCESS_MAIN];
1169*774Svbart@nginx.com 
1170*774Svbart@nginx.com         (void) nxt_port_socket_write(task, main_port, NXT_PORT_MSG_CERT_DELETE,
1171*774Svbart@nginx.com                                      -1, 0, 0, b);
1172*774Svbart@nginx.com     }
1173*774Svbart@nginx.com }
1174*774Svbart@nginx.com 
1175*774Svbart@nginx.com 
1176*774Svbart@nginx.com void
1177*774Svbart@nginx.com nxt_cert_store_delete_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
1178*774Svbart@nginx.com {
1179*774Svbart@nginx.com     u_char           *p;
1180*774Svbart@nginx.com     nxt_str_t        name;
1181*774Svbart@nginx.com     nxt_runtime_t    *rt;
1182*774Svbart@nginx.com     nxt_file_name_t  *path;
1183*774Svbart@nginx.com 
1184*774Svbart@nginx.com     rt = task->thread->runtime;
1185*774Svbart@nginx.com 
1186*774Svbart@nginx.com     if (nxt_slow_path(rt->certs.start == NULL)) {
1187*774Svbart@nginx.com         nxt_alert(task, "no certificates storage directory");
1188*774Svbart@nginx.com         return;
1189*774Svbart@nginx.com     }
1190*774Svbart@nginx.com 
1191*774Svbart@nginx.com     name.start = msg->buf->mem.pos;
1192*774Svbart@nginx.com     name.length = nxt_strlen(name.start);
1193*774Svbart@nginx.com 
1194*774Svbart@nginx.com     path = nxt_malloc(rt->certs.length + name.length + 1);
1195*774Svbart@nginx.com 
1196*774Svbart@nginx.com     if (nxt_fast_path(path != NULL)) {
1197*774Svbart@nginx.com         p = nxt_cpymem(path, rt->certs.start, rt->certs.length);
1198*774Svbart@nginx.com         p = nxt_cpymem(p, name.start, name.length + 1);
1199*774Svbart@nginx.com 
1200*774Svbart@nginx.com         (void) nxt_file_delete(path);
1201*774Svbart@nginx.com 
1202*774Svbart@nginx.com         nxt_free(path);
1203*774Svbart@nginx.com     }
1204*774Svbart@nginx.com }
1205