Skip to main content

Elevating COM objects from .Net

While I was working on one of my personal projects I needed to do some administrative tasks from a program launched as a normal user. Since I try to follow best practice to the best of my ability I knew I had to write an external module that could elevate to handle the administrative tasks required.

After doing quite a bit of research I came across two possible methods

  1. Create a new external program with <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/> in the embedded manifest and pass all of the information needed through the command line.

  2. Create a COM object and elevate the object when using it. Information would be passed through COM object calls in real time and allows the caller to handle problems.

Obviously because I like a challenge and I'm a sadist, I decided on option number two, creating and elevating a COM object. Following the directions generously provided by Christoph Wille I was able to successfully create and register a .Net COM object that was able to be elevated. Unfortunately after elevating the object I was unable to invoke any methods and kept getting an odd exception (I think it was 0x80070005, Access Denied, I didn't keep notes for it – so it may be I'm mixing them up).

Honestly, I didn't get the .Net method 100% working, no matter how I ran the commands the methods with the [ComRegisterFunction] and [ComUnregisterFunction] never executed, so I had to finish registration by hand (without those extra registry entries, the COM object won't elevate). As part of my troubleshooting and because of the actions I needed to take when elevated I switched from a .Net component to an ATL component. This simplified development since I could incorporate the registry entries directly into the .rgu file.

Thinking I've solved all of the problems I wrote the ATL COM component, coded it to the best of my ability, set up the .Net calling code and tried it out. Guess what... 0x80070005 Access Denied. At this point I was going insane, everything I tried and everything I did was being denied when it was elevated. If I launched the object under the normal user I was able to interact with it. Elevate it? BOOM access denied. *sigh*

Continuing to research and try to find the problem, I eventually read the small nugget of information about Over-The-Shoulder elevation. Having been on this page many, many times trying to find the information I need, I felt quite stupid when I realized the information I needed was right there the whole time.

For such servers, COM computes a security descriptor that allows only SELF, SYSTEM, and Builtin\Administrators to makes COM calls into the server. This arrangement will not work in OTS scenarios. Instead, the server must call CoInitializeSecurity, either explicitly or implicitly, and specify an ACL that includes the INTERACTIVE group SID and SYSTEM.

Totally makes sense right? Well, to break it down simpler, the default security on the COM object is such that only SYSTEM and Administrators have access to the COM object when elevated, and even though you just gave it permission, your limited user process can't access it. Turns out to properly allow a limited process access to the elevated COM object you need to grant Local Activation to the INTERACTIVE SID.

After using the Component Services snap-in (dcomcnfg) and manually granting the right permissions and confirming that it worked I looked for a way to make the change programmatically, and what do you know, there's an example right there on that same MSDN article!

Below is the code I use to set up the proper security for the COM object (grants local activation to INTERACTIVE and SYSTEM, grants local and remote activation to the Built-in Administrators and SELF SIDS) and is executed through the DllRegisterServer function that ATL calls when registration is to occur. The registry entries required for elevation are handled by ATL when it processes the .rgu file.

STDAPI DllRegisterServer(void) {
  // (0x3 = Local Access, 0x7 = Local + Remote Access)
  // See http://msdn.microsoft.com/en-us/library/ms693364(VS.85).aspx
  static const wchar_t comSDDL[] =
      L"O:BAG:BAD:(A;;0x3;;;IU)(A;;0x3;;;SY)(A;;0x7;;;BA)(A;;0x7;;;PS)";
  bool perUser = false;
  ULONG securityDescriptorSize = 0;
  SECURITY_DESCRIPTOR* securityDescriptor = NULL;

  // Determine if the registration is per user.
  ATL::AtlGetPerUserRegistration(&perUser);

  // registers object, typelib and all interfaces in typelib
  HRESULT hr = _AtlModule.DllRegisterServer();

  // Only set up the elevation moniker if it is a system-wide install.
  // (Elevation doesn't work on per-user COM)
  if (SUCCEEDED(hr) && !perUser) {
    hr = E_FAIL;
    if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(comSDDL, SDDL_REVISION_1, (PSECURITY_DESCRIPTOR*)&securityDescriptor, &securityDescriptorSize))
      return E_FAIL;

    ATL::CRegKey rootAppId;
    ATL::CRegKey appId;
    if (ERROR_SUCCESS == rootAppId.Open(HKEY_CLASSES_ROOT, L"AppID", KEY_READ | KEY_WOW64_32KEY) &&
        ERROR_SUCCESS == appId.Open(rootAppId, _AtlModule.GetAppIdT(), KEY_WRITE | KEY_WOW64_32KEY) &&
        ERROR_SUCCESS == appId.SetBinaryValue(L"AccessPermission", securityDescriptor, securityDescriptorSize)) {

        hr = S_OK;
    }
    LocalFree(securityDescriptor);
  }
  return hr;
}

Http:BL anti-spam plugin for blogengine.net

With the recent update to blogengine.net 2.5 and a somewhat renewed interest in blogging I decided to port my Http:BL anti-spam plugin from my old custom blog engine to blogengine.net.

The Http:BL (DNS blacklist) is run by the people at Project Honeypot. Rather than using normal HTTP requests like Akismet, Stopforumspam, and others it instead uses a DNS query to determine whether a given IP address is considered spam or not. (If you're interested in how the query works, you can check out the API, it's pretty well documented.) With my previous engine, about 80% of the blocked spam was blocked by Http:BL so I am hoping that this plugin will work as well as it used to. Once I've confirmed it works and there aren't any bugs I plan on packaging it up and putting it on the new blogengine.net gallery.

If you would like to try it out now, just sign up for an API key, download the extension, and place it in the App_Code\Extensions directory of your site, enable and configure it and you should be good to go.

Best Buy [still] sucks

Picture of a Best Buy storefront.

It has been a long, long time since I last set foot in a Best Buy store. Ever since I was able to purchase products from sites like Amazon and Newegg I hardly ever set foot in a brick and mortar store. In fact, the only time I would even think of visiting a store was in the case of an emergency: hard drive failure, power supply failure, and the like. Since I started to notice that may laptop’s current hard drive, a Samsung HM160HI, is starting to overheat I have been looking for a replacement laptop hard drive. Unfortunately, I made quite a mistake....

I had spent the last couple of days researching different laptop hard drives, looking for a drive that has a good balance between capacity, speed, and heat. I had entertained the thought of using this as an excuse to purchase a Solid State Drive but quickly abandoned that thought as 64GB drives currently run between $200 and $300. Of course, I’m currently using about 90GB on my current 160GB drive so I would have to purchase the next viable size up: a 128GB drive, which runs about $400. Forget that!

Having previously upgraded a laptop hard drive in my old laptop I knew that having a 7200 RPM drive over a 5400 RPM drive really does make a world of difference in speed. Of course, a faster speed means higher power consumption and rotational speed (duh), which leads to a higher operating temperature. So with that, I had a target drive type I was looking for:

  1. Must be at least 120GB. (Easy!)

  2. Must run at 7200RPM. (Narrows it down a bit)

  3. Must support SATA II and NCQ. (Pretty much weeds the cheap ones out right there)

  4. Must have positive user reviews. (Narrows it down even more)

  5. Must run cooler than my current one. (Any drive can hit that with no effort!)

Power consumption is actually not on the list since my battery is already 49% worn (meaning it currently operates at 51% of its designed capacity) and I hardly use my laptop away from a power outlet. After much searching and reading I decided to settle on the Western Digital Scorpio Black 320GB 7200RPM drive (Model: WD3200KTRTL). After researching that model I found that it did not have the freefall sensor to park the heads when it was dropped. This was OK actually since I am hardly on the road with it and I *starts knocking on wood* haven’t dropped my laptop yet *stops knocking*.

After checking online prices for the drive through various vendors, I stumbled on the fact that the drive sold at Best Buy for the same price as other online retailers! Not only that but it is in stock!

Screenshot of the product listing for the hard drive listed at a price of $79.99.

See? It’s available AND only for $79.99! Well, happy as I am, I finish off the work day and head down to the Greeley Best Buy, expecting to walk out with a hard drive. Of course, I should have known better.

When I finally got in to the store it took me a bit to find their hard drive … shelf. I say shelf because all of the drives they have are on the bottom shelf in the store. They have a few portable ones hanging up in the corner but that doesn’t count. After searching all of the tags on the shelf for about 10 minutes I finally found it. Or rather, where it should be and with a higher price! It was marked at $94.98! (I wish I had taken a picture of it!) $15 markup for going to the store... wonderful! At that point, I pulled the website up on my phone to make sure it was "still in stock" and being sold for $79.99. Yup, it was. A Geek Squad member came over and asked if I needed any help. I directed them to the empty spot on the shelf and asked if it was in stock. What a mistake that was, it took him about 10 minutes to come back and say "Nope, the website must be out of date." Thanks, geek squad, really!

As I was walking towards the door I spotted a normal Best Buy employee. Naturally they asked if there was anything they could do so I figured I'd try again. I asked if they had the drive and about 5 minutes later he came back with it in hand. The exact model I was wanting too (impressive)! Rather than inquire why the previous employee took longer and couldn't find it, I took the drive to the checkout counter.

Checkout Lady

Did you find everything you need?

Me

Eventually.

Checkout Lady
That's great.
The price rings up as $94.99. I expected this.
Me

I noticed online that the price was $79.99. Any chance I can get it at that price?

Checkout Lady

No it doesn't.

Me

I'm sorry?

Checkout Lady

The website wouldn't have a different price.

Me
I pull out my phone and load the website
See? Here's the site, the model matches, and it's for $79.99.
Checkout Lady

I can't trust your phone, how do I know you didn't make that up?

Me
*sigh*
You can pull it up here if you want.
Checkout Lady

The Checkout Lady calls the manager.

Manager

Is there something wrong?

Me

Not really, I was just wanting to purchase this at the advertised price.

Manager

The website doesn't have the right price.

Me

Really? That would be bad if the price is wrong then.

Manager

It is wrong. We don't have to match what the website says anyway.

Me
All right. Have a nice day. Thanks anyway.
I walk out. I heard them calling after me but I really didn't want to deal with them anymore.

As I was walking out of the store I wondered why I even went there in the first place. I didn't really need it right away and the online price was competitive. I suppose I could have ordered it online and picked the "in-store" option, but I am glad I didn't. After getting home, taking a nap, and looking online again. I found the model with the freefall sensor (the WD3200BJKT) available on Amazon for $89.98. So for $5 less I can get a higher model than Best Buy offered as well as free two-day shipping from Amazon! It should be here on April 2nd! Wee!

OK, as I was writing this, something really funny and irritating happened. As I loaded the Amazon page for the WD3200BJKT I noticed something odd... the price dropped! To $79.99! Go figure. Amazon may or may not refund the difference (they don't offer a post-purchase price match guarantee) but I'm not going to worry about it. It's not worth the hassle.

So what did I learn? Best Buy still sucks, the employees are a pain, and I don't plan on ever going to one again. Their prices may be (somewhat) competitive online, but it seems like you still have to buy online and forget about the store.... Oh well, I'll be happy when my drive comes in on the 2nd!

Imported Comments

Anon on Sunday, July 3, 2011 at 11:00 AM wrote:

newegg.com... Just sayin'.

Roger on Sunday, July 3, 2011 at 12:05 AM wrote:

The last time (against my better judgment) I went to Best Buy they explained that they couldn't possibly match a competitors advert on the grounds that it was a different geographical market (and Best Buy employees decide what constitutes one). So I drove for 10 hours to the other store. Kidding, I drove for a few minutes to this other store and bought it there, and thanking Best Buy for reminding me never to go to them again.

Daniel Wayne Buchanan on Sunday, July 3, 2011 at 12:35 AM wrote:

I know exactly what you mean! Every time I go in there, I feel like its the most complicated thing I've ever done, and also feel like a total computer illiterate. I won't go there now, since they're my competitor with the Geek Squad (not really).

Deafjams on Monday, July 4, 2011 2011-07-04 at 7:25 AM wrote:

The Best Buy online store and the Best Buy brick and mortal stores are basically two separate beasts. It costs less for Best Buy to operate online, therefore there are lower prices online than at the stores. That's their reasoning behind not price matching the online store.

Comment Amusement

As I was upgrading the site, changing themes, and cleaning up spam comments, I came across something amusing. This is a spam script (obviously) from the same computer, but what amused me is that a day after the first (top) comment was posed the second comment was posted as a response (somewhat anyway).

Screenshot of a spam comment replying to another comment about the year 2010 and parying like it's 1999 with different email addresses but originating from the same IP address.

1992 meet 2009. 2009 say hello to 1992

I can still remember when I got my first computer. It was a Leading Edge computer running an Intel 8086 with a whopping 512 KB of RAM and an internal hard drive of 10MB! Now that was a kicking system! I quickly learned that HIMEM.SYS and EMM386.EXE only worked with Extended Memory (XMS) and Expanded Memory (EMS).

I quickly learned what things broke the system and which things don’t. format c: /y? Bad! Deletes the drive data! command.com? It’s ok, though it nests a shell.

Well, after installing the latest Windows 7, I started to feel a little nostalgic. I kind of miss those days, back where the operating system didn’t take care of you and you actually had to have an idea of what you were doing.

It got the better of me, so I managed to get Windows 3.11 online and running inside of Windows 7. They got to say hello over the network! It’s amazing that technology from 1992 can still operate in today’s world!

Without further ado, say hello again to Windows for Workgroups 3.11!

Screenshot of Internet Explorer 7 running behind a VirtualBox window running Windows for Workgroups 3.11 showing the "winver" about dialog box.

Explorer still limited by MAX_PATH

With the upcoming release of Windows 7 by Microsoft I am still surprised at a limitation of Explorer that has been around since explorer was first introduced with Windows 95. When Microsoft first introduced Windows 95 they included long filename support. This long filename support allowed Windows to store a filename of “up to 255 characters” while still maintaining compatibility with DOS and previous versions of Windows.

Remember filenames like LETTER~1.DOC and directories like PROGRA~1? Well, believe it or not they still exist by default in Windows 7! You can confirm this yourself by opening a command prompt and running the dir /x command. This will display files in the current directory with both their long filenames and DOS compatible filenames. Of course you can turn this filename generation off by setting the appropriate registry keys.

Back with Windows 95 the Windows API generally provided only ANSI functions. ANSI strings were either single-byte or a variable-width multi-byte arrays. When Windows NT came along with its new kernel, all of the internal strings were represented in Unicode (double-width) character arrays.

The string "HELLO" in ANSI:

Byte

1

2

3

4

5

6

Data

H

E

L

L

O

\0

The string "HELLO" in Unicode:

Byte

1

2

3

4

5

6

7

8

9

10

11

12

Data

H

\0

E

\0

L

\0

L

\0

O

\0

\0

\0

Along with the Unicode kernel, Windows NT also included updated APIs that used the new Unicode strings. So for every CreateFileA you now had a CreateFileW. To maintain backwards compatibility the original CreateFileA still had the limitation on the length of the path that Windows 95 did. Otherwise known by the macro of MAX_PATH which is defined as 260. Since CreateFileW was a new function, there was no backwards compatibility to maintain, so Microsoft provided a way to provide a path up to 32,676 characters. You can read more about the maximum path length on MSDN.

The drawback of still providing both APIs is that as applications pick and choose they can run into issues with the length of the path. Microsoft Word uses the CreateFileW function which allows them to create longer filenames than explorer can handle. I was hoping that in Windows 7 Explorer would finally be updated to use the Unicode CreateFile function… since Windows 95 was released over 14 years ago.

Unfortunately I ran into Explorer’s 260 character limitation just the other day. One of the deleted files resulted in a path of 273 characters long and the following dialog popped up:

The folder contains items whose names are too long for the Recycle Bin.

Imported Comments

T800 on Thursday, January 6, 2011 at 12:41 AM wrote:

What is even more incredible is that they still didn't fix shell path handling functions such as PathFindExtension, they are still limited to MAX_PATH (256) characters, so I have to write my own if I want my code to be 21 century-compatible. I'll skip Win7 and stick with XP because nothing changes.

ken masters on Sunday, April 3, 2011 at 8:05 PM wrote:

「ストリートファイター」>「ヴァンガードプリンセス」

Understanding xAP with .Net

I am currently writing a .Net application to listen, log and graph the temperatures and fan speeds of my servers using the xAP protocol. I’ll be using SpeedFan to monitor the temperatures and fan speeds of the servers because SpeedFan includes the ability to broadcast the current sensors using xAP. Sounds complicated right? We’ll lets look at the xAP protocol first.

The xAP Home Automation Protocol is a lightweight, ASCII based communication protocol. It’s a simple protocol that takes very little processing power to use and is actually easy to work with. It seems their goal is to work across multiple mediums (ethernet, serial, parallel) and multiple systems (full machine, light embedded systems, and even audrino boards!) while remaining simple and human readable.

xAP protocol message from SpeedFan

xAP-header
{
v=12
hop=1
uid=FF671100
class=PC.status
source=Almico.SpeedFan.NARU
}
temp.1
{
id=Core
curr=48.0
want=40
warn=50
}
temp.2
{
id=CPU Off Die
curr=40.0
want=40
warn=50
}
temp.3
{
id=Motherboard
curr=40.0
want=40
warn=50
}
temp.4
{
id=HD0
curr=48.0
want=40
warn=50
}
fan.1
{
id=Fan1
curr=3342
}

As you can see in the message above you can easily read the following information:

  • The class of the message is the type of PC.Status.

  • The source is from SpeedFan by Almico on the computer Naru.

  • There are 4 temperature sensors and 1 fan sensor on the server as reported by SpeedFan.

xAP over Ethernet uses a UDP broadcast on port 3639. Now, armed with this information, writing a simple .Net console application doesn’t seem too hard does it?

Hopefully by now you can already see a way or two in processing this text-based message. Believe me, it isn’t as hard as it seems! Over the next couple posts we’ll see a method for parsing the message and the incorporation of a few of the newer .Net technologies (some extension methods and lambda expressions)! Stay tuned!

TFS 2008, Server 2008, SQL 2008

Oye, so apparently I like pain. I keep doing it to myself too. I decided to set up a virtual machine (through Hyper-V) running Windows Server 2008 that would host a new instance of Team Foundation Server 2008. If any of you have heard of TFS before you may know that it is a pain in the ass to install. Out of the three installs I have completed I have never had one work the first time. So while following every step of the instructions to the letter, I still ended up with a failed install. As much as I would love to contribute to helping people, I have lost any idea of what I was doing  so there's no way a decent write up is happening. So here I am, wrestling with TFS yet again... so that's why I figure I like pain... I keep doing this to myself.

I changed it again!

Well, in less than one week of using dasBlog I went ahead and switched blogging systems again. Surprise, surprise. I guess it was coming. Anyway the major reasons I switched is I liked their theme engine better, they allow the ability to have pages not part of the blog timeline, they can use a database if needed, and seems to be a bit more active.

As you can see I am already importing my theme that Jared designed for me for my old blog. While it isn't completed yet it should be in the next day or so (I hope).

Naturally with this engine change the syndication feed changed so make sure you update your feed readers!

Now, after many infomercials about Obama coins and listening to Chuck Norris talk about how great the Total Gym is I am headed to bed at 5:30am.