From 6a76740b83c1ced26e9464a7abb5b30de4a60e6f Mon Sep 17 00:00:00 2001
From: Ashley Sanders <asanders@digium.com>
Date: Fri, 30 Jan 2015 17:21:50 +0000
Subject: [PATCH] HTTP: For httpd server, need option to define server name for
 security purposes

Added a new config property [servername] to the http.conf file; updated the http server to use the new property when sending responses, for showing http status through the CLI and when reporting status through the 'httpstatus' webpage. In this version, [servername] is uncommented by default.

ASTERISK-24316 #close
Reported By: Andrew Nagy
Review: https://reviewboard.asterisk.org/r/4374/
........

Merged revisions 431471 from http://svn.asterisk.org/svn/asterisk/branches/13


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@431484 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 configs/samples/http.conf.sample |  10 ++
 include/asterisk/http.h          |  22 ++++
 main/http.c                      | 172 ++++++++++++++++++++-----------
 3 files changed, 145 insertions(+), 59 deletions(-)

diff --git a/configs/samples/http.conf.sample b/configs/samples/http.conf.sample
index 44095a11e9..a4093bd88a 100644
--- a/configs/samples/http.conf.sample
+++ b/configs/samples/http.conf.sample
@@ -13,6 +13,16 @@
 ;
 [general]
 ;
+; The name of the server, advertised in both the Server field in HTTP
+; response message headers, as well as the <address /> element in certain HTTP
+; response message bodies. If not furnished here, "Asterisk/{version}" will be
+; used as a default value for the Server header field and the <address />
+; element. Setting this property to a blank value will result in the omission
+; of the Server header field from HTTP response message headers and the
+; <address /> element from HTTP response message bodies.
+;
+servername=Asterisk
+;
 ; Whether HTTP/HTTPS interface is enabled or not.  Default is no.
 ; This also affects manager/rawman/mxml access (see manager.conf)
 ;
diff --git a/include/asterisk/http.h b/include/asterisk/http.h
index ad91823b59..bb8973dce4 100644
--- a/include/asterisk/http.h
+++ b/include/asterisk/http.h
@@ -200,6 +200,28 @@ void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method
 	int status_code, const char *status_title, struct ast_str *http_header,
 	struct ast_str *out, int fd, unsigned int static_content);
 
+/*!
+ * \brief Creates and sends a formatted http response message.
+ * \param ser                   TCP/TLS session object
+ * \param status_code           HTTP response code (200/401/403/404/500)
+ * \param status_title          English equivalent to the status_code parameter
+ * \param http_header_data      The formatted text to use in the http header
+ * \param text                  Additional informational text to use in the
+ *                              response
+ *
+ * \note Function constructs response headers from the status_code, status_title and
+ * http_header_data parameters.
+ *
+ * The response body is created as HTML content, from the status_code,
+ * status_title, and the text parameters.
+ *
+ * The http_header_data parameter will be freed as a result of calling function.
+ *
+ * \since 13.2.0
+ */
+void ast_http_create_response(struct ast_tcptls_session_instance *ser, int status_code,
+	const char *status_title, struct ast_str *http_header_data, const char *text);
+
 /*! \brief Send http "401 Unauthorized" response and close socket */
 void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm, const unsigned long nonce, const unsigned long opaque, int stale, const char *text);
 
diff --git a/main/http.c b/main/http.c
index a9e50cbc2e..ef3b4b22ba 100644
--- a/main/http.c
+++ b/main/http.c
@@ -77,6 +77,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #define MIN_INITIAL_REQUEST_TIMEOUT	10000
 /*! (ms) Idle time between HTTP requests */
 #define DEFAULT_SESSION_KEEP_ALIVE 15000
+/*! Max size for the http server name */
+#define	MAX_SERVER_NAME_LENGTH 128
+/*! Max size for the http response header */
+#define	DEFAULT_RESPONSE_HEADER_LENGTH 512
 
 /*! Maximum application/json or application/x-www-form-urlencoded body content length. */
 #if !defined(LOW_MEMORY)
@@ -92,6 +96,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #define MAX_HTTP_LINE_LENGTH 1024
 #endif	/* !defined(LOW_MEMORY) */
 
+static char http_server_name[MAX_SERVER_NAME_LENGTH];
+
 static int session_limit = DEFAULT_SESSION_LIMIT;
 static int session_inactivity = DEFAULT_SESSION_INACTIVITY;
 static int session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE;
@@ -375,6 +381,7 @@ static int httpstatus_callback(struct ast_tcptls_session_instance *ser,
 		"<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
 		"<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
 
+	ast_str_append(&out, 0, "<tr><td><i>Server</i></td><td><b>%s</b></td></tr>\r\n", http_server_name);
 	ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
 	ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
 		       ast_sockaddr_stringify_addr(&http_desc.old_address));
@@ -446,14 +453,23 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 	char timebuf[80];
 	int content_length = 0;
 	int close_connection;
+	struct ast_str *server_header_field = ast_str_create(MAX_SERVER_NAME_LENGTH);
 
-	if (!ser || !ser->f) {
+	if (!ser || !ser->f || !server_header_field) {
 		/* The connection is not open. */
 		ast_free(http_header);
 		ast_free(out);
+		ast_free(server_header_field);
 		return;
 	}
 
+	if(!ast_strlen_zero(http_server_name)) {
+		ast_str_set(&server_header_field,
+	                0,
+	                "Server: %s\r\n",
+	                http_server_name);
+	}
+
 	/*
 	 * We shouldn't be sending non-final status codes to this
 	 * function because we may close the connection before
@@ -491,7 +507,7 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 	/* send http header */
 	fprintf(ser->f,
 		"HTTP/1.1 %d %s\r\n"
-		"Server: Asterisk/%s\r\n"
+		"%s"
 		"Date: %s\r\n"
 		"%s"
 		"%s"
@@ -499,7 +515,7 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 		"Content-Length: %d\r\n"
 		"\r\n",
 		status_code, status_title ? status_title : "OK",
-		ast_get_version(),
+		ast_str_buffer(server_header_field),
 		timebuf,
 		close_connection ? "Connection: close\r\n" : "",
 		static_content ? "" : "Cache-Control: no-cache, no-store\r\n",
@@ -532,6 +548,7 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 
 	ast_free(http_header);
 	ast_free(out);
+	ast_free(server_header_field);
 
 	if (close_connection) {
 		ast_debug(1, "HTTP closing session.  status_code:%d\n", status_code);
@@ -541,76 +558,101 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 	}
 }
 
-void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm,
-	const unsigned long nonce, const unsigned long opaque, int stale,
-	const char *text)
+void ast_http_create_response(struct ast_tcptls_session_instance *ser, int status_code,
+	const char *status_title, struct ast_str *http_header_data, const char *text)
 {
-	struct ast_str *http_headers = ast_str_create(128);
-	struct ast_str *out = ast_str_create(512);
+	char server_name[MAX_SERVER_NAME_LENGTH];
+	struct ast_str *server_address = ast_str_create(MAX_SERVER_NAME_LENGTH);
+	struct ast_str *out = ast_str_create(MAX_CONTENT_LENGTH);
 
-	if (!http_headers || !out) {
-		ast_free(http_headers);
+	if (!http_header_data || !server_address || !out) {
+		ast_free(http_header_data);
+		ast_free(server_address);
 		ast_free(out);
 		if (ser && ser->f) {
-			ast_debug(1, "HTTP closing session.  Auth OOM\n");
+			ast_debug(1, "HTTP closing session. OOM.\n");
 			ast_tcptls_close_session_file(ser);
 		}
 		return;
 	}
 
-	ast_str_set(&http_headers, 0,
-		"WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
-		"Content-type: text/html\r\n",
-		realm ? realm : "Asterisk",
-		nonce,
-		opaque,
-		stale ? ", stale=true" : "");
-
-	ast_str_set(&out, 0,
-		"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
-		"<html><head>\r\n"
-		"<title>401 Unauthorized</title>\r\n"
-		"</head><body>\r\n"
-		"<h1>401 Unauthorized</h1>\r\n"
-		"<p>%s</p>\r\n"
-		"<hr />\r\n"
-		"<address>Asterisk Server</address>\r\n"
-		"</body></html>\r\n",
-		text ? text : "");
-
-	ast_http_send(ser, AST_HTTP_UNKNOWN, 401, "Unauthorized", http_headers, out, 0, 0);
+	if(!ast_strlen_zero(http_server_name)) {
+		ast_xml_escape(http_server_name, server_name, sizeof(server_name));
+		ast_str_set(&server_address,
+	                0,
+	                "<address>%s</address>\r\n",
+	                server_name);
+	}
+
+	ast_str_set(&out,
+	            0,
+	            "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+	            "<html><head>\r\n"
+	            "<title>%d %s</title>\r\n"
+	            "</head><body>\r\n"
+	            "<h1>%s</h1>\r\n"
+	            "<p>%s</p>\r\n"
+	            "<hr />\r\n"
+	            "%s"
+	            "</body></html>\r\n",
+	            status_code,
+	            status_title,
+	            status_title,
+	            text ? text : "",
+	            ast_str_buffer(server_address));
+
+	ast_free(server_address);
+
+	ast_http_send(ser,
+	              AST_HTTP_UNKNOWN,
+	              status_code,
+	              status_title,
+	              http_header_data,
+	              out,
+	              0,
+	              0);
 }
 
-void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code, const char *status_title, const char *text)
+void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm,
+	const unsigned long nonce, const unsigned long opaque, int stale,
+	const char *text)
 {
-	struct ast_str *http_headers = ast_str_create(40);
-	struct ast_str *out = ast_str_create(256);
-
-	if (!http_headers || !out) {
-		ast_free(http_headers);
-		ast_free(out);
-		if (ser && ser->f) {
-			ast_debug(1, "HTTP closing session.  error OOM\n");
-			ast_tcptls_close_session_file(ser);
-		}
-		return;
-	}
+	int status_code = 401;
+	char *status_title = "Unauthorized";
+	struct ast_str *http_header_data = ast_str_create(DEFAULT_RESPONSE_HEADER_LENGTH);
+
+	if (http_header_data) {
+		ast_str_set(&http_header_data,
+		            0,
+		            "WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
+		            "Content-type: text/html\r\n",
+		            realm ? realm : "Asterisk",
+		            nonce,
+		            opaque,
+		            stale ? ", stale=true" : "");
+	}
+
+	ast_http_create_response(ser,
+	                         status_code,
+	                         status_title,
+	                         http_header_data,
+	                         text);
+}
 
-	ast_str_set(&http_headers, 0, "Content-type: text/html\r\n");
+void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code,
+	const char *status_title, const char *text)
+{
+	struct ast_str *http_header_data = ast_str_create(DEFAULT_RESPONSE_HEADER_LENGTH);
 
-	ast_str_set(&out, 0,
-		"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
-		"<html><head>\r\n"
-		"<title>%d %s</title>\r\n"
-		"</head><body>\r\n"
-		"<h1>%s</h1>\r\n"
-		"<p>%s</p>\r\n"
-		"<hr />\r\n"
-		"<address>Asterisk Server</address>\r\n"
-		"</body></html>\r\n",
-		status_code, status_title, status_title, text);
+	if (http_header_data) {
+		ast_str_set(&http_header_data, 0, "Content-type: text/html\r\n");
+	}
 
-	ast_http_send(ser, AST_HTTP_UNKNOWN, status_code, status_title, http_headers, out, 0, 0);
+	ast_http_create_response(ser,
+	                         status_code,
+	                         status_title,
+	                         http_header_data,
+	                         text);
 }
 
 /*!
@@ -2029,6 +2071,7 @@ static int __ast_http_load(int reload)
 	int enabled=0;
 	int newenablestatic=0;
 	char newprefix[MAX_PREFIX] = "";
+	char server_name[MAX_SERVER_NAME_LENGTH];
 	struct http_uri_redirect *redirect;
 	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
 	uint32_t bindport = DEFAULT_PORT;
@@ -2071,6 +2114,8 @@ static int __ast_http_load(int reload)
 	session_inactivity = DEFAULT_SESSION_INACTIVITY;
 	session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE;
 
+	snprintf(server_name, sizeof(server_name), "Asterisk/%s", ast_get_version());
+
 	v = ast_variable_browse(cfg, "general");
 	for (; v; v = v->next) {
 		/* read tls config options while preventing unsupported options from being set */
@@ -2087,7 +2132,13 @@ static int __ast_http_load(int reload)
 			continue;
 		}
 
-		if (!strcasecmp(v->name, "enabled")) {
+		if (!strcasecmp(v->name, "servername")) {
+			if (!ast_strlen_zero(v->value)) {
+				ast_copy_string(server_name, v->value, sizeof(server_name));
+			} else {
+				server_name[0] = '\0';
+			}
+		} else if (!strcasecmp(v->name, "enabled")) {
 			enabled = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "enablestatic")) {
 			newenablestatic = ast_true(v->value);
@@ -2139,6 +2190,8 @@ static int __ast_http_load(int reload)
 	if (strcmp(prefix, newprefix)) {
 		ast_copy_string(prefix, newprefix, sizeof(prefix));
 	}
+
+	ast_copy_string(http_server_name, server_name, sizeof(http_server_name));
 	enablestatic = newenablestatic;
 
 	if (num_addrs && enabled) {
@@ -2209,6 +2262,7 @@ static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 	}
 	ast_cli(a->fd, "HTTP Server Status:\n");
 	ast_cli(a->fd, "Prefix: %s\n", prefix);
+	ast_cli(a->fd, "Server: %s\n", http_server_name);
 	if (ast_sockaddr_isnull(&http_desc.old_address)) {
 		ast_cli(a->fd, "Server Disabled\n\n");
 	} else {
-- 
GitLab