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