Monday, September 23, 2013

Adhoc LDAP information requests

Many times in the life of an Active Directory administrator, you will get various business people or application guys coming to you to pull a lot of information from the directory, or to add on to information they already have. Its great in these instances to have some basic powershell skills to make what looks like a lot of work, turn into a few minute task with some adhoc scripting. In this case, I'll skip the AD commandlets and Quest AD commandlets that many people commonly use, and I'll give you a basic sample of an easy request and how one may go about working on it.


Scenario:


Here is an excel file that has 5000 rows containing Full Name, Logon name (a really helpful people making the request), email address, and a blank column to fill in (Current status: enabled/disabled)


First of all, to make things easy, we want to take the Excel file and convert it to CSV with a save-as. We want to keep column headers in the first row to show what the various attributes will be. And to help with further processing, you may want to take out the spaces and any special characters that powershell may not like as part of an attribute name. Once you have that all together, save your csv file in a location where you want to run the script:


$entries = import-csv .\userlist.csv
$de = [System.DirectoryServices.ActiveDirectory.Domain]::getcurrentdomain().getdirectoryentry()
$ds = new-object directoryservices.directorysearcher($de)
$ds.propertiestoload.add("useraccountcontrol")|out-null

foreach ($entry in $entries) {
 $ds.filter = "(&(objectclass=user)(objectcategory=user)(|(samaccountname=$($entry.samaccountname))(mail=$($entry.email))))"
 $DSsearchresult = $ds.findone()
 if ($DSsearchresult -eq $null) {
  $entry.status = "NotFound"
 } else {
  if ($DSsearchresult.properties.useraccountcontrol[0] -band 2) {
   $entry.status = "Disabled"
  } else {
   $entry.status = "Active"
  }
 }
}

$entries|convertto-csv -notypeinformation |out-file results.csv


To walk through this a bit, the first line is importing the CSV file into an array of objects. Each object will have attributes for all of the defined columns in the CSV file's first row...including any blanks. See why its important to have column headers?


Secondly, we want to set up something to search the directory. Here I am just using .NET classes. The DirectoryServices.DirectorySearcher will do the main work. To keep the results small, we use the propertiestoload method to restrict the results to a single attribute, in this case we want to see if the account is enabled so we use UserAccountControl.


Next, the all important loop. Whenever processing large amounts of data, we will end up in a loop, even if its hidden as a pipeline, basically the end result is the same, though loops may be a bit more controllable and readable. Inside I'm defining the ldap search filter. We want these to be restrictive as possible to minimize results and for faster lookup times. Here I'm doing an OR on samaccountname and email just in case the information given wasn't 100% perfect. This will be likely to find users with a better percentage change of success. As we are referencing attribute properties of $entry, we wrap the whole object in a $() to process what is inside first. Then we continue to do a search for a single result (samaccountname should be unique here, so we get one result). If there is no result, we can put a status into that object stating it is not there. Otherwise we view the useraccountcontrol against 0x2 (disabled flag) to see if its active or not. Notice the [0] here. When working with directoryservices results, the properties are usually collections, so treat it like a single value array in most cases (even if the attribute isn't a multivalued one).


Now that we are done with our loop, our whole intial import into the variable $entries will have their Status attributes populated. So we can just dump that back out into CSV format, bring it back into excel and save it in xslx format to sent back to the requestor.


As requests vary a lot, the complexity can grow and further processing and error handling may be required. This is just a good starting point to see how to turn 5000 lookup's into a one minute script. In other cases, other commandlets may be useful for you, or you could take values to write out a large batch file full of dsmod/dsget type commands. Whatever works best and whatever seems the most efficient for you.

Wednesday, September 4, 2013

IE compatibility modes

Hello,

Let me start by saying, I'm no expert in web applications and browsers, however I have run into HTML compatibility issues on and off for many years.  For those that were developing sites way back in the day when the internet was just starting to be something that people were adopting in the home, you may remember having to write javascript to detect the browser version to see what little nuances were going to work for Netscape and what would work for Internet Explorer.  Now that browser technology is even more all-over-the-place with additional players in the browser field, and a wide range of versions for each.  With IE specifically, you may see anything from IE7 to the latest...if not even older.  Later versions of IE (I believe starting with IE8) have various options in them for compatibility.  Certain websites may fail to work correctly in one version of IE, while they work fine in another.  Newer versions of the browser have the F12 debugging tools, which makes it easy to test sites with.  Whenever you have IE open, you can hit the F12 key and you will get a popup window with various tools for website debugging.  Along the menu bar, the last two items on the right site are browser mode and document mode.  In each, you can see a list of versions that you want to display with.

IE F12 debugger tools



Typically your pages will load at whatever settings you had it at the last time you set it.  However there are some places that can override these.

#1 Group policy.
In both user and computer settings -> Administrative Templates -> Windows Components -> Internet Explorer -> Compatibility View, you will see a list of settings


In this list we can see various options.  Some allow you to provide a list of domain names.  So if you put: contoso.com in there, anything in the whole contoso.com namespace (ie: www.contoso.com, myapp.contoso.com) will all be effected.  Looking through the details will help you decide what may be needed.  The last two options allow lists of sites to be defined.  These settings will effect the Document Mode option in the compatibility functions.  Changing it to IE7 will break HTML5 and newer technologies not supported.

#2 Controlled by the site via META Tags
The X-UA-Compatible meta tag is something you can define in your application. In this article, you can view the allowed values. From my basic testing with this, using the Emulate values seems to force the browser to use that mode. While the others seem to be guidelines which may be ignored if higher version level functionality is implemented in the site. In my case, I threw a canvas tag into a basic website with !DOCTYPE html. When playing with the numbers, it displays with IE9 mode, and with emulate at lower versions, my Canvas disappeared. One interesting thing to note here is that the X-UA-compatible tag seems to override whatever group policy you have in place. That way if you have contoso.com set for one level, but one outlying site mynewapp.contoso.com needs HTML5 support, you can override it from the application itself.

example of tag:
<meta http-equiv="X-UA-Compatible" content="IE=8">

NOTE: This tag needs to be the first tag in the HEAD block

In any case, whenever working with web site problems, its always good to check different version levels to see if a site supports all the variations of browsers that may be in the environment, and make adjustments accordingly using whatever method is best.  Also play around with the F12 tools as they can be quite useful.  Similar tools exist in Chrome, and as extensions in firefox.


Tuesday, September 3, 2013

FIM Certificate manager portal "Value does not fall within the expected range"

Recently I ran into an unusual issue with a FIM certificate manager portal installation occasionally throwing the "Value does not fall within the expected range" error when doing searches.  Some searches would work all of the time, while others would fail all of the time.


The error:

1) Exception Information
*********************************************
Exception Type: System.ArgumentException
Message: Value does not fall within the expected range.
ParamName: NULL
Data: System.Collections.ListDictionaryInternal
TargetSite: Int32 SecurityDescriptorToBinarySD(Microsoft.Clm.Security.Structs.VariantIDispatch, IntPtr ByRef, UInt32 ByRef, System.String, System.String, System.String, UInt32)
HelpLink: NULL
Source: Microsoft.Clm.Security.Authorization

StackTrace Information
*********************************************
   at Microsoft.Clm.Security.NativeMethods.SecurityDescriptorToBinarySD(VariantIDispatch vVarSecDes, IntPtr& ppSecurityDescriptor, UInt32& pdwSDLength, String pszServerName, String userName, String passWord, UInt32 dwFlags)
   at Microsoft.Clm.Security.Authorization.SecurityDescriptor.ConvertToByteArray(DirectoryEntry entry)



After digging through various components, checking AD, etc, a pattern seemed to emerge.  Whenever the search result should have returned results of users that were in a specific OU, it would fail.  While results that gave only results in other OU's would work.  On checking the metadata for the OU, the ntSecurityDescriptor had recently changed right around the time that the FIM CM portal started throwing errors.  A large number of property management ACE's had been added, which pushed the size of the ACL too high for the system to deal with.  According to Microsoft, the max size for an ACL is 64k.  My previous post shows how easy it can be to hit that limit when you get too fine grained in your entries.  Removing the added ACE's resolved the issue.

What not to do with access control lists on Active Directory objects

Although Active Directory can give a very fine level of control over properties of objects, its best to perform a bit of planning before making changes.  Some ACL entry changes can give a lot of access, while adding very little to an Access control list, while other property specific changes can make a huge size difference on your access control list.

For those not familiar with access controls, basically all objects in active directory have an attribute on them which specifies the access to the object.  This can be referred to as an Access Control List (ACL), security descriptor (SD, SDDL), or object security.  Within the ACL, there are entries which may be referred to as Access Rules, or Access Control Entries (ACE).  You will see different terminologies in different tools and .NET classes that manipulate the information.  There are also different formats that the rules can be read in.  Typically everyone is familiar with the security tab in Active Directory Users and Computers (available in advanced view).  In the advanced mode of this, you have a better view of the access control entries.  The ACE's themselves contain information such as:

ActiveDirectoryRights :  ReadProperty, WriteProperty
InheritanceType          :  None
ObjectType                :  e45795b2-9455-11d1-aebd-0000f80367c1
InheritedObjectType     : 00000000-0000-0000-0000-000000000000
ObjectFlags                : ObjectAceTypePresent
AccessControlType     : Allow
IdentityReference         : S-1-5-10
IsInherited                   : False
InheritanceFlags           : None
PropagationFlags         : None

You can find more about decoding these in my previous post which provides a script for this.  In the background though, you have uglier formats to deal with like:

SDDL language   O:DAG:DAD:AI(A;;LCRPLORC;;;PS)
Binary: hex code

If you look at technet on Security Descriptors, the maximum size of an ACE is 64k or roughly 1820 entries.  That's quite a few, but its not too hard to shoot yourself in the foot with this.  For example, you want to give someone access to almost every property of an object, but then you decided that there is one or two specific properties you don't want them to have.  So you may start with giving "read all properties" and "write all properties" rights to the account.  Then you go back into advanced view and uncheck a few properties.  This removes the previous few entries for read/write all, and expands it into hundreds of ACE's for each specific property.  We can see here how this affects the size.  I created a directoryservices.directoryentry object pointing to a computer object

PS> $de.psbase.objectsecurity.getsecuritydescriptorbinaryform()|measure-object
Count    : 11112

Here we see how many bytes are in the ACL.  Now if I go and do what I just described to the ACL

PS> $de.psbase.objectsecurity.getsecuritydescriptorbinaryform()|measure-object
Count    : 45692

The size has quickly exploded to a value that is edging towards the maximum size.  When we hit the max size, we may end up with various failures in different places, with perhaps some very vague errors as to what the real problem is.  The functions that manage the ACL and do conversions may be limited to a length value of 64K, causing exceptions to be thrown when they are processed.

If you really need to do something like this, what you should do is grant the broad level of access and then create a few separate deny permission entries for the few properties that they shouldn't have access to.