Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Negotiating TLS with ESMTP over sockets

I have a simple SMTP client, to which I am attempting to add TLS support. I am unsure as to what occurs after the client issues the 'STARTTLS' command. Most sources (including the RFC itself) describe it as the negotiation of a TLS session, but this is not particularly clear.

How does one go about doing this? My client is written in Objective C and uses Cocoa's stream objects (a wrapper for sockets). Cocoa streams have the ability to designate TLS as the the socket security level system with NSStream's setProperty function.

It seems, however, that this must be done before the connection is opened. If this is the case, then is the client expected to disconnect after receiving code 220 from the server (in response to STARTTLS) and then reconnect while specifying TLS?

Or rather, is this just a limitation of NSStream? Do plain sockets re-negotiate TLS or SSL without being closed?

Furthermore, once STARTTLS has been issued and the subsequent negotiating completed, is any other encoding/decoding expected on the part of the client?

Apologies if these are simple questions. I've had difficulty finding proper examples.

Cheers!

like image 584
gxcode Avatar asked Feb 27 '23 15:02

gxcode


1 Answers

I just found out that NSStream allows you to upgrade an already active non-TLS connection to TLS by setting the kCFStreamSSLLevel property and reopening the streams. I have just tested this on an SMTP connection to smtp.gmail.com:25, and surprisingly it works! I am describing the NSStream/SMTP sequence in case anyone is interested:

NSHost* host = [NSHost hostWithName:address];
[NSStream getStreamsToHost:host port:port inputStream:&is outputStream:&os];
[is setDelegate:self];
[os setDelegate:self];
[is scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[os scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[is open];
[os open];

SMTP dialogue until we send STARTTLS and server says OK:

-> 220 mx.google.com ESMTP gb6sm4472052wbb.0
<- EHLO example.address.com
-> 250-mx.google.com at your service, [84.227.165.204]
-> 250-SIZE 35882577
-> 250-8BITMIME
-> 250-STARTTLS
-> 250 ENHANCEDSTATUSCODES
<- STARTTLS
-> 220 2.0.0 Ready to start TLS

At this point we execute the following to upgrade the socket to SSL:

NSMutableDictionary* settings = [NSMutableDictionary dictionary];
[settings setObject:NSStreamSocketSecurityLevelNegotiatedSSL forKey:(NSString*)kCFStreamSSLLevel];
[settings setObject:address forKey:(NSString*)kCFStreamSSLPeerName];
[is setProperty:settings forKey:(NSString*)kCFStreamPropertySSLSettings];
[os setProperty:settings forKey:(NSString*)kCFStreamPropertySSLSettings];
[is open];
[os open];

We can now use is and os just like before. The AUTH options now available proves that the server considers the connection as secure.

<- EHLO example.address.com
-> 250-mx.google.com at your service, [84.227.165.204]
-> 250-SIZE 35882577
-> 250-8BITMIME
-> 250-AUTH LOGIN PLAIN XOAUTH
-> 250 ENHANCEDSTATUSCODES
<- AUTH PLAIN hIdDeNbAsE64dAtA
-> 235 2.7.0 Accepted
<- MAIL FROM: <[email protected]>
-> 250 2.1.0 OK gb6sm4472052wbb.0
<- RCPT TO: <[email protected]>
-> 250 2.1.5 OK gb6sm4472052wbb.0
<- DATA
<- From: =?UTF-8?B?QWxlc3NhbmRybyBWb2x6?= <[email protected]>
<- To: =?UTF-8?B?QWxl?= <[email protected]>
<- Subject: =?UTF-8?B?VGVzdA==?=
<- Mime-Version: 1.0;
<- Content-Type: text/html; charset="UTF-8";
<- Content-Transfer-Encoding: 7bit;
<- 
<- Ciao!
<- .
-> 354  Go ahead gb6sm4472052wbb.0
-> 250 2.0.0 OK 1307994916 gb6sm4472052wbb.0
<- QUIT
-> 221 2.0.0 closing connection gb6sm4472052wbb.0

I hope this is useful to someone... Cheers,

like image 74
Alessandro Avatar answered Mar 03 '23 14:03

Alessandro