[SCM] The rsync repository. - branch master updated
Rsync CVS commit messages
rsync-cvs at lists.samba.org
Sun Dec 26 20:33:28 UTC 2021
The branch, master has been updated
via 72adf49b rrsync improvements
from 73ceea6a Convert atomic-rsync to python.
https://git.samba.org/?p=rsync.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit 72adf49ba8cb81426e2b9799fbd43c6284b013a9
Author: Wayne Davison <wayne at opencoder.net>
Date: Sun Dec 26 12:29:00 2021 -0800
rrsync improvements
- Convert rrsync to python.
- Enhance security of arg & option checking.
- Reject `-L` (`--copy-links`) by default.
- Add `-munge` and `-no-del` options.
- Tweak the logfile line format.
- Created an rrsync man page.
- Use `configure --with-rrsync` if you want `make install` to install
rrsync and its man page.
- Give lsh more rrsync testing support.
-----------------------------------------------------------------------
Summary of changes:
Makefile.in | 10 +-
NEWS.md | 18 +-
configure.ac | 7 +
maybe-make-man | 2 +-
md2man | 13 +-
packaging/cull_options | 36 +--
support/lsh | 12 +-
support/rrsync | 595 +++++++++++++++++++++++++++----------------------
support/rrsync.1.md | 89 ++++++++
9 files changed, 488 insertions(+), 294 deletions(-)
create mode 100644 support/rrsync.1.md
Changeset truncated at 500 lines:
diff --git a/Makefile.in b/Makefile.in
index 3c8c2240..5eed339e 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -6,6 +6,7 @@ exec_prefix=@exec_prefix@
bindir=@bindir@
libdir=@libdir@/rsync
mandir=@mandir@
+with_rrsync=@with_rrsync@
LIBS=@LIBS@
CC=@CC@
@@ -80,6 +81,10 @@ install: all
if test -f rsync.1; then $(INSTALLMAN) -m 644 rsync.1 $(DESTDIR)$(mandir)/man1; fi
if test -f rsync-ssl.1; then $(INSTALLMAN) -m 644 rsync-ssl.1 $(DESTDIR)$(mandir)/man1; fi
if test -f rsyncd.conf.5; then $(INSTALLMAN) -m 644 rsyncd.conf.5 $(DESTDIR)$(mandir)/man5; fi
+ if test "$(with_rrsync)" = yes; then \
+ $(INSTALLCMD) -m 755 $(srcdir)/support/rrsync $(DESTDIR)$(bindir); \
+ if test -f rrsync.1; then $(INSTALLMAN) -m 644 rrsync.1 $(DESTDIR)$(mandir)/man1; fi; \
+ fi
install-ssl-daemon: stunnel-rsyncd.conf
-$(MKDIR_P) $(DESTDIR)/etc/stunnel
@@ -247,7 +252,7 @@ proto.h-tstamp: $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h
$(AWK) -f $(srcdir)/mkproto.awk $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h
.PHONY: man
-man: rsync.1 rsync-ssl.1 rsyncd.conf.5
+man: rsync.1 rsync-ssl.1 rsyncd.conf.5 rrsync.1
rsync.1: rsync.1.md md2man version.h Makefile
@$(srcdir)/maybe-make-man $(srcdir) rsync.1.md
@@ -258,6 +263,9 @@ rsync-ssl.1: rsync-ssl.1.md md2man version.h Makefile
rsyncd.conf.5: rsyncd.conf.5.md md2man version.h Makefile
@$(srcdir)/maybe-make-man $(srcdir) rsyncd.conf.5.md
+rrsync.1: support/rrsync.1.md md2man Makefile
+ @$(srcdir)/maybe-make-man $(srcdir) support/rrsync.1.md
+
.PHONY: clean
clean: cleantests
rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) \
diff --git a/NEWS.md b/NEWS.md
index eaa82b39..b3002e89 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -97,9 +97,15 @@
- More ASM optimizations from Shark64.
- - Make rrsync pass --munge-links to rsync by default to make the restricted
- dir extra safe (with an option to turn it off if you trust your users).
- Also updated the known options list.
+ - Transformed rrsync into a python script with improvements: security has been
+ beefed up; the known rsync options were updated to include recent additions;
+ rrsync rejects `-L` (`--copy-links`) by default to make it harder to exploit
+ any out-of-subdir symlinks; a new rrsync option of `-munge` tells rrsync to
+ always enable the `--munge-links` rsync option on the server side; a new
+ rrsync option of `-no-del` disables all `--remove*` and `--delete*` rsync
+ options on the server side; the log format has been tweaked slightly to add
+ seconds to the timestamp and output the command executed as a tuple; an
+ rrsync.1 manpage is now created.
- Work around a glibc bug where lchmod() breaks in a chroot w/o /proc mounted.
@@ -107,6 +113,12 @@
### PACKAGING RELATED:
+ - Give configure the --with-rrsync option if you want `make install` to
+ install the (now python3) rrsync script and its (new) man page.
+
+ - If the rrsync script is installed, make its package depend on python3 and
+ (suggested but not required) the python3 braceexpand lib.
+
- When creating a package from a non-release version (w/o a git checkout), the
packager can elect to create git-version.h and define RSYNC_GITVER to the
string they want `--version` to output. (The file is still auto-generated
diff --git a/configure.ac b/configure.ac
index 9e7338cf..84111de8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -136,6 +136,13 @@ if test x"$GCC" = x"yes"; then
CFLAGS="$CFLAGS -Wall -W"
fi
+AC_ARG_WITH(rrsync,
+ AS_HELP_STRING([--with-rrsync],[also install the rrsync script and its man page]))
+if test x"$with_rrsync" != x"yes"; then
+ with_rrsync=no
+fi
+AC_SUBST(with_rrsync)
+
AC_ARG_WITH(included-popt,
AS_HELP_STRING([--with-included-popt],[use bundled popt library, not from system]))
diff --git a/maybe-make-man b/maybe-make-man
index b7f0a9f1..59f2dce4 100755
--- a/maybe-make-man
+++ b/maybe-make-man
@@ -37,4 +37,4 @@ if [ ! -f "$flagfile" ]; then
fi
fi
-"$srcdir/md2man" "$srcdir/$inname"
+"$srcdir/md2man" -s "$srcdir" "$srcdir/$inname"
diff --git a/md2man b/md2man
index fa1d2e82..fd546f19 100755
--- a/md2man
+++ b/md2man
@@ -85,7 +85,9 @@ def main():
die('Failed to parse NAME.NUM.md out of input file:', args.mdfile)
fi = argparse.Namespace(**fi.groupdict())
- if not fi.srcdir:
+ if args.srcdir:
+ fi.srcdir = args.srcdir + '/'
+ elif not fi.srcdir:
fi.srcdir = './'
fi.title = fi.prog + '(' + fi.sect + ') man page'
@@ -105,7 +107,7 @@ def main():
for fn in (fi.srcdir + 'version.h', 'Makefile'):
try:
st = os.lstat(fn)
- except:
+ except OSError:
die('Failed to find', fi.srcdir + fn)
if not fi.mtime:
fi.mtime = st.st_mtime
@@ -129,6 +131,10 @@ def main():
if var == 'srcdir':
break
+ fi.prog_ver = 'rsync ' + env_subs['VERSION']
+ if fi.prog != 'rsync':
+ fi.prog_ver = fi.prog + ' from ' + fi.prog_ver
+
with open(fi.fn, 'r', encoding='utf-8') as fh:
txt = fh.read()
@@ -140,7 +146,7 @@ def main():
txt = None
fi.date = time.strftime('%d %b %Y', time.localtime(fi.mtime))
- fi.man_headings = (fi.prog, fi.sect, fi.date, fi.prog + ' ' + env_subs['VERSION'], env_subs['prefix'])
+ fi.man_headings = (fi.prog, fi.sect, fi.date, fi.prog_ver, env_subs['prefix'])
HtmlToManPage(fi)
@@ -374,6 +380,7 @@ def die(*msg):
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Transform a NAME.NUM.md markdown file into a NAME.NUM.html web page & a NAME.NUM man page.', add_help=False)
+ parser.add_argument('--srcdir', '-s', help='Specify the source dir if the input file is not in it.')
parser.add_argument('--test', action='store_true', help='Test if we can parse the input w/o updating any files.')
parser.add_argument('--debug', '-D', action='count', default=0, help='Output copious info on the html parsing. Repeat for even more.')
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
diff --git a/packaging/cull_options b/packaging/cull_options
index 85311c7c..d4e1c626 100755
--- a/packaging/cull_options
+++ b/packaging/cull_options
@@ -7,7 +7,7 @@ import re, argparse
short_no_arg = { }
short_with_num = { '@': 1 };
-long_opt = { # These include some extra long-args that BackupPC uses:
+long_opts = { # These include some extra long-args that BackupPC uses:
'block-size': 1,
'daemon': -1,
'debug': 1,
@@ -25,6 +25,7 @@ long_opt = { # These include some extra long-args that BackupPC uses:
'owner': 0,
'perms': 0,
'recursive': 0,
+ 'stderr': 1,
'times': 0,
'write-devices': -1,
}
@@ -49,8 +50,8 @@ def main():
m = re.search(r'args\[ac\+\+\] = "--([^"=]+)"', line)
if m:
last_long_opt = m.group(1)
- if last_long_opt not in long_opt:
- long_opt[last_long_opt] = 0
+ if last_long_opt not in long_opts:
+ long_opts[last_long_opt] = 0
else:
last_long_opt = None
continue
@@ -58,13 +59,13 @@ def main():
if last_long_opt:
m = re.search(r'args\[ac\+\+\] = ([^["\s]+);', line)
if m:
- long_opt[last_long_opt] = 2
+ long_opts[last_long_opt] = 2
last_long_opt = None
continue
m = re.search(r'return "--([^"]+-dest)";', line)
if m:
- long_opt[m.group(1)] = 2
+ long_opts[m.group(1)] = 2
last_long_opt = None
continue
@@ -74,19 +75,18 @@ def main():
if not m:
m = re.search(r'fmt = .*: "--([^"=]+)=', line)
if m:
- long_opt[m.group(1)] = 1
+ long_opts[m.group(1)] = 1
last_long_opt = None
- long_opt['files-from'] = 3
+ long_opts['files-from'] = 3
- txt = """
-# These options are the only options that rsync might send to the server,
-# and only in the option format that the stock rsync produces.
+ txt = """\
+### START of options data produced by the cull_options script. ###
# To disable a short-named option, add its letter to this string:
"""
- txt += str_assign('short_disabled', 's') + "\n"
+ txt += str_assign('short_disabled', 'Ls') + "\n"
txt += str_assign('short_no_arg', ''.join(sorted(short_no_arg)), 'DO NOT REMOVE ANY')
txt += str_assign('short_with_num', ''.join(sorted(short_with_num)), 'DO NOT REMOVE ANY')
@@ -99,24 +99,24 @@ def main():
print(txt, end='')
if args.python:
- print("long_opt = {")
+ print("long_opts = {")
sep = ':'
else:
print("our %long_opt = (")
sep = ' =>'
- for opt in sorted(long_opt):
+ for opt in sorted(long_opts):
if opt.startswith(('min-', 'max-')):
val = 1
else:
- val = long_opt[opt]
+ val = long_opts[opt]
print(' ', repr(opt) + sep, str(val) + ',')
if args.python:
print("}")
else:
print(");")
- print('')
+ print("\n### END of options data produced by the cull_options script. ###")
def str_assign(name, val, comment=None):
@@ -129,10 +129,12 @@ def str_assign(name, val, comment=None):
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Output culled rsync options for rrsync.", add_help=False)
out_group = parser.add_mutually_exclusive_group()
- out_group.add_argument('--perl', action='store_true', help="Output perl code (the default).")
- out_group.add_argument('--python', action='store_true', help="Output python code.")
+ out_group.add_argument('--perl', action='store_true', help="Output perl code.")
+ out_group.add_argument('--python', action='store_true', help="Output python code (the default).")
parser.add_argument('--help', '-h', action='help', help="Output this help message and exit.")
args = parser.parse_args()
+ if not args.perl:
+ args.python = True
main()
# vim: sw=4 et
diff --git a/support/lsh b/support/lsh
index ebfe898c..40fe3d73 100755
--- a/support/lsh
+++ b/support/lsh
@@ -18,6 +18,8 @@ GetOptions(
'rrsync=s' => \( my $rrsync_dir ),
'ro' => \( my $rrsync_ro = '' ),
'wo' => \( my $rrsync_wo = '' ),
+ 'munge' => \( my $rrsync_munge = '' ),
+ 'no-del' => \( my $rrsync_no_del = '' ),
) or &usage;
&usage unless @ARGV > 1;
@@ -71,16 +73,12 @@ unless ($no_chdir) {
}
if ($rrsync_dir) {
- my $cmd = '';
- foreach (@ARGV) {
- (my $arg = $_) =~ s/(['";|()\[\]{}\$!*?<> \t&~\\])/\\$1/g;
- $cmd .= ' ' . $arg;
- }
- $cmd =~ s/^\s+//;
- $ENV{SSH_ORIGINAL_COMMAND} = $cmd;
+ $ENV{SSH_ORIGINAL_COMMAND} = join(' ', @ARGV);
push @cmd, 'rrsync';
push @cmd, '-ro' if $rrsync_ro;
push @cmd, '-wo' if $rrsync_wo;
+ push @cmd, '-munge' if $rrsync_munge;
+ push @cmd, '-no-del' if $rrsync_no_del;
push @cmd, $rrsync_dir;
} else {
push @cmd, '/bin/sh', '-c', "@ARGV";
diff --git a/support/rrsync b/support/rrsync
index 4c5dd2aa..5b43a819 100755
--- a/support/rrsync
+++ b/support/rrsync
@@ -1,282 +1,353 @@
-#!/usr/bin/env perl
-# Name: /usr/local/bin/rrsync (should also have a symlink in /usr/bin)
-# Purpose: Restricts rsync to subdirectory declared in .ssh/authorized_keys
-# Author: Joe Smith <js-cgi at inwap.com> 30-Sep-2004
-# Modified by: Wayne Davison <wayne at opencoder.net>
-use strict;
-
-use Socket;
-use Cwd 'abs_path';
-use File::Glob ':glob';
-
-# You may configure these values to your liking. See also the section
-# of options if you want to disable any options that rsync accepts.
-use constant RSYNC => '/usr/bin/rsync';
-use constant LOGFILE => 'rrsync.log';
-
-my $Usage = <<EOM;
-Use 'command="$0 [-ro|-wo|-no-munge] SUBDIR"'
- in front of lines in $ENV{HOME}/.ssh/authorized_keys
-EOM
-
-# Handle the -ro, -wo, & -no-munge options.
-our $only = '';
-our $force_munge = 1;
-while (@ARGV) {
- if ($ARGV[0] =~ /^-([rw])o$/) {
- my $r_or_w = $1;
- if ($only && $only ne $r_or_w) {
- die "$0: the -ro and -wo options conflict.\n";
- }
- $only = $r_or_w;
- } elsif ($ARGV[0] eq '-no-munge') {
- $force_munge = 0;
- } else {
- last;
- }
- shift;
-}
+#!/usr/bin/env python3
-our $subdir = shift;
-die "$0: No subdirectory specified\n$Usage" unless defined $subdir;
-$subdir = abs_path($subdir);
-die "$0: Restricted directory does not exist!\n" if $subdir ne '/' && !-d $subdir;
-
-# The client uses "rsync -av -e ssh src/ server:dir/", and sshd on the server
-# executes this program when .ssh/authorized_keys has 'command="..."'.
-# For example:
-# command="rrsync logs/client" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAzGhEeNlPr...
-# command="rrsync -ro results" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAmkHG1WCjC...
-#
-# Format of the environment variables set by sshd:
-# SSH_ORIGINAL_COMMAND=rsync --server -vlogDtpr --partial . ARG # push
-# SSH_ORIGINAL_COMMAND=rsync --server --sender -vlogDtpr --partial . ARGS # pull
-# SSH_CONNECTION=client_addr client_port server_port
-
-my $command = $ENV{SSH_ORIGINAL_COMMAND};
-die "$0: Not invoked via sshd\n$Usage" unless defined $command;
-die "$0: SSH_ORIGINAL_COMMAND='$command' is not rsync\n" unless $command =~ s/^rsync\s+//;
-die "$0: --server option is not first\n" unless $command =~ /^--server\s/;
-our $am_sender = $command =~ /^--server\s+--sender\s/; # Restrictive on purpose!
-die "$0 sending to read-only server not allowed\n" if $only eq 'r' && !$am_sender;
-die "$0 reading from write-only server not allowed\n" if $only eq 'w' && $am_sender;
+# Restricts rsync to subdirectory declared in .ssh/authorized_keys. See
+# the rrsync man page for details of how to make use of this script.
-### START of options data produced by the cull_options script. ###
+# NOTE: install python3 braceexpand to support brace expansion in the args!
+
+# Originally a perl script by: Joe Smith <js-cgi at inwap.com> 30-Sep-2004
+# Python version by: Wayne Davison <wayne at opencoder.net>
+
+# You may configure these 2 values to your liking. See also the section of
+# short & long options if you want to disable any options that rsync accepts.
+RSYNC = '/usr/bin/rsync'
+LOGFILE = 'rrsync.log' # NOTE: the file must exist for a line to be appended!
+
+# The following options are mainly the options that a client rsync can send
+# to the server, and usually just in the one option format that the stock
+# rsync produces. However, there are some additional convenience options
+# added as well, and thus a few options are present in both the short and
+# long lists (such as --group, --owner, and --perms).
-# These options are the only options that rsync might send to the server,
-# and only in the option format that the stock rsync produces.
+# NOTE when disabling: check for both a short & long version of the option!
+
+### START of options data produced by the cull_options script. ###
# To disable a short-named option, add its letter to this string:
-our $short_disabled = 's';
+short_disabled = 'Ls'
-our $short_no_arg = 'ACDEHIJKLNORSUWXbcdgklmnopqrstuvxyz'; # DO NOT REMOVE ANY
-our $short_with_num = '@B'; # DO NOT REMOVE ANY
+short_no_arg = 'ACDEHIJKLNORSUWXbcdgklmnopqrstuvxyz' # DO NOT REMOVE ANY
+short_with_num = '@B' # DO NOT REMOVE ANY
# To disable a long-named option, change its value to a -1. The values mean:
# 0 = the option has no arg; 1 = the arg doesn't need any checking; 2 = only
# check the arg when receiving; and 3 = always check the arg.
-our %long_opt = (
- 'append' => 0,
- 'backup-dir' => 2,
- 'block-size' => 1,
- 'bwlimit' => 1,
- 'checksum-choice' => 1,
- 'checksum-seed' => 1,
- 'compare-dest' => 2,
- 'compress-choice' => 1,
- 'compress-level' => 1,
- 'copy-dest' => 2,
- 'copy-unsafe-links' => 0,
- 'daemon' => -1,
- 'debug' => 1,
- 'delay-updates' => 0,
- 'delete' => 0,
- 'delete-after' => 0,
- 'delete-before' => 0,
- 'delete-delay' => 0,
- 'delete-during' => 0,
- 'delete-excluded' => 0,
- 'delete-missing-args' => 0,
- 'existing' => 0,
- 'fake-super' => 0,
- 'files-from' => 3,
- 'force' => 0,
- 'from0' => 0,
- 'fsync' => 2,
- 'fuzzy' => 0,
- 'group' => 0,
- 'groupmap' => 1,
- 'hard-links' => 0,
- 'iconv' => 1,
- 'ignore-errors' => 0,
- 'ignore-existing' => 0,
- 'ignore-missing-args' => 0,
- 'ignore-times' => 0,
- 'info' => 1,
- 'inplace' => 0,
- 'link-dest' => 2,
- 'links' => 0,
- 'list-only' => 0,
- 'log-file' => 3,
- 'log-format' => 1,
- 'max-alloc' => 1,
- 'max-delete' => 1,
- 'max-size' => 1,
- 'min-size' => 1,
- 'mkpath' => 0,
- 'modify-window' => 1,
- 'msgs2stderr' => 0,
- 'munge-links' => 0,
- 'new-compress' => 0,
- 'no-W' => 0,
- 'no-implied-dirs' => 0,
- 'no-msgs2stderr' => 0,
- 'no-munge-links' => -1,
- 'no-r' => 0,
- 'no-relative' => 0,
- 'no-specials' => 0,
- 'numeric-ids' => 0,
- 'old-compress' => 0,
- 'one-file-system' => 0,
- 'only-write-batch' => 1,
- 'open-noatime' => 0,
- 'owner' => 0,
- 'partial' => 0,
- 'partial-dir' => 2,
- 'perms' => 0,
- 'preallocate' => 0,
- 'recursive' => 0,
- 'remove-sent-files' => 0,
- 'remove-source-files' => 0,
- 'safe-links' => 0,
- 'sender' => 0,
- 'server' => 0,
- 'size-only' => 0,
- 'skip-compress' => 1,
- 'specials' => 0,
- 'stats' => 0,
- 'suffix' => 1,
- 'super' => 0,
- 'temp-dir' => 2,
- 'timeout' => 1,
- 'times' => 0,
- 'use-qsort' => 0,
--
The rsync repository.
More information about the rsync-cvs
mailing list