tag:blogger.com,1999:blog-12612924750253298612024-03-06T01:20:43.637+11:00Identity EngineeringA collection of tools, guidance and random thoughts about authentication, authorization and identity managementRyan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.comBlogger28125tag:blogger.com,1999:blog-1261292475025329861.post-55741438542963428812019-01-28T11:54:00.004+11:002023-03-21T06:54:22.588+11:00Getting started with Lithnet Password Protection - Part 1 - Blocking compromised passwords with the 'Have I Been Pwned?' password list<div class="separator" style="clear: both; text-align: center;">
<img border="0" data-original-height="96" data-original-width="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQq4QpyP9VZP8AZcLs6pred4e8d4OL2caWbzrlN_0wwKiIQFyQvWPc4pScl9nIVPOPVkdvoWNRrDOiHiJVkv84_7uXsKDBIarLR6siZdoZe-lIknMLGdGpk_RWHgZc9BB-cWN7GQLJKY1w/s1600/shield+icon+96x96.png" style="display: none;" /></div><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-W9lzdD5MQ01jAHySkz6iJTRgoHoF9wOmXwPVVKA34eyDsYTm4-vybxbGfBdmDXnicj60DbeRpsuhumsgDFeeNQ8_ffLMXE2Bbw8glL65cJDaBf_Tyk3KxVoAw4eJFk8M3wY9I996A5uY/s1600/PasswordProtectionLogo+1000x300.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="300" data-original-width="1100" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-W9lzdD5MQ01jAHySkz6iJTRgoHoF9wOmXwPVVKA34eyDsYTm4-vybxbGfBdmDXnicj60DbeRpsuhumsgDFeeNQ8_ffLMXE2Bbw8glL65cJDaBf_Tyk3KxVoAw4eJFk8M3wY9I996A5uY/s640/PasswordProtectionLogo+1000x300.png" width="640" /></a></div><p><span style="color: #990000; font-size: large;">NOTE: Please visit <a href="https://docs.lithnet.io/password-protection/installation/setup-guide">docs.lithnet.io </a>for the most up-to-date setup instructions</span><br /></p><p> In this guide, we will look at how to set up Lithnet Password Protection for Active Directory (LPP) from scratch. We'll configure a basic password policy, and integrate the haveibeenpwned.com pwned password data set to prevent users from changing their password to one that is known to be compromised.<br />
<br />
This guide will focus on testing in an Active Directory domain, by installing the agent on a domain controller. However, for testing purposes, you can install the agent on a member server or workstation. The only difference is you'll need to test using local accounts, rather than domain accounts.<br />
</p><h2>
Prerequisites</h2>
<ol>
<li>You'll need to be using an x64 version of windows. LPP is a x64-only application.</li>
<li>You must have local administrator rights on the server you want to install the agent on. For domain controllers, this means being a member of the Domain Admins group.</li>
<li>Download the latest version of the NTLM passwords from the <a href="https://haveibeenpwned.com/Passwords" target="_blank">haveibeenpwned.com pwned password list</a> (scroll to the end). Make sure you get the <b>"NTLM Ordered by hash"</b> version. Use the torrent link if you are able to so, as this helps minimize bandwidth and costs. Uncompress the file, and place it on your server to import later in the process.</li>
<li>Download the <a href="https://github.com/lithnet/ad-password-protection/releases" target="_blank">latest version of LPP</a> from the github site.</li>
<li>Create a user for testing. Once again, if you are installing this on a domain controller, it will need to be a domain user, otherwise it must be a local user.</li>
</ol>
<h2>
Selecting your store strategy</h2>
The LPP 'store' is essential a flat-file database of password hashes, efficiently stored in a binary format to maximize speed, while conserving space. Each domain controller must have read access to the store in order to compare incoming password changes against the compromised password lists. There are several different ways you can achieve this. Each has its own pros and cons, and you'll need to decide what's best for your environment.<br />
<h4>
Option 1: Each server accesses a single copy of the store via a file share</h4>
In this configuration, each server with LPP installed points to a network share that contains the single copy of the store. The benefits are that there is only one copy of the store to be managed, but the downside is that if the file server goes down, the password filter will be unable to perform compromised password checking. This might also be an issue for distributed environments, where bandwidth and latency between domain controllers and the file server is an issue. Remember, the agent must be installed on every writable domain controller in your environment, so they will all need read access to this store. If this is your preferred strategy, create your share, and grant the server's computer accounts read access to the store before you proceed. Users who be managing the store will need read and write access to this folder.<br />
<h4>
Option 2: Each server has its own copy of the store</h4>
In this configuration, each server with LPP installed has a local copy of the store. This means the store will always be available, and there will be no bandwidth or latency issues. The downside is that if you want to update the store with new passwords, you need to update it on each server manually. Updates to the store will be infrequent, so this may not be an issue. As the store is flat-file based, it is easy to copy around with a tool like robocopy and xcopy.<br />
<h4>
Option 3: Each server has its own copy of the store, but it is replicated with DFS-R</h4>
This is the configuration we recommend. Each server with LPP installed has its own copy of the store, but DFS-R is used to replicate and keep changes in sync. This means that new passwords can be added to a single server, and DFS-R will replicate those changes to the other servers. Do NOT place the store in the SYSVOL folder. Create a dedicated replication group for the LPP store.<br />
<h2>
Part 1: Install the software</h2>
<div>
1. Once you've completed the prerequisites, and decided on your store strategy, you can begin the installation process. On your chosen server, install the LPP agent, ensuring that you select all features for installation.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUE0XVIOlq5qpzobkBGkPxCEYT4xEmsjxcyhlUHPGOrOmD7eLme1p5rGKzcY7HoKz_QE7qSDp56R6Ku-8QegbLXA4NwTD3IwL3KTJoaKwbJl8SUQuMpWs9NPJY894u5UcfyeTGToxAfZdg/s1600/1-install.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="484" data-original-width="618" height="313" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUE0XVIOlq5qpzobkBGkPxCEYT4xEmsjxcyhlUHPGOrOmD7eLme1p5rGKzcY7HoKz_QE7qSDp56R6Ku-8QegbLXA4NwTD3IwL3KTJoaKwbJl8SUQuMpWs9NPJY894u5UcfyeTGToxAfZdg/s400/1-install.png" width="400" /></a></div>
<br />
2. Select a path to hold the store of compromised passwords and banned words. If you are using the 'file server' store strategy outlined above, this will be the UNC path to your share. Otherwise, pick a local path and ensure that the drive that hosts the store has enough space on it. You will need at least 6GB of space to hold the HIBP pwned password list.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGnWCz8123SwXcMGJB2Zg-myK-5kjWrDj8MhUssLPGfpZFUHZVgq7hV-LoU2zeSPHSG_f-wUwPBzK3wJFoN2sds8izlZlaNtFdLV4wXG3VNWjtNyuQ1ZKerK0hA7khKzoiD2pYWUe12hkS/s1600/2-store.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="484" data-original-width="618" height="312" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGnWCz8123SwXcMGJB2Zg-myK-5kjWrDj8MhUssLPGfpZFUHZVgq7hV-LoU2zeSPHSG_f-wUwPBzK3wJFoN2sds8izlZlaNtFdLV4wXG3VNWjtNyuQ1ZKerK0hA7khKzoiD2pYWUe12hkS/s400/2-store.png" width="400" /></a></div>
<br />
3. You'll need to reboot the server for the password filter to come online. Note that the password filter will remain inactive even after a reboot until we configure the group policy to enable it.<br />
<br />
4. If you have more than one domain controller, repeat the process on the other domain controllers.<br />
<h2>
Part 2: Build the store</h2>
Once you've downloaded the <b>NTLM</b> <b>ordered by hash</b> version of the pwned password list, you can start to populate the store. If you're not sure if you downloaded the <b>ordered by hash </b>version, this is the time to double check. While the 'NTLM ordered by prevalence' version will work, it is <b>extremely </b>slow to process. The SHA1 hash lists will not work at all.<br />
<br />
If you can, add the store path as an exclusion to your anti-virus software, at least for the process of building the store. It will make the process significantly faster.<br />
<br />
Open a PowerShell window and run the following commands, replacing the store path, and path to the pwned password text file as appropriate.<br />
<br />
<script src="https://gist.github.com/ryannewington/d114762c13a993b312fcdccecc6d5fef.js"></script>
This will read the text file and convert the hashes into a much smaller binary format. So while the plain-text hash list is about 20GB in size, the final store size should be about 6GB.<br />
<br />
This process usually takes about 40 minutes, so grab yourself a coffee, and perhaps have read of the <a href="https://github.com/lithnet/ad-password-protection/wiki/PowerShell-reference" target="_blank">PowerShell command reference</a> to familiarize yourself with the other cmdlets available.<br />
<br />
Once the import is complete, you can delete the HIBP text file if you need to save space.<br />
<br />
There is another cmdlet, <a href="https://github.com/lithnet/ad-password-protection/wiki/Import%E2%80%90CompromisedPasswords" target="_blank">Import-CompromisedPasswords</a>, that you can use to import any plain-text password lists you may want to add to your compromised password store.<br />
<br />
If you are using the standalone file store strategy, you should now copy the store folder to your other domain controllers. If you are replicating with DFS-R, create your replication group and wait for the replicas to sync up before proceeding.<br />
<h2>
Part 3: Configure the Group Policy</h2>
We can now configure the group policy to enable the agent and check passwords against the store. Open the Group Policy Management MMC, create a new GPO, and link it to an OU that your server is in. There is no harm at linking this at a higher level in the OU hierarchy if you prefer, as the policy only takes effect on machines that have LPP installed.<br />
<br />
<i>Note: If you have a specific machine where you perform group policy maintenance from, you'll need to install LPP on there, but select only the "Group Policy Templates (ADMX)" option in the installer. This will only copy the ADMX files to that server to allow you to edit the LPP policy settings. The <a href="https://github.com/lithnet/ad-password-protection/wiki/Configure-group-policy" target="_blank">group policy guide</a> on the wiki has information about setting up a central policy store as an alternative to this.</i><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDmnsDUPOII-kgo-5f6SklDkWdoVcgmuNZ5g35TdPbhYZDbBuE5sEOPXoUWwcqYrY8UOslTs6pd2Ea8cCvCdZ-7FhhmwIO2-SN4xa1poHMazd2spbWhkZY0SSyV9Y5rfew1mZdMMGY34Kc/s1600/3-creategpo.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="668" data-original-width="947" height="450" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDmnsDUPOII-kgo-5f6SklDkWdoVcgmuNZ5g35TdPbhYZDbBuE5sEOPXoUWwcqYrY8UOslTs6pd2Ea8cCvCdZ-7FhhmwIO2-SN4xa1poHMazd2spbWhkZY0SSyV9Y5rfew1mZdMMGY34Kc/s640/3-creategpo.png" width="640" /></a></div>
<br />
Give the policy an appropriate name when prompted, then right-click the policy, and select <i>Edit</i>. If you are not ready to make the policy live at this stage, right-click the policy, and untick <i>Link Enabled</i>.<br />
<br />
Navigate to <span style="font-family: "courier new" , "courier" , monospace;">Computer Configuration\Administrative Templates\Lithnet\Password Protection for Active Directory\Default Policy</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIR_WB-maPlnH_VXA8XpAEVOqZPseyRmd7d4NCmLl3FIVss7Av5Jr4ulZS-FvM7AUJLHCYTQj11Im37BBu3Yo-68xkXsnQJZBT-WLUTdp5UIHx5qxQZSNTN9VlghnigIT0OFKqDbhRus1F/s1600/4-editgpo.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="659" data-original-width="1118" height="377" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIR_WB-maPlnH_VXA8XpAEVOqZPseyRmd7d4NCmLl3FIVss7Av5Jr4ulZS-FvM7AUJLHCYTQj11Im37BBu3Yo-68xkXsnQJZBT-WLUTdp5UIHx5qxQZSNTN9VlghnigIT0OFKqDbhRus1F/s640/4-editgpo.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
Here you can set the policies you want to enforce. Double click <i>Reject passwords found in the compromised password store</i>, enable the policy, and check both boxes to enable password set and password change operations.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8gLFEkn2MF70nLwkupSgMUor5gN4hjjkvOXaES7dXOUR3wAfWluIkWecTPpH5UaS_GE596fNlgS_iJEWdU1DEXrak8aNmK9zBOY17_B6e98ILpZTxjI5NPwnZHxxc8OPdSvnueWI97TVg/s1600/5-enablepwned.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="795" data-original-width="857" height="592" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8gLFEkn2MF70nLwkupSgMUor5gN4hjjkvOXaES7dXOUR3wAfWluIkWecTPpH5UaS_GE596fNlgS_iJEWdU1DEXrak8aNmK9zBOY17_B6e98ILpZTxjI5NPwnZHxxc8OPdSvnueWI97TVg/s640/5-enablepwned.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
Run gpupdate on the server with LPP installed, to ensure it gets these settings.<br />
<h2>
Part 4: Test the policy</h2>
The policy is now active, so we can take our test user, and attempt to reset their password to one that we know is in the compromised password store. Let's use <span style="font-family: "courier new" , "courier" , monospace;">P@ssw0rd</span>, because I know that it is in there.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWCEr3QR8zamwoAtFbQ8bStodRbuy-e8OI90Gzp87wjQOW9FnjrEXh7uFI5ze5OBZT7t7r1CEKREBPv1s68_BeXnGnyV06y8ITTjwpUS5vMAvEfvA2yHRllNrWoP7sVF0ZYiZzsLmQddTW/s1600/6-resetpassword.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="770" data-original-width="1198" height="409" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWCEr3QR8zamwoAtFbQ8bStodRbuy-e8OI90Gzp87wjQOW9FnjrEXh7uFI5ze5OBZT7t7r1CEKREBPv1s68_BeXnGnyV06y8ITTjwpUS5vMAvEfvA2yHRllNrWoP7sVF0ZYiZzsLmQddTW/s640/6-resetpassword.png" width="640" /></a></div>
<br />
And we should see that the password change was rejected.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXvD8-NabHCNXvW_pGwaC7aN88LlAaR7f6WY2pli_KB7bNSAbHNjzTvx7kBE_gRyHiBBAyl-4dgwy8vJoW9ijHxle0QyV6aYsnB2nv-aL86EnFpG6EELNkZLMeFhxvL1PZzh-XVkN8pEtd/s1600/7-resetpassword-result.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="265" data-original-width="498" height="211" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXvD8-NabHCNXvW_pGwaC7aN88LlAaR7f6WY2pli_KB7bNSAbHNjzTvx7kBE_gRyHiBBAyl-4dgwy8vJoW9ijHxle0QyV6aYsnB2nv-aL86EnFpG6EELNkZLMeFhxvL1PZzh-XVkN8pEtd/s400/7-resetpassword-result.png" width="400" /></a></div>
<br />
Unfortunately, the Windows error messages do not tell the user anything other than the standard 'you can't use that password'. You'll need to find a way to communicate to your users that you are checking against known compromised passwords, and if their password change is rejected, it could be because they are using a password that has been compromised.<br />
<br />
We can use Event Viewer to validate that it was LPP that rejected the request. For every attempt, there will be two event entries. The first will be an event to say that a password change or set request was received by the filter.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdVu58QghDorAU3-Obw-O0MIjfLPDw7aFihaSQtHL3LY8Q_yuA3cEiDdqHA1jSjpHpMkdLP5a7J3-i7qqvucpNBcYKn9VgPaQFS9PPWw_54C6DRZUHL9nMCRsFOPp_2ibGNv9rN9JKQkY5/s1600/8-eventlog-request.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="669" data-original-width="897" height="477" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdVu58QghDorAU3-Obw-O0MIjfLPDw7aFihaSQtHL3LY8Q_yuA3cEiDdqHA1jSjpHpMkdLP5a7J3-i7qqvucpNBcYKn9VgPaQFS9PPWw_54C6DRZUHL9nMCRsFOPp_2ibGNv9rN9JKQkY5/s640/8-eventlog-request.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
If you don't see this event entry, there are a couple of possible explanations;<br />
<ul>
<li>You haven't rebooted the server since installing LPP</li>
<li>The password change was processed by another domain controller. In an Active Directory environment, any writable domain controller can process a password change, and only the one that initially receives the request validate that it meets the requirements. This is why LPP must be installed on all writable domain controllers. </li>
</ul>
<div>
After that entry, there will be a warning event giving the reason the password was rejected. If you want to build automated reporting based off these events, there is a <a href="https://github.com/lithnet/ad-password-protection/wiki/Event-logging-and-reporting" target="_blank">full list of event IDs</a> and descriptions available.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKvhMOclDUSzgugb6m1BH49-Yg-AWyc6SxD-cE-tjpHR9X7v4ciC9teSqAUFWCwGwUV_GoQD3IhWGuAiHQY_BMpQhGs_9eTqU1IaWt-GnetG9HFPibq_P9ujxteGPY1QR2cmHDLyRIVtrI/s1600/9-eventlog-result.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="669" data-original-width="897" height="476" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKvhMOclDUSzgugb6m1BH49-Yg-AWyc6SxD-cE-tjpHR9X7v4ciC9teSqAUFWCwGwUV_GoQD3IhWGuAiHQY_BMpQhGs_9eTqU1IaWt-GnetG9HFPibq_P9ujxteGPY1QR2cmHDLyRIVtrI/s640/9-eventlog-result.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
You can also test password quality without changing them in Active Directory by using the PowerShell module's <a href="https://github.com/lithnet/ad-password-protection/wiki/Get%E2%80%90PasswordFilterResult" target="_blank">Get-PasswordFilterResult</a> cmdlet.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIg8AfE9oHBz3VwANqqY66LRHh1_7BTy3Igk3TAeYNo7q8XCbdsTDCxR5F7dKgK6PcaAbFYWezQ1BIGV7xQATk7oPdTtIPkp4KNE9pk9gbdboYTUvB0ymkWu83Pt_S7e4cdT88hGVw7I2H/s1600/10-ps-cmdlet-test.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="247" data-original-width="952" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIg8AfE9oHBz3VwANqqY66LRHh1_7BTy3Igk3TAeYNo7q8XCbdsTDCxR5F7dKgK6PcaAbFYWezQ1BIGV7xQATk7oPdTtIPkp4KNE9pk9gbdboYTUvB0ymkWu83Pt_S7e4cdT88hGVw7I2H/s640/10-ps-cmdlet-test.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
The cmdlet will return a code that indicates if the password change was approved. Note that this cmdlet only tests the password against the LPP filter. A real password change may still be rejected by other password filters, or Active Directory's out-of-box password policies.<br />
<span style="font-weight: normal;"><br /></span>
<span style="font-weight: normal;">If you just want to test if a compromised password exists in the store, use the <a href="https://github.com/lithnet/ad-password-protection/wiki/Test%E2%80%90IsCompromisedPassword">Test-IsCompromisedPassword</a> cmdlet. Get-PasswordFilterResult will process the password change according to all group policy settings, whereas Test-IsCompromisedPassword justs checks to see if that password is in the store.</span><br />
<h2>
Part 5: Audit existing users</h2>
<span style="font-weight: normal;">Now that we have stopped users from changing their password to one that has been compromised, what do we do about all the existings users who may already be using compromised passwords? Using the <a href="https://github.com/lithnet/ad-password-protection/wiki/Test%E2%80%90IsADUserPasswordCompromised">Test-IsADUserPasswordCompromised</a> PowerShell cmdlet, you can compare users <i>current</i> password hashes against the compromised password store. </span>Read the <a href="https://github.com/lithnet/ad-password-protection/wiki/Audit-existing-passwords">full guide on the wiki</a> for the prequisites and example scripts you can use to audit these users.<br />
<h2>
Next Steps</h2>
<span style="font-weight: normal;"><a href="https://blog.lithnet.io/2019/01/lppad-2.html">Part 2 of this guide</a> will show you how you can block common and context-based words from being used in passwords.</span><br />
<br />
<br />Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-47354907189373187152019-01-28T11:54:00.003+11:002019-01-28T12:33:32.307+11:00Getting started with Lithnet Password Protection - Part 3 - Rewarding password length over complexity<div class="separator" style="clear: both; text-align: center;">
<img border="0" data-original-height="96" data-original-width="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQq4QpyP9VZP8AZcLs6pred4e8d4OL2caWbzrlN_0wwKiIQFyQvWPc4pScl9nIVPOPVkdvoWNRrDOiHiJVkv84_7uXsKDBIarLR6siZdoZe-lIknMLGdGpk_RWHgZc9BB-cWN7GQLJKY1w/s1600/shield+icon+96x96.png" style='display:none;'/></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-W9lzdD5MQ01jAHySkz6iJTRgoHoF9wOmXwPVVKA34eyDsYTm4-vybxbGfBdmDXnicj60DbeRpsuhumsgDFeeNQ8_ffLMXE2Bbw8glL65cJDaBf_Tyk3KxVoAw4eJFk8M3wY9I996A5uY/s1600/PasswordProtectionLogo+1000x300.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="300" data-original-width="1100" height="174" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-W9lzdD5MQ01jAHySkz6iJTRgoHoF9wOmXwPVVKA34eyDsYTm4-vybxbGfBdmDXnicj60DbeRpsuhumsgDFeeNQ8_ffLMXE2Bbw8glL65cJDaBf_Tyk3KxVoAw4eJFk8M3wY9I996A5uY/s640/PasswordProtectionLogo+1000x300.png" width="640" /></a></div>
<br />
Password complexity requirements can be frustrating. It comes as no surprise that the current NIST guidelines recommend doing away with them altogether. There was no way I was getting approval to do that across the board in my organization, but I could make the case for having less stringent requirements on <i>longer</i> passwords.<br />
<br />
Lithnet Password Protection for Active Directory (LPP) has a built-in policy to do exactly this. You can define up to 3 password-length thresholds, each with their own complexity requirements. In my organization, we still have the usual '3 out of 4' character set policy in place for passwords less than 13 characters in length. However, we decided that passwords over 13 characters would have no special requirements.<br />
<br />
You'll need to determine what rules are appropriate for your organization. Use resources such as <a href="https://howsecureismypassword.net/">https://howsecureismypassword.net/</a> to help you gauge the relative strength of passwords based on length and character sets, and come to a decision that's right for you.<br />
<br />
Once you've done that, setting up LPP to enforce this is straightforward. At this point I'll assume you've followed <a href="https://blog.lithnet.io/2019/01/lppad-1.html">part 1 of the getting started guide</a>, and have LPP installed and a group policy configured.<br />
<br />
Firstly, set the <i>minimum password length</i> policy to the appropriate value. In my example, I'm setting 8 as the minimum length.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_NKYG6XzZnvRlcZr58kveiLNczvO-vGepw82x4R0cZ-BYQ8PMxv_2yERhNMUgo7SyqhMjtbjpx3mlmgBdPYT9CoNaDpYCc_CyNM5mVZ5gkxZ07T0J-CyquWc56uBAU0HuFX-ZpOT3MCuD/s1600/15-minimum-password-length.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="791" data-original-width="897" height="564" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_NKYG6XzZnvRlcZr58kveiLNczvO-vGepw82x4R0cZ-BYQ8PMxv_2yERhNMUgo7SyqhMjtbjpx3mlmgBdPYT9CoNaDpYCc_CyNM5mVZ5gkxZ07T0J-CyquWc56uBAU0HuFX-ZpOT3MCuD/s640/15-minimum-password-length.png" width="640" /></a></div>
<br />
If you have this set in AD, you technically don't need to do so here, but having it set in LPP will mean you will get more meaningful rejection messages in your Active Directory event log and when using the LPP PowerShell cmdlets.<br />
<br />
Now, turn on the <i>Enable length-based complexity rules </i>policy. Here you will find three thresholds to configure. You don't need to configure them all, leave the thresholds that you don't need blank. Each threshold level requires you to specify a length of password that the settings apply to. Then you choose between either having a certain number of character sets present, or specific character sets present.<br />
<br />
In the example shown below, I've specified that for passwords less than 13 characters, They must contain a lower case letter, upper case letter, and either a number or a symbol.<br />
<br />
For passwords 13 characters or greater, but less than 128 characters (the maximum AD password length), a single character set is all that is required.<br />
<br />
I've excluded the third threshold level and left it blank, because I do not need it.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7kKzkdsD6vQfT3y6wgu0eCm7vI6-yFu3iFCQlgowVr7m7yDUGYfHrKa4xG3ffagYLd0__ziphkShKBw-tgMkm46LiG5Ud2kR7_cbxRngs5lkBJMhdbDsguojSE5KQRYCFAMau90iu8_pq/s1600/14-length-based-complexity.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="868" data-original-width="542" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7kKzkdsD6vQfT3y6wgu0eCm7vI6-yFu3iFCQlgowVr7m7yDUGYfHrKa4xG3ffagYLd0__ziphkShKBw-tgMkm46LiG5Ud2kR7_cbxRngs5lkBJMhdbDsguojSE5KQRYCFAMau90iu8_pq/s1600/14-length-based-complexity.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
At this stage, you'll want to disable Active Directory's built-in complexity requirements policy. Otherwise your long passwords will still be rejected by AD for not being complex enough. Before you do, turn on the <i>Reject passwords that contain the user's username</i> policy, as well as the <i>Reject passwords that contain the user's display name</i> in the LPP policy. These two checks are part of AD's built-in complexity check, so disabling it means we need to check them ourselves.<br />
<br />
Run gpupdate on the server with LPP installed. Now to confirm it all works, we can use the Get-PasswordFilterResult cmdlet.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6X-jNLntHCX94g3kma_jpsAT3vNhtlLVfMxQ9sW2G0edsP2_MWOu7kGTQPgpwjZJbMFEX9E7ixMf4Fg4AVtGH9XNUvNwoxsl-nX-iSvSx0LM-rp-4ZqLhd6Qn-0mMLizxB-xRrrwBdZuI/s1600/17-length-based-complexity-result.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="395" data-original-width="1066" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6X-jNLntHCX94g3kma_jpsAT3vNhtlLVfMxQ9sW2G0edsP2_MWOu7kGTQPgpwjZJbMFEX9E7ixMf4Fg4AVtGH9XNUvNwoxsl-nX-iSvSx0LM-rp-4ZqLhd6Qn-0mMLizxB-xRrrwBdZuI/s640/17-length-based-complexity-result.png" width="640" /></a></div>
<br />
Our "short" password is rejected with a <i>LengthRequirementsNotMet </i>error. Our "notcomplex" password which was less than 13 characters long was rejected with a <i>ComplexityThresholdNotMet </i>error. Our "longbutnotcomplex" password greater than 13 characters was approved. The attempts to use my username and part of my full name were rejected as expected.<br />
<br />
It's worth quickly covering the remaining LPP policy settings available to you. The first is a <a href="https://github.com/lithnet/ad-password-protection/wiki/Configuring-a-points-based-complexity-policy">points-based complexity policy</a>. This allows you to assign a minimum number of points a password must reach, and how those points are gained. The other settings allow you to specify regular expression that a password must meet, or not meet, to be approved.<br />
<br />
That concludes this introductory series on getting started with Lithnet Password Protection for Active Directory.<br />
<br />
Follow me on Twitter to keep up to date with the latest news and updates <a href="https://twitter.com/RyanLNewington">@RyanLNewington</a>.<br />
<br />
If you find any issues, or need some help, head on over to <a href="https://github.com/lithnet/ad-password-protection/issues">GitHub and log an issue</a>.<br />
<br />Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-3156511144462402782019-01-28T11:54:00.002+11:002019-01-28T12:34:19.824+11:00Getting started with Lithnet Password Protection - Part 2 - Blocking common and context-specific passwords<div class="separator" style="clear: both; text-align: center;">
<img border="0" data-original-height="96" data-original-width="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQq4QpyP9VZP8AZcLs6pred4e8d4OL2caWbzrlN_0wwKiIQFyQvWPc4pScl9nIVPOPVkdvoWNRrDOiHiJVkv84_7uXsKDBIarLR6siZdoZe-lIknMLGdGpk_RWHgZc9BB-cWN7GQLJKY1w/s1600/shield+icon+96x96.png" style='display:none;'/></div><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-W9lzdD5MQ01jAHySkz6iJTRgoHoF9wOmXwPVVKA34eyDsYTm4-vybxbGfBdmDXnicj60DbeRpsuhumsgDFeeNQ8_ffLMXE2Bbw8glL65cJDaBf_Tyk3KxVoAw4eJFk8M3wY9I996A5uY/s1600/PasswordProtectionLogo+1000x300.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="300" data-original-width="1100" height="174" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-W9lzdD5MQ01jAHySkz6iJTRgoHoF9wOmXwPVVKA34eyDsYTm4-vybxbGfBdmDXnicj60DbeRpsuhumsgDFeeNQ8_ffLMXE2Bbw8glL65cJDaBf_Tyk3KxVoAw4eJFk8M3wY9I996A5uY/s640/PasswordProtectionLogo+1000x300.png" width="640" /></a></div>
<br />
In <a href="https://blog.lithnet.io/2019/01/lppad-1.html">part 1</a>, we saw how Lithnet Password Protection for Active Directory (LPP) allows us to test incoming password changes for exact matches against known compromised passwords.<br />
<br />
Well, what if we want to prevent paswords based on certain words? Most users understandably want a password that is easy to remember, but in many cases, they end up choosing a well known 'password pattern' that can easily fall to brute force attacks.<br />
<br />
A common password pattern I have seen is where a user combines a season with the current year, for example Winter2018 or Summer2017. Now, while this and previous year's versions of these passwords are in HIBP, future ones may not be. We can prevent the use of all passwords based on these words, by using the banned word functionality of LPP.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
Banned words checks work a bit differently to compromised password checks. The compromised password check looks for an exact match in the store against the incoming password. The banned word check first <a href="https://github.com/lithnet/ad-password-protection/wiki/Normalization-rules" target="_blank">normalizes</a> the password, through a process that removes common patterns and character substitutions before performing the comparison. This allows us to determine if a password is based on a banned word, and prevent its use.<br />
<br />
In order to use this functionality, we first need to enable banned word checking. In the group policy we created in part 1, enable the policy <i>Reject normalized passwords found in the banned password store,</i> and run gpupdate on the server with LPP installed to update the policy.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKJkR55vyTHQbZN_7X8t59fFOjlwCR-eefbsdo0WDiXkNk2ezsIDfSUMb909TU-lVQubXy-ECHstYJgGYiOx5Xod8OZ96zbkA-wNT27zUV6B2QGj0a2NjB8NHgmiliBVz-6x-QwamiDAdt/s1600/13-enable-banned-words.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="795" data-original-width="857" height="592" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKJkR55vyTHQbZN_7X8t59fFOjlwCR-eefbsdo0WDiXkNk2ezsIDfSUMb909TU-lVQubXy-ECHstYJgGYiOx5Xod8OZ96zbkA-wNT27zUV6B2QGj0a2NjB8NHgmiliBVz-6x-QwamiDAdt/s640/13-enable-banned-words.png" width="640" /></a></div>
<br />
We can now add banned words to our store using the <a href="https://github.com/lithnet/ad-password-protection/wiki/Add%E2%80%90BannedWord">Add-BannedWord</a> cmdlet. Simply add the base word (such as 'winter'), and then all passwords based on that word will be rejected with the code "BannedNormalizedWord"<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAugDkg6r_wgyrgf3fSjbvbSzIPWQohf0TFuoDyrhZvusOQP6QRTA9rAVxRdIaGJnBkHzM3otG_B_PhW_ACOOoY6SZ7FLnScQS5XSib5wSDtb2LHmRsJVQdjFVajuUGSjH5aW5Xf0DjVpW/s1600/11-add-banned-word.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="247" data-original-width="1014" height="154" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAugDkg6r_wgyrgf3fSjbvbSzIPWQohf0TFuoDyrhZvusOQP6QRTA9rAVxRdIaGJnBkHzM3otG_B_PhW_ACOOoY6SZ7FLnScQS5XSib5wSDtb2LHmRsJVQdjFVajuUGSjH5aW5Xf0DjVpW/s640/11-add-banned-word.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
NIST's updated <a href="https://pages.nist.gov/800-63-3/sp800-63b.html" target="_blank">2018 password guidelines</a> recommends that we block users from selecting context-specific passwords, that is, those based on the service, web site, or organization the password is for. After auditing my own users, I was surprised to see how many users we had base a password on the organization's name. We can prevent that by adding our company name as a banned word.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyAxzmpaouMcv5EBLFTo5O-GgLV1TMIn6skXNW3Qm9t3c5kizRZt6MTCgFg3GS-cXINlrY0C9J5Gfw84urqPmpV3BeMDdifU-S3uLt7XyPKfyy2Y7CPCfOYSxGj5B8v5CwgxmIGzL9PFjx/s1600/12-add-banned-company.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="247" data-original-width="1014" height="154" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyAxzmpaouMcv5EBLFTo5O-GgLV1TMIn6skXNW3Qm9t3c5kizRZt6MTCgFg3GS-cXINlrY0C9J5Gfw84urqPmpV3BeMDdifU-S3uLt7XyPKfyy2Y7CPCfOYSxGj5B8v5CwgxmIGzL9PFjx/s640/12-add-banned-company.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
The normalization process isn't designed to prevent every single possible variation of a word that a user may select. It's designed based on real-world patterns people most commonly use to contruct these types of passwords. Our users may think they are being clever by swapping out o's for zeros and i's for ones, but hackers know these patterns all too well, and they offer little to no additional resistance against brute force attacks on passwords.<br />
<br />
If you want to prevent users from choosing passwords based on dictionary words, then import the entire dictionary into the banned word store! The <a href="https://github.com/lithnet/ad-password-protection/wiki/Import%E2%80%90BannedWords">Import-BannedWords</a> cmdlet will import a file of plain-text words (one per line) into the banned word store. This will mean your users will need to be thinking about using pass phrases. Single dictionary words, even appended with a few symbols or numbers will no longer work.<br />
<br />
You'll once again need to think about how you communicate these requirements to your users, and make sure you password change pages have this information on them. Remember, all Active Directory will tell them is "<i>the password does not meeting the password policy requirements</i>". You'll need to find the balance between security and usability that is right for your organization.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjY1ttIxstKc31W-Am12KTmQwAyKRNVUEW7LA0i5WBjfqM0J8NyMzKMPLplLUFE759edhDuQQB1BiURuPAknX3foP2NS7twpGL0JVleE87YQs64ay8dpiFboQlaPiIO-DRMR-hPzfowREi/s1600/7-resetpassword-result.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="265" data-original-width="498" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjY1ttIxstKc31W-Am12KTmQwAyKRNVUEW7LA0i5WBjfqM0J8NyMzKMPLplLUFE759edhDuQQB1BiURuPAknX3foP2NS7twpGL0JVleE87YQs64ay8dpiFboQlaPiIO-DRMR-hPzfowREi/s320/7-resetpassword-result.png" width="320" /></a></div>
<br />
<h2>
Next Steps</h2>
<span style="font-weight: normal;"><a href="https://blog.lithnet.io/2019/01/lppad-3.html">Part 3 of this guide</a> will show you how you can configure a password complexity policy that reward users who choose longer passwords, with less stringent complexity requirements.</span><br />
<br />Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-31765433145303140042019-01-28T11:54:00.000+11:002019-01-28T12:34:30.306+11:00Announcing Lithnet Password Protection for Active Directory<div class="separator" style="clear: both; text-align: center;">
<img border="0" data-original-height="96" data-original-width="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQq4QpyP9VZP8AZcLs6pred4e8d4OL2caWbzrlN_0wwKiIQFyQvWPc4pScl9nIVPOPVkdvoWNRrDOiHiJVkv84_7uXsKDBIarLR6siZdoZe-lIknMLGdGpk_RWHgZc9BB-cWN7GQLJKY1w/s1600/shield+icon+96x96.png" style='display:none;'/></div>Hackers have never had it easier when it comes to performing credential-based attacks. Massive lists of breached user names and passwords are readily available on the internet for use in <a href="https://en.wikipedia.org/wiki/Credential_stuffing">credential stuffing</a> attacks against organizations. Even without the help of such lists, attackers can rely on the fact that people generally choose terrible, predictable passwords. Target a large enough organization, and you have a chance of finding a user with a password from <a href="https://en.wikipedia.org/wiki/List_of_the_most_common_passwords" target="_blank">a top 100 bad password</a> list.<br />
<div>
<br /></div>
<div>
Keeping up with these sorts of threats is a challenge for every organization, no matter the technology they use. For a significant number of organizations, Microsoft's Active Directory is the core authentication service, connecting users and services across the enterprise. It is disappointing then, that AD hasn't done much to adapt to the changing threat landscape, with password protection capabilities that haven't changed in decades. A true sign of its age is that it stores passwords using an unsalted MD4 hash (a meaty target if ever there was one). Its out-of-the-box password policies leave a lot to be desired: even with password complexity enforcement turned on, AD is quite happy to allow a user to use objectively bad passwords like 'P@ssw0rd'. At the time of writing this post, that particular gem has been seen over 51,000 times in data breaches known to <a href="https://haveibeenpwned.com/" target="_blank">haveibeenpwned.com</a>.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihyphenhypheny6xLiGFOoWYNBSGHypgZOOKHU59wfjbjqyqtUDNpNl_vrEQ7LjNCg9IaGKdqPDNnNTYVvFt8OM04JI0AMlZ-d-Y4a0bbRfKBi2U1rm5oMEwZr2mwCfyMbUFMRsfQAwB1c5XvOI-YMSo/s1600/pwned.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="223" data-original-width="1600" height="89" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihyphenhypheny6xLiGFOoWYNBSGHypgZOOKHU59wfjbjqyqtUDNpNl_vrEQ7LjNCg9IaGKdqPDNnNTYVvFt8OM04JI0AMlZ-d-Y4a0bbRfKBi2U1rm5oMEwZr2mwCfyMbUFMRsfQAwB1c5XvOI-YMSo/s640/pwned.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
In 2018, NIST put out <a href="https://pages.nist.gov/800-63-3/sp800-63b.html" target="_blank">new guidelines</a> for password management. These offer practical recommendations that can help combat these sorts of attacks, with one that stood out as the most important to me.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">When processing requests to establish and change memorized secrets, verifiers SHALL compare the prospective secrets against a list of known commonly-used, expected, and/or compromised values. For example, the list MAY include (but is not limited to):</span><br />
<br />
<ul>
<li><span style="font-family: "courier new" , "courier" , monospace;">Passwords obtained from previous breach corpuses</span></li>
<li><span style="font-family: "courier new" , "courier" , monospace;">Dictionary words</span></li>
<li><span style="font-family: "courier new" , "courier" , monospace;">Context specific words, such as the name of the service, the username, and derivates thereof</span></li>
</ul>
However, if you are using AD for authentication, you'll find that it falls short of being able to implement these and many other key recommendations.<br />
<br />
Now, to be fair, Microsoft are not completely absent from the party here. Azure Active Directory provides infinitely better credential protection capabilities. There's been a significant investment made in the technology in AAD, and I have to say it offers some of the best account protection features in the market. Unfortunately, not everyone has AAD, nor is it (yet) a replacement for AD. AAD also comes at an additional cost, and the best of the account and password protection functionality is only included in its premium tiers.<br />
<br />
I'm a firm believer that security essentials shouldn't be an <i>additional </i>offering that an organization must pay a premium for. We need to consider credential hygiene a security <i>minimum</i>, not an add-on. As a result, I decided to offer one way to close that gap, and make the technology to provide good credential hygiene freely available. I've spent the better part of a year working on that solution, and today, I'm releasing Lithnet Password Protection for Active Directory (LPP).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmwGwnGzo8irULqBeZUyDt3O5jNksITtWaqOTQJ5SHT7d2hpDNnzQ9tbP-ExT5LFl0uugxIKm1l4z2unEiAoQ31dRLSa6kHHlt9d0NnmPEHOwjQMCZ0oZMHvLVzdcl-ERXmAyPtMFhhZuc/s1600/PasswordProtectionLogo+1000x300.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="300" data-original-width="1100" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmwGwnGzo8irULqBeZUyDt3O5jNksITtWaqOTQJ5SHT7d2hpDNnzQ9tbP-ExT5LFl0uugxIKm1l4z2unEiAoQ31dRLSa6kHHlt9d0NnmPEHOwjQMCZ0oZMHvLVzdcl-ERXmAyPtMFhhZuc/s640/PasswordProtectionLogo+1000x300.png" width="640" /></a></div>
<br />
LPP is a module that you install on your Active Directory servers that uses a <a href="https://docs.microsoft.com/en-us/windows/desktop/secmgmt/password-filters" target="_blank">password filter</a> to inspect passwords as users attempt to change them. Using group policy, you customize the types of checks you want to perform on those passwords and they are either rejected, or approved, and committed to the directory.<br />
<br />
LPP gives you the ability to take control of what a good password means to you. Whether you want to adopt the NIST recommendations in part, or in full, it provides a rich set of group policy-based controls that allow you to enable any combination of the following checks on attempted password changes.<br />
<ul>
<li><b>Block compromised passwords from being used.</b> We've made it super easy to import the <a href="https://haveibeenpwned.com/Passwords" target="_blank">HIBP</a> data set, but you can also import any plain-text passwords or NTLM hashes that you can get your hands on.</li>
<li><b>Block passwords based on certain words.</b> Adding a banned word prevents it from being used as the base of a password. For example, adding the word 'password' to the banned word store, prevents not only the word itself, but common variants such as 'P@ssw0rd', 'pa55word!' and 'password123456!'. LPP is aware of common character substitutions and weak obfuscations and prevents their use through a <a href="https://github.com/lithnet/ad-password-protection/wiki/Normalization-rules" target="_blank">normalization process</a>. </li>
<li><b>Define complexity policies based on length.</b> For example, you can require number, symbol, upper and lower for passwords less than 13 characters, but have no special requirements for passwords 13 characters or longer. Reward length, with less complexity.</li>
<li><b>Regular expression-based policies</b>. If regular expressions are your thing, you can define a regular expression that the password must match (or not match).</li>
<li><b>Points-based complexity.</b> Assign points for the use of certain characters and categories and set a minimum point threshold a password must meet.</li>
</ul>
It also includes the ability to audit your users' existing passwords against the comrpomised password list. You'll be able to find the weak and known compromised passwords, and force those users to change their password.<br />
<br />
While it's not the only service out there to offer custom complexity policies, or compromised password checking for AD, it has the following additional features;<br />
<ul>
<li>Does not require internet access</li>
<li>No additional servers required for deployment</li>
<li>Passwords never leave the domain controller</li>
<li>Designed for large environments where high performance and reliability are required</li>
<li>Creates detailed event logs, easily digestable by Splunk or OMS</li>
<li>Uses a DFS-R friendly/compatible data store</li>
</ul>
I've put together a <a href="https://blog.lithnet.io/2019/01/lppad-1.html">3 part getting started guide</a> to get you up and running with the most common scenarios quickly. You can also visit to the <a href="https://github.com/lithnet/ad-password-protection">Lithnet GitHub site</a> to view the documentation wiki, download the latest release, or inspect the code.<br />
<br />
If you find any issues, or need some help, head on over to <a href="https://github.com/lithnet/ad-password-protection/issues">GitHub and log an issue</a>.<br />
<br />
You can follow me on <a href="https://twitter.com/RyanLNewington">Twitter</a> or subscribe to the <a href="https://feeds.feedburner.com/bloglithiumbluecom">RSS feed</a> to keep up to date with the latest news and updates.<br />
<h4>
Acknowledgements</h4>
None of this would be possible without Troy Hunt's haveibeenpwned.com service. <a href="https://haveibeenpwned.com/Donate" target="_blank">Buy him a beer to say thanks</a>! If you have not yet signed up to be notified when your account has been seen in a breach, do so now.<br />
<br />
The capability to audit existing user passwords comes from the incredible work Michael Grafnetter has done with his <a href="https://github.com/MichaelGrafnetter/DSInternals" target="_blank">DSInternals</a> tools.<br />
<br />
A special thanks to those who have dedicated time to help test the module and improve the documentation. <a href="https://blog.darrenjrobinson.com/" target="_blank">Darren Robinson</a>, Matt Solly, and Jaysn Rye in particular.Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-7903459666549235472019-01-20T09:33:00.000+11:002019-01-20T09:33:40.793+11:00Announcing the Lithnet Okta Management Agent for Microsoft Identity Manager<div style="text-align: justify;">
Okta is a leading provider of single sign-on, MFA, lifecycle management, and API access management products. And starting today, you can easily integrate Okta with Microsoft Identity Manager using the <a href="https://github.com/lithnet/okta-managementagent" target="_blank">Lithnet Okta Management Agent</a>. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Using the native Okta API platform, the management agent can add, delete, and update users, as well as synchronize password changes. You can manage users independently, or coexist with Okta's native AD sync agents, to provide supplementary attributes to objects that are not found in your AD.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The management agent provides support for importing groups, but not modifying them. Why? Well, at this stage, Okta doesn't support nested groups (🤦♂️!), a fundamental capability of MIM and Active Directory. There's simply no reliable way to translate these concepts using only the MIM connector model. Okta's AD sync agent flattens groups before syncing them to Okta, so if you need to use AD groups, that's still the best way to get them in there. Okta may add nested group support in the future, at which point I'll be able to update the connector to support group management.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
If user management is your primary scenario, head over to the <a href="https://github.com/lithnet/okta-managementagent" target="_blank">Github site</a> to download the module and get started.</div>
Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-40645963004113008122018-12-04T21:01:00.000+11:002019-01-19T18:37:37.209+11:00Disabling Unauthenticated Binds in Active DirectoryIn January last year, I <a href="https://blog.lithiumblue.com/2017/01/ad-lds-and-ldap-unauthenticated-binds.html">wrote a (long) post</a> detailing a curious behavior I stumbled across in Active Directory's LDAP interface. By providing a username, but leaving the password blank, you were authenticated as an 'anonymous user'. This is technically a valid LDAP behavior, and is known as an 'unauthenticated bind'. However it's obscure, not well known, and the cause of many <a href="https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=unauthenticated+bind">security vulnerabilities</a>.<br />
<br />
To recap, the LDAP RFC states the following;<br />
<div style="line-height: 1.8em; margin-bottom: 1em; overflow-wrap: break-word;">
<blockquote class="tr_bq" style="color: black; font-size: medium;">
<i>5.1.2. </i><u><i>Unauthenticated Authentication</i></u><i> Mechanism of Simple Bind</i><br />
<i>An LDAP client may use the unauthenticated authentication mechanism of the simple Bind method to establish an anonymous authorization state by sending a Bind request with a name value (a distinguished name in LDAP string form [</i><a href="https://tools.ietf.org/html/rfc4514"><i>RFC4514</i></a><i>] of non-zero length) and specifying the simple authentication choice containing a password value of zero length</i>
</blockquote>
<div>
<span style="font-family: inherit;">The RFC goes on to acknowledge this is an objectively terrible idea, and recommends that servers reject unauthenticated bind requests by default.</span>
</div>
<div>
<blockquote class="tr_bq">
<i style="color: black; font-size: medium;">Unauthenticated Bind operations can have significant security issues (see </i><a href="https://tools.ietf.org/html/rfc4513#section-6.3.1" style="font-size: medium;"><i>Section 6.3.1</i></a><i style="color: black; font-size: medium;">). In particular, users intending to perform Name/Password Authentication may inadvertently provide an empty password and thus cause poorly implemented clients to request Unauthenticated access.</i>
</blockquote>
</div>
<div>
<blockquote class="tr_bq">
<i style="color: black; font-size: medium;"><b>Additionally, Servers SHOULD by default fail Unauthenticated Bind requests with a </b><b>resultCode</b><b> of </b><b>unwillingToPerform</b><b>.</b></i>
</blockquote>
</div>
<div>
<span style="font-family: inherit;">Sadly, Microsoft's Active Directory and Lightweight Directory Services products did not support disabling this behavior at all, let alone having it off by default as suggested by the RFC.</span><br />
<span style="font-family: inherit;"><span style="color: black;"><br /></span></span>
<span style="font-family: inherit;"><span style="color: black;">The good news is that in </span><b style="color: black;">Windows Server 2019</b><span style="color: black;">, Microsoft have added in the ability to disable unauthenticated binds. So Windows now joins all other major LDAP vendors in providing the capability to turn off this 'feature'</span></span>
</div>
<div>
<i style="color: black; font-size: medium;"><b><br /></b></i>
</div>
<table style="border-collapse: collapse; border: 1px solid rgb(221, 221, 221); color: #575757; font-size: 15px; padding: 5px; width: 642px;">
<tbody>
<tr>
<th><div style="text-align: left;">
Product
</div>
</th>
<th><div style="text-align: left;">
Can be disabled
</div>
</th>
<th><div style="text-align: left;">
Disabled by default
</div>
</th>
</tr>
<tr><td>Red Hat Directory Server</td><td>Yes</td><td><a href="https://access.redhat.com/documentation/en-US/Red_Hat_Directory_Server/8.1/html/Administration_Guide/unauthenticated-binds.html">Yes</a></td></tr>
<tr><td>OpenLDAP</td><td>Yes</td><td><a href="http://www.openldap.org/doc/admin24/security.html">Yes</a></td></tr>
<tr><td>Novell eDirectory</td><td>Yes</td><td><a href="https://www.novell.com/support/kb/doc.php?id=3449660">No</a></td></tr>
<tr><td>Oracle/Sun Directory Server</td><td>Yes</td><td><a href="https://docs.oracle.com/cd/E19424-01/820-4809/auto30/index.html">Yes</a></td></tr>
<tr>
<td>Microsoft AD LDS/ADAM</td>
<td><b><span style="color: red;">Yes* (Server 2019+)</span></b><br />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="color: red;"></span></b>
</div>
</td>
<td>No</td>
</tr>
<tr><td>Microsoft Active Directory</td><td><b><span style="color: red;">Yes* </span></b><b><span style="color: red;">(Server 2019+)</span></b></td><td>No</td></tr>
</tbody>
</table>
</div>
<div>
<span style="font-family: inherit;">They stopped short of making it off by default, which would have been preferable, but at least it is an option now.</span>
<br />
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">So, how do you enable it?</span>
<br />
<span style="font-family: inherit;"><br /></span></div>
<div>
Using ADSIEdit, open the Configuration partition, and open the properties of the <span style="font-family: "courier new" , "courier" , monospace;">CN=Directory Service, CN=Windows NT, CN=Services, CN=Configuration</span> object. Modify the <span style="font-family: "courier new" , "courier" , monospace;">msDS-Other-Settings</span> attribute, and add a new entry for <span style="font-family: "courier new" , "courier" , monospace;">DenyUnauthenticatedBind=1</span>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0wQqHBJH6FYVn8MGrd8OmJaX1RcEv-z2otpEZ7jN4cGgvbKpP7sCXmDYko6f3pqmcoz6zpCsr4uPPbXnjN6EAxLXcs4jR3W1YNjxXpEcxjB7Wh3hHOmSC3QLH36xb0BgLWjpJZk7nw8Iz/s1600/unauth-binds-fix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1350" data-original-width="1595" height="541" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0wQqHBJH6FYVn8MGrd8OmJaX1RcEv-z2otpEZ7jN4cGgvbKpP7sCXmDYko6f3pqmcoz6zpCsr4uPPbXnjN6EAxLXcs4jR3W1YNjxXpEcxjB7Wh3hHOmSC3QLH36xb0BgLWjpJZk7nw8Iz/s640/unauth-binds-fix.png" width="640" /></a>
</div>
<div>
<br /></div>
<div>
<div>
The setting takes effect immediately, and does not require a reboot.
<br />
<br /></div>
<div>
<div>
Now, when you attempt to login without a password, it will fail with an Unwilling To Perform error message.
</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">-----------</span>
</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">res = ldap_simple_bind_s(ld, 'let me in', <unavailable>); // v.3</span>
</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">Error <53>: ldap_simple_bind_s() failed: Unwilling To Perform</span>
</div>
<span style="font-family: "courier new" , "courier" , monospace;">Server error: 00002035: LdapErr: DSID-0C0903AD, comment: <span style="background-color: yellow;">The server has been configured to deny unauthenticated simple binds</span>., data 0, v4563</span><br />
<div>
<span style="font-family: "courier new" , "courier" , monospace;">Error 0x2035 The server is unwilling to process the request.</span>
</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">-----------</span>
</div>
<div>
That's all there is to it. You follow the the same process to apply the fix to your LDS instances.
</div>
<div>
</div>
<div>
Granted, it's not a perfect outcome - you still need to upgrade all your domain controllers and/or LDS servers to Windows Server 2019 to take advantage of this capability, but if this is important to you, there is at least a solution available.
</div>
<div>
<br /></div>
<div>
Time to start planning those upgrades...
</div>
<div>
<br /></div>
</div>
</div>
Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com2tag:blogger.com,1999:blog-1261292475025329861.post-70928829343377431172018-07-26T09:06:00.000+10:002019-01-19T18:37:10.783+11:00Announcing the Lithnet LAPS Web App<a href="https://technet.microsoft.com/en-us/mt227395.aspx">Microsoft's Local Admin Password Solution </a>(LAPS) is a very important tool that protects against the risk of lateral movement of threats between computers when the same local admin password is used on each machine.<br />
<br />
It is an agent that is deployed to each computer that randomises and rotates the local administrator password on each machine, and securely stores it in the Active Directory.<br />
<br />
While the LAPS mechanism itself is robust and does exactly what it needs to do, the process of accessing the LAPS passwords, and auditing that access is not so straight forward.<br />
<br />
Support staff out in the field may not have easy access to the tools required to get those passwords. You either need to use PowerShell, LAPS client, or another directory tool such as AD Users and Computers.<br />
<br />
Auditing access to LAPS passwords is a bit of a nightmare. It requires configuring audit policies on domain controllers for directory object access, which is a very board audit category and can be very noisy. You need to find your LAPS events in this dataset, filter them, and send them off somewhere manageable.<br />
<br />
To help address some of these 'client-side' issues, I'm releasing the Lithnet LAPS web app, a mobile-friendly, web-based interface to gaining access to LAPS passwords.<br />
<br />
<h3>
Web-based access to LAPS passwords</h3>
<br />
The LAPS web app provides a simple web-based and mobile-friendly interface for accessing local admin passwords. There's no need for admins to install custom software, or have access to AD administrative tools to access LAPS passwords. Simply provide the computer name, and if you have access, the password is shown.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://github.com/lithnet/laps-web/wiki/images/RequestPassword.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="346" data-original-width="505" height="436" src="https://github.com/lithnet/laps-web/wiki/images/RequestPassword.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://github.com/lithnet/laps-web/wiki/images/ShowPassword.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="481" data-original-width="497" height="617" src="https://github.com/lithnet/laps-web/wiki/images/ShowPassword.png" width="640" /></a></div>
Application administrators have the option of forcing an expiry time once a password has been accessed. This ensures the password is rotated after use.<br />
<br />
<h3>
Audit success and failure logs</h3>
All success and failure events are logged to the event log and a file. These can be easily shipped off to a SIEM for record keeping or further analysis and reporting. Audit events can also be sent via email, and are configurable based on the LAPS target computer.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://github.com/lithnet/laps-web/wiki/images/AuditSuccess.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="620" data-original-width="800" height="496" src="https://github.com/lithnet/laps-web/wiki/images/AuditSuccess.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://github.com/lithnet/laps-web/wiki/images/AuditFail.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="619" data-original-width="800" height="492" src="https://github.com/lithnet/laps-web/wiki/images/AuditFail.png" width="640" /></a></div>
<br />
<h3>
Fine grained authorisation</h3>
<div>
The web app supports authorising access to target passwords at a computer, group, or OU level. Permission can be granted to individual users and groups in the directory. </div>
<div>
<br /></div>
<h3>
Rate limiting</h3>
The web app uses a dedicated service account to read LAPS passwords, and does not require individual users to have LAPS password access in the directory. This reduces the risk of a compromised LAPS reader account being able to enumerate all the LAPS credentials they have access to. The web app imposes configurable rate-limits on the number of times per minute, hour and day each user and/or IP address can read a LAPS password.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://github.com/lithnet/laps-web/wiki/images/RateLimited.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="202" data-original-width="510" height="249" src="https://github.com/lithnet/laps-web/wiki/images/RateLimited.png" width="640" /></a></div>
<br />
<h3>
Modern authentication options - protect your LAPS access with multi-factor authentication!</h3>
<div>
The web app supports traditional integrated windows authentication, as well as WS-Federation and OpenID Connect. This means you can leverage external authentication providers such as ADFS, Azure AD, and Okta. These providers give you the ability to enhance the security of your application by enabling multi-factor authentication. There are step-by-step guides for setting up <a href="https://github.com/lithnet/laps-web/wiki/Setting-up-authentication-with-Active-Directory-Federation-Services">ADFS</a>, <a href="https://github.com/lithnet/laps-web/wiki/Setting-up-authentication-with-Azure-Active-Directory">Azure AD</a> and <a href="https://github.com/lithnet/laps-web/wiki/Setting-up-authentication-with-Okta">Okta</a>, to get you up and running in no time.</div>
<br />
<h2>
Getting started</h2>
Visit the <a href="https://github.com/lithnet/laps-web">Lithnet LAPS Web app site </a>today to download the app and get startedRyan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-81102213844372001072018-03-14T20:14:00.000+11:002019-01-19T18:38:16.258+11:00The LDAP ‘authentication’ anti-pattern<div style="text-align: justify;">
You could walk into just about any organization today, and you’re bound to find an LDAP directory populated with its users. Look a bit further, and you’ll likely find one or more applications using that directory for ‘authentication’. I say 'authentication' with quotes, because LDAP authentication is something of a misnomer. LDAP is a directory access protocol, designed for reading, writing, and searching a directory service. It's not an authentication protocol. LDAP authentication typically refers to the part of the protocol (binding) that is meant to establish who you are in order to determine what privileges you have to the information in the directory. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Over time, it’s become a de facto authentication service. The wide-spread availability of LDAP services, such as Active Directory, has turned it into an easy win for software developers who are looking to build authentication into their products. LDAP client libraries are available for just about any framework, and it’s relatively easy to get an LDAP integration functioning. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
While using LDAP authentication might solve the development problem of how to authenticate users across an enterprise, it creates a host of other problems. There are inherent weakness and security issues that using LDAP introduces, that are not present in true authentication protocols. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
In order to understand what these issues are, we first need to understand how LDAP authentication actually works.</div>
<div style="text-align: justify;">
<br /></div>
<h2 style="text-align: justify;">
How LDAP ‘authentication’ works</h2>
<div style="text-align: justify;">
Consider the following situation (it’s rather absurd but bear with me).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Let’s say I order a parcel from an online store, only to have it delivered when I wasn’t home. The driver leaves a ‘sorry we missed you’ card in my mail box asking me to visit the local parcel pick-up point to collect the package in my own time. I arrive at the collection point, and the person behinds the counter asks for my name and address, and the keys to my house so they can validate who I am. They get in their car and drive to my house. They approach the front door, insert my key, and turn it. The door opens! They have a look inside for some evidence that I actually live there like some photos on the wall, or some mail with my name on it. They drive back to the parcel centre and let me know they have been able to successfully confirm my identity and I can have my package! Great!</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Logistical issues aside, there are a quite a few problems with this situation. What if the attendant wasn’t trustworthy, and they took a copy of my key? What if they were trustworthy, but left the key lying around long enough for someone else to copy it? What if they were mugged, and my keys were stolen off them? Once the keys leave my possession, I really can’t be sure who has them and what they will be used for.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Thankfully in the real world, we have documentation like driver’s licenses and passports to prove our identities. I can hand over a piece of documentation that is issued to me by a 3rd party we both trust, such as a government agency, and my identity can be proven without giving away my keys.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
In the LDAP world, we still have to hand over our keys to a 3rd party to unlock the door on our behalf. We give our password to the third party and they try to get into the LDAP server with it. If the password works, then you must be the owner of that password. However, now we have the same problem that we have when we hand over our keys. We really don’t know what is going to happen to those credentials and what they could be used for. If the credentials were compromised, the attacker would not just have access to unlock the LDAP door, but any application using those same credentials.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Thankfully, in the wider world of authentication, we have passports and drivers licenses too! Authentication protocols like Kerberos, SAML and OpenID Connect all issue tokens to 3rd parties that prove you are who you say you are, without having to hand them your keys. As LDAP was never designed to be an authentication protocol, it does not have appropriate mechanisms to do this.</div>
<div style="text-align: justify;">
<br /></div>
<h2 style="text-align: justify;">
LDAP’s weaknesses as an authentication system</h2>
<div style="text-align: justify;">
Shumon Huque wrote a fantastic article in 2007 - LDAP Weaknesses as a Central Authentication System which he highlights 3 specific problems with the approach of authenticating using LDAP.</div>
<div style="text-align: justify;">
<br /></div>
<h3 style="text-align: justify;">
1. The application probably isn't secure enough to be touching credentials</h3>
<div style="text-align: justify;">
Shumon makes the very valid point that it is far easier to defend a small set of authentication servers against an attack, than it is to defend a large population of application servers. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Authentication servers are generally locked down and run by people with significant expertise in security. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
On the other hand, application servers have a very different security profile, and are more likely to be compromised. They have less rigorous protection, run more complex software stacks and are more likely to have security bugs. They are also more likely to be run by people who do not have an in-depth knowledge of security. That's not a criticism of the hard-working application administrators out there – getting security right is hard, getting it wrong is too easy! </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The problem becomes that if a single application server is compromised, all the credentials that were used during the window of compromise are also compromised. Any other system that those users could log into with those same credentials, can also be considered compromised.</div>
<div style="text-align: justify;">
<br /></div>
<h3 style="text-align: justify;">
2. The LDAP server cannot enforce the security of the authentication mechanism used to obtain the credentials</h3>
<div style="text-align: justify;">
The security of the transaction cannot be guaranteed by the LDAP server. While the LDAP server might for example be able enforce binding over TLS to ensure credentials are not transmitted in plain text, it never played a role in capturing those credentials in the first place. The application might be receiving the password over an insecure channel.</div>
<div style="text-align: justify;">
<br /></div>
<h3 style="text-align: justify;">
3. The user must reveal their authentication secret to a 3rd party </h3>
<div style="text-align: justify;">
A user's password, or authentication secret, should remain that, a secret. It should be known only to the authentication system, and the user themselves. When using LDAP for authentication, the user must disclose their secret to a 3rd party, for them to replay that secret against the LDAP directory.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
It's probably important to call out here, that with true authentication protocols such as Kerberos, and even the older NTLM, the user secret never passes over the network. The client device and server use cryptographic operations to prove they both have the same secret, without ever exchanging the secret itself.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
I'm going to add a few of my own concerns to Shumon's points. These relate to my personal experience, primarily with consumers of Active Directory;</div>
<div style="text-align: justify;">
<br /></div>
<h3 style="text-align: justify;">
4. Many developers don't know how LDAP works well enough to use it correctly</h3>
<div style="text-align: justify;">
One of my previous blog posts details how anonymous and unauthenticated binds have tricked many developers into letting unauthorized users into their applications. Unauthenticated binds are one of the subtleties in the protocol that even the most seasoned LDAP professionals are unaware of. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Directories are complex, and they can store an enormous amount of organizational information and provide a highly customizable way of doing that. I've seen so many cases where the developer of an application assumed a specific object class or attribute existed, and the whole thing falls over when it doesn't. To authenticate a user, you shouldn't need to have a working knowledge of the structure of the data stored in a directory. An authentication protocol should abstract away the detail of the underlying object store.</div>
<div style="text-align: justify;">
<br /></div>
<h3 style="text-align: justify;">
5. Application administrators often don't configure LDAP clients correctly</h3>
<div style="text-align: justify;">
One of the frustrating things about managing an Active Directory in a large distributed environment, is that it's difficult to determine when services are using AD as an LDAP directory, and how the application administrators have configured their LDAP client. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Some of the configuration horrors I’ve seen include;</div>
<br />
<ul>
<li style="text-align: justify;">Hardcoding DNs in applications, or using DNs in bind configuration. I've seen countless incidents caused by a rename or move of an object within in the directory, because someone has hard coded a DN somewhere. (A side note to those performing simple binds against AD - you don't need to use a DN to do this. AD also provides alternative DN formats that are more robust than using a traditional DN).</li>
<li style="text-align: justify;">Using personal user accounts as a ‘bind’ account instead of a service account (guess what happens when that person leaves the organization)</li>
<li style="text-align: justify;">Sending passwords in plain-text over port 389</li>
<li style="text-align: justify;">I’ve seen applications with a check box to optionally ‘validate certificate’ when connection to AD using TLS (port 636). Why is this even an option? You are going to throw a password at an endpoint, and you’re not even going to check if it’s the endpoint that you think it is? </li>
</ul>
<br />
<div style="text-align: justify;">
It’s easy to get an LDAP client working, but just because it’s working, doesn’t mean the configuration is correct.</div>
<div style="text-align: justify;">
<br /></div>
<h3 style="text-align: justify;">
6. LDAP authentication and modern authentication are mutually exclusive</h3>
<div style="text-align: justify;">
An application using LDAP for authentication will forever be limited to usernames and passwords. Trying to implement modern technologies such as multi-factor authentication and single sign on are virtually impossible (unless you are going to roll your own, which is a bad idea in its own right). The FIDO alliance aims to make passwords a thing of the past. Granted, this is a long term vision, but every application using LDAP authentication is going to stand in the way of an organization going password-less.</div>
<div style="text-align: justify;">
<br /></div>
<h2 style="text-align: justify;">
What are the alternatives?</h2>
<div style="text-align: justify;">
Web applications today really have no need to use LDAP for authentication. There are many great web-enabled authentication protocols such as SAML, WS-Federation and OpenID Connect that do not require 3rd party applications having to touch credentials. Any number of products are available that provide these services, including Active Directory Federation Services (built into Windows Server), or hosted offerings such as Microsoft's Azure AD, Okta, Ping, and more. If you don’t have a federated identity provider available in your organization, you need to set one up as a first step.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
When you are looking at new software, or going to tender, set a requirement that it must support modern authentication protocols. You may be able to use your organizations purchasing power to negotiate a better security outcome for your users. It’s ok if the vendor pushes back, or the business wants an application that only has LDAP support today. Have the conversation with the vendor anyway and tell them you want them to add support for modern authentication protocols to their product roadmap. It gets the conversation going. The more customers ask for it, the more likely they are to build it. However, if none of their customers ask for it, they’ll never do it.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
A trend I’m really happy to see is a growing number of ‘thick-client’ desktop applications supporting modern authentication protocols. These apps have typically been an LDAP authentication stronghold. There are a growing number of SDKs such as the Microsoft Authentication Library (MSAL) that make it easy for developers to drop-in support for modern authentication into their mobile and desktop applications. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Ultimately, there is a reality to accept in that not all applications today support modern authentication protocols, and perhaps they never will. Implementing a total ban on LDAP authentication is likely not possible in any organization. However, LDAP authentication should be strongly discouraged within the organization. It should only be considered when no other options are available. When going to market for new software, mandate modern authentication support as a requirement. Have vendors or application owners prove that LDAP authentication is the only option before allowing it. Don't offer it as a commodity authentication service.</div>
<div style="text-align: justify;">
<br /></div>
Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com2tag:blogger.com,1999:blog-1261292475025329861.post-70389802015919078782017-09-20T07:25:00.001+10:002019-01-19T18:38:48.743+11:00Announcing Lithnet AutoSync for Microsoft Identity ManagerOne of the things that I've always believed was missing from MIM and its predecessors was the ability to automatically 'run' the sync engine. The supported way of 'automating' the sync engine is to develop scripts that call the MIIS WMI methods. However, those scripts usually just cycle through the management agents and run profiles in a predetermined order, at a predetermined interval.<br />
<br />
Over the years, I've often thought that there must be a better way than this! When we consider the various operations that can be performed on each management agent, the clues to how to do this start to become clear.<br />
<br />
<table>
<tbody>
<tr>
<td>Delta import</td>
<td>Performed when a change occurs in a connected system</td>
</tr>
<tr>
<td>Delta synchronization</td>
<td>Performed when an import operation stages <i>changes in a connector space</i></td>
</tr>
<tr>
<td>Export</td>
<td>Performed when a synchronization stages outbound <i>changes in a connector space</i></td>
</tr>
<tr>
<td>Confirming import</td>
<td>Performed when an export leaves <i>unconfirmed imports in the connector space</i></td>
</tr>
</tbody></table>
<br />
<div>
In all cases, except the delta import, the operations occur within the connector spaces of the sync engine itself. This means that the sync engine has the information needed to know when a sync, export and confirming import is required - It's all right there in the run history. All we really have to do, is somehow trigger the initial delta import when we know that changes have been made in the connected system.<br />
<br />
Today, I'm releasing my solution to this problem - Lithnet AutoSync.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj1Nkkoze_jjgjUmWymIsXbnpQpDLA7Fg0GmfquHMFyanjU9Mx81W4z_E2yv9F9oi-CIgStaLkdxiw8X94su8vo06b_chVwQLhgMe_gt665u6UIdLJc-Hl2zK4g98Osa83dG2M97lgfLmY/s1600/autosync-logo-sm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="188" data-original-width="561" height="132" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj1Nkkoze_jjgjUmWymIsXbnpQpDLA7Fg0GmfquHMFyanjU9Mx81W4z_E2yv9F9oi-CIgStaLkdxiw8X94su8vo06b_chVwQLhgMe_gt665u6UIdLJc-Hl2zK4g98Osa83dG2M97lgfLmY/s400/autosync-logo-sm.png" width="400" /></a></div>
<br />
AutoSync is designed to run the sync engine for you. All you need to do is tell it when to run the import operations. You can do this on a schedule (eg every 15 minutes), or in response to an event if your connected system supports change detection. Built-in triggers are provided for Active Directory, LDS, and the MIM service, and you can write your own triggers using PowerShell. I've put a <a href="https://github.com/lithnet/miis-autosync/wiki/Powershell-script-trigger-gallery" target="_blank">community PowerShell trigger gallery page up</a> where you'll eventually be able to find and share trigger scripts for various systems.<br />
<br />
Once the import has been triggered, AutoSync takes care of the rest. If the import results in staged changes in the connector space, a delta sync is performed. If the delta sync stages outbound changes on other management agents, then export operations are performed. After those exports, confirming imports are performed, and the cycle continues.<br />
<br />
AutoSync is fast.<span style="font-family: inherit;"> <span style="background-color: white; color: #24292e; font-size: 16px;">By ensuring run profiles are executed only when needed, AutoSync keeps the sync engine from doing work it doesn't need to do. When combined with event-based triggers, the sync engine can respond to changes in connected systems in as close to real-time as possible. AutoSync allows for the fastest propagation of changes possible, while respecting the sync engine rules for overlapping run profiles.</span></span><br />
<br />
<span style="background-color: white; color: #24292e; font-size: 16px;"><span style="font-family: inherit;">AutoSync follows the Microsoft guidelines when it comes to running multiple management agents simultaneously. While import and export operations are allowed to overlap, synchronizations must be run exclusively.</span></span><br />
<br />
AutoSync has been in development for over 18 months, and I'm very happy to be sharing this with you today. A special thanks to <a href="https://twitter.com/Piyush_" target="_blank">Piyush Khandelwal </a>is in order, as this project wouldn't have been possible without his many, many hours of diligent testing, bug-finding and feedback. A big thank you to those who have also helped test over the life of the project including <a href="https://twitter.com/darrenjrobinson" target="_blank">Darren Robinson</a>, <a href="https://twitter.com/mrgranfeldt" target="_blank">Søren Granfeldt</a>, and the whole team back at the office who have put up with untold numbers of pre-release builds.<br />
<br />
Head over to <a href="https://github.com/lithnet/miis-autosync/wiki">github to read the getting started guide</a>, and happy auto-syncing!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTxavQlg7w2kKcqF1dIrlZoG-qScuKiS0NdCssoFi17pOygiUr0JtnoRQ0_NA3MFFTgbORaTkB2E2zEn7ImkC6BJUww2yPV6v01Z-SgNNIB46WieOyfh_hPCWbsw1RL7fJc7efk-v1u3Sw/s1600/Animation5.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="314" data-original-width="1020" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTxavQlg7w2kKcqF1dIrlZoG-qScuKiS0NdCssoFi17pOygiUr0JtnoRQ0_NA3MFFTgbORaTkB2E2zEn7ImkC6BJUww2yPV6v01Z-SgNNIB46WieOyfh_hPCWbsw1RL7fJc7efk-v1u3Sw/s640/Animation5.gif" width="640" /></a></div>
<br />
<br />
<br />
<br /></div>
Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com6tag:blogger.com,1999:blog-1261292475025329861.post-13748488638927626702017-03-13T20:55:00.000+11:002017-03-13T20:55:54.568+11:00Assisted password reset add-on for the FIM/MIM portalMicrosoft Identity Manager and its predecessor, Forefront Identity Manager cater for self-service password reset (SSPR) scenarios with out of the box workflows that support SMS, email, and question/answer authentication. Self-service password reset is a very important capability for any organization, and when properly deployed, can significantly reduce calls to the service desk. <br />
<br />
However, even when SSPR is available in an organization, there will always be a percentage of password resets that the service desk performs. It could be that the user is not enrolled in SSPR, that they didn't know SSPR was available, or their registered SSPR mechanisms were no longer available (eg they have a new phone number, or no longer have access to their registered email address). In these cases, the service desk is usually called and a manual password reset is performed. This is not a scenario that is current supported by MIM directly, which typically results in the service desk dropping back to the AD admin centre or the users and computers console to perform this task. <br />
<br />
Today, I'm announcing the release of the Lithnet Assisted Password Reset (APR) add-on for the FIM/MIM portal. This tool integrates with the user RCDC to display a "Reset password" link, allowing quick access to reset the user's password using either a generated or user-specified password.<br />
<br />
It's simple, easy to install, and highly customizable. <br />
<br />
It supports<br />
<ul>
<li>Automatically generated random passwords of a configurable length</li>
<li>Manually specified passwords</li>
<li>Includes the option to force the user to change their password at next logon</li>
<li>Optionally forces service desk staff member to re-authenticate in order to reset a password</li>
<li>Can unlock locked accounts</li>
</ul>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibGry8bzm-R2JipOy3VOMHApfi3d0bxHfGjIgx9-YUJvgQZZIQqgQF4gKBvyxARGjH_fYb59nVNhe18wYosFnfv3D_nVH2r12ZVepsw8uYuyXd-ZXsVuZbmwkBL3WpRcHcRdQ-P733bU8G/s1600/screen-shot1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibGry8bzm-R2JipOy3VOMHApfi3d0bxHfGjIgx9-YUJvgQZZIQqgQF4gKBvyxARGjH_fYb59nVNhe18wYosFnfv3D_nVH2r12ZVepsw8uYuyXd-ZXsVuZbmwkBL3WpRcHcRdQ-P733bU8G/s1600/screen-shot1.png" /></a></div>
<br />
<br />
Visit the <a href="https://github.com/lithnet/resourcemanagement-ui-assistedpasswordreset/">GitHub site</a> to get started.<br />
<br />Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com2tag:blogger.com,1999:blog-1261292475025329861.post-41976778990690195412017-02-22T18:33:00.000+11:002017-02-22T18:33:46.075+11:00User verification add-on for the FIM/MIM PortalToday I'm releasing a new add-on for the FIM/MIM portal. The Lithnet User Verification Module allows IT staff to use the MIM portal to send an SMS code sent to a user's mobile phone. This is useful in scenarios where a user calls the service desk and needs to be verified before the service desk can take an action such as resetting a password or asking for a change to a group that they own.<br />
<br />
If you have your users registered for SMS-based self-service password reset, then this module is ready for you to use today. It will use the same<a href="https://github.com/lithnet/resourcemanagement-ui-userverification/wiki/Creating-an-SmsServiceProvider"> SmsServiceProvider.dll</a> you created to enable SSPR, and will get the user's mobile number from the msIdmOtpMobilePhone attribute.<br />
<br />
There are lots of <a href="https://github.com/lithnet/resourcemanagement-ui-userverification/wiki/Configuration-file-reference">configuration options available</a>, so if you want to get the mobile number from a different attribute, you can certainly do that. You can also customize the attributes displayed by the tool, change the length of the security code, and even restrict access to the tool to a particular set of users<br />
<br />
You access the tool by adding a new UocHyperLink control to your RCDC as shown below.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWHISGywf58W23-FzVpXflTE6WqyJ8bPEM-pqROqPCO_JEeQOUlFxxX75dzgCPY0ZP3lnG9GEd0wXRM7LVKCoD6CL1_u33BcFYzo1JKOMpCEOaV66Y5Cc7Rc0UIskLonSb7TEJbWhdESpW/s1600/rcdc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="471" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWHISGywf58W23-FzVpXflTE6WqyJ8bPEM-pqROqPCO_JEeQOUlFxxX75dzgCPY0ZP3lnG9GEd0wXRM7LVKCoD6CL1_u33BcFYzo1JKOMpCEOaV66Y5Cc7Rc0UIskLonSb7TEJbWhdESpW/s640/rcdc.png" width="640" /></a></div>
<br />
A new window will open with a customizable list of user attributes shown. Clicking the Send Code button will generate a unique code and send it to the user's phone<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjawFQaZDkE_ka6a06E0XmTXU52ENbZO28ADAcPdRUi20ck-P11hEr_DcszGnxdqREhy47FCtbFYWZ-h342VVNWqWyJ9H9wRk6oYgO-Hm7lBlE9Z424m1ckJS0qL2NKUNIrtXoDN_3Ozd-S/s1600/screen-shot1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjawFQaZDkE_ka6a06E0XmTXU52ENbZO28ADAcPdRUi20ck-P11hEr_DcszGnxdqREhy47FCtbFYWZ-h342VVNWqWyJ9H9wRk6oYgO-Hm7lBlE9Z424m1ckJS0qL2NKUNIrtXoDN_3Ozd-S/s1600/screen-shot1.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
The code is shown to the service desk operator, and the user receives the code on their phone</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgviQ-_8447sNtoOycXk5PZOl0oJfg42Oku8MNNIpnQ3nqmG9P1NBvwqH76UF5w6QbiA3M2CYZYWitDvMLVrP4RFWAl1aspzF2eE47v3yA9bhDB86rAV4KixsVegxK42hd-uyulkJkK5uHX/s1600/wp_ss_20170209_0001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgviQ-_8447sNtoOycXk5PZOl0oJfg42Oku8MNNIpnQ3nqmG9P1NBvwqH76UF5w6QbiA3M2CYZYWitDvMLVrP4RFWAl1aspzF2eE47v3yA9bhDB86rAV4KixsVegxK42hd-uyulkJkK5uHX/s640/wp_ss_20170209_0001.png" width="360" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
It's a simple, but useful tool for authenticating users over the phone.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Visit the <a href="https://github.com/lithnet/resourcemanagement-ui-userverification">GitHub site</a> to download the latest release and read the <a href="https://github.com/lithnet/resourcemanagement-ui-userverification/wiki/Installation-and-upgrade-steps">installation instructions</a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<br />Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-78511195625356037272017-02-02T18:51:00.000+11:002017-02-02T18:51:11.517+11:00Announcing v2 of the Lithnet FIM/MIM Service REST APIIn 2015, I released the first version of the REST API for the FIM/MIM Service. I designed it to abstract away the complexities of the native SOAP endpoint, and open up the possibilities of integrating with FIM from operating systems and libraries outside of the Windows/.NET ecosystem. <br />
<br />
It's been used by many awesome public and private projects since then. Check out Peter Stapf's guide on using it to <a href="https://justidm.wordpress.com/2016/05/16/creating-mobile-apps-for-mim-with-powerapps-and-lithnet-restapi/">create PowerApps</a>. <br />
<br />
Features have been added over time, usually by request, which means now, using simple JSON calls, you can perform the following tasks<br />
<ul>
<li><span class="fwn fcg"><span class="fcg"><span class="fwb">Create resources</span></span></span></li>
<li><span class="fwn fcg"><span class="fcg"><span class="fwb">Modify resources</span></span></span></li>
<li><span class="fwn fcg"><span class="fcg"><span class="fwb">Delete resources</span></span></span></li>
<li>Get a resource</li>
<li>Get the current user's permissions on a resource</li>
<li><span class="fwn fcg"><span class="fcg"><span class="fwb">Search for resources</span></span></span></li>
<li><span class="fwn fcg"><span class="fcg"><span class="fwb">Full localization support</span></span></span></li>
<li><span class="fwn fcg"><span class="fcg"><span class="fwb">Getting approval requests</span></span></span></li>
<li><span class="fwn fcg"><span class="fcg"><span class="fwb">Approving or rejecting requests</span></span></span></li>
</ul>
<span class="fwn fcg"><span class="fcg"><span class="fwb">However, I needed to make some changes to the API that would have broken compatibility with existing versions, so I decided to add another endpoint to this API, and release a new version.</span></span></span><br />
<br />
Both versions of the API are contained in the one installation package, and can safely run side-by-side. Any existing applications will continue to work with the v1 API version.<br />
<br />
Here are a list of fixes, enhancements, and new features of the v2 API.<br />
<span class="fwn fcg"><span class="fcg"><span class="fwb"><br /></span></span></span>
<br />
<h4>
Fixes</h4>
<ul>
<li>Fixes an issue where all attribute values were returned as strings, rather than their native JSON data type</li>
<li>Fixes an issue where multivalued attributes containing only a single value were serialized as a string, instead of an array</li>
</ul>
<h4>
Enhancements</h4>
<ul>
<li>Provides flexibility in how resources are returned from the API by allowing the caller to specify</li>
<ul>
<li>If null values should be returned or not</li>
<li>If values for single-valued attributes should be returned as arrays</li>
<li>If the resource should be rendered as a fixed structure, that does not depend on knowing the resource schema in advance</li>
</ul>
<li>Aligns the API to a set of REST API <a href="https://github.com/Microsoft/api-guidelines/blob/master/Guidelines.md">guidelines</a> published by Microsoft</li>
<ul>
</ul>
</ul>
<h4>
New Features</h4>
<ul>
<li>Adds support for getting permission hints for each attribute on a resource</li>
<li>Adds paged search support </li>
</ul>
<div>
<br /></div>
<span class="fwn fcg"><span class="fcg"><span class="fwb">Check out the updated <a href="https://github.com/lithnet/resourcemanagement-webservice/wiki">documentation</a>, and download the <a href="https://github.com/lithnet/resourcemanagement-webservice">new version today</a></span></span></span><br />
<span class="fwn fcg"><span class="fcg"><span class="fwb"><br /></span></span></span>
<span class="fwn fcg"><span class="fcg"><span class="fwb"><br /></span></span></span>
<span class="fwn fcg"><span class="fcg"><span class="fwb"><br /></span></span></span>Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-23598340476831262072017-01-23T06:39:00.000+11:002019-01-19T18:34:57.739+11:00AD, LDS and LDAP unauthenticated binds: A series of unfortunate security events<i style="background-color: white;">Update: December 2018: Microsoft have provided an option to <a href="https://blog.lithiumblue.com/2018/12/disabling-unauthenticated-binds-in.html">disable unauthenticated binds </a>in Windows Server 2019</i><br />
<i><br /></i>
A caution to anyone that uses applications that rely on LDAP authentication against Active Directory, or Active Directory Lightweight Directory Services to do so. Both services will appear to accept a blank password for any users when performing a simple bind. While behind the scenes, that's not what is happening, if your application doesn't check for and reject a logon attempt with the blank password itself, it might incorrectly assume a successful authentication against LDAP. This post details how I came to learn about this behaviour, how wide spread the problem is, and what can be done about it.<br />
<br />
<h2>
The discovery</h2>
A few weeks ago, I was at my desk, enjoying my lunch, when I received a call from a customer in a panic. He told me that our AD LDS server was allowing people to access his application without typing in a password. I assumed he was talking about anonymous binds (binding with no username and password) and informed him that it's a normal LDAP thing, and it just means his application probably hadn't been coded properly.<br />
<br />
But he insisted that was not what was happening. An anonymous bind requires a blank username and password, but he told me he was entering a username but had a blank password. I assumed he was going mad, but decided to fire up ldp.exe to test the scenario, confident that I'd be back to my sandwich and mindless web browsing in no time.<br />
<br />
Imagine my surprise when I was greeted with the following response<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">-----------<br />res = ldap_simple_bind_s(ld, 'uid=ryan,ou=users,dc=lithnet,dc=local, <unavailable>); // v.3<br />Authenticated as: 'NT AUTHORITY\ANONYMOUS LOGON'.<br />-----------</span><br />
<br />
Sorry... <i>what</i>?! <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifFCEUag4VEq9DKlyPb6xPpNkf1r-ybJmeWxiJBy5uqHmOjytbkjQ4g2IS7uYycoqrZdyDN79z_bhfu62hznudLk1LcbLKHYvY2XoRsicqLWtC-acv0Kxiq1lEQOrEQml2z2Q-VH_Faw9X/s1600/maxwell_smart__confused.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifFCEUag4VEq9DKlyPb6xPpNkf1r-ybJmeWxiJBy5uqHmOjytbkjQ4g2IS7uYycoqrZdyDN79z_bhfu62hznudLk1LcbLKHYvY2XoRsicqLWtC-acv0Kxiq1lEQOrEQml2z2Q-VH_Faw9X/s1600/maxwell_smart__confused.gif" /></a></div>
<br />
<br />
"Ummm..... Let me call you back..."<br />
<br />
<h2>
The investigation</h2>
We were transitioning away from an Oracle LDAP server at the time, so I tried to perform the same bind operation there:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">-----------<br />res = ldap_simple_bind_s(ld, 'uid=ryan,ou=users,dc=lithnet,dc=local', <unavailable>); // v.3<br />Error <48>: ldap_simple_bind_s() failed: Inappropriate Authentication<br />Server error: binds with a dn require a password</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">-----------</span><br />
<br />
Good. That's what I would have expected. What on earth was LDS doing? First I needed to make sure I didn't have an information disclosure issue. There is sensitive information in our directory, and it would be really bad news if this data was disclosed. I tried to perform a search in LDS after 'binding' with no password.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">-----------</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">ldap_search_s(ld, "DC=lithnet,DC=local", 2, "uid=ryan", attrList, 0, &msg)<br />Error: Search: Operations Error. <1><br />Server error: 000004DC: LdapErr: DSID-0C090752, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v2580<br />Error 0x4DC The operation being requested was not performed because the user has not been authenticated.<br />-----------</span><b></b><i></i><u></u><sub></sub><sup></sup><strike></strike><br />
<br />
Phew. Looking back at the logon result, it did indeed show I was authenticated as the built-in account<span style="font-family: "courier new" , "courier" , monospace;"> 'NT AUTHORITY\ANONYMOUS LOGON'</span>. This is the same result as performing an anonymous bind. Without granting permissions to this account, no data was going to be inadvertently disclosed. But it still didn't explain why the user could bind without a password. It <i>looks </i>like the user specified is being authenticated successfully without a password, although we are not being authenticating as that user at all.<br />
<br />
Now, I want to talk about anonymous binds for a moment. There is some confusion out there in articles and posts I have read about this behaviour, with people saying that this is an anonymous bind and it is normal behaviour. Anonymous binds are indeed normal and required by the LDAP specification. Clients must be able to connect to the RootDSE anonymously, which contains information they need to understand the capabilities, configuration, and authentication types that the directory supports. LDS allows anonymous binds to the RootDSE, as does any RFC-compliant LDAP client. The fact that we were bound as ANONYMOUS LOGON isn't the issue. The fact that we provided a username and no password <i>is</i> an issue. This is NOT an anonymous bind as per the <a href="https://tools.ietf.org/html/rfc4513#section-5.1.1">RFC specification</a>. <br />
<br />
Moving on, I decided to try to log into an application that I knew used LDS for authentication.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8QfIbYjHkS0YpLk5lQm0KeEEN71gCTewTyVxjWwU4lECmfbFm-m77SDZdvRpjl4d6eDSmCCDDM4Oe43iuVgr8yomWUVHk6UYOM5TBJWWdbJXoip11f3tgpKle4HLfbdRkRK2AkomChZqp/s1600/access-denied.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="143" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8QfIbYjHkS0YpLk5lQm0KeEEN71gCTewTyVxjWwU4lECmfbFm-m77SDZdvRpjl4d6eDSmCCDDM4Oe43iuVgr8yomWUVHk6UYOM5TBJWWdbJXoip11f3tgpKle4HLfbdRkRK2AkomChZqp/s320/access-denied.png" width="320" /></a></div>
<br />
Phew, again. The application is obviously smart enough to detect the blank password and refuse to authenticate. I started wondering if this the <i>responsibility</i> of all applications to implement this check? It does sound like a reasonable safety precaution to implement when coding an application, but I've seen my fair share of code enough to know that not all developers are as prudent as this. I tried to login to the application the customer called me about, and sure enough, it let me into the application. I had varying results from other applications I tried. It really came down to the specific LDAP library and authentication implementation that these clients used. Suffice to say, it was not a one-off problem.<br />
<br />
<h2>
Rolling up the sleeves</h2>
<br />
It was time to dig into the LDAP RFC to see what is going on here. <a href="https://tools.ietf.org/html/rfc4513#section-5.1.2">Section 5.1.2 of RFC 4513</a> revealed something very interesting that I did not know about LDAP before. It reads;<br />
<blockquote class="tr_bq">
<br />
<i>5.1.2. </i><u><i>Unauthenticated Authentication</i></u><i> Mechanism of Simple Bind</i><br />
<i></i><br />
<i>An LDAP client may use the unauthenticated authentication mechanism of the simple Bind method to establish an anonymous authorization state by sending a Bind request with a name value (a distinguished name in LDAP string form [</i><a href="https://tools.ietf.org/html/rfc4514"><i>RFC4514</i></a><i>] of non-zero length) and specifying the simple authentication choice containing a password value of zero length</i></blockquote>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvlwtzOg4qs56BeM5CjXfwDz5sDEcwpnTLDrMSMlKFt_PIvzelq-gZ2XS08Wr3N4k-OpWFdXHSaoAmoQQVG81WMuUiWwo0hpTrhbT6Ew8p6iibIntG1GGw1aD2-bfFRp-Arv3ynz8mUxTL/s1600/piccard.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvlwtzOg4qs56BeM5CjXfwDz5sDEcwpnTLDrMSMlKFt_PIvzelq-gZ2XS08Wr3N4k-OpWFdXHSaoAmoQQVG81WMuUiWwo0hpTrhbT6Ew8p6iibIntG1GGw1aD2-bfFRp-Arv3ynz8mUxTL/s320/piccard.gif" width="320" /></a></div>
<br />
So this was apparently a legitimate thing, and it's called an <b>Unauthenticated bind</b>. Note this is distinct from an "Anonymous bind" where <i>both</i> a blank username and blank password must be provided. Reading further along reveals something even more interesting<br />
<br />
<blockquote class="tr_bq">
<i>The distinguished name value provided by the client is intended to be used for trace (e.g., logging) purposes only. The value is not to be authenticated or otherwise validated (including verification that the DN refers to an existing directory object). The value is not to be used (directly or indirectly) for authorization purposes.</i> </blockquote>
<div>
It seems that this 'feature' exists only for logging purposes. According to the RFC, you don't even need a real DN. I tested this out.</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">-----------<br />res = ldap_simple_bind_s(ld, 'this is not a DN', <unavailable>); // v.3<br />Authenticated as: 'NT AUTHORITY\ANONYMOUS LOGON'.<br />-----------</span></div>
<div>
<br />
Well, that worked, as the RFC said it should. At this point I started to get a bit worried. I've spent a long time working with LDAP directories and I didn't know this was a thing. What chance do we have that application developers, who don't spend their lives living and breathing identity and LDAP, are aware of this and implement appropriate mitigations? Reading the next paragraph did give me some hope.<br />
<br />
<blockquote class="tr_bq">
<i>Unauthenticated Bind operations can have significant security issues (see </i><a href="https://tools.ietf.org/html/rfc4513#section-6.3.1"><i>Section 6.3.1</i></a><i>). In particular, users intending to perform Name/Password Authentication may inadvertently provide an empty password and thus cause poorly implemented clients to request Unauthenticated access.</i> </blockquote>
</div>
<div>
<br /></div>
<div>
Yes! This is exactly what I was concerned about. </div>
<div>
<br /></div>
<div>
<blockquote class="tr_bq">
<i>Clients SHOULD be implemented to require user selection of the Unauthenticated Authentication Mechanism by means other than user input of an empty password. Clients SHOULD disallow an empty password input to a Name/Password Authentication user interface.</i> </blockquote>
</div>
<div>
<br /></div>
<div>
Urgh. Well, yes I agree 100% in principal. They SHOULD do that. It's basic input validation. But how many of it actually even know about this curiosity though? The next line was the one I wanted to see.</div>
<div>
<br /></div>
<blockquote class="tr_bq">
<i><b>Additionally, Servers SHOULD by default fail Unauthenticated Bind requests with a </b><b>resultCode</b><b> of </b><b>unwillingToPerform</b><b>.</b></i> </blockquote>
<div>
By default, the server should not accept this! Perhaps we changed something in our build and accidentally enabled this? To rule this out, I built a brand new LDS instance on a fresh Windows Server 2012 R2 server. I fired up LDP.exe, and guess what?</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">-----------<br />res = ldap_simple_bind_s(ld, 'this is not a DN', <unavailable>); // v.3<br />Authenticated as: 'NT AUTHORITY\ANONYMOUS LOGON'.<br />-----------</span><br />
<span style="font-family: "courier new";"><br /></span>
<span style="font-family: "courier new";"></span></div>
<div>
Then, I had a thought, accompanied by a feeling of dread. AD LDS uses the same underlying engine as Active Directory. Could the same problem exist there?</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">-----------<br />res = ldap_simple_bind_s(ld, 'this is not a DN', <unavailable>); // v.3<br />Authenticated as: 'NT AUTHORITY\ANONYMOUS LOGON'.<br />-----------</span></div>
<div>
<br />
Yep. :(<br />
<br /></div>
<h2>
Mixed emotions</h2>
<span style="font-family: "courier new";"><br /></span>I've got mixed emotions at this point. I'm happy that we didn't do something wrong in our build, but also, part of me was wishing that we had. Trawling through the AD and LDS documentation made no mention of a setting to be able to turn this off. At this point we had over a hundred applications moving off the old Oracle LDAP server and onto LDS. Some of these were home grown, and could be fixed ourselves, others were written by 3rd parties, and getting code updates would be a lot more complicated.<br />
<br />
At this point, I'd given up on lunch.<br />
<br />
It is important to note, that this issue poses absolutely no risk to the security of your <i>LDAP</i> system. AD and LDS are secure and there is no risk of information disclosure (unless you deliberately grant rights to NT Authority\Anonymous logon). The security of your applications that unconditionally delegate responsibility for authentication to the LDAP directory however, is another question.<br />
<br />
We logged a case with Microsoft, and after much deliberation about the intent of the RFC and whether it was the client or servers responsibility to fix this, we submitted a product change request, which is still under consideration.<br />
<br />
The discussion we had centred around the fact that this issue stems from poorly coded applications. It's hard to disagree with this point. Especially since the RFC says that clients should not allow this to happen. But the RFC also says that the servers also should not allow this to happen. When it comes down to it, we have to consider which is easier to do? Find and update every bad application that was ever made, and keep an eye on every application to come in the future? That's going to be about as successful as herding cats. <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2UZA_6SyUk-7_shulDFvz2ItfPGGnE_qwLj5JXixfHDnydkLP7duJYlkEqmKJant3QjC0omMvWXfi5TD32gCZG7r0U_Y_gdJ3ht7s2SHXlpyUS4SGVeeSPg4RP25t45O8KDtHVNj4Dfme/s1600/herding-cats.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2UZA_6SyUk-7_shulDFvz2ItfPGGnE_qwLj5JXixfHDnydkLP7duJYlkEqmKJant3QjC0omMvWXfi5TD32gCZG7r0U_Y_gdJ3ht7s2SHXlpyUS4SGVeeSPg4RP25t45O8KDtHVNj4Dfme/s1600/herding-cats.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2UZA_6SyUk-7_shulDFvz2ItfPGGnE_qwLj5JXixfHDnydkLP7duJYlkEqmKJant3QjC0omMvWXfi5TD32gCZG7r0U_Y_gdJ3ht7s2SHXlpyUS4SGVeeSPg4RP25t45O8KDtHVNj4Dfme/s1600/herding-cats.gif" /></a></div>
<br />
Or do we prevent the problem at its source? Unauthenticated binds serve no useful purpose that I am aware of. The RFC mentions that it exists for trace purposes, and I doubt that many people even know such a thing exists. There is certainly no product I have ever come across that required unauthenticated binds turned on for it to work. The RFC states that it should be turned off by default, so we can consider that it isn't crucial to LDAP operations. <br />
<br />
<h2>
How wide spread is this problem?</h2>
<br />
There are only a handful of LDAP server vendors, but untold numbers of LDAP clients. I decided to investigate a few well-known LDAP providers, to determine what their support for disabling unauthenticated binds is.<br />
<br />
<table style="border-collapse: collapse; border: 1px solid #ddd; padding: 5px; text-align: left; width: 100%;">
<tbody>
<tr>
<th>Product</th><th>Can be disabled</th><th>Disabled by default</th>
</tr>
<tr>
<td>Red Hat Directory Server</td>
<td>Yes</td>
<td><a href="https://access.redhat.com/documentation/en-US/Red_Hat_Directory_Server/8.1/html/Administration_Guide/unauthenticated-binds.html">Yes</a></td>
</tr>
<tr>
<td>OpenLDAP</td>
<td>Yes</td>
<td><a href="http://www.openldap.org/doc/admin24/security.html">Yes</a></td>
</tr>
<tr>
<td>Novell eDirectory</td>
<td>Yes</td>
<td><a href="https://www.novell.com/support/kb/doc.php?id=3449660">No</a></td>
</tr>
<tr>
<td>Oracle/Sun Directory Server</td>
<td>Yes</td>
<td><a href="https://docs.oracle.com/cd/E19424-01/820-4809/auto30/index.html">Yes</a></td>
</tr>
<tr>
<td>Microsoft AD LDS/ADAM</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td>Microsoft Active Directory</td>
<td>No</td>
<td>No</td>
</tr>
</tbody></table>
<br />
Red Hat Directory Server, Open LDAP and Oracle/Sun Directory all ship with unauthenticated binds disabled by default. As per the RFC. Novell has it enabled, but provide guidance on turning it off. Microsoft has the only product (in my admittedly limited research) that does not provide this option at all. Given how wide-spread the deployment of Active Directory is, this is really a concern.<br />
<br />
It's a common enough problem that there are <a href="https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=unauthenticated+bind">many</a> <a href="https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=ldap+blank+password">publicly listed</a> <a href="https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=ldap+null+password">vulnerabilities</a> in the CVE database. Keep in mind that these are only the ones that were publicly disclosed. There are no doubt many more not listed, or not yet even known. If well-established vendors such as VMWare and Apache can make these mistakes, it goes to show that anyone can. We're talking some serious issues here. Even the <a href="https://access.redhat.com/security/cve/CVE-2010-2940">SSSD authentication module in Linux</a> was vulnerable until 2010.<br />
<br />
We have to ask the question, if unauthenticated binds are the source of so many critical security issues in other products, and they add little to no measurable value, why should that capability be turned on by default in any product? Why should that capability even exist? <br />
<br />
<h2>
So, who should fix it?</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyKZ3rmiWkZRIWaVDddrnOYHlxiB3GCErmGokSTfpB2lmR1KWHW2cKsZXbwZfCBOdpAxdEKsdjpKyp63IXgwWaPCxUKlrK4Yer5trcIOWp2GjYsrdS0tBYW-u_Fu37J5-ZHCTuUzqAD1r6/s1600/dunno.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="133" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyKZ3rmiWkZRIWaVDddrnOYHlxiB3GCErmGokSTfpB2lmR1KWHW2cKsZXbwZfCBOdpAxdEKsdjpKyp63IXgwWaPCxUKlrK4Yer5trcIOWp2GjYsrdS0tBYW-u_Fu37J5-ZHCTuUzqAD1r6/s320/dunno.gif" width="320" /></a></div>
<div>
<br /></div>
Discussion forums and bug reports are full of <a href="https://mail.python.org/pipermail/python-ldap/2005q1/001484.html">back</a>-<a href="https://github.com/BorisMorel/LdapBundle/issues/36">and</a>-<a href="https://github.com/ruby-ldap/ruby-net-ldap/issues/5">forthing</a> between the developers saying that its a misconfiguration on the LDAP server side, and users being stuck with products like AD LDS that do not allow you to configure this, one way or another, begging the developers to fix it on the application side. The RFC compounds this problem by stating that it is both the client and server's responsibility to protect against the problem.<br />
<br />
In my view, both parties have a responsibility here.<br />
<br />
We can't reasonably expect every application developer to read every page of the LDAP RFCs. This is a counter-intuitive 'feature' hidden away in the depths of a protocol definition. One could argue that adequate testing would identify this, but that's assuming you are testing against a directory that has this setting turned on. You could also argue that applications should be checking their input values, and again you wouldn't be wrong. Ultimately, an application is responsible for its own security.<br />
<br />
But server vendors have a responsibility to ship products in a secure configuration that protects the organizations using them. I'm a big believer in the fact that LDAP is NOT an authentication protocol (I've got a blog post coming about this), and yes technically, there are no security flaws in the LDAP server itself. However, we have to accept the reality, rightly or wrongly, that it's a very widely-used <i>de facto </i>authentication protocol. A default setting change here (or at minimum, the <i>option </i>to change the setting), protects the organizations who use these applications from easy to avoid cases of information disclosure, unauthorized access, and a potential raft of other serious security issues.<br />
<br />
There is no discernible impact to turning off unauthenticated binds. If we were to see an LDAP v4 in the future, I would hope to see this confusing and unnecessary feature removed. The only better than that, would be if we stop using LDAP as an authentication protocol altogether. While that's a future that, at least for now, only exists in my dreams, Microsoft can and should do something now to provide the ability to turn off unauthenticated binds in AD and LDS, and set it to be off by default in a future version of the product. The world will be a better (safer) place.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg50_gRWuWM2hg2icQrkqoTiU3z61HSjnvkl7Th-bunVGE_UKBPNh1secChSy-4dme_ThwR0bFt-5Y42FoV2muoTzUtO0tAUMv5kPVkk4qL5V1TQrDkaicHFbo23gSgkaA5BEbrVkCyWNmB/s1600/dreamy.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg50_gRWuWM2hg2icQrkqoTiU3z61HSjnvkl7Th-bunVGE_UKBPNh1secChSy-4dme_ThwR0bFt-5Y42FoV2muoTzUtO0tAUMv5kPVkk4qL5V1TQrDkaicHFbo23gSgkaA5BEbrVkCyWNmB/s320/dreamy.gif" width="320" /></a></div>
<br />
<br />Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-46857944003106689722017-01-21T11:13:00.000+11:002017-01-21T11:13:08.394+11:00The FIM/MIM Synchronization engine stops responding<h3>
Problem</h3>
There is a known issue with FIM/MIM that causes the synchronization service to stop responding, requiring you to kill miiserver.exe with task manager and restart the service.<br />
<h3>
Cause</h3>
This is triggered when the following two conditions occur<br />
<ol>
<li>A delta import on the FIM MA finishes</li>
<li>A synchronization run on another MA is in progress</li>
</ol>
<h3>
Investigation</h3>
When the FIM MA goes to write its delta watermark, it does so by updating the value in its MA configuration. Unlike other MAs, this requires a full MA config update (the same as if you changed a flow rule or other setting in the MA config), which increments the version number, and requires an exclusive database lock.<br />
<br />
A synchronization running at the same time reads the same database table and causes a deadlock situation that is never resolved.<br />
<br />
Evidence that this behaviour is different from other MAs can be seen by running the following command using <a href="https://github.com/lithnet/miis-powershell">Lithnet MIIS PowerShell</a>. The FIM MA will always have a much higher version number that any other MA, and will increment after every import operation. Other MAs only update their version number when their configuration is changed by an admin in the UI.<br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">PS C:\Windows\system32> Get-ManagementAgent | select Name, Version, Category</span><br />
<span style="font-family: "Courier New", Courier, monospace;">Name Version Category <br />---- ------- -------- <br />AD 59 AD <br />_IAM_ReleaseVersion 42 Extensible2<br /><span style="background-color: yellow;">FIMService 113951 FIM </span><br />_IAM_SyncEngineVersion 41 Extensible2<br />Sap 35 Extensible2<br />GoogleApps 83 Extensible2<br />LDS 23 ADAM <br />ACMA 76 Extensible2<br />StudentDB 93 Extensible2<br />ZAVip 99 MSSQL </span><span style="font-family: "Courier New", Courier, monospace;"></span><br />
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">Interestingly, the UI prevents you from making changes to an MA when other MAs are running, presumably to protect against this kind of situation. However a FIM MA delta import ultimately does exactly the same thing.</span></div>
<span style="font-family: "Courier New", Courier, monospace;"><br /></span><h3>
Workaround</h3>
Unfortunately, the only way to prevent this from happening is to not run any synchronization profiles, when an import is running on the FIM MA.<br />
<br />
This issue was logged with Microsoft, however, this will not be fixed, as their support statement is that running multiple run profiles simultaneously is not supported.<br />
<br />Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-53202323395371237492017-01-17T16:58:00.000+11:002017-01-17T16:58:29.966+11:00"The cause of the error is not clear" - User will not sync into Azure AD with AAD Sync or AD ConnectRecently, we had an issue where four specific users would not sync into Azure AD. There were no noticeable differences in attributes between these users and ones that were working.<br />
<br />
Compounding the issue was a rather unhelpful error message<br />
<br />
<i>The cause of the error is not clear. This operation will be retried during the next synchronization. If the issue persists, contact Technical Support</i><br />
<br />
With a little help from Microsoft support, we were able to resolve the issue using the following steps<br />
<ol>
<li>First, create a new user in Office 365 with a default domain UPN (eg org.onmicrosoft.com)</li>
<li>Get the users ObjectGUID from AD</li>
<li>Set the ImmutableID attribute on the new account to be the ObjectGUID of the AD account</li>
<li>Run a delta sync or wait for next scheduled sync. At this point, the AD user will be joined with the Azure user account, and the user's attributes will be updated appropriately.</li>
</ol>
<div>
For example, if you receive the following error in an email</div>
<span style="font-family: "calibri";"></span>
<br />
<table border="1" cellpadding="0" cellspacing="1" style="margin: 0px;">
<tbody>
<tr style="mso-yfti-firstrow: yes; mso-yfti-irow: 0; mso-yfti-lastrow: yes;">
<td style="background-color: transparent; border: 0px rgb(0, 0, 0); margin: 0px; padding: 0.75pt;"><div style="margin: 0px 0px 11px;">
<u><span style="color: blue; font-size: 12pt; line-height: 107%; margin: 0px;">user1@lithnet.io</span></u></div>
</td>
<td style="background-color: transparent; border: 0px rgb(0, 0, 0); margin: 0px; padding: 0.75pt;"><div style="margin: 0px 0px 11px;">
<span style="font-size: 12pt; line-height: 107%; margin: 0px;">The cause of the error is not clear. This operation will be retried
during the next synchronization. If the issue persists, contact Technical
Support.</span></div>
</td>
<td style="background-color: transparent; border: 0px rgb(0, 0, 0); margin: 0px; padding: 0.75pt;"><div style="margin: 0px 0px 11px;">
<span style="font-size: 12pt; line-height: 107%; margin: 0px;">HiRTTFj/bVurY4hUBb+zwg==</span></div>
</td>
</tr>
</tbody></table><br/>
You can use the following PowerShell command to resolve the issue<br />
<script src="https://gist.github.com/ryannewington/8b4c07948b052a6b24b1dd4db1973f20.js"></script>
<br />
Credit goes to Cameron Duck for the troubleshooting process and coming up these resolution steps
Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-91522867639454640612016-06-21T10:04:00.002+10:002016-06-21T10:04:57.603+10:00Manually adding admins to the FIM/MIM ServiceOne of the first things I do when building a new FIM service instance is to create an admin account for myself, so I can use PowerShell tools, log into the portal etc without having to use things like runas to log in as the built-in admin account.
<br />
<br />
While you can create a resource manually in the portal, you can't easily set the binary ObjectSID value on the resource, and without that, you can't log in. Manipulating binary attributes quite tricky to do with the out of box tools. To set the users up properly, you usually have to flow them in from the AD MA with the ObjectSID present. Which can take quite a bit of effort.<br />
<br />
I prefer to cheat, and get my own account into the portal via script. The <a href="https://github.com/lithnet/resourcemanagement-powershell" target="_blank">Lithnet FIM/MIM Service PowerShell Module</a> has first-class support for binary attributes, so using this to add the ObjectSID is just as easy as it is to set a string value.<br />
<br />
This script will add the specified AD user to the FIM/MIM service and add them to the administrators set. They can then log in using their own credentials.
<br />
<br />
<script src="https://gist.github.com/ryannewington/286d311a0755eec58d3f6f5591b6c3c9.js"></script>
Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-57427019407018428772016-02-20T15:00:00.000+11:002016-06-23T15:35:28.250+10:00Powering though bulk object updates with Lithnet FIM/MIM Service PowerShell ModuleThe <a href="https://github.com/lithnet/resourcemanagement-powershell">Lithnet FIM/MIM Service PowerShell module (LithnetRMA)</a> allows you to not only <a href="http://blog.lithiumblue.com/2015/08/cut-down-on-your-powershell-code-with.html">drastically reduce the code you have to write</a> to perform basic create, update and delete operations, but it’s significantly faster than the out-of-box FimAutomation module too. I love hearing reports from people about how they were able to cut hours off the execution time of their existing scripts by converting them to use LithnetRMA!<br />
<blockquote class="twitter-tweet" data-lang="en">
<div dir="ltr" lang="en">
Read about how Jon got a script down from 22 hours to 6 mins using the Lithnet <a href="https://twitter.com/hashtag/fim2010?src=hash">#fim2010</a> PowerShell module. Awesome! <a href="https://t.co/bVr1dHx7ey">https://t.co/bVr1dHx7ey</a></div>
— Ryan Newington (@RyanLNewington) <a href="https://twitter.com/RyanLNewington/status/652582583218270208">October 9, 2015</a></blockquote>
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script> <br />
<blockquote class="twitter-tweet" data-lang="en">
<div dir="ltr" lang="en">
Read about Ike's performance improvements using Lithnet <a href="https://twitter.com/hashtag/fim2010?src=hash">#fim2010</a> PowerShell module - 12 hours down to 30 minutes! <a href="http://t.co/KVpv4qreV1">http://t.co/KVpv4qreV1</a></div>
— Ryan Newington (@RyanLNewington) <a href="https://twitter.com/RyanLNewington/status/638896484847480832">September 2, 2015</a></blockquote>
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script> <br />
One of the biggest speed advantages that LithnetRMA has over the out-of-box FIMAutomation module is that it supports composite updates. Composite updates combine multiple updates for different objects into a single message that is sent to the server, providing a far more efficient approach than sending one message per object. Combining composite updates with the ability to selectively request only the attributes you need to work with, we reduce the processing effort and amount of data transferred to a bare minimum. Let’s have a look at a real world scenario.<br />
Recently, I needed to delete a custom attribute from the schema. As luck would have it, this attribute had a value present on about 120,000 user objects, so all those values has to be cleared before I could remove the attribute from the schema. I could have been waiting for hours, if not days, had I used the FIMAutomation module. Instead, I wrote a quick script to do the job using LithnetRMA. It managed to do the job in a little under 20 minutes.<br />
<script src="https://gist.github.com/ryannewington/00debe1dadabb0a8ebe73e96da4b5846.js"></script>
This script will get all the objects with a value present for myAttribute, in batches of 500, save them to the server, and repeat the process until there are no objects left. Note the use of the AttributesToGet parameter. We are only interested in the myAttribute value – so we can tell the server to only send us that attribute. <br />
The MaxResults and PageSize parameters can be tuned to suit the operation you need to perform. In general, the larger these values, the faster the operation will be – but make them too large, and you risk timeout errors if the operation takes too long. You will need to tune these values to get the right balance between performance and reliability. There are no recommended values for these parameters as it varies based on many factors, including;<br />
<ul>
<li>How many attributes are being requested
<li>How many updates are being made
<li>The hardware specifications of the FIM service server and database</li>
</li>
</li>
</ul>
A good starting point is probably to set the parameters at 200, and work your way up from there.<br />
<a href="https://github.com/lithnet/resourcemanagement-powershell">Get LithnetRMA</a> today and start saving time with your bulk updates!Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-54403931856403169492015-11-14T08:59:00.001+11:002016-06-23T15:02:08.265+10:00Resources from WIN332 Microsoft Ignite Session<p>Thanks to all who came along to my session at Microsoft Ignite today. Here are the resources I presented at today’s session WIN332 – From Fortran to FIM: Dragging your identity management system of our the dark ages.</p> <h2>Resources for Engineers and Admins</h2> <p><a href="https://github.com/lithnet/acma">Lithnet ACMA Codeless business rules engine</a></p> <p>ACMA is a fast, efficient, codeless way of implementing business rules that can create and transform information within your FIM/MIM implementation. ACMA comes with a UI editor for your rules file, a PowerShell module for modifying ACMA objects directly, and a unit testing engine that allows you to test all the rules you have created. Check out the video link below for a more detailed demonstration of the capabilities of ACMA</p> <p><a href="https://github.com/lithnet/umare">Lithnet Universal MA Rules Extension</a> (UMARE)</p> <p>UMARE is a codeless rules extension for FIM/MIM. It can be used on any MA to perform transform operations on incoming and outgoing identity data. With over 40 transforms available out of the box, including very common scenarios we all need to support like converting an ‘accountDisabled’ attribute to a bitmask on the AD userAccountControl attribute, and converting the FIM Service group type strings into the right groupType value in AD. If there is a transform that’s missing, let me know and I can add it in.</p> <p><a href="http://fimmre.codeplex.com/">Granfeldt FIM Metaverse Rules Extension</a></p> <p>Forget DREs, EREs and sync rules. Get a hold of Soren’s Metaverse Rules Extension. It’s a very powerful and flexible component that can reduce the complexity of your provisioning time. Create a provisionToAD attribute in ACMA, flow it out to the metaverse, and add a provisioning rule to the MRE to provision when that flag is true. Keep the complexity in ACMA, and let MRE handle the ‘acting’.</p> <p><a href="https://www.visualstudio.com/pricing/visual-studio-online-pricing-vs">Visual Studio Online</a></p> <p>If you don’t have GIT or TFS in your organization, you can get a Visual Studio Online account from Microsoft that is free for up to 5 users. A version control system is a must-have for tracking your documents, scripts and code versions for your various components</p> <p><a href="https://github.com/lithnet/resourcemanagement-powershell">Lithnet FIM Service PowerShell Module</a> (LithnetRMA)</p> <p>The FIMAutomation module can do a lot, I find it is just overly complicated when we want to simply add, update, create and delete objects in the FIM service. It’s also very very slow. The Lithnet PowerShell module abstracts the complexity of the FIM service, and exposes a more natural and much faster set of cmdlets for working with the FIM service. It also comes with cmdlets to help you build XPath queries correctly, as well as the Import-RMConfig cmdlet for importing your configuration from files, as demonstrated in todays session. People using this module have reported their scripts improving from hours to minutes using this module. It’s also many orders of magnitude less PowerShell code to write and maintain. </p> <h2>Resources for Developers</h2> <p><a href="https://github.com/lithnet/resourcemanagement-client">Lithnet FIM Service Client</a> (LithnetRMC)</p> <p>If you have had to write .NET code to talk to the FIM service endpoints, you know how daunting this can be. The fim2010client on codeplex took us partially there by setting up the scaffolding for us, but still left us having to deal with the internals of the FIM service. The Lithnet FIM Service client is a nuget package you can install in your project, and start using simple, get, update, save operations. It’s fast, supports multi-threading out of the box, and has a complete MSDN-style documentation with examples on how to use it. The LithnetRMA PowerShell module, as well as the REST API are both lightweight wrappers for the functionality contained in this module.</p> <p><a href="https://github.com/lithnet/resourcemanagement-webservice">Lithnet FIM Service REST API</a> (LithnetRMWS)</p> <p>Ever tried talking to the FIM service endpoints from a non-windows device such as linux? I don't recommend trying. The Lithnet FIM Service REST API exposes the FIM service using very simple JSON and standard REST API calls. </p> <h2>Further learning</h2> <p><a href="https://unifysolutions.jira.com/wiki/display/FIMTEAMCOM/2014-09-17+-+Business+Rules+MA%2C+Engine+and+Configuration">FIM User group presentation on ACMA</a></p> <p>Want to see ACMA in action? Check out my presentation to the FIM team user group. You’ll get to see how you can easily create business rules, unit tests, and see some more advanced topics like creating admin accounts with the shadow object feature, and inheritance of values between referenced objects.</p> <p><a href="https://unifysolutions.jira.com/wiki/display/FIMTEAMCOM/2015-08-19+-+A+new+FIM+Client">FIM User group presentation on the Lithnet FIM Service Toolkit</a></p> <p>The Lithnet FIM Service toolkit contains the .NET client, PowerShell module, and REST API. You can see how these all work in this presentation to the FIM team user group.</p> <h2>FIM Team User Group</h2> <p>I highly recommend that you join the <a href="https://www.thefimteam.com/fim-team-user-group">FIM Team User Group</a>. The group meets monthly and experts from around the world present on various topics relating to FIM/MIM. It’s a great way to make connections, and learn how other people are solving challenges in the identity management space</p> Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-44039069242020525612015-10-22T19:56:00.001+11:002016-07-11T09:56:05.630+10:00Providing confirming imports to the sync engine when your target system doesn’t support delta importsThere are many systems out there that just don’t support delta imports. Deltas are important for ensuring speedy and efficient operation of the FIM synchronization engine. While we can’t invent true deltas when the connected system doesn’t provide the information, sometimes its enough to just provide FIM the deltas of what <strong>it</strong> has changed. This is especially relevant for systems where FIM is mostly responsible for the data in the connected system.<br />
What would be handy, is if at export time, we could provide FIM with the confirmed changes made in the target system. While FIM allows us to say that an export was successful, it still expects to confirm those changes took place on the next import operation. What if we could construct our import <a href="https://msdn.microsoft.com/en-us/library/hh858421">CSEntryChange</a> objects at export time?<br />
The <a href="https://github.com/lithnet/mms-utilities">Lithnet.MetadirectoryServices</a> library contains a set of helper classes to make writing management agents and rules extensions a little bit easier. One of the tools it contains is a <a href="https://lithnet.github.io/documentation/msu/html/C0CF58A4.htm">CSEntryChangeQueue</a> class, that gives you the ability to serialize <a href="https://msdn.microsoft.com/en-us/library/hh858421">CSEntryChange</a> objects to XML and back again. <a href="https://msdn.microsoft.com/en-us/library/hh858421">CSEntryChange</a> objects are placed in the queue on export and saved to disk at the end of the operation. On a delta import operation, the queue is reloaded from disk, and the <a href="https://msdn.microsoft.com/en-us/library/hh858421">CSEntryChanges</a> are passed back to the sync engine. There are two scenarios we can explore that can take advantage of this functionality. <br />
<br />
<h3>
Scenario 1: Re-playing the exported CSEntryChange back to the sync engine on delta import</h3>
<div>
This is a really quick and easy way to get confirming imports. After the successful export of an item to the target system, we can simply pass the <a href="https://msdn.microsoft.com/en-us/library/hh858421">CSEntryChange</a> exported by FIM into the <a href="https://lithnet.github.io/documentation/msu/html/C0CF58A4.htm">CSEntryChangeQueue</a>, and get it back from the queue when the next import operation is called. Provided nothing went wrong with the export, we have all the information needed to provide the confirming import to FIM on the next import operation.</div>
Be warned – you must ensure that you only replay <a href="https://msdn.microsoft.com/en-us/library/hh858421">CSEntryChanges</a> for successful exports, otherwise you may misrepresent the state of your connected system to the sync engine. A full import will be required for FIM to get the correct information.<br />
<br />
<h3>
Scenario 2: Constructing the delta import CSEntryChange at export time, and saving it for the next delta import</h3>
This is a really cool way to provide deltas. Let’s say you update a resource, and upon update, the system provides you a copy of (or link to) the resource after your update. This is quite a common practice with REST APIs. After a successful PUT or POST operation, you may receive a new copy of the resource as it appears after your modification. You actually have a true delta representation that you want to provide to the sync engine, its just that you are in the middle of an export, and the sync engine doesn’t want it yet! So, you can construct your <a href="https://msdn.microsoft.com/en-us/library/hh858421">CSEntryChange</a> for import as you normally would with the information returned by the system, and submit it to the delta queue. The next time an import is called, the correct data will be passed to the sync engine, without making another potentially expensive call to the target system. <br />
Even if the target system doesn’t automatically provide you an updated copy of the resource, there’s nothing stopping you from getting the object yourself after export and constructing your import <a href="https://msdn.microsoft.com/en-us/library/hh858421">CSEntryChange</a>. After all, at this point in time, you know the resource has been changed – once the export operation is complete, you’ve lost that information. <br />
<br />
<h3>
Remember, its not a true delta</h3>
Both these scenarios can potentially save a call to the target system and each allows you to clear the pending export confirmation without having to do a full import. What you don’t get however, is changes made outside of FIM. These will still need to be obtained via a full import process. However, if your target system is only updated by the sync engine, then this process will work well. In a worst case scenario, you can have confirming imports immediately after an export, and run regular (perhaps less regular) full imports to obtain other changes.<br />
<br />
<h3>
Using the Lithnet.MetadirectoryServices.CSEntryChangeQueue</h3>
Let’s have a look at how to use the built-in <a href="https://lithnet.github.io/documentation/msu/html/C0CF58A4.htm">CSEntryChangeQueue</a> object to load and save deltas to a file after export. The following code shows a call-based ECMA 2 MA, that stores the delta information at export time in the queue. At the end of the export operation, the <a href="https://msdn.microsoft.com/en-us/library/hh858421">CSEntryChanges</a> in queue are saved to an XML file.<br />
<script src="https://gist.github.com/ryannewington/6f0f3f0d2bb976cdc79ebce4e5d9bb12.js"></script>
Upon import, we first check to see if we are doing a full or delta import. If a full import has been requested, we import directly from the source system. If a delta import is requested, we load the queue from the disk, and replay the <a href="https://msdn.microsoft.com/en-us/library/hh858421">CSEntryChanges</a> back to the sync engine. Once either a delta or full import operation is completed, we clear the queue, and save the empty list back to the disk.<br />
<br />
<h3>
Summary</h3>
While this pattern is not necessarily applicable when dealing with ‘<em>source’ </em>systems, it does have a place for ‘<em>target’</em> systems that are predominately managed by FIM. Even in systems that do have a lot of changes that aren't made by the sync engine, there is still a net gain. If you have 10,000 objects in your target, and are doing hourly full imports to get those changes, these methods allow you to supplement your full imports with fast, frequent confirming delta imports. Keep in mind the following points;<br />
<ol>
<li>It <em>doesn’t</em> negate the need to do full imports <br />
<li>It <em>does</em> negate the need to do a full import after export purely to confirm the last export run <br />
<li>If there are no changes made in the target system that FIM doesn’t care about, it’s as good as having delta support in the target system <br />
<li>You need to ensure that the ‘delta’ <a href="https://msdn.microsoft.com/en-us/library/hh858421">CSEntryChange</a> accurately reflects the state of the target system</li>
</li>
</li>
</li>
</ol>
<br />
Get the <a href="http://nuget.org/packages/Lithnet.MetadirectoryServices">nuget package today</a> and read the <a href="https://github.com/lithnet/mms-utilities">documentation</a> for full details.Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-85671409210933908112015-09-10T05:49:00.001+10:002016-06-23T16:04:54.081+10:00Take the guess work out of XPath with the Lithnet FIM Service PowerShell Module<h4>
Summary</h4>
The FIM Service allows you to query for resources using a subset of the XPath 2.0 dialect. It provides a quite powerful mechanism for searching for resources, but has more than a few curiosities when it comes to constructing queries for different attribute types.<br />
The <a href="https://github.com/lithnet/resourcemanagement-powershell/">Lithnet FIM Service PowerShell module</a> includes three cmdlets to help take the guess work out of constructing your XPath queries. <br />
<h5>
<a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/New-XPathQuery">New-XPathQuery</a></h5>
The <a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/New-XPathQuery">New-XPathQuery</a> cmdlet creates a predicate that forms part of an XPath expression. The query is the <strong>Attribute = ‘value’</strong> component of the expression<br />
<h5>
<a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/New-XPathQueryGroup">New-XPathQueryGroup</a></h5>
An XPath query group contains multiple XPath query objects, or other query groups, that are combined together with an ‘and’ or ‘or’ operator.<br />
<h5>
<a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/New-XPathExpression">New-XPathExpression</a></h5>
The XPath expression wraps the query or query group with the appropriate syntax defining the object type (eg <strong>/Person[query]</strong>)<br />
<h4>
Working with different attribute types</h4>
The cmdlets generate the correct syntax for each attribute type, without you have having to remember all the different ways the query needs to be expressed. <br />
For example, lets have a look at the way we have to test for presence for each different attribute type<br />
<table border="1" cellpadding="2" cellspacing="0" style="width: 765px;"> <tbody>
<tr> <td valign="top" width="203">String</td> <td valign="top" width="560">/Person[(starts-with(AccountName, '%'))]</td></tr>
<tr> <td valign="top" width="205">Integer</td> <td valign="top" width="560">/Person[(unixGid <= 999999999999999)]</td></tr>
<tr> <td valign="top" width="206">DateTime</td> <td valign="top" width="560">/Person[(ExpiryDate <= '9999-12-31T23:59:59.997')]</td></tr>
<tr> <td valign="top" width="207">Boolean</td> <td valign="top" width="560">/Person[((AccountDisabled = true) or (AccountDisabled = false))]</td></tr>
<tr> <td valign="top" width="207">Reference</td> <td valign="top" width="560">/Person[(Manager = /*)]</td></tr>
</tbody></table>
The New-XPathQuery cmdlet has a simple syntax, that is independent of the type of attribute. <br />
<script src="https://gist.github.com/ryannewington/5d23523bdcef0870424f1d8036c0172c.js"></script>
<br />
The cmdlet will automatically generate the syntax that is <a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/XPath%20expression%20examples">appropriate for the attribute specified</a>. It’s not just the <strong>IsPresent </strong>operator that is made simpler. The cmdlets support all the attribute types and operators that are supported by FIM. The underlying Lithnet RMC library used by the cmdlets has over 100 associated unit tests to ensure all combinations of operators and attributes generate the correct XPath syntax. <br />
<br />
<h3>
How do i use it?</h3>
<br />
<h4>
Simple query</h4>
The following example shows a simple query that checks for an AccountName of ‘ryan’ <br />
<script src="https://gist.github.com/ryannewington/1f1bf12eab4e6df75b0d8b1bd6598838.js"></script>
<br />
<h4>
Combining queries into a group</h4>
You can combine multiple queries together in a group using the <a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/new-xpathquerygroup">New-XPathQueryGroup</a> cmdlet. This allows you to join queries created by <a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/New-XPathQuery">New-XPathQuery</a> together with an <strong>And </strong>or <strong>Or</strong> operator. Searching for an AccountName of ‘bob’ or ‘ryan’ is shown in the example below. <br />
<script src="https://gist.github.com/ryannewington/80859893eabf1a32de17b72c8e91bc82.js"></script>
<br />
<h4>
Nested query groups</h4>
Query groups can contain child query groups as well. You can build complex nested expressions using multiple groups. The following example looks for all users who have a display name starting with ‘ryan’ or ‘bob’ that also have an email address <br />
<script src="https://gist.github.com/ryannewington/e3133e22aa729a56db589da2c23e97fa.js"></script>
<br />
<h4>
Nested expressions</h4>
When querying a reference attribute, you can use another expression as a query parameter. This allows you to build dereferencing expressions with ease. The following example searches for all people who have a manager with the AccountName ‘ryan’ <br />
<script src="https://gist.github.com/ryannewington/a33dd70c0d3b9150e4171b5f5398582b.js"></script>
<br />
<h3>
Dereferencing expressions</h3>
Creating a dereferencing expression is easy with the DereferenceAttribute parameter on the New-XPathExpression cmdlet. The following example gets the manager of the person with the AccountName ‘ryan’ <br />
<script src="https://gist.github.com/ryannewington/709c046f7e16e1c05cf876f9caff46e9.js"></script>
<br />
<h3>
Using the expression object</h3>
<br />
<h4>
Passing the expression to Search-Resources</h4>
Expression objects can be passed to other cmdlets such as <a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/Search-Resources">Search-Resources</a>. Rather than providing an XPath string to Search-Resources, you can simply pass the expression object. <br />
<script src="https://gist.github.com/ryannewington/89d2880865751a32ee03bf0359546648.js"></script>
<br />
<h4>
Filter syntax</h4>
You can also use the builder to create the Filter attribute used is sets and groups. The –WrapFilterXml parameter ensures that the <Filter> XML element is wrapped around your expression <br />
<script src="https://gist.github.com/ryannewington/67cbcbe046b9cae59cb76a5ef3377e90.js"></script>
<br />
<h4>
Setting Filter attributes directly</h4>
<br />
The library supports setting the value of a Filter attribute to an expression object directly. Ensure you set the –WrapFilterXml parameter on your expression.<br />
<br />
<script src="https://gist.github.com/ryannewington/d8a0da42e8ee1aa9613cb2c208f3eb74.js"></script>
<br />
<h3>
Further Reading</h3>
<br />
<ul><br />
<li>The <a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/Cmdlet%20Reference">cmdlet reference</a> documents the full set of features available in the PowerShell module. <br />
<li>The <a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/XPath%20expression%20examples">XPath Expression Examples</a> page shows all the possible resulting XPath expressions for each attribute type </li>
</li>
</ul>
<br />
<h3>
Feedback</h3>
<br />
If you have an idea for a new feature, contact me using one of the methods below<br />
<br />
Email: <a href="mailto:ryan@lithiumblue.com">ryan@lithiumblue.com</a> <br />
<br />
<a class="twitter-follow-button" data-show-count="false" href="https://twitter.com/RyanLNewington">Follow @RyanLNewington</a> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com2tag:blogger.com,1999:blog-1261292475025329861.post-2172510292154987842015-08-25T13:27:00.001+10:002016-06-23T16:06:20.171+10:00Cut down on your PowerShell code with the Lithnet FIM Service PowerShell module<p>The FIMAutomation PowerShell module requires you to write a lot of code to perform even the most basic tasks. Let’s have a look at the following example in which <a href="http://blog.msresource.net/2012/06/06/fim-powershell-and-datetime-type-attributes/">Paul Williams</a> provides some very well-written code for updating the EmployeeEndDate attribute of a user using the FIMAutomation snap-in.</p>
<script src="https://gist.github.com/ryannewington/dcbf92613353be5f8537eed31adb7789.js"></script>
<br/>
Now lets look at doing the same thing with the <a href="https://github.com/lithnet/resourcemanagement-powershell/">Lithnet FIM Service PowerShell module</a><br/>
<script src="https://gist.github.com/ryannewington/5e4bfc72d7855d4d934436267944bb76.js"></script>
<br />
The same task requires much less code, and much easier to understand. Perhaps most importantly, we don’t need to understand the inner workings of the FIM Service itself (import changes, put operations, etc) to do something as simple as updating an attribute value. Just get, set, and save.Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-21489032948907504392015-08-24T09:32:00.000+10:002016-06-23T16:09:30.419+10:00Version control your FIM Service configuration<p>Keeping track of your FIM Service configuration can seem like a daunting task. Even more so when you have multiple DEV, QA, and production instances that need to be kept consistent. We can make version controlling the FIM service a lot easier with some simple modifications to the schema, some clever scripts and a bit of process control. <p>This post will reference the <a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/Configuration%20Management">configuration management</a> capabilities of the <a href="https://github.com/lithnet/resourcemanagement-powershell/">Lithnet FIM Service PowerShell module</a>, but the same concepts can apply even when using your own tools. <ol> <li>Firstly, break up your FIM service design into components. A component is a collection of resources such as sets, MPRs and workflows that come together to perform a particular function. For example, the self-service password reset functionality can be grouped together as an SSPR component. You might have a group of workflows, sets and email templates that handle expiry notifications. I generally use the following components as a starting point; <ul> <li>User Interface (RCDCs, Nav bar links, etc) <li>Email notifications (welcome emails, expiry notifications, etc) <li>Schema (attributes, bindings, resource types) <li>Security model (permissions) <li>SSPR </li></ul> <li>Create a custom resource in the FIM service for tracking these components and their versions. Create a <i>changeLogEntry</i> resource with a binding for a new <i>version</i> and details <i>attribute</i>. You can use the <a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/Import-RMConfig">Import-RMConfig cmdlet</a> to make these schema modifications for you. Save the following XML into a file, and apply the configuration changes with Import-RMConfig <br />
<script src="https://gist.github.com/ryannewington/5feebf6c7a0a3569d51399f4c05aeb26.js"></script>
<br/>
<li>Each component should have its own design document. The component design document defines the configuration of all the objects that make up that component. The document itself should be version controlled, and is the authoritative source of both the version and configuration of the component. <br /><li>Translate these documents into a set of scripts that can create and update the components. Each document should have its own script, and the script should be written to allow them to be run repeatedly, supporting both the creation of the necessary resources, and updating any existing objects to the documented configuration. Once again, you can use the <a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/Import-RMConfig">Import-RMConfig cmdlet</a> of the <a href="https://github.com/lithnet/resourcemanagement-powershell/">Lithnet FIM Service PowerShell Module</a> to do this for you automatically. Each script should create or modify the changeLogEntry for that component to reflect the version in the component design document. The following XML demonstrates creating an email notification component, and updates the change log automatically as part of that process. <br />
<script src="https://gist.github.com/ryannewington/50857c6a2766081662149781214b93c1.js"></script>
<br /><li>If you have differences in parameters between your development, QA, and production configurations, make use of the <a href="https://github.com/lithnet/resourcemanagement-powershell/wiki/Variables%20File">variables file</a> that the ConfigSync file provides as part of the <font face="Consolas"><Variables import-file=""></font> attribute. Extract the parameters into a separate variables file for each of your environments. Each variables file should be saved and managed independently, you never want to have to change the file itself whenever you move between versions. When importing the configuration, copy the appropriate environment-specific variables file into the folder where the config xml is stored, and rename it <i>environment-variables.xml</i> (or the name you have chosen to use in your main xml file). As an example, you might have a QA and a production variables file as shown below. When used with the example file above, the <em>#env#</em> placeholder is substituted with either <em>QA</em> or <em>PROD</em>, depending on the file that is used.<br>
<script src="https://gist.github.com/ryannewington/e0834a8691868863e966f71dfbf0e384.js"></script>
<script src="https://gist.github.com/ryannewington/a0b9fd8aa4e6522eab7a7e75df44a11d.js"></script><br/>
<br /><li>Use a source control system to store your component design documents and scripts. You can get a free <a href="https://www.visualstudio.com/en-us/products/what-is-visual-studio-online-vs.aspx">Visual Studio online</a> account from Microsoft, or if you have an existing system such as GIT, TFS, or SVN service, you can use that. <br /><li>When you have a tested, working set of components, that you are ready to deploy, bundle them together and create a release. Create a release document that details each component and its version, as well as the changes made since the last version. Move this release through the development, QA, and prod environments as a bundle. If you are using a source control system, branch your source control tree for each release. That way, you have a permanent, point-in-time copy of what each release looked like. </li></ol><br /><h3>In summary</h3><br /><ol><br /><li>Don’t try and version control the whole FIM service configuration. Break it down into smaller components, and release them in defined bundles <br /><li>Use the FIM Service to keep track of its own component versions by creating a custom change log resource <br /><li>Your design documents are authoritative. Ensure the appropriate controls are in place to make sure documents are kept up to date, and accurate reflect your components <br /><li>Ensure your scripts can be run repeatedly, only making changes where needed. (And remember Import-RMConfig does this out-of-the-box) <br /><li>Never modify your scripts as part of a deployment. Make use of variable files to apply per-environment settings <br /><li>Use a source control system to give you a complete version and release history </li></ol>Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0tag:blogger.com,1999:blog-1261292475025329861.post-64582280270450165802012-01-29T22:09:00.001+11:002016-07-11T07:58:53.009+10:00Lithnet.IdleLogoff – Log off users after periods of inactivity (with group policy support)At the University I work for, we recently had an opportunity to redesign our student lab workstation environment from scratch. One of the seemingly simple requirements we had was to ensure that after a certain period of inactivity, users were logged off the machines. Sounds simple right?<br />
Microsoft have a <a href="http://support.microsoft.com/kb/314999">KB article</a> that suggests a method to do this, but it’s not the best solution. It uses a screen saver as the timing mechanism, and starts a count-down timer in the background. If the user returns to the computer, they need to click a ‘cancel’ button that appears to stop them from being booted out. Not a very good user experience. <br />
We couldn't find anything that did what we wanted. Something that would sit in the background, unobtrusively, and just log a user out after a predetermined amount of time. Oh, and it would be nice to control that amount of time if needed rather easily. Oh, and it would also be nice to disable the auto-logout completely if needed. And if its not asking too much, we want to be able to manage all this centrally.<br />
So putting the screen saver idea aside, it sounded like it was time to develop a small app to do what we needed to. Lithnet.IdleLogoff was born…<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRUtvEAMzJd6M4QYtPZ_EMNj4FTi0bDhZUyMW7A2B66r8kd1u-HAcAQghOBAbwfUQay63aaHuEzmphhCUzPa_U6mPubTnGPoeBnOXdxw5PDoejqJNJl6K4LS-qiZ6Tur44vukQM5KB4xhQ/s1600-h/image%25255B9%25255D.png"><img alt="image" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0at_UChi1eJnn4boO_VTYRQvjv7YZCIpLtKBlouHUXFby6JqDKbid7phtU9M8ClKjZpmR2IIlUltcLueSaRkZseyX8IEW9MppVSPojn6qJz6MM9o3CkNbTsEtpKBeCQXP94eGr4WuSi6_/?imgmax=800" height="196" style="background-image: none; border-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="378" /></a><br />
As you can see, it is a really simple app, with only a few options for either enabling or disabling the agent and then setting the idle period. The app simply queries the relevant Windows API for the time since the user last interacted with the computer, and calls the logoff function after the specified period has elapsed. The power of this application comes from the fact you can either configure it locally, or manage it centrally via group policy.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8IHhJRCTcL0nOj0-gQR9fk3NeEakQ7HZOZ64qhI7ZyHWF6Tw4br6lNl6_I3oqvHW4dCZpliunTEbK3aPEJuyyMU2eBpKjy5IgGG6ykS9hrU_EnwIgrZLShcJ5-eILkGpSFW82YZO-2ARq/s1600-h/image%25255B13%25255D.png"><img alt="image" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZIxcA_63euKxoYLmImoGVnaQj082HtrabgMCCIbCnPVRcwBtNFI2ViQnospjCtjC7LNLSKc-d0eOqDgPhJrfynwssmmRL_LeVAlcEo_aG_hJzuFjAG_-gy743Lb1JsBFw_MRFgjqIDfB2/?imgmax=800" height="310" style="background-image: none; border-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="532" /></a><br />
The ADMX files are included in the installer. If you enable the setting, then the agent will be activated and log users off at the time you specify. If you disable the setting, then the agent will be disabled and will not log users off automatically. If you leave it as ‘not configured’, then whatever the local administrator of the PC has manually configured will take effect. Group policy will always override whatever you set locally.<br />
To get started with the tool, install it and navigate to %ProgramFiles%\Lithnet\Lithnet.IdleLogoff, and run lithnet.idlelogoff.exe. This will launch the GUI to allow you to enable the agent, and configure the idle timeout. Alternatively, if you are configuring via group policy, then no further action is needed. Log off the workstation, and the next user to login will be subject to your idle logoff policy.<br />
That’s it! No screen savers, message boxes, countdowns, beeps or other annoyances. Unobtrusive, simple, and centrally managed – my three requirements for anything that interacts with our managed desktops.<br />
<br />
<a href="https://github.com/lithnet/idle-logoff/releases/" onclick="_gaq.push(['_trackEvent', 'Tools', 'Downloaded', 'Lithnet.IdleLogoff.zip']);">Download the latest version</a><br />
<h2>
Change log</h2>
<table border="1" cellpadding="2" cellspacing="0" style="width: 400px;"> <tbody>
<tr> <td valign="top" width="199">Date</td> <td valign="top" width="199">Version Details</td></tr>
<tr> <td valign="top" width="199">29/01/2012</td> <td valign="top" width="199">1.0.4411 Initial release</td></tr>
<tr> <td valign="top" width="199">25/11/2014</td> <td valign="top" width="199">1.0.5442 Updated to provide support for user-based GPO settings</td></tr>
<tr> <td valign="top" width="199">11/07/2016</td> <td valign="top" width="199">1.1.6016 New combined installer for application and GPO extensions and built on .NET Framework 4.5.2</td></tr>
</tbody></table>
Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com32tag:blogger.com,1999:blog-1261292475025329861.post-38808663269466062092012-01-29T20:34:00.000+11:002017-03-13T19:15:58.989+11:00Lithnet.MoveUser - replacement for Microsoft's moveuser.exe and Win32_UserProfile.ChangeOwner<br />
Lithnet.MoveUser is a command line tool that can be used to change the owner of a profile from one user to another. It is designed to be a replacement for Microsoft's moveuser.exe tool (used for Windows XP), originally included in the Windows Resource Kit, and the Win32_UserProfile.ChangeOwner WMI method, used for Windows Vista and above. <br /><br />The Lithnet.MoveUser tool provides the same functionality as the other tools, but <a href="http://blog.lithiumblue.com/2011/11/building-enterprise-ready-replacement.html">overcomes some of the shortcomings of the Microsoft provided toolsets</a>. It does not require any scripting knowledge, provides a consistent experience across Windows XP, Vista, and Windows 7, and provides detailed logging of progress and any errors encountered.<br /><br />The tool will perform the following tasks<br />
<ol>
<li>Change the owner of the profile to the destination user, and update associated permissions </li>
<li>Add the destination user to the same local groups that the source user was a member of </li>
<li>If the source account is a local account, then it can either be deleted, disabled, or left as-is after a successful migration. By default it is deleted </li>
<li>The source and destination usernames can either be provided in standard username format (domain\username, computer\username) or as a SID </li>
<li>The tool can also scan areas outside of a users profile for permissions assigned to the source user, and update them to apply to the destination user instead.</li>
</ol>
<span style="font-family: "courier new" , "courier" , monospace;"></span>Ensure you have at least Microsoft .NET framework 3.5 installed, then download the tool and run the following command for help<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">lithnet.moveuser.exe /?</span><br />
<br />
<br />
<div>
<span style="font-family: inherit;">Examples:</span></div>
<div>
<span style="font-family: inherit;">The following examples take the profile for the local account for 'bob' and migrate it to bob's account in the lithnet domain</span></div>
<div>
</div>
<span style="font-family: "courier new" , "courier" , monospace;">lithnet.moveuser.exe .\bob lithnet\bob</span><br />
<div>
<span style="font-family: "courier new" , "courier" , monospace;">lithnet.moveuser.exe bob lithnet\bob</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">lithnet.moveuser.exe BOBSPC\bob lithnet\bob</span></div>
<br />
<span style="font-family: inherit;">The following example migrates the local profile for 'jane' and migrates it to the SID of jane's domain account (the workstation may not yet be joined to the new domain)</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">lithnet.moveuser.exe .\jane S-1-5-21-2656768339-1635643127-14959812366-1038</span><br />
<br />
<span style="font-family: inherit;">Other examples</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">lithnet.moveuser.exe .\dave lithnet\davidm /replace</span><br />
<div>
<span style="font-family: "courier new" , "courier" , monospace;">lithnet.moveuser.exe john lithnet\johnsmith /postmoveaction:keep</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">lithnet.moveuser.exe S-1-5-21-2656768339-1635643127-14959812366-3442 S-1-5-21-2</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">56768339-1635643127-14959812366-1030</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">lithnet.moveuser.exe otherdomain\john lithnet\john</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">lithnet.moveuser.exe lithnet\john .\john</span></div>
<h5>
<a href="https://github.com/lithnet/moveuser" onclick="_gaq.push(['_trackEvent', 'Tools', 'Downloaded', 'Lithnet.moveuser.zip'])">Download Latest Version</a></h5>
<h2>
Change log</h2>
<table border="1" cellpadding="2" cellspacing="0" style="width: 400px;"> <tbody>
<tr> <td valign="top" width="141">Date</td> <td valign="top" width="134">Version</td> <td valign="top" width="123">Details</td></tr>
<tr> <td valign="top" width="141">29/1/2012</td> <td valign="top" width="147">1.0.4411</td> <td valign="top" width="154">Initial release</td></tr>
</tbody></table>
Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com1tag:blogger.com,1999:blog-1261292475025329861.post-20879822894014139592012-01-29T20:32:00.000+11:002012-01-29T20:32:24.558+11:00Building an enterprise-ready replacement for MoveUser.exe and Win32_UserProfile.ChangeOwner<h2>Moveuser.exe</h2> <p>Back in the early days of Windows XP, Microsoft released a tool for changing the owner of a user profile from one user to another. Moveuser.exe came in the Windows Resource Kit, and for the most part did an OK job.<br><br>The most common use was for taking profiles that belonged to local user accounts, and attaching them to domain user accounts. It could also be used for migrating profiles from one domain to another, although the Active Directory Migration Tool (ADMT) was more commonly used for this task.<br><br>The command line was simple enough;<br><br><span style="font-family: 'Courier New', courier, monospace"><strong>moveuser.exe oldusername newusername</strong></span><br><br>It took usernames in format of <em>COMPUTER\username</em> or <em>DOMAIN\username</em>. Once of the other nice things about moveuser.exe is that it also copied a user’s group membership on the local machine.<br><br>Moveuser wasn't without its problems;<br></p> <ol> <li>Error messages returned by the tool were generally unhelpful. A single Win32 error code can't tell you a lot about why a profile migration might have failed. <li>Moveuser.exe was limited to using account names that the local PC could resolve. This was problematic when trying to move a user profile from one domain user to another, without having a trust relationship between the two domains.</li></ol> <p>However, support for moveuser.exe was dropped in Windows Vista, and replaced by a WMI method on the Win32_UserProfile class called <em>'ChangeOwner'</em>. <br></p> <h2>Win32_UserProfile.ChangeOwner</h2> <p><br>ChangeOwner was a little trickier to use. Firstly, you have to delve into the world of WMI and scripting. While not too difficult, it requires knowledge of WMI and an appropriate scripting language such as VBScript. <br><br>Secondly, the method requires you to pass it the SID of the user accounts involved in the migration. You need to be able to translate your account names in advance into the user's corresponding SIDs. This adds quite a bit more scripting complexity. However, the benefit of this approach, is that if you know the SIDs in advance, the local computer does not need to know about the source or destination username at all.<br><br>However, the ChangeOwner function dropped support for copying the user's local group membership, so once again, we are having to do more scripting to perform this function. <br><br>Then, as mentioned previously, Microsoft have the ADMT tool, that can be used to perform profile migrations within domains. However, there is no support for migrating workgroup-based computers and local accounts.<br><br>So the situation we are left with, is having three completely different tools, each with their own capabilities and caveats, to use across different operating system generations.<br></p> <h2>ADWMT was born</h2> <p>At the organisation I work for, we just moved 16,000 users from a Novell-based workgroup to an Active Directory domain. I needed a solution that I could reliably push out to all supported operating systems, join all workgroup-based computers to the AD, as well as consolidate 9 other AD domains into a new central domain. None of these tools were going to cut it! We had to come up with our own solution.<br><br>After several months of solid development and testing, we had a tool that would reliably migrate the whole fleet of workstations, without user interaction, and provide us with detailed management reporting. The Active Directory Workstation Migration Tool (ADWMT) was born. It was capable of;<br></p> <ol> <li>Joining workstations to the domain <li>Automatically mapping local user accounts to user accounts in the domain <li>Automatically mapping user accounts in one domain to user accounts in another domain <li>Migrating local user profiles to domain accounts <li>Migrating profiles from one domain to accounts in another domain <li>Keeping local group membership in tact <li>Working without any user interaction <li>Providing detailed reporting <li>Running on Windows XP, Vista and 7 <li>Performing migrations over a wired or wireless network <li>Auto-repairing network configuration issues (especially where no lanmanserver or lanmanworkstation installed) <li>Running custom scripts at any stage throughout the migration <li>Being able to be run by a user without admin rights, and auto-elevate using a predefined list of admin accounts</li></ol> <p>If you are interested in using the ADWMT tool for use in your organisation, send me an email to discuss licensing options.<br></p> <h2>Lithnet.MoveUser</h2> <p>I decided to extract the profile migration engine from ADWMT, and make it available for anyone to use. It is designed as a replacement for moveuser.exe and ChangeOwner, combining the benefits of both tools, with some additional enhancements.</p> <ol> <li>A single command line tool, that takes either usernames (as moveuser.exe does), or SIDs (as ChangeOwner does) <li>Runs on XP, Vista, and Windows 7 <li>Provides detailed logging and reporting</li></ol>The tool requires .NET framework 3.5 to be installed. See <a href="http://blog.lithiumblue.com/2011/11/lithnetmoveuser-replacement-for.html">this post for full details</a> including a download link. Ryan Newingtonhttp://www.blogger.com/profile/12272463417723961465noreply@blogger.com0