Using Side-by-side Assemblies for Kerberos for Windows
This proposal describes a Kerberos for Windows deployment model using Microsoft Windows Side-by-side Assemblies. The goal is to produce a set of installation components that third party installers can include such that the resulting installations will be able to co-exist with other installations built from different versions of Kerberos for Windows libraries.
The Kerberos for Windows product has evolved since the mid-90s from a loose collection of uninstalled binaries to a set of fully packaged installers providing core libraries, an in-memory credential cache service, command line tools, single sign-on integration, and a graphical credential management tool built upon a pluggable framework permitting third parties to extend the Kerberos credentials acquisition process to obtain derivative credential types.
One of the major challenges to the distribution of Kerberos for Windows as a reliable platform upon which third party applications could rely on has been the lack of a distribution model that permits each third party application to install the version of KFW it depends upon without adversely affecting previously installed applications. To combat this problem MIT has struggled over the years to convince third party application developers to not distribute their own version of the KFW libraries and instead require their users to deploy the official distribution produced by MIT and Secure Endpoints Inc. The official version has been developed over the years such that backward compatibility is maintained.
With the introduction of Microsoft Windows XP, Microsoft provided library developers an alternative approach called Side-by-side Assemblies [1]. Side-by-side Assemblies permit each application to install and load a specific version of a library subsystem without searching the PATH environment variable. This permits multiple versions of the same library subsystem to be installed in parallel on the same system without producing cross-application interference.
This proposal outlines a deployment model based upon the use of Windows Side-by-side Assemblies. Kerberos for Windows core libraries will be packaged and distributed as a set of assemblies that applications can bind to. The assemblies will be provided in the form of Windows Installer merge modules that third parties can integrate into their installers for the purpose of redistributing the Kerberos for Windows libraries along with their applications.
Side-by-side assemblies are fully supported on Windows XP and later operating systems. They can also be deployed on Windows 2000 as shared assemblies. However, on versions of Windows prior to Windows XP, multiple different versions of a library cannot co-exist in a manner transparent to the application.
There are a number of components that comprise the existing Kerberos for Windows installers. A list of these components is presented in Appendix A. Of these components, the side-by-side assembly organization will only be concerned with the core libraries that are shared by third party applications.
Typically, an assembly only contains libraries. However because of its implementation and use, the Kerberos credentials cache service, krbcc32s.exe, is tightly bound to krbcc32.dll and is therefore included in the assembly.
There are two approaches to organizing the assembly. One method is to group all the binaries into a single assembly. The other is to have different assemblies for each library. Both approaches are discussed below.
This is the simplest method of organizing the assembly. The disadvantage with this method is that all the binaries in the assembly have to be versioned together. If there is a change in one of the binaries, a new assembly must be deployed with an larger version number containing all the binaries including the new binary.
The binaries will be organized as follows:
MIT.KerberosV.$CPU[.Debug] ;(Assembly name)
MIT.KerberosV.$CPU[.Debug].manifest ;(Assembly manifest)
krb5_32.dll / krb5_64.dll
krb5_32.pdb / krb5_64.pdb
comerr32.dll / comerr64.dll
comerr32.pdb / comerr64.pdb
gssapi32.dll / gssapi64.dll
gssapi32.pdb / gssapi64.pdb
k5sprt32.dll / k5sprt64.dll
k5sprt32.pdb / k5sprt64.pdb
krbcc32.dll / krbcc64.dll
krbcc32s.exe / krbcc64s.exe
krbcc32.pdb / krbcc64.pdb
krbcc32s.pdb / krbcc64s.pdb
xpprof32.dll / xpprof64.dll
xpprof32.pdb / xpprof64.pdb
wshelp32.dll / wshelp64.dll
wshelp32.pdb / wshelp64.pdb
Figure 1: Example Assembly
As the first line indicates, the assemblies will tentatively be named MIT.KerberosV.$CPU[.Debug]. Assemblies containing debug or checked binaries will have a .Debug suffix. $CPU is the target architecture (x86 or IA-64).
Since an assembly can only target one architecture, 32-bit and 64-bit libraries will need to be packaged into two separate assemblies. The $CPU component of the name will differentiate between these. Although the name of the assembly does not need to differentiate based on the target architecture, doing so allows 32-bit and 64-bit assemblies to be deployed in the same location as a private assembly (See Section 3.1).
Each application is linked to a particular assembly instance via the use of an application manifest bound to the dependent executable or dynamical link library. The .manifest file is an assembly manifest [4] file that describes the contents of the assembly. A suggested manifest file is shown below for 32-bit debug libraries:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="MIT.KerberosV.x86.Debug"
version="3.2.2.0"
processorArchitecture="x86"
publicKeyToken="5a761611aa6ede5a" />
<file name="comerr32.dll" hash="2ef55db0f20f83905b0392031ddbef1e7db63e69" hashalg="SHA1"></file>
<file name="gssapi32.dll" hash="7844c963943a7bbd6d57a5936b2dd17f3efa505c" hashalg="SHA1"></file>
<file name="k5sprt32.dll" hash="b0e7ac59ce43150018564669154d5dfe933d7151" hashalg="SHA1"></file>
<file name="krb5_32.dll" hash="783385f697bb866c256f26439b1b4285eab44fcb" hashalg="SHA1"></file>
<file name="krbcc32.dll" hash="76f3163415deed42010db8f5880842fdfcd85440" hashalg="SHA1"></file>
<file name="leashw32.dll" hash="df2f3a030f26e5db7a5f6bb6c42840184a3383f2" hashalg="SHA1"></file>
<file name="wshelp32.dll" hash="1520d062f0e17fcbb81a8a8b4c6d34106b7dc5f9" hashalg="SHA1"></file>
<file name="xpprof32.dll" hash="6768d1ffc749f9b77a6ff97fe8ccba4df132277a" hashalg="SHA1"></file>
<file name="krbcc32s.exe" hash="f0092316d791de2bece39828245c9b83c2dcbdb5" hashalg="SHA1"></file>
</assembly>
Figure 2: Example Assembly Manifest
The publicKeyToken value as well as the hash values, although based on actual binaries and a certificate issued by a self-signed test certificate authority, are for illustrative purposes only. When producing a real assembly a code signing certificate from Verisign will be required.
Such a manifest would be made public along with the Kerberos for Windows assemblies so that third party application developers can author corresponding application manifests to bind with the correct assembly. The requirements for application assemblies is discussed in Section 3.3.
Each library would be packaged in its own assembly. Both krbcc32.dll, krbcc32s.exe would be included in the same assembly so that the library can load the the credentials cache server using a known path.
The resulting assembly structure is:
MIT.KerberosV.KerberosV.$CPU[.Debug]
MIT.KerberosV.KerberosV.$CPU[.Debug].manifest
krb5_32.dll / krb5_64.dll
krb5_32.pdb / krb5_64.pdb
MIT.KerberosV.ComErr32.$CPU[.Debug]
MIT.KerberosV.ComErr32.$CPU[.Debug].manifest
comerr32.dll / comerr64.dll
comerr32.pdb / comerr64.pdb
MIT.KerberosV.GSSAPI.$CPU[.Debug]
MIT.KerberosV.GSSAPI.$CPU[.Debug].manifest
gssapi32.dll / gssapi64.dll
gssapi32.pdb / gssapi64.pdb
MIT.KerberosV.K5Sprt.$CPU[.Debug]
MIT.KerberosV.K5Sprt.$CPU[.Debug].manifest
k5sprt32.dll / k5sprt64.dll
k5sprt32.pdb / k5sprt64.pdb
MIT.KerberosV.KrbCC.$CPU[.Debug]
MIT.KerberosV.KrbCC.$CPU[.Debug].manifest
krbcc32.dll / krbcc64.dll
krbcc32s.exe / krbcc64s.exe
krbcc32.pdb / krbcc64.pdb
krbcc32s.pdb / krbcc64s.pdb
MIT.KerberosV.XPProf.$CPU[.Debug]
MIT.KerberosV.XPProf.$CPU[.Debug].manifest
xpprof32.dll / xpprof64.dll
xpprof32.pdb / xpprof64.pdb
MIT.KerberosV.WSHelp.$CPU[.Debug]
MIT.KerberosV.WSHelp.$CPU[.Debug].manifest
wshelp32.dll / wshelp64.dll
wshelp32.pdb / wshelp64.pdb
Figure 3: Example Assembly Structure
The KFW libraries are dependent on each other. Each library is only expected to function properly when linked with other dependent libraries from the same KFW release. Therefore, it is apropriate to treat the set of KFW libraries as a single assembly.
Third party applications can use this method to install Kerberos for Windows libraries for its exclusive use as discussed in [2].
When deploying as a private assembly, the assembly files are placed in a subdirectory that has the same name as the assembly, in the corresponding application directory. The assembly will be referenced by name by an application manifest Section 3.3.
The resulting directory structure would look like the following:
...
Program Files\
MyApp\
myapp.exe
myapp.exe.manifest (Application manifest)
MIT.KerberosV.$CPU[.Debug]\
MIT.KerberosV.$CPU[.Debug].manifest
krb5_32.dll / krb5_64.dll
krb5_32.pdb / krb5_64.pdb
comerr32.dll / comerr64.dll
comerr32.pdb / comerr64.pdb
...
Figure 4: Private Assembly Installation Directory Structure
If the Kerberos for Windows libraries are being installed in a shared fashion, it will be required that they be installed as a side-by-side assembly. Such a depolyment would look like the following:
...
Program Files\
MyApp\
myapp.exe
myapp.exe.manifest (Application manifest)
Windows\
WinSxS\
Manifests\
$versionedname.manifest (Assembly manifest)
$versionedname\
krb5_32.dll / krb5_64.dll
krb5_32.pdb / krb5_64.pdb
comerr32.dll / comerr64.dll
comerr32.pdb / comerr64.pdb
...
Figure 5: Shared Assembly Installation Directory Structure
The $versionedname is a strong name that is generated using the assembly name, target architecture, a token value based on the certificate used to sign the assembly and the version of the assembly. It is generated when needed from assembly and application manifest data. Usage of this name is largely opaque to application developers and assembly authors.
Each application that links against the Kerberos for Windows libraries should declare the assembly dependency using an application manifest [5] such as the following (assuming all the Kerberos libraries are in a single assembly):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="x86"
name="YourCompanyName.YourDivision.YourApp"
type="win32"
/>
<description>Your app description here</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="MIT.KerberosV.x86.Debug"
version="3.2.2.0"
processorArchitecture="X86"
publicKeyToken="5a761611aa6ede5a"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
This application manifest corresponds to the assembly manifest shown in Figure 2.
Figure 6: Example Application Manifest
In order for assembly deployment to be successful, there are a number of requirements and restrictions which must be satisfied by the KFW libraries:
Kerberos for Windows assemblies will be packaged into Microsoft Windows Installer Merge Modules so that they can be easily integrated into existing product deployments. A single merge module can serve both as a source for a private assembly or as a source for a shared side-by-side assembly. The source code presented in Appendix B shows how such an module might be constructed.
In addition to the binaries that comprise the Kerberos for Windows libraries and their debug symbols, shared side-by-side assemblies must be digitally signed and must contain a signed catalog. The process for creating signed files and catalogs is outlined in [6].
The example merge module given in Appendix B can be used without any configuration if the Kerberos for Windows libraries are to be used as a shared side-by-side assembly.
The FileApp configuration setting can be used to associate the assembly with a particular executable if it is to be used as a private assembly. This configuration value should be set to a string that is a key into the File table. The File table record corresponding to the key would be used as the host executable file, and the private assembly will be installed in the directory into which the executable is installed.
The following WiX code fragment shows how the merge module can be used to install the libraries as a private assembly. Any Windows Installer editing tool that supports configurable Merge Modules can be used to set the FileApp configuration value.
...
<Directory Id="TESTAPPDIR" Name="TestApp" SourceName=".">
<Directory Id="MM" Name="MM" LongName="MIT.KerberosV.x86.Debug">
<Merge Id="KFWMSM" DiskId="1" Language="0" src="kfwlibs.msm">
<ConfigurationData Name="FileApp" Value="file_testapp.exe" />
</Merge>
</Directory>
<Component Id="cmp_testapp" Guid="2F7BD33D-3FC0-4C3C-9E70-DD1382D391B1">
<File Id="file_testapp.exe" Name="testapp.exe" KeyPath="yes" DiskId="1"/>
<File Id="file_testapp.exe.manifest" Name="testapp.exm" LongName="testapp.exe.manifest" DiskId="1" />
</Component>
</Directory>
...
Figure 7: Installing Merge Module with WiX as a Private Assembly
Changes that are not backwards compatible should result in the release of a new version of the assembly.
If a third party needs to deploy Kerberos for Windows with modifications, they should bundle the libraries into an assembly with a different base name than MIT.KerberosV. Having a different name allows modified libraries to co-exist with MIT Kerberos for Windows on the same machine even when installed as a shared assembly.
Applications that depend on custom libraries can bind to them directly using an application manifest that refers to the custom manifest by name.
| [1] | Microsoft Corporation , “Isolated Applications and Side-by-side Assemblies”, <http://msdn.microsoft.com/en-us/library/aa375193(VS.85).aspx>. |
| [2] | Microsoft Corporation , “Private Assemblies”, <http://msdn.microsoft.com/en-us/library/aa375674(VS.85).aspx>. |
| [3] | Microsoft Corporation , “Authoring DLLs for Side-by-side assemblies”, <http://msdn.microsoft.com/en-us/library/aa374238(VS.85).aspx>. |
| [4] | Microsoft Corporation , “Assembly Manifests”, <http://msdn.microsoft.com/en-us/library/aa374219(VS.85).aspx>. |
| [5] | Microsoft Corporation , “Application Manifests”, <http://msdn.microsoft.com/en-us/library/aa374191(VS.85).aspx>. |
| [6] | Microsoft Corporation , “Creating Signed Files and Catalogs”, <http://msdn.microsoft.com/en-us/library/aa375139(VS.85).aspx>. |
The components of the current Kerberos for Windows installers are as follows. Note that the following list excludes Kerberos v4 libraries, registry keys, configuration files and other installation steps.
The following is the Windows Installer XML (WiX) source code for a possible merge module that includes the Kerberos for Windows libraries as a single assembly.
The source code can be compiled using WiX v2. In addition to the binaries and debug symbols that comprise the Kerberos for Windows libraries an assembly manifest file (E.g.: Figure 2) and a catalog file are required. The catalog file can be created using the process outlined in [6].
<?xml version='1.0'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2003/01/wi'>
<Module Id='MIT.KerberosV.x86.Debug'
Guid='B5AA69A2-87E7-4AD5-AB60-78098846E609'
Language='0' Version='1.0.0.0'>
<Package Id='????????-????-????-????-????????????'
Description='Kerberos for Windows Libraries'
Comments='Library assembly'
Manufacturer='MIT' InstallerVersion='200' Compressed='yes' />
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ModuleDir' Name='.' FileSource=".\">
<Component Id='cmp_KrbVAssembly' Guid='065094BA-A0F3-432A-AD7D-A96900298602'>
<File Id="file_comerr32.dll" Name="comerr32.dll" Vital="yes" />
<File Id="file_comerr32.pdb" Name="comerr32.pdb" Vital="yes" />
<File Id="file_gssapi32.dll" Name="gssapi32.dll" Vital="yes" />
<File Id="file_gssapi32.pdb" Name="gssapi32.pdb" Vital="yes" />
<File Id="file_k5sprt32.dll" Name="k5sprt32.dll" Vital="yes" />
<File Id="file_k5sprt32.pdb" Name="k5sprt32.pdb" Vital="yes" />
<File Id="file_krb5_32.dll" Name="krb5_32.dll" Vital="yes"
KeyPath="yes" Assembly="win32"
AssemblyManifest="file_MIT.KerberosV.x86.Debug.manifest"/>
<File Id="file_krb5_32.pdb" Name="krb5_32.pdb" Vital="yes" />
<File Id="file_krbcc32.dll" Name="krbcc32.dll" Vital="yes" />
<File Id="file_krbcc32.pdb" Name="krbcc32.pdb" Vital="yes" />
<File Id="file_krbcc32s.exe" Name="krbcc32s.exe" Vital="yes" />
<File Id="file_krbcc32s.pdb" Name="krbcc32s.pdb" Vital="yes" />
<File Id="file_leashw32.dll" Name="leashw32.dll" Vital="yes" />
<File Id="file_leashw32.pdb" Name="leashw32.pdb" Vital="yes" />
<File Id="file_MIT.KerberosV.x86.Debug.cat"
Name="KrbV.cat" LongName="MIT.KerberosV.x86.Debug.cat"
Vital="yes" />
<File Id="file_MIT.KerberosV.x86.Debug.manifest"
Name="KrbV.man" LongName="MIT.KerberosV.x86.Debug.manifest"
Vital="yes" />
<File Id="file_wshelp32.dll" Name="wshelp32.dll" Vital="yes" />
<File Id="file_wshelp32.pdb" Name="wshelp32.pdb" Vital="yes" />
<File Id="file_xpprof32.dll" Name="xpprof32.dll" Vital="yes" />
<File Id="file_xpprof32.pdb" Name="xpprof32.pdb" Vital="yes" />
</Component>
</Directory>
</Directory>
<Configuration Name="FileApp" Format="Text"
DisplayName="File_Application value"
NonNullable="no"
Description="File identifier for host executable if Kerberos for Windows is to be used as private assembly. If this value is NULL, then the assembly will be installed as a side-by-side assembly." />
<Substitution Table="MsiAssembly" Row="cmp_KrbVAssembly"
Column="File_Application" Value="[=FileApp]" />
</Module>
</Wix>
Figure 8: Sample WiX Source for Assembly Merge Module