Browse Source

Merge branch 'master' of https://github.com/civetweb/civetweb

bel2125 2 days ago
parent
commit
b3a34ad21f
6 changed files with 72 additions and 21 deletions
  1. 1 1
      .github/workflows/cibuild.yml
  2. 1 1
      .github/workflows/codeql.yml
  3. 1 1
      README.md
  4. 65 14
      src/civetweb.c
  5. 2 2
      test/page4.lp
  6. 2 2
      test/page4kepler.lp

+ 1 - 1
.github/workflows/cibuild.yml

@@ -392,7 +392,7 @@ jobs:
 
 
     steps:
     steps:
         - name: Checkout code
         - name: Checkout code
-          uses: actions/checkout@v4.1.7
+          uses: actions/checkout@v5
 
 
         - name: Export number of CPUs
         - name: Export number of CPUs
           run: |
           run: |

+ 1 - 1
.github/workflows/codeql.yml

@@ -45,7 +45,7 @@ jobs:
 
 
     steps:
     steps:
     - name: Checkout repository
     - name: Checkout repository
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
       with:
         submodules: recursive
         submodules: recursive
 
 

+ 1 - 1
README.md

@@ -172,7 +172,7 @@ CivetWeb is based on the [Mongoose project](https://github.com/cesanta/mongoose)
 Sergey Lyubka(2004-2013) who released it under the MIT license.
 Sergey Lyubka(2004-2013) who released it under the MIT license.
 However, on August 16, 2013,
 However, on August 16, 2013,
 [Mongoose was relicensed to a dual GPL V2 + commercial license](https://groups.google.com/forum/#!topic/mongoose-users/aafbOnHonkI)
 [Mongoose was relicensed to a dual GPL V2 + commercial license](https://groups.google.com/forum/#!topic/mongoose-users/aafbOnHonkI)
-and CiwetWeb was created by Thomas Davis (sunsetbrew) as "the MIT fork of mongoose".
+and CivetWeb was created by Thomas Davis (sunsetbrew) as "the MIT fork of mongoose".
 The license change and CivetWeb fork was mentioned on the Mongoose
 The license change and CivetWeb fork was mentioned on the Mongoose
 [Wikipedia](https://en.wikipedia.org/wiki/Mongoose_(web_server))
 [Wikipedia](https://en.wikipedia.org/wiki/Mongoose_(web_server))
 page as well, but it's getting deleted (and added again) there every
 page as well, but it's getting deleted (and added again) there every

+ 65 - 14
src/civetweb.c

@@ -2087,7 +2087,7 @@ enum {
 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
 	LUA_WEBSOCKET_EXTENSIONS,
 	LUA_WEBSOCKET_EXTENSIONS,
 #endif
 #endif
-
+	REPLACE_ASTERISK_WITH_ORIGIN,
 	ACCESS_CONTROL_ALLOW_ORIGIN,
 	ACCESS_CONTROL_ALLOW_ORIGIN,
 	ACCESS_CONTROL_ALLOW_METHODS,
 	ACCESS_CONTROL_ALLOW_METHODS,
 	ACCESS_CONTROL_ALLOW_HEADERS,
 	ACCESS_CONTROL_ALLOW_HEADERS,
@@ -2253,6 +2253,7 @@ static const struct mg_option config_options[] = {
 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
     {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
     {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
 #endif
 #endif
+	{"replace_asterisk_with_origin", MG_CONFIG_TYPE_BOOLEAN, "no"},
     {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"},
     {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"},
     {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"},
     {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"},
     {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"},
     {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"},
@@ -4234,16 +4235,27 @@ send_cors_header(struct mg_connection *conn)
 	    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
 	    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
 	const char *cors_meth_cfg =
 	const char *cors_meth_cfg =
 	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
 	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
-
-	if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) {
+	const char *cors_repl_asterisk_with_orig_cfg = 
+		conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN];
+		
+	if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr && cors_repl_asterisk_with_orig_cfg && *cors_repl_asterisk_with_orig_cfg) {
+		int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes");
+		
 		/* Cross-origin resource sharing (CORS), see
 		/* Cross-origin resource sharing (CORS), see
 		 * http://www.html5rocks.com/en/tutorials/cors/,
 		 * http://www.html5rocks.com/en/tutorials/cors/,
 		 * http://www.html5rocks.com/static/images/cors_server_flowchart.png
 		 * http://www.html5rocks.com/static/images/cors_server_flowchart.png
 		 * CORS preflight is not supported for files. */
 		 * CORS preflight is not supported for files. */
-		mg_response_header_add(conn,
+		if (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') {
+			mg_response_header_add(conn,
+		                       "Access-Control-Allow-Origin",
+		                       origin_hdr,
+		                       -1);
+		} else {
+			mg_response_header_add(conn,
 		                       "Access-Control-Allow-Origin",
 		                       "Access-Control-Allow-Origin",
 		                       cors_orig_cfg,
 		                       cors_orig_cfg,
 		                       -1);
 		                       -1);
+		}
 	}
 	}
 
 
 	if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) {
 	if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) {
@@ -15088,7 +15100,7 @@ handle_request(struct mg_connection *conn)
 		}
 		}
 		return;
 		return;
 	}
 	}
-	uri_len = (int)strlen(ri->local_uri);
+	
 
 
 	/* 1.3. decode url (if config says so) */
 	/* 1.3. decode url (if config says so) */
 	if (should_decode_url(conn)) {
 	if (should_decode_url(conn)) {
@@ -15116,6 +15128,12 @@ handle_request(struct mg_connection *conn)
 	}
 	}
 	remove_dot_segments(tmp);
 	remove_dot_segments(tmp);
 	ri->local_uri = tmp;
 	ri->local_uri = tmp;
+	#if !defined(NO_FILES)  /* Only compute if later code can actually use it */
+    /* Cache URI length once; recompute only if the buffer changes later. */
+       uri_len = (int)strlen(ri->local_uri);
+    #endif
+
+
 
 
 	/* step 1. completed, the url is known now */
 	/* step 1. completed, the url is known now */
 	DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri);
 	DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri);
@@ -15168,13 +15186,18 @@ handle_request(struct mg_connection *conn)
 		const char *cors_acrm = get_header(ri->http_headers,
 		const char *cors_acrm = get_header(ri->http_headers,
 		                                   ri->num_headers,
 		                                   ri->num_headers,
 		                                   "Access-Control-Request-Method");
 		                                   "Access-Control-Request-Method");
-
+		const char *cors_repl_asterisk_with_orig_cfg = 
+			conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN];
+		
 		/* Todo: check if cors_origin is in cors_orig_cfg.
 		/* Todo: check if cors_origin is in cors_orig_cfg.
 		 * Or, let the client check this. */
 		 * Or, let the client check this. */
 
 
 		if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0)
 		if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0)
 		    && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
 		    && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
-		    && (cors_origin != NULL) && (cors_acrm != NULL)) {
+		    && (cors_origin != NULL) && (cors_acrm != NULL)
+			&& (cors_repl_asterisk_with_orig_cfg != NULL) && (*cors_repl_asterisk_with_orig_cfg != 0)) {
+			int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes");
+			
 			/* This is a valid CORS preflight, and the server is configured
 			/* This is a valid CORS preflight, and the server is configured
 			 * to handle it automatically. */
 			 * to handle it automatically. */
 			const char *cors_acrh =
 			const char *cors_acrh =
@@ -15195,7 +15218,7 @@ handle_request(struct mg_connection *conn)
 			          "Content-Length: 0\r\n"
 			          "Content-Length: 0\r\n"
 			          "Connection: %s\r\n",
 			          "Connection: %s\r\n",
 			          date,
 			          date,
-			          cors_orig_cfg,
+			          (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg,
 			          ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
 			          ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
 			          suggest_connection_header(conn));
 			          suggest_connection_header(conn));
 
 
@@ -15586,8 +15609,9 @@ handle_request(struct mg_connection *conn)
 	}
 	}
 
 
 	/* 12. Directory uris should end with a slash */
 	/* 12. Directory uris should end with a slash */
-	if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0)
-	    && (ri->local_uri[uri_len - 1] != '/')) {
+	if (file.stat.is_directory && (uri_len > 0)
+        && (ri->local_uri[uri_len - 1] != '/')) {
+
 
 
 		/* Path + server root */
 		/* Path + server root */
 		size_t buflen = UTF8_PATH_MAX * 2 + 2;
 		size_t buflen = UTF8_PATH_MAX * 2 + 2;
@@ -15601,12 +15625,26 @@ handle_request(struct mg_connection *conn)
 			mg_send_http_error(conn, 500, "out or memory");
 			mg_send_http_error(conn, 500, "out or memory");
 		} else {
 		} else {
 			mg_get_request_link(conn, new_path, buflen - 1);
 			mg_get_request_link(conn, new_path, buflen - 1);
-			strcat(new_path, "/");
+
+			size_t len = strlen(new_path);
+			if (len + 1 < buflen) {
+				new_path[len] = '/';
+				new_path[len + 1] = '\0';
+				len++;
+			}
+
 			if (ri->query_string) {
 			if (ri->query_string) {
-				/* Append ? and query string */
-				strcat(new_path, "?");
-				strcat(new_path, ri->query_string);
+				if (len + 1 < buflen) {
+					new_path[len] = '?';
+					new_path[len + 1] = '\0';
+					len++;
+				}
+
+				/* Append with size of space left for query string + null terminator */
+				size_t max_append = buflen - len - 1;
+				strncat(new_path, ri->query_string, max_append);
 			}
 			}
+
 			mg_send_http_redirect(conn, new_path, 301);
 			mg_send_http_redirect(conn, new_path, 301);
 			mg_free(new_path);
 			mg_free(new_path);
 		}
 		}
@@ -18867,6 +18905,19 @@ get_uri_type(const char *uri)
 	 * and % encoded symbols.
 	 * and % encoded symbols.
 	 */
 	 */
 	for (i = 0; uri[i] != 0; i++) {
 	for (i = 0; uri[i] != 0; i++) {
+		/* Check for CRLF injection attempts */
+		if (uri[i] == '%') {
+			if (uri[i+1] == '0' && (uri[i+2] == 'd' || uri[i+2] == 'D')) {
+				/* Found %0d (CR) */
+				DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri);
+				return 0;
+			}
+			if (uri[i+1] == '0' && (uri[i+2] == 'a' || uri[i+2] == 'A')) {
+				/* Found %0a (LF) */
+				DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri);
+				return 0;
+			}
+		}
 		if ((unsigned char)uri[i] < 33) {
 		if ((unsigned char)uri[i] < 33) {
 			/* control characters and spaces are invalid */
 			/* control characters and spaces are invalid */
 			return 0;
 			return 0;

+ 2 - 2
test/page4.lp

@@ -22,10 +22,10 @@ the "Kepler syntax" uses <code>&lt;?lua chunk ?&gt;</code>, <code>&lt;?lua= expr
 <h2>Tags</h2>
 <h2>Tags</h2>
 
 
 <code>
 <code>
-&lt;? greeting = 'CiwetWeb' ?&gt;<br/>
+&lt;? greeting = 'CivetWeb' ?&gt;<br/>
 &lt;strong&gt;&lt;?= greeting %&gt;&lt;/strong&gt;<br/>
 &lt;strong&gt;&lt;?= greeting %&gt;&lt;/strong&gt;<br/>
 </code><br/>
 </code><br/>
-<? greeting = 'CiwetWeb' ?>
+<? greeting = 'CivetWeb' ?>
 ==> <strong><?= greeting ?></strong><br/>
 ==> <strong><?= greeting ?></strong><br/>
 
 
 <br/>
 <br/>

+ 2 - 2
test/page4kepler.lp

@@ -19,10 +19,10 @@ the "Kepler syntax" uses <code>&lt;?lua chunk ?&gt;</code>, <code>&lt;?lua= expr
 <h2>Tags</h2>
 <h2>Tags</h2>
 
 
 <code>
 <code>
-&lt;? greeting = 'CiwetWeb' ?&gt;<br/>
+&lt;? greeting = 'CivetWeb' ?&gt;<br/>
 &lt;strong&gt;&lt;?= greeting %&gt;&lt;/strong&gt;<br/>
 &lt;strong&gt;&lt;?= greeting %&gt;&lt;/strong&gt;<br/>
 </code><br/>
 </code><br/>
-<? greeting = 'CiwetWeb' ?>
+<? greeting = 'CivetWeb' ?>
 ==> <strong><?= greeting ?></strong><br/>
 ==> <strong><?= greeting ?></strong><br/>
 
 
 <br/>
 <br/>