[PATCH 2 of 3] PSK: add configuration directives
Karstens, Nate
Nate.Karstens at garmin.com
Thu Jun 1 17:21:18 UTC 2017
# HG changeset patch
# User Nate Karstens <nate.karstens at garmin.com>
# Date 1496332865 18000
# Thu Jun 01 11:01:05 2017 -0500
# Node ID 7aa7771191d61ef635478460017446bca1f6db55
# Parent a38066b79d71b6ecb62a9f7618afe2cf3ed8a4f9
PSK: add configuration directives
Adds the directive "ssl_psk_dir" to the ngx_http_ssl_module. This
allows the user to specify a folder that contains the set of PSKs
that can be used to form a connection. Each key in the directory
is read into memory and the file name is saved as the key identity.
Also added the "ssl_psk_hash_max_size" and "ssl_psk_hash_bucket_size"
directives to configure the hash that stores PSK data.
This functionality can be easily tested with the OpenSSL s_client
using the "-psk" and "-psk_identity" options.
Signed-off-by: Nate Karstens <nate.karstens at garmin.com>
diff -r a38066b79d71 -r 7aa7771191d6 contrib/vim/syntax/nginx.vim
--- a/contrib/vim/syntax/nginx.vim Thu Jun 01 10:55:04 2017 -0500
+++ b/contrib/vim/syntax/nginx.vim Thu Jun 01 11:01:05 2017 -0500
@@ -551,6 +551,9 @@
syn keyword ngxDirective contained ssl_prefer_server_ciphers
syn keyword ngxDirective contained ssl_preread
syn keyword ngxDirective contained ssl_protocols
+syn keyword ngxDirective contained ssl_psk_path
+syn keyword ngxDirective contained ssl_psk_hash_bucket_size
+syn keyword ngxDirective contained ssl_psk_hash_max_size
syn keyword ngxDirective contained ssl_session_cache
syn keyword ngxDirective contained ssl_session_ticket_key
syn keyword ngxDirective contained ssl_session_tickets
diff -r a38066b79d71 -r 7aa7771191d6 src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c Thu Jun 01 10:55:04 2017 -0500
+++ b/src/event/ngx_event_openssl.c Thu Jun 01 11:01:05 2017 -0500
@@ -23,6 +23,8 @@
static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
int ret);
+static unsigned int ngx_ssl_psk_callback(SSL *ssl, const char *identity,
+ unsigned char *psk, unsigned int max_psk_len);
static void ngx_ssl_passwords_cleanup(void *data);
static void ngx_ssl_handshake_handler(ngx_event_t *ev);
static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n);
@@ -114,6 +116,7 @@
int ngx_ssl_next_certificate_index;
int ngx_ssl_certificate_name_index;
int ngx_ssl_stapling_index;
+int ngx_ssl_psk_index;
ngx_int_t
@@ -225,6 +228,13 @@
return NGX_ERROR;
}
+ ngx_ssl_psk_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+
+ if (ngx_ssl_psk_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed");
+ return NGX_ERROR;
+ }
+
return NGX_OK;
}
@@ -345,6 +355,7 @@
SSL_CTX_set_read_ahead(ssl->ctx, 1);
SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback);
+ SSL_CTX_set_psk_server_callback(ssl->ctx, ngx_ssl_psk_callback);
return NGX_OK;
}
@@ -875,6 +886,40 @@
}
+static unsigned int ngx_ssl_psk_callback(SSL *ssl, const char *identity,
+ unsigned char *psk, unsigned int max_psk_len)
+{
+ SSL_CTX *ssl_ctx;
+ size_t identity_len;
+ ngx_hash_t *psk_hash;
+ ngx_uint_t key;
+ ngx_str_t *psk_str;
+
+ ssl_ctx = SSL_get_SSL_CTX(ssl);
+ identity_len = strlen(identity);
+
+ psk_hash = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_psk_index);
+ if (psk_hash == NULL) {
+ return 0;
+ }
+
+ key = ngx_hash_key((u_char *)identity, identity_len);
+
+ psk_str = ngx_hash_find(psk_hash, key, (u_char *)identity, identity_len);
+ if (psk_str == NULL) {
+ return 0;
+ }
+
+ if (psk_str->len > max_psk_len) {
+ return 0;
+ }
+
+ ngx_memcpy(psk, psk_str->data, psk_str->len);
+
+ return psk_str->len;
+}
+
+
RSA *
ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,
int key_length)
@@ -3137,6 +3182,158 @@
#endif
+ngx_int_t
+ngx_ssl_psk_path(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *path,
+ ngx_uint_t psk_hash_max_size, ngx_uint_t psk_hash_bucket_size)
+
+{
+ ngx_err_t err;
+ ngx_uint_t level;
+ ngx_dir_t dir;
+ ngx_file_t file;
+ ssize_t read;
+ ngx_str_t *psk_str;
+ ngx_hash_keys_arrays_t psk_keys;
+ ngx_str_t key;
+ ngx_hash_t *psk_hash;
+ ngx_hash_init_t hash;
+
+ if (path->len == 0) {
+ return NGX_OK;
+ }
+
+ psk_keys.pool = cf->pool;
+ psk_keys.temp_pool = cf->temp_pool;
+
+ ngx_hash_keys_array_init(&psk_keys, NGX_HASH_SMALL);
+
+ if (ngx_open_dir(path, &dir) == NGX_ERROR) {
+ err = ngx_errno;
+
+ level = (err == NGX_ENOENT
+ || err == NGX_ENOTDIR
+ || err == NGX_ENAMETOOLONG
+ || err == NGX_EACCES) ? NGX_LOG_ERR : NGX_LOG_CRIT;
+
+ ngx_ssl_error(level, ssl->log, err,
+ ngx_open_dir_n " \"%s\" failed", path->data);
+
+ return NGX_ERROR;
+ }
+
+ for ( ;; ) {
+ ngx_set_errno(0);
+
+ if (ngx_read_dir(&dir) == NGX_ERROR) {
+ err = ngx_errno;
+
+ if (err == NGX_ENOMOREFILES) {
+ break;
+ }
+
+ ngx_ssl_error(NGX_LOG_CRIT, ssl->log, err,
+ ngx_read_dir_n " \"%V\" failed", &path);
+ return NGX_ERROR;
+ }
+
+ if (!ngx_de_is_file(&dir)) {
+ continue;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ssl->log, 0,
+ "loading PSK from file: \"%s\"", ngx_de_name(&dir));
+
+ file.name.len = ngx_de_namelen(&dir) + 2;
+ file.name.data = ngx_pnalloc(cf->pool, file.name.len);
+ if (file.name.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_sprintf(file.name.data, "%V/%s%Z", path, ngx_de_name(&dir));
+
+ file.log = ssl->log;
+
+ file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, 0, 0);
+ if (file.fd == NGX_INVALID_FILE) {
+ err = ngx_errno;
+ ngx_ssl_error(NGX_LOG_CRIT, ssl->log, err,
+ ngx_open_file_n " \"%s\" failed", file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (ngx_fd_info(file.fd, &file.info) == NGX_FILE_ERROR) {
+ ngx_ssl_error(NGX_LOG_CRIT, ssl->log, ngx_errno,
+ ngx_fd_info_n " \"%s\" failed", file.name.data);
+ return NGX_ERROR;
+ }
+
+ psk_str = ngx_palloc(cf->pool, sizeof(ngx_str_t));
+ if (psk_str == NULL) {
+ return NGX_ERROR;
+ }
+
+ psk_str->len = file.info.st_size;
+ psk_str->data = ngx_pnalloc(cf->pool, psk_str->len);
+
+ read = ngx_read_file(&file, psk_str->data, psk_str->len, 0);
+ if (read == NGX_ERROR) {
+ ngx_ssl_error(NGX_LOG_CRIT, ssl->log, ngx_errno,
+ ngx_read_file_n " \"%s\" failed", file.name.data);
+ return NGX_ERROR;
+ }
+
+ if ((size_t)read != psk_str->len) {
+ ngx_ssl_error(NGX_LOG_CRIT, ssl->log, 0,
+ ngx_read_file_n
+ " \"%s\" returned only %z bytes instead of %z",
+ file.name.data, read, psk_str->len);
+ return NGX_ERROR;
+ }
+
+ if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+ ngx_ssl_error(NGX_LOG_ALERT, ssl->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", file.name.data);
+ }
+
+ ngx_pfree(cf->pool, file.name.data);
+
+ key.data = ngx_de_name(&dir);
+ key.len = ngx_de_namelen(&dir);
+
+ ngx_hash_add_key(&psk_keys, &key, psk_str, NGX_HASH_READONLY_KEY);
+ }
+
+ if (ngx_close_dir(&dir) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, ssl->log, ngx_errno,
+ ngx_close_dir_n " \"%s\" failed", path->data);
+ }
+
+ psk_hash = ngx_palloc(cf->pool, sizeof(ngx_hash_t));
+
+ hash.hash = psk_hash;
+ hash.key = ngx_hash_key;
+ hash.max_size = psk_hash_max_size;
+ hash.bucket_size = psk_hash_bucket_size;
+ hash.name = "psk_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = cf->temp_pool;
+
+ if (ngx_hash_init(&hash, psk_keys.keys.elts, psk_keys.keys.nelts)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_psk_index, psk_hash) == 0) {
+ ngx_ssl_error(NGX_LOG_ALERT, ssl->log, 0,
+ "SSL_CTX_set_ex_data() failed");
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
void
ngx_ssl_cleanup_ctx(void *data)
{
diff -r a38066b79d71 -r 7aa7771191d6 src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h Thu Jun 01 10:55:04 2017 -0500
+++ b/src/event/ngx_event_openssl.h Thu Jun 01 11:01:05 2017 -0500
@@ -171,6 +171,9 @@
ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout);
ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_array_t *paths);
+ngx_int_t ngx_ssl_psk_path(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *path,
+ ngx_uint_t psk_hash_max_size,
+ ngx_uint_t psk_hash_bucket_size);
ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
ngx_uint_t flags);
@@ -255,6 +258,7 @@
extern int ngx_ssl_next_certificate_index;
extern int ngx_ssl_certificate_name_index;
extern int ngx_ssl_stapling_index;
+extern int ngx_ssl_psk_index;
#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */
diff -r a38066b79d71 -r 7aa7771191d6 src/http/modules/ngx_http_ssl_module.c
--- a/src/http/modules/ngx_http_ssl_module.c Thu Jun 01 10:55:04 2017 -0500
+++ b/src/http/modules/ngx_http_ssl_module.c Thu Jun 01 11:01:05 2017 -0500
@@ -240,6 +240,27 @@
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, stapling_verify),
NULL },
+
+ { ngx_string("ssl_psk_path"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, psk_path),
+ NULL },
+
+ { ngx_string("ssl_psk_hash_max_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, psk_hash_max_size),
+ NULL },
+
+ { ngx_string("ssl_psk_hash_bucket_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, psk_hash_bucket_size),
+ NULL },
ngx_null_command
};
@@ -546,6 +567,7 @@
* sscf->shm_zone = NULL;
* sscf->stapling_file = { 0, NULL };
* sscf->stapling_responder = { 0, NULL };
+ * sscf->psk_path = { 0, NULL };
*/
sscf->enable = NGX_CONF_UNSET;
@@ -563,6 +585,8 @@
sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;
sscf->stapling = NGX_CONF_UNSET;
sscf->stapling_verify = NGX_CONF_UNSET;
+ sscf->psk_hash_max_size = NGX_CONF_UNSET_UINT;
+ sscf->psk_hash_bucket_size = NGX_CONF_UNSET_UINT;
return sscf;
}
@@ -629,6 +653,14 @@
ngx_conf_merge_str_value(conf->stapling_responder,
prev->stapling_responder, "");
+ ngx_conf_merge_str_value(conf->psk_path, prev->psk_path, "");
+ ngx_conf_merge_uint_value(conf->psk_hash_max_size,
+ prev->psk_hash_max_size, 512);
+ ngx_conf_merge_uint_value(conf->psk_hash_bucket_size,
+ prev->psk_hash_bucket_size, 64);
+ conf->psk_hash_bucket_size = ngx_align(conf->psk_hash_bucket_size,
+ ngx_cacheline_size);
+
conf->ssl.log = cf->log;
if (!conf->nocert) {
@@ -815,6 +847,13 @@
}
+ if (ngx_ssl_psk_path(cf, &conf->ssl, &conf->psk_path,
+ conf->psk_hash_max_size, conf->psk_hash_bucket_size)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
return NGX_CONF_OK;
}
diff -r a38066b79d71 -r 7aa7771191d6 src/http/modules/ngx_http_ssl_module.h
--- a/src/http/modules/ngx_http_ssl_module.h Thu Jun 01 10:55:04 2017 -0500
+++ b/src/http/modules/ngx_http_ssl_module.h Thu Jun 01 11:01:05 2017 -0500
@@ -56,6 +56,10 @@
ngx_str_t stapling_file;
ngx_str_t stapling_responder;
+ ngx_str_t psk_path;
+ ngx_uint_t psk_hash_max_size;
+ ngx_uint_t psk_hash_bucket_size;
+
u_char *file;
ngx_uint_t line;
} ngx_http_ssl_srv_conf_t;
________________________________
CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you.
More information about the nginx-devel
mailing list