【问题标题】:dcomcnfg functionality programmaticallydcomcnfg 功能以编程方式
【发布时间】:2010-10-02 01:08:05
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: c# .net security permissions dcom


    【解决方案1】:

    我找不到任何 .NET 方式来执行此操作 - 您可以使用作为 SDK 一部分的 MS 命令行实用程序 DCOMPerm(也是 here)。

    【讨论】:

    • 是否还有 dcomper.exe 存在?我在任何地方都找不到预编译版本,指向它的链接似乎已失效
    【解决方案2】:

    此信息存储在HKCR\AppID\{Your-AppID}\LaunchPermissionAccessPermission 中。这些是包含序列化安全描述符的 REG_BINARY 值。不知道是否有任何东西可以方便地访问 .NET...

    更多关于MSDN的信息。

    【讨论】:

      【解决方案3】:

      面对类似的情况(从 MSI 配置 DCOM 安全性),我设法通过更改 HKEY_CLASSES_ROOT\AppID{APP-GUID-GOES-HERE} 中的注册表项值创建了一个解决方案,可以满足我的需求。感谢 Arnout 的回答让我走上了正确的道路。

      具体来说,我创建了一个方法来编辑 DCOM 对象的安全权限,这些对象存储在 LaunchPermission 和 AccessPermission 注册表项值中。这些是序列化的安全描述符,您可以通过RawSecurityDescriptor 传递二进制数据来访问它们。这个类以一种美味的 .NET-y 方式简化了许多细节,但是您仍然需要关注有关 Windows ACL 的所有逻辑细节,并且您必须确保使用 @ 将安全描述符写回注册表987654322@.

      我创建的方法名为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 的示例

      【讨论】:

      • 哎呀!刚刚注意到你的回答,抱歉。我会尝试一下,希望很快,然后回复你。看起来我们需要一个完整的图书馆。
      【解决方案4】:

      丹尼尔发布的答案非常有帮助。非常感谢,丹尼尔!

      Microsoft's documentation 的一个问题是它们表明注册表值包含二进制形式的 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);
          }
      }
      

      【讨论】:

      • 你和丹尼尔似乎都在正确的轨道上。这一切仍然如此痛苦,但它确实有效。我很纠结该信任谁。但是您看起来可以更多地使用积分。 :)
      • 不客气。我通过追踪 DCOMPerm 的源代码并分析它在做什么(在 C++ 中)获得了我的大部分详细信息。
      【解决方案5】:

      我发现这个解决方案有效:

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

      万一它帮助别人......

      【讨论】: