diff options
author | Valentin Bartenev <vbart@nginx.com> | 2018-09-20 15:27:08 +0300 |
---|---|---|
committer | Valentin Bartenev <vbart@nginx.com> | 2018-09-20 15:27:08 +0300 |
commit | 8d844bc2aa78d5acc789c5865a62bedbeba76902 (patch) | |
tree | cbc6e225402434d5b14cb454a2b7c02cebc93bbf /src/nxt_cert.c | |
parent | 2dfd8ffc2fa36712c8987afa870c355185d61af9 (diff) | |
download | unit-8d844bc2aa78d5acc789c5865a62bedbeba76902.tar.gz unit-8d844bc2aa78d5acc789c5865a62bedbeba76902.tar.bz2 |
Controller: certificates storage interface.
Diffstat (limited to 'src/nxt_cert.c')
-rw-r--r-- | src/nxt_cert.c | 1204 |
1 files changed, 1204 insertions, 0 deletions
diff --git a/src/nxt_cert.c b/src/nxt_cert.c new file mode 100644 index 00000000..ee258646 --- /dev/null +++ b/src/nxt_cert.c @@ -0,0 +1,1204 @@ + +/* + * Copyright (C) Valentin V. Bartenev + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> +#include <nxt_conf.h> +#include <nxt_cert.h> + +#include <dirent.h> + +#include <openssl/bio.h> +#include <openssl/pem.h> +#include <openssl/evp.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/rsa.h> +#include <openssl/err.h> + + +struct nxt_cert_s { + EVP_PKEY *key; + nxt_uint_t count; + X509 *chain[]; +}; + + +typedef struct { + nxt_str_t name; + nxt_conf_value_t *value; + nxt_mp_t *mp; +} nxt_cert_info_t; + + +typedef struct { + nxt_str_t name; + nxt_fd_t fd; +} nxt_cert_item_t; + + +static nxt_cert_t *nxt_cert_fd(nxt_task_t *task, nxt_fd_t fd); +static nxt_cert_t *nxt_cert_bio(nxt_task_t *task, BIO *bio); +static int nxt_nxt_cert_pem_suffix(char *pem_str, const char *suffix); + +static nxt_conf_value_t *nxt_cert_details(nxt_mp_t *mp, nxt_cert_t *cert); +static nxt_conf_value_t *nxt_cert_name_details(nxt_mp_t *mp, X509 *x509, + nxt_bool_t issuer); + + +static nxt_lvlhsh_t nxt_cert_info; + + +nxt_cert_t * +nxt_cert_mem(nxt_task_t *task, nxt_buf_mem_t *mbuf) +{ + BIO *bio; + nxt_cert_t *cert; + + bio = BIO_new_mem_buf(mbuf->pos, nxt_buf_mem_used_size(mbuf)); + if (nxt_slow_path(bio == NULL)) { + nxt_openssl_log_error(task, NXT_LOG_ALERT, "BIO_new_mem_buf() failed"); + return NULL; + } + + cert = nxt_cert_bio(task, bio); + + BIO_free(bio); + + return cert; +} + + +static nxt_cert_t * +nxt_cert_fd(nxt_task_t *task, nxt_fd_t fd) +{ + BIO *bio; + nxt_cert_t *cert; + + bio = BIO_new_fd(fd, 0); + if (nxt_slow_path(bio == NULL)) { + nxt_openssl_log_error(task, NXT_LOG_ALERT, "BIO_new_fd() failed"); + return NULL; + } + + cert = nxt_cert_bio(task, bio); + + BIO_free(bio); + + return cert; +} + + +static nxt_cert_t * +nxt_cert_bio(nxt_task_t *task, BIO *bio) +{ + int ret, suffix, key_id; + long length, reason; + char *type, *header; + X509 *x509; + EVP_PKEY *key; + nxt_uint_t nalloc; + nxt_cert_t *cert, *new_cert; + u_char *data; + const u_char *data_copy; + PKCS8_PRIV_KEY_INFO *p8inf; + const EVP_PKEY_ASN1_METHOD *ameth; + + nalloc = 4; + + cert = nxt_zalloc(sizeof(nxt_cert_t) + nalloc * sizeof(X509 *)); + if (cert == NULL) { + return NULL; + } + + for ( ;; ) { + ret = PEM_read_bio(bio, &type, &header, &data, &length); + + if (ret == 0) { + reason = ERR_GET_REASON(ERR_peek_last_error()); + if (reason != PEM_R_NO_START_LINE) { + nxt_openssl_log_error(task, NXT_LOG_ALERT, + "PEM_read_bio() failed"); + goto fail; + } + + ERR_clear_error(); + break; + } + + nxt_debug(task, "PEM type: \"%s\"", type); + + key = NULL; + x509 = NULL; +/* + EVP_CIPHER_INFO cipher; + + if (PEM_get_EVP_CIPHER_INFO(header, &cipher) != 0) { + nxt_alert(task, "encrypted PEM isn't supported"); + goto done; + } +*/ + if (nxt_strcmp(type, PEM_STRING_PKCS8) == 0) { + nxt_alert(task, "PEM PKCS8 isn't supported"); + goto done; + } + + if (nxt_strcmp(type, PEM_STRING_PKCS8INF) == 0) { + data_copy = data; + + p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &data_copy, length); + + if (p8inf == NULL) { + nxt_openssl_log_error(task, NXT_LOG_ALERT, + "d2i_PKCS8_PRIV_KEY_INFO() failed"); + goto done; + } + + key = EVP_PKCS82PKEY(p8inf); + + PKCS8_PRIV_KEY_INFO_free(p8inf); + goto done; + } + + suffix = nxt_nxt_cert_pem_suffix(type, PEM_STRING_PKCS8INF); + + if (suffix != 0) { + + ameth = EVP_PKEY_asn1_find_str(NULL, type, suffix); + if (ameth == NULL) { + nxt_openssl_log_error(task, NXT_LOG_ALERT, + "EVP_PKEY_asn1_find_str() failed"); + goto done; + } + + EVP_PKEY_asn1_get0_info(&key_id, NULL, NULL, NULL, NULL, ameth); + + data_copy = data; + + key = d2i_PrivateKey(key_id, NULL, &data_copy, length); + goto done; + } + + if (nxt_strcmp(type, PEM_STRING_X509) == 0 + || nxt_strcmp(type, PEM_STRING_X509_OLD) == 0) + { + data_copy = data; + + x509 = d2i_X509(NULL, &data_copy, length); + if (x509 == NULL) { + nxt_openssl_log_error(task, NXT_LOG_ALERT, + "d2i_X509() failed"); + } + + goto done; + } + + if (nxt_strcmp(type, PEM_STRING_X509_TRUSTED) == 0) { + data_copy = data; + + x509 = d2i_X509_AUX(NULL, &data_copy, length); + if (x509 == NULL) { + nxt_openssl_log_error(task, NXT_LOG_ALERT, + "d2i_X509_AUX() failed"); + } + + goto done; + } + + nxt_alert(task, "unsupported PEM type: \"%s\"", type); + + done: + + OPENSSL_free(data); + OPENSSL_free(header); + OPENSSL_free(type); + + if (key != NULL) { + if (cert->key != NULL) { + EVP_PKEY_free(key); + nxt_alert(task, "multiple private keys in PEM"); + goto fail; + } + + cert->key = key; + continue; + } + + if (x509 != NULL) { + + if (cert->count == nalloc) { + nalloc += 4; + + new_cert = nxt_realloc(cert, sizeof(nxt_cert_t) + + nalloc * sizeof(X509 *)); + if (new_cert == NULL) { + X509_free(x509); + goto fail; + } + + nxt_free(cert); + cert = new_cert; + } + + cert->chain[cert->count++] = x509; + continue; + } + + goto fail; + } + + if (cert->key == NULL) { + nxt_alert(task, "no key found"); + goto fail; + } + + if (cert->count == 0) { + nxt_alert(task, "no certificates found"); + goto fail; + } + + return cert; + +fail: + + nxt_cert_destroy(cert); + + return NULL; +} + + +static int +nxt_nxt_cert_pem_suffix(char *pem_str, const char *suffix) +{ + char *p; + nxt_uint_t pem_len, suffix_len; + + pem_len = strlen(pem_str); + suffix_len = strlen(suffix); + + if (suffix_len + 1 >= pem_len) { + return 0; + } + + p = pem_str + pem_len - suffix_len; + + if (nxt_strcmp(p, suffix) != 0) { + return 0; + } + + p--; + + if (*p != ' ') { + return 0; + } + + return p - pem_str; +} + + +void +nxt_cert_destroy(nxt_cert_t *cert) +{ + nxt_uint_t i; + + EVP_PKEY_free(cert->key); + + for (i = 0; i != cert->count; i++) { + X509_free(cert->chain[i]); + } + + nxt_free(cert); +} + + + +static nxt_int_t +nxt_cert_info_hash_test(nxt_lvlhsh_query_t *lhq, void *data) +{ + nxt_cert_info_t *info; + + info = data; + + if (nxt_strcasestr_eq(&lhq->key, &info->name)) { + return NXT_OK; + } + + return NXT_DECLINED; +} + + +static const nxt_lvlhsh_proto_t nxt_cert_info_hash_proto + nxt_aligned(64) = +{ + NXT_LVLHSH_DEFAULT, + nxt_cert_info_hash_test, + nxt_lvlhsh_alloc, + nxt_lvlhsh_free, +}; + + +void +nxt_cert_info_init(nxt_task_t *task, nxt_array_t *certs) +{ + uint32_t i; + nxt_cert_t *cert; + nxt_cert_item_t *items; + + for (items = certs->elts, i = 0; i < certs->nelts; i++) { + cert = nxt_cert_fd(task, items[i].fd); + + if (nxt_slow_path(cert == NULL)) { + continue; + } + + (void) nxt_cert_info_save(&items[i].name, cert); + + nxt_cert_destroy(cert); + } +} + + +nxt_int_t +nxt_cert_info_save(nxt_str_t *name, nxt_cert_t *cert) +{ + nxt_mp_t *mp; + nxt_int_t ret; + nxt_cert_info_t *info; + nxt_conf_value_t *value; + nxt_lvlhsh_query_t lhq; + + mp = nxt_mp_create(1024, 128, 256, 32); + if (nxt_slow_path(mp == NULL)) { + return NXT_ERROR; + } + + info = nxt_mp_get(mp, sizeof(nxt_cert_info_t)); + if (nxt_slow_path(info == NULL)) { + goto fail; + } + + name = nxt_str_dup(mp, &info->name, name); + if (nxt_slow_path(name == NULL)) { + goto fail; + } + + value = nxt_cert_details(mp, cert); + if (nxt_slow_path(value == NULL)) { + goto fail; + } + + info->mp = mp; + info->value = value; + + lhq.key_hash = nxt_djb_hash(name->start, name->length); + lhq.replace = 1; + lhq.key = *name; + lhq.value = info; + lhq.proto = &nxt_cert_info_hash_proto; + + ret = nxt_lvlhsh_insert(&nxt_cert_info, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + + if (lhq.value != info) { + info = lhq.value; + nxt_mp_destroy(info->mp); + } + + return NXT_OK; + +fail: + + nxt_mp_destroy(mp); + return NXT_ERROR; +} + + +nxt_conf_value_t * +nxt_cert_info_get(nxt_str_t *name) +{ + nxt_int_t ret; + nxt_cert_info_t *info; + nxt_lvlhsh_query_t lhq; + + lhq.key_hash = nxt_djb_hash(name->start, name->length); + lhq.key = *name; + lhq.proto = &nxt_cert_info_hash_proto; + + ret = nxt_lvlhsh_find(&nxt_cert_info, &lhq); + if (ret != NXT_OK) { + return NULL; + } + + info = lhq.value; + + return info->value; +} + + +nxt_conf_value_t * +nxt_cert_info_get_all(nxt_mp_t *mp) +{ + uint32_t i; + nxt_cert_info_t *info; + nxt_conf_value_t *all; + nxt_lvlhsh_each_t lhe; + + nxt_lvlhsh_each_init(&lhe, &nxt_cert_info_hash_proto); + + i = 0; + + for ( ;; ) { + info = nxt_lvlhsh_each(&nxt_cert_info, &lhe); + + if (info == NULL) { + break; + } + + i++; + } + + all = nxt_conf_create_object(mp, i); + if (nxt_slow_path(all == NULL)) { + return NULL; + } + + nxt_lvlhsh_each_init(&lhe, &nxt_cert_info_hash_proto); + + i = 0; + + for ( ;; ) { + info = nxt_lvlhsh_each(&nxt_cert_info, &lhe); + + if (info == NULL) { + break; + } + + nxt_conf_set_member(all, &info->name, info->value, i); + + i++; + } + + return all; +} + + +static nxt_conf_value_t * +nxt_cert_details(nxt_mp_t *mp, nxt_cert_t *cert) +{ + BIO *bio; + X509 *x509; + u_char *end; + EVP_PKEY *key; + ASN1_TIME *asn1_time; + nxt_str_t str; + nxt_int_t ret; + nxt_uint_t i; + nxt_conf_value_t *object, *chain, *element, *value; + u_char buf[256]; + + static nxt_str_t key_str = nxt_string("key"); + static nxt_str_t chain_str = nxt_string("chain"); + static nxt_str_t since_str = nxt_string("since"); + static nxt_str_t until_str = nxt_string("until"); + static nxt_str_t issuer_str = nxt_string("issuer"); + static nxt_str_t subject_str = nxt_string("subject"); + static nxt_str_t validity_str = nxt_string("validity"); + + object = nxt_conf_create_object(mp, 2); + if (nxt_slow_path(object == NULL)) { + return NULL; + } + + if (cert->key != NULL) { + key = cert->key; + + switch (EVP_PKEY_base_id(key)) { + case EVP_PKEY_RSA: + end = nxt_sprintf(buf, buf + sizeof(buf), "RSA (%d bits)", + EVP_PKEY_bits(key)); + + str.length = end - buf; + str.start = buf; + break; + + case EVP_PKEY_DH: + end = nxt_sprintf(buf, buf + sizeof(buf), "DH (%d bits)", + EVP_PKEY_bits(key)); + + str.length = end - buf; + str.start = buf; + break; + + case EVP_PKEY_EC: + nxt_str_set(&str, "ECDH"); + break; + + default: + nxt_str_set(&str, "unknown"); + } + + ret = nxt_conf_set_member_string_dup(object, mp, &key_str, &str, 0); + + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } + + } else { + nxt_conf_set_member_null(object, &key_str, 0); + } + + chain = nxt_conf_create_array(mp, cert->count); + if (nxt_slow_path(chain == NULL)) { + return NULL; + } + + for (i = 0; i < cert->count; i++) { + element = nxt_conf_create_object(mp, 3); + if (nxt_slow_path(element == NULL)) { + return NULL; + } + + x509 = cert->chain[i]; + + value = nxt_cert_name_details(mp, x509, 0); + if (value == NULL) { + return NULL; + } + + nxt_conf_set_member(element, &subject_str, value, 0); + + value = nxt_cert_name_details(mp, x509, 1); + if (value == NULL) { + return NULL; + } + + nxt_conf_set_member(element, &issuer_str, value, 1); + + value = nxt_conf_create_object(mp, 2); + if (nxt_slow_path(value == NULL)) { + return NULL; + } + + bio = BIO_new(BIO_s_mem()); + if (nxt_slow_path(bio == NULL)) { + return NULL; + } + + asn1_time = X509_get_notBefore(x509); + + ret = ASN1_TIME_print(bio, asn1_time); + + if (nxt_fast_path(ret == 1)) { + str.length = BIO_get_mem_data(bio, &str.start); + ret = nxt_conf_set_member_string_dup(value, mp, &since_str, &str, + 0); + } else { + ret = NXT_ERROR; + } + + BIO_free(bio); + + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } + + bio = BIO_new(BIO_s_mem()); + if (nxt_slow_path(bio == NULL)) { + return NULL; + } + + asn1_time = X509_get_notAfter(x509); + + ret = ASN1_TIME_print(bio, asn1_time); + + if (nxt_fast_path(ret == 1)) { + str.length = BIO_get_mem_data(bio, &str.start); + ret = nxt_conf_set_member_string_dup(value, mp, &until_str, &str, + 1); + } else { + ret = NXT_ERROR; + } + + BIO_free(bio); + + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } + + nxt_conf_set_member(element, &validity_str, value, 2); + + nxt_conf_set_element(chain, i, element); + } + + nxt_conf_set_member(object, &chain_str, chain, 1); + + return object; +} + + +typedef struct { + int nid; + nxt_str_t name; +} nxt_cert_nid_t; + + +static nxt_conf_value_t * +nxt_cert_name_details(nxt_mp_t *mp, X509 *x509, nxt_bool_t issuer) +{ + int len; + X509_NAME *x509_name; + nxt_str_t str; + nxt_int_t ret; + nxt_uint_t i, n, count; + GENERAL_NAME *name; + nxt_conf_value_t *object, *names; + STACK_OF(GENERAL_NAME) *alt_names; + u_char buf[256]; + + static nxt_cert_nid_t nids[] = { + { NID_commonName, nxt_string("common_name") }, + { NID_countryName, nxt_string("country") }, + { NID_stateOrProvinceName, nxt_string("state_or_province") }, + { NID_localityName, nxt_string("locality") }, + { NID_organizationName, nxt_string("organization") }, + { NID_organizationalUnitName, nxt_string("department") }, + }; + + static nxt_str_t alt_names_str = nxt_string("alt_names"); + + count = 0; + + x509_name = issuer ? X509_get_issuer_name(x509) + : X509_get_subject_name(x509); + + for (n = 0; n != nxt_nitems(nids); n++) { + + if (X509_NAME_get_index_by_NID(x509_name, nids[n].nid, -1) < 0) { + continue; + } + + count++; + } + + alt_names = X509_get_ext_d2i(x509, issuer ? NID_issuer_alt_name + : NID_subject_alt_name, + NULL, NULL); + + if (alt_names != NULL) { + count++; + } + + object = nxt_conf_create_object(mp, count); + if (nxt_slow_path(object == NULL)) { + goto fail; + } + + for (n = 0, i = 0; n != nxt_nitems(nids) && i != count; n++) { + + len = X509_NAME_get_text_by_NID(x509_name, nids[n].nid, + (char *) buf, sizeof(buf)); + + if (len < 0) { + continue; + } + + if (i == 1 && alt_names != NULL) { + i++; + } + + str.length = len; + str.start = buf; + + ret = nxt_conf_set_member_string_dup(object, mp, &nids[n].name, + &str, i++); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + } + + if (alt_names != NULL) { + count = sk_GENERAL_NAME_num(alt_names); + + for (n = 0; n != count; n++) { + name = sk_GENERAL_NAME_value(alt_names, n); + + if (name->type != GEN_DNS) { + continue; + } + } + + names = nxt_conf_create_array(mp, n); + if (nxt_slow_path(names == NULL)) { + goto fail; + } + + for (n = 0, i = 0; n != count; n++) { + name = sk_GENERAL_NAME_value(alt_names, n); + + if (name->type != GEN_DNS) { + continue; + } + + str.length = ASN1_STRING_length(name->d.dNSName); +#if OPENSSL_VERSION_NUMBER > 0x10100000L + str.start = (u_char *) ASN1_STRING_get0_data(name->d.dNSName); +#else + str.start = ASN1_STRING_data(name->d.dNSName); +#endif + + ret = nxt_conf_set_element_string_dup(names, mp, i++, &str); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + } + + sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); + + nxt_conf_set_member(object, &alt_names_str, names, 1); + } + + return object; + +fail: + + if (alt_names != NULL) { + sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); + } + + return NULL; +} + + +nxt_int_t +nxt_cert_info_delete(nxt_str_t *name) +{ + nxt_int_t ret; + nxt_cert_info_t *info; + nxt_lvlhsh_query_t lhq; + + lhq.key_hash = nxt_djb_hash(name->start, name->length); + lhq.key = *name; + lhq.proto = &nxt_cert_info_hash_proto; + + ret = nxt_lvlhsh_delete(&nxt_cert_info, &lhq); + + if (ret == NXT_OK) { + info = lhq.value; + nxt_mp_destroy(info->mp); + } + + return ret; +} + + + +nxt_array_t * +nxt_cert_store_load(nxt_task_t *task) +{ + DIR *dir; + size_t size, alloc; + u_char *buf, *p; + nxt_mp_t *mp; + nxt_str_t name; + nxt_int_t ret; + nxt_file_t file; + nxt_array_t *certs; + nxt_runtime_t *rt; + struct dirent *de; + nxt_cert_item_t *item; + + rt = task->thread->runtime; + + if (nxt_slow_path(rt->certs.start == NULL)) { + nxt_alert(task, "no certificates storage directory"); + return NULL; + } + + mp = nxt_mp_create(1024, 128, 256, 32); + if (nxt_slow_path(mp == NULL)) { + return NULL; + } + + certs = nxt_array_create(mp, 16, sizeof(nxt_cert_item_t)); + if (nxt_slow_path(certs == NULL)) { + nxt_mp_destroy(mp); + return NULL; + } + + buf = NULL; + alloc = 0; + + dir = opendir((char *) rt->certs.start); + if (nxt_slow_path(dir == NULL)) { + nxt_alert(task, "opendir(\"%s\") failed %E", + rt->certs.start, nxt_errno); + goto fail; + } + + for ( ;; ) { + de = readdir(dir); + if (de == NULL) { + break; + } + + if (de->d_type != DT_REG) { + continue; + } + + item = nxt_array_add(certs); + if (nxt_slow_path(item == NULL)) { + goto fail; + } + + item->fd = -1; + + name.length = nxt_strlen(de->d_name); + name.start = (u_char *) de->d_name; + + size = rt->certs.length + name.length + 1; + + if (size > alloc) { + size += 32; + + p = nxt_realloc(buf, size); + if (p == NULL) { + goto fail; + } + + alloc = size; + buf = p; + } + + p = nxt_cpymem(buf, rt->certs.start, rt->certs.length); + p = nxt_cpymem(p, name.start, name.length + 1); + + nxt_memzero(&file, sizeof(nxt_file_t)); + + file.name = buf; + + ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, + NXT_FILE_OWNER_ACCESS); + + + if (nxt_slow_path(ret != NXT_OK)) { + nxt_array_remove_last(certs); + continue; + } + + item->fd = file.fd; + + if (nxt_slow_path(nxt_str_dup(mp, &item->name, &name) == NULL)) { + goto fail; + } + } + + if (buf != NULL) { + nxt_free(buf); + } + + (void) closedir(dir); + + return certs; + +fail: + + if (buf != NULL) { + nxt_free(buf); + } + + if (dir != NULL) { + (void) closedir(dir); + } + + nxt_cert_store_release(certs); + + return NULL; +} + + +void +nxt_cert_store_release(nxt_array_t *certs) +{ + uint32_t i; + nxt_cert_item_t *items; + + for (items = certs->elts, i = 0; + i < certs->nelts; + i++) + { + nxt_fd_close(items[i].fd); + } + + nxt_mp_destroy(certs->mem_pool); +} + + +#if 0 + +void +nxt_cert_store_discovery_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + DIR *dir; + size_t size; + nxt_buf_t *b; + nxt_int_t ret; + nxt_port_t *port; + nxt_runtime_t *rt; + struct dirent *de; + + port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid, + msg->port_msg.reply_port); + + if (nxt_slow_path(port == NULL)) { + return; + } + + b = NULL; + dir = NULL; + + rt = task->thread->runtime; + + if (nxt_slow_path(rt->certs.start == NULL)) { + nxt_alert(task, "no certificates storage directory"); + goto fail; + } + + dir = opendir((char *) rt->certs.start); + if (nxt_slow_path(dir == NULL)) { + nxt_alert(task, "opendir(\"%s\") failed %E", + rt->certs.start, nxt_errno); + goto fail; + } + + size = 0; + + for ( ;; ) { + de = readdir(dir); + if (de == NULL) { + break; + } + + if (de->d_type != DT_REG) { + continue; + } + + size += nxt_strlen(de->d_name) + 1; + } + + b = nxt_port_mmap_get_buf(task, port, size); + if (nxt_slow_path(b == NULL)) { + goto fail; + } + + rewinddir(dir); + + for ( ;; ) { + de = readdir(dir); + if (de == NULL) { + break; + } + + if (de->d_type != DT_REG) { + continue; + } + + size = nxt_strlen(de->d_name) + 1; + + if (nxt_slow_path(size > (size_t) nxt_buf_mem_free_size(&b->mem))) { + b->mem.free = b->mem.start; + break; + } + + b->mem.free = nxt_cpymem(b->mem.free, de->d_name, size); + } + + (void) closedir(dir); + dir = NULL; + + if (nxt_slow_path(nxt_buf_mem_free_size(&b->mem) != 0)) { + nxt_alert(task, "certificates storage directory " + "has changed while reading it"); + goto fail; + } + + ret = nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_READY_LAST, -1, + msg->port_msg.stream, 0, b); + + if (nxt_fast_path(ret == NXT_OK)) { + return; + } + +fail: + + if (dir != NULL) { + (void) closedir(dir); + } + + if (b != NULL) { + b->completion_handler(task, b, b->parent); + } + + (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, -1, + msg->port_msg.stream, 0, NULL); +} + +#endif + + +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) +{ + uint32_t stream; + nxt_int_t ret; + nxt_buf_t *b; + nxt_port_t *main_port, *recv_port; + nxt_runtime_t *rt; + + b = nxt_buf_mem_alloc(mp, name->length + 1, 0); + if (nxt_slow_path(b == NULL)) { + goto fail; + } + + nxt_buf_cpystr(b, name); + *b->mem.free++ = '\0'; + + rt = task->thread->runtime; + main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + recv_port = rt->port_by_type[rt->type]; + + stream = nxt_port_rpc_register_handler(task, recv_port, handler, handler, + -1, ctx); + if (nxt_slow_path(stream == 0)) { + goto fail; + } + + ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_CERT_GET, -1, + stream, recv_port->id, b); + + if (nxt_slow_path(ret != NXT_OK)) { + nxt_port_rpc_cancel(task, recv_port, stream); + goto fail; + } + + return; + +fail: + + handler(task, NULL, ctx); +} + + +void +nxt_cert_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + u_char *p; + nxt_int_t ret; + nxt_str_t name; + nxt_file_t file; + nxt_port_t *port; + nxt_runtime_t *rt; + nxt_port_msg_type_t type; + + port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid, + msg->port_msg.reply_port); + + if (port == NULL) { + return; + } + + nxt_memzero(&file, sizeof(nxt_file_t)); + + file.fd = -1; + type = NXT_PORT_MSG_RPC_ERROR; + + rt = task->thread->runtime; + + if (nxt_slow_path(rt->certs.start == NULL)) { + nxt_alert(task, "no certificates storage directory"); + goto error; + } + + name.start = msg->buf->mem.pos; + name.length = nxt_strlen(name.start); + + file.name = nxt_malloc(rt->certs.length + name.length + 1); + + if (nxt_slow_path(file.name == NULL)) { + goto error; + } + + p = nxt_cpymem(file.name, rt->certs.start, rt->certs.length); + p = nxt_cpymem(p, name.start, name.length + 1); + + ret = nxt_file_open(task, &file, NXT_FILE_RDWR, NXT_FILE_CREATE_OR_OPEN, + NXT_FILE_OWNER_ACCESS); + + nxt_free(file.name); + + if (nxt_fast_path(ret == NXT_OK)) { + type = NXT_PORT_MSG_RPC_READY_LAST | NXT_PORT_MSG_CLOSE_FD; + } + +error: + + (void) nxt_port_socket_write(task, port, type, file.fd, + msg->port_msg.stream, 0, NULL); +} + + +void +nxt_cert_store_delete(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp) +{ + nxt_buf_t *b; + nxt_port_t *main_port; + nxt_runtime_t *rt; + + b = nxt_buf_mem_alloc(mp, name->length + 1, 0); + + if (nxt_fast_path(b != NULL)) { + nxt_buf_cpystr(b, name); + *b->mem.free++ = '\0'; + + rt = task->thread->runtime; + main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + + (void) nxt_port_socket_write(task, main_port, NXT_PORT_MSG_CERT_DELETE, + -1, 0, 0, b); + } +} + + +void +nxt_cert_store_delete_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + u_char *p; + nxt_str_t name; + nxt_runtime_t *rt; + nxt_file_name_t *path; + + rt = task->thread->runtime; + + if (nxt_slow_path(rt->certs.start == NULL)) { + nxt_alert(task, "no certificates storage directory"); + return; + } + + name.start = msg->buf->mem.pos; + name.length = nxt_strlen(name.start); + + path = nxt_malloc(rt->certs.length + name.length + 1); + + if (nxt_fast_path(path != NULL)) { + p = nxt_cpymem(path, rt->certs.start, rt->certs.length); + p = nxt_cpymem(p, name.start, name.length + 1); + + (void) nxt_file_delete(path); + + nxt_free(path); + } +} |