Skip to content

Commit 702ffe7

Browse files
authored
Honor scalar conversion protocols across SSL backends (#9653)
* Honor scalar conversion protocols across SSL backends Reuse shared truth, float and bytes-like argument conversion without per-function parsing or changing backend policy. Assisted-by: OpenAI Codex:model version not exposed * Require certificate verification for WASM hostname checks Promote CERT_NONE to CERT_REQUIRED when enabling hostname checks in the browser backend, matching CPython and the other SSL backends. Preserve explicit verification modes and leave them unchanged when disabling hostname checks. Assisted-by: OpenAI Codex:model version not exposed
1 parent 0abb21a commit 702ffe7

4 files changed

Lines changed: 78 additions & 55 deletions

File tree

‎crates/stdlib/src/openssl.rs‎

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ mod _ssl {
7979
convert::ToPyException,
8080
exceptions,
8181
function::{
82-
ArgBytesLike, ArgMemoryBuffer, ArgStrOrBytesLike, Either, FsPath, OptionalArg,
83-
PyComparisonValue,
82+
ArgBytesLike, ArgIntoBool, ArgIntoFloat, ArgMemoryBuffer, ArgStrOrBytesLike,
83+
Either, FsPath, OptionalArg, PyComparisonValue,
8484
},
8585
object::{Traverse, TraverseFn},
8686
stdlib::_warnings,
@@ -466,7 +466,8 @@ mod _ssl {
466466
}
467467

468468
#[pyfunction(name = "RAND_add")]
469-
fn rand_add(string: ArgStrOrBytesLike, entropy: f64) {
469+
fn rand_add(string: ArgStrOrBytesLike, entropy: ArgIntoFloat) {
470+
let entropy = entropy.into_float();
470471
let f = |b: &[u8]| {
471472
for buf in b.chunks(core::ffi::c_int::MAX as usize) {
472473
unsafe { sys::RAND_add(buf.as_ptr() as *const _, buf.len() as _, entropy) }
@@ -1111,6 +1112,7 @@ mod _ssl {
11111112
args: GetGroupsArgs,
11121113
vm: &VirtualMachine,
11131114
) -> PyResult<PyListRef> {
1115+
let include_aliases = args.include_aliases.into_bool();
11141116
#[cfg(ossl350)]
11151117
unsafe {
11161118
const SSL_CTRL_GET0_IMPLEMENTED_GROUPS: core::ffi::c_int = 139;
@@ -1121,7 +1123,7 @@ mod _ssl {
11211123
let result = sys::SSL_CTX_ctrl(
11221124
zelf.ctx().as_ptr(),
11231125
SSL_CTRL_GET0_IMPLEMENTED_GROUPS,
1124-
core::ffi::c_long::from(args.include_aliases),
1126+
core::ffi::c_long::from(include_aliases),
11251127
groups.cast(),
11261128
);
11271129
if result == 0 {
@@ -1146,7 +1148,7 @@ mod _ssl {
11461148
}
11471149
#[cfg(not(ossl350))]
11481150
{
1149-
let _ = (zelf, args.include_aliases);
1151+
let _ = (zelf, include_aliases);
11501152
Err(vm.new_not_implemented_error("Getting groups requires OpenSSL 3.5 or later."))
11511153
}
11521154
}
@@ -1343,7 +1345,8 @@ mod _ssl {
13431345
zelf.check_hostname.load()
13441346
}
13451347
#[pygetset(setter)]
1346-
fn set_check_hostname(zelf: &Py<Self>, ch: bool) {
1348+
fn set_check_hostname(zelf: &Py<Self>, ch: ArgIntoBool) {
1349+
let ch = ch.into_bool();
13471350
let mut ctx = zelf.builder();
13481351
if ch && builder_as_ctx(&ctx).verify_mode() == SslVerifyMode::NONE {
13491352
ctx.set_verify(SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT);
@@ -1664,7 +1667,7 @@ mod _ssl {
16641667
args: GetCertArgs,
16651668
vm: &VirtualMachine,
16661669
) -> PyResult<Vec<PyObjectRef>> {
1667-
let binary_form = args.binary_form.unwrap_or(false);
1670+
let binary_form = args.binary_form.map_or(false, ArgIntoBool::into_bool);
16681671
let ctx = zelf.ctx();
16691672
let certs = cfg_select! {
16701673
ossl300 => ctx.cert_store().all_certificates(),
@@ -2207,8 +2210,12 @@ mod _ssl {
22072210
vm: &VirtualMachine,
22082211
) -> PyResult<PyObjectRef> {
22092212
// Use common helper function
2210-
let (ssl, socket_type, server_hostname) =
2211-
Self::new_py_ssl_socket(zelf.clone(), args.server_side, args.server_hostname, vm)?;
2213+
let (ssl, socket_type, server_hostname) = Self::new_py_ssl_socket(
2214+
zelf.clone(),
2215+
args.server_side.into_bool(),
2216+
args.server_hostname,
2217+
vm,
2218+
)?;
22122219

22132220
// Create SslStream with socket
22142221
let stream = ssl::SslStream::new(
@@ -2272,8 +2279,12 @@ mod _ssl {
22722279
vm: &VirtualMachine,
22732280
) -> PyResult<PyObjectRef> {
22742281
// Use common helper function
2275-
let (ssl, socket_type, server_hostname) =
2276-
Self::new_py_ssl_socket(zelf.clone(), args.server_side, args.server_hostname, vm)?;
2282+
let (ssl, socket_type, server_hostname) = Self::new_py_ssl_socket(
2283+
zelf.clone(),
2284+
args.server_side.into_bool(),
2285+
args.server_hostname,
2286+
vm,
2287+
)?;
22772288

22782289
// Create BioStream wrapper
22792290
let bio_stream = BioStream {
@@ -2337,7 +2348,7 @@ mod _ssl {
23372348
struct WrapBioArgs {
23382349
incoming: PyRef<PySslMemoryBio>,
23392350
outgoing: PyRef<PySslMemoryBio>,
2340-
server_side: bool,
2351+
server_side: ArgIntoBool,
23412352
#[pyarg(any, default)]
23422353
server_hostname: Option<PyStrRef>,
23432354
#[pyarg(named, default)]
@@ -2349,7 +2360,7 @@ mod _ssl {
23492360
#[derive(FromArgs)]
23502361
struct WrapSocketArgs {
23512362
sock: PyRef<PySocket>,
2352-
server_side: bool,
2363+
server_side: ArgIntoBool,
23532364
#[pyarg(any, default)]
23542365
server_hostname: Option<PyStrRef>,
23552366
#[pyarg(named, default)]
@@ -2380,13 +2391,13 @@ mod _ssl {
23802391
#[derive(FromArgs)]
23812392
struct GetCertArgs {
23822393
#[pyarg(any, optional)]
2383-
binary_form: OptionalArg<bool>,
2394+
binary_form: OptionalArg<ArgIntoBool>,
23842395
}
23852396

23862397
#[derive(FromArgs)]
23872398
struct GetGroupsArgs {
2388-
#[pyarg(named, default)]
2389-
include_aliases: bool,
2399+
#[pyarg(named, default = ArgIntoBool::FALSE)]
2400+
include_aliases: ArgIntoBool,
23902401
}
23912402

23922403
// Err is true if the socket is blocking
@@ -3557,7 +3568,7 @@ mod _ssl {
35573568
args: GetCertArgs,
35583569
vm: &VirtualMachine,
35593570
) -> PyResult<Option<PyObjectRef>> {
3560-
let binary = args.binary_form.unwrap_or(false);
3571+
let binary = args.binary_form.map_or(false, ArgIntoBool::into_bool);
35613572
with_ssl_pointer(&zelf.connection, |ssl_ptr| {
35623573
let ssl = unsafe { ssl::SslRef::from_ptr(ssl_ptr) };
35633574
if !ssl.is_init_finished() {

‎crates/stdlib/src/ssl.rs‎

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ mod _ssl {
6464
},
6565
convert::IntoPyException,
6666
function::{
67-
ArgBytesLike, ArgMemoryBuffer, Either, FsPath, FuncArgs, OptionalArg,
68-
PyComparisonValue, PySetterValue,
67+
ArgBytesLike, ArgIntoBool, ArgIntoFloat, ArgMemoryBuffer, ArgStrOrBytesLike,
68+
Either, FsPath, FuncArgs, OptionalArg, PyComparisonValue, PySetterValue,
6969
},
7070
stdlib::_warnings,
7171
types::{Comparable, Constructor, Hashable, PyComparisonOp, Representable},
@@ -420,7 +420,7 @@ mod _ssl {
420420
#[derive(FromArgs)]
421421
struct WrapSocketArgs {
422422
sock: PyObjectRef,
423-
server_side: bool,
423+
server_side: ArgIntoBool,
424424
#[pyarg(any, optional)]
425425
server_hostname: Option<PyUtf8StrRef>,
426426
#[pyarg(named, optional)]
@@ -433,7 +433,7 @@ mod _ssl {
433433
struct WrapBioArgs {
434434
incoming: PyRef<PyMemoryBIO>,
435435
outgoing: PyRef<PyMemoryBIO>,
436-
server_side: bool,
436+
server_side: ArgIntoBool,
437437
#[pyarg(any, optional)]
438438
server_hostname: Option<PyUtf8StrRef>,
439439
#[pyarg(named, optional)]
@@ -464,14 +464,14 @@ mod _ssl {
464464

465465
#[derive(FromArgs)]
466466
struct GetCertArgs {
467-
#[pyarg(positional, default)]
468-
der: bool,
467+
#[pyarg(positional, default = ArgIntoBool::FALSE)]
468+
der: ArgIntoBool,
469469
}
470470

471471
#[derive(FromArgs)]
472472
struct GetCaCertsArgs {
473-
#[pyarg(any, default)]
474-
binary_form: bool,
473+
#[pyarg(any, default = ArgIntoBool::FALSE)]
474+
binary_form: ArgIntoBool,
475475
}
476476

477477
#[pyclass(with(Constructor, Representable), flags(BASETYPE))]
@@ -576,7 +576,8 @@ mod _ssl {
576576
}
577577

578578
#[pygetset(setter)]
579-
fn set_check_hostname(zelf: &Py<Self>, value: bool) {
579+
fn set_check_hostname(zelf: &Py<Self>, value: ArgIntoBool) {
580+
let value = value.into_bool();
580581
*zelf.check_hostname.write() = value;
581582
// When check_hostname is enabled, ensure verify_mode is at least CERT_REQUIRED
582583
if value {
@@ -642,7 +643,8 @@ mod _ssl {
642643
}
643644

644645
#[pygetset(setter)]
645-
fn set_post_handshake_auth(zelf: &Py<Self>, value: bool) {
646+
fn set_post_handshake_auth(zelf: &Py<Self>, value: ArgIntoBool) {
647+
let value = value.into_bool();
646648
*zelf.post_handshake_auth.write() = value;
647649
zelf.invalidate_policy();
648650
}
@@ -1596,7 +1598,7 @@ mod _ssl {
15961598
args: GetCaCertsArgs,
15971599
vm: &VirtualMachine,
15981600
) -> PyResult<PyListRef> {
1599-
let binary_form = args.binary_form;
1601+
let binary_form = args.binary_form.into_bool();
16001602
let ca_certs_der = zelf.ca_certs_der.read();
16011603

16021604
let mut certs = Vec::new();
@@ -1726,6 +1728,7 @@ mod _ssl {
17261728
args: WrapSocketArgs,
17271729
vm: &VirtualMachine,
17281730
) -> PyResult<PyRef<PySSLSocket>> {
1731+
let server_side = args.server_side.into_bool();
17291732
let socket_mod = vm.import("socket", 0)?;
17301733
let socket_class = socket_mod.get_attr("socket", vm)?;
17311734

@@ -1741,7 +1744,7 @@ mod _ssl {
17411744
};
17421745

17431746
// Validate socket type and context protocol
1744-
if args.server_side && zelf.protocol == PROTOCOL_TLS_CLIENT {
1747+
if server_side && zelf.protocol == PROTOCOL_TLS_CLIENT {
17451748
return Err(vm
17461749
.new_os_subtype_error(
17471750
PySSLError::class(&vm.ctx).to_owned(),
@@ -1750,7 +1753,7 @@ mod _ssl {
17501753
)
17511754
.upcast());
17521755
}
1753-
if !args.server_side && zelf.protocol == PROTOCOL_TLS_SERVER {
1756+
if !server_side && zelf.protocol == PROTOCOL_TLS_SERVER {
17541757
return Err(vm
17551758
.new_os_subtype_error(
17561759
PySSLError::class(&vm.ctx).to_owned(),
@@ -1770,10 +1773,10 @@ mod _ssl {
17701773
record_cursor: PyMutex::new(RecordCursor::default()),
17711774
key_log: Arc::new(super::keylog::ConnectionKeyLog::new(zelf.key_log.clone())),
17721775
context: PyRwLock::new(zelf),
1773-
server_side: args.server_side,
1776+
server_side,
17741777
server_hostname: PyRwLock::new(hostname),
17751778
connection: PyMutex::new(None),
1776-
state: PyMutex::new(TlsState::new(args.server_side)),
1779+
state: PyMutex::new(TlsState::new(server_side)),
17771780
got_eof_error: AtomicBool::new(false),
17781781
session_was_reused: PyMutex::new(false),
17791782
owner: PyRwLock::new(args.owner.map(|o| o.downgrade(None, vm)).transpose()?),
@@ -1819,7 +1822,7 @@ mod _ssl {
18191822
None => None,
18201823
};
18211824

1822-
let server_side = args.server_side;
1825+
let server_side = args.server_side.into_bool();
18231826

18241827
// Validate socket type and context protocol
18251828
if server_side && zelf.protocol == PROTOCOL_TLS_CLIENT {
@@ -3604,7 +3607,7 @@ mod _ssl {
36043607
args: GetCertArgs,
36053608
vm: &VirtualMachine,
36063609
) -> PyResult<Option<PyObjectRef>> {
3607-
let binary = args.der;
3610+
let binary = args.der.into_bool();
36083611

36093612
// Check if handshake is complete
36103613
if !zelf.handshake_completed() {
@@ -4485,9 +4488,8 @@ mod _ssl {
44854488
}
44864489

44874490
#[pyfunction]
4488-
fn RAND_add(_string: PyObjectRef, _entropy: f64) {
4491+
fn RAND_add(_string: ArgStrOrBytesLike, _entropy: ArgIntoFloat) {
44894492
// No-op: the configured rustls provider handles its own entropy.
4490-
// Accept any type (str, bytes, bytearray)
44914493
}
44924494

44934495
#[pyfunction]

‎crates/stdlib/src/ssl_wasm.rs‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ mod _ssl {
2222
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
2323
VirtualMachine,
2424
builtins::{PyBytesRef, PyType, PyUtf8StrRef},
25-
function::{ArgBytesLike, FuncArgs, OptionalArg, PyComparisonValue, PySetterValue},
25+
function::{
26+
ArgBytesLike, ArgIntoBool, ArgIntoFloat, ArgStrOrBytesLike, FuncArgs, OptionalArg,
27+
PyComparisonValue, PySetterValue,
28+
},
2629
stdlib::_warnings,
2730
types::{Comparable, Constructor, PyComparisonOp, Representable},
2831
},
@@ -221,7 +224,8 @@ mod _ssl {
221224
}
222225

223226
#[pygetset(setter)]
224-
fn set_check_hostname(zelf: &Py<Self>, value: bool) {
227+
fn set_check_hostname(zelf: &Py<Self>, value: ArgIntoBool) {
228+
let value = value.into_bool();
225229
*zelf.check_hostname.write() = value;
226230
if value && *zelf.verify_mode.read() == CERT_NONE {
227231
*zelf.verify_mode.write() = CERT_REQUIRED;
@@ -278,7 +282,8 @@ mod _ssl {
278282
}
279283

280284
#[pygetset(setter)]
281-
fn set_post_handshake_auth(zelf: &Py<Self>, value: bool) {
285+
fn set_post_handshake_auth(zelf: &Py<Self>, value: ArgIntoBool) {
286+
let value = value.into_bool();
282287
*zelf.post_handshake_auth.write() = value;
283288
}
284289

@@ -572,16 +577,17 @@ mod _ssl {
572577
args: WrapBioArgs,
573578
vm: &VirtualMachine,
574579
) -> PyResult<PyObjectRef> {
580+
let server_side = args.server_side.map_or(false, ArgIntoBool::into_bool);
575581
if let Some(hostname) = args.server_hostname {
576582
validate_hostname(hostname.as_str(), vm)?;
577583
}
578-
if args.server_side.unwrap_or(false) && zelf.protocol == PROTOCOL_TLS_CLIENT {
584+
if server_side && zelf.protocol == PROTOCOL_TLS_CLIENT {
579585
return ssl_error(
580586
vm,
581587
"Cannot create a server socket with a PROTOCOL_TLS_CLIENT context",
582588
);
583589
}
584-
if !args.server_side.unwrap_or(false) && zelf.protocol == PROTOCOL_TLS_SERVER {
590+
if !server_side && zelf.protocol == PROTOCOL_TLS_SERVER {
585591
return ssl_error(
586592
vm,
587593
"Cannot create a client socket with a PROTOCOL_TLS_SERVER context",
@@ -669,7 +675,7 @@ mod _ssl {
669675
#[derive(FromArgs)]
670676
struct GetCertArgs {
671677
#[pyarg(any, optional)]
672-
binary_form: OptionalArg<bool>,
678+
binary_form: OptionalArg<ArgIntoBool>,
673679
}
674680

675681
#[derive(FromArgs)]
@@ -689,7 +695,7 @@ mod _ssl {
689695
#[allow(dead_code)]
690696
outgoing: PyRef<PyMemoryBIO>,
691697
#[pyarg(named, optional)]
692-
server_side: OptionalArg<bool>,
698+
server_side: OptionalArg<ArgIntoBool>,
693699
#[pyarg(named, optional)]
694700
server_hostname: Option<PyUtf8StrRef>,
695701
#[pyarg(named, optional)]
@@ -901,7 +907,7 @@ mod _ssl {
901907
}
902908

903909
#[pyfunction]
904-
fn RAND_add(_string: PyObjectRef, _entropy: f64) {}
910+
fn RAND_add(_string: ArgStrOrBytesLike, _entropy: ArgIntoFloat) {}
905911

906912
#[pyfunction]
907913
fn RAND_bytes(n: i32, vm: &VirtualMachine) -> PyResult<PyBytesRef> {

0 commit comments

Comments
 (0)