aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/std/crypto/tls/Client.zig18
-rw-r--r--lib/std/http/Client.zig10
2 files changed, 26 insertions, 2 deletions
diff --git a/lib/std/crypto/tls/Client.zig b/lib/std/crypto/tls/Client.zig
index 877ad33bb4..44891a1973 100644
--- a/lib/std/crypto/tls/Client.zig
+++ b/lib/std/crypto/tls/Client.zig
@@ -26,6 +26,14 @@ partial_ciphertext_end: u15,
/// When this is true, the stream may still not be at the end because there
/// may be data in `partially_read_buffer`.
received_close_notify: bool,
+/// By default, reaching the end-of-stream when reading from the server will
+/// cause `error.TlsConnectionTruncated` to be returned, unless a close_notify
+/// message has been received. By setting this flag to `true`, instead, the
+/// end-of-stream will be forwarded to the application layer above TLS.
+/// This makes the application vulnerable to truncation attacks unless the
+/// application layer itself verifies that the amount of data received equals
+/// the amount of data expected, such as HTTP with the Content-Length header.
+allow_truncation_attacks: bool = false,
application_cipher: tls.ApplicationCipher,
/// The size is enough to contain exactly one TLSCiphertext record.
/// This buffer is segmented into four parts:
@@ -900,8 +908,14 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)
const ask_iovecs = limitVecs(&ask_iovecs_buf, ask_len);
const actual_read_len = try stream.readv(ask_iovecs);
if (actual_read_len == 0) {
- // This is either a truncation attack, or a bug in the server.
- return error.TlsConnectionTruncated;
+ // This is either a truncation attack, a bug in the server, or an
+ // intentional omission of the close_notify message due to truncation
+ // detection handled above the TLS layer.
+ if (c.allow_truncation_attacks) {
+ c.received_close_notify = true;
+ } else {
+ return error.TlsConnectionTruncated;
+ }
}
// There might be more bytes inside `in_stack_buffer` that need to be processed,
diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig
index efae62680d..8a4a771416 100644
--- a/lib/std/http/Client.zig
+++ b/lib/std/http/Client.zig
@@ -1,3 +1,7 @@
+//! This API is a barely-touched, barely-functional http client, just the
+//! absolute minimum thing I needed in order to test `std.crypto.tls`. Bear
+//! with me and I promise the API will become useful and streamlined.
+
const std = @import("../std.zig");
const assert = std.debug.assert;
const http = std.http;
@@ -10,6 +14,9 @@ headers: std.ArrayListUnmanaged(u8) = .{},
active_requests: usize = 0,
ca_bundle: std.crypto.Certificate.Bundle = .{},
+/// TODO: emit error.UnexpectedEndOfStream or something like that when the read
+/// data does not match the content length. This is necessary since HTTPS disables
+/// close_notify protection on underlying TLS streams.
pub const Request = struct {
client: *Client,
stream: net.Stream,
@@ -133,6 +140,9 @@ pub fn request(client: *Client, url: Url, options: Request.Options) !Request {
.http => {},
.https => {
req.tls_client = try std.crypto.tls.Client.init(req.stream, client.ca_bundle, url.host);
+ // This is appropriate for HTTPS because the HTTP headers contain
+ // the content length which is used to detect truncation attacks.
+ req.tls_client.allow_truncation_attacks = true;
},
}