QUIC: the "quic_host_key" directive.

The token generation in QUIC is reworked. Single host key is used to generate
all required keys of needed sizes using HKDF.

The "quic_stateless_reset_token_key" directive is removed.  Instead, the
"quic_host_key" directive is used, which reads key from file, or sets it
to random bytes if not specified.
This commit is contained in:
Vladimir Homutov 2021-02-08 16:49:33 +03:00
parent 040a23bfc3
commit eab61bfc22
6 changed files with 345 additions and 87 deletions

View File

@ -225,6 +225,8 @@ static ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c,
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_send_stateless_reset(ngx_connection_t *c,
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid,
u_char *secret, u_char *token);
static ngx_int_t ngx_quic_process_stateless_reset(ngx_connection_t *c,
ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_negotiate_version(ngx_connection_t *c,
@ -1245,7 +1247,7 @@ ngx_quic_send_stateless_reset(ngx_connection_t *c, ngx_quic_conf_t *conf,
token = &buf[len - NGX_QUIC_SR_TOKEN_LEN];
if (ngx_quic_new_sr_token(c, &pkt->dcid, &conf->sr_token_key, token)
if (ngx_quic_new_sr_token(c, &pkt->dcid, conf->sr_token_key, token)
!= NGX_OK)
{
return NGX_ERROR;
@ -1257,6 +1259,32 @@ ngx_quic_send_stateless_reset(ngx_connection_t *c, ngx_quic_conf_t *conf,
}
static ngx_int_t
ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret,
u_char *token)
{
ngx_str_t tmp;
tmp.data = secret;
tmp.len = NGX_QUIC_SR_KEY_LEN;
if (ngx_quic_derive_key(c->log, "sr_token_key", &tmp, cid, token,
NGX_QUIC_SR_TOKEN_LEN)
!= NGX_OK)
{
return NGX_ERROR;
}
#if (NGX_DEBUG)
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic stateless reset token %*xs",
(size_t) NGX_QUIC_SR_TOKEN_LEN, token);
#endif
return NGX_OK;
}
static ngx_int_t
ngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt)
{
@ -1391,9 +1419,10 @@ ngx_quic_send_retry(ngx_connection_t *c, ngx_quic_conf_t *conf,
u_char buf[NGX_QUIC_RETRY_BUFFER_SIZE];
u_char dcid[NGX_QUIC_SERVER_CID_LEN];
expires = ngx_time() + NGX_QUIC_RETRY_LIFETIME;
expires = ngx_time() + NGX_QUIC_RETRY_TOKEN_LIFETIME;
if (ngx_quic_new_token(c, conf->token_key, &token, &inpkt->dcid, expires, 1)
if (ngx_quic_new_token(c, conf->av_token_key, &token, &inpkt->dcid,
expires, 1)
!= NGX_OK)
{
return NGX_ERROR;
@ -1723,7 +1752,7 @@ ngx_quic_init_connection(ngx_connection_t *c)
}
#endif
if (ngx_quic_new_sr_token(c, &qc->dcid, &qc->conf->sr_token_key,
if (ngx_quic_new_sr_token(c, &qc->dcid, qc->conf->sr_token_key,
qc->tp.sr_token)
!= NGX_OK)
{
@ -2235,7 +2264,7 @@ ngx_quic_process_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
if (pkt->level == ssl_encryption_initial) {
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic token len:%uz %xV",
"quic address validation token len:%uz %xV",
pkt->token.len, &pkt->token);
}
#endif
@ -2306,7 +2335,7 @@ ngx_quic_process_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
if (pkt->token.len) {
rc = ngx_quic_validate_token(c, conf->token_key, pkt);
rc = ngx_quic_validate_token(c, conf->av_token_key, pkt);
if (rc == NGX_ERROR) {
/* internal error */
@ -2321,10 +2350,10 @@ ngx_quic_process_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
/* token is invalid */
if (pkt->retried) {
/* invalid Retry token */
/* invalid address validation token */
return ngx_quic_send_early_cc(c, pkt,
NGX_QUIC_ERR_INVALID_TOKEN,
"invalid token");
"invalid address validation token");
} else if (conf->retry) {
/* invalid NEW_TOKEN */
return ngx_quic_send_retry(c, conf, pkt);
@ -3286,7 +3315,7 @@ ngx_quic_send_new_token(ngx_connection_t *c)
expires = ngx_time() + NGX_QUIC_NEW_TOKEN_LIFETIME;
if (ngx_quic_new_token(c, qc->conf->token_key, &token, NULL, expires, 0)
if (ngx_quic_new_token(c, qc->conf->av_token_key, &token, NULL, expires, 0)
!= NGX_OK)
{
return NGX_ERROR;
@ -4667,7 +4696,7 @@ ngx_quic_issue_server_ids(ngx_connection_t *c)
frame->u.ncid.len = NGX_QUIC_SERVER_CID_LEN;
ngx_memcpy(frame->u.ncid.cid, id, NGX_QUIC_SERVER_CID_LEN);
if (ngx_quic_new_sr_token(c, &dcid, &qc->conf->sr_token_key,
if (ngx_quic_new_sr_token(c, &dcid, qc->conf->sr_token_key,
frame->u.ncid.srt)
!= NGX_OK)
{

View File

@ -27,9 +27,11 @@
#define NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT 3
#define NGX_QUIC_DEFAULT_MAX_ACK_DELAY 25
#define NGX_QUIC_DEFAULT_SRT_KEY_LEN 32
#define NGX_QUIC_DEFAULT_HOST_KEY_LEN 32
#define NGX_QUIC_SR_KEY_LEN 32
#define NGX_QUIC_AV_KEY_LEN 32
#define NGX_QUIC_RETRY_LIFETIME 3 /* seconds */
#define NGX_QUIC_RETRY_TOKEN_LIFETIME 3 /* seconds */
#define NGX_QUIC_NEW_TOKEN_LIFETIME 600 /* seconds */
#define NGX_QUIC_RETRY_BUFFER_SIZE 256
/* 1 flags + 4 version + 3 x (1 + 20) s/o/dcid + itag + token(64) */
@ -96,8 +98,9 @@ typedef struct {
ngx_quic_tp_t tp;
ngx_flag_t retry;
ngx_flag_t require_alpn;
u_char token_key[32]; /* AES 256 */
ngx_str_t sr_token_key; /* stateless reset token key */
ngx_str_t host_key;
u_char av_token_key[NGX_QUIC_AV_KEY_LEN];
u_char sr_token_key[NGX_QUIC_SR_KEY_LEN];
} ngx_quic_conf_t;

View File

@ -942,57 +942,48 @@ ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res)
ngx_int_t
ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, ngx_str_t *secret,
u_char *token)
ngx_quic_derive_key(ngx_log_t *log, const char *label, ngx_str_t *secret,
ngx_str_t *salt, u_char *out, size_t len)
{
size_t is_len, info_len;
uint8_t *p;
size_t is_len, key_len, info_len;
ngx_str_t label;
const EVP_MD *digest;
uint8_t info[20];
uint8_t is[SHA256_DIGEST_LENGTH];
uint8_t key[SHA256_DIGEST_LENGTH];
/* 10.4.2. Calculating a Stateless Reset Token */
uint8_t is[SHA256_DIGEST_LENGTH];
uint8_t info[20];
digest = EVP_sha256();
ngx_str_set(&label, "sr_token_key");
if (ngx_hkdf_extract(is, &is_len, digest, secret->data, secret->len,
cid->data, cid->len)
salt->data, salt->len)
!= NGX_OK)
{
ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
"ngx_hkdf_extract(%V) failed", &label);
ngx_ssl_error(NGX_LOG_INFO, log, 0,
"ngx_hkdf_extract(%s) failed", label);
return NGX_ERROR;
}
key_len = SHA256_DIGEST_LENGTH;
info_len = 2 + 1 + label.len + 1;
info[0] = 0;
info[1] = key_len;
info[2] = label.len;
info[1] = len;
info[2] = ngx_strlen(label);
p = ngx_cpymem(&info[3], label.data, label.len);
*p = '\0';
info_len = 2 + 1 + info[2] + 1;
if (ngx_hkdf_expand(key, key_len, digest, is, is_len, info, info_len)
!= NGX_OK)
{
ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
"ngx_hkdf_expand(%V) failed", &label);
if (info_len >= 20) {
ngx_log_error(NGX_LOG_INFO, log, 0,
"ngx_quic_create_key label \"%s\" too long", label);
return NGX_ERROR;
}
ngx_memcpy(token, key, NGX_QUIC_SR_TOKEN_LEN);
p = ngx_cpymem(&info[3], label, info[2]);
*p = '\0';
#if (NGX_DEBUG)
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic stateless reset token %*xs",
(size_t) NGX_QUIC_SR_TOKEN_LEN, token);
#endif
if (ngx_hkdf_expand(out, len, digest, is, is_len, info, info_len) != NGX_OK)
{
ngx_ssl_error(NGX_LOG_INFO, log, 0,
"ngx_hkdf_expand(%s) failed", label);
return NGX_ERROR;
}
return NGX_OK;
}

View File

@ -11,6 +11,8 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event_quic_transport.h>
#define NGX_QUIC_ENCRYPTION_LAST ((ssl_encryption_application) + 1)
@ -27,10 +29,8 @@ void ngx_quic_keys_discard(ngx_quic_keys_t *keys,
enum ssl_encryption_level_t level);
void ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys);
ngx_int_t ngx_quic_keys_update(ngx_connection_t *c, ngx_quic_keys_t *keys);
ngx_int_t ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid,
ngx_str_t *key, u_char *token);
ngx_int_t ngx_quic_derive_key(ngx_log_t *log, const char *label,
ngx_str_t *secret, ngx_str_t *salt, u_char *out, size_t len);
ngx_int_t ngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_str_t *res);
ngx_int_t ngx_quic_decrypt(ngx_quic_header_t *pkt, uint64_t *largest_pn);

View File

@ -9,6 +9,8 @@
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_event_quic_protection.h>
static ngx_int_t ngx_http_variable_quic(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
@ -20,7 +22,8 @@ static char *ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post,
void *data);
static char *ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post,
void *data);
static char *ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_conf_post_t ngx_http_quic_max_ack_delay_post =
{ ngx_http_quic_max_ack_delay };
@ -125,11 +128,11 @@ static ngx_command_t ngx_http_quic_commands[] = {
offsetof(ngx_quic_conf_t, retry),
NULL },
{ ngx_string("quic_stateless_reset_token_key"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_str_slot,
{ ngx_string("quic_host_key"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_http_quic_host_key,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_quic_conf_t, sr_token_key),
0,
NULL },
ngx_null_command
@ -174,6 +177,8 @@ static ngx_http_variable_t ngx_http_quic_vars[] = {
ngx_http_null_variable
};
static ngx_str_t ngx_http_quic_salt = ngx_string("ngx_quic");
ngx_int_t
ngx_http_quic_init(ngx_connection_t *c)
@ -270,7 +275,7 @@ ngx_http_quic_create_srv_conf(ngx_conf_t *cf)
* conf->tp.sr_token = { 0 }
* conf->tp.sr_enabled = 0
* conf->tp.preferred_address = NULL
* conf->sr_token_key = { 0, NULL }
* conf->host_key = { 0, NULL }
*/
conf->tp.max_idle_timeout = NGX_CONF_UNSET_MSEC;
@ -346,23 +351,37 @@ ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->retry, prev->retry, 0);
if (RAND_bytes(conf->token_key, sizeof(conf->token_key)) <= 0) {
ngx_conf_merge_str_value(conf->host_key, prev->host_key, "");
if (conf->host_key.len == 0) {
conf->host_key.len = NGX_QUIC_DEFAULT_HOST_KEY_LEN;
conf->host_key.data = ngx_palloc(cf->pool, conf->host_key.len);
if (conf->host_key.data == NULL) {
return NGX_CONF_ERROR;
}
if (RAND_bytes(conf->host_key.data, NGX_QUIC_DEFAULT_HOST_KEY_LEN)
<= 0)
{
return NGX_CONF_ERROR;
}
}
if (ngx_quic_derive_key(cf->log, "av_token_key",
&conf->host_key, &ngx_http_quic_salt,
conf->av_token_key, NGX_QUIC_AV_KEY_LEN)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
ngx_conf_merge_str_value(conf->sr_token_key, prev->sr_token_key, "");
if (conf->sr_token_key.len == 0) {
conf->sr_token_key.len = NGX_QUIC_DEFAULT_SRT_KEY_LEN;
conf->sr_token_key.data = ngx_pnalloc(cf->pool, conf->sr_token_key.len);
if (conf->sr_token_key.data == NULL) {
return NGX_CONF_ERROR;
}
if (RAND_bytes(conf->sr_token_key.data, conf->sr_token_key.len) <= 0) {
return NGX_CONF_ERROR;
}
if (ngx_quic_derive_key(cf->log, "sr_token_key",
&conf->host_key, &ngx_http_quic_salt,
conf->sr_token_key, NGX_QUIC_SR_KEY_LEN)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module);
@ -407,3 +426,101 @@ ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data)
return NGX_CONF_OK;
}
static char *
ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_quic_conf_t *qcf = conf;
u_char *buf;
size_t size;
ssize_t n;
ngx_str_t *value;
ngx_file_t file;
ngx_file_info_t fi;
if (qcf->host_key.len) {
return "is duplicate";
}
buf = NULL;
#if (NGX_SUPPRESS_WARN)
size = 0;
#endif
value = cf->args->elts;
if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) {
return NGX_CONF_ERROR;
}
ngx_memzero(&file, sizeof(ngx_file_t));
file.name = value[1];
file.log = cf->log;
file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (file.fd == NGX_INVALID_FILE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
ngx_open_file_n " \"%V\" failed", &file.name);
return NGX_CONF_ERROR;
}
if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
ngx_fd_info_n " \"%V\" failed", &file.name);
goto failed;
}
size = ngx_file_size(&fi);
if (size == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%V\" zero key size", &file.name);
goto failed;
}
buf = ngx_pnalloc(cf->pool, size);
if (buf == NULL) {
goto failed;
}
n = ngx_read_file(&file, buf, size, 0);
if (n == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
ngx_read_file_n " \"%V\" failed", &file.name);
goto failed;
}
if ((size_t) n != size) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
ngx_read_file_n " \"%V\" returned only "
"%z bytes instead of %uz", &file.name, n, size);
goto failed;
}
qcf->host_key.data = buf;
qcf->host_key.len = n;
if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " \"%V\" failed", &file.name);
}
return NGX_CONF_OK;
failed:
if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " \"%V\" failed", &file.name);
}
if (buf) {
ngx_explicit_memzero(buf, size);
}
return NGX_CONF_ERROR;
}

View File

@ -9,6 +9,8 @@
#include <ngx_core.h>
#include <ngx_stream.h>
#include <ngx_event_quic_protection.h>
static ngx_int_t ngx_stream_variable_quic(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data);
@ -20,6 +22,8 @@ static char *ngx_stream_quic_max_ack_delay(ngx_conf_t *cf, void *post,
void *data);
static char *ngx_stream_quic_max_udp_payload_size(ngx_conf_t *cf, void *post,
void *data);
static char *ngx_stream_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_conf_post_t ngx_stream_quic_max_ack_delay_post =
@ -126,11 +130,11 @@ static ngx_command_t ngx_stream_quic_commands[] = {
offsetof(ngx_quic_conf_t, retry),
NULL },
{ ngx_string("quic_stateless_reset_token_key"),
{ ngx_string("quic_host_key"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_str_slot,
ngx_stream_quic_host_key,
NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_quic_conf_t, sr_token_key),
0,
NULL },
ngx_null_command
@ -172,6 +176,8 @@ static ngx_stream_variable_t ngx_stream_quic_vars[] = {
ngx_stream_null_variable
};
static ngx_str_t ngx_stream_quic_salt = ngx_string("ngx_quic");
static ngx_int_t
ngx_stream_variable_quic(ngx_stream_session_t *s,
@ -229,7 +235,7 @@ ngx_stream_quic_create_srv_conf(ngx_conf_t *cf)
* conf->tp.initial_scid = { 0, NULL };
* conf->tp.retry_scid = { 0, NULL };
* conf->tp.preferred_address = NULL
* conf->sr_token_key = { 0, NULL }
* conf->host_key = { 0, NULL }
* conf->require_alpn = 0;
*/
@ -305,23 +311,37 @@ ngx_stream_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->retry, prev->retry, 0);
if (RAND_bytes(conf->token_key, sizeof(conf->token_key)) <= 0) {
ngx_conf_merge_str_value(conf->host_key, prev->host_key, "");
if (conf->host_key.len == 0) {
conf->host_key.len = NGX_QUIC_DEFAULT_HOST_KEY_LEN;
conf->host_key.data = ngx_palloc(cf->pool, conf->host_key.len);
if (conf->host_key.data == NULL) {
return NGX_CONF_ERROR;
}
if (RAND_bytes(conf->host_key.data, NGX_QUIC_DEFAULT_HOST_KEY_LEN)
<= 0)
{
return NGX_CONF_ERROR;
}
}
if (ngx_quic_derive_key(cf->log, "av_token_key",
&conf->host_key, &ngx_stream_quic_salt,
conf->av_token_key, NGX_QUIC_AV_KEY_LEN)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
ngx_conf_merge_str_value(conf->sr_token_key, prev->sr_token_key, "");
if (conf->sr_token_key.len == 0) {
conf->sr_token_key.len = NGX_QUIC_DEFAULT_SRT_KEY_LEN;
conf->sr_token_key.data = ngx_pnalloc(cf->pool, conf->sr_token_key.len);
if (conf->sr_token_key.data == NULL) {
return NGX_CONF_ERROR;
}
if (RAND_bytes(conf->sr_token_key.data, conf->sr_token_key.len) <= 0) {
return NGX_CONF_ERROR;
}
if (ngx_quic_derive_key(cf->log, "sr_token_key",
&conf->host_key, &ngx_stream_quic_salt,
conf->sr_token_key, NGX_QUIC_SR_KEY_LEN)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
scf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_ssl_module);
@ -366,3 +386,101 @@ ngx_stream_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data)
return NGX_CONF_OK;
}
static char *
ngx_stream_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_quic_conf_t *qcf = conf;
u_char *buf;
size_t size;
ssize_t n;
ngx_str_t *value;
ngx_file_t file;
ngx_file_info_t fi;
if (qcf->host_key.len) {
return "is duplicate";
}
buf = NULL;
#if (NGX_SUPPRESS_WARN)
size = 0;
#endif
value = cf->args->elts;
if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) {
return NGX_CONF_ERROR;
}
ngx_memzero(&file, sizeof(ngx_file_t));
file.name = value[1];
file.log = cf->log;
file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (file.fd == NGX_INVALID_FILE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
ngx_open_file_n " \"%V\" failed", &file.name);
return NGX_CONF_ERROR;
}
if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
ngx_fd_info_n " \"%V\" failed", &file.name);
goto failed;
}
size = ngx_file_size(&fi);
if (size == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%V\" zero key size", &file.name);
goto failed;
}
buf = ngx_pnalloc(cf->pool, size);
if (buf == NULL) {
goto failed;
}
n = ngx_read_file(&file, buf, size, 0);
if (n == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
ngx_read_file_n " \"%V\" failed", &file.name);
goto failed;
}
if ((size_t) n != size) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
ngx_read_file_n " \"%V\" returned only "
"%z bytes instead of %uz", &file.name, n, size);
goto failed;
}
qcf->host_key.data = buf;
qcf->host_key.len = n;
if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " \"%V\" failed", &file.name);
}
return NGX_CONF_OK;
failed:
if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " \"%V\" failed", &file.name);
}
if (buf) {
ngx_explicit_memzero(buf, size);
}
return NGX_CONF_ERROR;
}