ASP.NET security exception with OpenWebConfiguration on shared host - asp.net

After moving my web site from my local development environment to a shared host I get:
Security Exception
Description: The application attempted to perform an operation not allowed by
the security policy. To grant this application the required permission please
contact your system administrator or change the application's trust level in
the configuration file.
The problem occurs in my web application everywhere the following is called:
WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath)
Since my web application is only trying to open it's own web.config file, I don't know why this is flagged as a security exception. Maybe someone can explain... But more importantly I need a solution, the couple solutions I found via Google are painful.
One solution (from numerous posts) said to configure the trust level to Full, but I'm told that is not possible on my shared host.
Another solution (from https://web.archive.org/web/20210525032809/http://www.4guysfromrolla.com/articles/100307-1.aspx) says to not use OpenWebConfiguration(), but I need to use it to encrypt configuration sections (e.g. connectionStrings) using DPAPI (for more info see https://web.archive.org/web/20211020203213/https://www.4guysfromrolla.com/articles/021506-1.aspx).
Please advise on why IIS barfs on my web application trying to open it's own web.config, and a work-around to be able to encrypt parts of the web.config using DPAPI.

I have had experience of this issue in the past. The OpenWebConfiguration() method also reads the machine.config file. Under partial trust and without the correct permissions you can't use this method.
If you were to step into the .NET Framework assemblies with your debugger in Visual Studio 2008/2010 you can see exactly what is happening.
The following is a call stack captured when stepping into WebConfigurationManager.OpenWebConfiguration():
mscorlib.dll!System.IO.FileStream.Init(string path = "C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727\\Config\\machine.config", System.IO.FileMode mode = Open, System.IO.FileAccess access = Read, int rights = 0, bool useRights = false, System.IO.FileShare share = Read, int bufferSize = 4096, System.IO.FileOptions options = None, Microsoft.Win32.Win32Native.SECURITY_ATTRIBUTES secAttrs = null, string msgPath = "machine.config", bool bFromProxy = false) Line 326 C#
mscorlib.dll!System.IO.FileStream.FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) Line 259 C#
System.Configuration.dll!System.Configuration.Internal.InternalConfigHost.StaticOpenStreamForRead(string streamName) + 0x56 bytes
System.Configuration.dll!System.Configuration.Internal.InternalConfigHost.System.Configuration.Internal.IInternalConfigHost.OpenStreamForRead(string streamName, bool assertPermissions) + 0x7d bytes
System.Configuration.dll!System.Configuration.Internal.InternalConfigHost.System.Configuration.Internal.IInternalConfigHost.OpenStreamForRead(string streamName) + 0xb bytes
System.Configuration.dll!System.Configuration.Internal.DelegatingConfigHost.OpenStreamForRead(string streamName) + 0xe bytes
System.Configuration.dll!System.Configuration.UpdateConfigHost.OpenStreamForRead(string streamName) + 0x2f bytes
System.Configuration.dll!System.Configuration.BaseConfigurationRecord.InitConfigFromFile() + 0x126 bytes
System.Configuration.dll!System.Configuration.BaseConfigurationRecord.Init(System.Configuration.Internal.IInternalConfigRoot configRoot, System.Configuration.BaseConfigurationRecord parent, string configPath, string locationSubPath) + 0xaa5 bytes
System.Configuration.dll!System.Configuration.MgmtConfigurationRecord.Init(System.Configuration.Internal.IInternalConfigRoot configRoot, System.Configuration.Internal.IInternalConfigRecord parent, string configPath, string locationSubPath) + 0x39 bytes
System.Configuration.dll!System.Configuration.MgmtConfigurationRecord.Create(System.Configuration.Internal.IInternalConfigRoot configRoot, System.Configuration.Internal.IInternalConfigRecord parent, string configPath, string locationSubPath) + 0x2a bytes
System.Configuration.dll!System.Configuration.Internal.InternalConfigRoot.GetConfigRecord(string configPath) + 0x12d bytes
System.Configuration.dll!System.Configuration.Configuration.Configuration(string locationSubPath, System.Type typeConfigHost, object[] hostInitConfigurationParams) + 0xfd bytes
System.Configuration.dll!System.Configuration.Internal.InternalConfigConfigurationFactory.System.Configuration.Internal.IInternalConfigConfigurationFactory.Create(System.Type typeConfigHost, object[] hostInitConfigurationParams) + 0x1e bytes
System.Web.dll!System.Web.Configuration.WebConfigurationHost.OpenConfiguration(System.Web.Configuration.WebLevel webLevel, System.Configuration.ConfigurationFileMap fileMap, System.Web.VirtualPath path, string site, string locationSubPath, string server, string userName, string password, System.IntPtr tokenHandle) Line 862 C#
System.Web.dll!System.Web.Configuration.WebConfigurationManager.OpenWebConfigurationImpl(System.Web.Configuration.WebLevel webLevel, System.Configuration.ConfigurationFileMap fileMap, string path, string site, string locationSubPath, string server, string userName, string password, System.IntPtr userToken) Line 77 + 0x1c bytes C#
System.Web.dll!System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(string path) Line 140 + 0x25 bytes C#
Unfortunately your only alternative is to use WebConfigurationManager.GetSection() which isn't as feature rich.
With regard to encrypting your connection strings. Sadly this feature demands Full Trust, there's no other way around it.

Related

Create file in Xamarin Cross Platform App Development

I'm new to Xamarin. I'm trying to create a file using code
string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string filename = Path.Combine(path, "myfile.txt");
using (var streamWriter = new StreamWriter(filename, true))
{
streamWriter.WriteLine(DateTime.UtcNow);
}
using (var streamReader = new StreamReader(filename))
{
string content = streamReader.ReadToEnd();
System.Diagnostics.Debug.WriteLine(content);
}
The above code sample is giving me exception
System.UnauthorizedAccessException: 'Access to the path 'C:\Users\hchittora\Documents\myfile.txt' is denied.'
Here is the full stacktrace
System.UnauthorizedAccessException
HResult=0x80070005
Message=Access to the path 'C:\Users\hchittora\Documents\myfile.txt' is denied.
Source=System.Private.CoreLib
StackTrace
at System.IO.FileStream.ValidateFileHandle(SafeFileHandle fileHandle)
at System.IO.FileStream.CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize)
at System.IO.StreamWriter..ctor(String path, Boolean append)
at FileHandlingSampleApplication.MainPage..ctor() in C:\Users\hchittora\Desktop\FileHandlingSampleApplication\FileHandlingSampleApplication\FileHandlingSampleApplication\MainPage.xaml.cs:line 19
at FileHandlingSampleApplication.App..ctor() in C:\Users\hchittora\Desktop\FileHandlingSampleApplication\FileHandlingSampleApplication\FileHandlingSampleApplication\App.xaml.cs:line 16
at FileHandlingSampleApplication.UWP.MainPage..ctor() in C:\Users\hchittora\Desktop\FileHandlingSampleApplication\FileHandlingSampleApplication\FileHandlingSampleApplication.UWP\MainPage.xaml.cs:line 24
at FileHandlingSampleApplication.UWP.FileHandlingSampleApplication_UWP_XamlTypeInfo.XamlTypeInfoProvider.Activate_4_MainPage() in C:\Users\hchittora\Desktop\FileHandlingSampleApplication\FileHandlingSampleApplication\FileHandlingSampleApplication.UWP\obj\x86\Debug\XamlTypeInfo.g.cs:line 255
at FileHandlingSampleApplication.UWP.FileHandlingSampleApplication_UWP_XamlTypeInfo.XamlUserType.ActivateInstance() in C:\Users\hchittora\Desktop\FileHandlingSampleApplication\FileHandlingSampleApplication\FileHandlingSampleApplication.UWP\obj\x86\Debug\XamlTypeInfo.g.cs:line 476
When I looked at the above issue, I come to know that UWP cannot directly interact with the system files. What can be another alternative to do so?
Because Xamarin.Forms runs on multiple platforms, each with its own file system, there is no single approach for loading and saving files created by the user.
I can not provide code for all platform here because you need to understand the fundamental for it. Check this for more details.
I'm using SecureStorage library with dependency injection to save on each platform. Although this is mostly used for token caching, you might find it useful.

Access denied error using asymmetric binding in web host server - .NET

I'm getting an Access denied error when I attempt to make a web service request. The web service requires an asymmetric security binding. The call is made via a webpage that references a .NET dll that references the web service. All of this works fine on our server but we loaded it up at a web hosting company we got hit with this error.
My theory is that whatever the GetKeyPairHelper method does it requires the ability to write a file to do it while we don't have access to the location it tries to write to. The support people at this company ran a trace for us and these were the last two lines:
06:03.1 w3wp.exe 5860 CreateFile C:\ProgramData NAME COLLISION >Desired Access: Read Data/List Directory, Synchronize, Disposition: Create, Options: >Directory, Synchronous IO Non-Alert, Open Reparse Point, Attributes: N, ShareMode: Read, >Write, AllocationSize: 0
06:03.1 w3wp.exe 5860 CreateFile C:\ProgramData ACCESS DENIED >Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, >Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a
Looks like something trying to write to C:\ProgramData but what? And why? And how do I get around it? I'm hoping there is some way to configure this so it doesn't have to write a file. Perhaps this job can be performed in memory or directed to an area we have access to.
Stacktrace:
Error: Access is denied. : Server stack trace:
at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)
at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()
at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)
at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()
at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.get_PrivateKey()
at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.GetSignatureFormatter(String algorithm)
at System.IdentityModel.SignedXml.ComputeSignature(SecurityKey signingKey)
at System.ServiceModel.Security.WSSecurityOneDotZeroSendSecurityHeader.CompletePrimarySignatureCore(SendSecurityHeaderElement[] signatureConfirmations, SecurityToken[] signedEndorsingTokens, SecurityToken[] signedTokens, SendSecurityHeaderElement[] basicTokens)
at System.ServiceModel.Security.SendSecurityHeader.CompleteSignature() at System.ServiceModel.Security.SendSecurityHeader.CompleteSecurityApplication()
at System.ServiceModel.Security.SecurityAppliedMessage.OnWriteMessage(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.Message.WriteMessage(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.Message.WriteMessage(XmlWriter writer)
at CustomMessageEncoder.CustomTextMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset)
in C:\xxxxx\CustomEncoders\CustomTextMessageEncoder.cs:line 86
Code:
public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
MemoryStream stream = new MemoryStream();
XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
message.WriteMessage(writer); //'line 86' in stacktrace.
writer.Close();
byte[] messageBytes = stream.GetBuffer();
int messageLength = (int)stream.Position;
stream.Close();
int totalLength = messageLength + messageOffset;
byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
Array.Copy(messageBytes, 0, totalBytes, messageOffset, messageLength);
//maybe parse message around here...
ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, messageLength);
return byteArray;
}
What happened here is that the hosting company was hosting our site on a shared server and so rightfully denied access to the C:\ProgramData folder which, further down the directory contains C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys which is the folder you need access to in order to use certificates based security. Their solution for us was to allow access to this folder for our app pool only. Works like a charm.

"Unable to validate data" using FormsAuthentication between different web apps

I have two .NET web applications running on the same server - sentinel (hosted at https://sentinel.mydomain.com/) and fortknox (at http://www.mydomain.com/fortknox)
Sentinel is an authentication 'portal'. FortKnox is a 'proof of concept' app that uses forms authentication but has the loginUrl set to https://sentinel.mydomain.com/login (along with a special Application_EndRequest handler to qualify the ReturnUrl). Sentinel is written in .NET 4.0 using MVC 4 and Razor; FortKnox is ASP.NET MVC 2 using .NET 2.0.
I'm using ASP.NET FormsAuthentication with the cookie domain set to .mydomain.com so that cookies set by sentinel.mydomain.com will be sent with requests to www.mydomain.com, and vice versa. The cookie part is working perfectly - both applications are getting the same .ASPXAUTH encrypted forms ticket.
The problem is that, on our production servers, fortknox can't decrypt cookies created by sentinel - even though they have identical machine keys. Even when both apps are running on the same physical box, it doesn't work.
A user hits fortknox, they're redirected to sentinel, they log in, the cookie is set, they're redirected back to fortknox, and then I get "Unable to validate data":
Exception: Unable to validate data.
at System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo, Boolean signData)
at System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo)
at System.Web.Security.FormsAuthentication.Decrypt(String encryptedTicket)
at FortKnox.Web.MvcApplication.Application_BeginRequest()
The machine keys are identical - I've gone as far as including this chunk of (nasty!) code in the mark-up of each page:
try {
var cookie = Request.Cookies[".ASPXAUTH"].Value;
Response.Write("Cookie: " + cookie + Environment.NewLine);
var ticket = FormsAuthentication.Decrypt(cookie);
Response.Write("Ticket name: " + ticket.Name + Environment.NewLine);
} catch (Exception x) {
Response.Write("Exception: " + x.Message + Environment.NewLine);
Response.Write(x.StackTrace);
}
Response.Write("<hr /></pre>");
var machineConfigMachineKey = (MachineKeySection)WebConfigurationManager.OpenMachineConfiguration().SectionGroups["system.web"].Sections["machineKey"];
var webConfigMachineKey = (MachineKeySection)WebConfigurationManager.OpenWebConfiguration("").SectionGroups["system.web"].Sections["machineKey"];
Response.Write("<pre>");
Response.Write("<b>machine.config decrypt: </b>" + machineConfigMachineKey.DecryptionKey + "<br />");
Response.Write("<b>web.config decrypt: </b>" + webConfigMachineKey.DecryptionKey + "<br />");
Response.Write("<br />");
Response.Write("<b>machine.config validate: </b>" + machineConfigMachineKey.ValidationKey + "<br />");
Response.Write("<b>web.config validate: </b>" + webConfigMachineKey.ValidationKey + "<br />");
Response.Write("</pre>");
Response.Write("<hr />");
and verified that the machine keys being used at runtime are exactly the same.
What's especially frustrating is that this has been working on our development and staging servers, and has only failed in production. The only difference between the servers is that the production boxes have Windows Updates installed frequently whilst our dev/staging boxes are potentially missing some updates; they're otherwise identical (cloned from the same image and created using the same setup scripts)
So... same server; same machine key. ASP.NET 4 sets a FormsAuthentication cookie. ASP.NET 2 app can't decrypt it. Bug only happening on certain servers; on others, it's working. At this point, I'm completely stuck... any ideas?
EDIT: Live server has been brought right up to the latest patch level. I have tried applying
<add key="aspnet:UseLegacyEncryption" value="true" />
as both true AND false, to both the login app and the fortknox app. Still no luck...
Any chance it has something to do with the old 2010 padding oracle security patch - http://weblogs.asp.net/scottgu/archive/2010/09/28/asp-net-security-update-now-available.aspx? Try setting
<add key="aspnet:UseLegacyEncryption" value="true" />
to force the patched servers to act like they used to before the patch?
(or, you know... patch your servers. Your choice.)
Did you try all the following keys? http://support.microsoft.com/kb/2425938
<add key="aspnet:UseLegacyEncryption" value="true" />
<add key="aspnet:UseLegacyMachineKeyEncryption" value="true" />
<add key="aspnet:ScriptResourceAllowNonJsFiles" value="true" />

If a ASMX WebService needs a DLL where should it be put?

I'm using some 3rd party software that's failing. Their API is wrapped in a ASP web service.
The call stack tells me that it needs maybe MySQL.Data.DLL or something (not sure exactly) in the correct directory. So I'm wondering where that directory would be. The Web Server is IIS.
Here is the call stack:
Unable to find the requested .Net Framework Data Provider. It may not be installed.
at System.Data.Common.DbProviderFactories.GetFactory(String providerInvariantName)
at WebReports.Api.Data.SqlObject.CreateConnectionObject(String dbType, String dataConnStr)
at WebReports.Api.Data.SqlObject..ctor(PageInfo pageInfo, Int32 dataSourceId)
at WebReports.Api.Data.DataObjectBase.GetDataObject(PageInfo pageInfo, Int32 dataSourceId, String objectType, Boolean isSqlSpecific)
at WebReports.Api.Reports.Entity.get_DataSource()
at WebReports.Api.Reports.Entity.GetColumnProcess(String colName, Boolean isActual)
at WebReports.Api.Reports.EntityColumnsCollection.GetColumnProcess(String colNameFull, Boolean isActual)
at WebReports.Api.Common.PageInfo.GetMnemonicFromId(String id)
at WebReports.Api.Reports.KeyColumnCollection.SetColumnMnemonics()
at WebReports.Api.Reports.ReportEntityCollection.SetColumnMnemonics()
at WebReports.Api.Reports.ReportEntityCollection.LoadData(DataSet ds, Boolean readSchema)
at WebReports.Api.Reports.Report.get_Entities()
at WebReports.Api.Common.PageInfo.GetMnemonicFromId(String id)
at WebReports.Api.Reports.Cell.set_SaveText(String value)
at WebReports.Api.Reports.ReportCellCollection.LoadData(DataSet ds)
at WebReports.Api.Reports.Report.get_Cells()
at WebReports.Api.Reports.Report.UpdateVersion()
at WebReports.Api.Reports.Report.Validate(Boolean validateJoins)
at WebReports.Api.Reports.Report.LoadData(Boolean validate)
at WebReports.Api.Reports.Report.Load(String reportName)
Above this line is their API, so I can't inspect it:
at eWebReportsLETG.ReportURLService.GenerateReportURL(Int32[] list, String m_szWebReportsVirtualDirectory, String m_szWebReportsUrl, String ReportDir, String ReportName, String PkSpecialName, Boolean& Failed) in C:\dev\eWebReports\eWebReportsLETG\ReportURLService.asmx.cs:line 51
I needed to copy MySQL.data.dll to the bin directory of my web service application directory. I also added the config key to the web.config.

ASP.NET Application variables set to null after being used in a Threadpool

I have a static class in my ASP.NET app that I use to hold an application wide data processing class used to manage long running batch operations sent in by users. This should be a singleton object as it is quite a heavy object.
public static class WebGISGlobals
{
private static MultiStatutoryMapPrintProcessor _batchPrintProcessor;
public static MultiStatutoryMapPrintProcessor BatchPrintProcessor
{
get
{
if (_batchPrintProcessor == null)
{
_batchPrintProcessor = new MultiStatutoryMapPrintProcessor(
(string)ConfigurationManager.AppSettings["statPrintWebServiceUrl"], HttpContext.Current.Server.MapPath("~/downloads"));
}
return _batchPrintProcessor;
}
}
}
This processor class passes the batch tasks to the Threadpool for execution and all works well....
except, when the queue becomes empty and the processes stop the next time the singleton is requested using WebGISGlobals.BatchPrintProcessor to collect the task results the object is null and creates a new instance. This unfortunately loses me reference to the output files it produces.
I have tried using Application["BatchProcessor"] to store my class instance for all to reference but it has the same effect.
Nowhere in my code do I dispose the instance or set it as null so I am stuck.
Anyone have any bright ideas.
UPDATE:
After a suggestion from rsbarro I have run a stack trace on the thread execution and it does not show any exceptions being fired.
> WebFGH.DLL!FGH.Global.Application_End(object sender = {System.Web.HttpApplicationFactory}, System.EventArgs e = {System.EventArgs}) Line 167 C#
[Native to Managed Transition]
[Managed to Native Transition]
System.Web.dll!System.Web.HttpApplication.ProcessSpecialRequest(System.Web.HttpContext context = null, System.Reflection.MethodInfo method, int paramCount, object eventSource, System.EventArgs eventArgs, System.Web.SessionState.HttpSessionState session) + 0x110 bytes
System.Web.dll!System.Web.HttpApplicationFactory.FireApplicationOnEnd() + 0x56 bytes
System.Web.dll!System.Web.HttpApplicationFactory.Dispose() + 0x109 bytes
System.Web.dll!System.Web.HttpRuntime.Dispose() + 0x114 bytes
System.Web.dll!System.Web.HttpRuntime.ReleaseResourcesAndUnloadAppDomain(object state) + 0x35 bytes
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(object state) + 0x2f bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x6f bytes
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(System.Threading._ThreadPoolWaitCallback tpWaitCallBack) + 0x53 bytes
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(object state) + 0x59 bytes
[Appdomain Transition]
If nothing in your code is nulling out _batchPrintProcessor then a possible cause for the behavior you are seeing is that your ASP.NET application is being shut down (or restarted) by IIS. By default, Application Pools in IIS 7.5 are shut down every 20 minutes if there is no activity. You application will also be restarted by ASP.NET/IIS for a number of reasons, including changes to the bin directory, unhandled exceptions on spawned threads, etc.
To check if this is the issue, just add some logging code to the Application_Start and Application_End events in your Global.asax.
See also:
What causes an application pool in IIS to recycle?

Resources