duesee / imap-next

Apache License 2.0
11 stars 3 forks source link

feat: Add `Client::new_without_greeting` #294

Open duesee opened 2 hours ago

duesee commented 2 hours ago

To allow a minimal (and secure) STARTTLS implementation, we need a way to start a session that skips the greeting. This allows us to handle the STARTTLS prefix in any way we please and continue with an isolated IMAP session after STARTTLS was already handled. This allows to fully isolate the plaintext and encrypted phase.

Implicit TLS

<--- TLS handshake --->
// Startpoint `Client::tls(...)`
S: * OK ... // Greeting
C: ...

STARTTLS

S: * OK ...
C: A STARTTLS
S: A OK ...
<--- TLS handshake --->
// Startpoint `Client::tls_no_greeting(...)`
// No greeting per spec.
C: ...

Do we want this to be a constructor or an Option? Any naming suggestions?

duesee commented 2 hours ago

The code to bring the stream up to the TLS transition could look like (just a quick draft for explanation)...

/// Bring a STARTTLS connection to the point where TLS is expected.
async fn do_starttls_prefix(stream: TcpStream) -> TcpStream {
    let reader = BufReader::new(stream);
    let mut lines = reader.lines();

    // Receive greeting.
    // Note: Greeting is *always* a single line.
    let _ = lines.next_line().await.unwrap();

    // Send STARTTLS command.
    lines.get_mut().write(b"A STARTTLS\r\n").await.unwrap();

    // Receive (and discard) all lines up until we get a
    // command completion result for our "A STARTTLS" command.
    loop {
        let line = lines.next_line().await.unwrap().unwrap();
        if line.starts_with("A ") {
            break;
        }
    }

    lines.into_inner().into_inner()
}