The LandPhil be honest, be honorable, be kind, be compassionate, and work hard.

September 3, 2015

Firefox, Root Certificates, and you

Filed under: PowerShell — Tags: , , — phil @ 4:41 pm

If you’ve ever dealt with a certificate authority (CA), you may know most of this.  If this is your first foray into it, hold onto your butts.

Many enterprises stand up and run their own certificate authorities to 1) maintain control over certificate issuance, 2) maintain the security of the certificate chain, and 3) not have to pay a public certificate authority to issue certs.  Until recently, you could get certificates for private addresses (10.x.x.x or 192.168.x.x) or private names (my.domain.local) from public certificate authorities.  Now, they won’t issue a certificate unless they can actually verify that the name/address space belongs to you.  So, for many enterprises, the 3rd option above isn’t viable for namespaces behind their firewalls.

The easiest way to set up a CA, is to use Microsoft’s Certificate Authority role that is part of Windows Server.  A handy thing to remember here is that the CA infrastructure is NOT tied to the domain infrastructure.  So, even though it says it’s domain integrated, doesn’t mean you can’t issue certificates for spaces OUTSIDE of the named domain.  (Example: I installed it in my.domain.local, but I want to issue a certificate for notmine.newdomain.local.  I can, and it won’t be a problem.)  The domain integration is just an easy way to publish and distribute the important parts; namely the root certificate and the certificate revocation lists (CRLs).

Once you have your CA set up and domain integrated and your root CA is published, you’d be all set… if the only browsers in your organization are Internet Explorer or Chrome.  See, a web browser use a certificate store to keep all of the root certificates from all of the relevant public authorities.  It also will store any certificates that you want.  In the case of IE and Chrome, they use the OS integrated certificate store that is conveniently updated by Microsoft Update and Active Directory.  In the case of Firefox (and pretty much every other browser), the developers have decided NOT to trust the operating system store and has their own.  This, of course, means that you can’t rely on MS Update or Active Directory to update it with new root certificates.

Doing searching online, and you’ll find a few references on how to overcome this problem with visualbasic scripts and the “certutil.exe” utility that is part of Firefox.  I suggest you go check that out.  You will actually need to jump through the hoops to obtain the certutil.exe utility (specific for Firefox) in order to use the script I’ve included below.  The visualbasic script is fairly basic and has a few caveats and bugs that aren’t readily apparent, so I re-wrote the entire thing in PowerShell for the new generation.

I make no guarantees on it’s execution and with ALL code you download from the internet, it’s best to analyze and test it on your own prior to deployment.

## Import Certificates to Firefox
## Name: import_cert2firefox_public.ps1
## Author: Phillip Cheetham
## Date: 08/26/2015

## For GPO, add script to User Configuration \ Policies \ Windows Settings \ Scripts \ Logon
## Can configure as Powershell script, link to script path; will only work on Windows 7/2008 R2 or later
## Can configure as regular script:
##    Script Name: powershell.exe
##    Script Parameters: -noninteractive -command <script-path\script.ps1>

## Set variables from computer environment
$strTempDir = $env:TEMP
$strAppDataDir = $env:APPDATA
$strFirefoxProfilesDir = $strAppDataDir + "\Mozilla\Firefox\Profiles"

## Set Domain specific variables for installation
## Replace \\servername.domain.local\share\path with the network path to folder containing certutil.exe and certificate files
$strCertutilFolder = "\\servername.domain.local\share\path"
## Replace Local CA with name of local certificate authority.
$strLocalCertificateAuthorityName = "Local CA"
## Replace certificate.crt with the name of the certificate file
## If you need to deploy multiple certificate files, proceed to line 99
$StrCertificateFileName = "certificate.crt"
## Set appropriate trust for the certificate authority by editing "CT,c,C"
## Refer to https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Tools/certutil for more information
$strTrustAttributes = "CT,c,C"

## Do not edit below this line
function IsInstalled
{
    param(
        [Parameter(Mandatory=$true)]
        [string]$ProgramName
        )

    if (($env:PROCESSOR_ARCHITECTURE -eq "AMD64") -or ($env:PROCESSOR_ARCHITECTURE -eq "IA64")) {
        $os64bit = $true
    } else { $os64bit = $false }

    if ($os64bit -eq $true) {
        if (Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object { $_.DisplayName -match $ProgramName }) {
            $true
            return
        }
        else {
            $false
            return
        }
    }
    else {
        if (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object { $_.DisplayName -match $ProgramName }) {
            $true
            return
        }
        else {
            $false
            return
        }
    }
}

if (IsInstalled("Firefox")) {
    if ((Test-Path -Path ($strTempDir + "\FirefoxTools")) -eq $true) {
        # Directory exists, ideally remove directory first to assure published files/certs are current
        Remove-Item -Path ($strTempDir + "\FirefoxTools") -Force -Recurse -ErrorAction SilentlyContinue #attempt to remove before overwriting
        if (New-Item -ItemType Directory ($strTempDir + "\FirefoxTools") -Force) { #if delete fails, force directory overwrite
            Copy-Item ($strCertutilFolder + "\*") ($strTempDir + "\FirefoxTools") -Force #in any case, force file overwrite
        }
        else {
            exit #terminate script execution if directory creation fails
        }
    }
    else {
        # Directory does not exist
        if (New-Item -ItemType Directory ($strTempDir + "\FirefoxTools") -Force) {
            Copy-Item ($strCertutilFolder + "\*") ($strTempDir + "\FirefoxTools") -Force
        }
        else {
            exit #terminate script execution if directory creation fails
        }
    }
    #insert certificates
    $arrFirefoxProfileList = Get-ChildItem $strFirefoxProfilesDir
    foreach ($profile in $arrFirefoxProfileList) {
        ## Backup certdb file
        Copy-Item ($profileDir + "\cert8.db") ($profileDir + "\cert8.db.old") -Force -ErrorAction SilentlyContinue

        ## Execute certificate insertion
        ## Build command line, one section at a time because Invoke-Expression will not process as single line
        ## and '&' command interpreter will not process as single variable
        $execCmd = $strTempDir + "\FirefoxTools\certutil.exe"
        $execCAName = "`'$strLocalCertificateAuthorityName`'" #needs to be encapsulated in single quotes
        $execRootFile = $strTempDir + "\FirefoxTools\" + $strCertificateFileName
        $execAttribs = "`"$strTrustAttributes`"" #needs to be encapsulated in double quotes - VERY IMPORTANT
        $execProfile = $profile.FullName
        & $execCmd -A -n $execCAName -i $execRootFile -t $execAttribs -d $execProfile

        ## To include multiple certificate files, uncomment and copy the lines below as necessary
        ## $strLocalCertificateAuthorityName = "Local CA" #<- Update this line
        ## $execCAName = "`'$strLocalCertificateAuthorityName`'"
        ## $strCertificateFileName = "certificate.crt" #<- Update this line
        ## & $execCmd -A -n $execCAName -i $execRootFile -t $execAttribs -d $execProfile
    }
    Remove-Item -Path ($strTempDir + "\FirefoxTools") -Force -Recurse -ErrorAction SilentlyContinue #remove temp directory
}

import_cert2firefox_public

3 Comments »

  1. Thanks a lot for publishing the PS script to the community, great help and timesaver in getting some certificates rolled out. I did have one error that I encountered while testing… on line #24, you have a variable defined as “$arrStrCertificateFileName”. When a similar variable is called on line #94 to build the full file path to the certificate, it is referenced as “$StrCertificateFileName”. Certutil’s error displays as “unable to open for reading (-5950, 3).” and isn’t totally obvious on the error until I tested the variables and saw where the “$execRootFile” wasn’t totally completed (just had the full path with the “\FirefoxTools\” string appended to it.

    I changed line #24’s variable to be just “$StrCertificateFileName” and took out the array prefix and it ran without a hiccup (so the path stored to the variable $execRootFile is complete with the filename at the end as defined on Line 24).

    Line 24 (modified): $StrCertificateFileName = “certificate.crt”

    Comment by Adomma — October 22, 2015 @ 12:45 pm

  2. Adomma, thanks for the feedback. I’ve corrected the error in the post and the linked file. I’m sure I found it too while testing. It’s possible that I added it accidentally when I removed my specific information and made it generic.

    Phil

    Comment by phil — October 29, 2015 @ 12:52 pm

  3. Hey there! Remember me, Phil?

    Just landed here by accident. Glad to see your blog is still alive!

    Comment by :: jozjozjoz :: — November 3, 2015 @ 8:18 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress