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