Example #1
0
        public void Authenticate(string userName,string password)
        {
            if(this.IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(!this.IsConnected){
                throw new InvalidOperationException("You must connect first.");
            }
            if(this.IsAuthenticated){
                throw new InvalidOperationException("Session is already authenticated.");
            }
            if(string.IsNullOrEmpty(userName)){
                throw new ArgumentNullException("userName");
            }
            if(password == null){
                password = "";
            }

            // Choose authentication method, we consider LOGIN as default.
            string authMethod = "LOGIN";
            List<string> authMethods = new List<string>(this.SaslAuthMethods);
            if(authMethods.Contains("DIGEST-MD5")){
                authMethod = "DIGEST-MD5";
            }
            else if(authMethods.Contains("CRAM-MD5")){
                authMethod = "CRAM-MD5";
            }

            #region AUTH LOGIN

            if(authMethod == "LOGIN"){
                /* LOGIN
                      Example:
                        C: AUTH LOGIN<CRLF>
                        S: 334 VXNlcm5hbWU6<CRLF>   VXNlcm5hbWU6 = base64("USERNAME")
                        C: base64(username)<CRLF>
                        S: 334 UGFzc3dvcmQ6<CRLF>   UGFzc3dvcmQ6 = base64("PASSWORD")
                        C: base64(password)<CRLF>
                        S: 235 Ok<CRLF>
                */

                WriteLine("AUTH LOGIN");

                // Read server response.
                string line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if(!line.StartsWith("334")){
                    throw new SMTP_ClientException(line);
                }

                // Send user name to server.
                WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(userName)));

                // Read server response.
                line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if(!line.StartsWith("334")){
                    throw new SMTP_ClientException(line);
                }

                // Send password to server.
                WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(password)));

                // Read server response.
                line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if(!line.StartsWith("235")){
                    throw new SMTP_ClientException(line);
                }

                m_pAuthdUserIdentity = new GenericIdentity(userName,"LOGIN");
            }

            #endregion

            #region AUTH CRAM-MD5

            else if(authMethod == "CRAM-MD5"){
                /* CRAM-M5
                    Description:
                        HMACMD5 key is "password".

                    Example:
                        C: AUTH CRAM-MD5<CRLF>
                        S: 334 base64(md5_calculation_hash)<CRLF>
                        C: base64(username password_hash)<CRLF>
                        S: 235 Ok<CRLF>
                */

                WriteLine("AUTH CRAM-MD5");

                // Read server response.
                string line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if(!line.StartsWith("334")){
                    throw new SMTP_ClientException(line);
                }

                HMACMD5 kMd5         = new HMACMD5(Encoding.ASCII.GetBytes(password));
                string  passwordHash = Net_Utils.ToHex(kMd5.ComputeHash(Convert.FromBase64String(line.Split(' ')[1]))).ToLower();

                // Send authentication info to server.
                WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(userName + " " + passwordHash)));

                // Read server response.
                line = ReadLine();
                // Response line must start with 235 or otherwise it's error response
                if(!line.StartsWith("235")){
                    throw new SMTP_ClientException(line);
                }

                m_pAuthdUserIdentity = new GenericIdentity(userName,"CRAM-MD5");
            }

            #endregion

            #region AUTH DIGEST-MD5

            else if(authMethod == "DIGEST-MD5"){
                /*
                    Example:
                        C: AUTH DIGEST-MD5<CRLF>
                        S: 334 base64(digestChallange)<CRLF>
                        C: base64(digestResponse)<CRLF>
                        S: 334 base64(serverDigestRpAuth)<CRLF>
                        C: <CRLF>
                        S: 235 Ok<CRLF>
                */

                WriteLine("AUTH DIGEST-MD5");

                // Read server response.
                string line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if(!line.StartsWith("334")){
                    throw new SMTP_ClientException(line);
                }

                // Parse server challenge.
                AUTH_SASL_DigestMD5_Challenge challenge = AUTH_SASL_DigestMD5_Challenge.Parse(Encoding.Default.GetString(Convert.FromBase64String(line.Split(' ')[1])));

                // Construct our response to server challenge.
                AUTH_SASL_DigestMD5_Response response = new AUTH_SASL_DigestMD5_Response(
                    challenge,
                    challenge.Realm[0],
                    userName,
                    password,Guid.NewGuid().ToString().Replace("-",""),
                    1,
                    challenge.QopOptions[0],
                    "smtp/" + this.RemoteEndPoint.Address.ToString()
                );

                // Send authentication info to server.
                WriteLine(Convert.ToBase64String(Encoding.Default.GetBytes(response.ToResponse())));

                // Read server response.
                line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if(!line.StartsWith("334")){
                    throw new SMTP_ClientException(line);
                }

                // Check rspauth value.
                if(!string.Equals(Encoding.Default.GetString(Convert.FromBase64String(line.Split(' ')[1])),response.ToRspauthResponse(userName,password),StringComparison.InvariantCultureIgnoreCase)){
                    throw new Exception("SMTP server 'rspauth' value mismatch.");
                }

                // Send empty line.
                WriteLine("");

                // Read server response.
                line = ReadLine();
                // Response line must start with 235 or otherwise it's error response.
                if(!line.StartsWith("235")){
                    throw new SMTP_ClientException(line);
                }

                m_pAuthdUserIdentity = new GenericIdentity(userName,"DIGEST-MD5");
            }

            #endregion
        }