1# 2# (c) Jan Gehring <jan.gehring@gmail.com> 3# 4# vim: set ts=2 sw=2 tw=0: 5# vim: set expandtab: 6 7package Rex::Interface::Exec::Sudo; 8 9use 5.010001; 10use strict; 11use warnings; 12 13our $VERSION = '1.13.4'; # VERSION 14 15use Rex::Config; 16use Rex::Interface::Exec::Local; 17use Rex::Interface::Exec::SSH; 18use Rex::Helper::Encode; 19use Rex::Interface::File::Local; 20use Rex::Interface::File::SSH; 21 22use Rex::Commands; 23use Rex::Helper::Path; 24 25use base 'Rex::Interface::Exec::Base'; 26 27sub new { 28 my $that = shift; 29 my $proto = ref($that) || $that; 30 my $self = {@_}; 31 32 bless( $self, $proto ); 33 34 return $self; 35} 36 37sub exec { 38 my ( $self, $cmd, $path, $option ) = @_; 39 40 if ( exists $option->{cwd} ) { 41 $cmd = "cd " . $option->{cwd} . " && $cmd"; 42 } 43 44 if ( exists $option->{path} ) { 45 $path = $option->{path}; 46 } 47 48 my ( $exec, $file, $shell ); 49 if ( my $ssh = Rex::is_ssh() ) { 50 if ( ref $ssh eq "Net::OpenSSH" ) { 51 $exec = Rex::Interface::Exec->create("OpenSSH"); 52 $file = Rex::Interface::File->create("OpenSSH"); 53 } 54 else { 55 $exec = Rex::Interface::Exec->create("SSH"); 56 $file = Rex::Interface::File->create("SSH"); 57 } 58 } 59 else { 60 $exec = Rex::Interface::Exec->create("Local"); 61 $file = Rex::Interface::File->create("Local"); 62 } 63 $shell = Rex::Interface::Shell->create("Sh"); # we're using sh for sudo 64 65######## envs setzen. aber erst nachdem wir wissen ob wir sh forcen duerfen 66 # if(exists $option->{env}) { 67 # $shell->set_environment($option->{env}); 68 # } 69 70 my $sudo_password = ( 71 defined task() ? task->get_sudo_password : Rex::Config->get_sudo_password ); 72 my $enc_pw; 73 my $random_file = ""; 74 75 Rex::Logger::debug("Sudo: Executing: $cmd"); 76 77 if ($sudo_password) { 78 my $random_string = get_random( length($sudo_password), 'a' .. 'z' ); 79 my $crypt = $sudo_password ^ $random_string; 80 81 $random_file = get_tmp_file; 82 83 $file->open( '>', $random_file ); 84 $file->write(<<EOF); 85#!/usr/bin/perl 86unlink \$0; 87 88for (0..255) { 89 \$escapes{chr(\$_)} = sprintf("%%%02X", \$_); 90} 91 92my \$txt = \$ARGV[0]; 93 94\$txt=~ s/%([0-9A-Fa-f]{2})/chr(hex(\$1))/eg; 95 96my \$rnd = '$random_string'; 97print \$txt ^ \$rnd; 98print "\\n" 99 100EOF 101 102 $file->close; 103 104 $enc_pw = Rex::Helper::Encode::url_encode($crypt); 105 } 106 else { 107 $enc_pw = ""; 108 } 109 110 #my $sudo_options = Rex::get_current_connection()->{sudo_options}; 111 my $sudo_options = 112 Rex::get_current_connection_object()->get_current_sudo_options; 113 my $sudo_options_str = ""; 114 if ( exists $sudo_options->{user} ) { 115 $sudo_options_str .= " -u " . $sudo_options->{user}; 116 } 117 118 if ( Rex::Config->get_sudo_without_locales() ) { 119 Rex::Logger::debug( 120 "Using sudo without locales. If the locale is NOT C or en_US it will break many things!" 121 ); 122 $option->{no_locales} = 1; 123 } 124 125 my $sudo_command = "sudo $sudo_options_str -p '' -S"; 126 127 if ( Rex::Config->get_sudo_without_sh() ) { 128 Rex::Logger::debug( 129 "Using sudo without sh will break things like file editing."); 130 131 # $option->{no_sh} = 1; 132 $shell->set_inner_shell(0); 133 $shell->set_sudo_env(1); 134 135 if ( exists $option->{env} ) { 136 $shell->set_environment( $option->{env} ); 137 } 138 139 if ($enc_pw) { 140 141 # $option->{format_cmd} = 142 # "perl $random_file '$enc_pw' | sudo $sudo_options_str -p '' -S {{CMD}}"; 143 $sudo_command = "perl $random_file '$enc_pw' 2>/dev/null | $sudo_command"; 144 } 145 146 # else { 147 # $option->{format_cmd} = "sudo $sudo_options_str -p '' -S {{CMD}}"; 148 # } 149 } 150 else { 151 152 $shell->set_locale("C"); 153 $shell->path($path); 154 155 if ( Rex::Config->get_source_global_profile ) { 156 $shell->source_global_profile(1); 157 } 158 159 if ( Rex::Config->get_source_profile ) { 160 $shell->source_profile(1); 161 } 162 163 if ( exists $option->{env} ) { 164 $shell->set_environment( $option->{env} ); 165 } 166 167 # escape some special shell things 168 # $option->{preprocess_command} = sub { 169 # my ($_cmd) = @_; 170 # $_cmd =~ s/\\/\\\\/gms; 171 # $_cmd =~ s/"/\\"/gms; 172 # $_cmd =~ s/\$/\\\$/gms; 173 # }; 174 175 $shell->set_inner_shell(1); 176 177 # $cmd =~ s/\\/\\\\/gms; 178 # $cmd =~ s/"/\\"/gms; 179 # $cmd =~ s/\$/\\\$/gms; 180 181# Calling sudo with sh(1) in this case we don't need to respect current user shell, pass _force_sh flag to ssh layer 182# $option->{_force_sh} = 1; 183 184 if ($enc_pw) { 185 $sudo_command = "perl $random_file '$enc_pw' 2>/dev/null | $sudo_command"; 186 187# $option->{format_cmd} = 188# "perl $random_file '$enc_pw' | sudo $sudo_options_str -p '' -S sh -c \"{{CMD}}\""; 189 } 190 191 # else { 192 # $option->{format_cmd} = 193 # "sudo $sudo_options_str -p '' -S sh -c \"{{CMD}}\""; 194 # } 195 } 196 197 $option->{prepend_command} = $sudo_command; 198 199 my $real_exec = $shell->exec( $cmd, $option ); 200 Rex::Logger::debug("sudo: exec: $real_exec"); 201 202 return $exec->direct_exec( $real_exec, $option ); 203} 204 205sub _exec { 206 my ( $self, $cmd, $path, $option ) = @_; 207 208 my ( $exec, $file, $shell ); 209 if ( my $ssh = Rex::is_ssh() ) { 210 if ( ref $ssh eq "Net::OpenSSH" ) { 211 $exec = Rex::Interface::Exec->create("OpenSSH"); 212 } 213 else { 214 $exec = Rex::Interface::Exec->create("SSH"); 215 } 216 } 217 else { 218 $exec = Rex::Interface::Exec->create("Local"); 219 } 220 221 return $exec->_exec( $cmd, $option ); 222} 223 2241; 225