OSRLogo
OSRLogoOSRLogoOSRLogo x OSR Custom Development Services
OSRLogo
x

Everything Windows Driver Development

x
x
x
GoToHomePage xLoginx
 
 

    Thu, 14 Mar 2019     118020 members

   Login
   Join


 
 
Contents
  Online Dump Analyzer
OSR Dev Blog
The NT Insider
The Basics
File Systems
Downloads
ListServer / Forum
  Express Links
  · The NT Insider Digital Edition - May-June 2016 Now Available!
  · Windows 8.1 Update: VS Express Now Supported
  · HCK Client install on Windows N versions
  · There's a WDFSTRING?
  · When CAN You Call WdfIoQueueP...ously

You've Gotta Use Protection -- Inside Driver & Device Security

We’ve been thinking a lot about security these past couple of months here at OSR. No, I’m not talking about the fact that our 401K is in the toilet, because that’s been true for a long time. And I’m not talking about that warm fuzzy feeling you get when you curl up with your significant other. I’m taking about security and device drivers – even more specifically, security that’s applied to devices and operations performed on them. While reading this article, please keep in mind that everything we discuss only applies to device’s that are directly opened and communicated with from user mode.

Just Gotta Have Faith? 

Face it. Most of the drivers we write for strange devices rely on the good intentions (or ignorance) of their users. Let’s say you have a driver that controls communications with a satellite. The driver implements a ton of IOCTLs that allow people to send and receive data from the satellite, enable and disable individual transponders, and perhaps even move the satellite around some. In most cases, these IOCTLs all receive the same protection:  Little or none.

You have an application that’s run by lots of folks, in lots of situations, to receive data from the satellite. Fine. You also have a “move the satellite around in orbit” application, that should only be run by certain engineering team members. Perhaps this critical application is sophisticated enough to check to see if the user that’s running it is a member of the group “ADMINISTRATORS”.

But what happens when some summer hire in the customer service group (and clearly not a member of the Admins group) gets pissed off and writes their own little application that issues an IOCTL_SAT_MOVE that requests the satellite to go jetting off towards Venus? If your driver is like about 95% of the drivers out there, what happens is that the satellite indeed heads for Venus. And given the problem your driver has caused, you might want to consider following it. Oh well, I’m sure your company has both a sense of humor and a satellite to spare.

Use Protection

So, before you lose any more satellites, let’s discuss some mechanisms by which we can avoid this problem. The first and easiest is to specify the protection to be applied to your Device Objects in your driver’s INF file.

If your device is in its own device class, you can specify a default security descriptor to be applied to all devices in the class as part of the AddReg section that’s referenced from the ClassInstall32 section of your INF file. The security descriptor is described using (appropriately enough) “security descriptor definition language” (SDDL) which is fully documented in the SDK.  Your ClassInstall32 and AddReg sections might look something like that in Figure 1.

[ClassInstall32]

Addreg=AcmeSatellite

 

[AcmeSatellite]

HKR,,,,"Satellites and Stuff"

HKR,,Icon,,103             ; Empty

HKR,,Security,,"D:P(A;CIOI;GR;;;WD)(A;CIOI;GA;;;BA)(A;CIOI;GA;;;SY)"

HKR,,DeviceCharacteristics,0x10001,0x00000100 ; FILE_DEVICE_SECURE_OPEN

Figure 1 — ClassInstall32 & AddReg

All that the ClassInstall32 section in Figure 1 does is refer to an AddReg section. As usual, this AddReg section establishes the name and attractive icon for the class. You could also specify a DLL with your device’s property pages here. The Security entry allows you to specify a default Security Descriptor (SD) for all devices in your device class. This SD will be applied to both to your device’s PDOs and FDOs.

Creating (or even reading) the SD in SDDL can be confusing, at least initially.  Once you figure it out, it’s really pretty straight forward.  Look in the SDK index under “Security Descriptor Definition Language” and you’ll have more information than you ever wanted to know. But to help get you started, the SD format is:

 O:owner-sidG:group-sidD:dacl-flags(ace)(ace)S:sacl-flags(ace)(ace)

Anything you don’t need, you leave out. So, the SDDL string in the AddReg section in Figure 1 contains only a DACL (because it starts D:). A DACL supplies “discretionary access control”, which is “discretionary” in the sense that it is at a user’s discretion to establish it (not in the sense that it’s “discretionary” for the operating system to enforce it!)

There’s one DACL flag supplied in the SD in Figure 1, P. This means the DACL is “protected” (SE_DACL_PROTECTED), i.e. it can’t be modified by any inheritable ACEs. Following the “P”, the SD in Figure 1 has three Access Control Entries (ACEs).

Each ACE specifies the access allowed by a specific group, for example: (A;CICO;GR;;;WD). The A in the ACE means that this is an access allowed ACE. So it’s going to describe the access to the device that will be available to a group. The CIOI specifies flags for the ACE header. CI here means that child containers inherit this ACE as the effective ACE. OI means that child objects inherit this ACE as the effective ACE. The GR specifies the allowed access, which in this case is Generic Read. The WD specifies the group. WD means “World” or “Everyone” as this is more typically labeled.

The other ACEs can be decoded similarly: (A;CIOI;GA;;;BA)(A;CIOI;GA;;;SY). BA (which is the “Built-in Administrators” group) gets GA (i.e. Generic All) access. SY (the System group) also gets Generic All access. See, SDDL may be ugly, but it’s really not so hard to understand.

You might also notice that the INF file segment shown in Figure 1 also specifies DeviceCharacteristics. This statement allows you to specify default values for the DeviceObject->DeviceCharacteristics field. In the case above, we specified the value 0x100 which works out to be FILE_DEVICE_SECURE_OPEN. Incidentally, you can also use this same AddReg section to specify a default Device Type for your device. And, believe it or not, this is all documented in the DDK. Just look for the phrase “INF AddReg Directive” in the DDK index.

Does this Solve the Satellite Problem?

This device protection scheme solves the satellite problem. Your IOCTLs to do nice, non-destructive, type things to the satellite are created with FILE_READ_ACCESS. These can include those in Figure 2.

#define IOCTL_SAT_READ            \

   CTL_CODE(FILE_DEVICE_SAT, 2048, METHOD_IN_DIRECT, FILE_READ_ACCESS);

#define IOCTL_SAT_GET_STATUS      \

   CTL_CODE(FILE_DEVICE_SAT, 2047, METHOD_BUFFERED, FILE_ANY_ACCESS);

#define IOCTL_SAT_MOVE            \

   CTL_CODE(FILE_DEVICE_SAT, 2046, METHOD_BUFFERED, FILE_WRITE_ACCESS);

Figure 2 — IOCTLs via FILE_READ_ACCESS

It’s important to realize that as long as you’re using IOCTLs (and not IRP_MJ_READ and IRP_MJ_WRITE) to talk to this driver, you could also include an IOCTL that can write data to the satellite, and have that IOCTL only require FILE_READ_ACCESS. It may look strange, but there’s no reason that it won’t work (See Figure 3).

#define IOCTL_SAT_WRITE         \
  CTL_CODE(FILE_DEVICE_SAT, 2045, METHOD_OUT_DIRECT, FILE_READ_ACCESS);

Figure 3 — Write Operation IOCTL (set for FILE_READ)

Remember, the sole purpose of the Access argument to the CTL_CODE macro is to tell the I/O Manager what bits to check in the mask of granted access that’s associated with the file handle. There’s no reason that these bits must reflect the actual I/O operation in any way.

More Control

Let’s say you want yet more control. Or, even more likely, you want more flexibility. In general, it’s not a great idea to have device drivers set system policy. At some point, somebody is likely to want to change something. In our example, perhaps they’ll want to change the group of people who can issue satellite maneuvering commands from Administrators (which is a pretty dumb group to use in the first place, actually) to a custom defined group such as Engineering.

Probably the best way to accomplish this aim, and to create a much more refined and flexible security implementation, is to:

  • Create an application that allows an appropriate privileged user to build a Security Descriptor and send that SD to the driver using an IOCTL.
  • Have the driver receive the SD built by the app and store it away.
  • Have the driver check the access of the requestor against the stored SD, whenever a restricted operation is requested

This really isn’t nearly as hard as it sounds. Creating that app to build the SD is pretty much a snap (for an app person that is) using the functions in ACLUI.DLL – It’s all described in the SDK of course. In fact, if you’re really clever, you could even add a security property sheet to your device property pages. Just make sure you build a Self Relative SD, and send it to your driver via an IOCTL.

The driver probably wants to be careful about from whom it accepts such SD information. For example, it might want to check to ensure that the caller has a specific privilege. The code might look something like that shown in Figure 4.

          case IOCTL_OSR_SET_SECURITY:  {

            LUID neededPrivilege = {0};

            BOOLEAN userHasAccess = FALSE;

                ULONG bufLength =

                        ioStack->Parameters.DeviceIoControl.InputBufferLength;

            SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;

            PSECURITY_DESCRIPTOR incomingSd=NULL;

                neededPrivilege =

                        RtlConvertLongToLuid(SE_REMOTE_SHUTDOWN_PRIVILEGE);

             userHasAccess = SeSinglePrivilegeCheck(neededPrivilege,

                                          Irp->RequestorMode);

             if(userHasAccess == FALSE)  {

                code = STATUS_PRIVILEGE_NOT_HELD;

                break;

            }

             incomingSd = (PSECURITY_DESCRIPTOR)

                                                        Irp->AssociatedIrp.SystemBuffer;

             __try  {

          

                if( (bufLength != 0) &&

                    (incomingSd != NULL) )  {

           

                    if(!RtlValidSecurityDescriptor(incomingSd))  {

                        code = STATUS_INVALID_PARAMETER;

                        break;

                    }

                } else {

                               

                    code = STATUS_INVALID_PARAMETER;

                          break;

                }

                 mySd = ExAllocatePoolWithTag(NonPagedPool,

                                              bufLength,

                                              'DSoN');

                RtlCopyMemory(mySd,

                              incomingSd,

                              bufLength);

 

                 code = STATUS_SUCCESS;

 

         } __except(EXCEPTION_EXECUTE_HANDLER)  {

                 if(mySd != NULL)  {

                    ExFreePool(mySd);

                }  

                 code = STATUS_INVALID_PARAMETER;   

        }

            break;

        }

Figure 4 — Confirming Proper Privileges

The code in Figure 4 only uses functions from WDM.H, believe it or not. It first checks to see if the caller has “SeRemoteShutdownPrivilege” – You should substitute whatever privilege you like here. We chose this priv because it’s almost exclusively held only by Administrators. Check the privilege set, and use whatever one makes sense for you.

Once the priv check is passed, the code in Figure 4 (which is designed to use METHOD_BUFFERED, in case that’s not obvious) validates the received buffer and if it’s valid, then calls RtlValidSecurityDescrptor(…) to validate the received SD. If that works, the driver allocates a hunk of pool, and copies the (self relative) SD to it. Note that the checks are done in a __try/__except block.

Now, when the driver receives an IOCTL that it wants to protect, it checks the access of the requestor against the stored SD. This check is pretty simple, and is shown in Figure 5.

 

         case IOCTL_OSR_PROTECTED2: {
            BOOLEAN accessGranted=FALSE;
            SECURITY_SUBJECT_CONTEXT subjectContext;
            ACCESS_MASK grantedAccess=FALSE;

             code = STATUS_ACCESS_DENIED;

             SeCaptureSubjectContext(&subjectContext);

             accessGranted = SeAccessCheck(mySd,              // Security descriptor
                                &subjectContext,              // Subject Context
                                FALSE,                        // Subject context locked??
                                FILE_WRITE_DATA,              // Desired access
                                0,                            // Previously granted access
                                NULL,                         // Privileges (output, optional)
                                IoGetFileObjectGenericMapping(), // Generic mapping
                                Irp->RequestorMode,           // Access mode
                                &grantedAccess,               // mask of granted access
                                &code);                       // completion status for function
             if(accessGranted)  {
                code = STATUS_SUCCESS;
            }

             SeReleaseSubjectContext(&subjectContext);

             if(accessGranted) {
                DoProtectedStuffHere(DeviceObject, Irp);
            }
            break;           
        }

Figure 5 — Checking Access Against Security Descriptor

In Figure 5, the driver captures the caller’s security context (to ensure it won’t change during the course of the call), using the function SeCaptureSubjectContext(…). It then checks the captured context against the SD that was previously stored away using SeAccessCheck (though not shown in this code, you probably want to ensure that the SD has been established before even accepting this IOCTL – That check appears earlier in this driver). The subject security context is released by calling, appropriately enough, SeReleaseSubjectContext. If the access check passed, the variable accessGranted is set to TRUE. The function can base its decision on whether or not to perform its protected actions on this variable.

Trust, Yet Verify

So you see, it’s really not necessary to just “trust” that your users will use your IOCTLs appropriately, or simply “hope” that the wrong people don’t discover your “send the satellite to Venus” IOCTL. There exist simple, and powerful, mechanisms that you can use to lock down your driver just about as much as you want.

Use the features wisely. Ahhh… I’m feeling more secure already.

Related Articles
Keeping Secrets - Windows Security (Part III)
Keeping Secrets - Windows NT Security (Part II)
Keeping Secrets - Windows NT Security (Part I)
Still Feeling Insecure? - IoCreateDeviceSecure( ) for Windows 2K/XP/.NET
Securing Device Interfaces - A Better Approach than Sending an SD
Security During Create Operations
Locking Down Drivers - A Survey of Techniques
What is Coming with Vista - Limited User Access

User Comments
Rate this article and give us feedback. Do you find anything missing? Share your opinion with the community!
Post Your Comment

"Important missing info"
If you try this, you might find it appears not to work. Don't despair!

Setting a security descriptor etc. as described here will only work the FIRST time you use the INF file on a particular PC.

The [ClassInstall32] section of an INF file is IGNORED on subsequent re-installations/and tests since the SetupClassGUID already exists in the registry on the PC. That itself is quite reasonable, if you think about it, but why would you? You just have to know.

Simply uninstalling your driver won't remove the SetupClassGUID from the registry either, even if there are no other drivers of that class remaining.

Finding the appropriate entry in the registry is quite hard, but (assuming I got the right one) it still can't be deleted by hand with the registry editors!

...Having trashed my registry (ouch) and reinstalling a virgin system image, lo-and-behold everything works as described in the article.

This behaviour is documented in the DDK (with a solution) but the reference is quite obscure. See...

Windows 2000 DDK Setup, Plug & Play, Power Management Reference, Part 3: Setup 1.0 INF File Sections and Directives INF Class32Install Section DelReg

Rating:
14-Nov-03, Jack Heeley


Post Your Comments.
Print this article.
Email this article.
bottom nav links