summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2011-02-01 16:04:30 +0100
committerDenys Vlasenko <dvlasenk@redhat.com>2011-02-01 16:04:30 +0100
commit55ae0eaea8684aa78089bbd0c2116e0c8cb25585 (patch)
tree1f26dd846eb28b0280f5b4e33ed90fe777c312c5 /src/lib
parent17a29f77ddfbb15beac9554a54892994f204b99d (diff)
downloadabrt-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/lib')
-rw-r--r--src/lib/Makefile.am1
-rw-r--r--src/lib/copy_file_recursive.c139
-rw-r--r--src/lib/create_dump_dir.c2
-rw-r--r--src/lib/dump_dir.c50
4 files changed, 185 insertions, 7 deletions
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);