File Coverage

lib/Rex/User/OpenBSD.pm
Criterion Covered Total %
statement 41 171 23.9
branch 0 68 0.0
condition 0 24 0.0
subroutine 14 21 66.6
pod 0 6 0.0
total 55 290 18.9


line stmt bran cond sub pod time code
1             #
2             # (c) Jan Gehring
3             #
4              
5             package Rex::User::OpenBSD;
6              
7 1     1   22 use v5.12.5;
  1         6  
8 1     1   5 use warnings;
  1         5  
  1         42  
9              
10             our $VERSION = '1.14.3'; # VERSION
11              
12 1     1   12 use Rex::Logger;
  1         7  
  1         17  
13 1     1   24 use Rex::Commands::MD5;
  1         3  
  1         12  
14 1     1   6 use Rex::Helper::Run;
  1         3  
  1         64  
15 1     1   17 use Rex::Helper::Encode;
  1         7  
  1         47  
16 1     1   16 use Rex::Commands::Fs;
  1         2  
  1         7  
17 1     1   10 use Rex::Interface::File;
  1         3  
  1         7  
18 1     1   19 use Rex::Interface::Fs;
  1         3  
  1         18  
19 1     1   26 use Rex::Interface::Exec;
  1         3  
  1         8  
20 1     1   28 use Rex::User::Linux;
  1         2  
  1         15  
21 1     1   35 use Rex::Helper::Path;
  1         3  
  1         63  
22 1     1   8 use JSON::MaybeXS;
  1         2  
  1         70  
23              
24 1     1   6 use base qw(Rex::User::Linux);
  1         3  
  1         1767  
25              
26             sub new {
27 0     0 0   my $that = shift;
28 0   0       my $proto = ref($that) || $that;
29 0           my $self = $proto->SUPER::new(@_);
30              
31 0           bless( $self, $proto );
32              
33 0           return $self;
34             }
35              
36             sub create_user {
37 0     0 0   my ( $self, $user, $data ) = @_;
38              
39 0           my $cmd;
40              
41 0           my $old_pw_md5 = md5("/etc/passwd");
42              
43 0           my $uid = $self->get_uid($user);
44 0           my %user_info = $self->get_user($user);
45 0           my $should_create_home;
46              
47 0 0 0       if ( $data->{'create_home'} || $data->{'create-home'} ) {
    0 0        
    0 0        
      0        
      0        
48 0           $should_create_home = 1;
49             }
50             elsif ( $data->{'no_create_home'} || $data->{'no-create-home'} ) {
51 0           $should_create_home = 0;
52             }
53             elsif ( ( exists $data->{'no_create_home'} && $data->{'no_create_home'} == 0 )
54             || ( exists $data->{'no-create-home'} && $data->{'no-create-home'} == 0 ) )
55             {
56 0           $should_create_home = 1;
57             }
58              
59 0 0         if ( !defined $uid ) {
60 0           Rex::Logger::debug("User $user does not exists. Creating it now.");
61 0           $cmd = "useradd ";
62              
63 0 0         if ( exists $data->{system} ) {
64 0           $cmd .= " -r";
65             }
66             }
67             else {
68 0           Rex::Logger::debug("User $user already exists. Updating...");
69              
70 0           $cmd = "usermod ";
71             }
72              
73 0 0         if ( defined $user_info{uid} ) {
74 0 0         if ( exists $data->{uid} ) {
75              
76             # On OpenBSD, "usermod -u n login" fails when the user login
77             # has already n as userid. So skip it from the command arg
78             # when the uid is already correct.
79 0 0         $cmd .= " -u " . $data->{uid} unless $data->{uid} == $user_info{uid};
80             }
81             }
82             else {
83             # the user does not exist yet.
84 0           $cmd .= " -u " . $data->{uid};
85             }
86              
87 0 0         if ( exists $data->{home} ) {
88 0           $cmd .= " -d " . $data->{home};
89             }
90              
91 0 0 0       if ( $should_create_home && !defined $uid ) { #useradd mode
92 0           $cmd .= " -m ";
93             }
94              
95 0 0         if ( exists $data->{shell} ) {
96 0           $cmd .= " -s " . $data->{shell};
97             }
98              
99 0 0         if ( exists $data->{comment} ) {
100 0           $cmd .= " -c '" . $data->{comment} . "'";
101             }
102              
103 0 0         if ( exists $data->{expire} ) {
104 0           $cmd .= " -e '" . $data->{expire} . "'";
105             }
106              
107 0 0         if ( exists $data->{login_class} ) {
108 0           $cmd .= " -L '" . $data->{login_class} . "'";
109             }
110              
111 0 0         if ( exists $data->{groups} ) {
112 0           my @groups = @{ $data->{groups} };
  0            
113 0           my $pri_group = shift @groups;
114              
115 0           $cmd .= " -g $pri_group";
116              
117 0 0         if (@groups) {
118 0           $cmd .= " -G " . join( ",", @groups );
119             }
120             }
121              
122 0           my $rnd_file = get_tmp_file;
123 0           my $fh = Rex::Interface::File->create;
124 0           $fh->open( ">", $rnd_file );
125 0           $fh->write("$cmd $user\nexit \$?\n");
126 0           $fh->close;
127              
128 0           i_run "/bin/sh $rnd_file", fail_ok => 1;
129 0 0         if ( $? == 0 ) {
130 0           Rex::Logger::debug("User $user created/updated.");
131             }
132             else {
133 0           Rex::Logger::info( "Error creating/updating user $user", "warn" );
134 0           die("Error creating/updating user $user");
135             }
136              
137 0           Rex::Interface::Fs->create()->unlink($rnd_file);
138              
139 0 0         if ( exists $data->{password} ) {
140 0           Rex::Logger::debug("Changing password of $user.");
141 0           $rnd_file = get_tmp_file;
142 0           $fh = Rex::Interface::File->create;
143 0           $fh->open( ">", $rnd_file );
144             $fh->write( "usermod -p \$(encrypt -b 6 '"
145             . $data->{password}
146 0           . "') $user\nexit \$?\n" );
147 0           $fh->close;
148              
149 0           i_run "/bin/sh $rnd_file", fail_ok => 1;
150 0 0         if ( $? != 0 ) {
151 0           die("Error setting password for $user");
152             }
153              
154 0           Rex::Interface::Fs->create()->unlink($rnd_file);
155             }
156              
157 0 0         if ( exists $data->{crypt_password} ) {
158 0           Rex::Logger::debug("Setting encrypted password of $user");
159 0           $rnd_file = get_tmp_file;
160 0           $fh = Rex::Interface::File->create;
161 0           $fh->open( ">", $rnd_file );
162             $fh->write(
163 0           "usermod -p '" . $data->{crypt_password} . "' $user\nexit \$?\n" );
164 0           $fh->close;
165              
166 0           i_run "/bin/sh $rnd_file", fail_ok => 1;
167 0 0         if ( $? != 0 ) {
168 0           die("Error setting password for $user");
169             }
170              
171 0           Rex::Interface::Fs->create()->unlink($rnd_file);
172             }
173              
174 0           my $new_pw_md5 = md5("/etc/passwd");
175              
176 0 0         if ( $new_pw_md5 eq $old_pw_md5 ) {
177             return {
178 0           changed => 0,
179             ret => $self->get_uid($user),
180             };
181             }
182             else {
183             return {
184 0           changed => 1,
185             ret => $self->get_uid($user),
186             },
187             ;
188             }
189              
190             }
191              
192             sub get_user {
193 0     0 0   my ( $self, $user ) = @_;
194              
195 0           Rex::Logger::debug("Getting information for $user");
196 0           my $rnd_file = get_tmp_file;
197 0           my $fh = Rex::Interface::File->create;
198 0           my $script = q|
199             unlink $0;
200             print to_json([ getpwnam($ARGV[0]) ]);
201             |;
202 0           $fh->open( ">", $rnd_file );
203 0           $fh->write($script);
204 0           $fh->write( func_to_json() );
205 0           $fh->close;
206              
207 0           my $data_str = i_run "perl $rnd_file $user", fail_ok => 1;
208 0 0         if ( $? != 0 ) {
209 0           die("Error getting user information for $user");
210             }
211              
212 0           my $data = decode_json($data_str);
213              
214             return (
215 0           name => $data->[0],
216             password => $data->[1],
217             uid => $data->[2],
218             gid => $data->[3],
219             pwchange => $data->[4],
220             class => $data->[5],
221             comment => $data->[6],
222             home => $data->[7],
223             shell => $data->[8],
224             expire => $data->[9],
225             );
226             }
227              
228             sub lock_password {
229 0     0 0   my ( $self, $user ) = @_;
230              
231             # Is the password already locked?
232 0           my $result = i_run "getent passwd $user", fail_ok => 1;
233              
234 0 0         if ( $result !~ /^$user.*$/ ) {
    0          
235 0           die "Unexpected result from getent: $result";
236             }
237             elsif ( $result =~ /^$user.*-$/ ) {
238              
239             # Already locked
240 0           return { changed => 0 };
241             }
242             else {
243 0           my $ret = i_run "usermod -Z $user", fail_ok => 1;
244 0 0         if ( $? != 0 ) {
245 0           die("Error locking account $user: $ret");
246             }
247             return {
248 0           changed => 1,
249             ret => $ret,
250             };
251             }
252             }
253              
254             sub unlock_password {
255 0     0 0   my ( $self, $user ) = @_;
256              
257             # Is the password already unlocked?
258 0           my $result = i_run "getent passwd $user", fail_ok => 1;
259              
260 0 0         if ( $result !~ /^$user.*$/ ) {
    0          
261 0           die "Unexpected result from getent: $result";
262             }
263             elsif ( $result !~ /^$user.*-$/ ) {
264              
265             # Already unlocked
266 0           return { changed => 0 };
267             }
268             else {
269 0     0     my $ret = i_run "usermod -U $user", sub { @_ }, fail_ok => 1;
  0            
270 0 0         if ( $? != 0 ) {
271 0           die("Error unlocking account $user: $ret");
272             }
273             return {
274 0           changed => 1,
275             ret => $ret,
276             };
277             }
278             }
279              
280             sub rm_user {
281 0     0 0   my ( $self, $user, $data ) = @_;
282              
283 0           Rex::Logger::debug("Removing user $user");
284              
285 0           my %user_info = $self->get_user($user);
286              
287 0           my $cmd = "userdel";
288              
289 0 0         if ( exists $data->{delete_home} ) {
290 0           $cmd .= " -r";
291             }
292              
293 0           my $output = i_run $cmd . " " . $user, fail_ok => 1;
294 0 0         if ( $? == 67 ) {
    0          
295 0           Rex::Logger::info( "Cannot delete user $user (no such user)", "warn" );
296             }
297             elsif ( $? != 0 ) {
298 0           die("Error deleting user $user ($output)");
299             }
300              
301 0 0 0       if ( exists $data->{delete_home} && is_dir( $user_info{home} ) ) {
302 0           Rex::Logger::debug(
303             "userdel doesn't delete home directory. removing it now by hand...");
304 0           rmdir $user_info{home};
305             }
306              
307 0 0         if ( $? != 0 ) {
308 0           die( "Error removing " . $user_info{home} );
309             }
310              
311             }
312              
313             1;