Saturday, August 23, 2008

Diseccionando un envío de Correo en Gmail

El envío del correo electrónico se lleva a cabo por medio del protocolo SMTP, sin embargo debido a cuestiones de seguridad, políticas, etc. se ha dividido el envío del correo electrónico, de la transmisión. Entiéndase como envío la comunicación entre el MUA -> MTA y como transmisión la comunicación entre MTA -> MTA.

De manera, que para cada uno de estos tipos de comunicación tenemos lo siguiente:

* Para la transmisión del correo se ocupa SMTP por el puerto 25 [RFC 2821].
* Para el envío del correo se ocupa SMTP por el puerto 587 [RFC 4409].

Para hacer la disección del envío de correo ocuparemos Gmail, por lo que la dirección del servidor SMTP que ocuparemos es smtp.gmail.com. (De aquí en adelante S-Servidor C-Cliente).

$ telnet smtp.gmail.com 587
S: 220 mx.google.com ESMTP m29sm12092147poh.3

Si vemos los servicios (extendidos) que nos ofrece este servidor obtenemos:
C: ehlo
S: 250-mx.google.com at your service, [w.x.y.z]
S: 250-SIZE 28311552
S: 250-8BITMIME
S: 250-STARTTLS
S: 250 ENHANCEDSTATUSCODES

Lo que nos importa es la línea que dice "250-STARTTLS", ya que en este punto si introducimos cualquier comando de SMTP obtendremos.

S: 530 5.7.0 Must issue a STARTTLS command first m29sm12092147poh.3

Es decir, tenemos que iniciar una comunicación "segura", ocupando SMTP sobre TLS [RFC 2487], que tiene que ser algo como lo siguiente:

S: <waits for connection on SMTP port>
C: <opens connection>
S: 220 mail.imc.org SMTP service ready
C: EHLO mail.ietf.org
S: 250-mail.imc.org offers a warm hug of welcome
S: 250 STARTTLS
C: STARTTLS
S: 220 Go ahead
C: <starts TLS negotiation>
C & S: <negotiate a TLS session>
C & S: <check result of negotiation>
C: <continues by sending an SMTP command>
. . .
(El estándar específica que el primer comando que DEBERÍA enviar el cliente después de haber establecido la negociación de TLS, es un EHLO)

Para la comunicación con el servidor ocuparemos Ruby.

=begin
Al parecer estas líneas no son necesarias en Windows
require 'socket'
require 'openssl' -> Si ocupas Lnx, tienes que tener instalado libssl-ruby
=end

require 'base64'

smtpSocket = TCPSocket.new("smtp.gmail.com",587)
S: 220 mx.google.com ESMTP z15sm18510036pod.11\r\n

smtpSocket.puts "ehlo"
S: 250-mx.google.com at your service, [w.x.y.z]
S: 250-SIZE 28311552
S: 250-8BITMIME
S: 250-STARTTLS
S: 250 ENHANCEDSTATUSCODES

smtpSocket.puts "starttls"
S: 220 2.0.0 Ready to start TLS\r\n

ssl=OpenSSL::SSL::SSLSocket.new(smtpSocket)
ssl.connect
ssl.puts "ehlo"
S: 250-mx.google.com at your service, [w.x.y.z]\r\n
S: 250-SIZE 28311552\r\n
S: 250-8BITMIME\r\n
S: 250-AUTH LOGIN PLAIN\r\n
S: 250 ENHANCEDSTATUSCODES\r\n

Y es en este punto donde encontramos algo interesante: "250-AUTH LOGIN PLAIN", esta línea nos dice la forma en la que podemos autenticarnos; toda esta autenticación se lleva en base a SASL (Simple Authentication and Security Layer) [RFC 4422]. ¿En algún lugar has escuchado SASL?, efectivamente es un framework para autenticación que es usado por varios protocolos y entre esos protocolos se encuentra LDAP y si alguna vez has ocupado Active Directory sabras que la autenticación (por default) se lleva a cabo ocupando Kerberos [RFC 4752]. En nuestro caso ocuparemos PLAIN [RFC 4616], que lo que nos dice es que tenemos que enviar un mensaje de la siguiente forma:

[authorization_identity] UTF8NUL authentication_identity UTF8NUL passwd

Como se especifica en el estándar no enviaremos el campo authorization_identity ("el cliente no provee una identidad de autorización [authorization identity] cuando quiere que el servidor derive una identidad de las credenciales proporcionadas"). De forma que nuestro mensaje queda:

\x00user\x00passwd

Pero antes, de poder enviarlo es necesario saber que tiene ir codificado en Base64.

#Continuando con el código de Ruby
autenticacion = "\x00" + "user" + "\x00" + "passwd"
autenticacion = Base64.encode64(autenticacion)
ssl.puts "auth plain" + autenticacion
S: 235 Accept

# Y ya que estamos adentro, ¿por qué no enviarnos un correo?
ssl.puts "mail from:<user@gmail.com>"
ssl.puts "rcpt to:<user@gmail.com>"
ssl.puts "data"
ssl.puts "From: <user@gmail.com>
To:<user@gmail.com>
Subject: Hola Mundo
Hola Mundo desde Ruby!"
ssl.puts "\r\n.\r\n"
ssl.puts "quit"

Referencias

[RFC 2821] Simple Mail Transfer Protocol, Abril 2001
[RFC 4409] Message Submission for Mail, Abril 2006
[RFC 2487] Secure SMTP over TLS, Enero 1999
[RFC 4422] Simple Authentication and Security Layer, Junio 2006
[RFC 4752] The Kerberos V5 (GSSAPI), SASL Mechanism, Noviembre 2006
[RFC 4616] The PLAIN SASL Mechanism, Agosto 2006

No comments: