line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Filesys::POSIX::ReducedPrivileges; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# Copyright (c) 2016, cPanel, Inc. |
4
|
|
|
|
|
|
|
# All rights reserved. |
5
|
|
|
|
|
|
|
# http://cpanel.net/ |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
# This is free software; you can redistribute it and/or modify it under the same |
8
|
|
|
|
|
|
|
# terms as Perl itself. See the LICENSE file for further details. |
9
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
368
|
use Filesys::POSIX::Error qw(throw); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
37
|
|
11
|
1
|
|
|
1
|
|
317
|
use Filesys::POSIX::ReducedPrivileges::Inode (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
15
|
|
12
|
1
|
|
|
1
|
|
299
|
use Filesys::POSIX::Real; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
17
|
|
13
|
1
|
|
|
1
|
|
4
|
use Carp (); |
|
1
|
|
|
|
|
0
|
|
|
1
|
|
|
|
|
9
|
|
14
|
1
|
|
|
1
|
|
2
|
use Errno (); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
189
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our @ISA = qw(Filesys::POSIX::Real); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 NAME |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
Filesys::POSIX::ReducedPrivileges - Portal to actual underlying filesystem as seen by a particular UID/GID. |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 SYNOPSIS |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
use Filesys::POSIX; |
25
|
|
|
|
|
|
|
use Filesys::POSIX::Real; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
my $fs = Filesys::POSIX->new(Filesys::POSIX::ReducedPrivileges->new, |
28
|
|
|
|
|
|
|
'path' => '/home/foo/test', |
29
|
|
|
|
|
|
|
'noatime' => 1, |
30
|
|
|
|
|
|
|
'uid' => 99, |
31
|
|
|
|
|
|
|
'gid' => 99, |
32
|
|
|
|
|
|
|
); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 DESCRIPTION |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
This module wraps the L filesystem type with entry and |
37
|
|
|
|
|
|
|
exit functions that switch the effective UID and GID whenever the filesystem |
38
|
|
|
|
|
|
|
is accessed. |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head1 MOUNT OPTIONS |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
The following values are mandatory: |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=over |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=item C |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
The path, in the real filesystem, upon which the new filesystem to be mounted |
49
|
|
|
|
|
|
|
will be based. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=item C |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
The numeric UID to use when accessing the real filesystem. |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=item C |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
The numeric GID to use when accessing the real filesystem. The suppelemental |
58
|
|
|
|
|
|
|
group list is also limited to this GID. |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=back |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=cut |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub new { |
65
|
3
|
|
|
3
|
1
|
1895
|
my ( $class, %opts ) = @_; |
66
|
3
|
|
|
|
|
13
|
my $self = $class->SUPER::new(); |
67
|
|
|
|
|
|
|
|
68
|
3
|
|
|
|
|
4
|
bless $self, $class; |
69
|
|
|
|
|
|
|
|
70
|
3
|
|
|
|
|
11
|
return $self; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub init { |
74
|
3
|
|
|
3
|
1
|
6
|
my ( $self, %opts ) = @_; |
75
|
3
|
100
|
|
|
|
9
|
my $path = $opts{'path'} or throw &Errno::EINVAL; |
76
|
|
|
|
|
|
|
|
77
|
2
|
|
|
|
|
3
|
$self->{_uid} = $opts{uid}; |
78
|
2
|
|
|
|
|
5
|
$self->{_gid} = "$opts{gid} $opts{gid}"; |
79
|
2
|
|
|
|
|
2
|
$self->{_privileges_reduced} = 0; |
80
|
|
|
|
|
|
|
|
81
|
2
|
|
|
|
|
8
|
my $root = Filesys::POSIX::ReducedPrivileges::Inode->from_disk( $path, 'dev' => $self ); |
82
|
|
|
|
|
|
|
|
83
|
2
|
100
|
|
|
|
10
|
throw &Errno::ENOTDIR unless $root->dir; |
84
|
|
|
|
|
|
|
|
85
|
1
|
|
|
|
|
3
|
$self->{'flags'} = \%opts; |
86
|
1
|
|
|
|
|
6
|
$self->{'path'} = Filesys::POSIX::Path->full($path); |
87
|
1
|
|
|
|
|
2
|
$self->{'root'} = $root; |
88
|
|
|
|
|
|
|
|
89
|
1
|
|
|
|
|
2
|
return $sel; |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub enter_filesystem { |
93
|
14
|
|
|
14
|
0
|
11
|
my $self = shift; |
94
|
14
|
|
|
|
|
15
|
$self->{_privileges_reduced}++; |
95
|
14
|
100
|
|
|
|
27
|
return unless ( $self->{_privileges_reduced} == 1 ); |
96
|
9
|
|
|
|
|
16
|
$self->{_original_uid} = $>; |
97
|
9
|
|
|
|
|
31
|
$self->{_original_gid} = $); |
98
|
9
|
|
|
|
|
193
|
$) = $self->{_gid}; |
99
|
9
|
|
|
|
|
24
|
$> = $self->{_uid}; |
100
|
1
|
|
|
1
|
|
3
|
no warnings 'numeric'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
100
|
|
101
|
|
|
|
|
|
|
|
102
|
9
|
50
|
33
|
|
|
63
|
unless ( $> == $self->{_uid} && int($)) eq int( $self->{_gid} ) ) { |
103
|
0
|
|
|
|
|
0
|
Carp::confess("failed to reduce privileges: $!"); |
104
|
|
|
|
|
|
|
} |
105
|
9
|
|
|
|
|
17
|
return; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub exit_filesystem { |
109
|
14
|
|
|
14
|
0
|
15
|
my $self = shift; |
110
|
14
|
|
|
|
|
11
|
$self->{_privileges_reduced}--; |
111
|
14
|
100
|
|
|
|
24
|
return unless ( $self->{_privileges_reduced} == 0 ); |
112
|
9
|
|
|
|
|
30
|
$> = $self->{_original_uid}; |
113
|
9
|
|
|
|
|
137
|
$) = $self->{_original_gid}; |
114
|
1
|
|
|
1
|
|
3
|
no warnings 'numeric'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
66
|
|
115
|
9
|
50
|
33
|
|
|
74
|
unless ( $> == $self->{_original_uid} && int($)) eq int( $self->{_original_gid} ) ) { |
116
|
0
|
|
|
|
|
0
|
Carp::confess("failed to restore privileges: $!"); |
117
|
|
|
|
|
|
|
} |
118
|
9
|
|
|
|
|
17
|
return; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
1; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
__END__ |