July 2009 Archives

Geneva Server runs in two modes: server and proxy. When a proxy STS communicates to its "master", it secures the communication with a certificate. Which cert does the master require the proxy use to talk to it? If you open the MMC, you'll see a Certificate node (as shown in the following screenshot):

Given this, you may ask yourself does the master server requires the proxy to use the one that is set under "Service communications", "Information card signing", "Token-decrypting", or "Token-signing"? The answer is none of these. There is a Proxies node in the same treeview. If you right-click this node, you'll see a "Add Proxy Certificate" menu item. This is where you configure the cert to be used not in the certificates node (obviously right?!).

In the Endpoint node of the Geneva Server MMC, there is a matrix of endpoints that are exposed by the service. This grid includes three columns whose meanings were not clear to me until Colin Brace from the Geneva Server team explained. Specifically, the MMC has three columns, Enabled, Proxy Enabled, and CardSpace which confused me. You can see them in the following screenshots:  

An endpoint that is enabled means Geneva Server is listening, essentially, at that address. If it is disabled, then requests sent to that URL will result in an HTTP 500 error. The endpoints that have the Proxy Enabled option set to Yes will be the ones that are exposed by the a Geneva Server instance that is running in proxy mode. I assume, but don't know for sure, that a Geneva Server that is running in this mode will figure out which endpoints to expose based on the configuration of its "master" not its own configuration. The endpoints that have CardSpace set to Yes are the ones that will be included in any Information Cards that Geneva Server provisions. These endpoints are the ones that the selector will communicate with when requesting claims of the STS.

Another thing to note about endpoints that you might overlook is the security mode. If you enabled, for example, WS-Trust 2005 (as above), you'll see a message in the event log that Geneva Server is listening on http://localhost/Trust/2005/Windows not HTTPS like the others in the log message. This is because the endpoint is using message-level security.

All of this is actually pretty obvious after someone points it out or you stop to observe and ponder the UI. There are a lot of endpoints listed there though, and you can easily overlook these details and their meanings. I did at least.

I was debugging some connectivity issues today that were preventing my apps from communicating via SSL. The certs being used to create this secure channel were self-signed. I was doing some of the debugging in Firefox, just to see if the tunnel was able to be set up correctly and without any security policy violations. Even though I added the self-signed cert to the Trusted Root Certificate Authorities store of my Windows workstation, Firefox kept alerting me that the cert was untrustworthy because the issuer was unknown. I scratched my head until I thought that maybe Firefox doesn't use the Windows cert store and uses its own for the sake of portability. So, I tested the HTTPS connection with IE, and, sure enough, it worked.

After a bit of surfing, I found that Firefox does indeed use its own database to store certs. To add your self-signed cert as a trusted CA, you have to go to Tools -> Options -> Advanced tab -> Encryption tab. Then, click View Certificates, and then you can import your cert by clicking Import in Authorities tab of that window. You can also use the command-line tool if you need to automate that.

My friend and former coworker, Cassiano Durand, went to Code Camp Portland last month and told me about a session he attended where the speaker, Steve Evans, mentioned a new directory-related API that was introduced in .NET 3.5. After reviewing the slides and code from the presentation, my colleague and I started using this new API this week. We got snared by a serious gotcha that cost us our coffee breaks.

   

I'll cut to the chase: If you create a user with ADSI Edit, you have to set the object's msDS-UserAccountDisabled property to false and userPrincipalName to match the name of the new account. If you do not do this, the ValidateCredentials method on PrincipalContext objects will always inexplicably return false. To see what I mean, perform the following steps and run the code below.

 

  1. Start by opening ADSI Edit and connecting to an AD LDS instance using settings such as these:

       

       

  2. Once connected, drill down into the OU where you want to create the user objects.

       

       

  3. Create a new object of type user and specify a name.

       

       

       

  4. After finishing the wizard, reset the new user's password via the Reset Password option in the object's context menu.

       

    OK. This is the important bit. Stop skimming and read the following!

       

  5. Open the user object's Properties dialog box.
  6. In this windows, scroll down to msDS-UserAccountDisabled and double click it. In the Boolean Attribute Editor, set the value to False and click OK.

       

       

  7. Scroll down to userPrincipalName and double click it. In the resulting String Attribute Editor dialog box, type the name that you used in step 3 above.

       

       

If you don't perform the last two steps, the new .NET 3.5 System.DirectoryServices.AccountManagement API will not work. Every time you call the ValidateCredentials method on a PrincipalContext object, it will return false. This method seems to check against the User Principal Name (UPN) rather than the name entered when creating the account in ADSI Edit. To see this in action, you can create a simple console application with the following code:

static void Main()

{

    var host = "localhost";

    var port = 50000;

    var userName = "test1";

    var password = "Password1";

    var machineName = string.Format("{0}:{1}", host, port);

    var container = "OU=Users,C=Tenant";

 

    var context = new PrincipalContext(ContextType.ApplicationDirectory, machineName, container);

 

    var authenticated = context.ValidateCredentials(userName, password, ContextOptions.SimpleBind);

 

    if (!authenticated)

    {

        authenticated = context.ValidateCredentials(userName, password, ContextOptions.Negotiate);

 

        if (!authenticated)

            throw new AuthenticationException();

    }

}

Try running it with a user that hasn't had these properties set, an account that has just one of them set, and one that has both the msDS-UserAccountDisabled and userPrincipalName properties updated.  You'll see that creating a user account by simply following the steps on the GUI's wizard will result in an inoperable user account.

If only we had the time back that we wasted figuring this out.  We all could have enjoyed a nice coffee.  As it was, however, we had to work right through our breaks.  Very lame :-(