Inconspicuous SharePoint Quirks #4

Mystery of the Identity Impersonator

This one is not exactly a SharePoint quirk, but a SharePoint app led me to it. Which means its an IIS quirk. Or a .Net quirk. Or a Service Pack quirk. Or a Windows Server 2008 quirk. Or a Microsoft quirk. Or whatever…

My co-worker had written a form-based-authentication login page for a SharePoint application. The users are created in Active Directory and the usual frills of user account – login, security questions, change password, reset password et al.

The app worked fine in dev environment, was working fine in Stage but one fine Monday, broke in Stage. The user was created in the AD, but was set in a disabled state and returned an error back to the application. It was working in Dev fine though. The web.config, wsp solution, content databases are all exactly same between dev and stage. The only difference was the stage environment had Windows Server 2008 R2 SP1, while the Dev was not. We decided to backout SP1 from stage and lo, it started working again. Could it be the sheer gravitons of SP1 cause to distort the user creation in the Active Directory?

Since SP1 was causing the problem, we got the dev environment to the same SP1 patch level as stage. And yes, the user creation broke. Repeatable bugs are like flights crashing without casualties. Yeah, it crashed, but no one died. I started looking at the ULS and noticed there were no log statements. Coming from the Java world, I rely more on debug statements rather than run-time debugging. A friend of mine reminds me that in the .Net world programmers first put the solution in Debug mode, then start writing the code. But here the log statements were there in the code, yet were not written.

You can spot the issue here:

public static class ExceptionExtension {
public static string Unroll(this Exception ex) {
if (ex == null) return "";
string exStr = ex.Message + "\n" + ex.StackTrace;

if (ex.InnerException != null) {
exStr += "\n" + ex.InnerException.Unroll();
}

return exStr;
}

Test Usage:
PortalLog.LogString("ERROR: {0} || [{1}]", errorMessage, ExceptionExtension.Unroll(ex));

The above code wont work, because the correct usage of the extension is ex.Unroll(), not by a static method call. Once I fixed this, I could see the actual errors in ULS.

Here is how the account was being created usig LDAP:

entry = new DirectoryEntry(ldapPath, ldapUser, ldapPassword, AuthenticationTypes.Secure);
//create new user object and write into AD
user = entry.Children.Add("CN=" + samAccountName, "user");
user.Properties["userprincipalname"].Add(emailAddress);
user.Properties["samaccountname"].Add(samAccountName);
//...
user.CommitChanges();
user.Invoke("SetPassword", password);
user.Invoke("Put", new object[] { "userAccountControl", "512" });

System.DirectoryServices threw exception with an error code 0x80070005 (ACCESS_DENIED) while invoking SetPassword. I double checked that the ldapUser has identical permissions in dev and stage. How can the user be created but still access denied? More importantly, WHO is being denied? Certainly not the ldapUser.

I added the debug for WindowsIdentity.GetCurrent().GetName(). And it returned NT AUTHORITY\IUSR. In IIS, the anonymous access is enabled and in the web.config the identity tag is

<identity impersonator="true"/>

Obviously IUSR does not have permission to set the password. But why was it working in dev, before SP1? When I tried in another environment without SP1, the current Windows Identity was the same as the Application Pool user for that SharePoint Web Application and not the IUSR. Google-o! Er.. I mean, Bing-o!

Back to dev with SP1, I set the impersonator to false, and now I saw the current identity as the Application Pool user! W2k8 R2 without SP1 was not respecting the identity impersonator flag. Obviously without SP1, the IUSR anonymous user was having access to create an user in active directory, while SP1 prevented it. The impersonation is not required when you are creating user using the ldap service user and password, who already have access.

To me it looks like a security hole (pre-SP1). I could not find any online documentation or release notes mentioning about this. If you know of such documentation, please drop a comment!

Advertisements

Inconspicuous SharePoint Quirks #3

Mystery of the missing Content Type column

Is there something called “Over-architecting a solution”? Or do some people get carried away too much by Object Oriented Concepts that a simple if-else condition is enshrined into a dazzling hierarchy of classes, interfaces and er.. content types? SharePoint’s answer to creating a class/type or pseudo-interface is the Content Type. While a powerful feature, there are times even the Microsoft Team gets confused what to do with it, unconsciously bury a feature and consciously let it go undocumented.

There was a requirement to show different banners with different styles based on items in the “Banner List”. The different banner types were represented as “Content Types” and so the end user will create Banner Type 1, Banner Type 2 etc; set it to active and it would appear on the web page. The solution was designed in such a way that a calculated column in that list was populated based on the content type of the banner. That is, the calculated column had a formula in the BannerText sitecolumn:


=IF [Content Type] == "Banner Type 1" Then BannerText = "Red Banner" ELSE IF [Content Type] == "Banner Type 2" Then BannerText = "Blue Banner" ELSE IF...

The site column was relying on the Content Type of the list, that will be known at runtime. And that was working perfectly fine. In SharePoint 2007, that is.

Over to 2010. When I was trying to add a new item in that list, the calculated column Text would populate with a value “#NAME?” indicating that there is something wrong with the formula. Going back the site column, initially said the formula is fine, but when I tried to update it, errored out saying invalid column. Upon inspection, I see that Content Type is missing in the “Insert Columns” list.

First I thought my migration was wrong. Looked back at the SharePoint 2007 implementation, the “Content Type” column is there (see above image from MOSS 2007)! So Microsoft Team couldn’t handle the power the Content Types and removed it, I guess. But why was it removed? (see image below, from SharePoint 2010)

The _layouts/fldnew.aspx page inherits from Microsoft.SharePoint.ApplicationPages.BasicFieldEditPage. The method that creates the Insert Columns is WriteFieldNamesForFormulaColumnPicker(), so I loaded the .Net Reflector and inspected the code.

protected internal static void WriteFieldNamesForFormulaColumnPicker(TextWriter output, SPFieldCollection fields, string currentDisplayName)
 {
 SortedList list = new SortedList(new Comparer(fields.Web.Locale), fields.Count);
 foreach (SPField field in fields)
 {
 if ((((!(field is SPFieldText) && !(field is SPFieldCalculated)) && (!(field is SPFieldNumber) && !(field is SPFieldDateTime))) && (!(field is SPFieldChoice) || (field is SPFieldWorkflowStatus))) && (!(field is SPFieldBoolean) || (field is SPFieldAllDayEvent)))
 {
 continue;
 }
 if (((field.InternalName != "_UIVersionString") && !field.Hidden) && (field.Title != currentDisplayName))
 {
 list.Add(field.Title + "_" + field.InternalName, field);
 }
 }
 bool flag = true;
 foreach (SPField field2 in list.Values)
 {
 string title = field2.Title;
 SPHttpUtility.NoEncode("<option value=\"[", output);
 SPHttpUtility.HtmlEncode(title, output);
 SPHttpUtility.NoEncode("]\"", output);
 if (flag)
 {
 SPHttpUtility.NoEncode(" selected=\"selected\"", output);
 flag = false;
 }
 SPHttpUtility.NoEncode(">", output);
 SPHttpUtility.HtmlEncode((title.Length > 20) ? (title.Substring(0, 20) + "...") : title, output);
 SPHttpUtility.NoEncode("</option>", output);
 }
 }

So if the field is only one of text, calculated, number, datetime, choice, workflowstatus, boolean it is available for formulas. So what type is the Content Type column anyway? Using Powershell, we can determine that —

PS C:\Users\spfarmadmin> $w.Fields["Content Type"]

EnableLookup                : False
FieldRenderingControl       : Microsoft.SharePoint.WebControls.ComputedField
FieldRenderingMobileControl : Microsoft.SharePoint.MobileControls.SPMobileComputedField
FieldTypeDefinition         : Microsoft.SharePoint.SPFieldTypeDefinition
TypeDisplayName             : Computed

So since the field is Computed type, it does not appear in the insert column list. The same code in SharePoint 2007 didn’t have this restriction. So there is no way to find content type from within a site column. So I had to redesign the list now. Instead of multiple content types, I just added a new dropdown column which contain a list of values which was exact same as the content type. That way the functionality still worked without changing the underlying custom code. So a list with several content types and calculated columns to determine a text could be replaced with a simple dropdown list! Instead of multiple content types and formulas.

But a fundamental feature was removed. May be the next release of SharePoint 2014 will not have content databases?