Ver código fonte

Fix HTTPS redirection

The "host" field is unnecessary. It can be extracted from "http_headers" easily.
We should not use a local (socket's) address for redirection. That may leak an internal IP address.
I do not apply the fix to mg_get_request_link() since I don't know its example of use.
xtne6f 5 anos atrás
pai
commit
e46dcc36dc
1 arquivos alterados com 62 adições e 53 exclusões
  1. 62 53
      src/civetweb.c

+ 62 - 53
src/civetweb.c

@@ -2912,7 +2912,6 @@ struct mg_connection {
 	                 * mg_get_connection_info_impl */
 	                 * mg_get_connection_info_impl */
 #endif
 #endif
 
 
-	const char *host;       /* Host (HTTP/1.1 header or SNI) */
 	SSL *ssl;               /* SSL descriptor */
 	SSL *ssl;               /* SSL descriptor */
 	struct socket client;   /* Connected client */
 	struct socket client;   /* Connected client */
 	time_t conn_birth_time; /* Time (wall clock) when connection was
 	time_t conn_birth_time; /* Time (wall clock) when connection was
@@ -4147,9 +4146,17 @@ mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
 			            &truncated,
 			            &truncated,
 			            buf,
 			            buf,
 			            buflen,
 			            buflen,
+#if defined(USE_IPV6)
+			            "%s://%s%s%s%s%s",
+			            proto,
+			            (is_ipv6 && (server_domain == server_ip)) ? "[" : "",
+			            server_domain,
+			            (is_ipv6 && (server_domain == server_ip)) ? "]" : "",
+#else
 			            "%s://%s%s%s",
 			            "%s://%s%s%s",
 			            proto,
 			            proto,
 			            server_domain,
 			            server_domain,
+#endif
 			            portstr,
 			            portstr,
 			            ri->local_uri);
 			            ri->local_uri);
 			if (truncated) {
 			if (truncated) {
@@ -13615,68 +13622,76 @@ get_first_ssl_listener_index(const struct mg_context *ctx)
 
 
 
 
 /* Return host (without port) */
 /* Return host (without port) */
-/* Use mg_free to free the result */
-static const char *
-alloc_get_host(struct mg_connection *conn)
+static void
+get_host_from_request_info(struct vec *host, const struct mg_request_info *ri)
 {
 {
-	char buf[1025];
-	size_t buflen = sizeof(buf);
-	const char *host_header = get_header(conn->request_info.http_headers,
-	                                     conn->request_info.num_headers,
-	                                     "Host");
-	char *host;
+	const char *host_header =
+	    get_header(ri->http_headers, ri->num_headers, "Host");
+
+	host->ptr = NULL;
+	host->len = 0;
 
 
 	if (host_header != NULL) {
 	if (host_header != NULL) {
 		char *pos;
 		char *pos;
 
 
-		/* Create a local copy of the "Host" header, since it might be
-		 * modified here. */
-		mg_strlcpy(buf, host_header, buflen);
-		buf[buflen - 1] = '\0';
-		host = buf;
-		while (isspace((unsigned char)*host)) {
-			host++;
-		}
-
 		/* If the "Host" is an IPv6 address, like [::1], parse until ]
 		/* If the "Host" is an IPv6 address, like [::1], parse until ]
 		 * is found. */
 		 * is found. */
-		if (*host == '[') {
-			pos = strchr(host, ']');
+		if (*host_header == '[') {
+			pos = strchr(host_header, ']');
 			if (!pos) {
 			if (!pos) {
 				/* Malformed hostname starts with '[', but no ']' found */
 				/* Malformed hostname starts with '[', but no ']' found */
 				DEBUG_TRACE("%s", "Host name format error '[' without ']'");
 				DEBUG_TRACE("%s", "Host name format error '[' without ']'");
-				return NULL;
+				return;
 			}
 			}
 			/* terminate after ']' */
 			/* terminate after ']' */
-			pos[1] = 0;
+			host->ptr = host_header;
+			host->len = (size_t)(pos + 1 - host_header);
 		} else {
 		} else {
 			/* Otherwise, a ':' separates hostname and port number */
 			/* Otherwise, a ':' separates hostname and port number */
-			pos = strchr(host, ':');
+			pos = strchr(host_header, ':');
 			if (pos != NULL) {
 			if (pos != NULL) {
-				*pos = '\0';
+				host->len = (size_t)(pos - host_header);
+			} else {
+				host->len = strlen(host_header);
 			}
 			}
+			host->ptr = host_header;
 		}
 		}
+	}
+}
 
 
+
+static int
+switch_domain_context(struct mg_connection *conn)
+{
+	struct vec host;
+
+	get_host_from_request_info(&host, &conn->request_info);
+
+	if (host.ptr) {
 		if (conn->ssl) {
 		if (conn->ssl) {
 			/* This is a HTTPS connection, maybe we have a hostname
 			/* This is a HTTPS connection, maybe we have a hostname
 			 * from SNI (set in ssl_servername_callback). */
 			 * from SNI (set in ssl_servername_callback). */
 			const char *sslhost = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
 			const char *sslhost = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
 			if (sslhost && (conn->dom_ctx != &(conn->phys_ctx->dd))) {
 			if (sslhost && (conn->dom_ctx != &(conn->phys_ctx->dd))) {
 				/* We are not using the default domain */
 				/* We are not using the default domain */
-				if (mg_strcasecmp(host, sslhost)) {
+				if ((strlen(sslhost) != host.len)
+				    || mg_strncasecmp(host.ptr, sslhost, host.len)) {
 					/* Mismatch between SNI domain and HTTP domain */
 					/* Mismatch between SNI domain and HTTP domain */
-					DEBUG_TRACE("Host mismatch: SNI: %s, HTTPS: %s",
+					DEBUG_TRACE("Host mismatch: SNI: %s, HTTPS: %.*s",
 					            sslhost,
 					            sslhost,
-					            host);
-					return NULL;
+					            (int)host.len,
+					            host.ptr);
+					return 0;
 				}
 				}
 			}
 			}
-			DEBUG_TRACE("HTTPS Host: %s", host);
 
 
 		} else {
 		} else {
 			struct mg_domain_context *dom = &(conn->phys_ctx->dd);
 			struct mg_domain_context *dom = &(conn->phys_ctx->dd);
 			while (dom) {
 			while (dom) {
-				if (!mg_strcasecmp(host, dom->config[AUTHENTICATION_DOMAIN])) {
+				if ((strlen(dom->config[AUTHENTICATION_DOMAIN]) == host.len)
+				    && !mg_strncasecmp(host.ptr,
+				                       dom->config[AUTHENTICATION_DOMAIN],
+				                       host.len)) {
 
 
 					/* Found matching domain */
 					/* Found matching domain */
 					DEBUG_TRACE("HTTP domain %s found",
 					DEBUG_TRACE("HTTP domain %s found",
@@ -13690,31 +13705,33 @@ alloc_get_host(struct mg_connection *conn)
 				dom = dom->next;
 				dom = dom->next;
 				mg_unlock_context(conn->phys_ctx);
 				mg_unlock_context(conn->phys_ctx);
 			}
 			}
-
-			DEBUG_TRACE("HTTP Host: %s", host);
 		}
 		}
 
 
 	} else {
 	} else {
-		sockaddr_to_string(buf, buflen, &conn->client.lsa);
-		host = buf;
-
-		DEBUG_TRACE("IP: %s", host);
+		DEBUG_TRACE("HTTP%s Host is not set", conn->ssl ? "S" : "");
+		return 1;
 	}
 	}
 
 
-	return mg_strdup_ctx(host, conn->phys_ctx);
+	DEBUG_TRACE("HTTP%s Host: %.*s",
+	            conn->ssl ? "S" : "",
+	            (int)host.len,
+	            host.ptr);
+	return 1;
 }
 }
 
 
 
 
 static void
 static void
 redirect_to_https_port(struct mg_connection *conn, int ssl_index)
 redirect_to_https_port(struct mg_connection *conn, int ssl_index)
 {
 {
+	struct vec host;
 	char target_url[MG_BUF_LEN];
 	char target_url[MG_BUF_LEN];
 	int truncated = 0;
 	int truncated = 0;
 
 
 	conn->must_close = 1;
 	conn->must_close = 1;
 
 
 	/* Send host, port, uri and (if it exists) ?query_string */
 	/* Send host, port, uri and (if it exists) ?query_string */
-	if (conn->host) {
+	get_host_from_request_info(&host, &conn->request_info);
+	if (host.ptr) {
 
 
 		/* Use "308 Permanent Redirect" */
 		/* Use "308 Permanent Redirect" */
 		int redirect_code = 308;
 		int redirect_code = 308;
@@ -13725,9 +13742,10 @@ redirect_to_https_port(struct mg_connection *conn, int ssl_index)
 		    &truncated,
 		    &truncated,
 		    target_url,
 		    target_url,
 		    sizeof(target_url),
 		    sizeof(target_url),
-		    "https://%s:%d%s%s%s",
+		    "https://%.*s:%d%s%s%s",
 
 
-		    conn->host,
+		    (int)host.len,
+		    host.ptr,
 #if defined(USE_IPV6)
 #if defined(USE_IPV6)
 		    (conn->phys_ctx->listening_sockets[ssl_index].lsa.sa.sa_family
 		    (conn->phys_ctx->listening_sockets[ssl_index].lsa.sa.sa_family
 		     == AF_INET6)
 		     == AF_INET6)
@@ -13752,6 +13770,8 @@ redirect_to_https_port(struct mg_connection *conn, int ssl_index)
 
 
 		/* Use redirect helper function */
 		/* Use redirect helper function */
 		mg_send_http_redirect(conn, target_url, redirect_code);
 		mg_send_http_redirect(conn, target_url, redirect_code);
+	} else {
+		mg_send_http_error(conn, 400, "%s", "Bad Request");
 	}
 	}
 }
 }
 
 
@@ -17059,11 +17079,6 @@ close_connection(struct mg_connection *conn)
 		conn->client.sock = INVALID_SOCKET;
 		conn->client.sock = INVALID_SOCKET;
 	}
 	}
 
 
-	if (conn->host) {
-		mg_free((void *)conn->host);
-		conn->host = NULL;
-	}
-
 	mg_unlock_connection(conn);
 	mg_unlock_connection(conn);
 
 
 #if defined(USE_SERVER_STATS)
 #if defined(USE_SERVER_STATS)
@@ -17770,12 +17785,7 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
 
 
 	/* Message is a valid request */
 	/* Message is a valid request */
 
 
-	/* Is there a "host" ? */
-	if (conn->host != NULL) {
-		mg_free((void *)conn->host);
-	}
-	conn->host = alloc_get_host(conn);
-	if (!conn->host) {
+	if (!switch_domain_context(conn)) {
 		mg_snprintf(conn,
 		mg_snprintf(conn,
 		            NULL, /* No truncation check for ebuf */
 		            NULL, /* No truncation check for ebuf */
 		            ebuf,
 		            ebuf,
@@ -18779,7 +18789,6 @@ worker_thread_run(struct mg_connection *conn)
 	conn->buf_size = (int)ctx->max_request_size;
 	conn->buf_size = (int)ctx->max_request_size;
 
 
 	conn->dom_ctx = &(ctx->dd); /* Use default domain and default host */
 	conn->dom_ctx = &(ctx->dd); /* Use default domain and default host */
-	conn->host = NULL;          /* until we have more information. */
 
 
 	conn->tls_user_ptr = tls.user_ptr; /* store ptr for quick access */
 	conn->tls_user_ptr = tls.user_ptr; /* store ptr for quick access */