From: Marek Schimara Date: Tue, 30 Aug 2016 15:06:28 +0000 (+0200) Subject: src/rrd_daemon.c,src/rrd_list.c: moves LIST-specific code into rrd_list_r X-Git-Tag: v1.7.0~36^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a78bccd512dc3b82ed1b255ce3a2f4fd18fe62b7;p=thirdparty%2Frrdtool-1.x.git src/rrd_daemon.c,src/rrd_list.c: moves LIST-specific code into rrd_list_r --- diff --git a/src/rrd.h b/src/rrd.h index 1b543770..92b2d1fc 100644 --- a/src/rrd.h +++ b/src/rrd.h @@ -166,6 +166,7 @@ struct rrd_t; void rrd_info_free( rrd_info_t *); char *rrd_list(int, char **); + char *rrd_list_r(char *dirname); int rrd_update( int, char **); diff --git a/src/rrd_daemon.c b/src/rrd_daemon.c index f1d98def..db548e78 100644 --- a/src/rrd_daemon.c +++ b/src/rrd_daemon.c @@ -105,7 +105,6 @@ #include #include #include -#include #ifdef HAVE_LIBWRAP #include @@ -2389,14 +2388,13 @@ done: static int handle_request_list (HANDLER_PROTO) /* {{{ */ { char *filename; - char fullpath[PATH_MAX]; + char *list, *start_ptr, *end_ptr, *ptr; + char fullpath[PATH_MAX], current[PATH_MAX], absolute[PATH_MAX]; char bwc[PATH_MAX], bwd[PATH_MAX]; - char *base = &config_base_dir[0], *current = &fullpath[0]; + char *base = &config_base_dir[0]; struct stat sc, sd; ssize_t len; int status; - DIR *dir; - struct dirent *entry; if (config_base_dir == NULL) { return send_response(sock, RESP_ERR, "No base directory defined\n"); @@ -2412,56 +2410,11 @@ static int handle_request_list (HANDLER_PROTO) /* {{{ */ snprintf(fullpath, PATH_MAX, "%s%s%s", config_base_dir, (filename[0] == '/') ? "" : "/", filename); - /* Prevent moving up the directory tree */ - if (strstr(fullpath, "..")) - send_response(sock, RESP_ERR, "%s\n", rrd_strerror(EACCES)); - - /* if filename contains wildcards, then use glob() */ - if (strchr(fullpath, '*') || strchr(fullpath, '?')) { - glob_t buf; - unsigned int i; - - if (glob(fullpath, 0, NULL, &buf)) { - globfree(&buf); - goto out_send_response; - } - - for (i = 0; i < buf.gl_pathc; i++) { - char *ptr; - - if (!check_file_access(buf.gl_pathv[i], sock)) - return send_response(sock, RESP_ERR, - "Cannot read: %s\n", buf.gl_pathv[i]); - - ptr = strrchr(buf.gl_pathv[i], '/'); - - if (ptr != NULL) - add_response_info(sock, "%s\n", ptr + 1); - } - - globfree(&buf); - goto out_send_response; - } - - if (!check_file_access(fullpath, sock)) + if (!check_file_access(fullpath, sock)) { return send_response(sock, RESP_ERR, "Cannot read: %s\n", fullpath); - - /* make sure we aren't following a symlink pointing outside of base_dir */ - if (lstat(fullpath, &sc) == -1) { - return send_response(sock, RESP_ERR, "stat %s: %s\n", - fullpath, rrd_strerror(errno)); - } - - if ((sc.st_mode & S_IFMT) == S_IFLNK) { - len = readlink(fullpath, bwc, sizeof(bwc) - 1); - if (len == -1) { - return send_response(sock, RESP_ERR, "readlink %s: %s\n", - fullpath, rrd_strerror(errno)); - } - bwc[len] = '\0'; - current = &bwc[0]; } + /* get real path of config_base_dir in case it's a symlink */ if (lstat(config_base_dir, &sd) == -1) { return send_response(sock, RESP_ERR, "stat %s: %s\n", config_base_dir, rrd_strerror(errno)); @@ -2477,44 +2430,89 @@ static int handle_request_list (HANDLER_PROTO) /* {{{ */ base = &bwd[0]; } - /* current path MUST be starting with base_dir */ - if (memcmp(current, base, strlen(base)) != 0) { - return send_response(sock, RESP_ERR, "Permission denied\n"); + list = rrd_list_r(fullpath); + + if (list == NULL) { + /* Empty directory listing */ + if (errno == 0) { + goto out_send_response; + } + + return send_response(sock, RESP_ERR, + "List %s: %s\n", fullpath, rrd_strerror(errno)); } - /* If filename matches an RRD file, then return it */ - if (strstr(current, ".rrd")) { - struct stat st; - char *ptr; + /* Check list items returned by rrd_list_r; + * the returned string is newline-separated: '%s\n%s\n...%s\n' + */ + start_ptr = list; + end_ptr = list; + + do { + end_ptr = strchr(start_ptr, '\n'); - if (!stat(current, &st) && S_ISREG(st.st_mode)) { - ptr = strrchr(current, '/'); + if (end_ptr == NULL) { + end_ptr = start_ptr + strlen(start_ptr); - if (ptr) - add_response_info(sock, "%s\n", ptr + 1); + if (end_ptr == start_ptr) { + break; + } } - goto out_send_response; + if ((end_ptr - start_ptr + strlen(fullpath) + 1) >= PATH_MAX) { + /* Name too long: skip entry */ + goto loop_next; } + strncpy(¤t[0], start_ptr, (end_ptr - start_ptr)); + current[end_ptr - start_ptr] = '\0'; + + /* if a single .rrd was asked for, absolute == fullpath */ + ptr = strstr(fullpath, ".rrd"); - dir = opendir(current); + if (ptr != NULL && strlen(ptr) == 4) { + snprintf(&absolute[0], PATH_MAX, "%s", fullpath); - if (dir == NULL) { + } else { + snprintf(&absolute[0], PATH_MAX, "%s/%s", fullpath, current); + } + + if (!check_file_access(absolute, sock)) { + /* Cannot access: skip entry */ + goto loop_next; + } + + /* Make sure we aren't following a symlink pointing outside of base_dir */ + if (lstat(absolute, &sc) == -1) { + free(list); return send_response(sock, RESP_ERR, - "Failed to open directory %s : %s\n", - current, strerror(errno)); + "stat %s: %s\n", absolute, rrd_strerror(errno)); } - while ((entry = readdir(dir)) != NULL) { + if ((sc.st_mode & S_IFMT) == S_IFLNK) { + len = readlink(absolute, bwc, sizeof(bwc) - 1); - if ((strcmp(entry->d_name, ".") == 0) || - (strcmp(entry->d_name, "..") == 0)) { - continue; + if (len == -1) { + free(list); + return send_response(sock, RESP_ERR, "readlink %s: %s\n", + absolute, rrd_strerror(errno)); + } + bwc[len] = '\0'; + strncpy(&absolute[0], bwc, PATH_MAX - 1); + absolute[PATH_MAX - 1] = '\0'; } - add_response_info(sock, "%s\n", entry->d_name); + /* Absolute path MUST be starting with base_dir; if not skip the entry. */ + if (memcmp(absolute, base, strlen(base)) != 0) { + goto loop_next; } - closedir(dir); + add_response_info(sock, "%s\n", current); + +loop_next: + start_ptr = end_ptr + 1; + + } while (start_ptr != '\0'); + + free(list); out_send_response: send_response(sock, RESP_OK, "RRDs\n"); diff --git a/src/rrd_list.c b/src/rrd_list.c index 402157ed..75325d76 100644 --- a/src/rrd_list.c +++ b/src/rrd_list.c @@ -1,5 +1,9 @@ #include +#include +#include +#include +#include #include "rrd_tool.h" #include "rrd_client.h" @@ -9,8 +13,145 @@ char *rrd_list(int argc, char **argv); char *rrd_list_r(char *dirname) { - printf("rrd_list_r not implemented yet\n"); - return NULL; +#define SANE_ASPRINTF(_dest_str, _format, _params...) \ + if (asprintf(&_dest_str, _format, _params) == -1) { \ + if (out != NULL) { \ + free(out); \ + } \ + errno = ENOMEM; \ + return NULL; \ + } +#define SANE_ASPRINTF2(_dest_str, _format, _params...) \ + if (asprintf(&_dest_str, _format, _params) == -1) { \ + if (out != NULL) { \ + free(out); \ + } \ + closedir(dir); \ + errno = ENOMEM; \ + return NULL; \ + } + + char *out = NULL, *tmp; + char current[PATH_MAX]; + glob_t buf; + char *ptr; + unsigned int i; + struct stat st; + DIR *dir; + struct dirent *entry; + + /* Prevent moving up the directory tree */ + if (strstr(dirname, "..")) { + errno = EACCES; + return NULL; + } + + /* if filename contains wildcards, then use glob() */ + if (strchr(dirname, '*') || strchr(dirname, '?')) { + + if (glob(dirname, 0, NULL, &buf)) { + globfree(&buf); + errno = ENOENT; + return NULL; + } + + for (i = 0; i < buf.gl_pathc; i++) { + ptr = strrchr(buf.gl_pathv[i], '/'); + + if (ptr == NULL) { + continue; + } + + if (out == NULL) { + SANE_ASPRINTF(out, "%s\n", ptr + 1); + + } else { + tmp = out; + SANE_ASPRINTF(out, "%s%s\n", out, ptr + 1); + free(tmp); + } + } + globfree(&buf); + + if (out == NULL) { + errno = ENOENT; + } + return out; + } + + /* If 'dirname' matches an RRD file, then return it. + * strlen() used to make sure it's matching the end of string. + */ + ptr = strstr(dirname, ".rrd"); + + if (ptr != NULL && strlen(ptr) == 4) { + + if (!stat(dirname, &st) && S_ISREG(st.st_mode)) { + ptr = strrchr(dirname, '/'); + + if (ptr) { + SANE_ASPRINTF(out, "%s\n", ptr + 1); + } + } + return out; + } + + /* Process directory */ + dir = opendir(dirname); + + if (dir == NULL) { + /* opendir sets errno */ + return NULL; + } + + while ((entry = readdir(dir)) != NULL) { + + if ((strcmp(entry->d_name, ".") == 0) || + (strcmp(entry->d_name, "..") == 0)) { + continue; + } + + if (strlen(dirname) + strlen(entry->d_name) + 1 >= PATH_MAX) { + continue; + } + snprintf(¤t[0], PATH_MAX, "%s/%s", dirname, entry->d_name); + + /* Only return directories and rrd files. + * NOTE: stat(2) follows symlinks and gives info on target. */ + if (stat(current, &st) != 0) { + continue; + } + + if (!S_ISDIR(st.st_mode)) { + + if (S_ISREG(st.st_mode)) { + + ptr = strstr(entry->d_name, ".rrd"); + + /* strlen() used to make sure it's matching + * the end of string. + */ + if (ptr == NULL || strlen(ptr) != 4) { + continue; + } + + } else { + continue; + } + } + + if (out == NULL) { + SANE_ASPRINTF2(out, "%s\n", entry->d_name); + + } else { + tmp = out; + SANE_ASPRINTF2(out, "%s%s\n", out, entry->d_name); + free(tmp); + } + } + closedir(dir); + + return out; } char *rrd_list(int argc, char **argv) @@ -110,6 +251,10 @@ char *rrd_list(int argc, char **argv) fprintf(stderr, "\n"); } list = rrd_list_r(argv[options.optind]); + + if (list == NULL) { + fprintf(stderr, "%s", strerror(errno)); + } } if (opt_daemon != NULL) {