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