dcomcnfg functionality programmatically

二次信任 提交于 2019-11-28 13:40:13

The answer posted by Daniel was HUGELY helpful. Thank you so much, Daniel!

An issue with Microsoft's documentation is that they indicate that the registry values contain an ACL in binary form. So, for instance, if you were trying to set the machine's default access (rather than per-process), you would be accessing registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole\DefaultAccessPermission. However, in my initial attempts to access this key using the System.Security.AccessControl.RawACL class were failing.

As Daniel's code indicate's the value is not actually an ACL, but really is a SecurityDescriptor with the ACL in it.

So, even though I know this post is old, I'm going to post my solution for checking and setting the security settings and adding NetworkService for Default local access. Of course, you could take this and make it better I'm sure, but to get started you would simply need to change the key and the access mask.

static class ComACLRights{
    public const int COM_RIGHTS_EXECUTE= 1;
    public const int COM_RIGHTS_EXECUTE_LOCAL = 2;
    public const int COM_RIGHTS_EXECUTE_REMOTE = 4;
    public const int COM_RIGHTS_ACTIVATE_LOCAL = 8;
    public const int COM_RIGHTS_ACTIVATE_REMOTE = 16;
}
class Program
{
    static void Main(string[] args)
    {
        var value = Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Ole", "DefaultAccessPermission", null);

        RawSecurityDescriptor sd;
        RawAcl acl;

        if (value == null)
        {
            System.Console.WriteLine("Default Access Permission key has not been created yet");
            sd = new RawSecurityDescriptor("");
        }else{
            sd = new RawSecurityDescriptor(value as byte[], 0);
        }
        acl = sd.DiscretionaryAcl;
        bool found = false;
        foreach (CommonAce ca in acl)
        {
            if (ca.SecurityIdentifier.IsWellKnown(WellKnownSidType.NetworkServiceSid))
            {
                //ensure local access is set
                ca.AccessMask |= ComACLRights.COM_RIGHTS_EXECUTE | ComACLRights.COM_RIGHTS_EXECUTE_LOCAL | ComACLRights.COM_RIGHTS_ACTIVATE_LOCAL;    //set local access.  Always set execute
                found = true;
                break;
            }
        }
        if(!found){
            //Network Service was not found.  Add it to the ACL
            SecurityIdentifier si = new SecurityIdentifier( 
                WellKnownSidType.NetworkServiceSid, null);
            CommonAce ca = new CommonAce(
                AceFlags.None, 
                AceQualifier.AccessAllowed, 
                ComACLRights.COM_RIGHTS_EXECUTE | ComACLRights.COM_RIGHTS_EXECUTE_LOCAL | ComACLRights.COM_RIGHTS_ACTIVATE_LOCAL, 
                si, 
                false, 
                null);
            acl.InsertAce(acl.Count, ca);
        }
        //re-set the ACL
        sd.DiscretionaryAcl = acl;

        byte[] binaryform = new byte[sd.BinaryLength];
        sd.GetBinaryForm(binaryform, 0);
        Registry.SetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Ole", "DefaultAccessPermission", binaryform, RegistryValueKind.Binary);
    }
}

Facing similar circumstances (configuring DCOM security from an MSI) I managed to create a solution that does what I want by altering registry key values in HKEY_CLASSES_ROOT\AppID{APP-GUID-GOES-HERE}. Thanks to Arnout's answer for setting me on the right path.

In specific, I created a method to edit the security permissions for DCOM objects, which are stored in the LaunchPermission and AccessPermission registry key values. These are serialized security descriptors, which you can access by passing the binary data through RawSecurityDescriptor. This class simplifies a lot of the details in a delicious .NET-y fashion, but you still have to attend to all the logical details regarding Windows ACL, and you have to make sure to write the security descriptor back to the registry by using RawSecurityDescriptor.GetBinaryForm.

The method I created was called EditOrCreateACE. This method will either edit an existing ACE for an account, or insert a new one, and ensure that the access mask has the passed flags set. I attach it here as an example, this is by no means any authority on how to deal with it, since I know very little of Windows ACL stuff still:

// These are constants for the access mask on LaunchPermission.
// I'm unsure of the exact constants for AccessPermission
private const int COM_RIGHTS_EXECUTE = 1;
private const int COM_RIGHTS_EXECUTE_LOCAL = 2;
private const int COM_RIGHTS_EXECUTE_REMOTE = 4;
private const int COM_RIGHTS_ACTIVATE_LOCAL = 8;
private const int COM_RIGHTS_ACTIVATE_REMOTE = 16;

void EditOrCreateACE(string keyname, string valuename,
                      string accountname, int mask)
{
    // Get security descriptor from registry
    byte[] keyval = (byte[]) Registry.GetValue(keyname, valuename,
                                               new byte[] { });
    RawSecurityDescriptor sd;
    if (keyval.Length > 0) {
        sd = new RawSecurityDescriptor(keyval, 0);
    } else {
        sd = InitializeEmptySecurityDescriptor();
    }
    RawAcl acl = sd.DiscretionaryAcl;

    CommonAce accountACE = null;

    // Look for the account in the ACL
    int i = 0;
    foreach (GenericAce ace in acl) {
        if (ace.AceType == AceType.AccessAllowed) {
            CommonAce c_ace = ace as CommonAce;
            NTAccount account = 
                c_ace.SecurityIdentifier.Translate(typeof(NTAccount))
                as NTAccount;
            if (account.Value.Contains(accountname)) {
                accountACE = c_ace;
            }
            i++;
        }
    }

    // If no ACE found for the given account, insert a new one at the end
    // of the ACL, otherwise just set the mask
    if (accountACE == null) {
        SecurityIdentifier ns_account = 
            (new NTAccount(accountname)).Translate(typeof(SecurityIdentifier))
            as SecurityIdentifier;
        CommonAce ns = new CommonAce(AceFlags.None, AceQualifier.AccessAllowed,
                                     mask, ns_account, false, null);
        acl.InsertAce(acl.Count, ns);
    } else {
        accountACE.AccessMask |= mask;
    }

    // Write security descriptor back to registry
    byte[] binarySd = new byte[sd.BinaryLength];
    sd.GetBinaryForm(binarySd, 0);
    Registry.SetValue(keyname, valuename, binarySd);
}

private static RawSecurityDescriptor InitializeEmptySecurityDescriptor()
{
    var localSystem = 
        new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
    var new_sd =
        new RawSecurityDescriptor(ControlFlags.DiscretionaryAclPresent,
                                  localSystem, localSystem, null,
                                  new RawAcl(GenericAcl.AclRevision, 1));
    return new_sd;
}

Do note that this code is by no means perfect. If the entire registry key value for these ACL is missing in the registry, the ACL that is synthesized will ONLY grant access to the passed account and nothing else. I'm also sure there are lots of error conditions I've not handled properly and details I've glossed over. Again, it's an example of how to deal with DCOM ACL in .NET.

This information is stored in HKCR\AppID\{Your-AppID}\LaunchPermission and AccessPermission. These are REG_BINARY values containing serialized security descriptors. No idea whether there's anything providing convenient access to those from .NET...

More info on MSDN.

I couldn't find any .NET way of doing this - you can use the MS command line utility DCOMPerm (also here) which is part of the SDK.

I found this solution working:

    public static void SetUp()
    {
        SetCOMSercurityAccess("DefaultAccessPermission");
        SetCOMSercurityAccess("DefaultLaunchPermission");
    }
    private static void SetCOMSercurityAccess(string regKey)
    {
        //This is the magic permission!
        byte[] binaryform = new string[]
        {
            "01","00","04","80","80","00","00","00","90","00","00","00","00","00","00","00","14","00","00","00","02","00","6c","00","04",
            "00","00","00","00","00","14","00","1f","00","00","00","01","01","00","00","00","00","00","05","12","00","00","00","00","00",
            "24","00","0b","00","00","00","01","05","00","00","00","00","00","05","15","00","00","00","a3","53","d8","c8","94","bd","63",
            "84","88","bf","fa","cf","a7","2b","00","00","00","00","18","00","1f","00","00","00","01","02","00","00","00","00","00","05",
            "20","00","00","00","20","02","00","00","00","00","14","00","1f","00","00","00","01","01","00","00","00","00","00","05","04",
            "00","00","00","01","02","00","00","00","00","00","05","20","00","00","00","20","02","00","00","01","02","00","00","00","00",
            "00","05","20","00","00","00","20","02","00","00"
        }.Select(o=> Convert.ToByte(o,16)).ToArray();
        Registry.SetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Ole", regKey, binaryform, RegistryValueKind.Binary);
    }

In case it help others...

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!