/* ADCSVDUMP - A tool to dump users from Active Directory in the CSV format (Substitutes the csvde tool) Copyright (C) 2007 Michele Baldessari This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Configuration; using System.DirectoryServices; using System.IO; using System.Text; using System.Security.Principal; namespace adcsvdump { class CSVProducer { System.Configuration.Configuration Config; ArrayList Attributes = null; string Domain = String.Empty; string Separator = String.Empty; private void ParseConfigFile() { if (Config.AppSettings.Settings.Count == 0) { Config.AppSettings.Settings.Add("filter", "(&(!(objectClass=computer))(objectClass=person))"); Config.AppSettings.Settings.Add("field1", "distinguishedName"); Config.AppSettings.Settings.Add("field2", "sAMAccountName"); Config.AppSettings.Settings.Add("field3", "mail"); Config.AppSettings.Settings.Add("field4", "sn"); Config.AppSettings.Settings.Add("field5", "description"); Config.AppSettings.Settings.Add("field6", "givenName"); Config.AppSettings.Settings.Add("field7", "displayName"); Config.AppSettings.Settings.Add("field8", "homeDirectory"); Config.AppSettings.Settings.Add("field9", "homeDrive"); Config.AppSettings.Settings.Add("field10", "pwdLastSet"); Config.AppSettings.Settings.Add("field11", "logonCount"); Config.AppSettings.Settings.Add("field12", "sAMAccountName"); Config.AppSettings.Settings.Add("field13", "userPrincipalName"); Config.AppSettings.Settings.Add("field14", "scriptPath"); Config.AppSettings.Settings.Add("field15", "primaryGroupID"); Config.AppSettings.Settings.Add("Separator", ";"); Config.AppSettings.Settings.Add("Domain", "DOMAINNAME"); Config.AppSettings.Settings.Add("firstline", "false"); Config.Save(ConfigurationSaveMode.Modified); ConfigurationManager.RefreshSection("appSettings"); } AppSettingsReader reader = new AppSettingsReader(); NameValueCollection appStgs = ConfigurationManager.AppSettings; string[] names = ConfigurationManager.AppSettings.AllKeys; String value = String.Empty; for (int i = 0; i < appStgs.Count; i++) { string key = names[i]; value = (String)reader.GetValue(key, value.GetType()); if (key.StartsWith("field")) { Attributes.Add(value); } } Domain = ConfigurationManager.AppSettings["Domain"]; Separator = ConfigurationManager.AppSettings["Separator"]; } public static string SIDtoString(byte[] sidBinary) { SecurityIdentifier sid = new SecurityIdentifier(sidBinary, 0); return sid.ToString(); } public static string GetRidFromSid(string Sid) { int index = Sid.LastIndexOf("-"); if (index == -1) return ""; return Sid.Substring(index+1); } public static string GetBaseFromSid(string Sid) { int index = Sid.LastIndexOf("-"); if (index == -1) return ""; return Sid.Substring(0, index); } public string GetAttributeFromSid(string SID, string Attribute) { SearchResult result = null; try { DirectoryEntry entry = new DirectoryEntry("LDAP://" + Domain); DirectorySearcher dSearch = new DirectorySearcher(entry); dSearch.PageSize = 500; dSearch.Filter = string.Format("(objectSid={0})", SID); result = dSearch.FindOne(); return result.Properties[Attribute][0].ToString(); } catch { return String.Empty; } } public void WriteCSV(string filename) { int ErrorCount = 0; Hashtable ResolvedGroups = new Hashtable(); TextWriter streamWriter = new StreamWriter(filename); if (ConfigurationManager.AppSettings["firstline"].ToLower().CompareTo("true") == 0) { string t = ""; foreach(string field in Attributes) { t = t + field + Separator; } streamWriter.Write(t + Environment.NewLine); } string Domain = ConfigurationManager.AppSettings["Domain"]; ArrayList attributeValues = null; SearchResultCollection results = null; try { Console.WriteLine(Domain); DirectoryEntry entry = new DirectoryEntry("LDAP://" + Domain); DirectorySearcher dSearch = new DirectorySearcher(entry); dSearch.PageSize = 500; dSearch.Filter = ConfigurationManager.AppSettings["filter"]; results = dSearch.FindAll(); foreach (SearchResult searchResult in results) { // FIXME: Use Hashtable and bring this loop to O(1) // FIXME: Multiple attributes aren't considered yet attributeValues = new ArrayList(); string s = ""; foreach(string field in Attributes) { if (searchResult.Properties.Contains(field)) { if (field.CompareTo("primaryGroupID") == 0) { byte[] tmp = (byte[])searchResult.Properties["objectSid"][0]; string group = searchResult.Properties["primaryGroupID"][0].ToString(); string sid = SIDtoString(tmp); string groupsid = GetBaseFromSid(sid) + "-" + group; if (ResolvedGroups.ContainsKey(groupsid)) { s = s + ResolvedGroups[groupsid] + Separator; } else { string groupname = GetAttributeFromSid(groupsid, "sAMAccountName"); if (groupname == "") { Console.WriteLine("ERROR resolving :" + groupsid); s = s + "ERROR resolving group " + Separator; ErrorCount += 1; } else { ResolvedGroups.Add(groupsid, groupname); s = s + groupname + Separator; } } } else { attributeValues.Add(searchResult.Properties[field][0].ToString()); s = s + searchResult.Properties[field][0].ToString() + Separator; } } else // Field doesn't exist, just add Separator { s = s + Separator; } } streamWriter.Write(s + Environment.NewLine); } Console.WriteLine("Lines written:" + results.Count); } catch (Exception e) { Console.WriteLine("Exception : " + e.Message); Console.WriteLine("Source : " + e.Source); Console.WriteLine("Stack : " + e.StackTrace); ErrorCount += 1; } finally { if (null != results) { results.Dispose(); results = null; } } streamWriter.Close(); if (ErrorCount > 0) Console.WriteLine("Errors occurred: " + ErrorCount); } public void Usage() { Console.WriteLine("Usage: adcsvdump.exe [-h] [outputfile]"); Console.WriteLine(); Console.WriteLine("-h : Prints this help page"); Console.WriteLine("[outputfile] : The CSV output file. If not specified \"out.csv\" will be used."); Console.WriteLine(); } public string ParseCommandLine(string[] args) { string csvfile = "out.csv"; foreach (string arg in args) { if (arg.StartsWith("-h")) { Usage(); Environment.Exit(0); } else csvfile = arg; } return csvfile; } public CSVProducer(string[] args) { string filename = ParseCommandLine(args); Attributes = new ArrayList(); Config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); Console.WriteLine("Config : " + filename); ParseConfigFile(); WriteCSV(filename); } public static void Main(string[] args) { CSVProducer csv = new CSVProducer(args); } } }