ietf-smtp
[Top] [All Lists]

Re: LOGIN mechanism

2011-05-11 21:55:45

Peter Bowen wrote:
I realize that SASL mechanisms are not directly in the domain of this
group, but I'm hoping I might get pointed the right way.

I've been working on implementing a framework for building SMTP
servers in ruby (https://github.com/pzb/groat-smtpd).  One of the
issues I've run into several times is interoperability of the LOGIN
mechanism with various clients.  The latest problem is certain
Microsoft SMTP implementations that send an initial client response,
and have major issues when it is not accepted.  Is there any reliable
specification for the LOGIN mechanism and/or how it is used with SMTP?

Thanks,
Peter

Hi Peter,

Is there an specific example of the problem to get an idea of what you seeing?

The specification for SASL is pretty straight forward. The documents to read should be:

   RFC4422 Simple Authentication and Security Layer (SASL)
   RFC2554 SMTP Service Extension for Authentication
   RFC2487 SMTP Service Extension for Secure SMTP over TLS
   RFC4409 Message Submission for Mail

But there are a few something you need to know that you may come across outside the docs or not obvious.

One specifically for LOGIN method is to display it twice like so:

  C: EHLO client.domain.com
  S: 250-server.com, whats up!
  S: 250-SIZE 10240000
  S: 250-AUTH CRAM-MD5 LOGIN DIGEST-MD5
  S: 250-AUTH=LOGIN
  S: 250 HELP

Some older clients look for that AUTH=LOGIN.

Also, LOGIN can be done in two forms

    AUTH LOGIN [base64_username]
    AUTH LOGIN

So your server needs to be ready for optional data.

Also, for the handshaking exchange, due to various client behavior (mostly cell I found), some pump multiple lines and if the server need to properly parse it on a per line basis (i.e. the next line reading comes off an input cache)

Finally, if you are just looking for LOGIN, which should be the minimum method, its pretty simple. Here is some (older version( C/C++ code for the AUTH command for the LOGIN method only. I pulled out the newer multi SASL methods version that interacts with a backend server in the handshake.

BOOL TSMTPServer::AUTH(char *args)
{

    if (!IsServiceAvailable()) return TRUE;

    if (!CheckTLS())return TRUE;

    if (!IsExtendedSmtp) {
       Send("500 AUTH not enabled at server!\r\n");
       return TRUE;
    }

    if (authattempts > 3) {
       Send("503 Too many AUTH attempts!\r\n");
       return TRUE;
    }

    if (IsUserAuthorized) {
       Send("503 Invalid AUTH request. Already Authenticated\r\n");
       return TRUE;
    }

    // Support for NON-RFC AUTH=LOGIN
    // - Designed by NETSCAPE, SUPPORTED by MICROSOFT
    // - supported by at least NS4, NS4.5, OL4, OL5
    // - optional: AUTH LOGIN [base64_username]
    // - optional: AUTH LOGIN

    char buf[1024];
    ZeroMemory(&username,sizeof(username));
    ZeroMemory(&password,sizeof(password));

    if (!strnicmp(args, "LOGIN", 5)) {

        int received = 0;
        int sockerr  = 0;

        ZeroMemory(&buf,sizeof(buf));

        //----------------------------------------------------------
        // Get USERNAME
        //----------------------------------------------------------

        // Some clients will provide the BASE64 username as part
        // of the LOGIN argument. Otherwise, ask for the username.

        if (strlen(args) > 6) {
            strcpy(buf, args+6);
            UnBase64(username, buf);
        }
        else {

            Send("334 VXNlcm5hbWU6====\r\n");

            ZeroMemory(&buf,sizeof(buf));
            received = Control->GetBlock(buf,sizeof(buf)-1);
            sockerr  = GetLastError();
            if ((received <= 0) && (sockerr != 0)) {
Send("454 Failure to receive AUTH USERNAME. Error %d\r\n",sockerr);
              return TRUE;
            }

            if (received <= 0) {
               Send("535 Authorization Failed, No Username!\r\n");
               authattempts++;
               return TRUE;
            }

            UnBase64(username, buf);

            // 450.3b7 - just in case it was encoded
            if (!strcmp(buf,"*") || !strcmp(username,"*")) {
               Send("501 Authorization cancellation accepted\r\n");
               authattempts++;
               return TRUE;
            }
            //

        } // user name

        //----------------------------------------------------------
        // Get PASSWORD
        //----------------------------------------------------------

        Send("334 UGFzc3dvcmQ6====\r\n");

        ZeroMemory(&buf,sizeof(buf));
        received = Control->GetBlock(buf,sizeof(buf)-1);
        sockerr  = GetLastError();
        if ((received <= 0) && (sockerr != 0)) {
Send("454 Failure to receive AUTH PASSWORD. Error %d\r\n",sockerr);
          return TRUE;
        }

        if (received <= 0) {
           Send("535 Authorization Failed, No Password!\r\n");
           authattempts++;
           return TRUE;
        }

        TrimCrLf(buf);
        UnBase64(password, buf);

        if (!strcmp(buf,"*") || !strcmp(password,"*")) {
            Send("501 Authorization cancellation accepted\r\n");
            authattempts++;
            return TRUE;
        }

        IsUserAuthorized = YourServerSideLogin(username,password);

        if (!IsUserAuthorized)
        {
           int err = GetLastError();
           switch (err) {
             case WC_USER_NOT_FOUND:
Send("535 Authentication Failure: User \"%s\" Unknown!\r\n",username);
                break;
             case WC_INCORRECT_PASSWORD:
Send("535 Authentication Failure: Invalid Password!\r\n");
                break;
             case WC_ACCESS_DENIED:
Send("535 Authentication Failure: Email Access Denied!\r\n");
                break;
             default:
                Send("535 System Authentication Failure %8X!\r\n",err);
                break;
           }
           authattempts++;
           InterlockedIncrement(&smtpstats.ConnectRejects);
           return TRUE;
        }
       Send("235 Authentication successful\r\n");
       return TRUE;
    }

    Send("504 Unrecognized authentication type: %s.\r\n",args);
    authattempts++;
    return TRUE;
}

Hope this helps

--
Sincerely

Hector Santos
http://www.santronics.com

<Prev in Thread] Current Thread [Next in Thread>