public void TestArgumentExceptions() { var credentials = new NetworkCredential("username", "password"); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, Encoding.UTF8, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", Encoding.UTF8, null)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.IsSupported(null)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.SaslPrep(null)); }
public void TestArgumentExceptions() { var credentials = new NetworkCredential("username", "password"); var uri = new Uri("smtp://localhost"); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, uri, Encoding.UTF8, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null, Encoding.UTF8, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, null, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, Encoding.UTF8, null)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, uri, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, null)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.SaslPrep(null)); }
public void TestIsSupported() { var supported = new [] { "PLAIN", "LOGIN", "CRAM-MD5", "DIGEST-MD5", "SCRAM-SHA-1", "SCRAM-SHA-256", "NTLM", "XOAUTH2" }; var unsupported = new [] { "ANONYMOUS", "GSSAPI", "KERBEROS_V4" }; var credentials = new NetworkCredential("username", "password"); var uri = new Uri("smtp://localhost"); foreach (var mechanism in supported) { Assert.IsTrue(SaslMechanism.IsSupported(mechanism), mechanism); var sasl = SaslMechanism.Create(mechanism, uri, credentials); Assert.IsNotNull(sasl, mechanism); Assert.AreEqual(mechanism, sasl.MechanismName, "MechanismName"); } foreach (var mechanism in unsupported) { Assert.IsFalse(SaslMechanism.IsSupported(mechanism), mechanism); } }
public void TestIsSupported() { var supported = new [] { "PLAIN", "LOGIN", "CRAM-MD5", "DIGEST-MD5", "SCRAM-SHA-1", "SCRAM-SHA-1-PLUS", "SCRAM-SHA-256", "SCRAM-SHA-256-PLUS", "SCRAM-SHA-512", "SCRAM-SHA-512-PLUS", "NTLM", "OAUTHBEARER", "XOAUTH2", "ANONYMOUS" }; var unsupported = new [] { "EXTERNAL", "GSSAPI", "KERBEROS_V4" }; var credentials = new NetworkCredential("username", "password"); foreach (var mechanism in supported) { Assert.IsTrue(SaslMechanism.IsSupported(mechanism), mechanism); var sasl = SaslMechanism.Create(mechanism, credentials); Assert.IsNotNull(sasl, mechanism); Assert.AreEqual(mechanism, sasl.MechanismName, "MechanismName"); sasl.Reset(); } foreach (var mechanism in unsupported) { Assert.IsFalse(SaslMechanism.IsSupported(mechanism), mechanism); } }
/// <summary> /// Authenticates using the supplied credentials. /// </summary> /// <remarks> /// <para>If the server supports one or more SASL authentication mechanisms, then /// the SASL mechanisms that both the client and server support are tried /// in order of greatest security to weakest security. Once a SASL /// authentication mechanism is found that both client and server support, /// the credentials are used to authenticate.</para> /// <para>If the server does not support SASL or if no common SASL mechanisms /// can be found, then LOGIN command is used as a fallback.</para> /// </remarks> /// <param name="credentials">The user's credentials.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="credentials"/> is <c>null</c>. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="ImapClient"/> is not connected or is already authenticated. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.Security.Authentication.AuthenticationException"> /// Authentication using the supplied credentials has failed. /// </exception> /// <exception cref="MailKit.Security.SaslException"> /// A SASL authentication error occurred. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// An IMAP protocol error occurred. /// </exception> public void Authenticate(ICredentials credentials, CancellationToken cancellationToken) { CheckDisposed(); if (!IsConnected) { throw new InvalidOperationException("The ImapClient must be connected before you can authenticate."); } if (engine.State >= ImapEngineState.Authenticated) { throw new InvalidOperationException("The ImapClient is already authenticated."); } if (credentials == null) { throw new ArgumentNullException("credentials"); } int capabilitiesVersion = engine.CapabilitiesVersion; var uri = new Uri("imap://" + host); NetworkCredential cred; ImapCommand ic; if ((engine.Capabilities & ImapCapabilities.StartTLS) != 0) { ic = engine.QueueCommand(cancellationToken, null, "STARTTLS\r\n"); engine.Wait(ic); if (ic.Result == ImapCommandResult.Ok) { var tls = new SslStream(engine.Stream, false, ValidateRemoteCertificate); tls.AuthenticateAsClient(host, ClientCertificates, SslProtocols.Tls, true); engine.Stream.Stream = tls; // Query the CAPABILITIES again if the server did not include an // untagged CAPABILITIES response to the STARTTLS command. if (engine.CapabilitiesVersion == 1) { engine.QueryCapabilities(cancellationToken); } } } else if ((engine.Capabilities & ImapCapabilities.Compress) != 0) { ic = engine.QueueCommand(cancellationToken, null, "COMPRESS DEFLATE\r\n"); engine.Wait(ic); if (ic.Result == ImapCommandResult.Ok) { var unzip = new DeflateStream(engine.Stream, CompressionMode.Decompress); var zip = new DeflateStream(engine.Stream, CompressionMode.Compress); engine.Stream.Stream = new DuplexStream(unzip, zip); // Query the CAPABILITIES again if the server did not include an // untagged CAPABILITIES response to the COMPRESS command. if (engine.CapabilitiesVersion == 1) { engine.QueryCapabilities(cancellationToken); } } } foreach (var authmech in SaslMechanism.AuthMechanismRank) { if (!engine.AuthenticationMechanisms.Contains(authmech)) { continue; } var sasl = SaslMechanism.Create(authmech, uri, credentials); cancellationToken.ThrowIfCancellationRequested(); var command = string.Format("AUTHENTICATE {0}", sasl.MechanismName); var ir = sasl.Challenge(null); if ((engine.Capabilities & ImapCapabilities.SaslIR) != 0 && ir != null) { command += " " + ir + "\r\n"; } else { command += "\r\n"; sasl.Reset(); } ic = engine.QueueCommand(cancellationToken, null, command); ic.ContinuationHandler = (imap, cmd, text) => { string challenge; if (sasl.IsAuthenticated) { // the server claims we aren't done authenticating, but our SASL mechanism thinks we are... // FIXME: will sending an empty string abort the AUTHENTICATE command? challenge = string.Empty; } else { challenge = sasl.Challenge(text); } cmd.CancellationToken.ThrowIfCancellationRequested(); var buf = Encoding.ASCII.GetBytes(challenge + "\r\n"); imap.Stream.Write(buf, 0, buf.Length); imap.Stream.Flush(); }; engine.Wait(ic); if (ic.Result != ImapCommandResult.Ok) { continue; } engine.State = ImapEngineState.Authenticated; // Query the CAPABILITIES again if the server did not include an // untagged CAPABILITIES response to the AUTHENTICATE command. if (engine.CapabilitiesVersion == capabilitiesVersion) { engine.QueryCapabilities(cancellationToken); } engine.QueryNamespaces(cancellationToken); engine.QuerySpecialFolders(cancellationToken); return; } if ((Capabilities & ImapCapabilities.LoginDisabled) != 0) { throw new AuthenticationException(); } // fall back to the classic LOGIN command... cred = credentials.GetCredential(uri, "LOGIN"); ic = engine.QueueCommand(cancellationToken, null, "LOGIN %S %S\r\n", cred.UserName, cred.Password); engine.Wait(ic); if (ic.Result != ImapCommandResult.Ok) { throw new AuthenticationException(); } engine.State = ImapEngineState.Authenticated; // Query the CAPABILITIES again if the server did not include an // untagged CAPABILITIES response to the LOGIN command. if (engine.CapabilitiesVersion == capabilitiesVersion) { engine.QueryCapabilities(cancellationToken); } engine.QueryNamespaces(cancellationToken); engine.QuerySpecialFolders(cancellationToken); }
public void TestArgumentExceptions() { var credentials = new NetworkCredential("username", "password"); var uri = new Uri("smtp://localhost"); SaslMechanism sasl; Assert.Throws <ArgumentNullException> (() => new SaslException(null, SaslErrorCode.MissingChallenge, "message")); sasl = new SaslMechanismCramMd5(credentials); Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(uri, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(uri, null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(uri, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5("username", null)); Assert.Throws <NotSupportedException> (() => sasl.Challenge(null)); sasl = new SaslMechanismDigestMd5(credentials) { Uri = uri }; Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(uri, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(uri, (string)null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(uri, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5("username", null)); Assert.Throws <NotSupportedException> (() => sasl.Challenge(null)); sasl = new SaslMechanismLogin(credentials); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin((Uri)null, Encoding.UTF8, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, Encoding.UTF8, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin((Uri)null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin((Uri)null, Encoding.UTF8, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, Encoding.UTF8, null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, Encoding.UTF8, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin((Uri)null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(Encoding.UTF8, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin((Encoding)null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(Encoding.UTF8, null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(Encoding.UTF8, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin("username", null)); Assert.Throws <NotSupportedException> (() => sasl.Challenge(null)); sasl = new SaslMechanismNtlm(credentials); Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm((Uri)null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm(uri, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm((Uri)null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm(uri, (string)null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm(uri, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm(null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm(null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm("username", null)); Assert.DoesNotThrow(() => sasl.Challenge(null)); sasl = new SaslMechanismOAuth2(credentials); Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2((Uri)null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2(uri, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2((Uri)null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2(uri, (string)null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2(uri, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2(null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2(null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2("username", null)); Assert.DoesNotThrow(() => sasl.Challenge(null)); sasl = new SaslMechanismPlain(credentials); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain((Uri)null, Encoding.UTF8, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, Encoding.UTF8, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain((Uri)null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain((Uri)null, Encoding.UTF8, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, Encoding.UTF8, null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, Encoding.UTF8, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain((Uri)null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(Encoding.UTF8, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain((Encoding)null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(Encoding.UTF8, null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(Encoding.UTF8, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain("username", null)); Assert.DoesNotThrow(() => sasl.Challenge(null)); sasl = new SaslMechanismScramSha1(credentials); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(uri, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(uri, (string)null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(uri, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1((string)null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1("username", null)); Assert.DoesNotThrow(() => sasl.Challenge(null)); sasl = new SaslMechanismScramSha256(credentials); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(null, credentials)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(uri, null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(null, "username", "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(uri, (string)null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(uri, "username", null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(null)); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256((string)null, "password")); Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256("username", null)); Assert.DoesNotThrow(() => sasl.Challenge(null)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, uri, Encoding.UTF8, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null, Encoding.UTF8, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, null, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, Encoding.UTF8, null)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, uri, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null, credentials)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, null)); Assert.Throws <ArgumentNullException> (() => SaslMechanism.SaslPrep(null)); }
/// <summary> /// Authenticates using the supplied credentials. /// </summary> /// <remarks> /// <para>If the SMTP server supports authentication, then the SASL mechanisms /// that both the client and server support are tried in order of greatest /// security to weakest security. Once a SASL authentication mechanism is /// found that both client and server support, the credentials are used to /// authenticate.</para> /// <para>If, on the other hand, authentication is not supported, then /// this method simply returns without attempting to authenticate.</para> /// </remarks> /// <param name="credentials">The user's credentials.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="credentials"/> is <c>null</c>. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="SmtpClient"/> is not connected or is already authenticated. /// </exception> /// <exception cref="System.NotSupportedException"> /// The SMTP server does not support authentication. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.Security.Authentication.AuthenticationException"> /// Authentication using the supplied credentials has failed. /// </exception> /// <exception cref="MailKit.Security.SaslException"> /// A SASL authentication error occurred. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="SmtpCommandException"> /// The SMTP command failed. /// </exception> /// <exception cref="SmtpProtocolException"> /// An SMTP protocol error occurred. /// </exception> public void Authenticate(ICredentials credentials, CancellationToken cancellationToken) { if (!IsConnected) { throw new InvalidOperationException("The SmtpClient must be connected before you can authenticate."); } if (authenticated) { throw new InvalidOperationException("The SmtpClient is already authenticated."); } if ((Capabilities & SmtpCapabilities.Authentication) == 0) { throw new NotSupportedException("The SMTP server does not support authentication."); } if (credentials == null) { throw new ArgumentNullException("credentials"); } var uri = new Uri("smtp://" + host); SmtpResponse response; string challenge; string command; foreach (var authmech in SaslMechanism.AuthMechanismRank) { if (!AuthenticationMechanisms.Contains(authmech)) { continue; } var sasl = SaslMechanism.Create(authmech, uri, credentials); cancellationToken.ThrowIfCancellationRequested(); // send an initial challenge if the mechanism supports it if ((challenge = sasl.Challenge(null)) != null) { command = string.Format("AUTH {0} {1}", authmech, challenge); } else { command = string.Format("AUTH {0}", authmech); } response = SendCommand(command, cancellationToken); if (response.StatusCode == SmtpStatusCode.AuthenticationMechanismTooWeak) { continue; } while (!sasl.IsAuthenticated) { if (response.StatusCode != SmtpStatusCode.AuthenticationChallenge) { throw new SmtpCommandException(SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); } challenge = sasl.Challenge(response.Response); response = SendCommand(challenge, cancellationToken); } if (response.StatusCode == SmtpStatusCode.AuthenticationSuccessful) { Ehlo(cancellationToken); authenticated = true; return; } throw new AuthenticationException(); } throw new NotSupportedException("No compatible authentication mechanisms found."); }