Ninja Nichols

The discipline of programming

Passing ADFS Claims to Shibboleth

According to the official Shibboleth documentation (https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPADFS#NativeSPADFS-AttributeHandling) ingesting claims from ADFS into Shibboleth requires adding the following mapping rules to /etc/shibboleth/attribute-map.xml:

<!-- WS-Fed attributes -->
<Attribute nameFormat="http://schemas.xmlsoap.org/claims" name="CommonName" id="cn"/>
<Attribute nameFormat="http://schemas.xmlsoap.org/claims" name="EmailAddress" id="email"/>
<Attribute nameFormat="http://schemas.xmlsoap.org/claims" name="UPN" id="userPrincipalName"/>
<Attribute nameFormat="http://schemas.xmlsoap.org/claims" name="Group" id="group"/>

This is NOT correct. Maybe it worked with older versions, but it doesn’t work at all with Shibboleth 2.4 and ADFS 2.x.

After much digging I stumbled across some interesting entries in the Shibboleth daemon log file /var/log/shibboleth/shibd.log. After each login attempt there were a number entries about unmapped SAML attributes:

2015-01-14 16:00:37 INFO Shibboleth.AttributeExtractor.XML [90]: skipping unmapped SAML 2.0 Attribute with Name: http://schemas.xmlsoap.org/claims/group, Format:urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified

Helpfully that log message tells us the Name and Format of each unmapped attribute. Plugging those values in results in an attribute mapping like this instead:

<Attribute nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" name="http://schemas.xmlsoap.org/claims/group" id="group"/>

Why do my custom datetime strings STILL depend on client locale?

C#’s built in culture support is great. Just call ToString() on any DateTime object and it will be converted into a localized string with the appropriate formatting for the client’s locale. Take a look:

Locale Culture DateTime.ToString()
English en 12/15/2014 10:48:51 PM
Arabic ar 23/02/36 10:48:51 م
Chinese zh 2014/12/15 22:48:51
Danish da 15-12-2014 22:48:51
German de 15.12.2014 22:48:51
Hungarian hu 2014.12.15. 22:48:51

Not only is the order different, but we’ve got slashes, dashes and periods. Fortunately it’s all taken care of for us! But while that’s great for the front-end, having the back-end pass around these localized date strings is just asking for trouble.

Of course, the Right Way to format DateTime strings for back-end use is with the Round-trip format specifier (“O”), which creates an unambiguous, Culture-invariant, timezone aware and ISO 8601-complaint string representation (which looks something like 2014-12-15T22:48:51.0000000-05:00).

But alas, not every application was written to emit/accept ISO 8601-formatted datetime strings. And so, dear reader, I now present one of the many pitfalls to doing custom format strings correctly. Here is the gist of some code I encountered recently:

WebService.ExternalCall(someData, timestamp.ToString("MM/dd/yyyy hh:mm:ss"));

Not too bad really, there might be some issues if the app happens to run in a different timezone than the web service, but at least that custom format string will make sure those timestamps are always in the same format. Except it doesn’t. Soon we start finding entries like this 12-15-2014 22:48:51. Dashes?!! WTF, we clearly specified *slashes*!

Turns out the “/” character in a DateTime format string is actually just a placeholder for the localized date separator. It will be replaced by whatever the client set it to. Keep in mind that users are not locked into the regional defaults and are free to customize their own date/time settings.

Windows 7 Region and Language Settings
Don’t assume that your English-speaking users won’t go and change the time format to something unexpected.

To make the “/” to always be a slash character, we have to either escape it or explicitly override the client’s culture (the same goes for the time separator, “:”, although none of the default Cultures/Locales use anything other than “:”):

timestamp.ToString("MM'/'dd'/'yyyy hh':'mm':'ss");

-or-

timestamp.ToString("MM/dd/yyyy hh:mm:ss", DateTimeFormatInfo.InvariantInfo);

Seriously though, just use “O” if at all possible. It’s quick, easy and solves this problem and many others.

timestamp.ToString("O");

Copy Unselectable Text from Windows Error Message

For many years now I’ve been able to search for solutions only after painstakingly transcribing the error messages from Windows dialog boxes like the one shown below.

replres.rll

Today I discovered the easy way to copy the message text. While the text in these dialogs stubbornly refuses to be selected, surprisingly, the whole error itself can be copied by pressing ctrl-c with the window in focus. When pasted into a text editor, it copies everything including the title, message content and button text:

[Window Title]
replsync.dll - DLL Load Error

[Content]
Cannot load resource dll: REPLRES.RLL

The specified module could not be found.

[OK]

No more manual transcriptions! Life’s too short to waste keystrokes.

PowerShell Variable Followed by Colon

I’ve got a little PowerShell script that writes some configuration settings. In it there’s a line that computes a certain URL for a given subdomain:

$url = "http://$subdomain.ninjanichols.com:8080/"

Pretty standard stuff. But something interesting happens when you try to generalize it and make the domain itself variable:

$url = "http://$subdomain.$domain:8080/"

Instead of http://sub.ninjanichols.com:8080/, I get http://sub./. What’s going on?

Turns out the colon has a special meaning in PowerShell, it’s used both to specify items on a PSDrive, $Env:Foo, or to associate a variable with a scope or namespace, $global:var. So when PowerShell tries to interpret $domain:8080 it sees one variable, instead of a variable + a string.

How to fix it? Just make the intention clear using curly brackets:

$url = "http://${subdomain}.${domain}:8080/"

AD FS 2.0 Service Fails to Start

Symptoms

  1. Instead of a login page, the user is presented with:
    There was a problem accessing the site. Try to browse to the site again.
    If the problem persists, contact the administrator of this site and provide the reference number to identify the problem.
    Reference number: 7aaab8f7-85ed-4910-9f4f-d105100cb604
  2. Going to Administrative Tools -> Services reveals that AD FS 2.0 service is not started. Trying to start the service manual results in:
  3. Event 220 appears in AD FS 2.0 Event Viewer logs:
    The Federation Service configuration could not be loaded correctly from the AD FS configuration database. 
    
    Additional Data 
    Error:  
    ADMIN0012: OperationFault
  4. Event 352 appears in AD FS 2.0 Event Viewer logs:
    A SQL operation in the AD FS configuration database with connection string Data Source=\.pipemssql$microsoft##sseesqlquery;Initial Catalog=AdfsConfiguration;Integrated Security=True failed.  
    
    Additional Data 
    
    Exception details: 
    A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)

Resolution

  1. Start “Windows Internal Database” service.
  2. Now you can start the “AD FS 2.0 Windows Service”.