diff options
Diffstat (limited to 'src/serv.c')
-rw-r--r-- | src/serv.c | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/src/serv.c b/src/serv.c index b34254d38e..308612a33e 100644 --- a/src/serv.c +++ b/src/serv.c @@ -81,6 +81,8 @@ const char *dh_params_file = NULL; const char *x509_crlfile = NULL; const char *priorities = NULL; const char *status_response_ocsp = NULL; +const char *sni_hostname = NULL; +int sni_hostname_fatal = 0; gnutls_datum_t session_ticket_key; static void tcp_server(const char *name, int port); @@ -318,6 +320,83 @@ int ret; return 0; } +/* callback used to verify if the host name advertised in client hello matches + * the one configured in server + */ +int +post_client_hello(gnutls_session_t session) +{ + int ret; + /* DNS names (only type supported) may be at most 256 byte long */ + char *name; + size_t len = 256; + unsigned int type; + int i; + + name = malloc(len); + if (name == NULL) + return GNUTLS_E_MEMORY_ERROR; + + for (i=0; ; ) { + ret = gnutls_server_name_get(session, name, &len, &type, i); + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + char *new_name; + new_name = realloc(name, len); + if (new_name == NULL) { + ret = GNUTLS_E_MEMORY_ERROR; + goto end; + } + name = new_name; + continue; /* retry call with same index */ + } + + /* check if it is the last entry in list */ + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + i++; + if (ret != GNUTLS_E_SUCCESS) + goto end; + /* unknown types need to be ignored */ + if (type != GNUTLS_NAME_DNS) + continue; + + if (strlen(sni_hostname) != len) + continue; + /* API guarantees that the name of type DNS will be null terminated */ + if (!strncmp(name, sni_hostname, len)) { + ret = GNUTLS_E_SUCCESS; + goto end; + } + }; + /* when there is no extension, we can't send the extension specific alert */ + if (i == 0) { + fprintf(stderr, "Warning: client did not include SNI extension, using default host\n"); + ret = GNUTLS_E_SUCCESS; + goto end; + } + + if (sni_hostname_fatal == 1) { + /* abort the connection, propagate error up the stack */ + ret = GNUTLS_E_UNRECOGNIZED_NAME; + goto end; + } + + fprintf(stderr, "Warning: client provided unrecognized host name\n"); + /* since we just want to send an alert, not abort the connection, we + * need to send it ourselves + */ + do { + ret = gnutls_alert_send(session, + GNUTLS_AL_WARNING, + GNUTLS_A_UNRECOGNIZED_NAME); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + /* continue handshake, fall through */ +end: + free(name); + return ret; +} + gnutls_session_t initialize_session(int dtls) { gnutls_session_t session; @@ -349,6 +428,10 @@ gnutls_session_t initialize_session(int dtls) &session_ticket_key); #endif + if (sni_hostname != NULL) + gnutls_handshake_set_post_client_hello_function(session, + &post_client_hello); + if (gnutls_priority_set_direct(session, priorities, &err) < 0) { fprintf(stderr, "Syntax error at: %s\n", err); exit(1); @@ -1642,6 +1725,12 @@ static void cmd_parser(int argc, char **argv) if (HAVE_OPT(OCSP_RESPONSE)) status_response_ocsp = OPT_ARG(OCSP_RESPONSE); + if (HAVE_OPT(SNI_HOSTNAME)) + sni_hostname = OPT_ARG(SNI_HOSTNAME); + + if (HAVE_OPT(SNI_HOSTNAME_FATAL)) + sni_hostname_fatal = 1; + } /* session resuming support */ |