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!

Lazy evaluation in PHP, ASP.NET, …

Wanneer ik gevraagd word als consultant op vlak van performance tuning van maatsoftware is er één zaak die bijna elke keer terugkomt. Heel wat programmeurs maken onvoldoende gebruik van Lazy evaluation. Quasi elke programmeertaal voorziet standaard in lazy evaluation. Eenvoudig gezegd komt het hierop hier dat een programmeertaal stopt met vergelijkingen evalueren op het ogenblik dat het toch geen zin meer heeft. Een voorbeeldje.

Je schrijft een functie die een getal als input krijgt en als dat getal ofwel deelbaar is door 4 ofwel een priemgetal dan returned de functie true anders false.

if (($input % 4 == 0) || (ispriemgetal($input)))
{
    return true;
}
else
{
    return false;
}

Als onze invoer variabele gelijk is aan pakweg 12 dan zal het tweede gedeelte van de if constructie niet meer uitgevoerd worden omdat het toch geen zin heeft, wat het resultaat ook is van “ispriemgetal($input)” het eindresulaat is toch true.  Het is dus belangrijk om de minst cpu intensieve vergelijking eerst te plaatsen zodat de meer cpu intensieve vergelijkingen niet uitgevoerd worden als dat niet echt nodig is. Ons voorbeeld is dus correct vanuit een performance tuning standpunt omdat we ervan mogen uitgaan dat controleren of een getal deelbaar is door 4 minder kracht vergt dan controleren of een getal een priemgetal is.  Dit zou minder goed zijn :

if ((ispriemgetal($input)) || ($input % 4 == 0))

In dit concreet geval zou het trouwens echt zinloos zijn omdat een priemgetal per definitie niet deelbaar is door 4 of een getal deelbaar door 4 per definitie geen priemgetal kan zijn.

FSRM API in C# gebruiken

In sommige situaties is het interessanter om quota’s te plaatsen op mappen ipv die te koppelen aan personeen. Het klassieke quota systeem ingebouwd in Windows 2003 is hiervoor niet geschikt. In Windows 2003 SP2 en Windows 2008 komt daarvoor echter een oplossing nl. de File System Resource Manager of kortweg FSRM waarin quota’s op mappen kunnen geplaatst worden.

Hoewel er geen .NET Wrapper is voor de FSRM API kan deze toch heel makkelijk gebruikt worden in C#.  Het volstaat de de juiste DLL (c:windowssystem32srm.dll) te referencen omd aan de slag te kunnen.

Enkele code voorbeelden.  Quota plaatsen op een map waar nog geen quota op staat kan als volgt.

 1: public static bool AddDiskQuota(string Folder, double quota)
 2: {
 3:     FsrmQuotaManager quotao = new FsrmQuotaManager();
 4:     IFsrmQuota quotadef = quotao.CreateQuota(Folder);
 5:     quotadef.QuotaLimit = quota * (1024 * 1024);
 6:     quotadef.Commit();
 7:     return true;
 8: }

Nagaan hoeveel quota op een bepaalde map werd toegekend kan met volgende code

 1: public static double GetDiskQuota(string Folder)
 2: {
 3:         FsrmQuotaManager quota = new FsrmQuotaManager();
 4:         if (quota.GetQuota(Folder) != null)
 5:             return (Convert.ToDouble(quota.GetQuota(Folder).QuotaLimit));
 6:         else
 7:             return -1;
 8: }

Ook handig is de mogelijkheid om na te gaan hoeveel schijfruimte er al gebruikt wordt binnen de map

 1: public static double GetUsedQuota(string folder)
 2: {
 3:         FsrmQuotaManager quota = new FsrmQuotaManager();
 4:         if (quota.GetQuota(folder) != null)
 5:             return (Convert.ToDouble(quota.GetQuota(folder).QuotaUsed));
 6:         else
 7:             return -1;
 8: }

Er kan max. één quota entrie op een map geplaatst worden, daarom is het goed vb. de GetUsedQuota functie te gebruiken om na te gaan of er al een Quota entry op de map staat.   Als dat het geval is kan je de entry aanpassen ipv een nieuwe aan te maken.

 1: public static bool ChangeDiskQuota(string Folder, double quota)
 2: {
 3:         if (GetDiskQuota(Folder) != Convert.ToDouble(-1))
 4:         {
 5:             FsrmQuotaManager quotao = new FsrmQuotaManager();
 6:             IFsrmQuota test = quotao.GetQuota(Folder);
 7:             test.QuotaLimit = quota * (1024 * 1024);
 8:             test.Commit();
 9:             return true;
 10:         }
 11:         else
 12:             return false;
 13: }

Happy coding ! En je weet het … als er vragen of opmerkingen zijn, dan hoor ik graag van u!

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)

“Forms Authentication” by Meneer.Depuydt

We maken allemaal wel eens fouten, zo ook meneer.depuydt. Een tijdje terug bouwde ik een backoffice waarbij de beveiliging werd afgehandeld door middel van Forms Authentication. De theorie is leuk, bijna niets programmeren en alles wat je kan dromen op gebied van authenticatie en authorisatie is voorzien. Accounts bijmaken, wachtwoorden resetten, wachtwoorden wijzigen … In theorie fijn tot je net iets meer wil dan voorzien is. Een stukje bijbouwen is gekkenwerk als het al niet onmogelijk is.

Alleen idioten veranderen nooit van visie en daarom besliste ik Forms Authentication vaarwel te zeggen en de authenticatie terug zelf in handen te nemen maar wat te doen met de door de Forms Authentication gebouwde tabellen en de daarin zittende duizenden gebruikers?  Iedereen een mailtje sturen dat hun login veranderd is? Weinig professioneel…

Wat research leerde ons hoe de Forms Authentication tabellen in elkaar zaten. Elke gebruiker heeft naast zijn login een SALT waarde en een geëncrypteerd wachtwoord. We hoeven niet terug te kunnen keren naar het ongeencrypteerde wachtwoord (dat is trouwens onmogelijk) als we op basis van de SALT en het geëncrypteerde wachtwoord het door een gebruiker ingetikte wachtwoord kunnen verifiëren zijn we al lang blij.

En dat kan … met deze code.

 1: public string CheckPassword(string salt, string plaintext)
 2: {
 3:     byte[] un64basesalt = Convert.FromBase64String(salt);
 4:     byte[] un64plaintext = ASCIIEncoding.Unicode.GetBytes(plaintext);
 5:
 6:     int lengtesalt = un64basesalt.Length;
 7:     int lengteplain = un64plaintext.Length;
 8:
 9:     byte[] plainTextWithSaltBytes = new byte[lengtesalt+lengteplain];
 10:
 11:     for (int i = 0; i < un64basesalt.Length; i++)
 12:         plainTextWithSaltBytes[i] = un64basesalt[i];
 13:
 14:     for (int i = 0; i < un64plaintext.Length; i++)
 15:         plainTextWithSaltBytes[lengtesalt + i] = un64plaintext[i];
 16:
 17:     HashAlgorithm hash;
 18:     hash = new SHA1Managed();
 19:
 20:     byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
 21:     return Convert.ToBase64String(hashBytes);
 22: }

Een woordje uitleg. De Salt waarde die je in de DB aantreft is met BASE64 gecodeerd, je kan die makkelijk decoderen (regel 3).  Het plaintext wachtwoord ingetikt door een gebruiker zetten we net als de SALT om in een byte array (regel 4). Van beide zaken vragen we de lengte op (6+7).  Vervolgens kleven we de byte array van de salt en het plain text wachtwoord achter elkaar (eerste wachtwoord dan de salt) (11,12 + 14,15) en her resultaat daarvan gaan we ‘hashen’ met het SHA1 algoritme. (17).

Het bekomen resultaat kan vergeleken worden met de geencrypteerde waarde in de databank. Als die overeenstemmen is het wachtwoord van de gebuiker correct. Uit de hele DB structuur van de forms authentication moeten we dus enkel de logins, salts en geencrypteerde wachtwoorden overnemen in onze nieuwe applicatie.

Hebt u nog vragen of opmerkingen, dan hoor ik graag van u!

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!

Afstand tussen twee GPS coordinaten berekenen

In alsmaar meer maatsoftware die we opleveren komt ergens wel de vraag “we willen een lijst van klanten/leveranciers in de buurt van …” Tot voor een tijdje werkten we daarom met een query op postcode (select * from tabel where postcode like ‘85%’).  Het gaf een vrij goed idee maar effectief gaan filteren op adressen die in een straal van X km rond een bepaald punt liggen is natuurlijk veel exacter.  Sinds kort hebben we dit in een aantal pakketten geïntegreerd; het berekenen van de afstand tussen twee punten is in principe vrij simpel.

 1: public Double GetDistance(Double[] from, Double[] to)
 2:  {
 3:      Double dLat1LnRad = from[0] * (Math.PI / 180.0);
 4:      Double dLong1LnRad = from[1] * (Math.PI / 180.0);
 5:      Double dLat2LnRad = to[0] * (Math.PI / 180.0);
 6:      Double dLong2LnRad = to[1] * (Math.PI / 180.0);
 7:
 8:      Double dLongitude = dLong2LnRad - dLong1LnRad;
 9:      Double dLatitude = dLat2LnRad - dLat1LnRad;
 10:
 11:      Double a = Math.Pow(Math.Sin(dLatitude / 2.0), 2.0) + Math.Cos(dLat1LnRad) * Math.Cos(dLat2LnRad) * Math.Pow(Math.Sin(dLongitude / 2.0), 2.0);
 12:      Double c = 2.0 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1.0 - a));
 13:
 14:      const Double kEarthRadiusKms = 6376.5;
 15:      return kEarthRadiusKms * c;
 16:  }

De moeilijkheid was om op een vlotte manier aan de juiste gps coordinaten te komen en dat is nu vrij simpel geworden dankzij Google Earth API.

 1: private const string _googleUri = "http://maps.google.com/maps/geo?q=";
 2: private const string _googleKey = "Get Your Own Free Google Key";
 3: private const string _outputType = "csv";
 4:
 5: public Double[] GetCoordinates(string address)
 6: {
 7:     address = HttpUtility.UrlEncode(address);
 8:     WebClient client = new WebClient();
 9:
 10:     string[] geocodeInfo = client.DownloadString(new Uri(String.Format("{0}{1}&output={2}&key={3}", _googleUri, address, _outputType, _googleKey))).ToString().Split(',');
 11:     Double lat = Convert.ToDouble(geocodeInfo[2].Replace(".", ","));
 12:     Double lon = Convert.ToDouble(geocodeInfo[3].Replace(".",","));
 13:     Double[] returnvalue = new Double[2];
 14:     returnvalue[0] = lat;
 15:     returnvalue[1] = lon;
 16:
 17:     return returnvalue;
 18: }

Als je nu wil weten welke adressen in een straal van 30 km rond je bedrijf liggen kan je dat eenvoudig berekenen door je eigen gps coordinaten op te vragen en dan alle adressen in je databank overlopen om te zien of ze verder of minder ver dan 30 km van je bedrijf verwijdert liggen. Voor kleine databanken is dat haalbaar voor grotere is het behalve CPU intensief ook IO intensief en het kan dus intelligenter.

Bepalen welke GPS coordinaten in een straal van x KM rond je bedrijf liggen is mogelijk maar onpraktisch, je zou in detail elk puntje op de cirkellijn moeten bijhouden en elk gps coordinaat van je contacten moeten afwegen bij de cirkellijn.  We zouden het moeten testen maar hoewel het intelligenter lijkt zou het waarschijnlijk meer belastend zijn voor de server dan de eerste oplossing.

We kozen ervoor een vierkant te tekenen waar onze cirkel net in kan. Op basis van de coordinaat links boven en die rechtsonder kunnen we een SQL commando schrijven die de adressen selecteert die in het vierkant zitten. We moeten dan enkel manueel nog de adressen in de paarse zone eruit filteren.

figuur

ASP.NET Impersonation voor Active Directory bewerkingen

Een tijdje geleden ontwikkelde ik een kleine Intranet toepassing waarmee gebruikers beperkte acties kunnen uitvoeren op de Active Directory.   Bij het programmeren ontstond intern al snel een discussie omtrent het veiligheidsaspect van deze applicatie.

De code voorziet oa. in de mogelijkheid om gebruikers aan te maken, die gebruikers toe te voegen aan groepen, het paswoord van een gebruiker te wijzigen (zonder het oude te kennen), enz … Als “iedereen” die code zou kunnen uitvoeren is er uiteraard sprake van een enorm veiligheidsprobleem. Uiteraard heeft Microsoft ervoor gezorgd dat dit niet kan. Enkel wie effectief het recht heeft om zo’n zaken te doen op Active Directory kan ook de code uitvoeren. Nu is de vraag “wie voert de code uit eenmaal ze binnen IIS draait?”.

In “the good old days” toen we nog volop aan de slag waren met classic ASP was het vrij eenvoudig. Wanneer een site vrij toegankelijk was (anonymous logon) werd code uitgevoerd door IUSR_Computername of IWAM_Computername. Als je je code toegang wou geven tot een bepaalde map/bestand volstond het om die gebruikers de gepaste toegang te geven. Het is overigens eenvoudig om dit gedrag te wijzigen. Bij het opvragen van de eigenschappen van een site, tabblad “Directory”, kan je zelf een gebruiker instellen die gebruikt wordt wanneer mensen “zomaar” toegang hebben tot je site. Stel je hier dus een administrator in, dan heeft je code quasi onbeperkte toegang tot je systeem.

Wanneer mensen moeten inloggen om je site/code te bezoeken wordt de code uitgevoerd met de rechten die de persoon heeft die aanmeldt.  Wanneer er niet aan fout opvolging gedaan wordt kan je dus een site hebben die in bepaalde situaties prima werkt (voldoende rechten om alles te doen) en diezelfde site die continue de mist in gaat (onvoldoende rechten om het nodige te doen).  Dit is wat we “impersonation” noemen.

In ASP.NET is het toch wel even anders. Als men niet moet inloggen om je website te bezoeken dan wordt je code uitgevoerd met de rechten van de “Identity” van de application pool waar je website in ondergebracht is. Als je moet inloggen om je website te bezoeken dan … wordt je code nog steeds uitgevoerd met de rechten van de “Identity” van de application pool waar je website in ondergebracht is. Impersonation werkt nooit in asp.net tenzij je er nadrukkelijk om vraagt. Nadrukkelijk … <identity impersonate=”true” /> toevoegen aan de system.Web sectie van je web.config volstaat.  Als men moet aanmelden om je site te bezoeken wordt je code uitgevoerd met de rechten van de aangemelde gebruiker. Eenvoudig, toch? Jammer, maar het is nog niet gedaan.

Wanneer de authenticatie van je website op NTLM gebaseerd is (bvb. Integrated Windows authentication) moet je rekening houden met de “one hop rule”.  De “one hop rule” is een trendy benaming voor het feit dat NTLM login gegevens niet kunnen doorgestuurd worden.  Dus concreet … zolang je code betrekking heeft op objecten binnen de webserver is er geen probleem (mappen op de webserver aanmaken bvb), je code wordt uitgevoerd met de rechten van de aangemelde gebruiker. Van zodra je code zaken wil gaan doen met objecten buiten de webserver (bvb. de SQL server, de domeincontroller, …) vervalt de impersonation en zal je code opnieuw uitgevoerd worden met de rechten van de “identity” van de application pool waar je website in ondergebracht is.

Misschien is dit gewenst gedrag, maar misschien ook niet. De “one hop rule” is niet van toepassing wanneer gebruikersnaam en paswoord worden meegegeven in de web.config => <identity impersonate=”true” userName=”domainuser” password=”xxx” /> in de system.Web sectie zorgt ervoor dat je code zoiezo wordt uitgevoerd met de rechten van de ingestelde gebruiker, ook als de website zonder login toegankelijk is en het maakt niet langer uit of je code nu iets doet op de webserver zelf of eender waar in het domein.  Het nadeel hier is dat de code een bepaalde hoeveelheid rechten krijgt ongeacht wie aangemeld is.  Je hebt niet de mogelijkheid te zeggen “als X aanmeldt, krijgt hij de rechten van X”. Wil je dit bekomen dan behoud je de eenvoudige impersonate code in web.config en schakel je over naar Kerberos gebaseerde authenticatie, die is immers ook niet onderhevig aan de “one hop rule”.