File Coverage

lib/Ubic/Credentials/OS/POSIX.pm
Criterion Covered Total %
statement 106 166 63.8
branch 30 76 39.4
condition 4 12 33.3
subroutine 20 22 90.9
pod 12 12 100.0
total 172 288 59.7


line stmt bran cond sub pod time code
1             package Ubic::Credentials::OS::POSIX;
2             $Ubic::Credentials::OS::POSIX::VERSION = '1.59';
3 38     38   137 use strict;
  38         44  
  38         920  
4 38     38   125 use warnings;
  38         59  
  38         902  
5              
6 38     38   467 use parent qw(Ubic::Credentials);
  38         249  
  38         206  
7              
8             # ABSTRACT: POSIX-specific credentials implementation
9              
10              
11 38     38   1841 use List::MoreUtils qw(uniq);
  38         44  
  38         169  
12              
13 38     38   10208 use Params::Validate qw(:all);
  38         48  
  38         4498  
14 38     38   150 use Carp;
  38         50  
  38         51677  
15              
16             sub new {
17 186     186 1 232 my $class = shift;
18 186         2797 my $params = validate(@_, {
19             user => 0,
20             group => 0,
21             service => { optional => 1, isa => 'Ubic::Service' },
22             });
23              
24 186         555 my $self = {};
25 186 50       558 if (defined $params->{user}) {
    100          
26 0 0       0 if (defined $params->{service}) {
27 0         0 croak "Only one of 'user' and 'service' parameters should be specified";
28             }
29 0         0 $self->{user} = $params->{user};
30 0 0       0 $self->{group} = $params->{group} if defined $params->{group};
31             }
32             elsif (defined $params->{service}) {
33 92         344 $self->{user} = $params->{service}->user;
34 92         284 my @group = $params->{service}->group;
35 92 50       226 $self->{group} = [ @group ] if @group;
36             }
37             else {
38 94         355 $self->{real_user_id} = $<;
39 94         209 $self->{effective_user_id} = $>;
40 94         519 $self->{real_group_id} = [ split / /, $( ];
41 94         345 $self->{effective_group_id} = [ split / /, $) ];
42             # TODO - derive user from real_user_id when user is not specified (or from effective_user_id?!)
43             }
44              
45 186         935 return bless $self => $class;
46             }
47              
48             sub user {
49 200     200 1 190 my $self = shift;
50 200 50       374 unless (defined $self->{user}) {
51 0         0 my $user = getpwuid($>);
52 0 0       0 unless (defined $user) {
53 0         0 die "failed to get user name by uid $>";
54             }
55 0         0 $self->{user} = $user;
56             }
57 200         397 return $self->{user};
58             }
59              
60             sub group {
61 108     108 1 141 my $self = shift;
62 108 100       245 unless (defined $self->{group}) {
63 92         209 $self->_user2group;
64             }
65 108 50       279 unless (ref $self->{group}) {
66 0         0 $self->{group} = [ $self->{group} ];
67             }
68 108         111 return @{ $self->{group} };
  108         286  
69             }
70              
71             sub _user2uid {
72 32     32   45 my $self = shift;
73 32         71 my $user = $self->user;
74 32         1734 my $id = scalar getpwnam($user);
75 32 50       118 unless (defined $id) {
76 0         0 die "user $user not found";
77             }
78 32         132 return $id;
79             }
80              
81             sub real_user_id {
82 34     34 1 45 my $self = shift;
83 34 100       203 return $self->{real_user_id} if defined $self->{real_user_id};
84 16         38 return $self->_user2uid;
85             }
86              
87             sub effective_user_id {
88 110     110 1 269 my $self = shift;
89 110 100       442 return $self->{effective_user_id} if defined $self->{effective_user_id};
90 16         67 return $self->_user2uid;
91             }
92              
93             sub _group2gid {
94 32     32   41 my $self = shift;
95 32         80 my @group = $self->group;
96 32         45 my @gid;
97 32         98 for my $group (@group) {
98 32         985 my $gid = getgrnam($group);
99 32 50       104 unless (defined $gid) {
100 0         0 croak "group $group not found";
101             }
102 32         91 push @gid, $gid;
103             }
104 32 50       478 @gid = (@gid, @gid) if @gid == 1; # otherwise $) = "1 0"; $) = "1" leaves 0 in group list
105 32         212 return @gid;
106             }
107              
108             sub real_group_id {
109 34     34 1 41 my $self = shift;
110 34 100       81 return @{ $self->{real_group_id} } if defined $self->{real_group_id};
  18         70  
111 16         31 return $self->_group2gid;
112             }
113              
114             sub effective_group_id {
115 110     110 1 151 my $self = shift;
116 110 100       216 return @{ $self->{effective_group_id} } if defined $self->{effective_group_id};
  94         281  
117 16         45 return $self->_group2gid;
118             }
119              
120             sub _user2group {
121 92     92   98 my $self = shift;
122 92         152 my $user = $self->user;
123 92 50       167 confess "user not defined" unless defined $user;
124              
125 92         5570 my @pwnam = getpwnam $user;
126 92 50       311 unless (@pwnam) {
127 0         0 die "getpwnam failed for user $user";
128             }
129 92         125 my $group_id = $pwnam[3];
130 92         2400 my $main_group = getgrgid($group_id);
131 92 50       267 unless ($main_group) {
132 0         0 die "failed to get group name by gid $group_id";
133             }
134              
135             # TODO - can getgrent fail?
136 92         204402 setgrent();
137 92         126 my @groups;
138 92         1339 while (my @grent = getgrent()) {
139 4048         3625 my @users = split / /, $grent[3];
140 4048 50       14816 push @groups, $grent[0] if grep { $_ eq $user } @users;
  92         575  
141             }
142 92         969 endgrent();
143              
144 92         558 $self->{group} = [ $main_group, @groups ];
145             }
146              
147             sub set_effective {
148 76     76 1 100 my $self = shift;
149              
150 76         219 my $current_creds = Ubic::Credentials->new;
151 76         190 my $euid = $current_creds->effective_user_id();
152 76         172 my ($egid) = $current_creds->effective_group_id();
153 76         475 $egid =~ s/^(\d+).*/$1/;
154              
155 76         4116 my $current_user = getpwuid($euid);
156 76 50       228 unless (defined $current_user) {
157 0         0 die "failed to get current user name by euid $euid";
158             }
159 76         2928 my $current_group = getgrgid($egid);
160 76 50       227 unless (defined $current_group) {
161 0         0 die "failed to get current group name by egid $egid";
162             }
163              
164 76         244 my $user = $self->user;
165 76         156 my ($group) = $self->group;
166              
167 76 50       213 if ($group ne $current_group) {
168 0         0 $self->{old_egid} = $);
169 0         0 my $new_gid = getgrnam($group);
170 0 0       0 unless (defined $new_gid) {
171 0         0 die "group $group not found";
172             }
173              
174             # AccessGuard don't need to handle supplementary groups correctly, so this is ok
175 0         0 $) = "$new_gid 0";
176 0         0 my ($current_gid) = $) =~ /^(\d+)/;
177 0 0       0 if ($current_gid != $new_gid) {
178 0         0 die "Failed to change group from $current_group to $group: $!";
179             }
180             }
181              
182 76 50       475 if ($user ne $current_user) {
183 0         0 $self->{old_euid} = $>;
184 0 0       0 if ($current_user ne 'root') {
185 0         0 die "Can't change user from $current_user to $user";
186             }
187 0         0 my $new_uid = getpwnam($user);
188 0 0       0 unless (defined $new_uid) {
189 0         0 die "user $user not found";
190             }
191 0         0 $> = $new_uid;
192 0 0       0 if ($> != $new_uid) {
193 0         0 die "Failed to change user from $current_user to $user: $!";
194             }
195             }
196             }
197              
198             sub _groups_equal {
199 34     34   65 my ($self, $g1, $g2) = @_;
200 34         111 my ($main1, @other1) = split / /, $g1;
201 34         73 my ($main2, @other2) = split / /, $g2;
202 34   33     595 return ($main1 == $main2 and join(' ', sort { $a <=> $b } uniq($main1, @other1)) eq join(' ', sort { $a <=> $b } uniq($main2, @other2)));
203             }
204              
205              
206             sub reset_effective {
207 76     76 1 106 my $self = shift;
208              
209 76 50       214 if (defined $self->{old_euid}) {
210 0         0 $> = $self->{old_euid}; # return euid back to normal
211 0 0       0 if ($> != $self->{old_euid}) {
212 0         0 warn "Failed to restore euid from $> to $self->{old_euid}: $!";
213             }
214             }
215 76 50       532 if (defined $self->{old_egid}) {
216 0         0 $) = $self->{old_egid}; # return egid back to normal
217 0 0       0 if ($) != $self->{old_egid}) {
218 0         0 warn "Failed to restore egid from '$)' to '$self->{old_egid}': $!";
219             }
220             }
221             }
222              
223             sub eq {
224 17     17 1 30 my ($self, $other) = @_;
225 17 50 33     62 if (
      33        
      33        
226             $self->effective_user_id == $other->effective_user_id
227             and $self->real_user_id == $other->real_user_id
228             and $self->_groups_equal(join(" ", $self->effective_group_id), join(" ", $other->effective_group_id))
229             and $self->_groups_equal(join(" ", $self->real_group_id), join(" ", $other->real_group_id))
230             ) {
231 17         70 return 1;
232             }
233             else {
234 0           return;
235             }
236             }
237              
238             sub set {
239 0     0 1   my ($self) = @_;
240 0           my @effective_gid = $self->effective_group_id;
241 0           $) = "@effective_gid";
242 0 0         unless ($self->_groups_equal($), "@effective_gid")) {
243 0           die "Failed to set effective gid to @effective_gid: $!";
244             }
245 0           my $new_euid = $self->effective_user_id;
246 0           $> = $new_euid;
247 0 0         unless ($> == $new_euid) {
248 0           die "Failed to set effective uid to $new_euid: $!";
249             }
250 0           my @real_gid = $self->real_group_id;
251 0           $( = $real_gid[0];
252 0 0         unless ($self->_groups_equal($(, "@real_gid")) {
253 0           die "Failed to set real gid to @real_gid: $! (\$( = $(, real_gid = @real_gid)";
254             }
255 0           my $new_ruid = $self->real_user_id;
256 0           $< = $new_ruid;
257 0 0         unless ($< == $new_ruid) {
258 0           die "Failed to set real uid to $new_ruid: $!";
259             }
260             }
261              
262             sub as_string {
263 0     0 1   my $self = shift;
264 0           my $user = $self->user;
265 0           my ($group) = $self->group; # ignore complementary groups for the sake of readability
266 0           return "$user:$group";
267             }
268              
269              
270             1;
271              
272             __END__