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
changes/42/42/1
Ashley Sanders 10 years ago
parent bd0bdf1e41
commit 6a76740b83

@ -13,6 +13,16 @@
; ;
[general] [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. ; Whether HTTP/HTTPS interface is enabled or not. Default is no.
; This also affects manager/rawman/mxml access (see manager.conf) ; This also affects manager/rawman/mxml access (see manager.conf)
; ;

@ -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, int status_code, const char *status_title, struct ast_str *http_header,
struct ast_str *out, int fd, unsigned int static_content); 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 */ /*! \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); 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);

@ -77,6 +77,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define MIN_INITIAL_REQUEST_TIMEOUT 10000 #define MIN_INITIAL_REQUEST_TIMEOUT 10000
/*! (ms) Idle time between HTTP requests */ /*! (ms) Idle time between HTTP requests */
#define DEFAULT_SESSION_KEEP_ALIVE 15000 #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. */ /*! Maximum application/json or application/x-www-form-urlencoded body content length. */
#if !defined(LOW_MEMORY) #if !defined(LOW_MEMORY)
@ -92,6 +96,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define MAX_HTTP_LINE_LENGTH 1024 #define MAX_HTTP_LINE_LENGTH 1024
#endif /* !defined(LOW_MEMORY) */ #endif /* !defined(LOW_MEMORY) */
static char http_server_name[MAX_SERVER_NAME_LENGTH];
static int session_limit = DEFAULT_SESSION_LIMIT; static int session_limit = DEFAULT_SESSION_LIMIT;
static int session_inactivity = DEFAULT_SESSION_INACTIVITY; static int session_inactivity = DEFAULT_SESSION_INACTIVITY;
static int session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE; 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" "<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"); "<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>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_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)); 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]; char timebuf[80];
int content_length = 0; int content_length = 0;
int close_connection; 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. */ /* The connection is not open. */
ast_free(http_header); ast_free(http_header);
ast_free(out); ast_free(out);
ast_free(server_header_field);
return; 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 * We shouldn't be sending non-final status codes to this
* function because we may close the connection before * 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 */ /* send http header */
fprintf(ser->f, fprintf(ser->f,
"HTTP/1.1 %d %s\r\n" "HTTP/1.1 %d %s\r\n"
"Server: Asterisk/%s\r\n" "%s"
"Date: %s\r\n" "Date: %s\r\n"
"%s" "%s"
"%s" "%s"
@ -499,7 +515,7 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
"Content-Length: %d\r\n" "Content-Length: %d\r\n"
"\r\n", "\r\n",
status_code, status_title ? status_title : "OK", status_code, status_title ? status_title : "OK",
ast_get_version(), ast_str_buffer(server_header_field),
timebuf, timebuf,
close_connection ? "Connection: close\r\n" : "", close_connection ? "Connection: close\r\n" : "",
static_content ? "" : "Cache-Control: no-cache, no-store\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(http_header);
ast_free(out); ast_free(out);
ast_free(server_header_field);
if (close_connection) { if (close_connection) {
ast_debug(1, "HTTP closing session. status_code:%d\n", status_code); 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, void ast_http_create_response(struct ast_tcptls_session_instance *ser, int status_code,
const unsigned long nonce, const unsigned long opaque, int stale, const char *status_title, struct ast_str *http_header_data, const char *text)
const char *text)
{ {
struct ast_str *http_headers = ast_str_create(128); char server_name[MAX_SERVER_NAME_LENGTH];
struct ast_str *out = ast_str_create(512); 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) { if (!http_header_data || !server_address || !out) {
ast_free(http_headers); ast_free(http_header_data);
ast_free(server_address);
ast_free(out); ast_free(out);
if (ser && ser->f) { 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); ast_tcptls_close_session_file(ser);
} }
return; return;
} }
ast_str_set(&http_headers, 0, if(!ast_strlen_zero(http_server_name)) {
"WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n" ast_xml_escape(http_server_name, server_name, sizeof(server_name));
"Content-type: text/html\r\n", ast_str_set(&server_address,
realm ? realm : "Asterisk", 0,
nonce, "<address>%s</address>\r\n",
opaque, server_name);
stale ? ", stale=true" : ""); }
ast_str_set(&out, 0, ast_str_set(&out,
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n" 0,
"<html><head>\r\n" "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
"<title>401 Unauthorized</title>\r\n" "<html><head>\r\n"
"</head><body>\r\n" "<title>%d %s</title>\r\n"
"<h1>401 Unauthorized</h1>\r\n" "</head><body>\r\n"
"<p>%s</p>\r\n" "<h1>%s</h1>\r\n"
"<hr />\r\n" "<p>%s</p>\r\n"
"<address>Asterisk Server</address>\r\n" "<hr />\r\n"
"</body></html>\r\n", "%s"
text ? text : ""); "</body></html>\r\n",
status_code,
ast_http_send(ser, AST_HTTP_UNKNOWN, 401, "Unauthorized", http_headers, out, 0, 0); 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); int status_code = 401;
struct ast_str *out = ast_str_create(256); char *status_title = "Unauthorized";
struct ast_str *http_header_data = ast_str_create(DEFAULT_RESPONSE_HEADER_LENGTH);
if (!http_headers || !out) {
ast_free(http_headers); if (http_header_data) {
ast_free(out); ast_str_set(&http_header_data,
if (ser && ser->f) { 0,
ast_debug(1, "HTTP closing session. error OOM\n"); "WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
ast_tcptls_close_session_file(ser); "Content-type: text/html\r\n",
} realm ? realm : "Asterisk",
return; 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, if (http_header_data) {
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n" ast_str_set(&http_header_data, 0, "Content-type: text/html\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);
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 enabled=0;
int newenablestatic=0; int newenablestatic=0;
char newprefix[MAX_PREFIX] = ""; char newprefix[MAX_PREFIX] = "";
char server_name[MAX_SERVER_NAME_LENGTH];
struct http_uri_redirect *redirect; struct http_uri_redirect *redirect;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
uint32_t bindport = DEFAULT_PORT; uint32_t bindport = DEFAULT_PORT;
@ -2071,6 +2114,8 @@ static int __ast_http_load(int reload)
session_inactivity = DEFAULT_SESSION_INACTIVITY; session_inactivity = DEFAULT_SESSION_INACTIVITY;
session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE; session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE;
snprintf(server_name, sizeof(server_name), "Asterisk/%s", ast_get_version());
v = ast_variable_browse(cfg, "general"); v = ast_variable_browse(cfg, "general");
for (; v; v = v->next) { for (; v; v = v->next) {
/* read tls config options while preventing unsupported options from being set */ /* read tls config options while preventing unsupported options from being set */
@ -2087,7 +2132,13 @@ static int __ast_http_load(int reload)
continue; 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); enabled = ast_true(v->value);
} else if (!strcasecmp(v->name, "enablestatic")) { } else if (!strcasecmp(v->name, "enablestatic")) {
newenablestatic = ast_true(v->value); newenablestatic = ast_true(v->value);
@ -2139,6 +2190,8 @@ static int __ast_http_load(int reload)
if (strcmp(prefix, newprefix)) { if (strcmp(prefix, newprefix)) {
ast_copy_string(prefix, newprefix, sizeof(prefix)); ast_copy_string(prefix, newprefix, sizeof(prefix));
} }
ast_copy_string(http_server_name, server_name, sizeof(http_server_name));
enablestatic = newenablestatic; enablestatic = newenablestatic;
if (num_addrs && enabled) { 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, "HTTP Server Status:\n");
ast_cli(a->fd, "Prefix: %s\n", prefix); 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)) { if (ast_sockaddr_isnull(&http_desc.old_address)) {
ast_cli(a->fd, "Server Disabled\n\n"); ast_cli(a->fd, "Server Disabled\n\n");
} else { } else {

Loading…
Cancel
Save