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