You have an ASP.NET application running on your Windows Server 2003 and you’re merrily logging events to the Application Event Log (or any event log for that matter). Then you upgrade to Windows Server 2003 SP1 and see something like this:
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.
Exception Details: System.Security.SecurityException: Requested registry access is not allowed.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[SecurityException: Requested registry access is not allowed.]
Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean writable) +473
System.Diagnostics.EventLog.CreateEventSource(String source, String logName, String machineName, Boolean useMutex) +443
System.Diagnostics.EventLog.WriteEntry(String message, EventLogEntryType type, Int32 eventID, Int16 category, Byte[] rawData) +348
System.Diagnostics.EventLog.WriteEntry(String message, EventLogEntryType type, Int32 eventID, Int16 category) +21
AspNetTest.WebForm1.Page_Load(Object sender, EventArgs e) in c:\dev\aspnettest\webform1.aspx.cs:23
System.Web.UI.Control.OnLoad(EventArgs e) +67
System.Web.UI.Control.LoadRecursive() +35
System.Web.UI.Page.ProcessRequestMain() +2112
System.Web.UI.Page.ProcessRequest() +218
System.Web.UI.Page.ProcessRequest(HttpContext context) +18
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication+IExecutionStep.Execute() +179
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +87
What happened? The problem is caused by Microsoft tightening up security around the event logs in Windows Server 2003 SP1. (See the section entitled “Tighter ACLs on Event Logs”.)Tightening security is a good idea, but logging events to the event log from an ASP.NET application is a rather common thing to do. So how do we re-enable the ability of ASP.NET to write to the event log?
I have seen information on the web, the sources of which shall remain nameless to protect the seriously misled, that basically tells you to modify your ASP.NET process identity in machine.config from machine to system so that your application can write to the event log. What a collosally bad idea! What’s so bad about this? Imagine that you’re going away for holidays and you ask a friend to water your plants. You realize that your friend cannot open the locked front door. So what do you do? You leave the door unlocked for two weeks while you’re away. That way your friend can get in and water the plants. Does this sound reasonable to you? Of course not. You give your friend a key to get into the house.
So how do you give ASP.NET a key to the event log? I’m glad you asked. There is a custom security descriptor on each event log that determines which users can access that particular event log. Where is this custom security descriptor stored? In the registry of course. So fire up regedt32 and browse to the following registry key:
HKLM\SYSTEM\CurrentControlSet\Services\EventLog\{logName}\CustomSD
where {logName} is Application in our case. You should see a string value that looks like:
O:BAG:SYD:(D;;0xf0007;;;AN)(D;;0xf0007;;;BG)(A;;0xf0007;;;SY)(A;;0x7;;;BA)(A;;0x7;;;SO)(A;;0x3;;;IU)(A;;0x3;;;SU)(A;;0x3;;;S-1-5-3)
What the heck is this? It’s Security Descriptor Description Language (SDDL), my dear Watson. SDDL is a text-based (and human authorable, believe it or not) representation of a security descriptor.
What does it mean in simple terms? The first part is some general information about the security descriptor:
O:BA |
Object owner is built-in administrators |
G:SY |
Primary Group is SYSTEM |
D: |
This is a discretionary access control list (DACL) as opposed to a SACL or audit entry |
Next come the access control entries (ACE). I’ll only talk about the fields needed for the custom security descriptor on the event logs. You can find full information about the ACE format here. First is allow (A) or deny (D). Hex number in the middle is a permissions bitmask:
0x1 |
READ |
0x2 |
WRITE |
0x4 |
CLEAR |
0xf000 |
DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER |
Last comes the SID, which has abbreviated forms for most common built-in accounts.
So here are the ACEs:
(D;;0xf0007;;;AN) |
Deny everything to Anonymous |
(D;;0xf0007;;;BG) |
Deny everything to Builtin Guests |
(A;;0xf0007;;;BA) |
Allow everything to Builtin Administrators |
(A;;0x7;;;SO) |
Allow read, write, and clear to Server Operators |
(A;;0x3;;;IU) |
Allow read and write to Interactive Users |
(A;;0x3;;;SU) |
Allow read and write to Service Accounts* |
(A;;0x3;;;S-1-5-3) |
Allow read and write to Batch Accounts* |
* These two are not an actual groups, but are flags in the security token, which can be configued by local or group security policy.
You’ll notice that Network Service isn’t mentioned anywhere on that list. Simply add (A;;0x3;;;NS) to the end of the list and Network Service will be permitted read and write access to the event log. Note that if you are using impersonation (<identity impersonate=”true” /> in your web.config), the event log access will be done under the credentials of the impersonated user. In this case, you’ll want to permit authenticated users read and write access, which can be done using (A;;0x3;;;AU). You should now be merrily writing to the event logs from your ASP.NET applications with your security intact.
Afterword: After writing this blog post, I discovered that Dominick Baier has a blog entry about the same problem. Dominick is extremely knowledgeable about all things security on the Windows platform. If you haven’t subscribed to his RSS feed, do so now!
Edit: I originally attributed the information on http://www.leastprivilege.com to Keith Brown. I feel so embarassed. Keith’s blog is http://www.pluralsight.com/blogs/Keith. Both Keith and Dominick are definitely Windows security gurus. I highly recommend subscribing to both of their blogs! Sorry, guys!
How to Reproduce this Error
To reproduce the problem, create a new event source called “AspNetTest” that can write to the local Application log by running the following code in a console app as a local administrator:
string sourceName = “AspNetTest”;
string logName = “Application”;
string machineName = “.”;
if(!EventLog.SourceExists(sourceName, machineName)) {
EventLog.CreateEventSource(sourceName, logName, machineName);
}
Create an ASP.NET web application using the wizard and add the following two lines to Page_Load of WebForm1:
private void Page_Load(object sender, System.EventArgs e) {
EventLog log = new EventLog(“Application”, “.”, “AspNetTest”);
log.WriteEntry(“Writing from AspNetTest application”, EventLogEntryType.Information, 42, 42);
}
When run on Windows Server 2003 SP1, you will see a security exception like the one above. Modify the CustomSD on the Application event log and browse to WebForm1.aspx again. The web application will successfully write into the event log, which you can verify using the Event Viewer.