Automatic NTLM auth with Firefox

on Thursday, April 29, 2010

Being a dedicated Firefox user, one of the few things that was still thwarting me was SharePoint.  We use SharePoint internally for a ton of stuff, and it was a drag to have to fall back to that other browser.  SharePoint pages look and work fine in Firefox, but I was having to reauthenticate on every single page, which really hindered my enjoyment of the experience.
I finally figured out how to get Firefox to do NTLM, which means I don’t have to deal with the authentication dialogs, thereby reducing my dependence on IE to one and only one application (Oddpost). 
It’s not at all obvious how to make it work, and it took me a few tries.  You have to go to your Firefox address bar and type about:config.  This will bring up the internal config editor, which allows you to set all kinds of properties that influence Firefox’s behavior.  Look for the key called network.automatic-ntlm-auth.trusted-uris.  Set that key’s value to a comma separated list of servers you want NTLM auth for.  So if your internal SharePoint sites are on servers called Larry and Mo, use “larry,mo”.  You can also add the same value to the key network.negotiate-auth.trusted-uris.  It’s unclear to me if that second one is required, but I set it, and everything works.  Now SharePoint works like a champ, and authenticates automatically.

Update Site Quota for existing sites- Tips & Tricks

on Wednesday, April 28, 2010

There is a little known discrepancy in the SharePoint 2007 Site Quota Templates; they are not easily updated. You see Site Quotas are based upon a template model that is disconnected in nature. That means that all new sites that have the template applied will get the updated version but any existing sites for which it was previously applied retain the previous values.

Unfortunately when you make changes to the template in Central Administration and then check the site quota of an existing site it does appear that the changes have taken effect. This is because the page is simply pulling the settings from the new Quota Template instead of pulling the settings applied at the site level. If you use StsAdm.exe to enumerate through the sites you will see that the site quota retains the previous values. I'll demonstrate.

Lets edit an existing Quota Template for Personal Sites. You navigate to Central Administration and click on Quota Templates. You can see that the existing values are set to allow a maximum storage limit of 100 MB.

quota1

Now change the Storage Limit Maximum to 200 MB as below.

quota2

If we navigate to Site Quotas and Locks and choose the MySites web application we can see that our changes have taken effect. Or so it would appear.

quota3

However, if we use Stsadm.exe and Enumerate through the sites in the web application though we can see that our initial value of 100 MB still applies.

enumSites

This is because the Quota Template is just that, a template, it only applies to new sites that are assigned the quota from that point forward. It does not propagate its changes. Actually there really is no way to propagate these changes and of course we can't have that, so I wrote a little extension to Stsadm.exe to make that possible.

Rather than bore you with the specifics of how I did this I'll refer you to the blog that I used for references on extending Stsadm.exe. John Holiday has a great article here. And of course there is the trusty SDK with which I could not live without.

Below is a screen shot of the help for the Update Quota command that I wrote for Stsadm.exe.

updateQuotaHelp

As you can see it takes 2 parameters, one for the URL and another for the Quota Template Name. The command would be stsadm -o updatequota -url "http://my.demo.com" -quotaname "Personal Site".

After running the command we can go back and use stsadm to enumerate through the sites and see that our changes have taken effect.

quotasUpdated

Most everything in MOSS 2007 is extensible in one way or another and Stsadm is no exceptions. Actually extending stsadm is quite simple. In this blog entry I am going to walk through creating a custom extension for stsadm that I use in a real life scenario. This extension is called Quota Updater and you can read a little history about how this came about in my post here.

The first thing we do is create a new VS project and add a new class called quotaupdater.cs and implement the ISPStsadmCommand interface. Now we have to implement the Run and GetHelpMessage methods. These look like this:

public int Run(string command, StringDictionary keyValues, out string output)
{
//logic here
}
public string GetHelpMessage(string command)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("-url <url>\t\tthe url of the site to process");
            sb.AppendLine("\t-quotaname <quotaname>\tthe template to process");
            return sb.ToString();
        }

The second thing we have to do is create an XML file that describes our commands and add it to the 12/config directory. This looks as follows:

<?xml version="1.0" encoding="utf-8" ?>
<commands>
    <command name="updatequotas"

          class="STSAdmExtensions.QuotaUpdater, STSAdmExtensions, 
      Version=1.0.0.0, Culture=neutral, PublicKeyToken=86a65a77eee85b51">
  </command>
</commands>

In the above XML file the command name is whatever you would like your user to type to activate the custom command with stsadm. This does not necessarily have to be the same as your method or class. It can literally be whatever you would like. The class section is the typical stuff you can get by using Refactor on your assembly.

Now for the meat of the application. Basically what I wanted to do was get a reference to a site collection and a quota template and force updates to the template down to the sites on which it is applied. The Run method is as follows:

public int Run(string command, StringDictionary keyValues, out string output)
{
    StringBuilder sb = new StringBuilder();
    string url = keyValues["url"], quotaName = keyValues["quotaname"];
    //if no URL specified then return 0 and message
    if (url == null || url.Length == 0)
    {
        output = "No url specified";
        return 0;
    }
    //if no Quota Template name supplied then return 0 and message
    if (quotaName == null || quotaName.Length == 0)
    {
        output = "No Quota Template specified";
        return 0;
    }
    try
    {
        SPSite site = new SPSite(url);
        SPWebApplication webApp = site.WebApplication;
        SPSiteCollection col = webApp.Sites;

        SPGlobalAdmin globalAdmin = new SPGlobalAdmin();
        SPGlobalConfig globalConfig = globalAdmin.Config;
        SPQuotaTemplate quota = globalConfig.QuotaTemplates[quotaName];

        UpdateQouta(col, quota);
        output = message;
        
        //clean up resources
        site.Close();
        site.Dispose();
        return 1;
    }
    catch (Exception ex)
    {
        output = ex.Message;
        return 0;
    }
}


And the UpdateQuota method is shown below.

/// <summary>
/// Updates all site quotas in a site collection with updated quota template values
/// </summary>
/// <param name="sites"></param>
/// <param name="quotaTemplate"></param>

public void UpdateQouta(SPSiteCollection sites, SPQuotaTemplate quotaTemplate)
{
    int iCount = 0;
    StringBuilder sb = new StringBuilder();

    foreach (SPSite site in sites)
    {
        //verify quota on the site against the quota template to prevent overwrite
        if (site.Quota.QuotaID == quotaTemplate.QuotaID)
        {
            iCount++;
            site.Quota.InvitedUserMaximumLevel = quotaTemplate.InvitedUserMaximumLevel;
            site.Quota.StorageMaximumLevel = quotaTemplate.StorageMaximumLevel;
            site.Quota.StorageWarningLevel = quotaTemplate.StorageWarningLevel;
        }
    }
    if (iCount == 0)
    { 
message = "No sites were updated. Make sure you specified the correct " +_ "Quota Template Name.";
} else { sb.AppendLine(iCount + " Sites Updated"); sb.AppendLine("The Command Completed Successfully"); message = sb.ToString(); } }

Now all you have to do is deploy the assembly to the GAC and copy your XML file to the 12/config directory and you are set to go.

Host Multiple Websites using SSL on Port 443- WildCard Server Certificate

on Monday, April 26, 2010

Wildcard SSL certificates can allow you to publish multiple IIS web sites--all using SSL on port 443--that are accessible by host headers. That is, if you're running on IIS 6 or above.

So you read about these things, wildcard certificates, but mostly you read about how to buy them. It is possible (and easy) to create one for yourself, however, so long as you don't mind it not being trusted by folks' browsers.

There are some really good uses for these, not least in a test environment in which you don't care about the trust. Production environments need them, too, sometimes, such as when you're using a network appliance like a Netscaler or F5 load balancer. Those devices allow you to point multiple URLs (cnames, really) to a single (or multiple) server(s). Very handy. In our case, we have several web sites that we'd like to secure using SSL. Problem is: they're all on a single server.

There are several options for dealing with such a case: assign multiple IP addresses to the server, use different SSL ports for each IIS site, or (definitely the coolest option) use a wildcard certificate on the server to allow IIS to decipher the http host header. This option, by the way, is a new feature with IIS 6, so if there's anyone out there still using IIS v5, this is another reason to upgrade.

Background
A little background on the problem might be in order: a SSL certificate is used to encrypt the http data. Normally, in setting up a IIS web site, we differentiate that site from all the others by assigning unique host headers to each site. IIS can use those host headers to determine to which site to route any given http request.
When the site is using ssl, however, the host header is encrypted, which introduces something of a chicken-and-egg thing: since the host header (along with all the rest of the data) is encrypted, IIS can't use that to determine which site to send the request to. And, since a SSL certificate is site-specific, IIS can't use a certificate to decrypt the data until it knows which site the request belongs to. Thus, it widely is reported that it isn't possible to have multiple SSL sites on a single server, all sharing the same port.

Enter wildcard SSL certificates and secure server bindings. The wildcard certificate allows IIS to use the same certificate for all of the sites on a particular port. That takes away the requirement that IIS know which site's certificate to use in decrypting the data. Secure server bindings help IIS in securing the wildcard certificate, a requirement for setting this up.
Note that a similar solution is available to apache. You can read about it here. Thus, it is very possible to have multiple SSL sites on a single server, all sharing the same port.

Configure your sites to use host headers

Host headers are the backbone of this. Open the properties window for each site in IIS and click the advanced button. There, you can add the appropriate host header on port 80.



Creating a self-signed wildcard SSL certificate:

The next step in this process is to make sure that all of the relevant sites have the same SSL port assigned to them. In IIS, go to each site's properties and enter the appropriate port number (the same one for each site) in the SSL Port field. 443 is the default SSL port.

Note that once you do this, all but one of the affected sites will stop. That is because, at this point, IIS isn't configured to allow multiple sites to share the same SSL port. That's OK; we'll take care of that in a moment.

Now, to generate the certificate: using SelfSSL, generate the certificate for one existing IIS site. SelfSSL is a tool that's a part of the Microsoft IIS Resource kit. If you haven't downloaded and installed it, you'll need to do that first. Here's the syntax:


selfssl /n:cn=*.server.edu /s:1 /P:443 /v:3650

Where *.server.edu would be your own URL. So if you had these sites:
mysite.sharepoint.com
portal.sharepoint.com
auth.sharepoint.com
you'd use this code:



selfssl /n:cn=*.sharepoint.com /s:1 /P:443 /v:3650
It's the * that makes it a wildcard certificate.

The /s:1 is the site identification. There are a variety of ways to view a site's identifier, but the easiest simply is to open the IIS manager and click on the "web sites" tree on the left. The right-hand pane will show the description and identifier. The default site has an identifier of 1; the other sites have very long identifiers.

/P: is the SSL port you're using, and /V: is the number of days for which this cert is valid. Why would you want it to expire, anyway?

Now you've generated a wildcard certificate.


Assign the Certificate to your sites

The easiest way to assign a certificate to a site, having already created it, is to view the site properties in IIS Manager. Click on Directory Security, and then on the Server Certificate button. This will start the wonderful wizard of IIS.

You already have created a certificate, so when the wizard prompts you, choose "Assign an existing certificate." When you click next, you'll see a list of available certs, including the wildcard certificate you created. Select this certificate, click next, and make sure you assign it the same port that was assigned to the first site.

That's it: once the wizard has finished its magic, you've assigned the cert to your site. Repeat this for all of the sites you want to secure on this port with SSL.


Configure Secure Server Bindings

This is the final step. It involves running a vbs script to set up secure bindings, which allows IIS to use host headers with SSL and secure the new wildcard certificate you created and installed.

The syntax is like this:



cscript adsutil.vbs set /w3svc/844934796/SecureBindings ":443:mysite.sharepoint.com"


The adsutil.vbs script, at least on my systems, is at C:\Inetpub\AdminScripts. You'll need to run the script command from that location.

The syntax breaks down like this:
844934796 is the site ID. Substitute your own site identifier there.
443, again, is the port
mysite.sharepoint.com is the host header for the site. Make sure that you have this host header configured in the site properties in IIS, as well.

Run this script for each of your IIS sites.

That should be all you need to do. Start up the stopped sites after you've run cscript, and you should be good to go.

Microsoft has a few documents that run through this, but they don't really put the pieces together for you. Here they are:

Configure SSL Host Headers (IIS 6.0)
Configuring Server Bindings for SSL Host Headers (IIS 6.0)
Obtaining and Installing a Wildcard Server Certificate


By the way: if you've done a lot of testing, and you've generated a bunch of certificates you're no longer using, you can delete them. Run mmc.exe and add the certificates snap-in. You'll want to choose the "local computer" version. Using that, you can manage all of your local certificates, including deleting the ones you no longer need.