Hello,

    Attached is a patch to version 3.0.2 to correct what I believe to be
a bug and serious problem in rsync. This patch causes rsync to honor the
absence of the "--inplace" option for permission, owner and group
changes. This patch is a greatly simplified version of Matt McCutchen's
excellent "tweak-opts" patch found in his git repository
(http://mattmccutchen.net/rsync/rsync.git/?a=shortlog;h=refs/heads/tweak-opts).
However this patch does not add any new options. Without this patch
rsync will always modify files in place to reflect changes in
permissions, ownership or group without regard to the "--inplace"
option. The "--inplace" option is supposed to regulate whether files to
be backed up that are already found in the destination directory are
modified in place or simply used as the basis for new files. Not being
able to completely eliminate modifying files in place greatly and
negatively affects a very common use of rsync: backup systems that
create incremental backup snapshots using hard links. In such systems
the current behavior of rsync makes it a trivial exercise for a
misconfigured or compromised / malicious client to seriously damage or
even destroy all current and prior backup snapshots in the set. The
"--link-dest" option attempts to solve the problem but adds serious
problems of its own. Some of the problems I see are:

   1. *The hard links and permission / owner / group change problem:*
      This is a problem that has been well known for many years. When
      using hard links to implement snapshots (as most incremental
      snapshot backup systems that utilize rsync do) when files are
      modified in place (as rsync always does for permission, owner and
      group changes without any regard for the "--inplace" option) those
      changes affect not just those files in the current backup but also
      in all previous backups (since the last time the contents of the
      file were changed). So if for example a client machine is
      compromised and the attacker executes the command *"chmod -R 666
      /**"* the next time the rsync backup is performed *all* of the
      backup snapshots become unreliable. This can also happen if the
      filesystem is accidentally backed up and restored locally using a
      medium that does not support permissions.
   2. *Problems with the "--link-dest" option:* The "--link-dest" option
      was apparently added to rsync to address the above problem but it
      does so in a way that introduces vulnerabilities of its own.
         1. A big problem with "--link-dest" is that it requires that
            the client machine be correctly configured to work. There is
            no way to enforce a correct "--link-dest" option on the
            server. Not only does the client need to specify the option
            but also its use of the option needs to exactly match what
            the rotation / renaming scripts on the server expect or
            not-so-wonderful things will happen (you can use your
            imagination). Configuration mistakes or malicious users can
            wreak all sorts of havoc by omitting or playing with this
            option. *Any backup solution that requires the server to
            trust that the client is properly configured in order for
            the server to work correctly is not reliable.
            *
         2. Another big problem with "--link-dest" is that when used as
            documented and intended there is only one copy of the
            previous backup and *it cannot be protected from the forked
            daemon by chroot!*  The daemon must have direct access to
            the one and only good copy of your data so a bug, exploit or
            malicious client can easily clobber it. *Any backup solution
            that requires the server to trust that the client is
            properly configured in order to protect the integrity of
            previous backups is not reliable.* On the other hand if the
            linked copy of the backup is created *after* the rsync
            daemon has finished (i.e., not using "--link-dest") your
            previous backups can be protected from the daemon by chroot
            with only a copy of the data acted upon by the daemon.*
         3. Lots of other problems and confusion related to
            "--link-dest" . (See bugs
            
<https://bugzilla.samba.org/buglist.cgi?query_format=specific&order=relevance+desc&bug_status=__all__&product=rsync&content=--link-dest>).

I hope the above adequately explains why this is an important problem
and why the "--link-dest" option should be avoided by anyone who cares
about the integrity of their backups. It's my opinion that this simple
patch makes any rsync-based backup system that uses hard links (as so
many do) more reliable and robust and significantly helps maintain the
integrity of the backups. It does not require any changes to the
configuration or use of the clients, server or backup system to achieve
this benefit.

*I do not mean to imply by this that *chroot()*, particularly the way
it's used by rsync, is more than trivial protection but it can help make
things a little safer.


Thank you,
Carl E. Thompson


Who am I? I have been a professional software developer and engineer for
about 20 years and have specialized in Unix and Linux networking and
security for the last 14 years.

diff -urN rsync-3.0.2-orig/generator.c rsync-3.0.2/generator.c
--- rsync-3.0.2-orig/generator.c        2008-03-28 10:30:11.000000000 -0700
+++ rsync-3.0.2/generator.c     2008-05-07 15:35:08.317364774 -0700
@@ -1508,6 +1508,7 @@
 
        if (preserve_links && S_ISLNK(file->mode)) {
 #ifdef SUPPORT_LINKS
+               int iflags = 0;
                const char *sl = F_SYMLINK(file);
                if (safe_symlinks && unsafe_symlink(sl, fname)) {
                        if (verbose) {
@@ -1528,7 +1529,15 @@
                        else if ((len = readlink(fname, lnk, MAXPATHLEN-1)) > 0
                              && strncmp(lnk, sl, len) == 0 && sl[len] == '\0') 
{
                                /* The link is pointing to the right place. */
-                               set_file_attrs(fname, file, &sx, NULL, 
maybe_ATTRS_REPORT);
+                               if (inplace) {
+                                       if (verbose > 2)
+                                               rprintf(FINFO, "possibly 
tweaking attributes of %s\n", fname);
+                                       set_file_attrs(fname, file, &sx, NULL, 
maybe_ATTRS_REPORT);
+                               } else if (!unchanged_attrs(fname, file, &sx)) {
+                                       if (verbose > 2)
+                                               rprintf(FINFO, "recreating %s 
due to changed attributes\n", fname);
+                                       goto recreate_symlink;
+                               }
                                if (itemizing)
                                        itemize(fname, file, ndx, 0, &sx, 0, 0, 
NULL);
 #if defined SUPPORT_HARD_LINKS && defined CAN_HARDLINK_SYMLINK
@@ -1538,7 +1547,9 @@
                                if (remove_source_files == 1)
                                        goto return_with_success;
                                goto cleanup;
-                       }
+                       } else
+                               iflags = ITEM_REPORT_CHANGE;
+               recreate_symlink:
                        /* Not the right symlink (or not a symlink), so
                         * delete it. */
                        if (delete_item(fname, sx.st.st_mode, del_opts | 
DEL_FOR_SYMLINK) != 0)
@@ -1572,18 +1583,20 @@
                        set_file_attrs(fname, file, NULL, NULL, 0);
                        if (itemizing) {
                                itemize(fname, file, ndx, statret, &sx,
-                                       ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 
0, NULL);
+                                       ITEM_LOCAL_CHANGE|iflags, 0, NULL);
                        }
-                       if (code != FNONE && verbose)
+                       if ((iflags & ITEM_REPORT_CHANGE) && code != FNONE && 
verbose)
                                rprintf(code, "%s -> %s\n", fname, sl);
 #ifdef SUPPORT_HARD_LINKS
                        if (preserve_hard_links && F_IS_HLINKED(file))
                                finish_hard_link(file, fname, ndx, NULL, 
itemizing, code, -1);
 #endif
-                       /* This does not check remove_source_files == 1
-                        * because this is one of the items that the old
-                        * --remove-sent-files option would remove. */
-                       if (remove_source_files)
+                       /* When the symlink value changed, we do not check
+                        * remove_source_files == 1 because this is one of the
+                        * items that the old --remove-sent-files option would
+                        * remove. */
+                       if ((iflags & ITEM_REPORT_CHANGE) ? remove_source_files
+                               : remove_source_files == 1)
                                goto return_with_success;
                }
 #endif
@@ -1592,6 +1605,7 @@
 
        if ((am_root && preserve_devices && IS_DEVICE(file->mode))
         || (preserve_specials && IS_SPECIAL(file->mode))) {
+               int iflags = 0;
                uint32 *devp = F_RDEV_P(file);
                dev_t rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
                if (statret == 0) {
@@ -1609,7 +1623,15 @@
                         && BITS_EQUAL(sx.st.st_mode, file->mode, _S_IFMT)
                         && sx.st.st_rdev == rdev) {
                                /* The device or special file is identical. */
-                               set_file_attrs(fname, file, &sx, NULL, 
maybe_ATTRS_REPORT);
+                               if (inplace) {
+                                       if (verbose > 2)
+                                               rprintf(FINFO, "possibly 
tweaking attributes of %s\n", fname);
+                                       set_file_attrs(fname, file, &sx, NULL, 
maybe_ATTRS_REPORT);
+                               } else if (!unchanged_attrs(fname, file, &sx)) {
+                                       if (verbose > 2)
+                                               rprintf(FINFO, "recreating %s 
due to changed attributes\n", fname);
+                                       goto recreate_D;
+                               }
                                if (itemizing)
                                        itemize(fname, file, ndx, 0, &sx, 0, 0, 
NULL);
 #ifdef SUPPORT_HARD_LINKS
@@ -1619,7 +1641,9 @@
                                if (remove_source_files == 1)
                                        goto return_with_success;
                                goto cleanup;
-                       }
+                       } else
+                               iflags = ITEM_REPORT_CHANGE;
+               recreate_D:
                        if (delete_item(fname, sx.st.st_mode, del_opts | 
del_for_flag) != 0)
                                goto cleanup;
                } else if (basis_dir[0] != NULL) {
@@ -1656,9 +1680,9 @@
                        set_file_attrs(fname, file, NULL, NULL, 0);
                        if (itemizing) {
                                itemize(fname, file, ndx, statret, &sx,
-                                       ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 
0, NULL);
+                                       ITEM_LOCAL_CHANGE|iflags, 0, NULL);
                        }
-                       if (code != FNONE && verbose)
+                       if ((iflags & ITEM_REPORT_CHANGE) && code != FNONE && 
verbose)
                                rprintf(code, "%s\n", fname);
 #ifdef SUPPORT_HARD_LINKS
                        if (preserve_hard_links && F_IS_HLINKED(file))
@@ -1776,13 +1800,31 @@
        else if (fnamecmp_type == FNAMECMP_FUZZY)
                ;
        else if (unchanged_file(fnamecmp, file, &sx.st)) {
+               /* fnamecmp == fname, fnamecmp_type == FNAMECMP_FNAME */
+               int iflags = 0;
+
                if (partialptr) {
                        do_unlink(partialptr);
                        handle_partial_dir(partialptr, PDIR_DELETE);
                }
-               set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+               if (inplace) {
+                       /* Currently, we call set_file_attrs on all tweakable
+                        * files, though ideally it would have no effect when
+                        * unchanged_attrs returns true. */
+                       if (verbose > 2)
+                               rprintf(FINFO, "possibly tweaking attributes of 
%s\n", fname);
+                       set_file_attrs(fname, file, &sx, NULL, 
maybe_ATTRS_REPORT);
+               } else if (!unchanged_attrs(fname, file, &sx)) {
+                       /* Need to recreate the file.
+                        * copy_altdest_file sets its attributes, etc. */
+                       if (verbose > 2)
+                               rprintf(FINFO, "recreating %s due to changed 
attributes\n", fname);
+                       if (!dry_run && copy_altdest_file(fnamecmp, fname, 
file))
+                               goto cleanup;
+                       iflags |= ITEM_LOCAL_CHANGE;
+               }
                if (itemizing)
-                       itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
+                       itemize(fnamecmp, file, ndx, statret, &sx, iflags, 0, 
NULL);
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_IS_HLINKED(file))
                        finish_hard_link(file, fname, ndx, &sx.st, itemizing, 
code, -1);
-- 
Please use reply-all for most replies to avoid omitting the mailing list.
To unsubscribe or change options: https://lists.samba.org/mailman/listinfo/rsync
Before posting, read: http://www.catb.org/~esr/faqs/smart-questions.html

Reply via email to