@@ -2087,7 +2087,7 @@ enum {
20872087#if defined(USE_LUA) && defined(USE_WEBSOCKET)
20882088 LUA_WEBSOCKET_EXTENSIONS,
20892089#endif
2090-
2090+ REPLACE_ASTERISK_WITH_ORIGIN,
20912091 ACCESS_CONTROL_ALLOW_ORIGIN,
20922092 ACCESS_CONTROL_ALLOW_METHODS,
20932093 ACCESS_CONTROL_ALLOW_HEADERS,
@@ -2253,6 +2253,7 @@ static const struct mg_option config_options[] = {
22532253#if defined(USE_LUA) && defined(USE_WEBSOCKET)
22542254 {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
22552255#endif
2256+ {"replace_asterisk_with_origin", MG_CONFIG_TYPE_BOOLEAN, "no"},
22562257 {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"},
22572258 {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"},
22582259 {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"},
@@ -4234,16 +4235,27 @@ send_cors_header(struct mg_connection *conn)
42344235 conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
42354236 const char *cors_meth_cfg =
42364237 conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
4237-
4238- if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) {
4238+ const char *cors_repl_asterisk_with_orig_cfg =
4239+ conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN];
4240+
4241+ if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr && cors_repl_asterisk_with_orig_cfg && *cors_repl_asterisk_with_orig_cfg) {
4242+ int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes");
4243+
42394244 /* Cross-origin resource sharing (CORS), see
42404245 * http://www.html5rocks.com/en/tutorials/cors/,
42414246 * http://www.html5rocks.com/static/images/cors_server_flowchart.png
42424247 * CORS preflight is not supported for files. */
4243- mg_response_header_add(conn,
4248+ if (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') {
4249+ mg_response_header_add(conn,
4250+ "Access-Control-Allow-Origin",
4251+ origin_hdr,
4252+ -1);
4253+ } else {
4254+ mg_response_header_add(conn,
42444255 "Access-Control-Allow-Origin",
42454256 cors_orig_cfg,
42464257 -1);
4258+ }
42474259 }
42484260
42494261 if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) {
@@ -15088,7 +15100,7 @@ handle_request(struct mg_connection *conn)
1508815100 }
1508915101 return;
1509015102 }
15091- uri_len = (int)strlen(ri->local_uri);
15103+
1509215104
1509315105 /* 1.3. decode url (if config says so) */
1509415106 if (should_decode_url(conn)) {
@@ -15116,6 +15128,12 @@ handle_request(struct mg_connection *conn)
1511615128 }
1511715129 remove_dot_segments(tmp);
1511815130 ri->local_uri = tmp;
15131+ #if !defined(NO_FILES) /* Only compute if later code can actually use it */
15132+ /* Cache URI length once; recompute only if the buffer changes later. */
15133+ uri_len = (int)strlen(ri->local_uri);
15134+ #endif
15135+
15136+
1511915137
1512015138 /* step 1. completed, the url is known now */
1512115139 DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri);
@@ -15168,13 +15186,18 @@ handle_request(struct mg_connection *conn)
1516815186 const char *cors_acrm = get_header(ri->http_headers,
1516915187 ri->num_headers,
1517015188 "Access-Control-Request-Method");
15171-
15189+ const char *cors_repl_asterisk_with_orig_cfg =
15190+ conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN];
15191+
1517215192 /* Todo: check if cors_origin is in cors_orig_cfg.
1517315193 * Or, let the client check this. */
1517415194
1517515195 if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0)
1517615196 && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
15177- && (cors_origin != NULL) && (cors_acrm != NULL)) {
15197+ && (cors_origin != NULL) && (cors_acrm != NULL)
15198+ && (cors_repl_asterisk_with_orig_cfg != NULL) && (*cors_repl_asterisk_with_orig_cfg != 0)) {
15199+ int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes");
15200+
1517815201 /* This is a valid CORS preflight, and the server is configured
1517915202 * to handle it automatically. */
1518015203 const char *cors_acrh =
@@ -15195,7 +15218,7 @@ handle_request(struct mg_connection *conn)
1519515218 "Content-Length: 0\r\n"
1519615219 "Connection: %s\r\n",
1519715220 date,
15198- cors_orig_cfg,
15221+ (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg,
1519915222 ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
1520015223 suggest_connection_header(conn));
1520115224
@@ -15586,8 +15609,9 @@ handle_request(struct mg_connection *conn)
1558615609 }
1558715610
1558815611 /* 12. Directory uris should end with a slash */
15589- if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0)
15590- && (ri->local_uri[uri_len - 1] != '/')) {
15612+ if (file.stat.is_directory && (uri_len > 0)
15613+ && (ri->local_uri[uri_len - 1] != '/')) {
15614+
1559115615
1559215616 /* Path + server root */
1559315617 size_t buflen = UTF8_PATH_MAX * 2 + 2;
@@ -15601,12 +15625,26 @@ handle_request(struct mg_connection *conn)
1560115625 mg_send_http_error(conn, 500, "out or memory");
1560215626 } else {
1560315627 mg_get_request_link(conn, new_path, buflen - 1);
15604- strcat(new_path, "/");
15628+
15629+ size_t len = strlen(new_path);
15630+ if (len + 1 < buflen) {
15631+ new_path[len] = '/';
15632+ new_path[len + 1] = '\0';
15633+ len++;
15634+ }
15635+
1560515636 if (ri->query_string) {
15606- /* Append ? and query string */
15607- strcat(new_path, "?");
15608- strcat(new_path, ri->query_string);
15637+ if (len + 1 < buflen) {
15638+ new_path[len] = '?';
15639+ new_path[len + 1] = '\0';
15640+ len++;
15641+ }
15642+
15643+ /* Append with size of space left for query string + null terminator */
15644+ size_t max_append = buflen - len - 1;
15645+ strncat(new_path, ri->query_string, max_append);
1560915646 }
15647+
1561015648 mg_send_http_redirect(conn, new_path, 301);
1561115649 mg_free(new_path);
1561215650 }
@@ -18867,6 +18905,19 @@ get_uri_type(const char *uri)
1886718905 * and % encoded symbols.
1886818906 */
1886918907 for (i = 0; uri[i] != 0; i++) {
18908+ /* Check for CRLF injection attempts */
18909+ if (uri[i] == '%') {
18910+ if (uri[i+1] == '0' && (uri[i+2] == 'd' || uri[i+2] == 'D')) {
18911+ /* Found %0d (CR) */
18912+ DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri);
18913+ return 0;
18914+ }
18915+ if (uri[i+1] == '0' && (uri[i+2] == 'a' || uri[i+2] == 'A')) {
18916+ /* Found %0a (LF) */
18917+ DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri);
18918+ return 0;
18919+ }
18920+ }
1887018921 if ((unsigned char)uri[i] < 33) {
1887118922 /* control characters and spaces are invalid */
1887218923 return 0;
0 commit comments