summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--meta/recipes-devtools/python/python/fix-cgi-http-server.patch176
-rw-r--r--meta/recipes-devtools/python/python_2.6.6.bb3
2 files changed, 178 insertions, 1 deletions
diff --git a/meta/recipes-devtools/python/python/fix-cgi-http-server.patch b/meta/recipes-devtools/python/python/fix-cgi-http-server.patch
new file mode 100644
index 0000000000..5df0fa5048
--- /dev/null
+++ b/meta/recipes-devtools/python/python/fix-cgi-http-server.patch
@@ -0,0 +1,176 @@
1This patch is taken from upstream and is a fix for CVE CVE-2011-1015
2
3Upstream-Status: Backport
4
5Signed-off-by: Joshua Lock <josh@linux.intel.com>
6
7# HG changeset patch
8# User Gregory P. Smith <greg@mad-scientist.com>
9# Date 1238999606 0
10# Node ID c6c4398293bd682b355f1050bb16bb3e18c8b40f
11# Parent e363958fcd70a43ad07b39df999552d0fa1cefc1
12- Issue #2254: Fix CGIHTTPServer information disclosure. Relative paths are
13 now collapsed within the url properly before looking in cgi_directories.
14
15Index: Python-2.6.6/Lib/CGIHTTPServer.py
16===================================================================
17--- Python-2.6.6.orig/Lib/CGIHTTPServer.py 2009-11-11 09:24:53.000000000 -0800
18+++ Python-2.6.6/Lib/CGIHTTPServer.py 2011-10-13 13:51:33.815347721 -0700
19@@ -70,27 +70,20 @@
20 return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
21
22 def is_cgi(self):
23- """Test whether self.path corresponds to a CGI script,
24- and return a boolean.
25+ """Test whether self.path corresponds to a CGI script.
26
27- This function sets self.cgi_info to a tuple (dir, rest)
28- when it returns True, where dir is the directory part before
29- the CGI script name. Note that rest begins with a
30- slash if it is not empty.
31-
32- The default implementation tests whether the path
33- begins with one of the strings in the list
34- self.cgi_directories (and the next character is a '/'
35- or the end of the string).
36+ Returns True and updates the cgi_info attribute to the tuple
37+ (dir, rest) if self.path requires running a CGI script.
38+ Returns False otherwise.
39+
40+ The default implementation tests whether the normalized url
41+ path begins with one of the strings in self.cgi_directories
42+ (and the next character is a '/' or the end of the string).
43 """
44-
45- path = self.path
46-
47- for x in self.cgi_directories:
48- i = len(x)
49- if path[:i] == x and (not path[i:] or path[i] == '/'):
50- self.cgi_info = path[:i], path[i+1:]
51- return True
52+ splitpath = _url_collapse_path_split(self.path)
53+ if splitpath[0] in self.cgi_directories:
54+ self.cgi_info = splitpath
55+ return True
56 return False
57
58 cgi_directories = ['/cgi-bin', '/htbin']
59@@ -299,6 +292,46 @@
60 self.log_message("CGI script exited OK")
61
62
63+# TODO(gregory.p.smith): Move this into an appropriate library.
64+def _url_collapse_path_split(path):
65+ """
66+ Given a URL path, remove extra '/'s and '.' path elements and collapse
67+ any '..' references.
68+
69+ Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
70+
71+ Returns: A tuple of (head, tail) where tail is everything after the final /
72+ and head is everything before it. Head will always start with a '/' and,
73+ if it contains anything else, never have a trailing '/'.
74+
75+ Raises: IndexError if too many '..' occur within the path.
76+ """
77+ # Similar to os.path.split(os.path.normpath(path)) but specific to URL
78+ # path semantics rather than local operating system semantics.
79+ path_parts = []
80+ for part in path.split('/'):
81+ if part == '.':
82+ path_parts.append('')
83+ else:
84+ path_parts.append(part)
85+ # Filter out blank non trailing parts before consuming the '..'.
86+ path_parts = [part for part in path_parts[:-1] if part] + path_parts[-1:]
87+ if path_parts:
88+ tail_part = path_parts.pop()
89+ else:
90+ tail_part = ''
91+ head_parts = []
92+ for part in path_parts:
93+ if part == '..':
94+ head_parts.pop()
95+ else:
96+ head_parts.append(part)
97+ if tail_part and tail_part == '..':
98+ head_parts.pop()
99+ tail_part = ''
100+ return ('/' + '/'.join(head_parts), tail_part)
101+
102+
103 nobody = None
104
105 def nobody_uid():
106Index: Python-2.6.6/Lib/test/test_httpservers.py
107===================================================================
108--- Python-2.6.6.orig/Lib/test/test_httpservers.py 2010-04-25 15:09:32.000000000 -0700
109+++ Python-2.6.6/Lib/test/test_httpservers.py 2011-10-13 13:51:33.815347721 -0700
110@@ -7,6 +7,7 @@
111 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
112 from SimpleHTTPServer import SimpleHTTPRequestHandler
113 from CGIHTTPServer import CGIHTTPRequestHandler
114+import CGIHTTPServer
115
116 import os
117 import sys
118@@ -324,6 +325,45 @@
119 finally:
120 BaseTestCase.tearDown(self)
121
122+ def test_url_collapse_path_split(self):
123+ test_vectors = {
124+ '': ('/', ''),
125+ '..': IndexError,
126+ '/.//..': IndexError,
127+ '/': ('/', ''),
128+ '//': ('/', ''),
129+ '/\\': ('/', '\\'),
130+ '/.//': ('/', ''),
131+ 'cgi-bin/file1.py': ('/cgi-bin', 'file1.py'),
132+ '/cgi-bin/file1.py': ('/cgi-bin', 'file1.py'),
133+ 'a': ('/', 'a'),
134+ '/a': ('/', 'a'),
135+ '//a': ('/', 'a'),
136+ './a': ('/', 'a'),
137+ './C:/': ('/C:', ''),
138+ '/a/b': ('/a', 'b'),
139+ '/a/b/': ('/a/b', ''),
140+ '/a/b/c/..': ('/a/b', ''),
141+ '/a/b/c/../d': ('/a/b', 'd'),
142+ '/a/b/c/../d/e/../f': ('/a/b/d', 'f'),
143+ '/a/b/c/../d/e/../../f': ('/a/b', 'f'),
144+ '/a/b/c/../d/e/.././././..//f': ('/a/b', 'f'),
145+ '../a/b/c/../d/e/.././././..//f': IndexError,
146+ '/a/b/c/../d/e/../../../f': ('/a', 'f'),
147+ '/a/b/c/../d/e/../../../../f': ('/', 'f'),
148+ '/a/b/c/../d/e/../../../../../f': IndexError,
149+ '/a/b/c/../d/e/../../../../f/..': ('/', ''),
150+ }
151+ for path, expected in test_vectors.iteritems():
152+ if isinstance(expected, type) and issubclass(expected, Exception):
153+ self.assertRaises(expected,
154+ CGIHTTPServer._url_collapse_path_split, path)
155+ else:
156+ actual = CGIHTTPServer._url_collapse_path_split(path)
157+ self.assertEquals(expected, actual,
158+ msg='path = %r\nGot: %r\nWanted: %r' % (
159+ path, actual, expected))
160+
161 def test_headers_and_content(self):
162 res = self.request('/cgi-bin/file1.py')
163 self.assertEquals(('Hello World\n', 'text/html', 200), \
164@@ -348,6 +388,12 @@
165 self.assertEquals(('Hello World\n', 'text/html', 200), \
166 (res.read(), res.getheader('Content-type'), res.status))
167
168+ def test_no_leading_slash(self):
169+ # http://bugs.python.org/issue2254
170+ res = self.request('cgi-bin/file1.py')
171+ self.assertEquals(('Hello World\n', 'text/html', 200),
172+ (res.read(), res.getheader('Content-type'), res.status))
173+
174
175 def test_main(verbose=None):
176 cwd = os.getcwd()
diff --git a/meta/recipes-devtools/python/python_2.6.6.bb b/meta/recipes-devtools/python/python_2.6.6.bb
index b0438fe164..59493f34ec 100644
--- a/meta/recipes-devtools/python/python_2.6.6.bb
+++ b/meta/recipes-devtools/python/python_2.6.6.bb
@@ -1,7 +1,7 @@
1require python.inc 1require python.inc
2DEPENDS = "python-native db gdbm openssl readline sqlite3 zlib" 2DEPENDS = "python-native db gdbm openssl readline sqlite3 zlib"
3DEPENDS_sharprom = "python-native db readline zlib gdbm openssl" 3DEPENDS_sharprom = "python-native db readline zlib gdbm openssl"
4PR = "${INC_PR}.2" 4PR = "${INC_PR}.3"
5LIC_FILES_CHKSUM = "file://LICENSE;md5=38fdd546420fab09ac6bd3d8a1c83eb6" 5LIC_FILES_CHKSUM = "file://LICENSE;md5=38fdd546420fab09ac6bd3d8a1c83eb6"
6 6
7DISTRO_SRC_URI ?= "file://sitecustomize.py" 7DISTRO_SRC_URI ?= "file://sitecustomize.py"
@@ -17,6 +17,7 @@ SRC_URI = "\
17 file://06-avoid_usr_lib_termcap_path_in_linking.patch \ 17 file://06-avoid_usr_lib_termcap_path_in_linking.patch \
18 file://07-linux3-regen-fix.patch \ 18 file://07-linux3-regen-fix.patch \
19 file://99-ignore-optimization-flag.patch \ 19 file://99-ignore-optimization-flag.patch \
20 file://fix-cgi-http-server.patch \
20 ${DISTRO_SRC_URI} \ 21 ${DISTRO_SRC_URI} \
21" 22"
22 23