Active Directory-wachtwoord wijzigen zonder aanmelden

In de school waar ik werk (http://www.burgerschool.be) werken steeds meer leerlingen met een eigen toestel. Ze melden dus niet meer aan op het computernetwerk maar maken wel gebruik van heel wat diensten (bv. draadloos netwerk) in het netwerk.

Elk van die diensten is afgeschermd en enkel bruikbaar voor mensen met een account in onze Active Directory.  Alle leerlingen krijgen dus zo’n account maar kunnen het wachtwoord ervan moeilijk wijzigen omdat ze nooit aanmelden op het domein.

Daarom maakte ik een mini-websiteje die slechts dat doel dient: de mogelijkheid om je Active Directory-wachtwoord te wijzigen zonder dat je bent aangemeld op het domein.

De broncode van die website (geschreven in ASP.NET C#) stel ik hier gratis ter beschikking.

Vragen of opmerkingen? Contacteer mij!

NTFS rechten instellen op remote server (in C#)

Quasi alle taken kunnen gescript worden maar moeilijker wordt het als je zaken wil uitvoeren op een server waarop je code niet draait, maar moeilijk gaat ook.

Voor veel zaken heb je de “System.Management” en “System.Management.Instrumentation” objecten nodig.  Vergeet zeker niet om die dus te referencen.  Het zijn standaard .NET objecten die je zoiezo op elk toestel hebt.

 1: public void CreateFolder(String foldername)
 2: {
 3:         String path = "\" + FileServer + "" + FileDrive + "$" + FileRoot + "" + foldername;
 4:         System.IO.Directory.CreateDirectory(path);
 5: }

FileServer, FileDrive, … zijn properties van de class waaruit ik deze functie heb gepulkt en die vb. kunnen gevuld worden door de constructor.  We gebruiken in deze en andere stukjes voorbeeld code vaak UNC paden waarbij we altijd via de administratieve share gaan (driveletter$) omdat dat zowat de enige share is waarvan je zeker bent. Andere shares kunnen verdwijnen en daarmee ook de goede werking van je code.

Een mapje delen doen we aan de hand van WMI classes (binnen C#)

 1: public void CreateShare(string Sharenaam, string Sharepad)
 2: {
 3:         ManagementScope MgScope = new ManagementScope("\" + FileServer +"rootcimv2");
 4:         ManagementPath MgPathShare = new ManagementPath("Win32_Share");
 5:         ManagementClass classObj = new ManagementClass(MgScope, MgPathShare, null);
 6:         ManagementBaseObject inParams = classObj.GetMethodParameters("Create");
 7:         inParams["Name"] = Sharenaam;
 8:         inParams["Path"] = Sharepad;
 9:         inParams["Type"] = 0; // Type = 0 => Het gaat om een DISK resource
 10:
 11:         ManagementBaseObject outParams = classObj.InvokeMethod("Create", inParams, null);
 12:         uint ret = (uint)(outParams.Properties["ReturnValue"].Value);
 13: }

In regel 3 geven we aan dat we WMI classes gaan aanspreken op onze FileServer, op regel 4 zien we dat het om de Win32_Share class gaat. In regel 5 wordt op basis van die twee zaken een object aangemaakt. De laatste parameter (null) wijst erop dat we geen specifieke connection paramters willen meegeven.  Om de Create functie te gebruiken moeten we een array van parameters meegeven met daarin de ShareNaam, het SharePath en het ShareType (0 = Disk).
En tenslotten NTFS rechten instellen kan op deze manier

 1: public void SetNTFS(string folder, byte[] SID)
 2: {
 3:     string path = @"" + FileServer + "" + FileDrive + "$" + FileRoot + "" + folder;
 4:
 5:     DirectoryInfo info = new DirectoryInfo(path);
 6:     DirectorySecurity security = info.GetAccessControl(AccessControlSections.All);
 7:     security.SetAccessRuleProtection(false, false);
 8:     InheritanceFlags flags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
 9:     SecurityIdentifier sid = new SecurityIdentifier(SID, 0);
 10:     security.AddAccessRule(new FileSystemAccessRule(sid, FileSystemRights.Modify, flags, PropagationFlags.None, AccessControlType.Allow));
 11:     info.SetAccessControl(security);
 12: }

Ook deze code is vrij recht door zee … We spreken de map waarvan we de NTFS settings willen wijzigen aan via het UNC pad.   We zouden hier kunnen opmerken dat we mogelijks een korter UNC pad kunnen gebruiken door rechtstreeks de ShareNaam van de map te gebruiken ipv via de Administratieve Share te gaan. MAAR god mag weten waarom maar bij het direct aanspreken van de share worden de “inherited NTFS instellingen” verwijderd.  We nemen dus het omwegje om dit niet voor te hebben.

In lijn 6 vragen we de huidige NTFS settings op, in lijn 10 voegen we er een extra regel aan toe om het geheel opnieuw weg te schrijven in regel 11.  Voor de nieuwe ACL in regel 10 hebben we een SID nodig dat we maken in regel 9. Op basis van het SID van ene gebruiker maken we een SecurityIdentifier. De 0 is de offset in de byte[], tenzij er nog iets anders in de byte[] zou zitten is dit dus altijd 0.  We hebben ook de inheritance flags nodig en die maken we in regel 8 we zeggen hierbij dat zowel mappen als bestanden de NTFS settings mogen overnemen.

Deze code kan makkelijk geïntegreerd worden in een ASP.NET of Winforms.NET project maar hou rekening met de nodige machtigingen.  Mogelijks moet je hier een beroep doen op Identity Impersonation. (ASP.NET Impersonation voor Active Directory bewerkingen)

Active Directory in .NET (deel 1)

Een tijdje geleden plaatsen we al een artikeltje betreft impersonation in ASP.NET. Impersonation komt oa. om de hoek kijken wanneer we via .NET code onze Active Directory willen manipuleren. In dit artikeltje geven we hieromtrent wat code cadeau (als nieuwjaarscadeau ;).  Opgelet deze code is met momenten zeer ingrijpend en vraagt nooit of u het wel zeker weet. Voorzichtigheid is geboden !

Voor alles hebben we de .NET Class “System.DirectoryServices” nodig dus zeker niet vergeten …

 1: using System.DirectoryServices;
 2: using System.DirectoryServices.ActiveDirectory;

Omdat zowel gebruikers/groepen/… ergens moeten gemaakt worden schrijven we eerst een kleine functie een OU zoekt.  We kunnen een OU ook wel rechstreeks aanspreken maar zijn dan nooit zeker dat hij wel bestaat. Als hij uit ons zoekresultaat komt wel.

 1: private DirectoryEntry Find(String sLdap, String sQuery)
 2: {
 3:     DirectoryEntry Found;
 4:     DirectoryEntry OU = new DirectoryEntry(sLdap, "admin", "admin");
 5:     DirectorySearcher dsZoeken = new DirectorySearcher(OU);
 6:     dsZoeken.Filter = sQuery;
 7:     SearchResult srResultaat = dsZoeken.FindOne();
 8:     if (srResultaat != null)
 9:         Found = srResultaat.GetDirectoryEntry();
 10:     else
 11:         Found = null;
 12:     return Found;
 13: }

Het resultaat van deze routine is een DirectoryEntry object (worst case zit er wel NULL in), dus dat moet je wel even controleren als je de functie gebruikt hebt.  Een user aanmaken in de net gevonden OU kan je doen met onderstaande code.  De eerste parameter van de functie is het DirectoryEntry object wat we net vonden met de vorige routine. UserObject is een zelf gedefinieerd object. (Zie volgende stukje code).

 1: private bool CreateUser(DirectoryEntry deOU, UserObject gebruiker)
 2: {
 3:     bool gelukt = true;
 4:     try
 5:     {
 6:         DirectoryEntries workingOU = deOU.Children;
 7:         DirectoryEntry user = workingOU.Add("CN=" + gebruiker.Gebruikersnaam, "user");
 8:         user.Properties["sAMAccountName"].Add(gebruiker.Gebruikersnaam);
 9:         user.Properties["sn"].Add(gebruiker.Familienaam);
 10:         user.Properties["givenName"].Add(gebruiker.Voornaam);
 11:         user.Properties["Description"].Add("Test Account");
 12:         user.CommitChanges();
 13:     }
 14:     catch (Exception e)
 15:     {
 16:         Console.WriteLine(e.Message);
 17:         gelukt = false;
 18:     }
 19:     return gelukt;
 20:   }

 

 1: public class UserObject
 2: {
 3:     public String Gebruikersnaam { get; set; }
 4:     public String Voornaam { get; set; }
 5:     public String Familienaam { get; set; }
 6:     public String Wachtwoord { get; set; }
 7:     public String Email { get; set; }
 8: }

Met het allereerste stukje code in deze post kan je niet alleen een OU zoeken maar ook een gebruiker. Dat gaan we doen als we een gebruiker willen wijzigen.  Afhankelijk of je een OU of een User zoekt gebruik je de routine als volgt.

 1: DirectoryEntry putithere = Find("LDAP://SomeADServer/DC=Domain,DC=TLD", "OU=lookingforthisou");
 2: of
 3: DirectoryEntry putithere = Find("LDAP://SomeADServer/DC=Domain,DC=TLD", "SAMAccountName=lookingforthisou");
 4:
 5: if (putithere != null)
 6:     //object is gevonden
 7: else
 8:     //object is niet gevonden
 9:

Bij vragen of opmerkingen hoor ik graag van u!