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 |
{ |
{ |