AclUIAdapter

Security Briefs

Syndication

I finally got around to finishing up my managed adapter for ISecurityInformation. Turns out the cleanest way was to write a custom CCW in MC++. For those of you who are wondering what the heck I'm talking about, this allows you to add that nifty ACL editor GUI to your managed applications:

...along with all its accoutrements...

The sample application that comes with the adapter uses it to edit service ACLs (something the Windows administration tools don't provide except via group policy):

To use this adapter, write a class that implements ISecurityInformationManaged and pass an instance to AclUIAdapter.EditSecurity(). All this stuff is in a namespace called Pluralsight.Security.Adapters. You can download the code and sample application here. This will be featured in an upcoming article for MSDN Magazine.

Here's an excerpt from the sample app:

class ServiceSecurityModel : ISecurityInformationManaged {
    string machineName;
    string serviceName;
    public ServiceSecurityModel(string machineName, string serviceName) {
        this.machineName = machineName;
        this.serviceName = serviceName;
    }

    public ObjectInfo GetObjectInformation() {
        return new ObjectInfo(
            ObjectInfoFlags.EditAll | ObjectInfoFlags.PageTitle | ObjectInfoFlags.Advanced,
            machineName,
            serviceName,
            "Service Permissions");
    }

    public AccessRights GetAccessRights(Guid objectType, Pluralsight.Security.Adapters.ObjectInfoFlags flags) {
        AccessRights rights = new AccessRights();
        Access[] access = rights.Access = new Access[17];

        // summary page permissions (note use of AccessFlags.General)
        access[0] = new Access(SERVICE_ALL, "Full Control", AccessFlags.General | AccessFlags.Specific);
        access[1] = new Access(SERVICE_READ, "Read", AccessFlags.General); // 0x0002008D
        access[2] = new Access(SERVICE_WRITE, "Write", AccessFlags.General);
        access[3] = new Access(SERVICE_EXECUTE, "Execute", AccessFlags.General);

        // permissions specific to services (only show up on advanced page)
        access[4] = new Access(0x0001, "Query Configuration", AccessFlags.Specific);
        access[5] = new Access(0x0002, "Change Configuration", AccessFlags.Specific);
        access[6] = new Access(0x0004, "Query Status", AccessFlags.Specific);
        access[7] = new Access(0x0008, "Enumerate Dependents", AccessFlags.Specific);
        access[8] = new Access(0x0010, "Start", AccessFlags.Specific);
        access[9] = new Access(0x0020, "Stop", AccessFlags.Specific);
        access[10] = new Access(0x0040, "Pause or Continue", AccessFlags.Specific);
        access[11] = new Access(0x0080, "Interrogate", AccessFlags.Specific);
        access[12] = new Access(0x0100, "Send User Defined Control", AccessFlags.Specific);

        // standard permissions that apply to services (only show up on advanced page)
        access[13] = new Access(0x00010000, "Delete", AccessFlags.Specific);
        access[14] = new Access(0x00020000, "Read Permissions", AccessFlags.Specific);
        access[15] = new Access(0x00040000, "Change Permissions", AccessFlags.Specific);
        access[16] = new Access(0x00080000, "Take Ownership", AccessFlags.Specific);

        rights.DefaultIndex = 1; // default mask when you add a new ACE (GENERIC_READ)

        return rights;
    }

    public InheritType[] GetInheritTypes() {
        return null; // services are not containers
    }

    // these generic mappings taken from the service access rights documentation
    const int SERVICE_READ    = 0x0002008D;
    const int SERVICE_WRITE   = 0x00020002;
    const int SERVICE_EXECUTE = 0x00020170;
    const int SERVICE_ALL     = 0x000F01FF;

    const int GENERIC_READ    = unchecked((int)0x80000000);
    const int GENERIC_WRITE   = 0x40000000;
    const int GENERIC_EXECUTE = 0x20000000;
    const int GENERIC_ALL     = 0x10000000;

    GenericMapping ServiceGenericMapping = new GenericMapping(
      SERVICE_READ, SERVICE_WRITE, SERVICE_EXECUTE, SERVICE_ALL);

    public void MapGeneric(GenericAccess generic) {
        MapGenericMask(ref generic.Mask, ref ServiceGenericMapping);
    }
    public void SetSecurity(SecurityInformation providedInformation, byte[] binarySecurityDescriptor) {
        ServiceHelper.SetServiceSecurity(machineName, serviceName,
          (int)providedInformation, binarySecurityDescriptor);
    }
    public byte[] GetSecurity(SecurityInformation requestedInformation, bool wantDefault) {
        return ServiceHelper.GetServiceSecurity(machineName,
          serviceName, (int)requestedInformation);
    }

    [DllImport("advapi32.dll")]
    static extern void MapGenericMask(ref int mask, ref GenericMapping mapping);
}


Posted Oct 04 2004, 01:51 PM by keith-brown
Filed under: ,

Comments

Greg wrote re: AclUIAdapter
on 10-04-2004 3:23 PM
VERY nice.
Sean Hederman wrote re: AclUIAdapter
on 10-04-2004 8:20 PM
Congratulations. I'll look forward to the article too. Since I'm getting my copy of your new book in a month or two, this is definately going to be a Keith Brown couple of months ;D
dominick wrote re: AclUIAdapter
on 10-04-2004 8:54 PM
cool!
Joseph E Shook wrote re: AclUIAdapter
on 10-04-2004 9:15 PM
Of course! I asked about this almost two years ago on some of the news groups. Know one answered. You are the onlyone that would see the bueaty of this. I figured I would just write my own via Directory Services and pinvoke. I am sure glad you wrote this.
Michael Dereszynski wrote re: AclUIAdapter
on 10-05-2004 10:39 AM
So the logical extension then is how to leverage ACLs on managed objects? Is this covered in your new book (which is on it's way from Amazon.)?
Geek Noise wrote Geek Notes 2004-10-05
on 10-06-2004 12:46 AM
Martin's WebLog wrote AclUIAdapter
on 10-06-2004 3:35 AM
Lorenzo wrote re: AclUIAdapter
on 10-08-2004 11:31 AM
I'll look forward to the article too! Especially about how you finally managed to cope with memory allocation, di you implemented it with custom marshaling? Please let us know on this blog when the article will be done!
cris wrote re: AclUIAdapter
on 10-18-2005 9:28 PM
this is a cool and useful library!
the download link doesn't work, though.
hope this is fixed soon.
Keith Brown wrote re: AclUIAdapter
on 10-18-2005 9:44 PM
Thanks for pointing out the broken links - it's fixed now :)
Security Briefs wrote HttpCfg GUI
on 10-27-2005 10:12 PM
Sascha Kiefer wrote re: AclUIAdapter
on 01-14-2006 1:52 PM
Very nice code. Do you know if it's possible to create a user-defined object that i can return in GetServiceSecurity? the point is, that i have objects that are going to be restricted by a ACL and I want to use the windows dialog to be able to edit the ACL of those objects. But i have not figured out how to implement my own SecurityDescriptor.
Sascha Kiefer wrote re: AclUIAdapter
on 01-14-2006 3:38 PM
never mind: just found the InitializeSecurityDescriptor function! :)
Sean Peters wrote re: AclUIAdapter
on 04-14-2006 1:54 PM
First off, am i horribly misunderstanding the gernic to specific mapping, or are the SERVICE_* constants incorrect? I say this because they all have the 0x00020000 bit set. My understanding was that each of the specific permissions is supposed to be mapped to exactly 1 of the general permissions.

Second, i am trying to figure out how to properly propagate permissions to private child objects. I'm using the AclUIAdapter but not the ServiceHelper (can't get it to build using VS2005 with .NET version 2.0.50727). In any case, when SetSecurity() is invoked due to modifications in from GUI, i need to determine what change was made, and if it involves inheritable permissions. Do i have to examine each ACE separately, or is there some general way to determine if the modifications involve inheritable properties.

I don't wannt to just automatically traverse the entire subtree below the object that the changes were made to, particularly because that will optentially involve over 10 million private objects (stored in a database) under certain circumstances.

Thanks.
Keith Brown wrote re: AclUIAdapter
on 04-15-2006 9:26 AM
I don't know of an easy way to tell other than traversing the DACL, no. That would be a very good optimization to make in your case though! AS for generic permissions, they will almost always map onto more than one standard/specific permission. Read http://pluralsight.com/wiki/default.aspx/Keith.GuideBook/WhatIsAPermission.html for more detail.
Sean Peters wrote re: AclUIAdapter
on 04-18-2006 8:20 AM
Well, i guess i may traverse the DACL's when changes are made.

As for my previous permission comment, i think that you read it backwards. I agree that each generic permission is made up of multiple specific permissions and thats fine. But, each specific permission should only be a part of one generic permission, but all of your SERVICE_* permissions use the 0x00020000 bit. Separated into their bits, the service permissions are:
SERVICE_READ = 0x00020000 | 0x00000080 | 0x00000008 | 0x00000004 | 0x00000001;
SERVICE_WRITE = 0x00020000 | 0x00000002;
SERVICE_EXECUTE = 0x00020000 | 0x00000100 | 0x00000040 | 0x00000020 | 0x00000010;

Also, i'd think that this should always be true:
SERVICE_ALL = SERVICE_READ | SERVICE_WRITE | SERVICE_EXECUTE;
(its not)

and that this should also be true:
SERVICE_READ & SERVICE_WRITE & SERVICE_EXECUTE = 0x00000000
(its not)

Thanks for your time.

Keith Brown wrote re: AclUIAdapter
on 04-19-2006 8:28 AM
Sean,

Where did you read that a standard or specific permission can only be in one of the generic permissions? I don't know of any such limitation.

If you look in winnt.h you'll find why I use 0x20000 in each of the generic permission mappings. This is the standard READ_CONTROL permission, which appears to be intended for all three generic permissions:

#define STANDARD_RIGHTS_READ (READ_CONTROL)
#define STANDARD_RIGHTS_WRITE (READ_CONTROL)
#define STANDARD_RIGHTS_EXECUTE (READ_CONTROL)
Sean Peters wrote re: AclUIAdapter
on 04-20-2006 8:49 AM
Thanks for the clarification. I hadn't read anywhere regarding the disjoint nature of the general permissions, it was more of an initial feeling about them. I'm new to ACL progarmming and wanted to make certain whether or not my intuition was correct.

I've got my app almost working, filling in the details for some of the lesser used features of the adapter...still need to deal with the sledgehammer :)

Anyway, one (hopefully) last question. In the definition of the SecurityInformation enum in Pluralsight.Security.Adapters there are 8 flags defined, but i sometimes get additional ones back from the adapter in the SetSecurity() method. For instance, when the propagate owner to subcontainers box is checked, i get back 257 for the SecurityInformation. Black box testing has convinced me that 256 is the flag for recursing the owner (1 is the existence of owner info in the modifications).

Just wondering if i can find a complete definition of those flags so that i don't have to figure them all out by trial and error.

Thanks a lot for your help, this project is really coming together for me now.
Keith Brown wrote re: AclUIAdapter
on 04-20-2006 9:01 AM
Happy to help. My adapter is a thin layer over ISecurityInformation. Just look that up in the help and you'll find everything you need.
Sean Peters wrote re: AclUIAdapter - Inherit Only
on 04-21-2006 2:11 PM
Hi Keith,
I have yet another question for you. I've gotten most of what i need to work properly in terms of propagating security modifications properly, etc. The thing that i still can't get to work properly is how to apply inherit only aces. When i get a modification descriptor back from the AclUiAdapter, i traverse the aces to determine what to do with them, apply, inherit, etc. But when i attempt to apply an inherit only ace to the object / container that is the root for that inherit only ace (doesn't apply to it, but will apply to the appropraite children), i can't get it to apply properly. I can see that it has the PropgateFlag 'Inherit Only', but when i attempt to add it to the existing ACL it applies as an effective ace. I assumed that the propagation flag would be applied.

I am using SetPrivateObjectSecurityEx() to merge the existing ACL with the new modified information.

Is this the wrong approach, or do i need to do some explicit bit flipping to get the new information as i need it?

Once again, thanks for your help.
Keith Brown wrote re: AclUIAdapter
on 04-23-2006 4:33 PM
Hmm. I'm not sure why this wouldn't work, but I can't say that I've written the code to test it myself. If you continue to have difficulty, send me a simple test case (if you don't have my email addr, ping me via http://pluralsight.com/blogs/keith/contact.aspx).
rick cameron wrote re: AclUIAdapter
on 07-14-2006 8:14 AM
Hi, Keith
I've adapted your program to edit the ACL of a network share. It appears to be working, but I had to make some guesses on the mask values for the various access levels. I couldn't find them in any Win32 header.
Would you like to include this code with your app?
Cheers
- rick
Bruce Hatton wrote re: AclUIAdapter
on 11-16-2006 6:15 AM
Hi Keith,
I can see how to apply this to custom security descriptors, is there any way of using custom users/groups, or can I only use the Windows ones?
Also, you mentioned that you would be updating the sample for .NET 2.0, is there any news on that?
Thanks.