XSS fix: HTML Escape Before Inserting Untrusted Data into HTML Element Content; fixes #1338
Costa Tsaousis (ktsaou) committed
Dec 10, 2016 at 03:38 UTC
7222ec3a268cc4db91cbbef349d0531b4b9e3b60
3 files changed
+62
-23
src/web_buffer.c
+20
@@ -143,6 +143,26 @@ void buffer_strcat(BUFFER *wb, const char *txt)
143
}
144
}
145
146
+void buffer_strcat_htmlescape(BUFFER *wb, const char *txt)
147
+{
148
+ char b[2] = { [0] = '\0', [1] = '\0' };
149
+
150
+ while(*txt) {
151
+ switch(*txt) {
152
+ case '&': buffer_strcat(wb, "&"); break;
153
+ case '<': buffer_strcat(wb, "<"); break;
154
+ case '>': buffer_strcat(wb, ">"); break;
155
+ case '"': buffer_strcat(wb, """); break;
156
+ case '/': buffer_strcat(wb, "/"); break;
157
+ case '\'': buffer_strcat(wb, "'"); break;
158
+ default: {
159
+ b[0] = *txt;
160
+ buffer_strcat(wb, b);
161
+ }
162
+ }
163
+ txt++;
164
+ }
165
+}
166
167
void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...)
168
{
src/web_buffer.h
+1
@@ -64,6 +64,7 @@ extern void buffer_increase(BUFFER *b, size_t free_size_required);
64
extern void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) __attribute__ (( format (printf, 3, 4)));
65
extern void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args);
66
extern void buffer_sprintf(BUFFER *wb, const char *fmt, ...) __attribute__ (( format (printf, 2, 3)));
67
+extern void buffer_strcat_htmlescape(BUFFER *wb, const char *txt);
68
69
extern void buffer_char_replace(BUFFER *wb, char from, char to);
70
src/web_client.c
+41
-23
@@ -308,7 +308,8 @@ int mysendfile(struct web_client *w, char *filename)
308
for(s = filename; *s ;s++) {
309
if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
310
debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
311
- buffer_sprintf(w->response.data, "File '%s' cannot be served. Filename contains invalid character '%c'", filename, *s);
311
+ buffer_sprintf(w->response.data, "Filename contains invalid characters: ");
312
+ buffer_strcat_htmlescape(w->response.data, filename);
313
return 400;
314
}
315
}
@@ -316,7 +317,8 @@ int mysendfile(struct web_client *w, char *filename)
317
// if the filename contains a .. refuse to serve it
318
if(strstr(filename, "..") != 0) {
319
debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
319
- buffer_sprintf(w->response.data, "File '%s' cannot be served. Relative filenames with '..' in them are not supported.", filename);
320
+ buffer_strcat(w->response.data, "Relative filenames are not supported: ");
321
+ buffer_strcat_htmlescape(w->response.data, filename);
322
return 400;
323
}
324
@@ -328,21 +330,24 @@ int mysendfile(struct web_client *w, char *filename)
330
struct stat stat;
331
if(lstat(webfilename, &stat) != 0) {
332
debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
331
- buffer_sprintf(w->response.data, "File '%s' does not exist, or is not accessible.", webfilename);
333
+ buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
334
+ buffer_strcat_htmlescape(w->response.data, webfilename);
335
return 404;
336
}
337
338
// check if the file is owned by expected user
339
if(stat.st_uid != web_files_uid()) {
340
error("%llu: File '%s' is owned by user %u (expected user %u). Access Denied.", w->id, webfilename, stat.st_uid, web_files_uid());
338
- buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
341
+ buffer_strcat(w->response.data, "Access to file is not permitted: ");
342
+ buffer_strcat_htmlescape(w->response.data, webfilename);
343
return 403;
344
}
345
346
// check if the file is owned by expected group
347
if(stat.st_gid != web_files_gid()) {
348
error("%llu: File '%s' is owned by group %u (expected group %u). Access Denied.", w->id, webfilename, stat.st_gid, web_files_gid());
345
- buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
349
+ buffer_strcat(w->response.data, "Access to file is not permitted: ");
350
+ buffer_strcat_htmlescape(w->response.data, webfilename);
351
return 403;
352
}
353
@@ -353,7 +358,8 @@ int mysendfile(struct web_client *w, char *filename)
358
359
if((stat.st_mode & S_IFMT) != S_IFREG) {
360
error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
356
- buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
361
+ buffer_strcat(w->response.data, "Access to file is not permitted: ");
362
+ buffer_strcat_htmlescape(w->response.data, webfilename);
363
return 403;
364
}
365
@@ -365,12 +371,14 @@ int mysendfile(struct web_client *w, char *filename)
371
if(errno == EBUSY || errno == EAGAIN) {
372
error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
373
buffer_sprintf(w->response.header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename);
368
- buffer_sprintf(w->response.data, "The file '%s' is currently busy. Please try again later.", webfilename);
374
+ buffer_strcat(w->response.data, "File is currently busy, please try again later: ");
375
+ buffer_strcat_htmlescape(w->response.data, webfilename);
376
return 307;
377
}
378
else {
379
error("%llu: Cannot open file '%s'.", w->id, webfilename);
373
- buffer_sprintf(w->response.data, "Cannot open file '%s'.", webfilename);
380
+ buffer_strcat(w->response.data, "Cannot open file: ");
381
+ buffer_strcat_htmlescape(w->response.data, webfilename);
382
return 404;
383
}
384
}
@@ -733,7 +741,8 @@ int web_client_api_request_single_chart(struct web_client *w, char *url, void ca
741
RRDSET *st = rrdset_find(chart);
742
if(!st) st = rrdset_find_byname(chart);
743
if(!st) {
736
- buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart);
744
+ buffer_strcat(w->response.data, "Chart is not found: ");
745
+ buffer_strcat_htmlescape(w->response.data, chart);
746
ret = 404;
747
goto cleanup;
748
}
@@ -1130,7 +1139,8 @@ int web_client_api_request_v1_data(struct web_client *w, char *url)
1139
RRDSET *st = rrdset_find(chart);
1140
if(!st) st = rrdset_find_byname(chart);
1141
if(!st) {
1133
- buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart);
1142
+ buffer_strcat(w->response.data, "Chart is not found: ");
1143
+ buffer_strcat_htmlescape(w->response.data, chart);
1144
ret = 404;
1145
goto cleanup;
1146
}
@@ -1309,27 +1319,31 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
1319
}
1320
1321
if(action == 'A' && (!machine_guid || !machine_url || !url_name)) {
1322
+ error("Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
1323
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
1324
buffer_flush(w->response.data);
1313
- buffer_sprintf(w->response.data, "Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
1314
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
1325
+ buffer_strcat(w->response.data, "Invalid registry Access request.");
1326
return 400;
1327
}
1328
else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) {
1329
+ error("Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
1330
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
1331
buffer_flush(w->response.data);
1319
- buffer_sprintf(w->response.data, "Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
1320
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
1332
+ buffer_strcat(w->response.data, "Invalid registry Delete request.");
1333
return 400;
1334
}
1335
else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) {
1336
+ error("Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
1337
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
1338
buffer_flush(w->response.data);
1325
- buffer_sprintf(w->response.data, "Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
1326
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
1339
+ buffer_strcat(w->response.data, "Invalid registry Search request.");
1340
return 400;
1341
}
1342
else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) {
1343
+ error("Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
1344
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
1345
buffer_flush(w->response.data);
1331
- buffer_sprintf(w->response.data, "Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
1332
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
1346
+ buffer_strcat(w->response.data, "Invalid registry Switch request.");
1347
return 400;
1348
}
1349
@@ -1460,13 +1474,14 @@ int web_client_api_request_v1(struct web_client *w, char *url) {
1474
1475
else {
1476
buffer_flush(w->response.data);
1463
- buffer_sprintf(w->response.data, "Unsupported v1 API command: %s", tok);
1477
+ buffer_strcat(w->response.data, "Unsupported v1 API command: ");
1478
+ buffer_strcat_htmlescape(w->response.data, tok);
1479
return 404;
1480
}
1481
}
1482
else {
1483
buffer_flush(w->response.data);
1469
- buffer_sprintf(w->response.data, "API v1 command?");
1484
+ buffer_sprintf(w->response.data, "Which API v1 command?");
1485
return 400;
1486
}
1487
}
@@ -1481,7 +1496,8 @@ int web_client_api_request(struct web_client *w, char *url)
1496
return web_client_api_request_v1(w, url);
1497
else {
1498
buffer_flush(w->response.data);
1484
- buffer_sprintf(w->response.data, "Unsupported API version: %s", tok);
1499
+ buffer_strcat(w->response.data, "Unsupported API version: ");
1500
+ buffer_strcat_htmlescape(w->response.data, tok);
1501
return 404;
1502
}
1503
}
@@ -2098,14 +2114,16 @@ void web_client_process(struct web_client *w) {
2114
if(!st) st = rrdset_find(tok);
2115
if(!st) {
2116
code = 404;
2101
- buffer_sprintf(w->response.data, "Chart %s is not found.\r\n", tok);
2117
+ buffer_strcat(w->response.data, "Chart is not found: ");
2118
+ buffer_strcat_htmlescape(w->response.data, tok);
2119
debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
2120
}
2121
else {
2122
code = 200;
2123
debug_flags |= D_RRD_STATS;
2124
st->debug = !st->debug;
2108
- buffer_sprintf(w->response.data, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled");
2125
+ buffer_sprintf(w->response.data, "Chart has now debug %s: ", st->debug?"enabled":"disabled");
2126
+ buffer_strcat_htmlescape(w->response.data, tok);
2127
debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled");
2128
}
2129
}