@cryptotaxi247 / netdata-1 / commits / 7222ec3a2

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, "&lt;"); break;
154 + case '>': buffer_strcat(wb, "&gt;"); break;
155 + case '"': buffer_strcat(wb, "&quot;"); break;
156 + case '/': buffer_strcat(wb, "&#x2F;"); break;
157 + case '\'': buffer_strcat(wb, "&#x27;"); 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 }