Commit | Line | Data |
---|---|---|
44a82a17 | 1 | #!/usr/bin/perl |
106a8ad9 | 2 | # Name: /usr/local/bin/rrsync (should also have a symlink in /usr/bin) |
44a82a17 | 3 | # Purpose: Restricts rsync to subdirectory declared in .ssh/authorized_keys |
106a8ad9 WD |
4 | # Author: Joe Smith <js-cgi@inwap.com> 30-Sep-2004 |
5 | # Modified by Wayne Davison <wayned@samba.org> 12-Jan-2005 | |
44a82a17 WD |
6 | |
7 | use Socket; | |
8 | use constant LOGFILE => 'rrsync.log'; | |
9 | my $Usage = <<EOM; | |
106a8ad9 | 10 | Use 'command="$0 [-ro] SUBDIR"' |
44a82a17 WD |
11 | in front of lines in $ENV{HOME}/.ssh/authorized_keys |
12 | EOM | |
13 | ||
14 | my $ro = (@ARGV and $ARGV[0] eq '-ro') ? shift : ''; # -ro = Read-Only | |
15 | my $subdir = shift; | |
16 | die "No subdirectory specified\n$Usage" unless defined $subdir; | |
17 | ||
18 | # The client uses "rsync -av -e ssh src/ server:dir/", and sshd on the server | |
19 | # executes this program when .ssh/authorized_keys has 'command="..."'. | |
20 | # For example: | |
21 | # command="rrsync logs/client" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAzGhEeNlPr... | |
22 | # command="rrsync -ro results" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAmkHG1WCjC... | |
23 | # | |
24 | # Format of the envrionment variables set by sshd: | |
25 | # SSH_ORIGINAL_COMMAND=rsync --server -vlogDtpr --partial . dir # push | |
26 | # SSH_ORIGINAL_COMMAND=rsync --server --sender -vlogDtpr --partial . dir # pull | |
106a8ad9 | 27 | # SSH_CONNECTION=client_addr client_port server_port |
44a82a17 WD |
28 | |
29 | my $command = $ENV{SSH_ORIGINAL_COMMAND}; | |
30 | die "Not invoked via sshd\n$Usage" unless defined $command; | |
106a8ad9 WD |
31 | die "SSH_ORIGINAL_COMMAND='$command' is not rsync\n" unless $command =~ /^rsync\s/; |
32 | die "$0 -ro: sending to read-only server not allowed\n" | |
33 | if $ro and $command !~ /^rsync --server --sender /; | |
a234bca4 WD |
34 | die "$0 -ro: use of $1 with read-only server not allowed\n" |
35 | if $ro and $command =~ /\s(--remove-\S+)/; | |
44a82a17 | 36 | |
106a8ad9 WD |
37 | my ($cmd,$dir) = $command =~ /^(rsync\s+(?:-[-a-zA-Z]+\s+)+\.) ?("[^"]*"|[^\s"]*)$/; |
38 | die "$0: invalid rsync-command syntax or options\n" if !defined $cmd; | |
44a82a17 | 39 | |
106a8ad9 | 40 | # Enforce default of $subdir instead of the normal $HOME default. |
44a82a17 | 41 | my $orig = $dir; |
106a8ad9 WD |
42 | my @dirs; |
43 | $dir =~ s/^"(.*?)"$/$1/; | |
44 | $dir =~ s/^\s+//; | |
45 | $dir =~ s/\s+$//; | |
46 | foreach (split(/(?<!\\)\s+/, $dir)) { | |
47 | s/\\(\s)/$1/g; # Unescape any escaped whitespace | |
48 | if ($subdir eq '/') { # Less checking for '/' access | |
49 | $dir = '/' if $dir eq ''; | |
50 | } else { | |
51 | s#^/##; # Don't allow absolute paths | |
52 | $_ = "$subdir/$_" unless m#^\Q$subdir\E(/|$)#; | |
53 | 1 while s#/\.\.(/|$)#/__/#g; # Don't allow foo/../../etc | |
54 | } | |
55 | tr#-_/a-zA-Z0-9.,+@^%: #_#c; # Don't allow '"&;|!=()[]{}<>*?#\$ | |
56 | s/(\s)/\\$1/g; # Re-escape whitespace | |
57 | push(@dirs, $_); | |
58 | } | |
59 | push(@dirs, $subdir) unless @dirs; | |
60 | $dir = join(' ', @dirs); | |
44a82a17 WD |
61 | |
62 | if (-f LOGFILE and open LOG,'>>',LOGFILE) { | |
63 | my ($mm,$hh) = (localtime)[1,2]; | |
106a8ad9 | 64 | my $host = $ENV{SSH_CONNECTION} || 'unknown'; |
44a82a17 | 65 | $host =~ s/ .*//; # Keep only the client's IP addr |
106a8ad9 | 66 | $host =~ s/^::ffff://; |
44a82a17 | 67 | $host = gethostbyaddr(inet_aton($host),AF_INET) || $host; |
106a8ad9 WD |
68 | my $dir_result = $dir eq $orig ? " OK" : "> \"$dir\""; |
69 | printf LOG "%02d:%02d %-13s [%s] =%s\n", $hh, $mm, $host, $command, $dir_result; | |
44a82a17 WD |
70 | close LOG; |
71 | } | |
72 | ||
106a8ad9 | 73 | exec "$cmd \"$dir\"" or die "exec($cmd \"$dir\") failed: $? $!"; |
44a82a17 | 74 | # Note: This assumes that the rsync protocol will not be maliciously hijacked. |