line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# |
2
|
|
|
|
|
|
|
# (c) Jan Gehring |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Rex::User::OpenBSD; |
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
13
|
use v5.12.5; |
|
1
|
|
|
|
|
8
|
|
8
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
51
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
our $VERSION = '1.14.2.2'; # TRIAL VERSION |
11
|
|
|
|
|
|
|
|
12
|
1
|
|
|
1
|
|
7
|
use Rex::Logger; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
13
|
1
|
|
|
1
|
|
24
|
use Rex::Commands::MD5; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
12
|
|
14
|
1
|
|
|
1
|
|
9
|
use Rex::Helper::Run; |
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
62
|
|
15
|
1
|
|
|
1
|
|
7
|
use Rex::Helper::Encode; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
64
|
|
16
|
1
|
|
|
1
|
|
10
|
use Rex::Commands::Fs; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
8
|
|
17
|
1
|
|
|
1
|
|
9
|
use Rex::Interface::File; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
10
|
|
18
|
1
|
|
|
1
|
|
23
|
use Rex::Interface::Fs; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
20
|
|
19
|
1
|
|
|
1
|
|
44
|
use Rex::Interface::Exec; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
20
|
1
|
|
|
1
|
|
22
|
use Rex::User::Linux; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
10
|
|
21
|
1
|
|
|
1
|
|
30
|
use Rex::Helper::Path; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
77
|
|
22
|
1
|
|
|
1
|
|
8
|
use JSON::MaybeXS; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
56
|
|
23
|
|
|
|
|
|
|
|
24
|
1
|
|
|
1
|
|
6
|
use base qw(Rex::User::Linux); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
1763
|
|
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; |