Skip to content

Commit 92599cd

Browse files
authored
Merge pull request #34 from ActiveState/BE-3126-cve-2023-40217
CVE-2023-40217
2 parents d958dcf + d0fee9c commit 92599cd

File tree

13 files changed

+2959
-17
lines changed

13 files changed

+2959
-17
lines changed

Doc/library/socket.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,23 @@ correspond to Unix system calls applicable to sockets.
636636
to decode C structures encoded as strings).
637637

638638

639+
.. method:: socket.getblocking()
640+
641+
Return ``True`` if socket is in blocking mode, ``False`` if in
642+
non-blocking.
643+
644+
This is equivalent to checking ``socket.gettimeout() == 0``.
645+
646+
.. versionadded:: 3.7
647+
648+
649+
.. method:: socket.gettimeout()
650+
651+
Return the timeout in seconds (float) associated with socket operations,
652+
or ``None`` if no timeout is set. This reflects the last call to
653+
:meth:`setblocking` or :meth:`settimeout`.
654+
655+
639656
.. method:: socket.ioctl(control, option)
640657

641658
:platform: Windows

Include/Python.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
#error "PYMALLOC_DEBUG requires WITH_PYMALLOC"
7979
#endif
8080
#include "pymath.h"
81+
#include "pytime.h"
8182
#include "pymem.h"
8283

8384
#include "object.h"

Include/pytime.h

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
#ifndef Py_LIMITED_API
2+
#ifndef Py_PYTIME_H
3+
#define Py_PYTIME_H
4+
5+
#include "pyconfig.h" /* include for defines */
6+
#include "object.h"
7+
8+
/**************************************************************************
9+
Symbols and macros to supply platform-independent interfaces to time related
10+
functions and constants
11+
**************************************************************************/
12+
#ifdef __cplusplus
13+
extern "C" {
14+
#endif
15+
16+
/* _PyTime_t: Python timestamp with subsecond precision. It can be used to
17+
store a duration, and so indirectly a date (related to another date, like
18+
UNIX epoch). */
19+
typedef int64_t _PyTime_t;
20+
#define _PyTime_MIN INT64_MIN
21+
#define _PyTime_MAX INT64_MAX
22+
23+
typedef enum {
24+
/* Round towards minus infinity (-inf).
25+
For example, used to read a clock. */
26+
_PyTime_ROUND_FLOOR=0,
27+
/* Round towards infinity (+inf).
28+
For example, used for timeout to wait "at least" N seconds. */
29+
_PyTime_ROUND_CEILING=1,
30+
/* Round to nearest with ties going to nearest even integer.
31+
For example, used to round from a Python float. */
32+
_PyTime_ROUND_HALF_EVEN=2,
33+
/* Round away from zero
34+
For example, used for timeout. _PyTime_ROUND_CEILING rounds
35+
-1e-9 to 0 milliseconds which causes bpo-31786 issue.
36+
_PyTime_ROUND_UP rounds -1e-9 to -1 millisecond which keeps
37+
the timeout sign as expected. select.poll(timeout) must block
38+
for negative values." */
39+
_PyTime_ROUND_UP=3,
40+
/* _PyTime_ROUND_TIMEOUT (an alias for _PyTime_ROUND_UP) should be
41+
used for timeouts. */
42+
_PyTime_ROUND_TIMEOUT = _PyTime_ROUND_UP
43+
} _PyTime_round_t;
44+
45+
46+
/* Convert a time_t to a PyLong. */
47+
PyAPI_FUNC(PyObject *) _PyLong_FromTime_t(
48+
time_t sec);
49+
50+
/* Convert a PyLong to a time_t. */
51+
PyAPI_FUNC(time_t) _PyLong_AsTime_t(
52+
PyObject *obj);
53+
54+
/* Convert a number of seconds, int or float, to time_t. */
55+
PyAPI_FUNC(int) _PyTime_ObjectToTime_t(
56+
PyObject *obj,
57+
time_t *sec,
58+
_PyTime_round_t);
59+
60+
/* Convert a number of seconds, int or float, to a timeval structure.
61+
usec is in the range [0; 999999] and rounded towards zero.
62+
For example, -1.2 is converted to (-2, 800000). */
63+
PyAPI_FUNC(int) _PyTime_ObjectToTimeval(
64+
PyObject *obj,
65+
time_t *sec,
66+
long *usec,
67+
_PyTime_round_t);
68+
69+
/* Convert a number of seconds, int or float, to a timespec structure.
70+
nsec is in the range [0; 999999999] and rounded towards zero.
71+
For example, -1.2 is converted to (-2, 800000000). */
72+
PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
73+
PyObject *obj,
74+
time_t *sec,
75+
long *nsec,
76+
_PyTime_round_t);
77+
78+
79+
/* Create a timestamp from a number of seconds. */
80+
PyAPI_FUNC(_PyTime_t) _PyTime_FromSeconds(int seconds);
81+
82+
/* Macro to create a timestamp from a number of seconds, no integer overflow.
83+
Only use the macro for small values, prefer _PyTime_FromSeconds(). */
84+
#define _PYTIME_FROMSECONDS(seconds) \
85+
((_PyTime_t)(seconds) * (1000 * 1000 * 1000))
86+
87+
/* Create a timestamp from a number of nanoseconds. */
88+
PyAPI_FUNC(_PyTime_t) _PyTime_FromNanoseconds(_PyTime_t ns);
89+
90+
/* Create a timestamp from nanoseconds (Python int). */
91+
PyAPI_FUNC(int) _PyTime_FromNanosecondsObject(_PyTime_t *t,
92+
PyObject *obj);
93+
94+
/* Convert a number of seconds (Python float or int) to a timetamp.
95+
Raise an exception and return -1 on error, return 0 on success. */
96+
PyAPI_FUNC(int) _PyTime_FromSecondsObject(_PyTime_t *t,
97+
PyObject *obj,
98+
_PyTime_round_t round);
99+
100+
/* Convert a number of milliseconds (Python float or int, 10^-3) to a timetamp.
101+
Raise an exception and return -1 on error, return 0 on success. */
102+
PyAPI_FUNC(int) _PyTime_FromMillisecondsObject(_PyTime_t *t,
103+
PyObject *obj,
104+
_PyTime_round_t round);
105+
106+
/* Convert a timestamp to a number of seconds as a C double. */
107+
PyAPI_FUNC(double) _PyTime_AsSecondsDouble(_PyTime_t t);
108+
109+
/* Convert timestamp to a number of milliseconds (10^-3 seconds). */
110+
PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
111+
_PyTime_round_t round);
112+
113+
/* Convert timestamp to a number of microseconds (10^-6 seconds). */
114+
PyAPI_FUNC(_PyTime_t) _PyTime_AsMicroseconds(_PyTime_t t,
115+
_PyTime_round_t round);
116+
117+
/* Convert timestamp to a number of nanoseconds (10^-9 seconds) as a Python int
118+
object. */
119+
PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
120+
121+
/* Create a timestamp from a timeval structure.
122+
Raise an exception and return -1 on overflow, return 0 on success. */
123+
PyAPI_FUNC(int) _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv);
124+
125+
/* Convert a timestamp to a timeval structure (microsecond resolution).
126+
tv_usec is always positive.
127+
Raise an exception and return -1 if the conversion overflowed,
128+
return 0 on success. */
129+
PyAPI_FUNC(int) _PyTime_AsTimeval(_PyTime_t t,
130+
struct timeval *tv,
131+
_PyTime_round_t round);
132+
133+
/* Similar to _PyTime_AsTimeval(), but don't raise an exception on error. */
134+
PyAPI_FUNC(int) _PyTime_AsTimeval_noraise(_PyTime_t t,
135+
struct timeval *tv,
136+
_PyTime_round_t round);
137+
138+
/* Convert a timestamp to a number of seconds (secs) and microseconds (us).
139+
us is always positive. This function is similar to _PyTime_AsTimeval()
140+
except that secs is always a time_t type, whereas the timeval structure
141+
uses a C long for tv_sec on Windows.
142+
Raise an exception and return -1 if the conversion overflowed,
143+
return 0 on success. */
144+
PyAPI_FUNC(int) _PyTime_AsTimevalTime_t(
145+
_PyTime_t t,
146+
time_t *secs,
147+
int *us,
148+
_PyTime_round_t round);
149+
150+
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE)
151+
/* Create a timestamp from a timespec structure.
152+
Raise an exception and return -1 on overflow, return 0 on success. */
153+
PyAPI_FUNC(int) _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts);
154+
155+
/* Convert a timestamp to a timespec structure (nanosecond resolution).
156+
tv_nsec is always positive.
157+
Raise an exception and return -1 on error, return 0 on success. */
158+
PyAPI_FUNC(int) _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts);
159+
#endif
160+
161+
/* Compute ticks * mul / div.
162+
The caller must ensure that ((div - 1) * mul) cannot overflow. */
163+
PyAPI_FUNC(_PyTime_t) _PyTime_MulDiv(_PyTime_t ticks,
164+
_PyTime_t mul,
165+
_PyTime_t div);
166+
167+
/* Get the current time from the system clock.
168+
169+
The function cannot fail. _PyTime_Init() ensures that the system clock
170+
works. */
171+
PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void);
172+
173+
/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
174+
The clock is not affected by system clock updates. The reference point of
175+
the returned value is undefined, so that only the difference between the
176+
results of consecutive calls is valid.
177+
178+
The function cannot fail. _PyTime_Init() ensures that a monotonic clock
179+
is available and works. */
180+
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
181+
182+
183+
/* Structure used by time.get_clock_info() */
184+
typedef struct {
185+
const char *implementation;
186+
int monotonic;
187+
int adjustable;
188+
double resolution;
189+
} _Py_clock_info_t;
190+
191+
/* Get the current time from the system clock.
192+
* Fill clock information if info is not NULL.
193+
* Raise an exception and return -1 on error, return 0 on success.
194+
*/
195+
PyAPI_FUNC(int) _PyTime_GetSystemClockWithInfo(
196+
_PyTime_t *t,
197+
_Py_clock_info_t *info);
198+
199+
/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
200+
The clock is not affected by system clock updates. The reference point of
201+
the returned value is undefined, so that only the difference between the
202+
results of consecutive calls is valid.
203+
204+
Fill info (if set) with information of the function used to get the time.
205+
206+
Return 0 on success, raise an exception and return -1 on error. */
207+
PyAPI_FUNC(int) _PyTime_GetMonotonicClockWithInfo(
208+
_PyTime_t *t,
209+
_Py_clock_info_t *info);
210+
211+
212+
/* Initialize time.
213+
Return 0 on success, raise an exception and return -1 on error. */
214+
PyAPI_FUNC(int) _PyTime_Init(void);
215+
216+
/* Converts a timestamp to the Gregorian time, using the local time zone.
217+
Return 0 on success, raise an exception and return -1 on error. */
218+
PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm);
219+
220+
/* Converts a timestamp to the Gregorian time, assuming UTC.
221+
Return 0 on success, raise an exception and return -1 on error. */
222+
PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
223+
224+
/* Get the performance counter: clock with the highest available resolution to
225+
measure a short duration.
226+
227+
The function cannot fail. _PyTime_Init() ensures that the system clock
228+
works. */
229+
PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);
230+
231+
/* Get the performance counter: clock with the highest available resolution to
232+
measure a short duration.
233+
234+
Fill info (if set) with information of the function used to get the time.
235+
236+
Return 0 on success, raise an exception and return -1 on error. */
237+
PyAPI_FUNC(int) _PyTime_GetPerfCounterWithInfo(
238+
_PyTime_t *t,
239+
_Py_clock_info_t *info);
240+
241+
#ifdef __cplusplus
242+
}
243+
#endif
244+
245+
#endif /* Py_PYTIME_H */
246+
#endif /* Py_LIMITED_API */

Lib/socket.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def getfqdn(name=''):
154154
_socketmethods = (
155155
'bind', 'connect', 'connect_ex', 'fileno', 'listen',
156156
'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
157-
'sendall', 'setblocking',
157+
'sendall', 'getblocking', 'setblocking',
158158
'settimeout', 'gettimeout', 'shutdown')
159159

160160
if os.name == "nt":

Lib/ssl.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@
9494
import os
9595
from collections import namedtuple
9696
from contextlib import closing
97+
import socket
98+
99+
# import logging
100+
97101

98102
import _ssl # if we can't import it, let the error propagate
99103

@@ -523,6 +527,7 @@ def __init__(self, sock=None, keyfile=None, certfile=None,
523527
server_hostname=None,
524528
_context=None):
525529

530+
self._sslobj = None
526531
self._makefile_refs = 0
527532
if _context:
528533
self._context = _context
@@ -568,6 +573,7 @@ def __init__(self, sock=None, keyfile=None, certfile=None,
568573
"in client mode")
569574
if self._context.check_hostname and not server_hostname:
570575
raise ValueError("check_hostname requires server_hostname")
576+
sock_timeout = sock.gettimeout()
571577
self.server_side = server_side
572578
self.server_hostname = server_hostname
573579
self.do_handshake_on_connect = do_handshake_on_connect
@@ -580,11 +586,42 @@ def __init__(self, sock=None, keyfile=None, certfile=None,
580586
if e.errno != errno.ENOTCONN:
581587
raise
582588
connected = False
589+
blocking = self.getblocking()
590+
self.setblocking(False)
591+
try:
592+
# We are not connected so this is not supposed to block, but
593+
# testing revealed otherwise on macOS and Windows so we do
594+
# the non-blocking dance regardless. Our raise when any data
595+
# is found means consuming the data is harmless.
596+
notconn_pre_handshake_data = self.recv(1)
597+
except (OSError, socket_error) as e:
598+
599+
# EINVAL occurs for recv(1) on non-connected on unix sockets.
600+
if e.errno not in (errno.ENOTCONN, errno.EINVAL):
601+
raise
602+
603+
notconn_pre_handshake_data = b''
604+
self.setblocking(blocking)
605+
if notconn_pre_handshake_data:
606+
# This prevents pending data sent to the socket before it was
607+
# closed from escaping to the caller who could otherwise
608+
# presume it came through a successful TLS connection.
609+
reason = "Closed before TLS handshake with data in recv buffer."
610+
notconn_pre_handshake_data_error = SSLError(e.errno, reason)
611+
# Add the SSLError attributes that _ssl.c always adds.
612+
notconn_pre_handshake_data_error.reason = reason
613+
notconn_pre_handshake_data_error.library = None
614+
try:
615+
self.close()
616+
except (OSError, socket_error):
617+
pass
618+
raise notconn_pre_handshake_data_error
583619
else:
584620
connected = True
585621

586622
self._closed = False
587623
self._sslobj = None
624+
self.settimeout(sock_timeout) # Must come after setblocking() calls.
588625
self._connected = connected
589626
if connected:
590627
# create the SSL object
@@ -598,7 +635,7 @@ def __init__(self, sock=None, keyfile=None, certfile=None,
598635
raise ValueError("do_handshake_on_connect should not be specified for non-blocking sockets")
599636
self.do_handshake()
600637

601-
except (OSError, ValueError):
638+
except (OSError, ValueError, socket_error):
602639
self.close()
603640
raise
604641

@@ -814,6 +851,12 @@ def unwrap(self):
814851
else:
815852
raise ValueError("No SSL wrapper around " + str(self))
816853

854+
def verify_client_post_handshake(self):
855+
if self._sslobj:
856+
return self._sslobj.verify_client_post_handshake()
857+
else:
858+
raise ValueError("No SSL wrapper around " + str(self))
859+
817860
def _real_close(self):
818861
self._sslobj = None
819862
socket._real_close(self)
@@ -915,7 +958,6 @@ def version(self):
915958
return None
916959
return self._sslobj.version()
917960

918-
919961
def wrap_socket(sock, keyfile=None, certfile=None,
920962
server_side=False, cert_reqs=CERT_NONE,
921963
ssl_version=PROTOCOL_TLS, ca_certs=None,

0 commit comments

Comments
 (0)