diff options
author | Denys Vlasenko <dvlasenk@redhat.com> | 2011-02-01 16:04:30 +0100 |
---|---|---|
committer | Denys Vlasenko <dvlasenk@redhat.com> | 2011-02-01 16:04:30 +0100 |
commit | 55ae0eaea8684aa78089bbd0c2116e0c8cb25585 (patch) | |
tree | 1f26dd846eb28b0280f5b4e33ed90fe777c312c5 /src | |
parent | 17a29f77ddfbb15beac9554a54892994f204b99d (diff) | |
download | abrt-55ae0eaea8684aa78089bbd0c2116e0c8cb25585.tar.gz abrt-55ae0eaea8684aa78089bbd0c2116e0c8cb25585.tar.xz abrt-55ae0eaea8684aa78089bbd0c2116e0c8cb25585.zip |
abrt-cli -r DIR: copy non-writable DIR into $HOME/abrt/spool
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/cli/CLI.cpp | 74 | ||||
-rw-r--r-- | src/daemon/abrt-server.c | 1 | ||||
-rw-r--r-- | src/hooks/abrt-hook-ccpp.c | 1 | ||||
-rw-r--r-- | src/include/abrtlib.h | 2 | ||||
-rw-r--r-- | src/include/report/dump_dir.h | 4 | ||||
-rw-r--r-- | src/lib/Makefile.am | 1 | ||||
-rw-r--r-- | src/lib/copy_file_recursive.c | 139 | ||||
-rw-r--r-- | src/lib/create_dump_dir.c | 2 | ||||
-rw-r--r-- | src/lib/dump_dir.c | 50 | ||||
-rw-r--r-- | src/plugins/abrt-dump-oops.c | 1 |
10 files changed, 259 insertions, 16 deletions
diff --git a/src/cli/CLI.cpp b/src/cli/CLI.cpp index 9af1cbc7..af85a9e9 100644 --- a/src/cli/CLI.cpp +++ b/src/cli/CLI.cpp @@ -38,7 +38,7 @@ static char *localize_crash_time(const char *timestr) static crash_data_t *FillCrashInfo(const char *dump_dir_name) { - struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); + struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ DD_OPEN_READONLY); if (!dd) return NULL; @@ -199,6 +199,46 @@ static void print_crash_info(crash_data_t *crash_data, bool show_backtrace) } } +static struct dump_dir *steal_directory(const char *base_dir, const char *dump_dir_name) +{ + const char *base_name = strrchr(dump_dir_name, '/'); + if (base_name) + base_name++; + else + base_name = dump_dir_name; + + struct dump_dir *dd_dst; + unsigned count = 100; + char *dst_dir_name = concat_path_file(base_dir, base_name); + while (1) + { + dd_dst = dd_create(dst_dir_name, (uid_t)-1); + free(dst_dir_name); + if (dd_dst) + break; + if (--count == 0) + { + error_msg("Can't create new dump dir in '%s'", base_dir); + goto ret; + } + struct timeval tv; + gettimeofday(&tv, NULL); + dst_dir_name = xasprintf("%s/%s.%u", base_dir, base_name, (int)tv.tv_usec); + } + + log("Creating copy in '%s'", dd_dst->dd_dir); + if (copy_file_recursive(dump_dir_name, dd_dst->dd_dir) < 0) + { + /* error. copy_file_recursive already emitted error message */ + dd_close(dd_dst); + xfunc_die(); + } + + ret: + return dd_dst; +} + + /* Program options */ enum { @@ -329,6 +369,14 @@ int main(int argc, char** argv) } end_of_arg_parsing: ; + if (!D_list) + { + char *home = getenv("HOME"); + if (home) + D_list = g_list_append(D_list, concat_path_file(home, "abrt/spool")); + D_list = g_list_append(D_list, (void*)DEBUG_DUMPS_DIR); + } + /* Handle option arguments. */ argc -= optind; switch (argc) @@ -368,13 +416,6 @@ int main(int argc, char** argv) { case OPT_GET_LIST: { - if (!D_list) - { - char *home = getenv("HOME"); - if (home) - D_list = g_list_append(D_list, concat_path_file(home, "abrt/spool")); - D_list = g_list_append(D_list, (void*)DEBUG_DUMPS_DIR); - } vector_of_crash_data_t *ci = new_vector_of_crash_data(); while (D_list) { @@ -388,6 +429,23 @@ int main(int argc, char** argv) } case OPT_REPORT: { + struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY); + if (!dd) + break; + int readonly = !dd->locked; + dd_close(dd); + if (readonly) + { + log("'%s' is not writable", dump_dir_name); + /* D_list can't be NULL here */ + struct dump_dir *dd_copy = steal_directory((char *)D_list->data, dump_dir_name); + if (dd_copy) + { + dump_dir_name = xstrdup(dd_copy->dd_dir); + dd_close(dd_copy); + } + } + exitcode = report(dump_dir_name, (always ? CLI_REPORT_BATCH : 0)); if (exitcode == -1) error_msg_and_die("Crash '%s' not found", dump_dir_name); diff --git a/src/daemon/abrt-server.c b/src/daemon/abrt-server.c index 82d69670..f34f3366 100644 --- a/src/daemon/abrt-server.c +++ b/src/daemon/abrt-server.c @@ -119,6 +119,7 @@ static void create_debug_dump() { error_msg_and_die("Error creating crash dump %s", path); } + dd_create_basic_files(dd, client_uid); dd_save_text(dd, FILENAME_ANALYZER, analyzer); dd_save_text(dd, FILENAME_EXECUTABLE, executable); diff --git a/src/hooks/abrt-hook-ccpp.c b/src/hooks/abrt-hook-ccpp.c index c9327723..dc95d6dc 100644 --- a/src/hooks/abrt-hook-ccpp.c +++ b/src/hooks/abrt-hook-ccpp.c @@ -497,6 +497,7 @@ int main(int argc, char** argv) struct dump_dir *dd = dd_create(path, uid); if (dd) { + dd_create_basic_files(dd, uid); char *cmdline = get_cmdline(pid); /* never NULL */ char *reason = xasprintf("Process %s was killed by signal %s (SIG%s)", executable, signal_str, signame ? signame : signal_str); dd_save_text(dd, FILENAME_ANALYZER, "CCpp"); diff --git a/src/include/abrtlib.h b/src/include/abrtlib.h index 22b63baf..bb3dddc2 100644 --- a/src/include/abrtlib.h +++ b/src/include/abrtlib.h @@ -123,6 +123,8 @@ off_t copyfd_size(int src_fd, int dst_fd, off_t size, int flags); void copyfd_exact_size(int src_fd, int dst_fd, off_t size); #define copy_file abrt_copy_file off_t copy_file(const char *src_name, const char *dst_name, int mode); +#define copy_file_recursive abrt_copy_file_recursive +int copy_file_recursive(const char *source, const char *dest); /* Returns malloc'ed block */ #define encode_base64 abrt_encode_base64 diff --git a/src/include/report/dump_dir.h b/src/include/report/dump_dir.h index 4f2914d0..3bcc526d 100644 --- a/src/include/report/dump_dir.h +++ b/src/include/report/dump_dir.h @@ -31,6 +31,7 @@ extern "C" { enum { DD_FAIL_QUIETLY = (1 << 0), + DD_OPEN_READONLY = (1 << 1), }; struct dump_dir { @@ -48,13 +49,14 @@ struct dump_dir *dd_opendir(const char *dir, int flags); * (IOW: if you aren't running under root): */ struct dump_dir *dd_create(const char *dir, uid_t uid); +void dd_create_basic_files(struct dump_dir *dd, uid_t uid); int dd_exist(struct dump_dir *dd, const char *path); DIR *dd_init_next_file(struct dump_dir *dd); int dd_get_next_file(struct dump_dir *dd, char **short_name, char **full_name); enum { /* DD_FAIL_QUIETLY bit is valid for dd_load_text_ext too, */ - DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE = (1 << 1), + DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE = (1 << 2), }; char* dd_load_text_ext(const struct dump_dir *dd, const char *name, unsigned flags); char* dd_load_text(const struct dump_dir *dd, const char *name); diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 89cad8ab..aece0533 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -20,6 +20,7 @@ libreport_la_SOURCES = \ read_write.c read_write.h \ logging.c logging.h \ copyfd.c \ + copy_file_recursive.c \ concat_path_file.c \ append_to_malloced_string.c \ overlapping_strcpy.c \ diff --git a/src/lib/copy_file_recursive.c b/src/lib/copy_file_recursive.c new file mode 100644 index 00000000..c3f021c7 --- /dev/null +++ b/src/lib/copy_file_recursive.c @@ -0,0 +1,139 @@ +/* + Copyright (C) 2011 ABRT team + Copyright (C) 2011 RedHat Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "abrtlib.h" + +int copy_file_recursive(const char *source, const char *dest) +{ + /* This is a recursive function, try to minimize stack usage */ + /* NB: each struct stat is ~100 bytes */ + struct stat source_stat; + struct stat dest_stat; + int retval = 0; + int dest_exists = 0; + + if (strcmp(source, ".lock") == 0) + goto skip; + + if (stat(source, &source_stat) < 0) { + perror_msg("Can't stat '%s'", source); + return -1; + } + + if (lstat(dest, &dest_stat) < 0) { + if (errno != ENOENT) { + perror_msg("Can't stat '%s'", dest); + return -1; + } + } else { + if (source_stat.st_dev == dest_stat.st_dev + && source_stat.st_ino == dest_stat.st_ino + ) { + error_msg("'%s' and '%s' are the same file", source, dest); + return -1; + } + dest_exists = 1; + } + + if (S_ISDIR(source_stat.st_mode)) { + DIR *dp; + struct dirent *d; + + if (dest_exists) { + if (!S_ISDIR(dest_stat.st_mode)) { + error_msg("Target '%s' is not a directory", dest); + return -1; + } + /* race here: user can substitute a symlink between + * this check and actual creation of files inside dest */ + } else { + /* Create DEST */ + mode_t mode = source_stat.st_mode; + /* Allow owner to access new dir (at least for now) */ + mode |= S_IRWXU; + if (mkdir(dest, mode) < 0) { + perror_msg("Can't create directory '%s'", dest); + return -1; + } + } + /* Recursively copy files in SOURCE */ + dp = opendir(source); + if (dp == NULL) { + retval = -1; + goto ret; + } + + while (retval == 0 && (d = readdir(dp)) != NULL) { + char *new_source, *new_dest; + + if (dot_or_dotdot(d->d_name)) + continue; + new_source = concat_path_file(source, d->d_name); + new_dest = concat_path_file(dest, d->d_name); + if (copy_file_recursive(new_source, new_dest) < 0) + retval = -1; + free(new_source); + free(new_dest); + } + closedir(dp); + + goto ret; + } + + if (S_ISREG(source_stat.st_mode)) { + int src_fd; + int dst_fd; + mode_t new_mode; + + src_fd = open(source, O_RDONLY); + if (src_fd < 0) { + perror_msg("Can't open '%s'", source); + return -1; + } + + /* Do not try to open with weird mode fields */ + new_mode = source_stat.st_mode; + + // security problem versus (sym)link attacks + // dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); + /* safe way: */ + dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); + if (dst_fd < 0) { + close(src_fd); + return -1; + } + + if (copyfd_eof(src_fd, dst_fd, COPYFD_SPARSE) == -1) + retval = -1; + close(src_fd); + /* Careful: do check that buffered writes succeeded... */ + if (close(dst_fd) < 0) { + perror_msg("Error writing to '%s'", dest); + retval = -1; + } + goto ret; + } + + /* Neither dir not regular file: skip */ + + skip: + log("Skipping '%s'", source); + ret: + return retval; +} diff --git a/src/lib/create_dump_dir.c b/src/lib/create_dump_dir.c index 652a16c6..2caf9af9 100644 --- a/src/lib/create_dump_dir.c +++ b/src/lib/create_dump_dir.c @@ -23,6 +23,8 @@ static struct dump_dir *try_dd_create(const char *base_dir_name, const char *dir { char *path = concat_path_file(base_dir_name, dir_name); struct dump_dir *dd = dd_create(path, (uid_t)-1L); + if (dd) + dd_create_basic_files(dd, (uid_t)-1L); free(path); return dd; } diff --git a/src/lib/dump_dir.c b/src/lib/dump_dir.c index 6b7fc6df..cacedaee 100644 --- a/src/lib/dump_dir.c +++ b/src/lib/dump_dir.c @@ -282,8 +282,21 @@ struct dump_dir *dd_opendir(const char *dir, int flags) dir = dd->dd_dir = rm_trailing_slashes(dir); + errno = 0; if (dd_lock(dd, WAIT_FOR_OTHER_PROCESS_USLEEP) < 0) { + if ((flags & DD_OPEN_READONLY) && errno == EACCES) + { + /* Directory is not writable. If it seems to be readable, + * return "read only" dd, not NULL */ + struct stat stat_buf; + if (stat(dir, &stat_buf) == 0 + && S_ISDIR(stat_buf.st_mode) + && access(dir, R_OK) == 0 + ) { + return dd; + } + } if (errno == EISDIR) { /* EISDIR: dd_lock can lock the dir, but it sees no time file there, @@ -378,12 +391,31 @@ struct dump_dir *dd_create(const char *dir, uid_t uid) return NULL; } + bool created_parents = false; + try_again: /* Was creating it with mode 0700 and user as the owner, but this allows * the user to replace any file in the directory, changing security-sensitive data * (e.g. "uid", "analyzer", "executable") */ if (mkdir(dir, 0750) == -1) { + int err = errno; + if (!created_parents && errno == ENOENT) + { + char *p = dd->dd_dir + 1; + while ((p = strchr(p, '/')) != NULL) + { + *p = '\0'; + int r = (mkdir(dd->dd_dir, 0755) == 0 || errno == EEXIST); + *p++ = '/'; + if (!r) + goto report_err; + } + created_parents = true; + goto try_again; + } + report_err: + errno = err; perror_msg("Can't create directory '%s'", dir); dd_close(dd); return NULL; @@ -430,12 +462,19 @@ struct dump_dir *dd_create(const char *dir, uid_t uid) } } + return dd; +} + +void dd_create_basic_files(struct dump_dir *dd, uid_t uid) +{ char long_str[sizeof(long) * 3 + 2]; time_t t = time(NULL); sprintf(long_str, "%lu", (long)t); dd_save_text(dd, FILENAME_TIME, long_str); + if (uid == (uid_t)-1) + uid = getuid(); sprintf(long_str, "%lu", (long)uid); dd_save_text(dd, FILENAME_UID, long_str); @@ -448,11 +487,8 @@ struct dump_dir *dd_create(const char *dir, uid_t uid) DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE); if (!release) release = load_text_file("/etc/redhat-release", /*flags:*/ 0); - strchrnul(release, '\n')[0] = '\0'; dd_save_text(dd, FILENAME_RELEASE, release); free(release); - - return dd; } static int delete_file_dir(const char *dir, bool skip_lock_file) @@ -608,8 +644,8 @@ static bool save_binary_file(const char *path, const char* data, unsigned size, char* dd_load_text_ext(const struct dump_dir *dd, const char *name, unsigned flags) { - if (!dd->locked) - error_msg_and_die("dump_dir is not opened"); /* bug */ +// if (!dd->locked) +// error_msg_and_die("dump_dir is not opened"); /* bug */ char *full_path = concat_path_file(dd->dd_dir, name); char *ret = load_text_file(full_path, flags); @@ -645,8 +681,8 @@ void dd_save_binary(struct dump_dir* dd, const char* name, const char* data, uns DIR *dd_init_next_file(struct dump_dir *dd) { - if (!dd->locked) - error_msg_and_die("dump_dir is not opened"); /* bug */ +// if (!dd->locked) +// error_msg_and_die("dump_dir is not opened"); /* bug */ if (dd->next_dir) closedir(dd->next_dir); diff --git a/src/plugins/abrt-dump-oops.c b/src/plugins/abrt-dump-oops.c index bf4f1e96..5b6a2062 100644 --- a/src/plugins/abrt-dump-oops.c +++ b/src/plugins/abrt-dump-oops.c @@ -506,6 +506,7 @@ static int save_oops_to_dump_dir(GList *oops_list, unsigned oops_cnt) struct dump_dir *dd = dd_create(path, /*uid:*/ 0); if (dd) { + dd_create_basic_files(dd, /*uid:*/ 0); dd_save_text(dd, FILENAME_ANALYZER, "Kerneloops"); dd_save_text(dd, FILENAME_EXECUTABLE, "kernel"); dd_save_text(dd, FILENAME_KERNEL, first_line); |