Craig Box's journeys, stories and notes...


Small treats

March 22nd, 2008

Split EnzGeoff points out to me that Split Enz are playing in Auckland, a week from today. I'm not immeasurably jealous, having seen them with Ewan in 1999 (help - that's almost 10 years ago!), but with any band formed in the 70s there's always the chance that it's the last time you could ever see them regroup. Unless it's Elton John, of course. He's bionic, and there's less of him to try and get in one place at once.

One of the good things about living in New Zealand is that occasionally you can see a little private gig from a Finn brother that fans in the US would kill for. In September, I traded that for the land of Celine Dion and apologies. But the treats do sometimes come.

Dan AykroydI took a train downtown for a job interview last September, and was reading the Metro1 when I was alerted to the fact that Steven Page from Barenaked Ladies (another of my all-time favourite bands) was playing a free show, in a concourse2 downtown, that lunchtime. Wow.

The show was organised by the LCBO, the Ontario liquor control agency3, who were launching a campaign for Ontario wine. The attractions were Page and Dan Aykroyd, one-time Blues Brother and now winery investor.

Steven PageAykroyd gave a little talk before Page came out, accompanied by his guitar. He played a five-song set, including starting Old Apartment twice (it was meant to be Brian Wilson, but you know, musicians like to write the same song over and over again, and sometimes get confused). He was then joined by Aykroyd on harmonica and vocal for some 12-bar blues.

Press took pictures, wine was imbibed (including free samples for the audience) and Aykroyd was on hand to sign bottles. I didn't care much for his unfortunately, but I believe there were more varieties on the way. Living in NZ, our standards for wine and food are pretty high!

(See photos of Steven Page and Dan Aykroyd at the LCBO VQA launch.)

Later on I learned from the same paper that Slash would be at a book signing in a week's time but unfortunately I couldn't make it to that.

  1. A giveaway paper targeted primarily at commuters. 
  2. Fancy word for "indoor shopping mall" 
  3. Government controlled: think West Auckland's Portage and Waitakere Trusts. That bureaucracy once ID'd Jamie when he, at age 28 (and lets face it, just a little bald) went in to buy a bag of ice. 

In which Craig becomes Sandwich Maker

March 22nd, 2008

Omelette for dinner. Verdict: it would have been better if the fillings were cooked into the egg, rather than just sprinkled on. Otherwise, the suggestions about flipping were spot-on. A smaller pan would probably have made a nicer (fatter) omelette.

What is particularly of note though, is if you butter two pieces of toast, add avocado and garlic mayonnaise, and place leftover omelette betwixt, you have a breakfast sandwich fit for a king.

More on Windows service permissions

March 12th, 2008

A while ago I wrote about the permission required to query the status of a Windows service.

Today, I started looking for a way to set this permission from the .NET framework, without using sc or subinacl. Registry and file system security is easy; services, not as much. After what seemed like an eternity of searching I came across the answer - an article on 'stackenbloggen'1 on setting ACLs on Windows services. There is magic in System.Security.AccessControl in .NET 2.0 which lets you control security on pretty much any Windows object, and here are some classes to expose it.

Words can be very specific in the English language, and even more so in code. For example, a method called "SetAccessControl" does not do what it would do if it was called "AddAccessControlEntry". So, if you create an ACE that contains just the item you want to add, and run SetAccessControl, you're screwed.

Never mind, I wrote about how to fix this:

C:\Craig\>sc sdset Service1 D:(A;;CCLS...)
[SC] OpenService FAILED 5:Access is denied.

Oops.

Maybe I'll get a better message out of subinacl?

C:\Craig\>subinacl /service Service1 /sacl=D:(A;;CCLS...)
Service1 - OpenService Error : 5 Access is denied.

Double oops.

New error message though, worth searching for:

OpenService Error 5 Access is Denied - Google results

It's very irritating to only find your own post when searching for something! Lots of old-school #wlug'ers used to get this all the time with the WLUG wiki.

A bit more Googling, and the answer is found in Microsoft's knowledge base: how to reset a DACL on a Windows service. The crux of the matter is you need to run a command prompt as LocalSystem - type something like at 15:37 cmd.exe /interactive, where 15:37 is the time one minute from now. (Don't make it bigger. Waiting 30 seconds is tedious enough!)

C:\WINDOWS\system32>whoami
NT AUTHORITY\SYSTEM

Booyah!

C:\WINDOWS\system32>sc sdset Service1 D:(A;;CCLS...)
[SC] SetServiceObjectSecurity SUCCESS

Double booyah!

I'll be calling GetAccessControl and adding my own ACE to its result from now on, I think.

  1. I love the name 'stackenbloggen', but a disclaimer that says "[..] my own personal opinions and do not represent my employer's view in any way" is meaningless if you don't say who you are on your blog! 

Welcome back

February 11th, 2008

Our welcome back.

Today

Back in NZ

January 29th, 2008

RaglanI should probably have posted this two weeks ago, but hey, I arrived in NZ two weeks ago, Fern shortly after.

Synopsis: The weather is a bit nicer here than Kitchener. Ask me about my fifteen minutes of famous-person story. Jamie and Tammy are married now. And no sooner than it was here, it was gone - we are leaving again on Friday or Saturday.

Not long after we arrived, we bumped into a couple of friends from a customer of my old work, who are apparently quite regular blog checkers! Hi, Juliette and Yonita, and hope you know lots about debugging IIS now. If anyone else reads this the old-fashioned way, there's a link to the right of this post that reads "Have updates sent by e-mail". Click it, or the link in the previous sentence, and enter your e-mail address for a totally spam-and-advertising-less copy of each post, in your inbox, as I write it.

Which is, obviously, once every not often enough.

You can also use a news-reader and RSS, which stands for Rodeo Style Syndication something geeky, but is neatly explained in very plain Queen's English in this here video:

I promise to do more on our return to Canada to make for interesting reading. In the meantime, a friend of mine from improv in Toronto writes online a fair bit, and even represented bloggers in Canada's televised IQ test "Test the Nation" recently. Even though bloggers are smarter than the average Canadian, I don't really want to be one. I just write stuff down so the Internet knows about it.

Debugging w3wp.exe and IIS application pools

December 29th, 2007

Problem: after hitting "Login" on an ASP.NET web application on IIS6, w3wp.exe races to high CPU usage, and about 70MB of memory. CPU usage falls off, but memory usage increases a few KB every second. No indication is made that the application has crashed or hung, no output.

The first utility I installed to check this out was IISTracer. Ignore the horrible web site - this is a nice little utility which lets you see what requests IIS is currently serving. In this instance the default.aspx page calls a dashboard.aspx, which was timing out indefinitely without returning a response to the client.

There are a number of utilities you can use to debug IIS faults. David Wang's guide on how to understand and diagnose an application pool crash gives some insight into how it should be done - attach a debugger and wait for the fault, then look at the stack trace. His suggestions include IISState, an IIS resource kit utility, which will give you an output of the current state of an IIS process; and DebugDiag, a much nicer and newer interactive debugger for IIS.

I spent a couple of days playing with DebugDiag, and unfortunately, due to the nature of my problem, I couldn't get anything useful. It seems that the application wasn't ever actually crashing or hanging - states that DebugDiag should be able to deal with easily. IISState, on the other hand, suggested I was regularly getting first chance exceptions thrown. A first chance exception means an exception has been raised - if the application handles it, then nothing more is mentioned, else it becomes a second chance exception, and the debugger or system exception handler is called). So, more digging was required.

WinDbg, the Windows DebuggerEnter the Windows debug tools, and specifically WinDbg. This is what the professionals use, and as such, it's ultimately powerful and incredibly scary. It's updated regularly, except for the interface, which is nicest described as "circa-1990".

There are a number of blogs that mention running WinDbg, but none that actually put together all the pieces on how to debug this kind of problem. So here's my story for you.

First, attach the debugger to your w3wp.exe process. I found it's process ID in Task Manager - you need to add the PID column. At this point, I get a breakpoint hit - you might not, so use Debug/Break or Ctrl-Break to stop the program.

Debugging symbols let you see not just the memory location of a failure, but the name of the function that it is in. They are not normally included with release software as they make it a lot larger, but can be downloaded separately in this case. DebugDiag is friendly and will automatically download appropriate debugging symbols for you from Microsoft's symbol server, and put them in c:\symcache\. WinDbg is less friendly and requires you set this up yourself. I used the same path, as I already had some symbols downloaded - I told WinDbg to look at the local symbols first, and look at the online symbol server if they're not already cached there. Do this like so:

.sympath SRV*c:\symcache*http://msdl.microsoft.com/download/symbols

Now, we need to load the SOS debugger helper, which lets you debug managed code in WinDbg (Nerd note: SOS stands for Son of Strike, which is a Microsoft in-joke.) If you started the process inside the debugger, you can apparently do this:

0:033> .loadby mscorlib sos
Unable to find module 'sos'

I couldn't, so I'll do this:

0:033> .load C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\SOS.dll

If you're running .NET 1.1, you can .load clr10\sos.dll for the version shipped with the debugger.

Now, we instruct it to break whenever there is an exception thrown:

0:033> sxe CLR

And hit Debug/Go, type 'g and hit Enter, or hit F5 to resume the program.

No sooner than I hit 'g', we get results!

0:033> g
(a78.8b4): CLR exception - code e0434f4d (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0230ec7c ebx=000d9c50 ecx=00000000 edx=00000024 esi=0230ed08 edi=e0434f4d
eip=77e4bee7 esp=0230ec78 ebp=0230eccc iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
kernel32!RaiseException+0x3c:
77e4bee7 5e              pop     esi

I don't speak assembly and really have no desire to. Thankfully the SOS extension lets us look at the exception, and the call stack (the list of functions that we called to get where we are). Type !pe to print the exception, and !clrstack to see the call stack.

0:012> !pe
Exception object: 048f8a4c
Exception type: System.Data.SqlClient.SqlException
Message: Cannot open database requested in login 'R622P1Sy1'. Login fails.
Login failed for user 'admin'.
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131904
0:012> !clrstack
OS Thread Id: 0x8b4 (12)
ESP       EIP
0230ed54 77e4bee7 [HelperMethodFrame: 0230ed54]
0230edf8 6539bf14 System.Data.SqlClient.SqlInternalConnection.OnError(System.Data.SqlClient.SqlException, Boolean)
0230ee0c 652f695d System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(System.Data.SqlClient.TdsParserStateObject)
0230ee3c 652f79e5 System.Data.SqlClient.TdsParser.Run(System.Data.SqlClient.RunBehavior, System.Data.SqlClient.SqlCommand, System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.BulkCopySimpleResultSet, System.Data.SqlClient.TdsParserStateObject)
0230ee90 652e932a System.Data.SqlClient.SqlInternalConnectionTds.CompleteLogin(Boolean)

And there's the problem. A first-class exception is handled trying to connect to a database with incorrect login details, so it continues, probably never freeing the memory from the last connection. This particular exception doesn't get logged and doesn't get displayed on screen (probably because it's being loaded in the background) and causes an infinite timeout. Oops!

Armed with a little knowledge about the debugging tools, it's easy to see what's going on inside your code, even if it's running in IIS.

Niagara Falls

December 24th, 2007

Winter really isn't the time to visit, although I was advised it was worthwhile because you'd still see the mist.  It takes a fairly long and determined walk to get to it from the car park.

Niagara Falls - In The Dark

The shiny building is in the US of A, who really got shafted by the river.  The falls can really only be seen from the Canadian side.

Retrospectively, it might have been a bad weekend to go...

img_3214-small.jpg

I believe this used to be a thriving butterfly conservatory. Not today.

Oh well. We had a nice stay at the Sheraton Fallsview (with a great view of the mist) and can tick it off the list until summer, where it will require renewed attention.

We also stopped off at the thriving metropolis of Hamilton, Ontario, on the (very slow) drive home. This is the central mall on the Sunday afternoon - but I guess you can expect it to be quiet. After all, it's only 9 days until Christmas...

Hamilton, a mecca of industry.

Parsing quotes in cmd.exe

December 24th, 2007

The Microsoft world can be horribly inconsistent. Take escaping a quote character:

  • "" in VB and SQL Server
  • \" in C++/C#
  • Unless you use a @ symbol first, in which case you have to use "" as \ won't be interpreted as an escape character

It never seemed at all you could escape characters on a command line. Until, of course, you go to do something like this:

rem xcopy is old, let's not use it any more
rem the backslash on the end of bar tells xcopy
rem that it's a directory, not a file, it's copying to
rem
rem xcopy /e "C:\foo\" "C:\bar\"
robocopy "C:\foo" "C:\bar\" /e /x "Extra Options"

Whereupon the much newer program parses the output as follows:

source=C:\foo
destination=C:\bar" /e /x "Extra Options"

This is a problem with anything that uses the .NET framework also. Watch your trailing slashes!

C#: How to tell if an IIS metabase property is inherited

December 24th, 2007

How can you tell, using managed code, if an IIS metabase property is inherited?

Scenario: I am using System.DirectoryServices to manage the IIS configuration (known as the "metabase", and stored in an XML file). I want to remove a property on a metabase node, but only if it is explicitly set here. This allows me to inherit the correct value from up the tree.

First attempt:

if (dirEntry.Properties["AnonymousUserName"].Count > 0) {
    dirEntry.Properties["AnonymousUserName"].Clear();
    Console.WriteLine("Log: Local value found for AnonymousUserName, removing");
}

My assumption was that this would remove the entry the first time, if it exists, and ignore it every time afterwards. However, every time it runs, Count is 1 and the property appears set.

It turns out that the Properties method on a DirectoryEntry object shows all the properties, including ones that are set on parent items in the tree.

How can we see only items that are defined on this particular node? This problem shows up all over the Internet with no solution. However, Microsoft's David Wang isn't one to leave a newsgroup question unanswered:

Never tried it in .Net, but with ADSI you can determine this.
Set objNode = GetObject( "IIS://foo/W3SVC" & "/" & "AppPools/AnAppPool" )
Set objNodeAttribute = objNode.GetPropertyAttribObj( "WamUserName" )
If ( objNodeAttribute.IsInherit = true ) Then
End If

I just needed a way to run GetPropertyAttribObj from C#, which was a much easier problem to solve. There were only a couple of relevant hits, the best one being some abandoned Pastebin code in Google's cache.

We now have an answer:

/// <summary>
/// Establish if a metabase property is set explicitly on this node.
/// </summary>
/// <param name="iisNode">Metabase node</param>
/// <param name="propertyName">Property name</param>
/// <returns>True if the property is defined on this node explicitly; false if the property is not defined at all, or is inherited.</returns>
private static bool PropertyDefinedExplicitly(DirectoryEntry iisNode, String propertyName)
{

    // check 1. this node has a valid key-type, and 2. it contains the required property
    if (iisNode.Properties.Contains("KeyType") &&
    iisNode.Properties["KeyType"].Value.ToString().Length > 0 &&
    iisNode.Properties.Contains(propertyName))
    {

        // call ADSI GetPropertyAttribObject method directly
        Object propertyAttributes = iisNode.Invoke("GetPropertyAttribObj",
        new String[] { propertyName });
        IISOle.IISPropertyAttribute attributes =
        (IISOle.IISPropertyAttribute)propertyAttributes;

        // Isinherit will be set if the property is inherited
        if (attributes.Isinherit)
        {
            return false; // property was inherited
        }
        else
        {
            return true; // property explicitly defined on this node
        }

    }
    else
    {
        return false; // not a valid IIsObject node, or property not found
    }
}

We then rewrite the original function like so:

if (PropertyDefinedExplicitly(dirEntry, "AnonymousUserName")) {
    dirEntry.Properties["AnonymousUserName"].Clear();
    Console.WriteLine("Log: Local value found for AnonymousUserName, removing");
}

There is a lesson to learn here for Windows programmers: Microsoft support is in the newsgroups. Don't fear Usenet because it's older than you are!

With useful tips like this, I'll win the NZ .NET blog of the year for sure!

Querying the status of a Windows service

December 7th, 2007

An interesting puzzle presented itself to me at work yesterday. We have some code called from an ASP.NET application, which queries the status of a service. This works fine when you run the application pool as NetworkService, but if you run it as the IWAM_SERVERNAME user that is created for running IIS processes, it would fail.

I wrote a console application with exactly the same code that the ASP.NET application used, and it worked fine.

Interesting aside: you can find the password for the IWAM_SERVERNAME user by using the Metabase Explorer from the IIS 6 Resource Kit. It's under the Server/LM/W3SVC node as WAMUserPass, and you have to enable "View/Secure Data" to see it. Then, use runas /user:iwam_servername cmd.exe, and enter in the password from the metabase.

Why? Well, surely it must just be some kind of permissions issue, not the Winter Madness.

What can't be done with subinacl isn't worth doing, so I pulled it out and had a look at the service. Here's the important part:

/pace =interactive      ACCESS_ALLOWED_ACE_TYPE-0x0
        SERVICE_QUERY_CONFIG-0x1           SERVICE_QUERY_STATUS-0x4           SERVICE_ENUMERATE_DEPEND-0x8
        SERVICE_INTERROGATE-0x80           READ_CONTROL-0x20000               SERVICE_USER_DEFINED_CONTROL-0x0100

(People finding this useful post via search will want to read the full subinacl output.)

Turns out an interactive user can query a server status, but IIS doesn't run interactively. Assigning permission to the IWAM account is the only way to do this:

subinacl /service Service1 /grant=IIS_WPG=s

Problem solved for the web app.

Always one for complete proof, I tested removing interactive access (subinacl /service Service1 /deny=interactive=s). Result as requested - the console app now throws the same exception the web application does.

But wait...

C:\Program Files\Windows Resource Kits\Tools\>subinacl /service Service1 /revoke=interactiveService1
- OpenService Error : 5 Access is denied.

Oops. Big oops. Nothing I can do now seems to be able to let me fix this, because I've lost access to query the status of the server myself.

Some searching led me to an answer: use the sc.exe utility, which lets you control services from the command line. It also lets you set their security descriptors!

C:\Program Files\Windows Resource Kits\Tools\>sc sdshow Service1 
D:(D;;LC;;;IU)(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;SU)(A;;CR;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)

C:\Program Files\Windows Resource Kits\Tools\>sc sdshow Service2
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CR;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)

C:\Program Files\Windows Resource Kits\Tools\>sc sdset Service1 D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CR;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
[SC] SetServiceObjectSecurity SUCCESS

C:\Program Files\Windows Resource Kits\Tools\>subinacl /service Service1
=========================
+Service Service1
=========================
<snip>

Whew. See how the SDs differ between the two services? Ignore the gobbledy-gook that is SDDL, and just copy an example from another similar service.

I suspect I could have granted access using with sc instead of subinacl, but the IWAM_SERVERNAME account and IIS_WPG group don't have well-known SIDs, so I would have had to find out the SID using another utility anyway.

(Hi Mum. This is what I do all day.)