예제 #1
0
        /// <summary>
        /// Parse the server's challenge token and return the next challenge response.
        /// </summary>
        /// <remarks>
        /// Parses the server's challenge token and returns the next challenge response.
        /// </remarks>
        /// <returns>The next challenge response.</returns>
        /// <param name="token">The server's challenge token.</param>
        /// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param>
        /// <param name="length">The length of the server's challenge.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <exception cref="System.NotSupportedException">
        /// The SASL mechanism does not support SASL-IR.
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="SaslException">
        /// An error has occurred while parsing the server's challenge token.
        /// </exception>
        protected override byte[] Challenge(byte[] token, int startIndex, int length, CancellationToken cancellationToken)
        {
            if (token == null)
            {
                throw new NotSupportedException("DIGEST-MD5 does not support SASL-IR.");
            }

            if (IsAuthenticated)
            {
                return(null);
            }

            switch (state)
            {
            case LoginState.Auth:
                if (token.Length > 2048)
                {
                    throw new SaslException(MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long.");
                }

                challenge = DigestChallenge.Parse(Encoding.UTF8.GetString(token, startIndex, length));
                encoding  = challenge.Charset != null ? Encoding.UTF8 : TextEncodings.Latin1;
                cnonce    = cnonce ?? GenerateEntropy(15);

                response = new DigestResponse(challenge, encoding, Uri.Scheme, Uri.DnsSafeHost, AuthorizationId, Credentials.UserName, Credentials.Password, cnonce);
                state    = LoginState.Final;

                return(response.Encode(encoding));

            case LoginState.Final:
                if (token.Length == 0)
                {
                    throw new SaslException(MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data.");
                }

                var    text = encoding.GetString(token, startIndex, length);
                string key, value;

                if (!DigestChallenge.TryParseKeyValuePair(text, out key, out value))
                {
                    throw new SaslException(MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data.");
                }

                if (!key.Equals("rspauth", StringComparison.OrdinalIgnoreCase))
                {
                    throw new SaslException(MechanismName, SaslErrorCode.InvalidChallenge, "Server response contained invalid data.");
                }

                var expected = response.ComputeHash(encoding, Credentials.Password, false);
                if (value != expected)
                {
                    throw new SaslException(MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash.");
                }

                IsAuthenticated = true;
                break;
            }

            return(null);
        }
예제 #2
0
 /// <summary>
 /// Resets the state of the SASL mechanism.
 /// </summary>
 public override void Reset()
 {
     state     = LoginState.Auth;
     challenge = null;
     response  = null;
     base.Reset();
 }
예제 #3
0
        /// <summary>
        /// Parses the server's challenge token and returns the next challenge response.
        /// </summary>
        /// <returns>The next challenge response.</returns>
        /// <param name="token">The server's challenge token.</param>
        /// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param>
        /// <param name="length">The length of the server's challenge.</param>
        /// <exception cref="SaslException">
        /// An error has occurred while parsing the server's challenge token.
        /// </exception>
        protected override byte[] Challenge(byte[] token, int startIndex, int length)
        {
            if (IsAuthenticated)
            {
                throw new InvalidOperationException();
            }

            if (token == null)
            {
                return(null);
            }

            var cred = Credentials.GetCredential(Uri, MechanismName);

            switch (state)
            {
            case LoginState.Auth:
                if (token.Length > 2048)
                {
                    throw new SaslException(MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long.");
                }

                challenge = DigestChallenge.Parse(Encoding.UTF8.GetString(token));
                response  = new DigestResponse(challenge, Uri.Scheme, Uri.DnsSafeHost, cred.UserName, cred.Password);
                state     = LoginState.Final;
                return(response.Encode());

            case LoginState.Final:
                if (token.Length == 0)
                {
                    throw new SaslException(MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data.");
                }

                var    text = Encoding.UTF8.GetString(token);
                string key, value;
                int    index = 0;

                if (!DigestChallenge.TryParseKeyValuePair(text, ref index, out key, out value))
                {
                    throw new SaslException(MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data.");
                }

                var expected = response.ComputeHash(cred.Password, false);
                if (value != expected)
                {
                    throw new SaslException(MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash.");
                }

                IsAuthenticated = true;
                return(new byte[0]);

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
		/// <summary>
		/// Resets the state of the SASL mechanism.
		/// </summary>
		/// <remarks>
		/// Resets the state of the SASL mechanism.
		/// </remarks>
		public override void Reset ()
		{
			state = LoginState.Auth;
			challenge = null;
			response = null;
			cnonce = null;
			base.Reset ();
		}
		/// <summary>
		/// Parses the server's challenge token and returns the next challenge response.
		/// </summary>
		/// <remarks>
		/// Parses the server's challenge token and returns the next challenge response.
		/// </remarks>
		/// <returns>The next challenge response.</returns>
		/// <param name="token">The server's challenge token.</param>
		/// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param>
		/// <param name="length">The length of the server's challenge.</param>
		/// <exception cref="System.InvalidOperationException">
		/// The SASL mechanism is already authenticated.
		/// </exception>
		/// <exception cref="System.NotSupportedException">
		/// THe SASL mechanism does not support SASL-IR.
		/// </exception>
		/// <exception cref="SaslException">
		/// An error has occurred while parsing the server's challenge token.
		/// </exception>
		protected override byte[] Challenge (byte[] token, int startIndex, int length)
		{
			if (IsAuthenticated)
				throw new InvalidOperationException ();

			if (token == null)
				throw new NotSupportedException ("DIGEST-MD5 does not support SASL-IR.");

			var cred = Credentials.GetCredential (Uri, MechanismName);

			switch (state) {
			case LoginState.Auth:
				if (token.Length > 2048)
					throw new SaslException (MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long.");

				challenge = DigestChallenge.Parse (Encoding.UTF8.GetString (token, startIndex, length));

				if (string.IsNullOrEmpty (cnonce)) {
					var entropy = new byte[15];

					using (var rng = RandomNumberGenerator.Create ())
						rng.GetBytes (entropy);

					cnonce = Convert.ToBase64String (entropy);
				}

				response = new DigestResponse (challenge, Uri.Scheme, Uri.DnsSafeHost, cred.UserName, cred.Password, cnonce);
				state = LoginState.Final;
				return response.Encode ();
			case LoginState.Final:
				if (token.Length == 0)
					throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data.");

				var text = Encoding.UTF8.GetString (token, startIndex, length);
				string key, value;
				int index = 0;

				if (!DigestChallenge.TryParseKeyValuePair (text, ref index, out key, out value))
					throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data.");

				var expected = response.ComputeHash (cred.Password, false);
				if (value != expected)
					throw new SaslException (MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash.");

				IsAuthenticated = true;
				return new byte[0];
			default:
				throw new IndexOutOfRangeException ("state");
			}
		}
예제 #6
0
        /// <summary>
        /// Parses the server's challenge token and returns the next challenge response.
        /// </summary>
        /// <remarks>
        /// Parses the server's challenge token and returns the next challenge response.
        /// </remarks>
        /// <returns>The next challenge response.</returns>
        /// <param name="token">The server's challenge token.</param>
        /// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param>
        /// <param name="length">The length of the server's challenge.</param>
        /// <exception cref="System.InvalidOperationException">
        /// The SASL mechanism is already authenticated.
        /// </exception>
        /// <exception cref="System.NotSupportedException">
        /// THe SASL mechanism does not support SASL-IR.
        /// </exception>
        /// <exception cref="SaslException">
        /// An error has occurred while parsing the server's challenge token.
        /// </exception>
        protected override byte[] Challenge(byte[] token, int startIndex, int length)
        {
            if (IsAuthenticated)
            {
                throw new InvalidOperationException();
            }

            if (token == null)
            {
                throw new NotSupportedException("DIGEST-MD5 does not support SASL-IR.");
            }

            switch (state)
            {
            case LoginState.Auth:
                if (token.Length > 2048)
                {
                    throw new SaslException(MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long.");
                }

                challenge = DigestChallenge.Parse(Encoding.UTF8.GetString(token, startIndex, length));

                if (string.IsNullOrEmpty(cnonce))
                {
                    var entropy = new byte[15];

                    using (var rng = RandomNumberGenerator.Create())
                        rng.GetBytes(entropy);

                    cnonce = Convert.ToBase64String(entropy);
                }

                response = new DigestResponse(challenge, Uri.Scheme, Uri.DnsSafeHost, AuthorizationId, Credentials.UserName, Credentials.Password, cnonce);
                state    = LoginState.Final;
                return(response.Encode());

            case LoginState.Final:
                if (token.Length == 0)
                {
                    throw new SaslException(MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data.");
                }

                var    text = Encoding.UTF8.GetString(token, startIndex, length);
                string key, value;
                int    index = 0;

                if (!DigestChallenge.TryParseKeyValuePair(text, ref index, out key, out value))
                {
                    throw new SaslException(MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data.");
                }

                var expected = response.ComputeHash(Credentials.Password, false);
                if (value != expected)
                {
                    throw new SaslException(MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash.");
                }

                IsAuthenticated = true;
                return(new byte[0]);

            default:
                throw new IndexOutOfRangeException("state");
            }
        }
예제 #7
0
        /// <summary>
        /// Parses the server's challenge token and returns the next challenge response.
        /// </summary>
        /// <returns>The next challenge response.</returns>
        /// <param name="token">The server's challenge token.</param>
        /// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param>
        /// <param name="length">The length of the server's challenge.</param>
        /// <exception cref="SaslException">
        /// An error has occurred while parsing the server's challenge token.
        /// </exception>
        protected override byte[] Challenge(byte[] token, int startIndex, int length)
        {
            if (IsAuthenticated)
                throw new InvalidOperationException ();

            if (token == null)
                return null;

            var cred = Credentials.GetCredential (Uri, MechanismName);

            switch (state) {
            case LoginState.Auth:
                if (token.Length > 2048)
                    throw new SaslException (MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long.");

                challenge = DigestChallenge.Parse (Encoding.UTF8.GetString (token));
                response = new DigestResponse (challenge, Uri.Scheme, Uri.DnsSafeHost, cred.UserName, cred.Password);
                state = LoginState.Final;
                return response.Encode ();
            case LoginState.Final:
                if (token.Length == 0)
                    throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data.");

                var text = Encoding.UTF8.GetString (token);
                string key, value;
                int index = 0;

                if (!DigestChallenge.TryParseKeyValuePair (text, ref index, out key, out value))
                    throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data.");

                var expected = response.ComputeHash (cred.Password, false);
                if (value != expected)
                    throw new SaslException (MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash.");

                IsAuthenticated = true;
                return new byte[0];
            default:
                throw new ArgumentOutOfRangeException ();
            }
        }