Fossil SCM
An alternate approach to [ae8fc0e0b5e6] which instead rejects all GET and COOKIE values which, after decoding, contain any control characters. We have(?) no(?) use cases where control characters are legitimately needed for GET/COOKIE values.
Commit
c61ae84cabe665acd7dd2a062bd9da2a1af94b6808324701112fa3530517809b
Parent
55c972103ffc130…
1 file changed
+22
-5
+22
-5
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -944,10 +944,20 @@ | ||
| 944 | 944 | ** portion is fixed but a copy is be made of zValue. |
| 945 | 945 | */ |
| 946 | 946 | void cgi_setenv(const char *zName, const char *zValue){ |
| 947 | 947 | cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0); |
| 948 | 948 | } |
| 949 | + | |
| 950 | +/* | |
| 951 | +** Returns true if NUL-terminated z contains any non-NUL | |
| 952 | +** control characters (<0x20, 32d). | |
| 953 | +*/ | |
| 954 | +static int contains_ctrl(const char *z){ | |
| 955 | + assert(z); | |
| 956 | + for( ; *z>=0x20; ++z ){} | |
| 957 | + return 0!=*z; | |
| 958 | +} | |
| 949 | 959 | |
| 950 | 960 | /* |
| 951 | 961 | ** Add a list of query parameters or cookies to the parameter set. |
| 952 | 962 | ** |
| 953 | 963 | ** Each parameter is of the form NAME=VALUE. Both the NAME and the |
| @@ -974,12 +984,16 @@ | ||
| 974 | 984 | ** before the NAME is ignored. |
| 975 | 985 | ** |
| 976 | 986 | ** The input string "z" is modified but no copies is made. "z" |
| 977 | 987 | ** should not be deallocated or changed again after this routine |
| 978 | 988 | ** returns or it will corrupt the parameter table. |
| 989 | +** | |
| 990 | +** If bPermitCtrl is false and the decoded value of any entry in z | |
| 991 | +** contains control characters (<0x20, 32d) then that key/value pair | |
| 992 | +** are skipped. | |
| 979 | 993 | */ |
| 980 | -static void add_param_list(char *z, int terminator){ | |
| 994 | +static void add_param_list(char *z, int terminator, int bPermitCtrl){ | |
| 981 | 995 | int isQP = terminator=='&'; |
| 982 | 996 | while( *z ){ |
| 983 | 997 | char *zName; |
| 984 | 998 | char *zValue; |
| 985 | 999 | while( fossil_isspace(*z) ){ z++; } |
| @@ -998,11 +1012,14 @@ | ||
| 998 | 1012 | }else{ |
| 999 | 1013 | if( *z ){ *z++ = 0; } |
| 1000 | 1014 | zValue = ""; |
| 1001 | 1015 | } |
| 1002 | 1016 | if( zName[0] && fossil_no_strange_characters(zName+1) ){ |
| 1003 | - if( fossil_islower(zName[0]) ){ | |
| 1017 | + if( 0==bPermitCtrl && contains_ctrl(zValue) ){ | |
| 1018 | + continue /* Reject it. An argument could be made | |
| 1019 | + ** for break instead of continue. */; | |
| 1020 | + }else if( fossil_islower(zName[0]) ){ | |
| 1004 | 1021 | cgi_set_parameter_nocopy(zName, zValue, isQP); |
| 1005 | 1022 | }else if( fossil_isupper(zName[0]) ){ |
| 1006 | 1023 | cgi_set_parameter_nocopy_tolower(zName, zValue, isQP); |
| 1007 | 1024 | } |
| 1008 | 1025 | } |
| @@ -1297,11 +1314,11 @@ | ||
| 1297 | 1314 | int rc = 0; |
| 1298 | 1315 | char * z = (char*)P("QUERY_STRING"); |
| 1299 | 1316 | if( z ){ |
| 1300 | 1317 | rc = 0x01; |
| 1301 | 1318 | z = fossil_strdup(z); |
| 1302 | - add_param_list(z, '&'); | |
| 1319 | + add_param_list(z, '&', 0); | |
| 1303 | 1320 | z = (char*)P("skin"); |
| 1304 | 1321 | if( z ){ |
| 1305 | 1322 | char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM); |
| 1306 | 1323 | rc |= 0x02; |
| 1307 | 1324 | if( !zErr && P("once")==0 ){ |
| @@ -1457,11 +1474,11 @@ | ||
| 1457 | 1474 | } |
| 1458 | 1475 | #endif |
| 1459 | 1476 | z = (char*)P("HTTP_COOKIE"); |
| 1460 | 1477 | if( z ){ |
| 1461 | 1478 | z = fossil_strdup(z); |
| 1462 | - add_param_list(z, ';'); | |
| 1479 | + add_param_list(z, ';', 0); | |
| 1463 | 1480 | z = (char*)cookie_value("skin",0); |
| 1464 | 1481 | if(z){ |
| 1465 | 1482 | skin_use_alternative(z, 2, SKIN_FROM_COOKIE); |
| 1466 | 1483 | } |
| 1467 | 1484 | } |
| @@ -1520,11 +1537,11 @@ | ||
| 1520 | 1537 | || fossil_strncmp(g.zContentType,"multipart/form-data",19)==0 |
| 1521 | 1538 | ){ |
| 1522 | 1539 | char *z = blob_str(&g.cgiIn); |
| 1523 | 1540 | cgi_trace(z); |
| 1524 | 1541 | if( g.zContentType[0]=='a' ){ |
| 1525 | - add_param_list(z, '&'); | |
| 1542 | + add_param_list(z, '&', 1); | |
| 1526 | 1543 | }else{ |
| 1527 | 1544 | process_multipart_form_data(z, len); |
| 1528 | 1545 | } |
| 1529 | 1546 | blob_init(&g.cgiIn, 0, 0); |
| 1530 | 1547 | } |
| 1531 | 1548 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -944,10 +944,20 @@ | |
| 944 | ** portion is fixed but a copy is be made of zValue. |
| 945 | */ |
| 946 | void cgi_setenv(const char *zName, const char *zValue){ |
| 947 | cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0); |
| 948 | } |
| 949 | |
| 950 | /* |
| 951 | ** Add a list of query parameters or cookies to the parameter set. |
| 952 | ** |
| 953 | ** Each parameter is of the form NAME=VALUE. Both the NAME and the |
| @@ -974,12 +984,16 @@ | |
| 974 | ** before the NAME is ignored. |
| 975 | ** |
| 976 | ** The input string "z" is modified but no copies is made. "z" |
| 977 | ** should not be deallocated or changed again after this routine |
| 978 | ** returns or it will corrupt the parameter table. |
| 979 | */ |
| 980 | static void add_param_list(char *z, int terminator){ |
| 981 | int isQP = terminator=='&'; |
| 982 | while( *z ){ |
| 983 | char *zName; |
| 984 | char *zValue; |
| 985 | while( fossil_isspace(*z) ){ z++; } |
| @@ -998,11 +1012,14 @@ | |
| 998 | }else{ |
| 999 | if( *z ){ *z++ = 0; } |
| 1000 | zValue = ""; |
| 1001 | } |
| 1002 | if( zName[0] && fossil_no_strange_characters(zName+1) ){ |
| 1003 | if( fossil_islower(zName[0]) ){ |
| 1004 | cgi_set_parameter_nocopy(zName, zValue, isQP); |
| 1005 | }else if( fossil_isupper(zName[0]) ){ |
| 1006 | cgi_set_parameter_nocopy_tolower(zName, zValue, isQP); |
| 1007 | } |
| 1008 | } |
| @@ -1297,11 +1314,11 @@ | |
| 1297 | int rc = 0; |
| 1298 | char * z = (char*)P("QUERY_STRING"); |
| 1299 | if( z ){ |
| 1300 | rc = 0x01; |
| 1301 | z = fossil_strdup(z); |
| 1302 | add_param_list(z, '&'); |
| 1303 | z = (char*)P("skin"); |
| 1304 | if( z ){ |
| 1305 | char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM); |
| 1306 | rc |= 0x02; |
| 1307 | if( !zErr && P("once")==0 ){ |
| @@ -1457,11 +1474,11 @@ | |
| 1457 | } |
| 1458 | #endif |
| 1459 | z = (char*)P("HTTP_COOKIE"); |
| 1460 | if( z ){ |
| 1461 | z = fossil_strdup(z); |
| 1462 | add_param_list(z, ';'); |
| 1463 | z = (char*)cookie_value("skin",0); |
| 1464 | if(z){ |
| 1465 | skin_use_alternative(z, 2, SKIN_FROM_COOKIE); |
| 1466 | } |
| 1467 | } |
| @@ -1520,11 +1537,11 @@ | |
| 1520 | || fossil_strncmp(g.zContentType,"multipart/form-data",19)==0 |
| 1521 | ){ |
| 1522 | char *z = blob_str(&g.cgiIn); |
| 1523 | cgi_trace(z); |
| 1524 | if( g.zContentType[0]=='a' ){ |
| 1525 | add_param_list(z, '&'); |
| 1526 | }else{ |
| 1527 | process_multipart_form_data(z, len); |
| 1528 | } |
| 1529 | blob_init(&g.cgiIn, 0, 0); |
| 1530 | } |
| 1531 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -944,10 +944,20 @@ | |
| 944 | ** portion is fixed but a copy is be made of zValue. |
| 945 | */ |
| 946 | void cgi_setenv(const char *zName, const char *zValue){ |
| 947 | cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0); |
| 948 | } |
| 949 | |
| 950 | /* |
| 951 | ** Returns true if NUL-terminated z contains any non-NUL |
| 952 | ** control characters (<0x20, 32d). |
| 953 | */ |
| 954 | static int contains_ctrl(const char *z){ |
| 955 | assert(z); |
| 956 | for( ; *z>=0x20; ++z ){} |
| 957 | return 0!=*z; |
| 958 | } |
| 959 | |
| 960 | /* |
| 961 | ** Add a list of query parameters or cookies to the parameter set. |
| 962 | ** |
| 963 | ** Each parameter is of the form NAME=VALUE. Both the NAME and the |
| @@ -974,12 +984,16 @@ | |
| 984 | ** before the NAME is ignored. |
| 985 | ** |
| 986 | ** The input string "z" is modified but no copies is made. "z" |
| 987 | ** should not be deallocated or changed again after this routine |
| 988 | ** returns or it will corrupt the parameter table. |
| 989 | ** |
| 990 | ** If bPermitCtrl is false and the decoded value of any entry in z |
| 991 | ** contains control characters (<0x20, 32d) then that key/value pair |
| 992 | ** are skipped. |
| 993 | */ |
| 994 | static void add_param_list(char *z, int terminator, int bPermitCtrl){ |
| 995 | int isQP = terminator=='&'; |
| 996 | while( *z ){ |
| 997 | char *zName; |
| 998 | char *zValue; |
| 999 | while( fossil_isspace(*z) ){ z++; } |
| @@ -998,11 +1012,14 @@ | |
| 1012 | }else{ |
| 1013 | if( *z ){ *z++ = 0; } |
| 1014 | zValue = ""; |
| 1015 | } |
| 1016 | if( zName[0] && fossil_no_strange_characters(zName+1) ){ |
| 1017 | if( 0==bPermitCtrl && contains_ctrl(zValue) ){ |
| 1018 | continue /* Reject it. An argument could be made |
| 1019 | ** for break instead of continue. */; |
| 1020 | }else if( fossil_islower(zName[0]) ){ |
| 1021 | cgi_set_parameter_nocopy(zName, zValue, isQP); |
| 1022 | }else if( fossil_isupper(zName[0]) ){ |
| 1023 | cgi_set_parameter_nocopy_tolower(zName, zValue, isQP); |
| 1024 | } |
| 1025 | } |
| @@ -1297,11 +1314,11 @@ | |
| 1314 | int rc = 0; |
| 1315 | char * z = (char*)P("QUERY_STRING"); |
| 1316 | if( z ){ |
| 1317 | rc = 0x01; |
| 1318 | z = fossil_strdup(z); |
| 1319 | add_param_list(z, '&', 0); |
| 1320 | z = (char*)P("skin"); |
| 1321 | if( z ){ |
| 1322 | char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM); |
| 1323 | rc |= 0x02; |
| 1324 | if( !zErr && P("once")==0 ){ |
| @@ -1457,11 +1474,11 @@ | |
| 1474 | } |
| 1475 | #endif |
| 1476 | z = (char*)P("HTTP_COOKIE"); |
| 1477 | if( z ){ |
| 1478 | z = fossil_strdup(z); |
| 1479 | add_param_list(z, ';', 0); |
| 1480 | z = (char*)cookie_value("skin",0); |
| 1481 | if(z){ |
| 1482 | skin_use_alternative(z, 2, SKIN_FROM_COOKIE); |
| 1483 | } |
| 1484 | } |
| @@ -1520,11 +1537,11 @@ | |
| 1537 | || fossil_strncmp(g.zContentType,"multipart/form-data",19)==0 |
| 1538 | ){ |
| 1539 | char *z = blob_str(&g.cgiIn); |
| 1540 | cgi_trace(z); |
| 1541 | if( g.zContentType[0]=='a' ){ |
| 1542 | add_param_list(z, '&', 1); |
| 1543 | }else{ |
| 1544 | process_multipart_form_data(z, len); |
| 1545 | } |
| 1546 | blob_init(&g.cgiIn, 0, 0); |
| 1547 | } |
| 1548 |