diff options
Diffstat (limited to 'src')
16 files changed, 94 insertions, 51 deletions
diff --git a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs index 5dd0dbd..f9cf612 100644 --- a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs +++ b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs @@ -114,6 +114,14 @@ namespace DotNetOpenAuth.Test.Mocks { return this.tokens[requestToken].Callback; } + public void SetTokenConsumerVersion(string token, Version version) { + this.tokens[token].ConsumerVersion = version; + } + + public Version GetTokenConsumerVersion(string token) { + return this.tokens[token].ConsumerVersion; + } + #endregion /// <summary> @@ -142,6 +150,7 @@ namespace DotNetOpenAuth.Test.Mocks { internal string Verifier; internal string Secret; internal Uri Callback; + internal Version ConsumerVersion; } } } diff --git a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs index 8f6696f..2a443ce 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs @@ -9,9 +9,9 @@ namespace DotNetOpenAuth.Test.OAuth { using System.Collections.Generic; using System.Linq; using System.Text; - using Microsoft.VisualStudio.TestTools.UnitTesting; - using DotNetOpenAuth.OAuth; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth; + using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class ServiceProviderTests : TestBase { @@ -20,10 +20,10 @@ namespace DotNetOpenAuth.Test.OAuth { /// </summary> [TestMethod] public void CreateVerificationCode() { - TestCode(VerificationCodeFormat.Numeric, 3, MessagingUtilities.Digits); - TestCode(VerificationCodeFormat.AlphaLower, 5, MessagingUtilities.LowercaseLetters); - TestCode(VerificationCodeFormat.AlphaUpper, 5, MessagingUtilities.UppercaseLetters); - TestCode(VerificationCodeFormat.AlphaNumericNoLookAlikes, 8, MessagingUtilities.AlphaNumericNoLookAlikes); + this.TestCode(VerificationCodeFormat.Numeric, 3, MessagingUtilities.Digits); + this.TestCode(VerificationCodeFormat.AlphaLower, 5, MessagingUtilities.LowercaseLetters); + this.TestCode(VerificationCodeFormat.AlphaUpper, 5, MessagingUtilities.UppercaseLetters); + this.TestCode(VerificationCodeFormat.AlphaNumericNoLookAlikes, 8, MessagingUtilities.AlphaNumericNoLookAlikes); } private void TestCode(VerificationCodeFormat format, int length, string allowableCharacters) { diff --git a/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs b/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs index 82910d5..22c660c 100644 --- a/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs +++ b/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.Messaging { using System; + using System.Diagnostics; using System.Net.Security; using System.Reflection; @@ -13,6 +14,7 @@ namespace DotNetOpenAuth.Messaging { /// Applied to fields and properties that form a key/value in a protocol message. /// </summary> [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = true, AllowMultiple = true)] + [DebuggerDisplay("MessagePartAttribute {Name}")] public sealed class MessagePartAttribute : Attribute { /// <summary> /// The overridden name to use as the serialized name for the property. diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index ae572ff..78342ae 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -199,7 +199,7 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires(allowableCharacters != null && allowableCharacters.Length >= 2); char[] randomString = new char[length]; - for(int i = 0; i < length;i++) { + for (int i = 0; i < length; i++) { randomString[i] = allowableCharacters[NonCryptoRandomDataGenerator.Next(allowableCharacters.Length)]; } diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index 493ed71..0732bb2 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Net.Security; @@ -17,6 +18,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// <summary> /// Describes an individual member of a message and assists in its serialization. /// </summary> + [DebuggerDisplay("MessagePart {Name}")] internal class MessagePart { /// <summary> /// A map of converters that help serialize custom objects to string values and back again. diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs index 1122988..58b61a4 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs @@ -51,5 +51,19 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <param name="requestToken">The request token.</param> /// <returns>The callback Uri. May be <c>null</c>.</returns> Uri GetRequestTokenCallback(string requestToken); + + /// <summary> + /// Sets the OAuth version used by the Consumer to request a token. + /// </summary> + /// <param name="token">The token.</param> + /// <param name="version">The OAuth version.</param> + void SetTokenConsumerVersion(string token, Version version); + + /// <summary> + /// Gets the OAuth version used by the Consumer to request a token. + /// </summary> + /// <param name="token">The token.</param> + /// <returns>The OAuth version</returns> + Version GetTokenConsumerVersion(string token); } } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs index 3f18d82..e05bb62 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs @@ -44,7 +44,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { MessageBase message = null; if (fields.ContainsKey("oauth_token")) { - message = new UserAuthorizationResponse(recipient.Location, Protocol.Default.Version); + Protocol protocol = fields.ContainsKey("oauth_verifier") ? Protocol.V10a : Protocol.V10; + message = new UserAuthorizationResponse(recipient.Location, protocol.Version); } if (message != null) { @@ -95,7 +96,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { Protocol protocol = fields.ContainsKey("oauth_callback_confirmed") ? Protocol.V10a : Protocol.V10; message = new UnauthorizedTokenResponse(unauthorizedTokenRequest, protocol.Version); } else if (authorizedTokenRequest != null) { - message = new AuthorizedTokenResponse(authorizedTokenRequest, Protocol.Default.Version); + message = new AuthorizedTokenResponse(authorizedTokenRequest); } else { Logger.OAuth.ErrorFormat("Unexpected response message given the request type {0}", request.GetType().Name); throw new ProtocolException(OAuthStrings.InvalidIncomingMessage); diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs index b3f750e..e8e8382 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs @@ -54,22 +54,36 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { ErrorUtilities.VerifyArgumentNotNull(fields, "fields"); MessageBase message = null; + Protocol protocol = Protocol.V10; // default to assuming the less-secure 1.0 instead of 1.0a until we prove otherwise. + string token; + if (fields.TryGetValue("oauth_token", out token)) { + // Discern between 1.0 and 1.0a requests by checking on the consumer version we stored + // when the consumer first requested an unauthorized token. + protocol = Protocol.Lookup(this.tokenManager.GetTokenConsumerVersion(token)); + } if (fields.ContainsKey("oauth_consumer_key") && !fields.ContainsKey("oauth_token")) { - Protocol protocol = fields.ContainsKey("oauth_callback") ? Protocol.V10a : Protocol.V10; + protocol = fields.ContainsKey("oauth_callback") ? Protocol.V10a : Protocol.V10; message = new UnauthorizedTokenRequest(recipient, protocol.Version); - } else if (fields.ContainsKey("oauth_consumer_key") && - fields.ContainsKey("oauth_token")) { + } else if (fields.ContainsKey("oauth_consumer_key") && fields.ContainsKey("oauth_token")) { // Discern between RequestAccessToken and AccessProtectedResources, // which have all the same parameters, by figuring out what type of token // is in the token parameter. - bool tokenTypeIsAccessToken = this.tokenManager.GetTokenType(fields["oauth_token"]) == TokenType.AccessToken; + bool tokenTypeIsAccessToken = this.tokenManager.GetTokenType(token) == TokenType.AccessToken; - message = tokenTypeIsAccessToken ? (MessageBase)new AccessProtectedResourceRequest(recipient, Protocol.Default.Version) : - new AuthorizedTokenRequest(recipient, Protocol.Default.Version); + message = tokenTypeIsAccessToken ? + (MessageBase)new AccessProtectedResourceRequest(recipient, protocol.Version) : + new AuthorizedTokenRequest(recipient, protocol.Version); } else { // fail over to the message with no required fields at all. - message = new UserAuthorizationRequest(recipient, Protocol.Default.Version); + + // If a callback parameter is included, that suggests either the consumer + // is following OAuth 1.0 instead of 1.0a, or that a hijacker is trying + // to attack. Either way, if the consumer started out as a 1.0a, keep it + // that way, and we'll just ignore the oauth_callback included in this message + // by virtue of the UserAuthorizationRequest message not including it in its + // 1.0a payload. + message = new UserAuthorizationRequest(recipient, protocol.Version); } if (message != null) { diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs index e077128..2ec5e0e 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs @@ -79,6 +79,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { var grantRequestTokenResponse = message as UnauthorizedTokenResponse; if (grantRequestTokenResponse != null) { this.tokenManager.StoreNewRequestToken(grantRequestTokenResponse.RequestMessage, grantRequestTokenResponse); + this.tokenManager.SetTokenConsumerVersion(grantRequestTokenResponse.RequestToken, grantRequestTokenResponse.Version); if (grantRequestTokenResponse.RequestMessage.Callback != null) { this.tokenManager.SetRequestTokenCallback(grantRequestTokenResponse.RequestToken, grantRequestTokenResponse.RequestMessage.Callback); } @@ -109,10 +110,10 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { ErrorUtilities.VerifyArgumentNotNull(message, "message"); - var request = message as AuthorizedTokenRequest; - if (request != null && request.Version >= Protocol.V10a.Version) { - string expectedVerifier = this.tokenManager.GetRequestTokenVerifier(request.RequestToken); - ErrorUtilities.VerifyProtocol(string.Equals(request.VerificationCode, expectedVerifier, StringComparison.Ordinal), OAuthStrings.IncorrectVerifier); + var authorizedTokenRequest = message as AuthorizedTokenRequest; + if (authorizedTokenRequest != null && authorizedTokenRequest.Version >= Protocol.V10a.Version) { + string expectedVerifier = this.tokenManager.GetRequestTokenVerifier(authorizedTokenRequest.RequestToken); + ErrorUtilities.VerifyProtocol(string.Equals(authorizedTokenRequest.VerificationCode, expectedVerifier, StringComparison.Ordinal), OAuthStrings.IncorrectVerifier); return MessageProtections.None; } diff --git a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs index d87c381..0b14819 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs @@ -19,9 +19,8 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="AuthorizedTokenResponse"/> class. /// </summary> /// <param name="originatingRequest">The originating request.</param> - /// <param name="version">The OAuth version.</param> - protected internal AuthorizedTokenResponse(AuthorizedTokenRequest originatingRequest, Version version) - : base(MessageProtections.None, originatingRequest, version) { + protected internal AuthorizedTokenResponse(AuthorizedTokenRequest originatingRequest) + : base(MessageProtections.None, originatingRequest, originatingRequest.Version) { } /// <summary> diff --git a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs index 23a138c..8ccd8e3 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs @@ -21,12 +21,11 @@ namespace DotNetOpenAuth.OAuth.Messages { /// <param name="requestMessage">The unauthorized request token message that this message is being generated in response to.</param> /// <param name="requestToken">The request token.</param> /// <param name="tokenSecret">The token secret.</param> - /// <param name="version">The OAuth version.</param> /// <remarks> /// This constructor is used by the Service Provider to send the message. /// </remarks> - protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest requestMessage, string requestToken, string tokenSecret, Version version) - : this(requestMessage, version) { + protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest requestMessage, string requestToken, string tokenSecret) + : this(requestMessage, requestMessage.Version) { ErrorUtilities.VerifyArgumentNotNull(requestToken, "requestToken"); ErrorUtilities.VerifyArgumentNotNull(tokenSecret, "tokenSecret"); diff --git a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs index 0924ac6..099729e 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs @@ -53,6 +53,14 @@ namespace DotNetOpenAuth.OAuth.Messages { } /// <summary> + /// Gets a value indicating whether this is a safe OAuth authorization request. + /// </summary> + /// <value><c>true</c> if the Consumer is using OAuth 1.0a or later; otherwise, <c>false</c>.</value> + public bool IsUnsafeRequest { + get { return this.Version < Protocol.V10a.Version; } + } + + /// <summary> /// Gets or sets the Request Token obtained in the previous step. /// </summary> /// <remarks> diff --git a/src/DotNetOpenAuth/OAuth/Protocol.cs b/src/DotNetOpenAuth/OAuth/Protocol.cs index b0a53fb..129433d 100644 --- a/src/DotNetOpenAuth/OAuth/Protocol.cs +++ b/src/DotNetOpenAuth/OAuth/Protocol.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.OAuth { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Linq; using System.Text; using DotNetOpenAuth.Messaging; @@ -33,6 +34,7 @@ namespace DotNetOpenAuth.OAuth { /// OAuth Protocol Parameter names and values are case sensitive. Each OAuth Protocol Parameters MUST NOT appear more than once per request, and are REQUIRED unless otherwise noted, /// per OAuth 1.0 section 5. /// </remarks> + [DebuggerDisplay("OAuth {Version}")] internal class Protocol { /// <summary> /// The namespace to use for V1.0 of the protocol. diff --git a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs index 7ef668d..311e531 100644 --- a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs +++ b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs @@ -188,7 +188,7 @@ namespace DotNetOpenAuth.OAuth { string token = this.TokenGenerator.GenerateRequestToken(request.ConsumerKey); string secret = this.TokenGenerator.GenerateSecret(); - UnauthorizedTokenResponse response = new UnauthorizedTokenResponse(request, token, secret, this.ServiceDescription.Version); + UnauthorizedTokenResponse response = new UnauthorizedTokenResponse(request, token, secret); return response; } @@ -233,17 +233,11 @@ namespace DotNetOpenAuth.OAuth { Contract.Requires(request != null); ErrorUtilities.VerifyArgumentNotNull(request, "request"); - Uri callback; - if (this.ServiceDescription.Version >= Protocol.V10a.Version) { - callback = this.TokenManager.GetRequestTokenCallback(request.RequestToken); - } else { - callback = request.Callback; - } - if (callback != null) { - return this.PrepareAuthorizationResponse(request, callback); - } else { - return null; - } + // It is very important for us to ignore the oauth_callback argument in the + // UserAuthorizationRequest if the Consumer is a 1.0a consumer or else we + // open up a security exploit. + Uri callback = request.Version >= Protocol.V10a.Version ? this.TokenManager.GetRequestTokenCallback(request.RequestToken) : request.Callback; + return callback != null ? this.PrepareAuthorizationResponse(request, callback) : null; } /// <summary> @@ -265,8 +259,12 @@ namespace DotNetOpenAuth.OAuth { var authorization = new UserAuthorizationResponse(callback, this.ServiceDescription.Version) { RequestToken = request.RequestToken, - VerificationCode = CreateVerificationCode(VerificationCodeFormat.IncludedInCallback, VerifierCodeLength), }; + + if (authorization.Version >= Protocol.V10a.Version) { + authorization.VerificationCode = CreateVerificationCode(VerificationCodeFormat.IncludedInCallback, VerifierCodeLength); + } + return authorization; } @@ -300,22 +298,15 @@ namespace DotNetOpenAuth.OAuth { /// <param name="request">The Consumer's message requesting an access token.</param> /// <returns>The HTTP response to actually send to the Consumer.</returns> public AuthorizedTokenResponse PrepareAccessTokenMessage(AuthorizedTokenRequest request) { - if (request == null) { - throw new ArgumentNullException("request"); - } + Contract.Requires(request != null); + ErrorUtilities.VerifyArgumentNotNull(request, "request"); - if (!this.TokenManager.IsRequestTokenAuthorized(request.RequestToken)) { - throw new ProtocolException( - string.Format( - CultureInfo.CurrentCulture, - OAuthStrings.AccessTokenNotAuthorized, - request.RequestToken)); - } + ErrorUtilities.VerifyProtocol(this.TokenManager.IsRequestTokenAuthorized(request.RequestToken), OAuthStrings.AccessTokenNotAuthorized, request.RequestToken); string accessToken = this.TokenGenerator.GenerateAccessToken(request.ConsumerKey); string tokenSecret = this.TokenGenerator.GenerateSecret(); this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(request.ConsumerKey, request.RequestToken, accessToken, tokenSecret); - var grantAccess = new AuthorizedTokenResponse(request, this.ServiceDescription.Version) { + var grantAccess = new AuthorizedTokenResponse(request) { AccessToken = accessToken, TokenSecret = tokenSecret, }; diff --git a/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs b/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs index ced20f1..d25c988 100644 --- a/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs +++ b/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs @@ -55,5 +55,4 @@ namespace DotNetOpenAuth.OAuth { /// </summary> Numeric, } - } diff --git a/src/DotNetOpenAuth/OpenId/Protocol.cs b/src/DotNetOpenAuth/OpenId/Protocol.cs index b9f2cca..7b8a2f1 100644 --- a/src/DotNetOpenAuth/OpenId/Protocol.cs +++ b/src/DotNetOpenAuth/OpenId/Protocol.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OpenId { using DotNetOpenAuth.Messaging; using System.Globalization; using System.Diagnostics.CodeAnalysis; + using System.Diagnostics; /// <summary> /// An enumeration of the OpenID protocol versions supported by this library. @@ -34,6 +35,7 @@ namespace DotNetOpenAuth.OpenId { /// Tracks the several versions of OpenID this library supports and the unique /// constants to each version used in the protocol. /// </summary> + [DebuggerDisplay("OpenID {Version}")] internal class Protocol { /// <summary> /// The value of the openid.ns parameter in the OpenID 2.0 specification. |