[PATCH] [PATCH 2 of 4] SSL: add support for PSK cipher suites

Nate Karstens nate.karstens at garmin.com
Thu Aug 24 02:20:50 UTC 2017


# HG changeset patch
# User Nate Karstens <nate.karstens at garmin.com>
# Date 1503540059 18000
#      Wed Aug 23 21:00:59 2017 -0500
# Node ID 97953fe374455a04973268c4b2fbadd7ced91ffe
# Parent  17c038b56051f922ec440d40e23e8d1b23d835df
[PATCH 2 of 4] SSL: add support for PSK cipher suites.

Adds support for TLS connections using PSK cipher suites. A new
configuration directive, ssl_psk_file, specifies the file that
contains a list of identities and associated PSKs. Each line of
the file begins with the identity, followed by a colon character
(':'), and ending with the PSK. As required by RFC 4279 section
5.4, PSKs may be entered either as plain text or using hexadecimal
encoding. Hexadecimal PSKs must begin with "{HEX}". PSKs without
this prefix are assumed to be plain text, but they may optionally
begin with "{PLAIN}" to denote this. Some examples:

gary:plain_text_password
min:{PLAIN}another_text_password
cliff:{HEX}ab0123CD

PSK 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 17c038b56051 -r 97953fe37445 contrib/vim/syntax/nginx.vim
--- a/contrib/vim/syntax/nginx.vim      Wed Aug 23 21:00:18 2017 -0500
+++ b/contrib/vim/syntax/nginx.vim      Wed Aug 23 21:00:59 2017 -0500
@@ -550,6 +550,7 @@ syn keyword ngxDirective contained ssl_p
 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_file
 syn keyword ngxDirective contained ssl_session_cache
 syn keyword ngxDirective contained ssl_session_ticket_key
 syn keyword ngxDirective contained ssl_session_tickets
diff -r 17c038b56051 -r 97953fe37445 src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c     Wed Aug 23 21:00:18 2017 -0500
+++ b/src/event/ngx_event_openssl.c     Wed Aug 23 21:00:59 2017 -0500
@@ -11,6 +11,7 @@


 #define NGX_SSL_PASSWORD_BUFFER_SIZE  4096
+#define NGX_SSL_PSK_BUFFER_SIZE       4096


 typedef struct {
@@ -24,6 +25,10 @@ static int ngx_ssl_verify_callback(int o
 static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
     int ret);
 static void ngx_ssl_passwords_cleanup(void *data);
+#ifdef PSK_MAX_IDENTITY_LEN
+static unsigned int ngx_ssl_psk_callback(ngx_ssl_conn_t *ssl_conn,
+    const char *identity, unsigned char *psk, unsigned int max_psk_len);
+#endif
 static void ngx_ssl_handshake_handler(ngx_event_t *ev);
 static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n);
 static void ngx_ssl_write_handler(ngx_event_t *wev);
@@ -110,6 +115,7 @@ int  ngx_ssl_connection_index;
 int  ngx_ssl_server_conf_index;
 int  ngx_ssl_session_cache_index;
 int  ngx_ssl_session_ticket_keys_index;
+int  ngx_ssl_psk_index;
 int  ngx_ssl_certificate_index;
 int  ngx_ssl_next_certificate_index;
 int  ngx_ssl_certificate_name_index;
@@ -195,6 +201,14 @@ ngx_ssl_init(ngx_log_t *log)
         return NGX_ERROR;
     }

+    ngx_ssl_psk_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+
+    if (ngx_ssl_psk_index == -1) {
+        ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+                      "SSL_CTX_get_ex_new_index() failed");
+        return NGX_ERROR;
+    }
+
     ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
                                                          NULL);
     if (ngx_ssl_certificate_index == -1) {
@@ -1163,6 +1177,211 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s


 ngx_int_t
+ngx_ssl_psk_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
+{
+#ifdef PSK_MAX_IDENTITY_LEN
+
+    ngx_fd_t            fd;
+    ngx_int_t           err;
+    ngx_file_info_t     fi;
+
+    err = NGX_OK;
+
+    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    fd = ngx_open_file(file->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+    if (fd == NGX_INVALID_FILE) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, ngx_errno,
+                      ngx_open_file_n " \"%V\" failed", file);
+        return NGX_ERROR;
+    }
+
+    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, ngx_errno,
+                      ngx_fd_info_n " \"%V\" failed", file);
+        err = NGX_ERROR;
+        goto failed;
+    }
+
+    if (ngx_file_size(&fi) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "PSK file \"%V\" is empty", file);
+        err = NGX_ERROR;
+        goto failed;
+    }
+
+failed:
+
+    if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, ngx_errno,
+                      ngx_close_file_n " %V failed", file);
+        err = NGX_ERROR;
+    }
+
+    if (err != NGX_OK) {
+        return err;
+    }
+
+    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_psk_index, file) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_set_ex_data() failed");
+        return NGX_ERROR;
+    }
+
+    SSL_CTX_set_psk_server_callback(ssl->ctx, ngx_ssl_psk_callback);
+
+#endif
+
+    return NGX_OK;
+}
+
+
+#ifdef PSK_MAX_IDENTITY_LEN
+
+static unsigned int
+ngx_ssl_psk_callback(ngx_ssl_conn_t *ssl_conn, const char *identity,
+    unsigned char *psk, unsigned int max_psk_len)
+{
+    u_char             *p, *last, *end, *colon;
+    size_t              len;
+    ssize_t             n;
+    SSL_CTX            *ssl_ctx;
+    ngx_fd_t            fd;
+    ngx_str_t          *file;
+    unsigned int        psk_len;
+    ngx_connection_t   *c;
+    u_char              buf[NGX_SSL_PSK_BUFFER_SIZE];
+
+    psk_len = 0;
+    c = ngx_ssl_get_connection(ssl_conn);
+
+    ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
+    file = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_psk_index);
+
+    fd = ngx_open_file(file->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+    if (fd == NGX_INVALID_FILE) {
+        ngx_ssl_error(NGX_LOG_ERR, c->log, ngx_errno,
+                      ngx_open_file_n " \"%V\" failed", file);
+        return 0;
+    }
+
+    len = 0;
+    last = buf;
+
+    do {
+        n = ngx_read_fd(fd, last, NGX_SSL_PSK_BUFFER_SIZE - len);
+
+        if (n == -1) {
+            ngx_ssl_error(NGX_LOG_ERR, c->log, ngx_errno,
+                          ngx_read_fd_n " \"%V\" failed", file);
+            psk_len = 0;
+            goto cleanup;
+        }
+
+        end = last + n;
+
+        if (len && n == 0) {
+            *end++ = LF;
+        }
+
+        for (p = buf; /* void */; p = last) {
+            last = ngx_strlchr(last, end, LF);
+
+            if (last == NULL) {
+                break;
+            }
+
+            len = last++ - p;
+
+            if (len && p[len - 1] == CR) {
+                len--;
+            }
+
+            if (len == 0) {
+                continue;
+            }
+
+            colon = ngx_strlchr(p, p + len, ':');
+
+            if (colon == NULL) {
+                continue;
+            }
+
+            *colon = '\0';
+
+            if (ngx_strcmp(p, identity) != 0) {
+                continue;
+            }
+
+            len -= colon + 1 - p;
+            p = colon + 1;
+
+            if (ngx_strncmp(p, "{HEX}", sizeof("{HEX}") - 1) == 0) {
+
+                p += sizeof("{HEX}") - 1;
+                len -= sizeof("{HEX}") - 1;
+
+                if (len / 2 > max_psk_len) {
+                    goto cleanup;
+                }
+
+                if (ngx_hex_decode(psk, p, len) != NGX_OK) {
+                    ngx_memzero(psk, len / 2);
+                    goto cleanup;
+                }
+
+                psk_len = len / 2;
+
+                goto cleanup;
+
+            } else if (ngx_strncmp(p, "{PLAIN}", sizeof("{PLAIN}") - 1) == 0) {
+                p += sizeof("{PLAIN}") - 1;
+                len -= sizeof("{PLAIN}") - 1;
+            }
+
+            if (len > max_psk_len) {
+                goto cleanup;
+            }
+
+            ngx_memcpy(psk, p, len);
+            psk_len = len;
+
+            goto cleanup;
+        }
+
+        len = end - p;
+
+        if (len == NGX_SSL_PSK_BUFFER_SIZE) {
+            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
+                          "too long line in \"%V\"", file);
+            psk_len = 0;
+            goto cleanup;
+        }
+
+        ngx_memmove(buf, p, len);
+        last = buf + len;
+
+    } while (n != 0);
+
+cleanup:
+
+    if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, ngx_errno,
+                      ngx_close_file_n " %V failed", file);
+        psk_len = 0;
+    }
+
+    ngx_memzero(buf, NGX_SSL_PSK_BUFFER_SIZE);
+
+    return psk_len;
+}
+
+#endif
+
+
+ngx_int_t
 ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
 {
     ngx_ssl_connection_t  *sc;
diff -r 17c038b56051 -r 97953fe37445 src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h     Wed Aug 23 21:00:18 2017 -0500
+++ b/src/event/ngx_event_openssl.h     Wed Aug 23 21:00:59 2017 -0500
@@ -172,6 +172,7 @@ ngx_int_t ngx_ssl_session_cache(ngx_ssl_
 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_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
+ngx_int_t ngx_ssl_psk_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
 ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
     ngx_uint_t flags);

@@ -253,6 +254,7 @@ extern int  ngx_ssl_connection_index;
 extern int  ngx_ssl_server_conf_index;
 extern int  ngx_ssl_session_cache_index;
 extern int  ngx_ssl_session_ticket_keys_index;
+extern int  ngx_ssl_psk_index;
 extern int  ngx_ssl_certificate_index;
 extern int  ngx_ssl_next_certificate_index;
 extern int  ngx_ssl_certificate_name_index;
diff -r 17c038b56051 -r 97953fe37445 src/http/modules/ngx_http_ssl_module.c
--- a/src/http/modules/ngx_http_ssl_module.c    Wed Aug 23 21:00:18 2017 -0500
+++ b/src/http/modules/ngx_http_ssl_module.c    Wed Aug 23 21:00:59 2017 -0500
@@ -234,6 +234,13 @@ static ngx_command_t  ngx_http_ssl_comma
       offsetof(ngx_http_ssl_srv_conf_t, stapling_verify),
       NULL },

+    { ngx_string("ssl_psk_file"),
+      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_file),
+      NULL },
+
       ngx_null_command
 };

@@ -543,6 +550,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t
      *     sscf->shm_zone = NULL;
      *     sscf->stapling_file = { 0, NULL };
      *     sscf->stapling_responder = { 0, NULL };
+     *     sscf->psk_file = { 0, NULL };
      */

     sscf->enable = NGX_CONF_UNSET;
@@ -624,6 +632,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
     ngx_conf_merge_str_value(conf->stapling_responder,
                          prev->stapling_responder, "");

+    ngx_conf_merge_str_value(conf->psk_file, prev->psk_file, "");
+
     conf->ssl.log = cf->log;

     if (conf->enable) {
@@ -804,6 +814,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *

     }

+    if (ngx_ssl_psk_file(cf, &conf->ssl, &conf->psk_file) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
     return NGX_CONF_OK;
 }

diff -r 17c038b56051 -r 97953fe37445 src/http/modules/ngx_http_ssl_module.h
--- a/src/http/modules/ngx_http_ssl_module.h    Wed Aug 23 21:00:18 2017 -0500
+++ b/src/http/modules/ngx_http_ssl_module.h    Wed Aug 23 21:00:59 2017 -0500
@@ -55,6 +55,8 @@ typedef struct {
     ngx_str_t                       stapling_file;
     ngx_str_t                       stapling_responder;

+    ngx_str_t                       psk_file;
+
     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