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