以编程方式实现dcomcnfg功能

7

我可以找到各种关于DCOM编程的资料,但几乎没有关于如何以编程方式设置/检查安全性的内容。

我并不是要重新创建dcomcnfg,但如果我知道如何在C#(首选)或VB.net中复制dcomcnfg的所有功能,那么我的目标就能实现。

我似乎找不到任何好的资源,没有开源API甚至没有如何执行每个步骤的快速示例。即使在这里搜索DCOM或dcomcnfg也只有很少的结果,而且没有关于如何设置/验证/列出安全性的内容。

如果有人能提供一些指向开放API或示例的指针,我将不胜感激。

5个回答

12

Daniel发布的答案非常有帮助。非常感谢你,Daniel!

Microsoft的文档存在一个问题,即它们指示注册表值以二进制形式包含ACL。因此,例如,如果您尝试设置机器的默认访问权限(而不是每个进程),则将访问注册表键HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole\DefaultAccessPermission。但是,在我最初尝试使用System.Security.AccessControl.RawACL类访问此键时失败了。

正如Daniel的代码所示,该值实际上不是ACL,而是带有ACL的SecurityDescriptor。

因此,即使我知道这篇文章已经过时,我仍然要发布我的解决方案,以检查和设置安全设置并添加NetworkService以进行默认本地访问。当然,您肯定可以采取这个方法并使其更好,但要开始,您只需要更改密钥和访问掩码。

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);
    }
}

你和丹尼尔似乎都在正确的轨道上。这一切仍然很痛苦,但它起作用了。我不知道该给谁功劳。但是看起来你更需要这些积分。 :) - PerryJ
1
不客气。我通过追踪DCOMPerm的源代码并分析其功能(使用C++)来获取大部分细节。 - Daniel Bruce
1
这可能也会有所帮助:https://github.com/pauldotknopf/WindowsSDK7-Samples/tree/master/com/fundamentals/dcom/dcomperm - NTDLS

7
面对类似的情况(在MSI中配置DCOM安全性),我通过修改HKEY_CLASSES_ROOT\AppID{APP-GUID-GOES-HERE}注册表键值来创建一个符合要求的解决方案。感谢Arnout的回答,让我找到了正确的路径。
具体而言,我创建了一种方法来编辑DCOM对象的安全权限,这些权限存储在LaunchPermission和AccessPermission注册表键值中。这些是序列化的安全描述符,你可以通过将二进制数据传递给RawSecurityDescriptor来访问它们。这个类以美味的.NET方式简化了很多细节,但您仍然必须注意所有关于Windows ACL的逻辑细节,并使用RawSecurityDescriptor.GetBinaryForm确保将安全描述符写回注册表。
我创建的方法名为EditOrCreateACE。这个方法会编辑一个现有的账户ACE,或者插入一个新的ACE,并确保访问掩码设置了传递的标志。我在这里附上它作为一个示例,这绝不是处理它的权威,因为我对Windows ACL的东西知之甚少:
// 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;
}

请注意,这段代码并不完美。如果这些ACL的整个注册表键值在注册表中丢失,那么合成的ACL将只授予传递的帐户访问权限,而不能授予其他任何东西。我确信还有许多错误条件我没有妥善处理和细节问题我没有详细说明。再次强调,这是处理.NET中DCOM ACL的示例。

哎呀!刚注意到你的回答,很抱歉。我会尽快试一下,并回复你。看起来我们需要一个完整的库来解决这个问题。 - PerryJ

4

我找不到任何 .NET 的方法来完成这个任务 - 你可以使用微软的命令行工具 DCOMPerm (也可以在这里找到),它是SDK的一部分。


1
有没有可能还有一个dcomper.exe存在?我再也找不到预编译版本了,链接似乎已经失效了。 - Max

4
这些信息存储在HKCR\AppID\{Your-AppID}\LaunchPermissionAccessPermission中。它们是包含序列化安全描述符的REG_BINARY值。不知道是否有任何提供方便访问这些内容的.NET工具...更多信息请参考MSDN

0

我发现这个解决方案可行:

    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);
    }

如果有助于他人的话...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接