# B01–B04 — sqUnixSSL: TLS hardening (verify + minimum version + cipher list + options)

Bug ref      : always.md B.1, B.2, B.3, B.4 ; pharo.md §2.9, §6.3
Severity     : CRITICAL (TLS without cert validation on Linux/BSD; MITM trivial)
File         : extracted/plugins/SqueakSSL/src/unix/sqUnixSSL.c
Lines (HEAD) : 89-143 (`sqSetupSSL`)

This single PR addresses four cooperating defects in `sqSetupSSL`
because each is fixed by adding one line to the same function; the
configuration only becomes safe when all four are applied together.

## Problems

### B.1 — no SSL_CTX_set_verify

`sqSetupSSL` never calls `SSL_CTX_set_verify`. With the default
`SSL_VERIFY_NONE`, `SSL_get_verify_result()` reports `X509_V_OK` for
any cert (even self-signed or expired). The image-side "is this
connection verified?" check therefore returns yes for every cert,
which is the textbook MITM bypass.

### B.2 — SSLv23_method with only SSLv2 + SSLv3 disabled

```c
ssl->method = (SSL_METHOD*) SSLv23_method();
ssl->ctx = SSL_CTX_new(ssl->method);
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
```

TLS 1.0 and 1.1 (both formally deprecated since 2018) remain enabled
on every handshake.

### B.3 — cipher list `"!ADH:HIGH:MEDIUM:@STRENGTH"` permits MEDIUM

`MEDIUM` includes 3DES, RC4, and several CBC suites without
authenticated encryption. Modern hardening drops MEDIUM.

### B.4 — missing protection-bit options

No `SSL_OP_NO_COMPRESSION` (defeats CRIME), no
`SSL_OP_NO_RENEGOTIATION` (defeats class of renegotiation-driven
DoS / ciphertext attacks), no `SSL_OP_CIPHER_SERVER_PREFERENCE`
(server cipher preference not honored).

## Fix

```diff
diff --git a/plugins/SqueakSSL/src/unix/sqUnixSSL.c b/plugins/SqueakSSL/src/unix/sqUnixSSL.c
index 1af71037e..645a3f9eb 100644
--- a/plugins/SqueakSSL/src/unix/sqUnixSSL.c
+++ b/plugins/SqueakSSL/src/unix/sqUnixSSL.c
@@ -99,20 +99,39 @@ sqInt sqSetupSSL(sqSSL *ssl, int server) {
 
 	logTrace("sqSetupSSL: setting method\n");
 
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	/* TLS_method negotiates the highest available TLS version */
+	ssl->method = (SSL_METHOD*) TLS_method();
+#else
 	ssl->method = (SSL_METHOD*) SSLv23_method();
+#endif
 
 	logTrace("sqSetupSSL: Creating context\n");
 	ssl->ctx = SSL_CTX_new(ssl->method);
-	logTrace("sqSetupSSL: Disabling SSLv2 and SSLv3\n");
-	SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
 	if(!ssl->ctx) {
 		ERR_print_errors_fp(stdout);
 		return 0;
 	}
 
+	logTrace("sqSetupSSL: configuring protocol versions and options\n");
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	SSL_CTX_set_min_proto_version(ssl->ctx, TLS1_2_VERSION);
+#else
+	SSL_CTX_set_options(ssl->ctx,
+	                    SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+	                    SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
+#endif
+	SSL_CTX_set_options(ssl->ctx,
+	                    SSL_OP_NO_COMPRESSION |
+	                    SSL_OP_CIPHER_SERVER_PREFERENCE |
+	                    SSL_OP_NO_RENEGOTIATION);
+
 	logTrace("sqSetupSSL: setting cipher list\n");
-	SSL_CTX_set_cipher_list(ssl->ctx, "!ADH:HIGH:MEDIUM:@STRENGTH");
+	/* Drop MEDIUM (3DES/RC4/legacy CBC). */
+	SSL_CTX_set_cipher_list(ssl->ctx, "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!RC4:!3DES:!PSK:!SRP:!IDEA:!ADH:@STRENGTH");
+
+	logTrace("sqSetupSSL: enabling peer verification\n");
+	SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, NULL);
 
 	/* if a cert is provided, use it */
 	if(ssl->certName) {

```

## Test plan

- Connect to a server whose cert is signed by a CA in the system
  trust store: handshake succeeds, `SSL_get_verify_result()` returns
  `X509_V_OK`.
- Connect to `https://untrusted-root.badssl.com/`: handshake fails
  with `X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT` (or similar). Before
  this PR, the connection succeeded and the image believed the
  cert was OK.
- Connect to `https://tls-v1-1.badssl.com:1011/` and
  `https://tls-v1-2.badssl.com:1012/`: TLS 1.1 must fail; TLS 1.2
  must succeed.
- Run `openssl s_client` against the VM acting as a TLS server (if
  applicable) with `-cipher 'MEDIUM'` and `-cipher 'NULL'` — both
  must be rejected. With `-cipher 'HIGH'` succeeds.

## Risk notes

- Behavioural change for callers that were relying on the broken
  "always-verified" return: they will now correctly see a verify
  failure on bad certs. Any image code that gated on
  `SQSSL_OK` for security must be re-tested; if there are
  workflows that intentionally talked to self-signed servers, they
  must now provide a CA or explicitly accept the cert via the
  image's verification opt-out (no such opt-out exists today; if
  one is needed, add a new image-controllable boolean rather than
  reverting verification).
- `SSL_CTX_set_min_proto_version` requires OpenSSL 1.1.0+. The
  `#if OPENSSL_VERSION_NUMBER` fallback retains the legacy bit-mask
  form for older OpenSSL. The bit-set covers everything below TLS 1.2.
- `SSL_OP_NO_RENEGOTIATION` is OpenSSL 1.1.0+; on older versions the
  bit is simply ignored (it's a `long` constant, not a function).
- Cipher list is conservative but matches what hardened TLS guides
  recommend for compatibility with mainstream services. If a service
  requires a weaker suite, prefer fixing the service.
