How to use SMTP Auth in C?

I'm trying to do a simple connection to a SMTP server (that requires authentification). I'm connected to smtp.live.com on port 587 using SSL (the OpenSSL library in C).

I first initialize my socket with the socket() function, then connect to the server with connect(). Then I launch the SSL channel and then I try to login.

Here's the output on my shell :

server : 220 BLU0-SMTP261.phx.gbl Microsoft ESMTP MAIL Service, Version: 6.0.3790.4675     ready at  Sat, 9 Mar 2013 07:25:17 -0800 

EHLO [127.0.0.1]
server : 250-BLU0-SMTP261.phx.gbl Hello [163.5.221.45]
250-TURN
250-SIZE 41943040
250-ETRN
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-8bitmime
250-BINARYMIME
250-CHUNKING
250-VRFY
250-TLS
250-STARTTLS
250 OK

STARTTLS
server : 220 2.0.0 SMTP server ready

EHLO [127.0.0.1]
server : 250-BLU0-SMTP261.phx.gbl Hello [163.5.221.45]
250-TURN
250-SIZE 41943040
250-ETRN
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-8bitmime
250-BINARYMIME
250-CHUNKING
250-VRFY
250-AUTH LOGIN PLAIN
250 OK

AUTH LOGIN
server : 334 VXNlcm5hbWU6

Y2hlcmGhvdXXXXXXXXXXXuY29tXHJcbg0K
serveur : 
U2hsYWXXXXXXXXXXXwblxyXG4NCg0K
server : 

As you can see, the username and the password are both encode in base64 (without \r\n). I don't understand why the server doesn't answer anything... (I tried to put a sleep() before reading the socket , but i got the same result)

Code to connect to the server :

static int              _connect_IPV4(int socket, struct hostent *host)
{
  struct sockaddr_in    addr;

  addr.sin_port = htons(587);
  addr.sin_family = AF_INET;
  addr.sin_addr = *(struct in_addr *)host->h_addr;
  bzero(&addr.sin_zero, 8);
  if (connect(socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == -1)
    printf("%s\n", strerror(errno));
  else
    return (socket);
}

Code to start SSL:

static void     _launch_ssl(int socket)
{
  read_socket(socket);
  write_socket(socket, "EHLO [127.0.0.1]\r\n");
  read_socket(socket);
  write_socket(socket, "STARTTLS\r\n");
  read_socket(socket);
}

Code to connect SSL:

connection      *ssl_connect()
{
  connection    *c;

  c = malloc(sizeof(connection));
  c->ssl_handle = NULL;
  c->ssl_context = NULL;
  c->socket = connect_TCP_IP();
  _launch_ssl(c->socket);
  if (c->socket != -1)
    {
      SSL_load_error_strings();
      SSL_library_init();

      SSL_CTX_new(SSLv23_client_method());
      SSL_new(c->ssl_context);
      SSL_set_fd(c->ssl_handle, c->socket);
      SSL_connect(c->ssl_handle);
    }
  return (c);
}

Main :

int             main()
{
  connection    *c;

  c = ssl_connect();
  ssl_write_socket(c, "EHLO [127.0.0.1]\r\n");
  ssl_read_socket(c);
  ssl_write_socket(c, "AUTH LOGIN\r\n");
  ssl_read_socket(c);
  ssl_write_socket(c, "Y2hlcmNoZXN0b3lhQGhvdG1haWwuY29tXHJcbg0K");
  printf("\n");
  ssl_read_socket(c);
  ssl_write_socket(c, "U2hsYWdldHRvUHIwblxyXG4NCg0K");
  printf("\n");
  ssl_read_socket(c);
}

Upvotes: 2

Views: 2261

Answers (1)

Celada
Celada

Reputation: 22261

First of all, I hope you intend to improve your client so that it actually reacts to what the server sends. For example, it looks like in your _launch_ssl function you do not actually check the server's reply to EHLO to make sure STARTTLS is offered as an option before you attempt to use it. The pseudocode of that function should be:

  1. send EHLO (preferably not with something meaningless like [127.0.0.1]!)
  2. check the server's reply
  3. if STARTTLS is not among the options offered, bail
  4. if STARTTLS is among the options offered, send STARTTLS and continue.

Similarily, in main(), you need to check after EHLO that the server has actually responded 250, and check after AUTH that the server has actually responded 334.

Otherwise, the problem seems to be that you don't send \r\n after your responses. In other words:

ssl_write_socket(c, "Y2hlcmNoZXN0b3lhQGhvdG1haWwuY29tXHJcbg0K");

should be:

ssl_write_socket(c, "Y2hlcmNoZXN0b3lhQGhvdG1haWwuY29tXHJcbg0K\r\n");

Furthermore, your base64 string contains an \\r\\n\r\n (backslash + 'r' + backslash + 'n' + CR + LF) embedded inside the string as though the \\r\\n\r\n were part of the username. Assuming that's wrong, then, in fact, your code should probably actually read:

ssl_write_socket(c, "Y2hlcmNoZXN0b3lhQGhvdG1haWwuY29t\r\n");

Upvotes: 1

Related Questions