2015-10-06 3 views
2

Я пытаюсь реализовать простой SMTP-сервер, используя Vala и GLib + GIO.Как я могу реализовать SMTP STARTTLS на стороне сервера?

Простая текстовая связь до сих пор не проблема, но когда дело доходит до TLS с использованием STARTTLS, все становится сложнее.

Это код, который я до сих пор:

const string appname = "vsmtpd"; 
const string hostname = "myserver"; 
const uint16 listenport = 10025; 
const string keyfile = "vsmtpd.key"; 
const string certfile = "vsmtpd.crt"; 
// TODO: Parse EHLO instead of constant string 
const string username = "myclient"; 

void process_request_plain (InputStream input, OutputStream output) throws Error { 
    output.write (@"220 $hostname ESMTP $appname\n".data); 
    var data_in = new DataInputStream (input); 
    string line; 
    while ((line = data_in.read_line (null)) != null) { 
     stdout.printf ("%s\n", line); 
     line = line.chomp(); 
     if (line.substring (0, 5) == "EHLO ") { 
      output.write (@"250-$hostname Hello $username\n".data); 
      output.write ("250 STARTTLS\n".data); 
     } 
     else if (line == "STARTTLS") { 
      output.write ("220 Go ahead\n".data); 
      break; 
     } 
     else { 
      output.write ("502 Command not implemented\n".data); 
     } 
    } 
} 

int main() { 
    try { 
     TlsCertificate cert = new TlsCertificate.from_files 
      (certfile, keyfile); 
     var service = new SocketService(); 
     service.add_inet_port (listenport, null); 
     service.start(); 
     while (true) { 
      SocketConnection conn = service.accept (null); 
      process_request_plain (conn.input_stream, conn.output_stream); 
      TlsServerConnection tlsconn = [email protected] (conn, cert); 
      assert_nonnull (tlsconn); 
      // TODO: Is this neccessary? 
      tlsconn.accept_certificate.connect ((peer_cert, errors) => { 
       stdout.printf ("TLS accepting peer cert\n"); 
       return true; 
      }); 
      try { 
       tlsconn.handshake(); 
       stdout.printf ("TLS handshake ok\n"); 
      } catch (Error e) { 
       stdout.printf ("TLS handshake failed\n"); 
       stderr.printf ("%s\n", e.message); 
      } 
     } 
    } catch (Error e) { 
     stderr.printf ("%s\n", e.message); 
    } 
    return 0; 
} 

Учитывая действующий сертификат SSL в vsmtpd.key и vsmtpd.crt (который я генерируемой с openssl req -x509 -newkey rsa:2048 -keyout vsmtpd.key -out vsmtpd.pem -days 365 -nodes) я запустить программу и я также запустить эту команду OpenSSL, чтобы проверить STARTTLS :

openssl s_client -connect localhost:10025 -starttls smtp -debug 

выход из моей программы:

EHLO openssl.client.net 
STARTTLS 
TLS handshake failed 
Stream is already closed 

Выход из OpenSSL является:

CONNECTED(00000003) 
read from 0x6ae470 [0x6af050] (4096 bytes => 26 (0x1A)) 
0000 - 32 32 30 20 6d 79 73 65-72 76 65 72 20 45 53 4d 220 myserver ESM 
0010 - 54 50 20 76 73 6d 74 70-64 0a      TP vsmtpd. 
write to 0x6ae470 [0x6b0060] (25 bytes => 25 (0x19)) 
0000 - 45 48 4c 4f 20 6f 70 65-6e 73 73 6c 2e 63 6c 69 EHLO openssl.cli 
0010 - 65 6e 74 2e 6e 65 74 0d-0a      ent.net.. 
read from 0x6ae470 [0x6af050] (4096 bytes => 28 (0x1C)) 
0000 - 32 35 30 2d 6d 79 73 65-72 76 65 72 20 48 65 6c 250-myserver Hel 
0010 - 6c 6f 20 6d 79 63 6c 69-65 6e 74 0a    lo myclient. 
read from 0x6ae470 [0x6af050] (4096 bytes => 13 (0xD)) 
0000 - 32 35 30 20 53 54 41 52-54 54 4c 53 0a   250 STARTTLS. 
write to 0x6ae470 [0x7ffdb4aea9e0] (10 bytes => 10 (0xA)) 
0000 - 53 54 41 52 54 54 4c 53-0d 0a      STARTTLS.. 
read from 0x6ae470 [0x6a13a0] (8192 bytes => 13 (0xD)) 
0000 - 32 32 30 20 47 6f 20 61-68 65 61 64 0a   220 Go ahead. 
write to 0x6ae470 [0x6aefa0] (204 bytes => 204 (0xCC)) 
0000 - 16 03 01 00 c7 01 00 00-c3 03 03 0e ac 05 35 45 ..............5E 
0010 - db 95 f6 a7 37 55 d8 ca-14 d7 5f 8e 6a 62 08 50 ....7U...._.jb.P 
0020 - c9 81 b7 55 75 a8 4c 17-c0 a1 53 00 00 76 00 a5 ...Uu.L...S..v.. 
0030 - 00 a3 00 a1 00 9f 00 6b-00 6a 00 69 00 68 00 39 .......k.j.i.h.9 
0040 - 00 38 00 37 00 36 00 88-00 87 00 86 00 85 00 9d .8.7.6.......... 
0050 - 00 3d 00 35 00 84 00 a4-00 a2 00 a0 00 9e 00 67 .=.5...........g 
0060 - 00 40 00 3f 00 3e 00 33-00 32 00 31 00 30 00 9a [email protected]?.>.3.2.1.0.. 
0070 - 00 99 00 98 00 97 00 45-00 44 00 43 00 42 00 9c .......E.D.C.B.. 
0080 - 00 3c 00 2f 00 96 00 41-00 07 00 05 00 04 00 16 .<./...A........ 
0090 - 00 13 00 10 00 0d 00 0a-00 15 00 12 00 0f 00 0c ................ 
00a0 - 00 09 00 ff 02 01 00 00-23 00 23 00 00 00 0d 00 ........#.#..... 
00b0 - 16 00 14 06 01 06 02 05-01 05 02 04 01 04 02 03 ................ 
00c0 - 01 03 02 02 01 02 02 00-0f 00 01 01    ............ 
read from 0x6ae470 [0x6b4500] (7 bytes => -1 (0xFFFFFFFFFFFFFFFF)) 
write:errno=104 
--- 
no peer certificate available 
--- 
No client certificate CA names sent 
--- 
SSL handshake has read 80 bytes and written 239 bytes 
--- 
New, (NONE), Cipher is (NONE) 
Secure Renegotiation IS NOT supported 
Compression: NONE 
Expansion: NONE 
No ALPN negotiated 
--- 

То, что я понимаю, с выхода моей программы закрывает соединение до рукопожатия TLS может завершить. (Я также пытался использовать Thunderbird и Claws Mail)

Что я здесь делаю неправильно?

PS: Я не мог найти никакого примера о том, как использовать GTLsServerConnection в ситуации STARTTLS.

Update:

Я попытался -ssl2, -ssl3, -tls1, -tls1_1, -tls1_2 варианты OpenSSL, которые также не работают.

openssl s_client -connect localhost:10025 -starttls smtp -state 

выходы:

CONNECTED(00000003) 
SSL_connect:before/connect initialization 
SSL_connect:SSLv2/v3 write client hello A 
SSL_connect:error in SSLv2/v3 read server hello A 
write:errno=104 
--- 
no peer certificate available 
--- 
No client certificate CA names sent 
--- 
SSL handshake has read 100 bytes and written 239 bytes 
--- 
New, (NONE), Cipher is (NONE) 
Secure Renegotiation IS NOT supported 
Compression: NONE 
Expansion: NONE 
No ALPN negotiated 
--- 

Таким образом, клиент посылает «клиент привет А», но сервер не посылает правильный «сервер привет А».

В качестве альтернативы вы также можете попробовать gnutls-cli --crlf --starttls-proto=smtp --port 10025 localhost.

Выход из GNUTLS_DEBUG_LEVEL=11 ./vsmtpd является:

gnutls[2]: Enabled GnuTLS logging... 
gnutls[2]: Intel SSSE3 was detected 
gnutls[2]: Intel AES accelerator was detected 
gnutls[2]: Intel GCM accelerator was detected 
gnutls[2]: Enabled GnuTLS logging... 
gnutls[2]: Intel SSSE3 was detected 
gnutls[2]: Intel AES accelerator was detected 
gnutls[2]: Intel GCM accelerator was detected 
gnutls[3]: ASSERT: x509_b64.c:299 
gnutls[9]: Could not find '-----BEGIN RSA PRIVATE KEY' 
gnutls[3]: ASSERT: x509_b64.c:299 
gnutls[9]: Could not find '-----BEGIN DSA PRIVATE KEY' 
gnutls[3]: ASSERT: x509_b64.c:299 
gnutls[9]: Could not find '-----BEGIN EC PRIVATE KEY' 
gnutls[3]: ASSERT: privkey.c:503 
gnutls[2]: Falling back to PKCS #8 key decoding 
EHLO openssl.client.net 
STARTTLS 
gnutls[5]: REC[0xfa67e0]: Allocating epoch #0 
gnutls[3]: ASSERT: gnutls_constate.c:586 
gnutls[5]: REC[0xfa67e0]: Allocating epoch #1 
gnutls[3]: ASSERT: gnutls_buffers.c:1138 
gnutls[10]: READ: -1 returned from 0xfa4120, errno=0 gerrno=5 
gnutls[3]: ASSERT: gnutls_buffers.c:364 
gnutls[3]: ASSERT: gnutls_buffers.c:572 
gnutls[3]: ASSERT: gnutls_record.c:1058 
gnutls[3]: ASSERT: gnutls_record.c:1179 
gnutls[3]: ASSERT: gnutls_buffers.c:1392 
gnutls[3]: ASSERT: gnutls_handshake.c:1428 
gnutls[3]: ASSERT: gnutls_handshake.c:3098 
gnutls[3]: ASSERT: gnutls_db.c:334 
TLS handshake failed 
Stream is already closed 
gnutls[5]: REC[0xfa67e0]: Start of epoch cleanup 
gnutls[5]: REC[0xfa67e0]: End of epoch cleanup 
gnutls[5]: REC[0xfa67e0]: Epoch #0 freed 
gnutls[5]: REC[0xfa67e0]: Epoch #1 freed 
+1

Это послужит хорошим примером, если вы можете заставить его работать. Вы по крайней мере получаете ответ - (7 bytes => -1 (0xFFFFFFFFFFFFFFFF)). Вы можете попробовать несколько вариантов вариантов openssl, как это предлагается в http://stackoverflow.com/questions/24457408/openssl-command-to-check-if-a-server-is-presenting-a-certificate – AlThomas

+0

Я не конечно, есть проблема с кодом Vala. https://developer.gnome.org/gio/stable/GTlsConnection.html#g-tls-connection-handshake говорит, что рукопожатие можно назвать «если вы хотите точно знать, было ли начальное рукопожатие успешным или неудачным». Таким образом, «Поток уже закрыт», как и ожидалось после первоначального сбоя квитирования. Проблема может быть вашим сертификатом.Я отмечаю, что вы используете «myserver», но подключаетесь к «localhost». Проверить имя сервера? – AlThomas

+1

Также я считаю, что GnuTLS - это бэкэнд, поэтому попробуйте установить GNUTLS_DEBUG_LEVEL - см. Http://gnutls.org/manual/html_node/Debugging-and-auditing.html Вы также можете попробовать ssldump для прослушивания по подключению, например. https://www.sslshopper.com/article-debugging-ssl-communications.html – AlThomas

ответ

1

Проблема скрывается где-то в реализации DataInputStream.

Как только я удалил его и вместо этого использовал следующую замену для read_line(), он работает нормально.

string? read_line (InputStream input) throws Error { 
    var buffer = new uint8[1]; 
    var sb = new StringBuilder(); 
    buffer[0] = '\0'; 
    while (buffer[0] != '\n') { 
     input.read (buffer); 
     sb.append_c ((char) buffer[0]); 
    } 
    return (string) sb.data; 
} 

void process_request_plain (InputStream input, OutputStream output) throws Error { 
    output.write (@"220 $hostname ESMTP $appname\n".data); 
    string line; 
    while ((line = read_line (input)) != null) { 
     stdout.printf ("%s\n", line); 
     line = line.chomp(); 
     if (line.substring (0, 5) == "EHLO ") { 
      output.write (@"250-$hostname Hello $username\n".data); 
      output.write ("250 STARTTLS\n".data); 
     } 
     else if (line == "STARTTLS") { 
      output.write ("220 Go ahead\n".data); 
      break; 
     } 
     else { 
      output.write ("502 Command not implemented\n".data); 
     } 
    } 
} 
Смежные вопросы