Friday, August 26, 2005

Logon's long gone

Admittedly most people never find the need to write a Winlogon Notification DLL. However, if you were tempted to: stop. Microsoft no longer supports them in LongHorny. Yes, they had a good run beginning with Windows 2000, continuing into Windows XP and Windows 2003 server. But, like many things where Microsoft just up and decides that "those darn developers out there crash our crap too much" they've pulled the plug on Winlogon Notification DLL's. Apparently too many people didn't free their pointers or something and caused Winlogon crashes. GINA's have been pulled too, but I imagine the GINA's of the world will unite in protest - maybe even the Regina's too.

So, if you want to acheive the same results that you used to be able to do with a Winlogon Notification DLL what's a poor developer to do? Never fear, some more difficult code is here! Instead of simply running a CreateProcess with the lpDesktop parameter of the StartupInformation structure set to "Winsta0\Default" like you could in the Notification DLL, you now have to create a service. The service must be setup to handle SERVICE_ACCEPT_SESSIONCHANGE and has a callback to get notified of logons, logoffs, etc. Now, due to some further changes in Windows, services can't easily put things on the user desktop. Simple things like what SMS does - deliver packages to the user desktop running as Local System are now more difficult because if you just launch them on WinSta0\Default they will run on the "services" session and not on the user console.

So how do you get them onto the user desktop?
Well, to quote Microsoft: "It becomes more difficult if you want to start LocalSystem code on the user’s “Default” desktop. That’s actually something that we would strongly discourage because of things like shatter attacks. So if you are calling CreateProcess and specify Winsta0\Default as the desktop, that won’t work as is from a service. It is still possible to do this, but as mentioned, that approach is strongly discouraged for security reasons..". It's always funny to then tell them, but you sell a product called SMS that does this. Anyway, since I still need that functionality I need to write this service. Besides isn't William Shatner getting too old to attack things anymore?

Can we get there from here?
It turns out that you need to do things like create a Security Descriptor (which is no simple task for VB.Net people like me), Duplicate the token, modify the token to be associated with a different session, and then call CreateProcessAsUser using your newly minted token (no, not a fake video game token; just a fake Windows token). All this uses some of the obtuse security API's that most people hope that they never even have to read about, let alone understand. So, I've done that (read about it, don't understand it). The good news is that it doesn't crash. The bad news is that it doesn't do much of anything at all. When debugging it under Vista, it shows "True" for the result codes of all of the calls. In fact, I even get a PID back in the ProcessInformation structure after the call to CreateProcessAsUser. The only problem is that the app never starts, even though I get thread handles, process handles, PID, etc. and a true result code. Boy this was simple before! Now its quite complicated. I'll let you know if I ever get this working. So far this has left me wondering if a shatter attack is what happens to your monitor when you get frustrated writing code for Windows.

Saturday, August 13, 2005

AD, LDAP and the urge to merge

Lately I've been working on code (don't call it a script - this is real code) to "migrate" user profiles to a new domain as part of a merger. Both companies have large Active Directory infrastructures, but one had to be chosen as the "post merger domain". However, it isn't physically possible to re-image everyone's machine the same day and just magically have them all logging on to a new domain as soon as regulatory and stockholder approvals are done. During that interim state that comes after the approvals, but before everyone is in one happy domain - there exists a need for certain tools.

Let's say that a user from company A is has a machine in domain a.company.com. He logs on to that same domain. However, after the merger he wants to logon to company B's domain - b.company.com. Put up the requisite trusts, migrate the groups and accounts with SID History and he can certainly do that, right? But - what happens to his "My Documents" folder, his application configurations and special toolbars, and most importantly of all the pictures of his kids that he uses as a screensaver? Logon to a brand new domain - you get a brand new NT profile, right? All that other stuff is MIA.

Enter profile migration. As I'm sure everyone knows, Windows NT based systems like Windows 2000 and Windows XP store pointers to your profile on disk. Browse to HKLM\Software\Microsoft\Windows NT\CurrentVersion\ProfileList and you'll see what I mean. Each SID shown there is the SID of a user (or system account). Under each is a "ProfileImagePath" that shows where the NTUser.dat (current user registry hive) and application settings, "my documents" etc. live. So, assuming the correct SID History migrations were done, all you need to do is get the NEW SID into the registry here and copy the info from the OLD SID over, reboot the machine and have them login with their new account. Presto! Their ugly kids are still on their desktops! The trick, when working with non-Admin accounts is to actually GET the new sid in there and copy the data over.

First, you need to locate the NEW account. Realize that in a large merger it is very likely that you will hit at least some name collisions between the two domains, so you can't just assume that user A\username should now logon as B\username. You have to find them by sid history since their new account may be named something different. I first tried "LookupAccountSID" since it was a Windows API call that was kinda short (unlike the horrible "ConvertStringSecurityDescriptorToSecurityDescriptorW" - how's that for a verbosity monstrosity, huh?). LookupAccountSid seemed good since it is documented as being able to lookup by primary SID or SID history. However, in practice - since you will need to have a trust up - it finds the old SID back in the old domain, even when you take pains to force the lookup to happen on a DC in the new domain. So, scratch that one.

Next, I thought I'd try LDAP instead. Turns out that LDAP is really only good at searching string data. So, when you have a blob type object like a SID, you have to mangle into a strange looking string first that looks something like this: \00\05\01\EA\... To do this, pass the SID pointer to ADsEncodeBinaryData which will convert it to a "funny string" for use in an LDAP query filter. Now, you can try something like "(ldap://dc=b,dc=company,dc=com");(&(objectClass=User)(sidHistory=" & sSID & "));name,samAccountName,ADsPath;subtree" to lookup that pesky primary SID from domain a.company.com in domain b.company.com. Except - you can't!

Nope, you can't make that query. Remember, the computer is in domain a.company.com, the user is logged on with their old account in a.company.com, and if you try that LDAP query against the domain that trusts yours b.company.com you get a "table does not exist" error. Thanks AD!! Thanks LDAP!! Nope, you end up having to first call DsGetDcName to find a domain controller in the other domain and send the LDAP query directly to it like this: "(ldap://somedomaincontroller.b.company.com);(&(objectClass=User)(sidHistory=" & sSID & "));name,samAccountName,ADsPath;subtree". Now, you've got the ADsPath of the user, you can use GetObject to get that user object, grab off the primary SID of the user, convert it to a string and... Whoops, you don't have Admin rights so you can't just pop that into the registry...

Now, what you have to do is ask the user for their password in the new domain and call CreateProcessWithLogonW to run your code again as the NEW user - passing in the old SID as a string on the command line. (Hopefully your user didn't run your code from a mapped drive: the reason for this is left as an excercise for the reader). Make sure to use the proper parameters to cause a user profile to be created. This will create those entries needed under ProfileList in the registry. Now that they exist, you just update them. Meanwhile, the first copy of your program is waiting for the second new one to exit. Since you have the old SID from the command line, you simply replicate the ProfileList values from the old to the new in the registry. Then the second copy can exit. The first copy might not have rights to create the new entries, but it DOES have read access. So the first copy reads the entries to make sure that they are correct. Now it can notify the user of success, inform them that they must logon with their new account and reboot the machine.

The user logs on with their new account, sees their ugly kids on their desktop and all is right with their world. They never knew the work that their friendly neighborhood geek had to go through to make this stuff work. They probably think the feature was built into Windows.

Saturday, August 06, 2005

The slide show that wouldn't quit

I'm just back from the event with Microsoft in Redmond that we have been asked not to blog about. So I certainly won't blog about the content of those sessions; that stuff is still NDA. However, I can certainly talk about how the sessions themselves went. I was pretty excited after the first day of seeing the unmentionable product. There were a lot of PowerPoint slides, but there was also some live demo. The live demo goes a long way towards proving that there is actually some code written and a product will exist soon (the demos hardly ever crashed).

By the end of the second day, I was beginning to suffer from that oft endured PowerPoint poisoning. Yes, that state you get into where there have just been too many slides and you stop paying attention to them at all. By the third day, it got worse and I was thinking, "Just give me the bits and stop with these presentations; I'll test the thing myself." (OK, so I had to skip out on the last two sessions - I have a feeling I would have left those ones saying "Just kill me".)

It's funny how the PowerPoint poisoning was made worse by an internal Microsoft competition. They were competing to see who could make the best "PowerPoint animation" (I don't think I even heard them say "The best use of a PowerPoint animation"). Now this thing was multi-track with up to 5 sessions going at once. I could only attend one at a time, so can't pretend to judge them all. But the animations made me think of that scene in the movie Galaxy Quest where "Crewman number 6" (Guy) asks the characters "Have you ever watched the show?". I was thinking of these MS PM's - "Have you ever used PowerPoint animations?" I mean most were rudimentary. The way they were plugging each one we needed to have someone with a buzzer and have Tommy Lee Jones (Men in Black II) come out and say "This one's an example of 'Go home and do it again'." Anyway, they were "Not Guud" (as Jim Carey would say in Bruce Almighty).

Now that I have begun to recover from the PowerPoint poisoning I have begun getting very excited to get hold of the product that was being shown. I think it must have been the coolaid. I had heard you weren't supposed to drink it...

By the way, thanks to some Microsoft folks who made the trip quite interesting (in no particular order): Sloan, Tony, Nick, David, Iuliana, Ronna, Dan, Maria, Joe. Thanks - you guys rock.

Wednesday, August 03, 2005

The View from afar

I've been attending a session at Microsoft this week that the attendees have been asked not to blog about. So I won't. However, there have been some discussions with other attendees that have nothing to do with the actual sessions that aren't covered by the prohibition against comment. So those are fair game.

Yesterday, I got the chance to catch up with an influential IT person from a really large company and chat about Vista. He was concerned that there really weren't enough business drivers for moving his machines over to Vista. We talked about the important fixed features like the improved cache manager / offline files that brings all documents that you open on the network local and manipulates them on your local drive (a big win for slower links), the destkop composition engine and the ability to run applications at different apparent resolutions, etc. However, at this point there still isn't enough Vista "there" for him to be convinced. Not being an MS evangelist (at least not being paid to be), I was pretty much done. We talked about Vista being the right answer for new hardware purchased in 2007, but that was as far as he'd go.

Sounds to me like MS needs to pump up the marketing machine and make any wins that are there in the product more obvious to IT managers and CIO's. If they don't, there will be a hard sell getting senior management to approve upgrading.