diff options
Diffstat (limited to 'src')
8 files changed, 194 insertions, 21 deletions
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs index 3fd7d20..973687f 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs @@ -23,6 +23,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { private IDictionary<string, string> callbackArguments; /// <summary> + /// The untrusted callback arguments that came with the authentication response. + /// </summary> + private IDictionary<string, string> untrustedCallbackArguments; + + /// <summary> /// Initializes a new instance of the <see cref="AuthenticationResponseSnapshot"/> class. /// </summary> /// <param name="copyFrom">The authentication response to copy from.</param> @@ -34,6 +39,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { this.Status = copyFrom.Status; this.Provider = copyFrom.Provider; this.callbackArguments = copyFrom.GetCallbackArguments(); + this.untrustedCallbackArguments = copyFrom.GetUntrustedCallbackArguments(); } #region IAuthenticationResponse Members @@ -229,6 +235,23 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Gets all the callback arguments that were previously added using + /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part + /// of the return_to URL. + /// </summary> + /// <returns>A name-value dictionary. Never null.</returns> + /// <remarks> + /// Callback parameters are only available even if the RP is in stateless mode, + /// or the callback parameters are otherwise unverifiable as untampered with. + /// Therefore, use this method only when the callback argument is not to be + /// used to make a security-sensitive decision. + /// </remarks> + public IDictionary<string, string> GetUntrustedCallbackArguments() { + // Return a copy so that the caller cannot change the contents. + return new Dictionary<string, string>(this.untrustedCallbackArguments); + } + + /// <summary> /// Gets a callback argument's value that was previously added using /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>. /// </summary> @@ -250,6 +273,28 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { return value; } + /// <summary> + /// Gets a callback argument's value that was previously added using + /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>. + /// </summary> + /// <param name="key">The name of the parameter whose value is sought.</param> + /// <returns> + /// The value of the argument, or null if the named parameter could not be found. + /// </returns> + /// <remarks> + /// Callback parameters are only available even if the RP is in stateless mode, + /// or the callback parameters are otherwise unverifiable as untampered with. + /// Therefore, use this method only when the callback argument is not to be + /// used to make a security-sensitive decision. + /// </remarks> + public string GetUntrustedCallbackArgument(string key) { + ErrorUtilities.VerifyArgumentNotNull(key, "key"); + + string value; + this.untrustedCallbackArguments.TryGetValue(key, out value); + return value; + } + #endregion } } diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs index d94af14..45f7f54 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs @@ -128,7 +128,23 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <para>Note that these values are NOT protected against tampering in transit.</para> /// </remarks> public IDictionary<string, string> GetCallbackArguments() { - return new Dictionary<string, string>(); + return EmptyDictionary<string, string>.Instance; + } + + /// <summary> + /// Gets all the callback arguments that were previously added using + /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part + /// of the return_to URL. + /// </summary> + /// <returns>A name-value dictionary. Never null.</returns> + /// <remarks> + /// Callback parameters are only available even if the RP is in stateless mode, + /// or the callback parameters are otherwise unverifiable as untampered with. + /// Therefore, use this method only when the callback argument is not to be + /// used to make a security-sensitive decision. + /// </remarks> + public IDictionary<string, string> GetUntrustedCallbackArguments() { + return EmptyDictionary<string, string>.Instance; } /// <summary> @@ -150,6 +166,24 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Gets a callback argument's value that was previously added using + /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>. + /// </summary> + /// <param name="key">The name of the parameter whose value is sought.</param> + /// <returns> + /// The value of the argument, or null if the named parameter could not be found. + /// </returns> + /// <remarks> + /// Callback parameters are only available even if the RP is in stateless mode, + /// or the callback parameters are otherwise unverifiable as untampered with. + /// Therefore, use this method only when the callback argument is not to be + /// used to make a security-sensitive decision. + /// </remarks> + public string GetUntrustedCallbackArgument(string key) { + return null; + } + + /// <summary> /// Tries to get an OpenID extension that may be present in the response. /// </summary> /// <typeparam name="T">The type of extension to look for in the response message.</typeparam> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs index 3ab48db..fd35a6b 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs @@ -113,6 +113,22 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { string GetCallbackArgument(string key); /// <summary> + /// Gets a callback argument's value that was previously added using + /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>. + /// </summary> + /// <param name="key">The name of the parameter whose value is sought.</param> + /// <returns> + /// The value of the argument, or null if the named parameter could not be found. + /// </returns> + /// <remarks> + /// Callback parameters are only available even if the RP is in stateless mode, + /// or the callback parameters are otherwise unverifiable as untampered with. + /// Therefore, use this method only when the callback argument is not to be + /// used to make a security-sensitive decision. + /// </remarks> + string GetUntrustedCallbackArgument(string key); + + /// <summary> /// Gets all the callback arguments that were previously added using /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part /// of the return_to URL. @@ -129,6 +145,21 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { IDictionary<string, string> GetCallbackArguments(); /// <summary> + /// Gets all the callback arguments that were previously added using + /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part + /// of the return_to URL. + /// </summary> + /// <returns>A name-value dictionary. Never null.</returns> + /// <remarks> + /// Callback parameters are only available even if the RP is in stateless mode, + /// or the callback parameters are otherwise unverifiable as untampered with. + /// Therefore, use this method only when the callback argument is not to be + /// used to make a security-sensitive decision. + /// </remarks> + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Historically an expensive operation.")] + IDictionary<string, string> GetUntrustedCallbackArguments(); + + /// <summary> /// Tries to get an OpenID extension that may be present in the response. /// </summary> /// <typeparam name="T">The type of extension to look for in the response message.</typeparam> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs index e66ac28..5aa2e24 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs @@ -159,6 +159,24 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Gets a callback argument's value that was previously added using + /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>. + /// </summary> + /// <param name="key">The name of the parameter whose value is sought.</param> + /// <returns> + /// The value of the argument, or null if the named parameter could not be found. + /// </returns> + /// <remarks> + /// Callback parameters are only available even if the RP is in stateless mode, + /// or the callback parameters are otherwise unverifiable as untampered with. + /// Therefore, use this method only when the callback argument is not to be + /// used to make a security-sensitive decision. + /// </remarks> + public string GetUntrustedCallbackArgument(string key) { + return null; + } + + /// <summary> /// Gets all the callback arguments that were previously added using /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part /// of the return_to URL. @@ -175,6 +193,22 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Gets all the callback arguments that were previously added using + /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part + /// of the return_to URL. + /// </summary> + /// <returns>A name-value dictionary. Never null.</returns> + /// <remarks> + /// Callback parameters are only available even if the RP is in stateless mode, + /// or the callback parameters are otherwise unverifiable as untampered with. + /// Therefore, use this method only when the callback argument is not to be + /// used to make a security-sensitive decision. + /// </remarks> + public IDictionary<string, string> GetUntrustedCallbackArguments() { + return EmptyDictionary<string, string>.Instance; + } + + /// <summary> /// Tries to get an OpenID extension that may be present in the response. /// </summary> /// <typeparam name="T">The type of extension to look for in the response message.</typeparam> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs index 2fa2109..e6e31e5 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs @@ -635,9 +635,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { var response = this.RelyingParty.GetResponse(); if (response != null) { - // We don't use response.GetCallbackArgument because it will return null in stateless - // mode since the arg may have been tampered with, but this isn't a security decision. - string persistentString = this.Page.Request.QueryString[UsePersistentCookieCallbackKey]; + string persistentString = response.GetUntrustedCallbackArgument(UsePersistentCookieCallbackKey); bool persistentBool; if (persistentString != null && bool.TryParse(persistentString, out persistentBool)) { this.UsePersistentCookie = persistentBool; diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs index 703dda0..0efabd2 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs @@ -459,9 +459,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { if (receiver == null || receiver == this.ClientID) { var response = this.RelyingParty.GetResponse(); if (response != null) { - // We don't use response.GetCallbackArgument because it will return null in stateless - // mode since the arg may have been tampered with, but this isn't a security decision. - string persistentString = this.Page.Request.QueryString[UsePersistentCookieCallbackKey]; + string persistentString = response.GetUntrustedCallbackArgument(UsePersistentCookieCallbackKey); bool persistentBool; if (persistentString != null && bool.TryParse(persistentString, out persistentBool)) { this.UsePersistentCookie = persistentBool; diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs index a920ac7..0723f55 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs @@ -1035,9 +1035,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { var response = this.RelyingParty.GetResponse(); if (response != null) { - // We don't use response.GetCallbackArgument because it will return null in stateless - // mode since the arg may have been tampered with, but this isn't a security decision. - string persistentString = this.Page.Request.QueryString[UsePersistentCookieCallbackKey]; + string persistentString = response.GetUntrustedCallbackArgument(UsePersistentCookieCallbackKey); bool persistentBool; if (persistentString != null && bool.TryParse(persistentString, out persistentBool)) { this.UsePersistentCookie = persistentBool; diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs index 4692b5b..baf30da 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs @@ -154,7 +154,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </remarks> public string GetCallbackArgument(string key) { if (this.response.ReturnToParametersSignatureValidated) { - return this.response.GetReturnToArgument(key); + return this.GetUntrustedCallbackArgument(key); } else { Logger.OpenId.WarnFormat(OpenIdStrings.CallbackArgumentsRequireSecretStore, typeof(IAssociationStore<Uri>).Name, typeof(OpenIdRelyingParty).Name); return null; @@ -162,6 +162,24 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Gets a callback argument's value that was previously added using + /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>. + /// </summary> + /// <param name="key">The name of the parameter whose value is sought.</param> + /// <returns> + /// The value of the argument, or null if the named parameter could not be found. + /// </returns> + /// <remarks> + /// Callback parameters are only available even if the RP is in stateless mode, + /// or the callback parameters are otherwise unverifiable as untampered with. + /// Therefore, use this method only when the callback argument is not to be + /// used to make a security-sensitive decision. + /// </remarks> + public string GetUntrustedCallbackArgument(string key) { + return this.response.GetReturnToArgument(key); + } + + /// <summary> /// Gets all the callback arguments that were previously added using /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part /// of the return_to URL. @@ -176,16 +194,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </remarks> public IDictionary<string, string> GetCallbackArguments() { if (this.response.ReturnToParametersSignatureValidated) { - var args = new Dictionary<string, string>(); - - // Return all the return_to arguments, except for the OpenID-supporting ones. - // The only arguments that should be returned here are the ones that the host - // web site adds explicitly. - foreach (string key in this.response.GetReturnToParameterNames().Where(key => !OpenIdRelyingParty.IsOpenIdSupportingParameter(key))) { - args[key] = this.response.GetReturnToArgument(key); - } - - return args; + return this.GetUntrustedCallbackArguments(); } else { Logger.OpenId.WarnFormat(OpenIdStrings.CallbackArgumentsRequireSecretStore, typeof(IAssociationStore<Uri>).Name, typeof(OpenIdRelyingParty).Name); return EmptyDictionary<string, string>.Instance; @@ -193,6 +202,32 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Gets all the callback arguments that were previously added using + /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part + /// of the return_to URL. + /// </summary> + /// <returns>A name-value dictionary. Never null.</returns> + /// <remarks> + /// Callback parameters are only available if they are complete and untampered with + /// since the original request message (as proven by a signature). + /// If the relying party is operating in stateless mode an empty dictionary is always + /// returned since the callback arguments could not be signed to protect against + /// tampering. + /// </remarks> + public IDictionary<string, string> GetUntrustedCallbackArguments() { + var args = new Dictionary<string, string>(); + + // Return all the return_to arguments, except for the OpenID-supporting ones. + // The only arguments that should be returned here are the ones that the host + // web site adds explicitly. + foreach (string key in this.response.GetReturnToParameterNames().Where(key => !OpenIdRelyingParty.IsOpenIdSupportingParameter(key))) { + args[key] = this.response.GetReturnToArgument(key); + } + + return args; + } + + /// <summary> /// Tries to get an OpenID extension that may be present in the response. /// </summary> /// <typeparam name="T">The type of extension to look for in the response message.</typeparam> |