Fossil SCM

Reject all GET/COOKIE vars in which the values contain control characters.

stephan 2025-09-01 15:37 trunk merge
Commit 0c1419a466f2152bdf31bc3a8985349df6b45e749a93226330a5840a5b469c65
2 files changed +22 -5 +22 -5
+22 -5
--- src/cgi.c
+++ src/cgi.c
@@ -946,10 +946,20 @@
946946
** portion is fixed but a copy is be made of zValue.
947947
*/
948948
void cgi_setenv(const char *zName, const char *zValue){
949949
cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
950950
}
951
+
952
+/*
953
+** Returns true if NUL-terminated z contains any non-NUL
954
+** control characters (<0x20, 32d).
955
+*/
956
+static int contains_ctrl(const char *z){
957
+ assert(z);
958
+ for( ; *z>=0x20; ++z ){}
959
+ return 0!=*z;
960
+}
951961
952962
/*
953963
** Add a list of query parameters or cookies to the parameter set.
954964
**
955965
** Each parameter is of the form NAME=VALUE. Both the NAME and the
@@ -976,12 +986,16 @@
976986
** before the NAME is ignored.
977987
**
978988
** The input string "z" is modified but no copies is made. "z"
979989
** should not be deallocated or changed again after this routine
980990
** returns or it will corrupt the parameter table.
991
+**
992
+** If bPermitCtrl is false and the decoded value of any entry in z
993
+** contains control characters (<0x20, 32d) then that key/value pair
994
+** are skipped.
981995
*/
982
-static void add_param_list(char *z, int terminator){
996
+static void add_param_list(char *z, int terminator, int bPermitCtrl){
983997
int isQP = terminator=='&';
984998
while( *z ){
985999
char *zName;
9861000
char *zValue;
9871001
while( fossil_isspace(*z) ){ z++; }
@@ -1000,11 +1014,14 @@
10001014
}else{
10011015
if( *z ){ *z++ = 0; }
10021016
zValue = "";
10031017
}
10041018
if( zName[0] && fossil_no_strange_characters(zName+1) ){
1005
- if( fossil_islower(zName[0]) ){
1019
+ if( 0==bPermitCtrl && contains_ctrl(zValue) ){
1020
+ continue /* Reject it. An argument could be made
1021
+ ** for break instead of continue. */;
1022
+ }else if( fossil_islower(zName[0]) ){
10061023
cgi_set_parameter_nocopy(zName, zValue, isQP);
10071024
}else if( fossil_isupper(zName[0]) ){
10081025
cgi_set_parameter_nocopy_tolower(zName, zValue, isQP);
10091026
}
10101027
}
@@ -1299,11 +1316,11 @@
12991316
int rc = 0;
13001317
char * z = (char*)P("QUERY_STRING");
13011318
if( z ){
13021319
rc = 0x01;
13031320
z = fossil_strdup(z);
1304
- add_param_list(z, '&');
1321
+ add_param_list(z, '&', 0);
13051322
z = (char*)P("skin");
13061323
if( z ){
13071324
char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
13081325
rc |= 0x02;
13091326
if( !zErr && P("once")==0 ){
@@ -1459,11 +1476,11 @@
14591476
}
14601477
#endif
14611478
z = (char*)P("HTTP_COOKIE");
14621479
if( z ){
14631480
z = fossil_strdup(z);
1464
- add_param_list(z, ';');
1481
+ add_param_list(z, ';', 0);
14651482
z = (char*)cookie_value("skin",0);
14661483
if(z){
14671484
skin_use_alternative(z, 2, SKIN_FROM_COOKIE);
14681485
}
14691486
}
@@ -1522,11 +1539,11 @@
15221539
|| fossil_strncmp(g.zContentType,"multipart/form-data",19)==0
15231540
){
15241541
char *z = blob_str(&g.cgiIn);
15251542
cgi_trace(z);
15261543
if( g.zContentType[0]=='a' ){
1527
- add_param_list(z, '&');
1544
+ add_param_list(z, '&', 1);
15281545
}else{
15291546
process_multipart_form_data(z, len);
15301547
}
15311548
blob_init(&g.cgiIn, 0, 0);
15321549
}
15331550
--- src/cgi.c
+++ src/cgi.c
@@ -946,10 +946,20 @@
946 ** portion is fixed but a copy is be made of zValue.
947 */
948 void cgi_setenv(const char *zName, const char *zValue){
949 cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
950 }
 
 
 
 
 
 
 
 
 
 
951
952 /*
953 ** Add a list of query parameters or cookies to the parameter set.
954 **
955 ** Each parameter is of the form NAME=VALUE. Both the NAME and the
@@ -976,12 +986,16 @@
976 ** before the NAME is ignored.
977 **
978 ** The input string "z" is modified but no copies is made. "z"
979 ** should not be deallocated or changed again after this routine
980 ** returns or it will corrupt the parameter table.
 
 
 
 
981 */
982 static void add_param_list(char *z, int terminator){
983 int isQP = terminator=='&';
984 while( *z ){
985 char *zName;
986 char *zValue;
987 while( fossil_isspace(*z) ){ z++; }
@@ -1000,11 +1014,14 @@
1000 }else{
1001 if( *z ){ *z++ = 0; }
1002 zValue = "";
1003 }
1004 if( zName[0] && fossil_no_strange_characters(zName+1) ){
1005 if( fossil_islower(zName[0]) ){
 
 
 
1006 cgi_set_parameter_nocopy(zName, zValue, isQP);
1007 }else if( fossil_isupper(zName[0]) ){
1008 cgi_set_parameter_nocopy_tolower(zName, zValue, isQP);
1009 }
1010 }
@@ -1299,11 +1316,11 @@
1299 int rc = 0;
1300 char * z = (char*)P("QUERY_STRING");
1301 if( z ){
1302 rc = 0x01;
1303 z = fossil_strdup(z);
1304 add_param_list(z, '&');
1305 z = (char*)P("skin");
1306 if( z ){
1307 char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
1308 rc |= 0x02;
1309 if( !zErr && P("once")==0 ){
@@ -1459,11 +1476,11 @@
1459 }
1460 #endif
1461 z = (char*)P("HTTP_COOKIE");
1462 if( z ){
1463 z = fossil_strdup(z);
1464 add_param_list(z, ';');
1465 z = (char*)cookie_value("skin",0);
1466 if(z){
1467 skin_use_alternative(z, 2, SKIN_FROM_COOKIE);
1468 }
1469 }
@@ -1522,11 +1539,11 @@
1522 || fossil_strncmp(g.zContentType,"multipart/form-data",19)==0
1523 ){
1524 char *z = blob_str(&g.cgiIn);
1525 cgi_trace(z);
1526 if( g.zContentType[0]=='a' ){
1527 add_param_list(z, '&');
1528 }else{
1529 process_multipart_form_data(z, len);
1530 }
1531 blob_init(&g.cgiIn, 0, 0);
1532 }
1533
--- src/cgi.c
+++ src/cgi.c
@@ -946,10 +946,20 @@
946 ** portion is fixed but a copy is be made of zValue.
947 */
948 void cgi_setenv(const char *zName, const char *zValue){
949 cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
950 }
951
952 /*
953 ** Returns true if NUL-terminated z contains any non-NUL
954 ** control characters (<0x20, 32d).
955 */
956 static int contains_ctrl(const char *z){
957 assert(z);
958 for( ; *z>=0x20; ++z ){}
959 return 0!=*z;
960 }
961
962 /*
963 ** Add a list of query parameters or cookies to the parameter set.
964 **
965 ** Each parameter is of the form NAME=VALUE. Both the NAME and the
@@ -976,12 +986,16 @@
986 ** before the NAME is ignored.
987 **
988 ** The input string "z" is modified but no copies is made. "z"
989 ** should not be deallocated or changed again after this routine
990 ** returns or it will corrupt the parameter table.
991 **
992 ** If bPermitCtrl is false and the decoded value of any entry in z
993 ** contains control characters (<0x20, 32d) then that key/value pair
994 ** are skipped.
995 */
996 static void add_param_list(char *z, int terminator, int bPermitCtrl){
997 int isQP = terminator=='&';
998 while( *z ){
999 char *zName;
1000 char *zValue;
1001 while( fossil_isspace(*z) ){ z++; }
@@ -1000,11 +1014,14 @@
1014 }else{
1015 if( *z ){ *z++ = 0; }
1016 zValue = "";
1017 }
1018 if( zName[0] && fossil_no_strange_characters(zName+1) ){
1019 if( 0==bPermitCtrl && contains_ctrl(zValue) ){
1020 continue /* Reject it. An argument could be made
1021 ** for break instead of continue. */;
1022 }else if( fossil_islower(zName[0]) ){
1023 cgi_set_parameter_nocopy(zName, zValue, isQP);
1024 }else if( fossil_isupper(zName[0]) ){
1025 cgi_set_parameter_nocopy_tolower(zName, zValue, isQP);
1026 }
1027 }
@@ -1299,11 +1316,11 @@
1316 int rc = 0;
1317 char * z = (char*)P("QUERY_STRING");
1318 if( z ){
1319 rc = 0x01;
1320 z = fossil_strdup(z);
1321 add_param_list(z, '&', 0);
1322 z = (char*)P("skin");
1323 if( z ){
1324 char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
1325 rc |= 0x02;
1326 if( !zErr && P("once")==0 ){
@@ -1459,11 +1476,11 @@
1476 }
1477 #endif
1478 z = (char*)P("HTTP_COOKIE");
1479 if( z ){
1480 z = fossil_strdup(z);
1481 add_param_list(z, ';', 0);
1482 z = (char*)cookie_value("skin",0);
1483 if(z){
1484 skin_use_alternative(z, 2, SKIN_FROM_COOKIE);
1485 }
1486 }
@@ -1522,11 +1539,11 @@
1539 || fossil_strncmp(g.zContentType,"multipart/form-data",19)==0
1540 ){
1541 char *z = blob_str(&g.cgiIn);
1542 cgi_trace(z);
1543 if( g.zContentType[0]=='a' ){
1544 add_param_list(z, '&', 1);
1545 }else{
1546 process_multipart_form_data(z, len);
1547 }
1548 blob_init(&g.cgiIn, 0, 0);
1549 }
1550
+22 -5
--- src/cgi.c
+++ src/cgi.c
@@ -946,10 +946,20 @@
946946
** portion is fixed but a copy is be made of zValue.
947947
*/
948948
void cgi_setenv(const char *zName, const char *zValue){
949949
cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
950950
}
951
+
952
+/*
953
+** Returns true if NUL-terminated z contains any non-NUL
954
+** control characters (<0x20, 32d).
955
+*/
956
+static int contains_ctrl(const char *z){
957
+ assert(z);
958
+ for( ; *z>=0x20; ++z ){}
959
+ return 0!=*z;
960
+}
951961
952962
/*
953963
** Add a list of query parameters or cookies to the parameter set.
954964
**
955965
** Each parameter is of the form NAME=VALUE. Both the NAME and the
@@ -976,12 +986,16 @@
976986
** before the NAME is ignored.
977987
**
978988
** The input string "z" is modified but no copies is made. "z"
979989
** should not be deallocated or changed again after this routine
980990
** returns or it will corrupt the parameter table.
991
+**
992
+** If bPermitCtrl is false and the decoded value of any entry in z
993
+** contains control characters (<0x20, 32d) then that key/value pair
994
+** are skipped.
981995
*/
982
-static void add_param_list(char *z, int terminator){
996
+static void add_param_list(char *z, int terminator, int bPermitCtrl){
983997
int isQP = terminator=='&';
984998
while( *z ){
985999
char *zName;
9861000
char *zValue;
9871001
while( fossil_isspace(*z) ){ z++; }
@@ -1000,11 +1014,14 @@
10001014
}else{
10011015
if( *z ){ *z++ = 0; }
10021016
zValue = "";
10031017
}
10041018
if( zName[0] && fossil_no_strange_characters(zName+1) ){
1005
- if( fossil_islower(zName[0]) ){
1019
+ if( 0==bPermitCtrl && contains_ctrl(zValue) ){
1020
+ continue /* Reject it. An argument could be made
1021
+ ** for break instead of continue. */;
1022
+ }else if( fossil_islower(zName[0]) ){
10061023
cgi_set_parameter_nocopy(zName, zValue, isQP);
10071024
}else if( fossil_isupper(zName[0]) ){
10081025
cgi_set_parameter_nocopy_tolower(zName, zValue, isQP);
10091026
}
10101027
}
@@ -1299,11 +1316,11 @@
12991316
int rc = 0;
13001317
char * z = (char*)P("QUERY_STRING");
13011318
if( z ){
13021319
rc = 0x01;
13031320
z = fossil_strdup(z);
1304
- add_param_list(z, '&');
1321
+ add_param_list(z, '&', 0);
13051322
z = (char*)P("skin");
13061323
if( z ){
13071324
char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
13081325
rc |= 0x02;
13091326
if( !zErr && P("once")==0 ){
@@ -1459,11 +1476,11 @@
14591476
}
14601477
#endif
14611478
z = (char*)P("HTTP_COOKIE");
14621479
if( z ){
14631480
z = fossil_strdup(z);
1464
- add_param_list(z, ';');
1481
+ add_param_list(z, ';', 0);
14651482
z = (char*)cookie_value("skin",0);
14661483
if(z){
14671484
skin_use_alternative(z, 2, SKIN_FROM_COOKIE);
14681485
}
14691486
}
@@ -1522,11 +1539,11 @@
15221539
|| fossil_strncmp(g.zContentType,"multipart/form-data",19)==0
15231540
){
15241541
char *z = blob_str(&g.cgiIn);
15251542
cgi_trace(z);
15261543
if( g.zContentType[0]=='a' ){
1527
- add_param_list(z, '&');
1544
+ add_param_list(z, '&', 1);
15281545
}else{
15291546
process_multipart_form_data(z, len);
15301547
}
15311548
blob_init(&g.cgiIn, 0, 0);
15321549
}
15331550
--- src/cgi.c
+++ src/cgi.c
@@ -946,10 +946,20 @@
946 ** portion is fixed but a copy is be made of zValue.
947 */
948 void cgi_setenv(const char *zName, const char *zValue){
949 cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
950 }
 
 
 
 
 
 
 
 
 
 
951
952 /*
953 ** Add a list of query parameters or cookies to the parameter set.
954 **
955 ** Each parameter is of the form NAME=VALUE. Both the NAME and the
@@ -976,12 +986,16 @@
976 ** before the NAME is ignored.
977 **
978 ** The input string "z" is modified but no copies is made. "z"
979 ** should not be deallocated or changed again after this routine
980 ** returns or it will corrupt the parameter table.
 
 
 
 
981 */
982 static void add_param_list(char *z, int terminator){
983 int isQP = terminator=='&';
984 while( *z ){
985 char *zName;
986 char *zValue;
987 while( fossil_isspace(*z) ){ z++; }
@@ -1000,11 +1014,14 @@
1000 }else{
1001 if( *z ){ *z++ = 0; }
1002 zValue = "";
1003 }
1004 if( zName[0] && fossil_no_strange_characters(zName+1) ){
1005 if( fossil_islower(zName[0]) ){
 
 
 
1006 cgi_set_parameter_nocopy(zName, zValue, isQP);
1007 }else if( fossil_isupper(zName[0]) ){
1008 cgi_set_parameter_nocopy_tolower(zName, zValue, isQP);
1009 }
1010 }
@@ -1299,11 +1316,11 @@
1299 int rc = 0;
1300 char * z = (char*)P("QUERY_STRING");
1301 if( z ){
1302 rc = 0x01;
1303 z = fossil_strdup(z);
1304 add_param_list(z, '&');
1305 z = (char*)P("skin");
1306 if( z ){
1307 char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
1308 rc |= 0x02;
1309 if( !zErr && P("once")==0 ){
@@ -1459,11 +1476,11 @@
1459 }
1460 #endif
1461 z = (char*)P("HTTP_COOKIE");
1462 if( z ){
1463 z = fossil_strdup(z);
1464 add_param_list(z, ';');
1465 z = (char*)cookie_value("skin",0);
1466 if(z){
1467 skin_use_alternative(z, 2, SKIN_FROM_COOKIE);
1468 }
1469 }
@@ -1522,11 +1539,11 @@
1522 || fossil_strncmp(g.zContentType,"multipart/form-data",19)==0
1523 ){
1524 char *z = blob_str(&g.cgiIn);
1525 cgi_trace(z);
1526 if( g.zContentType[0]=='a' ){
1527 add_param_list(z, '&');
1528 }else{
1529 process_multipart_form_data(z, len);
1530 }
1531 blob_init(&g.cgiIn, 0, 0);
1532 }
1533
--- src/cgi.c
+++ src/cgi.c
@@ -946,10 +946,20 @@
946 ** portion is fixed but a copy is be made of zValue.
947 */
948 void cgi_setenv(const char *zName, const char *zValue){
949 cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
950 }
951
952 /*
953 ** Returns true if NUL-terminated z contains any non-NUL
954 ** control characters (<0x20, 32d).
955 */
956 static int contains_ctrl(const char *z){
957 assert(z);
958 for( ; *z>=0x20; ++z ){}
959 return 0!=*z;
960 }
961
962 /*
963 ** Add a list of query parameters or cookies to the parameter set.
964 **
965 ** Each parameter is of the form NAME=VALUE. Both the NAME and the
@@ -976,12 +986,16 @@
986 ** before the NAME is ignored.
987 **
988 ** The input string "z" is modified but no copies is made. "z"
989 ** should not be deallocated or changed again after this routine
990 ** returns or it will corrupt the parameter table.
991 **
992 ** If bPermitCtrl is false and the decoded value of any entry in z
993 ** contains control characters (<0x20, 32d) then that key/value pair
994 ** are skipped.
995 */
996 static void add_param_list(char *z, int terminator, int bPermitCtrl){
997 int isQP = terminator=='&';
998 while( *z ){
999 char *zName;
1000 char *zValue;
1001 while( fossil_isspace(*z) ){ z++; }
@@ -1000,11 +1014,14 @@
1014 }else{
1015 if( *z ){ *z++ = 0; }
1016 zValue = "";
1017 }
1018 if( zName[0] && fossil_no_strange_characters(zName+1) ){
1019 if( 0==bPermitCtrl && contains_ctrl(zValue) ){
1020 continue /* Reject it. An argument could be made
1021 ** for break instead of continue. */;
1022 }else if( fossil_islower(zName[0]) ){
1023 cgi_set_parameter_nocopy(zName, zValue, isQP);
1024 }else if( fossil_isupper(zName[0]) ){
1025 cgi_set_parameter_nocopy_tolower(zName, zValue, isQP);
1026 }
1027 }
@@ -1299,11 +1316,11 @@
1316 int rc = 0;
1317 char * z = (char*)P("QUERY_STRING");
1318 if( z ){
1319 rc = 0x01;
1320 z = fossil_strdup(z);
1321 add_param_list(z, '&', 0);
1322 z = (char*)P("skin");
1323 if( z ){
1324 char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
1325 rc |= 0x02;
1326 if( !zErr && P("once")==0 ){
@@ -1459,11 +1476,11 @@
1476 }
1477 #endif
1478 z = (char*)P("HTTP_COOKIE");
1479 if( z ){
1480 z = fossil_strdup(z);
1481 add_param_list(z, ';', 0);
1482 z = (char*)cookie_value("skin",0);
1483 if(z){
1484 skin_use_alternative(z, 2, SKIN_FROM_COOKIE);
1485 }
1486 }
@@ -1522,11 +1539,11 @@
1539 || fossil_strncmp(g.zContentType,"multipart/form-data",19)==0
1540 ){
1541 char *z = blob_str(&g.cgiIn);
1542 cgi_trace(z);
1543 if( g.zContentType[0]=='a' ){
1544 add_param_list(z, '&', 1);
1545 }else{
1546 process_multipart_form_data(z, len);
1547 }
1548 blob_init(&g.cgiIn, 0, 0);
1549 }
1550

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button