Quantcast
Channel: わんくま同盟
Viewing all articles
Browse latest Browse all 994

セキュリティ記述子(SecurityDescriptor)の出力の.NETクラス使用版(C#)

$
0
0

セキュリティ記述子(SecurityDescriptor)の出力の.NETクラス使用版の C# のコードです。

Active Directoryデータのプロパティ出力のCOM対応版(C#)と比較しながら見ていただければと思います。

コードはサンプルアプリに組み込む前提で書いてます。

追加で System.Security.AccessControl 名前空間をインポートしてます。

 

まずは呼出し側。出力部分はメソッド化しました。

public static void OutputProperties(DirectoryEntry entry, string filePath)

{

  var props = entry.Properties.PropertyNames.Cast<string>().OrderBy(s => s).ToList();   //プロパティ名のリスト

  using (var writer = newStreamWriter(filePath, false, Encoding.UTF8))

  {

    foreach (var pname in props)  //プロパティ数分

    {

      if (pname.Equals("nTSecurityDescriptor"))   //セキュリティ記述子の時

      {

        writer.WriteLine(pname);

        OutputSecurityDescriptor(entry.ObjectSecurity, writer);   //セキュリティ記述子を出力

        continue;

      }

 

      var val = entry.Properties[pname].Value;

      if (val is byte[])  //バイト配列の時

      {

        var pstr = GetByteValue(pname, (byte[])val);  //バイト値を取得

        writer.WriteLine("{0}:{1}", pname, pstr);

      }

      else if (val is IADsLargeInteger)   //大きい整数の時

      {

        var li = GetLargeIntegerValue(pname, (IADsLargeInteger)val);  //大きい整数値を取得

        writer.WriteLine("{0}:{1}", pname, li);

      }

      else  //それ以外の時

      {

        foreach (var pval in entry.Properties[pname])   //値数分

        {

          var value = GetValue(pval);  //値を取得

          writer.WriteLine("{0}:{1}", pname, value);

        }

      }

    }

  }

}

 

続いて出力処理側。DirectoryEntry.ObjectSecurityプロパティのとこに少し書きましたが、Trustee の名前部分(ユーザ名やグループ名)を取得する必要があるので、別途メソッド化しました。

//セキュリティ記述子を出力

private static void OutputSecurityDescriptor(ActiveDirectorySecurity security, StreamWriter writer)

{

  var sd = newCommonSecurityDescriptor(true, true, security.GetSecurityDescriptorBinaryForm(), 0);

  var aceList = sd.DiscretionaryAcl.Cast<QualifiedAce>().ToList();

  var sidList = aceList.GroupBy(ace => ace.SecurityIdentifier).Select(

    group => group.First().SecurityIdentifier.Value).ToList();

  var sidNameDic = CreateSidNameDictionary(sidList);  //SID/アカウント名のコレクションを作成

  var aceGroups = aceList.OrderBy(ace => sidNameDic[ace.SecurityIdentifier.Value]).ThenBy(

    ace => ace.AccessMask).ThenBy(ace => ace.AceFlags).ThenBy(ace => ace.AceType).ThenBy(

    ace => (ace isObjectAce) ? ((ObjectAce)ace).ObjectAceFlags : 0).GroupBy(

    ace => String.Format("{0}|{1}|{2}|{3}|{4}",

      sidNameDic[ace.SecurityIdentifier.Value], ace.AccessMask, ace.AceFlags, ace.AceType,

      (ace isObjectAce) ? ((ObjectAce)ace).ObjectAceFlags : 0)).ToList();   //プロパティ値でグループ化したACE

  var ctr = 0;

 

  foreach (var aceGroup in aceGroups)   //ACE数分

  {

    var ace = aceGroup.First();

    ctr++;

    writer.WriteLine(" {0:D2}. Trustee   :{1}", ctr, sidNameDic[ace.SecurityIdentifier.Value]);

    writer.WriteLine(" {0:D2}. AccessMask:{1}", ctr, ToEnumValueText(ace.AccessMask, typeof(ActiveDirectoryRights)));

    writer.WriteLine(" {0:D2}. AceFlags  :{1}", ctr, ToEnumValueText((int)ace.AceFlags, typeof(AceFlags)));

    writer.WriteLine(" {0:D2}. AceType   :{1}", ctr, ToEnumValueText((int)ace.AceType, typeof(AceType)));

    if (ace isObjectAce)   //ディレクトリ オブジェクトに関連付けられたACEの時

    {

      writer.WriteLine(" {0:D2}. Flags     :{1}", ctr,

        ToEnumValueText((int)((ObjectAce)ace).ObjectAceFlags, typeof(ObjectAceFlags)));

    }

    else  //CommonAce(ACE)の時

    {

      writer.WriteLine(" {0:D2}. Flags     :0", ctr);

    }

  }

}

 

//SID/アカウント名のコレクションを作成

private staticDictionary<string, string> CreateSidNameDictionary(List<string> sidList)

{

  var sidNameDic = newDictionary<string, string>(sidList.Count);

  using (var root = GetRootEntry())   //ルートのDirectoryEntryを取得

  {

    using (var searcher = newDirectorySearcher(root))

    {

      foreach (var sid in sidList)  //SID数分

      {

        searcher.Filter = String.Format("(objectSid={0})", sid);

        var res = searcher.FindOne();   //検索

        if (res == null//見つからなかった時(SYSTEM、SELF、Everyoneなど)

        {

          var acc = newSecurityIdentifier(sid).Translate(typeof(NTAccount));   //アカウントに変換

          sidNameDic.Add(sid, System.IO.Path.GetFileName(acc.Value));

        }

        else  //見つかった時

        {

          using (var entry = res.GetDirectoryEntry())

          {

            var objectType = (CategoryType)Enum.Parse(typeof(CategoryType), entry.SchemaClassName, true);

            if (objectType == CategoryType.ForeignSecurityPrincipal)  //外部のセキュリティプリンシパルの時

            {

              var acc = newSecurityIdentifier(sid).Translate(typeof(NTAccount));   //アカウントに変換

              sidNameDic.Add(sid, System.IO.Path.GetFileName(acc.Value));

            }

            else  //外部のセキュリティプリンシパル以外の時

            {

              sidNameDic.Add(sid, entry.Properties["cn"].Value.ToString());

            }

          }

        }

      }

    }

  }

  return sidNameDic;

}

 

最後に既存のメソッド。内部実装を変更しました。

//列挙体のプロパティ値をテキスト化

private static string ToEnumValueText(int value, Type enumType)

{

  if (enumType == typeof(AceType))  //AceTypeの時

  {

    return String.Format("{0}({1})", value, Enum.ToObject(enumType, value));

  }

 

  Func<int, string> selector = e => Enum.ToObject(enumType, e).ToString();

  var values = Enum.GetValues(enumType).Cast<object>().Select(e => Convert.ToInt32(e)).Where(

    e => (value & e) == e).OrderBy(selector).Select(selector).ToList();   //設定されている値の列挙体文字列

 

  values.Remove("None");

  if (values.Count == 0)   //設定されている値がない時

  {

    return value.ToString();

  }

  return String.Format("{0}({1})", value, String.Join(" | ", values));

}


Viewing all articles
Browse latest Browse all 994

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>