# Kerberos for Win32

## version 2.0

## Release Notes

### October 7, 1999

### Table of Contents

- What is Kerberos?
- What's New
- Known Issues
- Building
- Installation and Configuration
- Command Line Options to Leash32, kinit, klist, and kdestroy
- To Do
- Developer Notes

## What's New

1. Support for Kerberos 4 and Kerberos 5. The Kerberos 5 included here is from the MIT Kerberos 5 1.1 distribution.

2. Leash32 replaces kview32. It support Kerberos 4 and/or Kerberos 5, depending on what's installed on the machine.

3. kinit, klist, and kdestroy support Kerberos 4 and/or Kerberos 5, depending on what's available.

4. We now support in-memory tickets. In fact, Kerberos 4 does not support file-based tickets at the moment.

5. New kclient behavior.

6. Missing kerberos entries in your services file will not necessarily cause problems.

7. Support for configuration DNS.

## Known Issues

1. The credentials cache does not necessarily iterate properly if it is written to while you're trying to iterate over it. This might cause behavior if an application is iterating over the cache while another is destroying it.

2. The credentials cache only has a few slots for credentials.

## Building the Sources

First, make sure that you have MSVC++ 6.0, ActiveState Perl build 517, sed, gawk, cat, and rm in your PATH. You can get sed, gawk, cat, and rm from the Cygwin distribution. Also make sure that perl has been installed so that .pl files are automatically executed with perl. You will probably also need to be using the default system shell (cmd or command, depending on whether you're running NT/2000 or 95/98) so that the Makefiles work properly.

- Go into the `athena` directory and type `..\scripts\build.pl` to build everything with debug information. For a release build, run `..\scripts\build.pl NODEBUG=1`. For help, do `..\scripts\build.pl -?` and `..\scripts\build.pl help`. (If krb5 does not build properly, it is because sed/gawk were not in your path. To fix this, delete `athena/auth/krb5/src/Makefile`, fix your `PATH`, and try again.)
- To make your life easier, you might try putting the `scripts` directory in your path.
- If you use the `build.pl` script, the targets should get copied into the `target` directory at the same level as the `athena` directory. You can go into `target\bin\i386\dbg` (replacing i386 for the right CPU and dbg for rel if building release targets), and run the binaries. You should be able to debug from there.

## Installation/Configuration

#### Table of Contents

- Binaries
- Kerberos Configuration Files
- Modifying Kerberos 4 Configuration Files
- Modifying Kerberos 5 Configuration File
- Services File
- Ticket Cache
- Date and Time Issues

### Binaries

- krbv4w32.dll - Kerberos 4
- krbcc32.dll - required by Kerberos 4, used by Kerberos 5 as well
- fleavius.exe - required by krbcc32.dll
- kclnt32.dll - required by some Kerberos 4 applications
- krb5_32.dll - Kerberos 5
- leash32.exe - Leash32 Integrated Kerberos ticket manager
- leashw32.dll - used by Leash32
- xpprof32.dll - used by Leash32
- comerr32.dll - used by Leash32 and Kerberos 5
- gssapi32.dll - GSS API for Kerberos 5
- wshelp32.dll - winsock herlper used by various things
- kinit.exe - command-line app to get Kerberos tickets
- klist.exe - command-line app to list Kerberos tickets
- kdestroy.exe - command-line app to destroy Kerberos tickets

At MIT we normally recommend to users that they have a directory `c:\net\mit` on their path and that the files reside in this directory. Many other sites recommend that users place these files in their Windows system directory or the Windows directory. MIT prefers to avoid this for a few reasons.

For MIT users we prefer using `c:\net\mit` for a few reasons. First this directory historically exists on most of our users' systems. It is also where we normally recommend installing locally developed applications. By using this setup we are able to support users that insist on running multiple operating systems or multiple versions of an operating system on their machine. It is much easier to track down a single copy of a DLL and determine its version number. Also the number of files in this directory is much smaller than in the OS directory so it is much easier for users to locate and determine the version number if this becomes necessary. Finally, very few third party installers will attempt to modify the `c:\net\mit` directory so we don't worry about what another application will do to our binaries.

The disadvantages to seperating the DLLs from the OS are that users must add yet another directory to their path. And when they do this manually they may make a typing mistake. Sometimes the typing mistakes can be subtle and difficult to resolve. E.g `c:\net\mit` is not the same as `c\net\mit`.

### Kerberos Configuration Files

Kerberos 5 needs a single configuration file: `krb5.ini`. You can just put it in the same directory as the DLL and everything will work fine. You can also put it in the Windows directory. You can even point to an arbitrary file by settings the `KRB5_CONFIG`.

Kerberos 4 needs two configuration files, typically called `krb.con` and `krbrealm.con`. You can just put these files in the same directory as the DLL and everything should work. You can also set KRB4_KRB.REALMS or KRB4_KRB.CONF to override each file. Or you can set KRB4_CONFIG to force Kerberos 4 to look for both files in a particular directory. If you do neither of these, this where Kerberos 4 will search:

1. %NDIR%\kerb\
2. The current directory
3. The Windows directory
4. The Windows system directory
5. The directory containing the executable file for the current task
6. The directories in the path (*)
7. The list of directories mapped in a network
8. %NDIR%\
9. %ETC%\

(*) Note: If you put the files in the DLL's directory, this part of the search is what will take you there. If you have another config file earlier in the search, that will take precendence, so be careful.

### Modifying Kerberos 4 Configuration Files

Note: Leash32 can be used to edit the Kerberos 4 configuration files.

It is anticipated that most sites using Kerberos version 4 on Windows will also have an existing UNIX Kerberos infrastrucutre. For that reason we kept the format of the `krb.con` indetical to the UNIX `krb.conf`and the format of `krbrealm.con` identical to the UNIX `krb.realms`. For many users the easiest way to configure these files for use at their local site will be to simply ftp the corresponding files from a local UNIX machine that is already properly configured.

The `krb.con` file contain configuration information describing the Kerberos realm and the Kerberos key distribution center (KDC) servers for known realms.

`krb.con` contains the name of the local realm in the first line, followed by lines indicating realm/host entries. The first token is a realm name, and the second is a hostname of a host running a KDC for that realm. The words "admin server" following the hostname indicate that the host also provides an administrative database server which is contacted when changing a user's password. For example:

    ATHENA.MIT.EDU
    ATHENA.MIT.EDU kerberos.mit.edu admin server
    ATHENA.MIT.EDU kerberos-1.mit.edu
    ATHENA.MIT.EDU kerberos-2.mit.edu
    LCS.MIT.EDU kerberos.lcs.mit.edu admin server

If this was your `krb.con` file and you wanted to change the default local realm to `CIT.CORNELL.EDU` you would edit it to look like:

    CIT.CORNELL.EDU
    CIT.CORNELL.EDU kerberos.cit.cornell.edu admin server
    ATHENA.MIT.EDU kerberos.mit.edu admin server
    ATHENA.MIT.EDU kerberos-1.mit.edu
    ATHENA.MIT.EDU kerberos-2.mit.edu
    LCS.MIT.EDU kerberos.lcs.mit.edu admin server

The `krbrealm.con` file is the host to Kerberos realm translation file. This provides a translation from a local hostname to the Kerberos realmname for the services provided by that host.

Each line of the translation file is in one the following forms (domain_name should be of the form `.XXX.YYY`, e.g. `.LCS.MIT.EDU`):

    	host_name kerberos_realm
    	domain_name kerberos_realm

If a hostname exactly matches the host_name field in a line of the first form, the corresponding realm is the realm of the host. If a hostname does not match any host_name in the file, but its domain exactly matches the domain_name field in a line of the second form, the corresponding realm is the realm of the host.

If no translation entry applies, the host's realm is considered to be the hostname's domain portion converted to uppercase.

### Modifying Kerberos 5 Configuration File

Note: Leash32 can be used to edit the Kerberos 5 configuration file.

See the Kerberos 5 documentation in athena/auth/krb5/doc for further documentation on Kerberos 5.

### Services File

The Kerberos DLLs need to know what port to use to talk to the Kerberos server. Kerberos 4 now defaults to ports 750 (kerberos 750/udp kdc) and 751 (kerberos-master 751/tcp) if there are no kerberos or kerberos-master entries in the services file. Kerberos 5 also has proper defaults (port 88 with a fallback to 750) in case the services file is missing the entries for kerberos and kerberos-sec.

If your site uses non-standard ports, you will still need a services file appropriate for your site.

### Ticket Cache

The new default for Kerberos 4 and 5 is to store their tickets in memory.

You can specify the name of the ticket file and the directory in which it is stored via the environment variables `KRBTKFILE` (krb4) and `KRB5CCNAME` (krb5). The krb4 credentials will always go into memory. The ticket string you see will always have an "API:" in front of it.

There are also registry settings for these things. Playing with Leash32 will reveal where they are (look in HKCU\Software\MIT\Kerberos4 and Kerberos5). You can set machine-wide values by playing with these settings in HKLM.

Kerberos 5 does support using file-based tickets, but we don't recomment this as it is less secure.

### Date and Time issues

Why Kerberos cares what time it is...

Kerberos authentication uses time stamps as part of its protocol. When the clocks of the Kerberos server and your computer are too far out of synchronization, you cannot authenticate properly. Both the Kerberos server and the Kerberos client depend on having clocks that are synchronized within a certain margin. This margin is normally 5 minutes.

The date and time on the machine running Kerberos will need to be "accurately" set. If the date or time is off "too far", Kerberos authentication will not work.

You can synchronize your clock using Leash32. It allows you to set the name of the host you will synchronize to. It saves this information in the registry (under HKCU\Software\MIT\Leash32 -- you can set machine-wide defaults in HKLM).

By default the server that the libraries will contact when synchronizing the time is `time`. The domain name has been left off on purpose. If local system administrators create a machine with a CNAME of time within the local domain the clients will contact this machine by default.

If you local system administrators are opposed to doing this for some reason people can edit the resource `LSH_TIME_HOST` in the `leashw32.dll` to the name appropriate for their local site. You can also edit the header files from the source distribution and recompile for your local site. However, this is not recommended. You can also just tweak the registry setting Leash32 uses.

You can also avoid this problem by running a local, properly configured, NTP program on your machine.

## Command Line Options to Leash32, kinit, klist, and kdestroy

The command line options for Leash32 are:

    	-kinit	only perform a kinit and then exit Leash

Docs for kinit, klist, and kdestroy are forthcoming. Typing the name of the utility followed by `-?` will give you some usage infomation.

## To Do

1. The tf_close in tf_save_cred still puzzles me. We should figure out what the spec is supposed to be there (if there is supposed to be one at all) and document he heck out of it. :)

2. Make Kerberos 4 thread-safe and investigate whether Kerberos 5 is thread-safe.

3. Revamp krbcc32 code to make it easier to read. Add support for dyncamic sizing of credentials by using LPC to another process. Also improve handling of iterators to avoid concurrency problems. (This may slow down krbcc32 performance. How can we avoid that?)

4. Use krb5's com_err instead of the krb4 mess we now have. Make sure all logging is funneled through one place and provide a way to control where that goes.

5. A clean Leash API.

## Developer Notes

### Kerberos 5

The Kerberos 5 sources included here are a subset of the MIT Kerberos 5 1.1 distribution. They do not include some Unix and Mac-specific directories.

### kclient

- kclient will now try to get tickets for foreign principals if you pass in a service with a non-local realm. It will use the service's realm instead of the default local realm.
- kclient now looks in the ticket file for the realm info before looking for the local default realm.

Below is Jeffrey Altmans's explanation of the problem. We put in a patch he submitted for the first 2 items in his 3-item solution below.

> The design of the Kclient interface is so simplified that applications cannot easily get access to necessary information. This has resulted in the need for the Kerberos configuration page in Eudora which includes fields for:

>       Realm:          CC.COLUMBIA.EDU
>       Service name:   pop
>       Service format: %1.%4@%3

> The specification of a Realm here without a "Principal name" is interesting.

> Some information on my system. The local realm is KRB5.COLUMBIA.EDU and the POP server is pop.cc.columbia.edu which is in realm CC.COLUMBIA.EDU. With a ticket manager I retrieve a TGT for CC.COLUMBIA.EDU. Kstatus.exe reports that I am authenticated. Kstatus uses the kclnt32.dll interface just as Eudora does. However, when I ask Eudora to check my mail an interesting thing happens:

> GetTicketForService() is called which results in a dialog being displayed

>         Please enter your Network ID and password.

>         Network ID: jaltman
>         Password:

> So I enter my password for CC.COLUMBIA.EDU and am told sorry but the password is incorrect.

> What happened here?

> First, Eudora does not include a field for the principal name to go along with the realm. So it does not call SetUserName() which should have been set to

>       jaltman@CC.COLUMBIA.EDU

> to match the realm specified in Eudora for the POP host. Eudora needs the realm so it can construct the service ticket name

>       pop.mailhub@CC.COLUMBIA.EDU

> but without setting the username it has no mechanism to report the desired realm to kclnt32.dll.

> GetTicketForService() calls an internal function to verify the TGT. But because no realm has been set and data is not shared between process boundaries this instance of kclnt32.dll has no idea that the ticket manager realm is CC.COLUMBIA.EDU. And instead of attempting to load the Ticket File Realm it calls krb_get_lrealm(). So it tries to verify a TGT

>       krbtgt.KRB5.COLUMBIA.EDU@KRB5.COLUMBIA.EDU

> which does not exist. So it destroys the existing tickets and then attempts to get a new TGT for

>       jaltman

> in the default realm which again is KRB5.COLUMBIA.EDU. But of course kclnt32.dll does not display the realm in the dialog box so the user has no idea that kclnt32.dll is confused.

> In order to correct this situation the following needs to be done to kclnt32.dll:

> 1. all calls to krb_get_lrealm() should be preceeded by an attempt to retrieve the realm from the ticket file with krb_get_tf_realm(). Only if krb_get_tf_realm() fails should krb_get_lream() be called.
> 2. if a realm is specified in the GetTicketForService() request that realm should be used for the verification of the TGT.
> 3. the dialog box displayed by UserInfo() should append the realm to the szNetID (if it is not already part of the string) when setting the default value for the box. This will indicate to the user which realm s/he is being authenticated against.

> Then the author's of Eudora should add a principal name to the configuration for Kerberos and call SetUserName() to set the principal and realm to the values needed to authenticate against the POP server.

### Registry and Environment Settings

    Leash DLL timesync code:
       1. Use TIMEHOST environment value if defined.
       2. Otherwise, use value from registry
          (HKCU\Software\MIT\Leash\Settings,timehost) if present.
       3. Otherwise, use value from registry
          (HKLM\Software\MIT\Leash\Settings,timehost) if present.
       4. Otherwise, use resource string if present.
       5. Otherwise, default to #defined value "time".

    Kerberos 4:
    A. location of krbrealm & krbconf:
       1. First, check for environment overrides:
          a. Use %KRB4_KRB.REALMS% as full filename for realms file if defined.
          a. Use %KRB4_KRB.CONF% as full filename for config file if defined.
          b. Otherwise, look for krbrealm.con and krb.con in dir %KRB4_CONFIG%.
       2. If nothing defined so far, look in registry:
          a. HKCU\Software\MIT\Kerberos4,krb.realms for realms full pathname.
          a. HKCU\Software\MIT\Kerberos4,krb.conf for config full pathname.
          b. HKCU\Software\MIT\Kerberos4,config as dir for both files.
          c. HKLM\Software\MIT\Kerberos4,krb.realms for realms full pathname .
          c. HKLM\Software\MIT\Kerberos4,krb.conf for config full pathname.
          d. HKLM\Software\MIT\Kerberos4,configdir as dir for both files.
       3. If any of the above are set, we use it even if the files are not there.
          If none of them are set, we use the old krb4 search.

    B. ticket file
       1. %KRBTKFILE% if defined
       2. Registry setting, if setting is present
          (HKCU\MIT\Kerberos4,ticketfile)
       3. Registry setting, if setting is present
          (HKLM\MIT\Kerberos4,ticketfile)
       4. Otherwise, "CCAPI:krb4cc".
      (
       If we ever support a file-based cache, we should use this:
       4. %TEMP%\ticket.krb, if var defined and dir exists
       5. %TMP%\ticket.krb, if var defined and dir exists
       6. c:\temp\ticket.krb if c:\temp exists
       7. c:\tmp\ticket.krb if c:\tmp exists
       8. GetWindowsDirectory()\ticket.krb as a last-ditch default?  It's
          either that or c:\ticket.krb!
       )

    Kerberos 5:
    A. location of krb5.ini:
       1. %KRB5_CONFIG% if defined
       2. (HKCU\Software\MIT\kerberos5,config) if defined
       3. (HKCU\Software\MIT\kerberos5,config) if defined
       4. Otherwise, use GetWindowsDirectory()\krb5.ini
       (do this instead of OpenFile to make things more explicit/simple)

    B. credentials cache
       1. %KRB5CCNAME% if defined
       2. (HKCU\Software\MIT\kerberos5,ccname) if defined
       3. (HKLM\Software\MIT\kerberos5,ccname) if defined
       4. If RegKRB5CCNAME is set under [Files] in kerberos.ini,
          look at that path in the registry (code already in krb5 for compat
          with Gradient DCE installations, I believe).
       5. Otherwise, if using CCAPI, default to "CCAPI:krb5cc".
                     if no CCAPI, use "FILE:" with:
          a. %TEMP%\krb5cc, if var defined and dir exists
          b. %TMP%\krb5cc, if var defined and dir exists
          c. c:\temp\krb5cc if c:\temp exists
          d. c:\tmp\krb5cc if c:\tmp exists
          e. GetWindowsDirectory()\krb5cc as a last-ditch default?
             it's either that or c:\krb5cc!

### Using DNS Lookups for Win32 Kerberos 2.0

#### What is it?

DNS Lookups provide Kebreros the ability to determine the Kerberos Realm that a host belongs to and to find the servers associated with a given Realm by using the Domain Name Service instead of or in addition to local configuration files.

Support for DNS is not enabled by default. You must explicitly turn it on when building Kerberos if you want to use it.

#### Building with DNS Support

To build Win32 Kerberos 2.0 with support for DNS lookups, you need to set the following variables:

>     DNS_INC=<path>\kerberos-win32-2.0\athena\wshelper\include
>     DNS_LIB=<path>\kerberos-win32-2.0\athena\wshelper\wshload\obj\i386\dbg\wshload.lib
>     KRB4_USE_DNS=1
>     KRB5_USE_DNS=1

You can set them as environment variables before executing the `..\scripts\build.pl` script or you can set them on the script command-line (or a combination).

For example:

>     set DNS_INC=<path>\kerberos-win32-2.0\athena\wshelper\include
>     set DNS_LIB=<path>\kerberos-win32-2.0\athena\wshelper\wshload\obj\i386\dbg\wshload.lib
>     ..\scripts\build.pl KRB4_USE_DNS=1 KRB5_USE_DNS=1

#### When are DNS Lookups used?

DNS Lookups are used under two circumstances:

1. No `krb.con` file is found for Kerberos 4 or no `krb5.ini` file is found for Kerberos 5.
2. The `krb.con` file or `krb5.ini` file contains a command to activate DNS Lookups and the lookup cannot be answered by data found in the appropriate configuration file.

To activate DNS lookups in a KRB.CON file place a line that reads:

>     .KERBEROS.OPTION. dns

as one of the Realm to Host entries. When DNS Lookups are used the first line may be left blank to indicate that the default realm should be determined by a DNS Lookup.

To activate DNS lookups in a KRB5.INI file place:

>     dns_fallback = true

into the `[libdefaults]` section. If a "default_realm" entry is not provided, a DNS lookup will be performed to determine the default realm.

#### What entries go into the DNS?

Host to realm lookups are performed using DNS TXT records. Example records are:

>     _kerberos.yclept.kermit.columbia.edu.  IN TXT "KRB5.COLUMBIA.EDU"
>     _kerberos.columbia.edu.                IN TXT "CC.COLUMBIA.EDU"

Realm to server lookups are performed using DNS SRV records. Example records are:

>     _kerberos._udp.KRB5.COLUMBIA.EDU.    IN SRV 0 0 750     yclept.kermit.columbia.edu

>     _kerberos-adm._tcp.KRB5.COLUMBIA.EDU IN SRV 0 0 749     yclept.kermit.columbia.edu

>     _kpasswd._udp.KRB5.COLUMBIA.EDU      IN SRV 0 0 464     yclept.kermit.columbia.edu

### Some older notes from prior releases...

1. Modification of the credential cache:

   We encountered a problem at MIT that we felt needed to be addressed even though it broke some backwards compatibility. We found that if someone used a Kerberized application spanning multiple PPP sessions a Kerberos error would be generated and few applications would catch this error and try to get new tickets instead. E.g. Suppose a user starts a PPP connection and then starts Eudora, fetching mail. The user then decides to close down the PPP connection while they read their mail and compose responses. Next they initiate a new PPP connection and incorporate mail again. Note that the user never exited Eudora. Instead of prompting the user for their name and password Eudora will generate and error message. The only way for the user to recover the functionality would be to use Leash, Kview, or kdestroy to destroy their old tickets so that Eudora would get new tickets.

   This happened because many ISPs hand out a new IP address to a user each time that user reconnects to the system. Also a Kerberos ticket includes the machines local IP address in an encrypted form this is used by most severs to insure that the ticket has not been copied to another users machine.

   Since the local IP address is stored in the ticket it seems that it should be easy to compare this data to the machine's local IP address at the same time that an application is checking to see if the ticket has expired. Unfortunately the IP address in the ticket is encrypted in the server's session key and so is inaccessible to the local machine.

   Instead we borrowed an idea from Kerberos version 5 and decided to store the local IP address, unencrypted, in the credential which is cached in the local cache. Within the Kclient function IsCredExpired or the krbv4wxx function kchktkt we verify that the ticket has not expired and that the local IP address matches the IP address stored in the ticket.

   This implies that machines with multiple copies of kclnt32.dll or krbv4w32.dll, of different versions, may encounter unexpected errors when using Kerberized applications. The normal error message generated will be BAD_TKT_FILE_FORMAT or NO_TKT_FILE.

   Users of applications that use other vendors Kerberos implementations may also be affected. E.g. some software from FTP, Inc.

2. Add a new function to Kclient DLL. This function is SendTicketsForService(). It is basically a send_auth type function. Before everyone complains please read the following explanation.

   Qualcomm has been working with Platinum on a 32-bit Kclient which would supports both Kerberos v4 and v5. From what I have heard this is a commercial implementation. It ignores GSS or other abstraction layers above the Kerberos layer that application developers should write to. It keeps its ticket cache in the DLL, as such it will not share the ticket cache with other Kerberos implementations that may reside on the user's system.

   Platinum and Qualcomm decide to add a new API call to the Kclient interface. Eudora uses this new function if it finds a KCLNT32.DLL. In this case it does not use the thunking application KERB16.

   We have duplicated this function in our release of KCLNT32 so that Eudora will not GPF. Please DO NOT WRITE APPLICATIONS TO THIS FUNCTION.

3. We stole an idea from Cornell. If the clock is out of synch when we are trying to obtain a ticket we resynch the clock and try again. We inform the user if this occured.

4. Fixed up some problems relating to DLL initialization. WSAStartup will be called if necessary by a few functions. This was needed to handle some differences in DLL initialization under Win32 when multiple applications were using the DLL at the same time. Also fixed up some initialization of the com_err functions due to similar issues.

5. Added two new functions to leashw32.dll. The first is Leash_set_help_file(char*szHelpFile) which allows an application developer to specify which help file to use from the Dialogbox presented when using the Leash_kinit function. If the argument is NULL the function will check the environment variable KERB_HELP. If this is not set the hard coded value of kerberos.hlp will be used. The other function is Leash_get_help_file which allows an application developer to determine the name of the help file being used. These are defined in lshfunc.c

6. Fix the send_auth so that we do not fail on a null realm. Also detects when an invalid socket descriptor has been passed (special thanks to Eudora 3.0 for providing a test case.)