xref: /unit/src/nxt_cert.c (revision 2069:a74adcc53b78)
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 *
nxt_cert_mem(nxt_task_t * task,nxt_buf_mem_t * mbuf)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 *
nxt_cert_fd(nxt_task_t * task,nxt_fd_t fd)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 *
nxt_cert_bio(nxt_task_t * task,BIO * bio)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                 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
nxt_nxt_cert_pem_suffix(char * pem_str,const char * suffix)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
nxt_cert_destroy(nxt_cert_t * cert)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
nxt_cert_info_hash_test(nxt_lvlhsh_query_t * lhq,void * data)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
nxt_cert_info_init(nxt_task_t * task,nxt_array_t * certs)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
nxt_cert_info_save(nxt_str_t * name,nxt_cert_t * cert)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 *
nxt_cert_info_get(nxt_str_t * name)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 *
nxt_cert_info_get_all(nxt_mp_t * mp)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 *
nxt_cert_details(nxt_mp_t * mp,nxt_cert_t * cert)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 *
nxt_cert_name_details(nxt_mp_t * mp,X509 * x509,nxt_bool_t issuer)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         names = nxt_cert_alt_names_details(mp, alt_names);
694 
695         sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
696 
697         if (nxt_slow_path(names == NULL)) {
698             return NULL;
699         }
700 
701         count++;
702 
703     } else {
704         names = NULL;
705     }
706 
707     object = nxt_conf_create_object(mp, count);
708     if (nxt_slow_path(object == NULL)) {
709         return NULL;
710     }
711 
712     for (n = 0, i = 0; n != nxt_nitems(nids) && i != count; n++) {
713 
714         len = X509_NAME_get_text_by_NID(x509_name, nids[n].nid,
715                                         (char *) buf, sizeof(buf));
716 
717         if (n == 1 && names != NULL) {
718             nxt_conf_set_member(object, &alt_names_str, names, i++);
719         }
720 
721         if (len < 0) {
722             continue;
723         }
724 
725         str.length = len;
726         str.start = buf;
727 
728         ret = nxt_conf_set_member_string_dup(object, mp, &nids[n].name,
729                                              &str, i++);
730         if (nxt_slow_path(ret != NXT_OK)) {
731             return NULL;
732         }
733     }
734 
735     return object;
736 }
737 
738 
739 static nxt_conf_value_t *
nxt_cert_alt_names_details(nxt_mp_t * mp,STACK_OF (GENERAL_NAME)* alt_names)740 nxt_cert_alt_names_details(nxt_mp_t *mp, STACK_OF(GENERAL_NAME) *alt_names)
741 {
742     nxt_str_t         str;
743     nxt_int_t         ret;
744     nxt_uint_t        i, n, count;
745     GENERAL_NAME      *name;
746     nxt_conf_value_t  *array;
747 
748     count = sk_GENERAL_NAME_num(alt_names);
749     n = 0;
750 
751     for (i = 0; i != count; i++) {
752         name = sk_GENERAL_NAME_value(alt_names, i);
753 
754         if (name->type != GEN_DNS) {
755             continue;
756         }
757 
758         n++;
759     }
760 
761     array = nxt_conf_create_array(mp, n);
762     if (nxt_slow_path(array == NULL)) {
763         return NULL;
764     }
765 
766     for (n = 0, i = 0; n != count; n++) {
767         name = sk_GENERAL_NAME_value(alt_names, n);
768 
769         if (name->type != GEN_DNS) {
770             continue;
771         }
772 
773         str.length = ASN1_STRING_length(name->d.dNSName);
774 #if OPENSSL_VERSION_NUMBER > 0x10100000L
775         str.start = (u_char *) ASN1_STRING_get0_data(name->d.dNSName);
776 #else
777         str.start = ASN1_STRING_data(name->d.dNSName);
778 #endif
779 
780         ret = nxt_conf_set_element_string_dup(array, mp, i++, &str);
781         if (nxt_slow_path(ret != NXT_OK)) {
782             return NULL;
783         }
784     }
785 
786     return array;
787 }
788 
789 
790 nxt_int_t
nxt_cert_info_delete(nxt_str_t * name)791 nxt_cert_info_delete(nxt_str_t *name)
792 {
793     nxt_int_t           ret;
794     nxt_cert_info_t     *info;
795     nxt_lvlhsh_query_t  lhq;
796 
797     lhq.key_hash = nxt_djb_hash(name->start, name->length);
798     lhq.key = *name;
799     lhq.proto = &nxt_cert_info_hash_proto;
800 
801     ret = nxt_lvlhsh_delete(&nxt_cert_info, &lhq);
802 
803     if (ret == NXT_OK) {
804         info = lhq.value;
805         nxt_mp_destroy(info->mp);
806     }
807 
808     return ret;
809 }
810 
811 
812 
813 nxt_array_t *
nxt_cert_store_load(nxt_task_t * task,nxt_mp_t * mp)814 nxt_cert_store_load(nxt_task_t *task, nxt_mp_t *mp)
815 {
816     DIR              *dir;
817     size_t           size, alloc;
818     u_char           *buf, *p;
819     nxt_str_t        name;
820     nxt_int_t        ret;
821     nxt_file_t       file;
822     nxt_array_t      *certs;
823     nxt_runtime_t    *rt;
824     struct dirent    *de;
825     nxt_cert_item_t  *item;
826 
827     rt = task->thread->runtime;
828 
829     if (nxt_slow_path(rt->certs.start == NULL)) {
830         nxt_alert(task, "no certificates storage directory");
831         return NULL;
832     }
833 
834     certs = nxt_array_create(mp, 16, sizeof(nxt_cert_item_t));
835     if (nxt_slow_path(certs == NULL)) {
836         return NULL;
837     }
838 
839     buf = NULL;
840     alloc = 0;
841 
842     dir = opendir((char *) rt->certs.start);
843     if (nxt_slow_path(dir == NULL)) {
844         nxt_alert(task, "opendir(\"%s\") failed %E",
845                   rt->certs.start, nxt_errno);
846         goto fail;
847     }
848 
849     for ( ;; ) {
850         de = readdir(dir);
851         if (de == NULL) {
852             break;
853         }
854 
855         nxt_debug(task, "readdir(\"%s\"): \"%s\"", rt->certs.start, de->d_name);
856 
857         name.length = nxt_strlen(de->d_name);
858         name.start = (u_char *) de->d_name;
859 
860         if (nxt_str_eq(&name, ".", 1) || nxt_str_eq(&name, "..", 2)) {
861             continue;
862         }
863 
864         item = nxt_array_add(certs);
865         if (nxt_slow_path(item == NULL)) {
866             goto fail;
867         }
868 
869         item->fd = -1;
870 
871         size = rt->certs.length + name.length + 1;
872 
873         if (size > alloc) {
874             size += 32;
875 
876             p = nxt_realloc(buf, size);
877             if (p == NULL) {
878                 goto fail;
879             }
880 
881             alloc = size;
882             buf = p;
883         }
884 
885         p = nxt_cpymem(buf, rt->certs.start, rt->certs.length);
886         p = nxt_cpymem(p, name.start, name.length + 1);
887 
888         nxt_memzero(&file, sizeof(nxt_file_t));
889 
890         file.name = buf;
891 
892         ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN,
893                             NXT_FILE_OWNER_ACCESS);
894 
895 
896         if (nxt_slow_path(ret != NXT_OK)) {
897             nxt_array_remove_last(certs);
898             continue;
899         }
900 
901         item->fd = file.fd;
902 
903         if (nxt_slow_path(nxt_str_dup(mp, &item->name, &name) == NULL)) {
904             goto fail;
905         }
906     }
907 
908     if (buf != NULL) {
909         nxt_free(buf);
910     }
911 
912     (void) closedir(dir);
913 
914     return certs;
915 
916 fail:
917 
918     if (buf != NULL) {
919         nxt_free(buf);
920     }
921 
922     if (dir != NULL) {
923         (void) closedir(dir);
924     }
925 
926     nxt_cert_store_release(certs);
927 
928     return NULL;
929 }
930 
931 
932 void
nxt_cert_store_release(nxt_array_t * certs)933 nxt_cert_store_release(nxt_array_t *certs)
934 {
935     uint32_t         i;
936     nxt_cert_item_t  *items;
937 
938     for (items = certs->elts, i = 0;
939          i < certs->nelts;
940          i++)
941     {
942         nxt_fd_close(items[i].fd);
943     }
944 
945     nxt_array_destroy(certs);
946 }
947 
948 
949 #if 0
950 
951 void
952 nxt_cert_store_discovery_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
953 {
954     DIR            *dir;
955     size_t         size;
956     nxt_buf_t      *b;
957     nxt_int_t      ret;
958     nxt_port_t     *port;
959     nxt_runtime_t  *rt;
960     struct dirent  *de;
961 
962     port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
963                                  msg->port_msg.reply_port);
964 
965     if (nxt_slow_path(port == NULL)) {
966         return;
967     }
968 
969     b = NULL;
970     dir = NULL;
971 
972     rt = task->thread->runtime;
973 
974     if (nxt_slow_path(rt->certs.start == NULL)) {
975         nxt_alert(task, "no certificates storage directory");
976         goto fail;
977     }
978 
979     dir = opendir((char *) rt->certs.start);
980     if (nxt_slow_path(dir == NULL)) {
981         nxt_alert(task, "opendir(\"%s\") failed %E",
982                   rt->certs.start, nxt_errno);
983         goto fail;
984     }
985 
986     size = 0;
987 
988     for ( ;; ) {
989         de = readdir(dir);
990         if (de == NULL) {
991             break;
992         }
993 
994         if (de->d_type != DT_REG) {
995             continue;
996         }
997 
998         size += nxt_strlen(de->d_name) + 1;
999     }
1000 
1001     b = nxt_port_mmap_get_buf(task, port, size);
1002     if (nxt_slow_path(b == NULL)) {
1003         goto fail;
1004     }
1005 
1006     rewinddir(dir);
1007 
1008     for ( ;; ) {
1009         de = readdir(dir);
1010         if (de == NULL) {
1011             break;
1012         }
1013 
1014         if (de->d_type != DT_REG) {
1015             continue;
1016         }
1017 
1018         size = nxt_strlen(de->d_name) + 1;
1019 
1020         if (nxt_slow_path(size > (size_t) nxt_buf_mem_free_size(&b->mem))) {
1021             b->mem.free = b->mem.start;
1022             break;
1023         }
1024 
1025         b->mem.free = nxt_cpymem(b->mem.free, de->d_name, size);
1026     }
1027 
1028     (void) closedir(dir);
1029     dir = NULL;
1030 
1031     if (nxt_slow_path(nxt_buf_mem_free_size(&b->mem) != 0)) {
1032         nxt_alert(task, "certificates storage directory "
1033                   "has changed while reading it");
1034         goto fail;
1035     }
1036 
1037     ret = nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_READY_LAST, -1,
1038                                 msg->port_msg.stream, 0, b);
1039 
1040     if (nxt_fast_path(ret == NXT_OK)) {
1041         return;
1042     }
1043 
1044 fail:
1045 
1046     if (dir != NULL) {
1047         (void) closedir(dir);
1048     }
1049 
1050     if (b != NULL) {
1051         b->completion_handler(task, b, b->parent);
1052     }
1053 
1054     (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, -1,
1055                                  msg->port_msg.stream, 0, NULL);
1056 }
1057 
1058 #endif
1059 
1060 
1061 void
nxt_cert_store_get(nxt_task_t * task,nxt_str_t * name,nxt_mp_t * mp,nxt_port_rpc_handler_t handler,void * ctx)1062 nxt_cert_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp,
1063     nxt_port_rpc_handler_t handler, void *ctx)
1064 {
1065     uint32_t       stream;
1066     nxt_int_t      ret;
1067     nxt_buf_t      *b;
1068     nxt_port_t     *main_port, *recv_port;
1069     nxt_runtime_t  *rt;
1070 
1071     b = nxt_buf_mem_alloc(mp, name->length + 1, 0);
1072     if (nxt_slow_path(b == NULL)) {
1073         goto fail;
1074     }
1075 
1076     nxt_mp_retain(mp);
1077     b->completion_handler = nxt_cert_buf_completion;
1078 
1079     nxt_buf_cpystr(b, name);
1080     *b->mem.free++ = '\0';
1081 
1082     rt = task->thread->runtime;
1083     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
1084     recv_port = rt->port_by_type[rt->type];
1085 
1086     stream = nxt_port_rpc_register_handler(task, recv_port, handler, handler,
1087                                            -1, ctx);
1088     if (nxt_slow_path(stream == 0)) {
1089         goto fail;
1090     }
1091 
1092     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_CERT_GET, -1,
1093                                 stream, recv_port->id, b);
1094 
1095     if (nxt_slow_path(ret != NXT_OK)) {
1096         nxt_port_rpc_cancel(task, recv_port, stream);
1097         goto fail;
1098     }
1099 
1100     return;
1101 
1102 fail:
1103 
1104     handler(task, NULL, ctx);
1105 }
1106 
1107 
1108 static void
nxt_cert_buf_completion(nxt_task_t * task,void * obj,void * data)1109 nxt_cert_buf_completion(nxt_task_t *task, void *obj, void *data)
1110 {
1111     nxt_mp_t   *mp;
1112     nxt_buf_t  *b;
1113 
1114     b = obj;
1115     mp = b->data;
1116     nxt_assert(b->next == NULL);
1117 
1118     nxt_mp_free(mp, b);
1119     nxt_mp_release(mp);
1120 }
1121 
1122 
1123 void
nxt_cert_store_get_handler(nxt_task_t * task,nxt_port_recv_msg_t * msg)1124 nxt_cert_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
1125 {
1126     u_char               *p;
1127     nxt_int_t            ret;
1128     nxt_str_t            name;
1129     nxt_file_t           file;
1130     nxt_port_t           *port;
1131     nxt_runtime_t        *rt;
1132     nxt_port_msg_type_t  type;
1133 
1134     port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
1135                                  msg->port_msg.reply_port);
1136 
1137     if (nxt_slow_path(port == NULL)) {
1138         nxt_alert(task, "process port not found (pid %PI, reply_port %d)",
1139                   msg->port_msg.pid, msg->port_msg.reply_port);
1140         return;
1141     }
1142 
1143     if (nxt_slow_path(port->type != NXT_PROCESS_CONTROLLER
1144                       && port->type != NXT_PROCESS_ROUTER))
1145     {
1146         nxt_alert(task, "process %PI cannot store certificates",
1147                   msg->port_msg.pid);
1148         return;
1149     }
1150 
1151     nxt_memzero(&file, sizeof(nxt_file_t));
1152 
1153     file.fd = -1;
1154     type = NXT_PORT_MSG_RPC_ERROR;
1155 
1156     rt = task->thread->runtime;
1157 
1158     if (nxt_slow_path(rt->certs.start == NULL)) {
1159         nxt_alert(task, "no certificates storage directory");
1160         goto error;
1161     }
1162 
1163     name.start = msg->buf->mem.pos;
1164     name.length = nxt_strlen(name.start);
1165 
1166     file.name = nxt_malloc(rt->certs.length + name.length + 1);
1167 
1168     if (nxt_slow_path(file.name == NULL)) {
1169         goto error;
1170     }
1171 
1172     p = nxt_cpymem(file.name, rt->certs.start, rt->certs.length);
1173     p = nxt_cpymem(p, name.start, name.length + 1);
1174 
1175     ret = nxt_file_open(task, &file, NXT_FILE_RDWR, NXT_FILE_CREATE_OR_OPEN,
1176                         NXT_FILE_OWNER_ACCESS);
1177 
1178     nxt_free(file.name);
1179 
1180     if (nxt_fast_path(ret == NXT_OK)) {
1181         type = NXT_PORT_MSG_RPC_READY_LAST | NXT_PORT_MSG_CLOSE_FD;
1182     }
1183 
1184 error:
1185 
1186     (void) nxt_port_socket_write(task, port, type, file.fd,
1187                                  msg->port_msg.stream, 0, NULL);
1188 }
1189 
1190 
1191 void
nxt_cert_store_delete(nxt_task_t * task,nxt_str_t * name,nxt_mp_t * mp)1192 nxt_cert_store_delete(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp)
1193 {
1194     nxt_buf_t      *b;
1195     nxt_port_t     *main_port;
1196     nxt_runtime_t  *rt;
1197 
1198     b = nxt_buf_mem_alloc(mp, name->length + 1, 0);
1199 
1200     if (nxt_fast_path(b != NULL)) {
1201         nxt_buf_cpystr(b, name);
1202         *b->mem.free++ = '\0';
1203 
1204         rt = task->thread->runtime;
1205         main_port = rt->port_by_type[NXT_PROCESS_MAIN];
1206 
1207         (void) nxt_port_socket_write(task, main_port, NXT_PORT_MSG_CERT_DELETE,
1208                                      -1, 0, 0, b);
1209     }
1210 }
1211 
1212 
1213 void
nxt_cert_store_delete_handler(nxt_task_t * task,nxt_port_recv_msg_t * msg)1214 nxt_cert_store_delete_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
1215 {
1216     u_char           *p;
1217     nxt_str_t        name;
1218     nxt_port_t       *ctl_port;
1219     nxt_runtime_t    *rt;
1220     nxt_file_name_t  *path;
1221 
1222     rt = task->thread->runtime;
1223     ctl_port = rt->port_by_type[NXT_PROCESS_CONTROLLER];
1224 
1225     if (nxt_slow_path(ctl_port == NULL)) {
1226         nxt_alert(task, "controller port not found");
1227         return;
1228     }
1229 
1230     if (nxt_slow_path(nxt_recv_msg_cmsg_pid(msg) != ctl_port->pid)) {
1231         nxt_alert(task, "process %PI cannot delete certificates",
1232                   nxt_recv_msg_cmsg_pid(msg));
1233         return;
1234     }
1235 
1236     if (nxt_slow_path(rt->certs.start == NULL)) {
1237         nxt_alert(task, "no certificates storage directory");
1238         return;
1239     }
1240 
1241     name.start = msg->buf->mem.pos;
1242     name.length = nxt_strlen(name.start);
1243 
1244     path = nxt_malloc(rt->certs.length + name.length + 1);
1245 
1246     if (nxt_fast_path(path != NULL)) {
1247         p = nxt_cpymem(path, rt->certs.start, rt->certs.length);
1248         p = nxt_cpymem(p, name.start, name.length + 1);
1249 
1250         (void) nxt_file_delete(path);
1251 
1252         nxt_free(path);
1253     }
1254 }
1255