aboutsummaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/perl/perl/fixes/file_path_chmod_race.diff
blob: 9902e0f3e1e8f655b3c37aaa5816fd7795e3a588 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
From b8507de17cceea6fe6ab7d67807600814d2eb971 Mon Sep 17 00:00:00 2001
From: John Lightsey <john@nixnuts.net>
Date: Tue, 2 May 2017 12:03:52 -0500
Subject: [PATCH] Prevent directory chmod race attack.

CVE-2017-6512 is a race condition attack where the chmod() of directories
that cannot be entered is misused to change the permissions on other
files or directories on the system. This has been corrected by limiting
the directory-permission loosening logic to systems where fchmod() is
supported.

[Backported (whitespace adjustments) to File-Path 2.12 / perl 5.24 by
Dominic Hargreaves for Debian.]

Bug: https://rt.cpan.org/Public/Bug/Display.html?id=121951
Bug-Debian: https://bugs.debian.org/863870
Patch-Name: fixes/file_path_chmod_race.diff

---
 cpan/File-Path/lib/File/Path.pm | 39 +++++++++++++++++++++++++--------------
 cpan/File-Path/t/Path.t         | 40 ++++++++++++++++++++++++++--------------
 2 files changed, 51 insertions(+), 28 deletions(-)

diff --git a/cpan/File-Path/lib/File/Path.pm b/cpan/File-Path/lib/File/Path.pm
index 034da1e..a824cc8 100644
--- a/cpan/File-Path/lib/File/Path.pm
+++ b/cpan/File-Path/lib/File/Path.pm
@@ -354,21 +354,32 @@ sub _rmtree {
 
                 # see if we can escalate privileges to get in
                 # (e.g. funny protection mask such as -w- instead of rwx)
-                $perm &= oct '7777';
-                my $nperm = $perm | oct '700';
-                if (
-                    !(
-                           $arg->{safe}
-                        or $nperm == $perm
-                        or chmod( $nperm, $root )
-                    )
-                  )
-                {
-                    _error( $arg,
-                        "cannot make child directory read-write-exec", $canon );
-                    next ROOT_DIR;
+                # This uses fchmod to avoid traversing outside of the proper
+                # location (CVE-2017-6512)
+                my $root_fh;
+                if (open($root_fh, '<', $root)) {
+                    my ($fh_dev, $fh_inode) = (stat $root_fh )[0,1];
+                    $perm &= oct '7777';
+                    my $nperm = $perm | oct '700';
+                    local $@;
+                    if (
+                        !(
+                            $arg->{safe}
+                           or $nperm == $perm
+                           or !-d _
+                           or $fh_dev ne $ldev
+                           or $fh_inode ne $lino
+                           or eval { chmod( $nperm, $root_fh ) }
+                        )
+                      )
+                    {
+                        _error( $arg,
+                            "cannot make child directory read-write-exec", $canon );
+                        next ROOT_DIR;
+                    }
+                    close $root_fh;
                 }
-                elsif ( !chdir($root) ) {
+                if ( !chdir($root) ) {
                     _error( $arg, "cannot chdir to child", $canon );
                     next ROOT_DIR;
                 }
diff --git a/cpan/File-Path/t/Path.t b/cpan/File-Path/t/Path.t
index 5644f57..fffc49c 100644
--- a/cpan/File-Path/t/Path.t
+++ b/cpan/File-Path/t/Path.t
@@ -3,7 +3,7 @@
 
 use strict;
 
-use Test::More tests => 127;
+use Test::More tests => 126;
 use Config;
 use Fcntl ':mode';
 use lib 't/';
@@ -17,6 +17,13 @@ BEGIN {
 
 my $Is_VMS = $^O eq 'VMS';
 
+my $fchmod_supported = 0;
+if (open my $fh, curdir()) {
+    my ($perm) = (stat($fh))[2];
+    $perm &= 07777;
+    eval { $fchmod_supported = chmod( $perm, $fh); };
+}
+
 # first check for stupid permissions second for full, so we clean up
 # behind ourselves
 for my $perm (0111,0777) {
@@ -298,16 +305,19 @@ is($created[0], $dir, "created directory (old style 3 mode undef) cross-check");
 
 is(rmtree($dir, 0, undef), 1, "removed directory 3 verbose undef");
 
-$dir = catdir($tmp_base,'G');
-$dir = VMS::Filespec::unixify($dir) if $Is_VMS;
+SKIP: {
+    skip "fchmod of directories not supported on this platform", 3 unless $fchmod_supported;
+    $dir = catdir($tmp_base,'G');
+    $dir = VMS::Filespec::unixify($dir) if $Is_VMS;
 
-@created = mkpath($dir, undef, 0200);
+    @created = mkpath($dir, undef, 0400);
 
-is(scalar(@created), 1, "created write-only dir");
+    is(scalar(@created), 1, "created read-only dir");
 
-is($created[0], $dir, "created write-only directory cross-check");
+    is($created[0], $dir, "created read-only directory cross-check");
 
-is(rmtree($dir), 1, "removed write-only dir");
+    is(rmtree($dir), 1, "removed read-only dir");
+}
 
 # borderline new-style heuristics
 if (chdir $tmp_base) {
@@ -449,26 +459,28 @@ SKIP: {
 }
 
 SKIP : {
-    my $skip_count = 19;
+    my $skip_count = 18;
     # this test will fail on Windows, as per:
     #   http://perldoc.perl.org/perlport.html#chmod
 
     skip "Windows chmod test skipped", $skip_count
         if $^O eq 'MSWin32';
+    skip "fchmod() on directories is not supported on this platform", $skip_count
+        unless $fchmod_supported;
     my $mode;
     my $octal_mode;
     my @inputs = (
-      0777, 0700, 0070, 0007,
-      0333, 0300, 0030, 0003,
-      0111, 0100, 0010, 0001,
-      0731, 0713, 0317, 0371, 0173, 0137,
-      00 );
+      0777, 0700, 0470, 0407,
+      0433, 0400, 0430, 0403,
+      0111, 0100, 0110, 0101,
+      0731, 0713, 0317, 0371,
+      0173, 0137);
     my $input;
     my $octal_input;
-    $dir = catdir($tmp_base, 'chmod_test');
 
     foreach (@inputs) {
         $input = $_;
+        $dir = catdir($tmp_base, sprintf("chmod_test%04o", $input));
         # We can skip from here because 0 is last in the list.
         skip "Mode of 0 means assume user defaults on VMS", 1
           if ($input == 0 && $Is_VMS);