|
version 1.1.1.1, 2012/02/17 15:09:30
|
version 1.1.1.4, 2021/03/17 00:32:36
|
|
Line 1
|
Line 1
|
| #!/usr/bin/perl | #!/usr/bin/env perl |
| # Name: /usr/local/bin/rrsync (should also have a symlink in /usr/bin) |
# Name: /usr/local/bin/rrsync (should also have a symlink in /usr/bin) |
| # Purpose: Restricts rsync to subdirectory declared in .ssh/authorized_keys |
# Purpose: Restricts rsync to subdirectory declared in .ssh/authorized_keys |
| # Author: Joe Smith <js-cgi@inwap.com> 30-Sep-2004 |
# Author: Joe Smith <js-cgi@inwap.com> 30-Sep-2004 |
| # Modified by: Wayne Davison <wayned@samba.org> | # Modified by: Wayne Davison <wayne@opencoder.net> |
| use strict; |
use strict; |
| |
|
| use Socket; |
use Socket; |
|
Line 15 use constant RSYNC => '/usr/bin/rsync';
|
Line 15 use constant RSYNC => '/usr/bin/rsync';
|
| use constant LOGFILE => 'rrsync.log'; |
use constant LOGFILE => 'rrsync.log'; |
| |
|
| my $Usage = <<EOM; |
my $Usage = <<EOM; |
| Use 'command="$0 [-ro] SUBDIR"' | Use 'command="$0 [-ro|-wo] SUBDIR"' |
| in front of lines in $ENV{HOME}/.ssh/authorized_keys |
in front of lines in $ENV{HOME}/.ssh/authorized_keys |
| EOM |
EOM |
| |
|
| our $ro = (@ARGV && $ARGV[0] eq '-ro') ? shift : ''; # -ro = Read-Only | # Handle the -ro and -wo options. |
| | our $only = ''; |
| | while (@ARGV && $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; |
| | shift; |
| | } |
| | |
| our $subdir = shift; |
our $subdir = shift; |
| die "$0: No subdirectory specified\n$Usage" unless defined $subdir; |
die "$0: No subdirectory specified\n$Usage" unless defined $subdir; |
| $subdir = abs_path($subdir); |
$subdir = abs_path($subdir); |
|
Line 31 die "$0: Restricted directory does not exist!\n" if $s
|
Line 41 die "$0: Restricted directory does not exist!\n" if $s
|
| # command="rrsync logs/client" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAzGhEeNlPr... |
# command="rrsync logs/client" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAzGhEeNlPr... |
| # command="rrsync -ro results" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAmkHG1WCjC... |
# command="rrsync -ro results" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAmkHG1WCjC... |
| # |
# |
| # Format of the envrionment variables set by sshd: | # Format of the environment variables set by sshd: |
| # SSH_ORIGINAL_COMMAND=rsync --server -vlogDtpr --partial . ARG # push |
# SSH_ORIGINAL_COMMAND=rsync --server -vlogDtpr --partial . ARG # push |
| # SSH_ORIGINAL_COMMAND=rsync --server --sender -vlogDtpr --partial . ARGS # pull |
# SSH_ORIGINAL_COMMAND=rsync --server --sender -vlogDtpr --partial . ARGS # pull |
| # SSH_CONNECTION=client_addr client_port server_port |
# SSH_CONNECTION=client_addr client_port server_port |
|
Line 41 die "$0: Not invoked via sshd\n$Usage" unless defined
|
Line 51 die "$0: Not invoked via sshd\n$Usage" unless defined
|
| die "$0: SSH_ORIGINAL_COMMAND='$command' is not rsync\n" unless $command =~ s/^rsync\s+//; |
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/; |
die "$0: --server option is not first\n" unless $command =~ /^--server\s/; |
| our $am_sender = $command =~ /^--server\s+--sender\s/; # Restrictive on purpose! |
our $am_sender = $command =~ /^--server\s+--sender\s/; # Restrictive on purpose! |
| die "$0 -ro: sending to read-only server not allowed\n" if $ro && !$am_sender; | 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; |
| |
|
| ### START of options data produced by the cull_options script. ### |
### START of options data produced by the cull_options script. ### |
| |
|
|
Line 51 die "$0 -ro: sending to read-only server not allowed\n
|
Line 62 die "$0 -ro: sending to read-only server not allowed\n
|
| # To disable a short-named option, add its letter to this string: |
# To disable a short-named option, add its letter to this string: |
| our $short_disabled = 's'; |
our $short_disabled = 's'; |
| |
|
| our $short_no_arg = 'ACDEHIKLORSWXbcdgklmnoprstuvxz'; # DO NOT REMOVE ANY | our $short_no_arg = 'ACDEHIJKLORSUWXbcdgklmnopqrstuvxyz'; # DO NOT REMOVE ANY |
| our $short_with_num = 'B'; # DO NOT REMOVE ANY | our $short_with_num = '@B'; # DO NOT REMOVE ANY |
| |
|
| # To disable a long-named option, change its value to a -1. The values mean: |
# 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 |
# 0 = the option has no arg; 1 = the arg doesn't need any checking; 2 = only |
|
Line 60 our $short_with_num = 'B'; # DO NOT REMOVE ANY
|
Line 71 our $short_with_num = 'B'; # DO NOT REMOVE ANY
|
| our %long_opt = ( |
our %long_opt = ( |
| 'append' => 0, |
'append' => 0, |
| 'backup-dir' => 2, |
'backup-dir' => 2, |
| |
'block-size' => 1, |
| 'bwlimit' => 1, |
'bwlimit' => 1, |
| |
'checksum-choice' => 1, |
| 'checksum-seed' => 1, |
'checksum-seed' => 1, |
| 'compare-dest' => 2, |
'compare-dest' => 2, |
| |
'compress-choice' => 1, |
| 'compress-level' => 1, |
'compress-level' => 1, |
| 'copy-dest' => 2, |
'copy-dest' => 2, |
| 'copy-unsafe-links' => 0, |
'copy-unsafe-links' => 0, |
| 'daemon' => -1, |
'daemon' => -1, |
| |
'debug' => 1, |
| 'delay-updates' => 0, |
'delay-updates' => 0, |
| 'delete' => 0, |
'delete' => 0, |
| 'delete-after' => 0, |
'delete-after' => 0, |
|
Line 74 our %long_opt = (
|
Line 89 our %long_opt = (
|
| 'delete-delay' => 0, |
'delete-delay' => 0, |
| 'delete-during' => 0, |
'delete-during' => 0, |
| 'delete-excluded' => 0, |
'delete-excluded' => 0, |
| |
'delete-missing-args' => 0, |
| 'existing' => 0, |
'existing' => 0, |
| 'fake-super' => 0, |
'fake-super' => 0, |
| 'files-from' => 3, |
'files-from' => 3, |
| 'force' => 0, |
'force' => 0, |
| 'from0' => 0, |
'from0' => 0, |
| 'fuzzy' => 0, |
'fuzzy' => 0, |
| |
'group' => 0, |
| |
'groupmap' => 1, |
| |
'hard-links' => 0, |
| 'iconv' => 1, |
'iconv' => 1, |
| 'ignore-errors' => 0, |
'ignore-errors' => 0, |
| 'ignore-existing' => 0, |
'ignore-existing' => 0, |
| |
'ignore-missing-args' => 0, |
| |
'ignore-times' => 0, |
| |
'info' => 1, |
| 'inplace' => 0, |
'inplace' => 0, |
| 'link-dest' => 2, |
'link-dest' => 2, |
| |
'links' => 0, |
| 'list-only' => 0, |
'list-only' => 0, |
| 'log-file' => 3, | 'log-file' => $only eq 'r' ? -1 : 3, |
| 'log-format' => 1, |
'log-format' => 1, |
| |
'max-alloc' => 1, |
| 'max-delete' => 1, |
'max-delete' => 1, |
| 'max-size' => 1, |
'max-size' => 1, |
| 'min-size' => 1, |
'min-size' => 1, |
| 'modify-window' => 1, |
'modify-window' => 1, |
| |
'new-compress' => 0, |
| 'no-implied-dirs' => 0, |
'no-implied-dirs' => 0, |
| 'no-r' => 0, |
'no-r' => 0, |
| 'no-relative' => 0, |
'no-relative' => 0, |
| 'no-specials' => 0, |
'no-specials' => 0, |
| 'numeric-ids' => 0, |
'numeric-ids' => 0, |
| |
'old-compress' => 0, |
| |
'one-file-system' => 0, |
| 'only-write-batch' => 1, |
'only-write-batch' => 1, |
| |
'open-noatime' => 0, |
| |
'owner' => 0, |
| 'partial' => 0, |
'partial' => 0, |
| 'partial-dir' => 2, |
'partial-dir' => 2, |
| 'remove-sent-files' => $ro ? -1 : 0, | 'perms' => 0, |
| 'remove-source-files' => $ro ? -1 : 0, | 'preallocate' => 0, |
| | 'recursive' => 0, |
| | 'remove-sent-files' => $only eq 'r' ? -1 : 0, |
| | 'remove-source-files' => $only eq 'r' ? -1 : 0, |
| 'safe-links' => 0, |
'safe-links' => 0, |
| 'sender' => 0, | 'sender' => $only eq 'w' ? -1 : 0, |
| 'server' => 0, |
'server' => 0, |
| 'size-only' => 0, |
'size-only' => 0, |
| 'skip-compress' => 1, |
'skip-compress' => 1, |
|
Line 113 our %long_opt = (
|
Line 145 our %long_opt = (
|
| 'super' => 0, |
'super' => 0, |
| 'temp-dir' => 2, |
'temp-dir' => 2, |
| 'timeout' => 1, |
'timeout' => 1, |
| |
'times' => 0, |
| 'use-qsort' => 0, |
'use-qsort' => 0, |
| |
'usermap' => 1, |
| |
'write-devices' => -1, |
| ); |
); |
| |
|
| ### END of options data produced by the cull_options script. ### |
### END of options data produced by the cull_options script. ### |
|
Line 178 while ($command =~ /((?:[^\s\\]+|\\.[^\s\\]*)+)/g) {
|
Line 213 while ($command =~ /((?:[^\s\\]+|\\.[^\s\\]*)+)/g) {
|
| s{//+}{/}g; |
s{//+}{/}g; |
| s{^/}{}; |
s{^/}{}; |
| s{^$}{.}; |
s{^$}{.}; |
| die "$0: do not use .. in any path!\n" if m{(^|/)\\?\.\\?\.(\\?/|$)}; |
|
| } |
} |
| push(@args, bsd_glob($_, GLOB_LIMIT|GLOB_NOCHECK|GLOB_BRACE|GLOB_QUOTE)); |
push(@args, bsd_glob($_, GLOB_LIMIT|GLOB_NOCHECK|GLOB_BRACE|GLOB_QUOTE)); |
| } |
} |
| } |
} |
| die "$0: invalid rsync-command syntax or options\n" if $in_options; |
die "$0: invalid rsync-command syntax or options\n" if $in_options; |
| |
|
| |
if ($subdir ne '/') { |
| |
die "$0: do not use .. in any path!\n" if grep m{(^|/)\.\.(/|$)}, @args; |
| |
} |
| |
|
| @args = ( '.' ) if !@args; |
@args = ( '.' ) if !@args; |
| |
|
| if ($write_log) { |
if ($write_log) { |
|
Line 198 if ($write_log) {
|
Line 236 if ($write_log) {
|
| } |
} |
| |
|
| # Note: This assumes that the rsync protocol will not be maliciously hijacked. |
# Note: This assumes that the rsync protocol will not be maliciously hijacked. |
| exec(RSYNC, @opts, @args) or die "exec(rsync @opts @args) failed: $? $!"; | exec(RSYNC, @opts, '--', @args) or die "exec(rsync @opts -- @args) failed: $? $!"; |
| |
|
| sub check_arg |
sub check_arg |
| { |
{ |