@cryptotaxi247 / netdata-1 / commits / 81a61a59f

added option to allow running as a user and serving files owned by another; added support for serving files in directories within the netdata web dir

Costa Tsaousis (ktsaou) committed Nov 28, 2015 at 15:59 UTC 81a61a59f6344651801aa58268ce2dba9596ccb1
3 files changed +75 -35
src/main.c
+2 -1
@@ -368,7 +368,8 @@ int main(int argc, char **argv)
368 // --------------------------------------------------------------------
369
370 prepare_rundir();
371 - user = config_get("global", "run as user", (getuid() == 0)?NETDATA_USER:"");
371 + user = config_get("global", "run as user" , (getuid() == 0)?NETDATA_USER:"");
372 + web_files_uid();
373
374 // --------------------------------------------------------------------
375
src/web_client.c
+69 -34
@@ -13,6 +13,7 @@
13 #include <fcntl.h>
14 #include <netinet/tcp.h>
15 #include <malloc.h>
16 +#include <pwd.h>
17
18 #include "common.h"
19 #include "log.h"
@@ -146,14 +147,15 @@ void web_client_reset(struct web_client *w)
147 long size = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->response.rlen:w->response.data->len;
148
149 if(likely(w->last_url[0]))
149 - log_access("%llu: (sent/all = %ld/%ld bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %s: '%s'",
150 + log_access("%llu: (sent/all = %ld/%ld bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %s: %d '%s'",
151 w->id,
152 sent, size, -((size>0)?((float)(size-sent)/(float)size * 100.0):0.0),
153 (float)usecdiff(&w->tv_ready, &w->tv_in) / 1000.0,
154 (float)usecdiff(&tv, &w->tv_ready) / 1000.0,
155 (float)usecdiff(&tv, &w->tv_in) / 1000.0,
156 (w->mode == WEB_CLIENT_MODE_FILECOPY)?"filecopy":"data",
156 - w->last_url
157 + w->response.code,
158 + w->last_url
159 );
160
161 debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id);
@@ -173,6 +175,7 @@ void web_client_reset(struct web_client *w)
175 buffer_reset(w->response.data);
176 w->response.rlen = 0;
177 w->response.sent = 0;
178 + w->response.code = 0;
179
180 w->wait_receive = 1;
181 w->wait_send = 0;
@@ -217,26 +220,59 @@ struct web_client *web_client_free(struct web_client *w)
220 return(n);
221 }
222
223 +uid_t web_files_uid(void)
224 +{
225 + static char *web_owner = NULL;
226 + static uid_t owner_uid = 0;
227 +
228 + if(unlikely(!web_owner)) {
229 + web_owner = config_get("global", "web files owner", NETDATA_USER);
230 + if(!web_owner || !*web_owner)
231 + owner_uid = geteuid();
232 + else {
233 + struct passwd *pw = getpwnam(web_owner);
234 + if(!pw) {
235 + error("User %s is not present. Ignoring option. Error: %s\n", web_owner, strerror(errno));
236 + owner_uid = geteuid();
237 + }
238 + else {
239 + debug(D_WEB_CLIENT, "Web files owner set to %s.\n", web_owner);
240 + owner_uid = pw->pw_uid;
241 + }
242 + }
243 + }
244 +
245 + return(owner_uid);
246 +}
247 +
248 int mysendfile(struct web_client *w, char *filename)
249 {
250 static char *web_dir = NULL;
223 - if(!web_dir) web_dir = config_get("global", "web files directory", WEB_DIR);
251
225 - debug(D_WEB_CLIENT, "%llu: Looking for file '%s'...", w->id, filename);
252 + // initialize our static data
253 + if(unlikely(!web_dir)) web_dir = config_get("global", "web files directory", WEB_DIR);
254 +
255 + debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, web_dir, filename);
256
257 // skip leading slashes
258 while (*filename == '/') filename++;
259
260 // if the filename contain known paths, skip them
231 - if(strncmp(filename, WEB_PATH_DATA "/", strlen(WEB_PATH_DATA) + 1) == 0) filename = &filename[strlen(WEB_PATH_DATA) + 1];
232 - else if(strncmp(filename, WEB_PATH_DATASOURCE "/", strlen(WEB_PATH_DATASOURCE) + 1) == 0) filename = &filename[strlen(WEB_PATH_DATASOURCE) + 1];
233 - else if(strncmp(filename, WEB_PATH_GRAPH "/", strlen(WEB_PATH_GRAPH) + 1) == 0) filename = &filename[strlen(WEB_PATH_GRAPH) + 1];
234 - else if(strncmp(filename, WEB_PATH_FILE "/", strlen(WEB_PATH_FILE) + 1) == 0) filename = &filename[strlen(WEB_PATH_FILE) + 1];
261 + if(strncmp(filename, WEB_PATH_FILE "/", strlen(WEB_PATH_FILE) + 1) == 0) filename = &filename[strlen(WEB_PATH_FILE) + 1];
262 +
263 + char *s;
264 + for(s = filename; *s ;s++) {
265 + if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
266 + debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
267 + buffer_sprintf(w->response.data, "File '%s' cannot be served. Filename contains invalid character '%c'", *s);
268 + return 400;
269 + }
270 + }
271
236 - // if the filename contains a / or a .., refuse to serve it
237 - if(strchr(filename, '/') != 0 || strstr(filename, "..") != 0) {
272 + // if the filename contains a .. refuse to serve it
273 + if(strstr(filename, "..") != 0) {
274 debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
239 - buffer_sprintf(w->response.data, "File '%s' cannot be served. Filenames cannot contain / or ..", filename);
275 + buffer_sprintf(w->response.data, "File '%s' cannot be served. Relative filenames with '..' in them are not supported.", filename);
276 return 400;
277 }
278
@@ -247,14 +283,25 @@ int mysendfile(struct web_client *w, char *filename)
283 // check if the file exists
284 struct stat stat;
285 if(lstat(webfilename, &stat) != 0) {
250 - error("%llu: File '%s' is not found.", w->id, webfilename);
286 + debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
287 buffer_sprintf(w->response.data, "File '%s' does not exist, or is not accessible.", filename);
288 return 404;
289 }
290
291 // check if the file is owned by us
256 - if(stat.st_uid != getuid() && stat.st_uid != geteuid()) {
257 - error("%llu: File '%s' is owned by user %d (I run as user %d). Access Denied.", w->id, webfilename, stat.st_uid, getuid());
292 + if(stat.st_uid != web_files_uid()) {
293 + debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is owned by user %d (I run as user %d). Access Denied.", w->id, webfilename, stat.st_uid, getuid());
294 + buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", filename);
295 + return 403;
296 + }
297 +
298 + if((stat.st_mode & S_IFMT) == S_IFDIR) {
299 + snprintf(webfilename, FILENAME_MAX+1, "%s/index.html", filename);
300 + return mysendfile(w, webfilename);
301 + }
302 +
303 + if((stat.st_mode & S_IFMT) != S_IFREG) {
304 + debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
305 buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", filename);
306 return 403;
307 }
@@ -794,7 +841,7 @@ void web_client_process(struct web_client *w) {
841 strncpy(w->last_url, url, URL_MAX);
842 w->last_url[URL_MAX] = '\0';
843
797 - tok = mystrsep(&url, "/?&");
844 + tok = mystrsep(&url, "/?");
845
846 debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
847
@@ -897,24 +944,15 @@ void web_client_process(struct web_client *w) {
944 buffer_flush(w->response.data);
945 generate_config(w->response.data, 0);
946 }
900 - else if(strcmp(tok, WEB_PATH_FILE) == 0) { // "file"
901 - tok = mystrsep(&url, "/?&");
902 - if(tok && *tok) code = mysendfile(w, tok);
903 - else {
904 - code = 400;
905 - buffer_flush(w->response.data);
906 - buffer_strcat(w->response.data, "You have to give a filename to get.\r\n");
907 - }
908 - }
909 - else if(!tok[0]) {
910 - buffer_flush(w->response.data);
911 - code = mysendfile(w, "index.html");
912 - }
947 else {
948 + char filename[FILENAME_MAX+1];
949 + url = filename;
950 + strncpy(filename, w->last_url, FILENAME_MAX);
951 + filename[FILENAME_MAX] = '\0';
952 + tok = mystrsep(&url, "?");
953 buffer_flush(w->response.data);
915 - code = mysendfile(w, tok);
954 + code = mysendfile(w, (tok && *tok)?tok:"/");
955 }
917 -
956 }
957 else {
958 strcpy(w->last_url, "not a valid response");
@@ -944,13 +982,10 @@ void web_client_process(struct web_client *w) {
982 return;
983 }
984
947 - if(w->response.data->len > w->response.data->size) {
948 - error("%llu: memory overflow encountered (size is %ld, written %ld).", w->response.data->size, w->response.data->len);
949 - }
950 -
985 gettimeofday(&w->tv_ready, NULL);
986 w->response.data->date = time(NULL);
987 w->response.sent = 0;
988 + w->response.code = code;
989
990 // prepare the HTTP response header
991 debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
src/web_client.h
+4
@@ -31,6 +31,8 @@ struct response {
31 BUFFER *header_output; // internal use
32 BUFFER *data; // our response data buffer
33
34 + int code; // the HTTP response code
35 +
36 long rlen; // if non-zero, the excepted size of ifd (input)
37 long sent; // current data length sent to output
38
@@ -77,6 +79,8 @@ struct web_client {
79
80 extern struct web_client *web_clients;
81
82 +extern uid_t web_files_uid(void);
83 +
84 extern struct web_client *web_client_create(int listener);
85 extern struct web_client *web_client_free(struct web_client *w);
86