Files
nginx-unit/src/nxt_cert.c
Tiago Natel de Moura e9e5ddd5a5 Refactor of process management.
The process abstraction has changed to:

  setup(task, process)
  start(task, process_data)
  prefork(task, process, mp)

The prefork() occurs in the main process right before fork.

The file src/nxt_main_process.c is completely free of process
specific logic.

The creation of a process now supports a PROCESS_CREATED state.  The
The setup() function of each process can set its state to either
created or ready.  If created, a MSG_PROCESS_CREATED is sent to main
process, where external setup can be done (required for rootfs under
container).

The core processes (discovery, controller and router) doesn't need
external setup, then they all proceeds to their start() function
straight away.

In the case of applications, the load of the module happens at the
process setup() time and The module's init() function has changed
to be the start() of the process.

The module API has changed to:

  setup(task, process, conf)
  start(task, data)

As a direct benefit of the PROCESS_CREATED message, the clone(2) of
processes using pid namespaces now doesn't need to create a pipe
to make the child block until parent setup uid/gid mappings nor it
needs to receive the child pid.
2020-03-09 16:28:25 +00:00

1198 lines
27 KiB
C

/*
* 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, nxt_mp_t *mp)
{
DIR *dir;
size_t size, alloc;
u_char *buf, *p;
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;
}
certs = nxt_array_create(mp, 16, sizeof(nxt_cert_item_t));
if (nxt_slow_path(certs == NULL)) {
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_array_destroy(certs);
}
#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);
}
}