diff options
Diffstat (limited to 'src')
21 files changed, 84 insertions, 10 deletions
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs index 7701090..083b988 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs @@ -59,6 +59,18 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { Assert.AreEqual(AuthenticationStatus.Failed, authResponse.Status); } + /// <summary> + /// Verifies that the RP rejects positive assertions with HTTP Claimed + /// Cdentifiers when RequireSsl is set to true. + /// </summary> + [TestMethod, ExpectedException(typeof(ProtocolException))] + public void InsecureIdentifiersRejectedWithRequireSsl() { + PositiveAssertionResponse assertion = this.GetPositiveAssertion(); + var rp = CreateRelyingParty(); + rp.SecuritySettings.RequireSsl = true; + var authResponse = new PositiveAuthenticationResponse(assertion, rp); + } + [TestMethod] public void GetCallbackArguments() { PositiveAssertionResponse assertion = this.GetPositiveAssertion(); diff --git a/src/DotNetOpenAuth/Configuration/OAuthConsumerSecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OAuthConsumerSecuritySettingsElement.cs index 5e75390..38a183a 100644 --- a/src/DotNetOpenAuth/Configuration/OAuthConsumerSecuritySettingsElement.cs +++ b/src/DotNetOpenAuth/Configuration/OAuthConsumerSecuritySettingsElement.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.Configuration { using System; using System.Collections.Generic; using System.Configuration; + using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using DotNetOpenAuth.OAuth; @@ -26,6 +27,7 @@ namespace DotNetOpenAuth.Configuration { /// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file. /// </summary> /// <returns>The newly created security settings object.</returns> + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "By design")] internal ConsumerSecuritySettings CreateSecuritySettings() { return new ConsumerSecuritySettings(); } diff --git a/src/DotNetOpenAuth/GlobalSuppressions.cs b/src/DotNetOpenAuth/GlobalSuppressions.cs index 842e6dd..6997632 100644 --- a/src/DotNetOpenAuth/GlobalSuppressions.cs +++ b/src/DotNetOpenAuth/GlobalSuppressions.cs @@ -42,3 +42,4 @@ [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "We sign it when producing drops.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.OAuth")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.Messaging.Reflection")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "oauthverifier", Scope = "resource", Target = "DotNetOpenAuth.OAuth.OAuthStrings.resources")] diff --git a/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs b/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs index 1d4d28e..3d624a6 100644 --- a/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs +++ b/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs @@ -73,7 +73,7 @@ namespace DotNetOpenAuth.Messaging.Bindings { /// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property. /// </remarks> public bool StoreNonce(string context, string nonce, DateTime timestamp) { - if (timestamp.ToUniversalTime() + this.maximumMessageAge < DateTime.UtcNow) { + if (timestamp.ToUniversalTimeSafe() + this.maximumMessageAge < DateTime.UtcNow) { // The expiration binding element should have taken care of this, but perhaps // it's at the boundary case. We should fail just to be safe. return false; @@ -115,7 +115,7 @@ namespace DotNetOpenAuth.Messaging.Bindings { /// </summary> public void ClearExpiredNonces() { lock (this.nonceLock) { - var oldNonceLists = this.usedNonces.Keys.Where(time => time.ToUniversalTime() + this.maximumMessageAge < DateTime.UtcNow).ToList(); + var oldNonceLists = this.usedNonces.Keys.Where(time => time.ToUniversalTimeSafe() + this.maximumMessageAge < DateTime.UtcNow).ToList(); foreach (DateTime time in oldNonceLists) { this.usedNonces.Remove(time); } diff --git a/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs b/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs index 5fcf4bd..7b00e34 100644 --- a/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs +++ b/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs @@ -87,7 +87,7 @@ namespace DotNetOpenAuth.Messaging.Bindings { if (expiringMessage != null) { // Yes the UtcCreationDate is supposed to always be in UTC already, // but just in case a given message failed to guarantee that, we do it here. - DateTime expirationDate = expiringMessage.UtcCreationDate.ToUniversalTime() + MaximumMessageAge; + DateTime expirationDate = expiringMessage.UtcCreationDate.ToUniversalTimeSafe() + MaximumMessageAge; if (expirationDate < DateTime.UtcNow) { throw new ExpiredMessageException(expirationDate, expiringMessage); } diff --git a/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs b/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs index e532e99..79a1107 100644 --- a/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs +++ b/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs @@ -12,6 +12,7 @@ namespace DotNetOpenAuth.Messaging { /// An immutable description of a URL that receives messages. /// </summary> [DebuggerDisplay("{AllowedMethods} {Location}")] + [Serializable] public class MessageReceivingEndpoint { /// <summary> /// Initializes a new instance of the <see cref="MessageReceivingEndpoint"/> class. diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index da95771..463b556 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -765,6 +765,32 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Ensures that UTC times are converted to local times. Unspecified kinds are unchanged. + /// </summary> + /// <param name="value">The date-time to convert.</param> + /// <returns>The date-time in local time.</returns> + internal static DateTime ToLocalTimeSafe(this DateTime value) { + if (value.Kind == DateTimeKind.Unspecified) { + return value; + } + + return value.ToLocalTime(); + } + + /// <summary> + /// Ensures that local times are converted to UTC times. Unspecified kinds are unchanged. + /// </summary> + /// <param name="value">The date-time to convert.</param> + /// <returns>The date-time in UTC time.</returns> + internal static DateTime ToUniversalTimeSafe(this DateTime value) { + if (value.Kind == DateTimeKind.Unspecified) { + return value; + } + + return value.ToUniversalTime(); + } + + /// <summary> /// A class to convert a <see cref="Comparison<T>"/> into an <see cref="IComparer<T>"/>. /// </summary> /// <typeparam name="T">The type of objects being compared.</typeparam> diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs index 123582a..a1939dd 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -307,7 +307,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <para>This method implements OAuth 1.0 section 5.2, item #1 (described in section 5.4).</para> /// </remarks> private HttpWebRequest InitializeRequestAsAuthHeader(IDirectedProtocolMessage requestMessage) { - var protocol = Protocol.Lookup(requestMessage.Version); var dictionary = this.MessageDescriptions.GetAccessor(requestMessage); // copy so as to not modify original diff --git a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs index 89e0276..944bc5c 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs @@ -17,6 +17,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// <summary> /// A base class for all OAuth messages. /// </summary> + [Serializable] public abstract class MessageBase : IDirectedProtocolMessage, IDirectResponseProtocolMessage { /// <summary> /// A store for extra name/value data pairs that are attached to this message. diff --git a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs index 8ccd8e3..ce09213 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs @@ -89,6 +89,8 @@ namespace DotNetOpenAuth.OAuth.Messages { /// <summary> /// Gets a value indicating whether the Service Provider recognized the callback parameter in the request. /// </summary> + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Message serialization invoked.")] + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Message parts must be instance members.")] [MessagePart("oauth_callback_confirmed", IsRequired = true, MinVersion = Protocol.V10aVersion)] private bool CallbackConfirmed { get { return true; } diff --git a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs index 099729e..a5823bb 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs @@ -15,6 +15,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// so the Service Provider can ask the user to authorize the Consumer's access to some /// protected resource(s). /// </summary> + [Serializable] public class UserAuthorizationRequest : MessageBase, ITokenContainingMessage { /// <summary> /// Initializes a new instance of the <see cref="UserAuthorizationRequest"/> class. diff --git a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs index 69a327c..73fddc7 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs @@ -14,6 +14,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// <remarks> /// The class is sealed because extra parameters are determined by the callback URI provided by the Consumer. /// </remarks> + [Serializable] public sealed class UserAuthorizationResponse : MessageBase, ITokenContainingMessage { /// <summary> /// Initializes a new instance of the <see cref="UserAuthorizationResponse"/> class. diff --git a/src/DotNetOpenAuth/OAuth/Protocol.cs b/src/DotNetOpenAuth/OAuth/Protocol.cs index f535b10..cd4e486 100644 --- a/src/DotNetOpenAuth/OAuth/Protocol.cs +++ b/src/DotNetOpenAuth/OAuth/Protocol.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth { using System; using System.Collections.Generic; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using DotNetOpenAuth.Messaging; @@ -24,6 +25,7 @@ namespace DotNetOpenAuth.OAuth { /// <summary> /// OAuth 1.0a specification /// </summary> + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "a", Justification = "By design.")] V10a, } diff --git a/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs b/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs index d25c988..3afd44e 100644 --- a/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs +++ b/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs @@ -5,6 +5,8 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth { + using System.Diagnostics.CodeAnalysis; + /// <summary> /// The different formats a user authorization verifier code can take /// in order to be as secure as possible while being compatible with @@ -33,6 +35,8 @@ namespace DotNetOpenAuth.OAuth { /// Some letters and numbers will be skipped where they are visually similar /// enough that they can be difficult to distinguish when displayed with most fonts. /// </remarks> + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Alikes", Justification = "Breaking change of existing API")] + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "AlphaNumeric", Justification = "Breaking change of existing API")] AlphaNumericNoLookAlikes, /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/Association.cs b/src/DotNetOpenAuth/OpenId/Association.cs index eb7c880..ce129bb 100644 --- a/src/DotNetOpenAuth/OpenId/Association.cs +++ b/src/DotNetOpenAuth/OpenId/Association.cs @@ -149,7 +149,7 @@ namespace DotNetOpenAuth.OpenId { if (privateData == null) { throw new ArgumentNullException("privateData"); } - expires = expires.ToUniversalTime(); + expires = expires.ToUniversalTimeSafe(); TimeSpan remainingLifeLength = expires - DateTime.UtcNow; byte[] secret = privateData; // the whole of privateData is the secret key for now. // We figure out what derived type to instantiate based on the length of the secret. diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/DateTimeEncoder.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/DateTimeEncoder.cs index 82297d0..9dc0574 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/DateTimeEncoder.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/DateTimeEncoder.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy { using System; using System.Globalization; + using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; /// <summary> @@ -39,7 +40,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy { public string Encode(object value) { DateTime? dateTime = value as DateTime?; if (dateTime.HasValue) { - return dateTime.Value.ToUniversalTime().ToString(PermissibleDateTimeFormats[0], CultureInfo.InvariantCulture); + return dateTime.Value.ToUniversalTimeSafe().ToString(PermissibleDateTimeFormats[0], CultureInfo.InvariantCulture); } else { return null; } diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs index 4b2bcc9..b476cf7 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs @@ -90,7 +90,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy { // Convert to UTC and cut to the second, since the protocol only allows for // that level of precision. - this.authenticationTimeUtc = OpenIdUtilities.CutToSecond(value.Value.ToUniversalTime()); + this.authenticationTimeUtc = OpenIdUtilities.CutToSecond(value.Value.ToUniversalTimeSafe()); } else { this.authenticationTimeUtc = null; } diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs index 0d105ad..9462d21 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs @@ -201,7 +201,7 @@ namespace DotNetOpenAuth.OpenId.Messages { /// </exception> DateTime IExpiringProtocolMessage.UtcCreationDate { get { return this.creationDateUtc; } - set { this.creationDateUtc = value.ToUniversalTime(); } + set { this.creationDateUtc = value.ToUniversalTimeSafe(); } } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs index a6e02fe..fb6e997 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs @@ -470,6 +470,15 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Looks up a localized string similar to Sorry. This site only accepts OpenIDs that are HTTPS-secured, but {0} is not a secure Identifier.. + /// </summary> + internal static string RequireSslNotSatisfiedByAssertedClaimedId { + get { + return ResourceManager.GetString("RequireSslNotSatisfiedByAssertedClaimedId", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to The response is not ready. Use IsResponseReady to check whether a response is ready first.. /// </summary> internal static string ResponseNotReady { diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx index 95fe655..20d3b67 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx @@ -301,4 +301,7 @@ Discovered endpoint info: <data name="UnsupportedChannelConfiguration" xml:space="preserve"> <value>This feature is unavailable due to an unrecognized channel configuration.</value> </data> -</root>
\ No newline at end of file + <data name="RequireSslNotSatisfiedByAssertedClaimedId" xml:space="preserve"> + <value>Sorry. This site only accepts OpenIDs that are HTTPS-secured, but {0} is not a secure Identifier.</value> + </data> +</root> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs index 32980f5..a7cf801 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs @@ -316,6 +316,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { private void VerifyDiscoveryMatchesAssertion() { Logger.OpenId.Debug("Verifying assertion matches identifier discovery results..."); + // Ensure that we abide by the RP's rules regarding RequireSsl for this discovery step. + Identifier claimedId = this.Response.ClaimedIdentifier; + if (this.relyingParty.SecuritySettings.RequireSsl) { + if (!claimedId.TryRequireSsl(out claimedId)) { + Logger.OpenId.ErrorFormat("This site is configured to accept only SSL-protected OpenIDs, but {0} was asserted and must be rejected.", this.Response.ClaimedIdentifier); + ErrorUtilities.ThrowProtocol(OpenIdStrings.RequireSslNotSatisfiedByAssertedClaimedId, this.Response.ClaimedIdentifier); + } + } + // While it LOOKS like we're performing discovery over HTTP again // Yadis.IdentifierDiscoveryCachePolicy is set to HttpRequestCacheLevel.CacheIfAvailable // which means that the .NET runtime is caching our discoveries for us. This turns out @@ -325,7 +334,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // is signed by the RP before it's considered reliable. In 1.x stateless mode, this RP // doesn't (and can't) sign its own return_to URL, so its cached discovery information // is merely a hint that must be verified by performing discovery again here. - var discoveryResults = this.response.ClaimedIdentifier.Discover(this.relyingParty.WebRequestHandler); + var discoveryResults = claimedId.Discover(this.relyingParty.WebRequestHandler); ErrorUtilities.VerifyProtocol(discoveryResults.Contains(this.endpoint), OpenIdStrings.IssuedAssertionFailsIdentifierDiscovery, this.endpoint, discoveryResults.ToStringDeferred(true)); } } |