line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Passwd::Linux; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
654
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
28
|
|
4
|
1
|
|
|
1
|
|
4
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
98
|
|
5
|
1
|
|
|
1
|
|
5
|
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
1609
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
require Exporter; |
8
|
|
|
|
|
|
|
require DynaLoader; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
@ISA = qw(Exporter DynaLoader); |
11
|
|
|
|
|
|
|
# Items to export into callers namespace by default. Note: do not export |
12
|
|
|
|
|
|
|
# names by default without a very good reason. Use EXPORT_OK instead. |
13
|
|
|
|
|
|
|
# Do not simply export all your public functions/methods/constants. |
14
|
|
|
|
|
|
|
@EXPORT = qw(); |
15
|
|
|
|
|
|
|
@EXPORT_OK = qw( |
16
|
|
|
|
|
|
|
modpwinfo |
17
|
|
|
|
|
|
|
setpwinfo |
18
|
|
|
|
|
|
|
rmpwnam |
19
|
|
|
|
|
|
|
mgetpwnam |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
$VERSION = '1.2'; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
bootstrap Passwd::Linux $VERSION; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our $have_lock = 0; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub _lock_and_read ($) { |
29
|
6
|
|
|
6
|
|
12
|
my $hold_lock = shift; |
30
|
6
|
|
|
|
|
10
|
my @pass; |
31
|
|
|
|
|
|
|
my @shad; |
32
|
0
|
|
|
|
|
0
|
my %entries; |
33
|
0
|
|
|
|
|
0
|
my $lock; |
34
|
|
|
|
|
|
|
|
35
|
6
|
|
|
|
|
12
|
my $save_separator = $/; # just in case calling program change the separator |
36
|
6
|
|
|
|
|
37
|
$/ = "\n"; |
37
|
|
|
|
|
|
|
|
38
|
6
|
100
|
|
|
|
17
|
if ($hold_lock != 0) { |
39
|
4
|
|
|
|
|
191
|
$lock = xs_getlock(); |
40
|
4
|
50
|
|
|
|
13
|
if ($lock != 0) { |
41
|
0
|
|
|
|
|
0
|
croak "Couldn't get lock on password files (are you root?)"; |
42
|
|
|
|
|
|
|
} |
43
|
4
|
|
|
|
|
5
|
$have_lock = 1; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
6
|
50
|
|
|
|
231
|
open(FILE, "
|
47
|
0
|
0
|
|
|
|
0
|
if ($hold_lock != 0) { |
48
|
0
|
|
|
|
|
0
|
$lock = xs_releaselock(); |
49
|
0
|
0
|
|
|
|
0
|
if ($lock != 0) { |
50
|
0
|
|
|
|
|
0
|
croak "Couldn't release lock after /etc/passwd read failure"; |
51
|
|
|
|
|
|
|
} else { |
52
|
0
|
|
|
|
|
0
|
croak "Read of /etc/passwd failed"; |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
}; |
56
|
6
|
|
|
|
|
247
|
chomp(@pass = ); |
57
|
|
|
|
|
|
|
|
58
|
6
|
|
|
|
|
66
|
close(FILE); |
59
|
|
|
|
|
|
|
|
60
|
6
|
50
|
|
|
|
187
|
if (open(FILE, "
|
61
|
6
|
|
|
|
|
133
|
@shad = ; |
62
|
6
|
|
|
|
|
63
|
close(FILE); |
63
|
|
|
|
|
|
|
} else { |
64
|
0
|
0
|
|
|
|
0
|
if ($hold_lock != 0) { |
65
|
0
|
|
|
|
|
0
|
$lock = xs_releaselock(); |
66
|
0
|
0
|
|
|
|
0
|
if ($lock != 0) { |
67
|
0
|
|
|
|
|
0
|
croak "Couldn't release lock after /etc/shadow read failure"; |
68
|
|
|
|
|
|
|
} else { |
69
|
0
|
|
|
|
|
0
|
croak "Read of /etc/shadow failed"; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
} else { # non-shadow permission read |
72
|
0
|
|
|
|
|
0
|
for (my $j = 0; $j <= $#pass; $j++) { |
73
|
0
|
|
|
|
|
0
|
$shad[$j] = "unknown:x\n"; |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
}; |
77
|
|
|
|
|
|
|
|
78
|
6
|
50
|
|
|
|
21
|
if ($#pass != $#shad) { |
79
|
0
|
|
|
|
|
0
|
croak "Mismatch in number of entries between /etc/passwd and /etc/shadow"; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
6
|
|
|
|
|
11
|
my $info = []; |
83
|
6
|
|
|
|
|
19
|
for (my $i=0; $i <= $#pass; $i++) { |
84
|
123
|
|
|
|
|
438
|
my @pentry = split(/:/, $pass[$i]); |
85
|
123
|
|
|
|
|
428
|
my @sentry = split(/:/, $shad[$i]); |
86
|
123
|
|
|
|
|
1003
|
my $entry = []; |
87
|
123
|
|
|
|
|
207
|
chomp(@sentry); |
88
|
123
|
|
|
|
|
130
|
$pentry[1] = $sentry[1]; |
89
|
123
|
|
|
|
|
105
|
push @{$entry}, @pentry, @sentry[2..$#sentry]; |
|
123
|
|
|
|
|
738
|
|
90
|
123
|
|
|
|
|
155
|
push @{$info}, $pentry[0]; #preserve the order |
|
123
|
|
|
|
|
189
|
|
91
|
123
|
|
|
|
|
223
|
$entries{$pentry[0]} = $entry; |
92
|
123
|
|
|
|
|
111
|
my @test = @{$entry}; |
|
123
|
|
|
|
|
1044
|
|
93
|
|
|
|
|
|
|
#print "added $pentry[0] - @{$entry}\n"; |
94
|
|
|
|
|
|
|
} |
95
|
6
|
|
|
|
|
14
|
$entries{':ORDER:'} = $info; |
96
|
6
|
|
|
|
|
13
|
$/ = $save_separator; # restore original input separator |
97
|
6
|
|
|
|
|
103
|
return %entries; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _write_and_release (%) { |
101
|
3
|
|
|
3
|
|
25
|
my %entries = @_; |
102
|
3
|
|
|
|
|
4
|
my $info; |
103
|
|
|
|
|
|
|
my $err; |
104
|
0
|
|
|
|
|
0
|
my $err2; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# verify we have the lock? |
107
|
3
|
50
|
|
|
|
10
|
if ($have_lock == 0) { |
108
|
0
|
|
|
|
|
0
|
croak "_write_and_release called and we didn't have the lock, how did that happen?" |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
3
|
50
|
|
|
|
9
|
if (exists($entries{':ORDER:'})) { |
112
|
3
|
|
|
|
|
6
|
$info = $entries{':ORDER:'}; |
113
|
|
|
|
|
|
|
} else { |
114
|
0
|
|
|
|
|
0
|
xs_releaselock(); |
115
|
0
|
|
|
|
|
0
|
croak "_write_and_release called with no entry order info"; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
3
|
50
|
|
|
|
235274
|
rename "/etc/passwd", "/etc/opasswd" or do { |
119
|
0
|
|
|
|
|
0
|
xs_releaselock(); |
120
|
0
|
|
|
|
|
0
|
croak "Couldn't rename /etc/passwd"; |
121
|
|
|
|
|
|
|
}; |
122
|
|
|
|
|
|
|
|
123
|
3
|
50
|
|
|
|
328
|
rename "/etc/shadow", "/etc/oshadow" or do { |
124
|
0
|
|
|
|
|
0
|
$err = rename "/etc/opasswd", "/etc/passwd"; |
125
|
0
|
|
|
|
|
0
|
xs_releaselock(); |
126
|
0
|
0
|
|
|
|
0
|
if ($err == 0) { |
127
|
0
|
|
|
|
|
0
|
croak "/etc/passwd may not exist, /etc/opasswd contains the correct entries"; |
128
|
|
|
|
|
|
|
} |
129
|
0
|
|
|
|
|
0
|
croak "Couldn't rename /etc/shadow"; |
130
|
|
|
|
|
|
|
}; |
131
|
|
|
|
|
|
|
|
132
|
3
|
50
|
|
|
|
328
|
open(PASS, ">/etc/passwd") or do { |
133
|
0
|
|
|
|
|
0
|
$err = rename "/etc/opasswd", "/etc/passwd"; |
134
|
0
|
|
|
|
|
0
|
$err2 = rename "/etc/oshadow", "/etc/shadow"; |
135
|
0
|
|
|
|
|
0
|
xs_releaselock(); |
136
|
0
|
0
|
0
|
|
|
0
|
if (($err == 0) || ($err2 == 0)) { |
137
|
0
|
|
|
|
|
0
|
croak "/etc/passwd or /etc/shadow may not exist, /etc/opasswd and /etc/oshadow contain the correct entries"; |
138
|
|
|
|
|
|
|
} |
139
|
0
|
|
|
|
|
0
|
croak "Couldn't open /etc/passwd for writing"; |
140
|
|
|
|
|
|
|
}; |
141
|
3
|
|
|
|
|
105
|
chmod 0644, "/etc/passwd"; |
142
|
|
|
|
|
|
|
|
143
|
3
|
50
|
|
|
|
209
|
open(SHAD, ">/etc/shadow") or do { |
144
|
0
|
|
|
|
|
0
|
$err = rename "/etc/opasswd", "/etc/passwd"; |
145
|
0
|
|
|
|
|
0
|
$err2 = rename "/etc/oshadow", "/etc/shadow"; |
146
|
0
|
|
|
|
|
0
|
xs_releaselock(); |
147
|
0
|
0
|
0
|
|
|
0
|
if (($err == 0) || ($err2 == 0)) { |
148
|
0
|
|
|
|
|
0
|
croak "/etc/passwd or /etc/shadow may not exist, /etc/opasswd and /etc/oshadow contain the correct entries"; |
149
|
|
|
|
|
|
|
} |
150
|
0
|
|
|
|
|
0
|
croak "Couldn't open /etc/passwd for writing"; |
151
|
|
|
|
|
|
|
}; |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# if a shadow group exists give it read permissions |
154
|
3
|
|
|
|
|
3109
|
my @sgrp = getgrnam("shadow"); |
155
|
3
|
50
|
|
|
|
17
|
if (@sgrp > 1) { |
156
|
3
|
|
|
|
|
113
|
chown 0, $sgrp[2], "/etc/shadow"; |
157
|
3
|
|
|
|
|
70
|
chmod 0640, "/etc/shadow"; |
158
|
|
|
|
|
|
|
} else { |
159
|
0
|
|
|
|
|
0
|
chmod 0600, "/etc/shadow"; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
3
|
|
|
|
|
11
|
my $save_separator = $/; # just in case the program using this has changed it |
163
|
3
|
|
|
|
|
10
|
$/ = "\n"; |
164
|
3
|
|
|
|
|
4
|
foreach my $user (@{$info}) { |
|
3
|
|
|
|
|
11
|
|
165
|
63
|
100
|
|
|
|
137
|
if (exists($entries{$user})) { |
166
|
62
|
|
|
|
|
55
|
my @data = @{$entries{$user}}; |
|
62
|
|
|
|
|
294
|
|
167
|
62
|
|
|
|
|
170
|
my $pentry = join(":", $data[0], "x", @data[2..6]); |
168
|
62
|
|
|
|
|
180
|
my $sentry = join(":", @data[0..1], @data[7..$#data]); |
169
|
62
|
|
|
|
|
146
|
print PASS "$pentry\n"; |
170
|
62
|
|
|
|
|
66
|
chomp $sentry; |
171
|
62
|
|
|
|
|
214
|
print SHAD "$sentry\n"; |
172
|
|
|
|
|
|
|
} # else skip, its a deleted entry |
173
|
|
|
|
|
|
|
} |
174
|
3
|
|
|
|
|
7
|
$/ = $save_separator; |
175
|
|
|
|
|
|
|
|
176
|
3
|
|
|
|
|
172
|
close(SHAD); |
177
|
3
|
|
|
|
|
91
|
close(PASS); |
178
|
|
|
|
|
|
|
|
179
|
3
|
|
|
|
|
50
|
my $lock = xs_releaselock(); |
180
|
3
|
50
|
|
|
|
25
|
if ($lock != 0) { |
181
|
0
|
|
|
|
|
0
|
croak "Couldn't release lock on password files"; |
182
|
|
|
|
|
|
|
} |
183
|
3
|
|
|
|
|
5
|
$have_lock = 0; |
184
|
3
|
|
|
|
|
25
|
return 0; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub _set_user ($$) { |
188
|
2
|
|
|
2
|
|
2
|
my %entries = %{$_[0]}; |
|
2
|
|
|
|
|
20
|
|
189
|
2
|
|
|
|
|
6
|
my @info = @{$_[1]}; |
|
2
|
|
|
|
|
9
|
|
190
|
2
|
|
|
|
|
13
|
my $days = int(time()/86400); |
191
|
2
|
|
|
|
|
2
|
my @data; |
192
|
|
|
|
|
|
|
|
193
|
2
|
|
|
|
|
4
|
$info[7] = $days; |
194
|
2
|
100
|
|
|
|
6
|
if (exists($entries{$info[0]})) { |
195
|
1
|
|
|
|
|
2
|
@data = @{$entries{$info[0]}}; |
|
1
|
|
|
|
|
6
|
|
196
|
|
|
|
|
|
|
} else { |
197
|
1
|
|
|
|
|
1
|
push @{$entries{":ORDER:"}}, $info[0]; |
|
1
|
|
|
|
|
3
|
|
198
|
|
|
|
|
|
|
} |
199
|
2
|
|
|
|
|
7
|
for (my $i = 0; $i <= $#info; $i++) { |
200
|
28
|
|
|
|
|
55
|
$data[$i] = $info[$i]; |
201
|
|
|
|
|
|
|
} |
202
|
2
|
|
|
|
|
4
|
$entries{$info[0]} = \@data; |
203
|
|
|
|
|
|
|
|
204
|
2
|
50
|
|
|
|
4
|
if (eval { _write_and_release(%entries); } ) { |
|
2
|
|
|
|
|
13
|
|
205
|
0
|
|
|
|
|
0
|
print $@; |
206
|
0
|
|
|
|
|
0
|
return 1; |
207
|
|
|
|
|
|
|
} |
208
|
2
|
|
|
|
|
30
|
return 0; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub modpwinfo { |
212
|
2
|
|
|
2
|
0
|
1656
|
my @info = @_; |
213
|
2
|
|
|
|
|
5
|
my @user; |
214
|
|
|
|
|
|
|
|
215
|
2
|
50
|
33
|
|
|
17
|
if (($#info < 1) || ($#info > 13)) { |
216
|
0
|
|
|
|
|
0
|
croak "modpwinfo: (name, crypted_pass, [uid, gid, gecos, home, shell, stuff from shadow file] )"; |
217
|
|
|
|
|
|
|
} |
218
|
2
|
|
|
|
|
7
|
my %entries = _lock_and_read(1); |
219
|
2
|
100
|
|
|
|
25
|
if (exists($entries{$info[0]})) { |
220
|
1
|
|
|
|
|
3
|
@user = @{$entries{$info[0]}}; |
|
1
|
|
|
|
|
6
|
|
221
|
1
|
|
|
|
|
6
|
for (my $i = 0; $i <= $#info; $i++) { |
222
|
13
|
|
|
|
|
29
|
$user[$i] = $info[$i]; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
} else { |
225
|
1
|
|
|
|
|
15
|
xs_releaselock(); |
226
|
1
|
|
|
|
|
2
|
$have_lock = 0; |
227
|
1
|
|
|
|
|
24
|
return 2; |
228
|
|
|
|
|
|
|
} |
229
|
1
|
|
|
|
|
7
|
return _set_user(\%entries, \@user); |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
sub setpwinfo { |
233
|
1
|
|
|
1
|
0
|
33
|
my @info = @_; |
234
|
1
|
|
|
|
|
2
|
my @user; |
235
|
|
|
|
|
|
|
|
236
|
1
|
50
|
33
|
|
|
9
|
if (($#info < 1) || ($#info > 13)) { |
237
|
0
|
|
|
|
|
0
|
print "setpwinfo croaking\n"; |
238
|
0
|
|
|
|
|
0
|
croak "setpwinfo: (name, crypted_pass, uid, gid, gecos, home, shell, [(man shadow for the rest of the fields)] )"; |
239
|
|
|
|
|
|
|
} |
240
|
1
|
|
|
|
|
3
|
my %entries = _lock_and_read(1); |
241
|
1
|
50
|
|
|
|
6
|
if (exists($entries{$info[0]})) { |
242
|
0
|
|
|
|
|
0
|
@user = @{$entries{$info[0]}}; |
|
0
|
|
|
|
|
0
|
|
243
|
|
|
|
|
|
|
} else { |
244
|
1
|
|
|
|
|
1
|
$user[8] = 0; |
245
|
1
|
|
|
|
|
2
|
$user[9] = 99999; |
246
|
1
|
|
|
|
|
2
|
$user[10] = 7; |
247
|
1
|
|
|
|
|
1
|
$user[13] = "\n"; # fill in the rest as empty |
248
|
|
|
|
|
|
|
} |
249
|
1
|
|
|
|
|
8
|
for (my $i = 0; $i <= $#info; $i++) { |
250
|
7
|
|
|
|
|
23
|
$user[$i] = $info[$i]; |
251
|
|
|
|
|
|
|
} |
252
|
1
|
|
|
|
|
4
|
return _set_user(\%entries, \@user); |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub mgetpwnam { |
256
|
2
|
|
|
2
|
0
|
95
|
my ($login) = @_; |
257
|
|
|
|
|
|
|
|
258
|
2
|
|
|
|
|
9
|
my %entries = _lock_and_read(0); |
259
|
2
|
100
|
|
|
|
11
|
if (exists($entries{$login})) { |
260
|
1
|
|
|
|
|
1
|
my @info = @{$entries{$login}}; |
|
1
|
|
|
|
|
6
|
|
261
|
1
|
|
|
|
|
34
|
return @info; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
1
|
|
|
|
|
23
|
return; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub rmpwnam { |
268
|
1
|
|
|
1
|
0
|
49
|
my %entries = _lock_and_read(1); |
269
|
1
|
|
|
|
|
5
|
foreach my $login (@_) { |
270
|
1
|
50
|
|
|
|
4
|
if (exists($entries{$login})) { |
271
|
1
|
|
|
|
|
3
|
my @data = @{$entries{$login}}; |
|
1
|
|
|
|
|
5
|
|
272
|
1
|
50
|
|
|
|
7
|
if ($data[2] != 0) { # don't delete uid 0 accounts |
273
|
1
|
|
|
|
|
8
|
delete $entries{$login}; |
274
|
|
|
|
|
|
|
} else { |
275
|
0
|
|
|
|
|
0
|
return 1; |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
1
|
|
|
|
|
8
|
_write_and_release(%entries); |
280
|
1
|
|
|
|
|
31
|
return 0; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
1; |
284
|
|
|
|
|
|
|
__END__ |