The Integration of Kerberos V5, AFS, and Windows XP using the AFSLogonShell

 

 

 

 

Rodney M. Dyer

Windows Systems Programmer

Mosaic Computing Group

William States Lee College of Engineering

University of North Carolina at Charlotte

Email: rmdyer@uncc.edu

Web: http://www.coe.uncc.edu/~rmdyer

Phone: (704)687-3518

Help Desk Line: (704)687-3150

FAX: (704)687-2352

Office:  267 Smith Building

 

 

Origin:  February 25, 2003

Last revision:  August 18, 2003

 

 

 

Personal note:   This document is somewhat disordered in it's presentation.  Reading over the document fully will help explain some missing pieces of information from the beginning.  The extent to which I can explain our entire environment is limited due to security considerations, the depth of information needed, as well as changing design considerations.  This is a first attempt and I expect this document to change over time.  Check back for updated revisions if you find this information helpful.

 

 

 

 

Summary

 

            In this document I describe the method we used for setting up single-sign-on, or SSO, with Kerberos V5 authentication, and AFS within our Mosaic 2000/XP Active Directory infrastructure.  This paper is intended for a technical audience already familliar with the concepts of Kerberos, AFS, and Active Directory.  I do not describe in any thorough detail how to perform all of the neccessary steps to put togther a similar computing environment.  For more information on the Mosaic computing system see the somewhat dated "Mosaic White Paper" in the reference section below.

 

 

History

 

            The University of North Carolina at Charlotte has been Internet connected since the late 80s.  We are the class B Internet domain "uncc.edu".  In the early 90's the William States Lee College of Engineering put together a group called Mosaic (unaffiliated with NCSA Mosaic in any way, it's just a coincidence).  The Mosaic group was founded to provide high performance networked computing workstations on the desktops of students, faculty, and staff.  Project Mosaic, as it was called in the early years, was largely a success.  The Mosaic platform was modeled similarly to the Athena project at MIT.  We used Sun workstations running Sun OS, then changed to Sun Solaris a bit later on.  The workstations were networked with the Transarc AFS file system making them extremely secure, scaleable, and because they were UNIX they were easily managed.

 

            In the mid-nineties there was a gradual shift to adding lower cost Windows PC systems sold by Gateway and Dell.  At first, the PCs were networked using NFS and largely managed individually.  This became a nightmare as time went by because the DOS/Windows 3x/9x operating systems were unsecure, buggy, horribly mis-managed by the users, and the number of PCs just kept increasing.  In the spring of '96 we decided it was time to put together a sub-project to bring the PCs in line with the standards set forth using the UNIX environment.  We selected Microsoft Windows NT as the operating system for the PCs because it was very close to operating like UNIX.  NT had security and could be managed similarly to UNIX although many hours were spent creating the tools neccessary to do so.  The driving mission for the PCs was to use the same networked file system, or AFS, that the Sun workstations used.  Early on, we tried using the so called "NFS to AFS translator" but had varying success.  Then, late in '96 Transarc released the first full AFS client for Windows NT.  Now, although we had an AFS client for the PCs, we didn't have a good solution for providing password syncronization between the NT domain servers and the AFS kerberos authentication database.  We ended up coding our own TCP/IP password change services to keep the password databases sync'ed.  We even provided the ability for the PC users to roam from PC to PC using Microsoft Roaming User Profiles, or RUPs.  The back-end AFS file system and services are still run off of Sun UNIX equipment and we still use Sun workstations for more sophisticated engineering applications and student labs.

 

            We rolled out our first set of Mosaic PC workstations using Windows NT 4.0 and AFS in the fall of '97.  Of course we had some troubles early on, but issues were addressed one at a time until they were minimized.  From '97 to mid '2002 we ran the Windows NT 4.0 PC network without much architectural change although management processes were substantially improved.  Around mid 2000 we started looking into our transistion to Windows 2000 Professional and Windows 2000 Server Active Directory.  We projected a release date for the new platform as summer 2001.  As time went by we started adding more and more features into the project list.  One of those features, Kerberos V5, was looking more and more promising.  We wanted to integrate Kerberos, AFS, and Windows 2000 to create a single authentication database.  A bit later along the line we decided to go ahead and make our switch to Windows XP Pro on the desktop.  Because of conversion issues and time constraints we missed our summer 2001 rollout of Windows 2000 Pro on the desktop, which in hind-site was a good thing.  Microsoft was well on the way to releasing Windows XP in the fall of 2001.  We figured we'd just go ahead and roll everything we'd been doing with Windows 2000 into the new XP upgrade, instead of being behind one more time.  This allowed us to gather everything together for a well tested finalized rollout of the new XP architecture in the summer of 2002.  What follows in this document is the information about the integration of Kerberos, AFS, and Windows XP.

 

 

Plans

 

            The holy grail was to go completely to one user authentication database using Kerberos V5.  This would provide single-sign-on, or SSO, as well as increase the security of our infrastructure.  We looked into using Microsoft Windows 2000 Kerberos but it contained too much baggage all rolled up into one.  Windows 2000 integrates a Kerberos V5 compatible authentication system, an LDAP compatible directory service, DNS services, as well as the Microsoft architectures for domain/forest implementations.  All we really wanted was to keep everything modular and simple.  We also didn't want to get our IT department too sticky in the glue of Microsoft technology.  So we decided we would setup a few low cost Sun machines as MIT Kerberos KDCs.  Using several machines would provide load balancing and fail-over, and since they would only be used for authentication they didn't need to be big high-cost high-end machines.

 

            The most important thing to work out was how to get our AFS file system to accept Kerberos V5 credentials.  This wasn't too complicated as AFS has always been based on a slightly tuned older version of Kerberos IV.  There had already been a toolset and information available on the Internet for making just such a thing possible.  The toolset was maintained at a FTP site managed by Ken Hornstein.  See "AFS Kerberos Migration Tool Kits" in the reference section below.  Using the toolset, we setup our MIT Kerberos V5 KDCs and added a service called the "5 to 4" daemon, or simply "524".  This allows the workstation users to obtain a Kerberos V5 TGT using kinit, then run a program called aklog to convert a Kerberos V5 AFS service ticket to a Kerberos V4 AFS file system credential (token).  We did this for our Sun Solaris environment at first without issues, but later found problems when implementing the same solution for the Windows XP clients.

 

 

            Roaming User Profiles

 

            A sticky issue we encountered was that we had implemented "roaming user profiles", or RUPs, on our older Windows NT 4.0 platform.  RUPs allow user account desktop and application settings to follow users around the network as they logon and off of different workstations.  The user data that "roams" is simply stored in a single "profile" subfolder (directory) in the user's home directory file space on the server network share.  When a user logs onto a machine the contents of the "profile" data directory are then copied to the local machine for their session at the workstation.  When the user logs out, the "profile" data contents are then sync'ed back to the user's home directory filespace "profile" folder on the server.

 

            The RUPs worked fine in our Windows NT 4.0 AFS environment.  We had created a global drive that mounts our AFS file space, at workstation boot time, before the user's logged onto the machines.  This global drive to AFS would always be available to all users logging on to the machine as well as being "unmountable" by the users (great for security).  We then specified on the users NT domain accounts that their profiles would be available by referencing their home directory "profile" path using the global drive.

 

Example:  AFS user "rmdyer".

               AFS user home directory "/afs/uncc/usr2/rmdyer".

               RUP path on global network drive "n:\uncc\usr2\rmdyer\nt_profile".

 

            Microsoft NT workstation could not just copy the users RUP data from their AFS accounts without authorization, so we also enabled AFS logon authentication on the workstation.  This is a feature of the AFS client that allows the user to obtain an AFS credential (token) for file system access at logon.  At Windows logon, the user would enter their Windows domain username and password.  The NT workstation winlogon process would then log them on, but before the user's profile downloaded from their home directory, the AFS authenticator would authenticate the users to the AFS filespace.  This allowed Windows NT 4.0 access into the user's "profile" data stored in their home directories in AFS.  The profile was then downloaded and the user's were logged on.

 

To restate...

 

1.   A user enters username and password to authenticate to the local workstation OS.

2.   The local workstation's winlogon process starts the logon process.

3.   The winlogon process calls upon the AFS authenticator "afslogon.dll" to obtain a token for the AFS file system.

4.   The user profile is then downloaded from the user's AFS home directory.

5.   The user's desktop environment and explorer shell is then initialized.

 

 

            Folder Redirection

 

            With our new Windows XP environment we wanted to keep using RUPs as well as add the new "folder redirection" profile option.  Folder redirection allows an administrator to maintain some of the user profile data in AFS while the user is logged on.  The user still uses their profile folders as-if they are all local to the machine, but any data put into those folders gets magically mirrored to the user's network share, or in our case AFS.  This mirroring of data by the way is made possible using Microsoft's Intellimirror services.  Folder redirection helps cut down on the user's logon time and decreases the amount of traffic on the network.  The nicest thing about folder redirection is that an administrator can include the user's desktop folder in the redirection list.  The users can maintain files right on their desktop without having to copy those files back and forth in the user profile at logon and logoff.  See "User Data and Settings Management for Windows XP in a Windows 2000 Environment" in the references section for more information on folder redirection.

 

 

            Problem and Solution

 

            RUPs and folder redirection was a problem however because going to Kerberos V5 would mean that we would no longer be able to use the standard AFS logon authentication as it used Kerberos IV exclusively.  The solution, after months of debate, trial, and error, was to modify the AFS logon authenticator source code "afslogon.c" so that it would be able to perform a Kerberos V5 authentication.  We were of course lucky that we were able to do this as IBM had only recently released the source code for AFS.  At first, we experimented with coding directly into the "afslogon.c" code that performed the authentication, but this quickly became a hassle because of compile-time library issues.  Kerberos and AFS originated in the UNIX world and because of poor library documentation and mis-aligned versioning trying to merge the compile environments of the two is almost impossible.  Then it was realized we could simplify things by performing a system call to "shell-out" and use the existing tools already made to authenticate to Kerberos V5 with minor changes.  Since this would also allow us to perform operations before the users profiles were downloaded out of AFS space, we could also add in management code for administrative purposes.  The "shell-out" operation became known as the AFSLogonShell and was the glue that would bind everything together.  See "The AFSLogonShell" in the references section below.

 

 

            Putting Everything Together

 

The following is a synopsis of what our group did to glue together Kerberos, AFS, and a AD domain Windows XP machine.  While I make many references to our network configuration, you may find that your IT setup operates similarly.  The full AFSLogonShell script "krblogon.cmd" that we use is included in appendix A.  I describe what the script does in detail a bit later on although many people familiar with NT command shell scripts may be able to follow its operation.

 

 

Step 1.  We setup MIT Kerberos V5 authentication servers, also known as Key Distribution Centers (KDCs).

 

As previously stated we used a couple of cheap Sun blade machines for the KDC servers running Solaris 8.  We populated the KDCs with user accounts (principles) as well as service principles for our various services, including AFS.  See "Implementation of Kerberos V5 in the Mosaic Computing Environment" in the references section below for more information.  I will not go into any explanation here on how we generate or manage the MIT principle database, that is beyond the scope of this paper.  See "MIT Kerberos" in the reference section for information on obtaining the software.

 

Note:  A critical piece here is extracting the AFS file system Kerberos 4 service key(s) from the older AFS KA database and moving them into the Kerberos V5 database.  Doing this wan't required until we wanted to create an AFS token from a Keberos V5 TGT.  This was done with AKLOG.EXE.  I won't be describing this until after step 9 below.

 

 

Step 2.  We setup our new Windows 2000 Active Directory domain to group all the Windows XP clients into one administrative unit.  The following is an example...

 

Our new Windows 2000 AD domain is called "mosaic.uncc.edu".  Only the AD servers would be DNS resolved in this domain.  Our first AD server was called "adcsm1" for "Active Directory Controller Smith 1" (Smith is a building name).  The XP clients would resolve in DNS as workstations with hostnames in the upper domain such as "mws100.uncc.edu".  We setup a simple single domain/forest implementation.  A single domain/forest should handle more than enough users for any typical university.

 

By the way, it isn't neccessary to setup a "real" DNS subdomain for the AD domain if you already have an entrenched UNIX DNS system as we did.  What you can do is create an alias DNS name for all of your Microsoft AD servers.  For example, our "real" DNS domain is "uncc.edu".  I didn't want to have to create a "real" authorative sub-domain called "mosaic.uncc.edu" so what I did was create aliases for the servers.  I created the following entries in the upper "uncc.edu" UNIX DNS system...

 

"adcsm1.uncc.edu" resolves to some IP address in our domain (152.15.x.x)

"adcsm1.mosaic" as DNS alias (CNAME record) for "adcsm1.uncc.edu"

 

I did this for all of the other AD servers serving the "mosaic.uncc.edu" domain.  I then setup DNS on the AD servers for the clients and servers of the AD domain to use as a resolver, so that they "think" they are all a part of a "real" authorative sub-domain.  I've not had any trouble with this setup.

 

 

Step 3.  We created user accounts on our AD domain server that matched the AFS and KDC account principle names.

 

The user accounts that we created for the AD domain were the ones that we already used with AFS and Kerberos.  Since in step 4 we create a trust between our Kerberos realm and the AD domain, we added the user accounts using random passwords.  We didn't setup RUPs or folder redirection just yet.  We needed to make sure that the standard AD domain plus Windows XP with AFS authentication was going to work first.  I will not go into any explanation on how to administer a Windows 2000 AD server, that is beyond the scope of this paper.

 

 

Step 4.  We setup a "trust" between the AD domain "mosaic.uncc.edu" and our MIT Kerberos realm.

 

This is so that the Kerberos TGT, obtained when the user's logon to the Windows XP machine, can be used for services on the AD domain.  This is also called "cross-realm" authentication.  This is pretty simple to do, although the Microsoft article on how to do it is not very good.  To perform this see "Step-by-Step Guide to Kerberos 5 (krb5 1.0)" in the reference section below.

 

Important:  Don't forget to add your MIT Kerberos KDC names to the Windows XP machine you are testing.  You do this with "ksetup.exe" from Microsoft.  See "http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B324143" for more information.

 

At our site this was as simple as:

 

ksetup.exe /addkdc UNCC.EDU kdc-sm1.uncc.edu

ksetup.exe /addkdc UNCC.EDU kdc-sm2.uncc.edu

 

Part of the trust setup is ensuring that all the user accounts on the AD server have their Kerberos "Name Mapping" set.  This is also known as the "altSecurityIdentities" property on the user account in the AD directory.  For example under my AD account "rmdyer", the Kerberos name mapping is set to "rmdyer@UNCC.EDU".

 

Note:   The trust can cause problems for Microsoft shares served by the AD servers.  This is because of a Kerberos "replay-packet" bug in the MIT/Microsoft Kerberos implementations.  Microsoft and MIT are currently working this issue out.  See "Bug in Microsoft/MIT Kerberos V5 replay detection breaks AD server shares" in the problems and resolutions section below.

 

 

Step 5.  We then tested the trust we created in step 4.

 

When the trust we created was working, we were able to logon to the Windows XP machine using our Kerberos realm name.  At our site we used "UNCC.EDU" as the realm name of our MIT Kerberos system.  The realm name selection shows up in the WinLogon dialog domain combo-box.  This appears after you add the kdc names to the Windows XP machine using ksetup.exe as described in step 4.

 

 

Step 6.  We then installed and tested the standard AFS client on the Windows XP workstation.

 

We configured AFS logon authentication so that when we logged on to the XP machine, the AFS authenticator "afslogon.dll" would obtain a token for us.  This was out-of-the-box AFS configuration.  This was for testing only, since we would not be using the Kerberos 4 authentication built into the AFS logon authenticator when we finally finished our setup.

 

 

At this point, with a test user account, we were able to...

 

a.  Logon to Windows XP using our Kerberos realm password.

b.  Obtain a normal AFS filesystem token at logon.

c.  Mount a drive to the AFS root as a user.

d.  Access files in our AFS home directory on the mounted drive.

 

Everything done up to this point is out-of-the-box stuff.  Windows XP machines "should" be able to authenticate to MIT Kerberos V5 KDCs.  The Kerberos TGT obtained from the MIT Kerberos V5 KDC should be "trusted" by the Microsoft AD servers.  The AFS client "should" be able to get a token at logon.  There's no magic up to this point.

 

 

Step 7.  We installed the MIT Kerberos for Windows software suite.

 

This is a suite of MIT Kerberos utilities for the PC.  It came with a program called "Leash32.exe" that allowed us to view and manage our MIT Kerberos credentials cache.  We added the directory that contains these tools to our system path.  See "MIT Kerberos" in the references section for information on how to obtain this software suite.

 

We made sure we properly setup our Kerberos configuration files.  At our site we use the following for the "c:\winnt\krb5.ini" file...

 

[domain_realm]

.uncc.edu = UNCC.EDU

uncc.edu = UNCC.EDU

 

[libdefaults]

default_realm = UNCC.EDU

default_tgs_enctypes = des-cbc-crc

default_tkt_enctypes = des-cbc-crc

forwardable = true

renewable = true

proxiable = true

ticket_lifetime = 1500

 

[login]

krb4_convert = false

krb4_get_tickets = false

 

[realms]

UNCC.EDU = {

kdc = kdc-sm1.uncc.edu

kdc = kdc-sm2.uncc.edu

admin_server = kdc-sm1.uncc.edu:749

}

 

And for both the "krb.con" file...

 

UNCC.EDU

UNCC.EDU kdc-sm1.uncc.edu

 

And for the "krbrealm.con" file...

 

.uncc.edu UNCC.EDU

uncc.edu UNCC.EDU

 

 

Step 8.  We setup the MIT Kerberos for Windows credentials cache as an XP service.

 

The MIT Kerberos for Windows suite of tools comes with a memory based credential cache tool called "krbcc32s.exe".  This is extremely nice because it increases the security of the system while at the same time simplifies the proceedure for securing the MIT Kerberos user credentials for administrators.  The "krbcc32s.exe" process can be made to run as a service very easily.  When this is done, each user that is logged on the XP machine will have a separate in-memory K5 credentials cache.

 

To setup the "krbcc32s.exe" binary as a service just use the "srvany.exe" program that comes with the Windows NT/2000 resource kit.  We renamed our "srvany.exe" binary to "kcachesrv.exe" and placed it in the same directory as the suite of tools from the MIT Kerberos for Windows toolset.  We then configured the "kcachesrv.exe" (srvany.exe) to execute "krbcc32s.exe".  This was done with the following registry parameters file...

 

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\kcachesrv\Parameters]

"Application"="\"c:\\admin\\bin\\krb5\\krbcc32s.exe\""

"AppParameters"="-min"

"AppDirectory"="\"c:\\admin\\bin\\krb5\""

 

Note:  Directory paths would obviously vary.

 

We then used the "instsrv.exe" program (also from the NT/2000 resource kit) to install the "kcachesrv.exe" program as a service called "kcachesrv".  The default mode of the service is to auto-start at reboot.

 

Now, for kerberized applications that needed to know where our in-memory MIT Kerberos user credentials are, we setup an environment variable such as...

 

KRB5CCNAME=API:username.krb5cc

(Where "username" is the user logging on to the system.)

 

This would traditionally be done in a user logon script, but we are presented with a dilema here caused by Microsoft's logon architecture.  The dilema is caused by the fact that Microsoft user logon scripts don't run "before" the "userinit.exe" program runs.  The "userinit.exe" program is the process that fires up the logon scripts and the "explorer" shell for the user.  Hence, any environment variable set in the logon script won't be seen by any of the other applications we fire up, including "explorer.exe".  There is a way to solve this problem however, enter our friend AFSLogonShell.

 

Since the AFSLogonShell runs before the "userinit.exe" program, any system-wide environment variables that are created in the shell will be inherited by "userinit.exe".  Using this knowledge, you will see in the "krblogon.cmd" script in appendix A, that we set the KRB5CCNAME environment variable at the beginning of the script for the name of the user logging on.

 

Now, when the user logs on to the machine they will have a KRB5CCNAME environment variable that can be seen by all of the processes they start up.

 

Important:  Since we hadn't reached the point yet where we were running a AFSLogonShell, we just set a system-wide KRB5CCNAME environment variable for the test user that we were using.

 

 

            To test steps 9 & 10, we logged on as a test user that had an AFS account.

 

 

Step 9.  We tested "ms2mit.exe".

 

The "ms2mit.exe" utility is for copying the Kerberos TGT from the Microsoft SSPI credentials cache to the MIT Kerberos credentials cache.  To test this we just ran the "ms2mit.exe" utility, then we checked our MIT Kerberos credentials cache to see if we had a TGT.  We made sure by running "kdestroy" first, then running "ms2mit.exe" again.  This utility came with the MIT Kerberos for Windows toolset.  We made sure we created the KRB5CCNAME environment variable first.  See step 8.

 

 

Step 10.   We tested "aklog.exe".

 

The "aklog.exe" program will create an AFS token from a valid Kerberos V5 TGT.  It will find the K5 TGT using the KRB5CCNAME environment variable.  Just run "aklog.exe" without any options and then type "tokens" to see if it worked.  It is assumed here that you already have a valid K5 TGT generated through either "ms2mit.exe" or by running "kinit.exe".  Either way you need to make sure you have created your KRB5CCNAME variable first.

 

If the "aklog.exe" program worked, then you are well on your way to making the AFSLogonShell work for you.  If it didn't, then something is wrong and needs to be fixed before you continue any further.

 

If "aklog.exe" generated a token, then you might also want to make sure that token is valid for your AFS filesystem by mounting an AFS drive and checking protected directories.

 

Step 11.   We setup a workstation boot script.

 

The workstation boot script mounts a global AFS drive.  See appendix C for how we setup the workstation boot script.

 

 

Now we were ready to add the AFSLogonShell function to the AFS logon authenticator "afslogon.dll".  This allowed us to add in our command shell code that get us an AFS token from a Kerberos V5 TGT.

 

 

Step 12.   We compiled a new AFS logon authenticator "afslogon.dll" with AFSLogonShell function included.

 

See "The AFSLogonShell" in the reference section below for more information on how to do this.

 

 

Step 13.   We configured the AFSLogon.INI file so that the AFSLogonShell would execute the "krblogon.cmd" script.

 

See appendix A for the "krblogon.cmd" script code below.

 

You should realize that many of the "krblogon.cmd" script program and data paths are setup for our environment here at our university.  The astute admin should check the "krblogon.cmd" script to guarantee that everything at their site will work correctly.

 

AS IT IS, THE "KRBLOGON.CMD" SCRIPT WILL ONLY WORK IN OUR NETWORK ENVIRONMENT.  DO NOT ASSUME IT WILL WORK IN YOURS WITHOUT MODIFICATION!

 

There are many things in the "krblogon.cmd" script that need to be understood.  The "krblogon.cmd" script definitely depends on the workstation boot script from step 11.

 

 

Step 14.   We made sure that we set a few important AD group policy settings on the AD server.

 

At our site we have certain preferences for the roaming profile operations.

 

* If the user profile can't be downloaded we don't let the user's logon (obviously something needs to be fixed).  See "AFS Logon Authentication and AFSLogonShell can't prevent logon" under the "Very Important Notes" section below.

 

"Log users off when roaming profile fails"             Enabled

 

* We don't cache roaming profiles, they are deleted from the local workstations when the user's logout.

 

"Delete cached copies of roaming profiles"            Enabled

 

Note:  This works, most of the time, but we've found that when machines crash, or because of other problems, the user profiles are sometimes never cleaned up.  To fix this we just scheduled a cleaning process that runs occasionally when the user's are logged out.

 

* Others, non-specific...

 

"Do not detect slow network connections"             Enabled

"Wait for remote user profile"                              Enabled

"Timout for dialog boxes"                                    Disabled

 

 

 

Logging On with "krblogon.cmd"

 

            Now, the complete Mosaic XP logon proceedure becomes...

 

0.  User presses Ctrl+Alt+Del secure key sequence.

1.  User enters username and password into the Winlogon dialog to authenticate to the Windows XP OS.

Note:  User specifies "UNCC.EDU" kerberos realm as the logon domain.

2.  The Winlogon process authenticates the user to the system using Kerberos V5.

Note:  Windows XP obtains a Kerberos V5 TGT for the user from the MIT KDC and stores that into the Microsoft credential cache.

3.  The Winlogon process calls upon the AFS authenticator "afslogon.dll" to obtain a user token for the AFS file system.

4.  The "afslogon.dll" makes a system call to the command shell script "krblogon.cmd" using the AFSLogonShell function.

5.  The "krblogon.cmd" command shell script does the following...

 

Btw, the user never sees this script execute, it is run hidden without a window and typically takes less than 2 seconds to run.

 

a.   Sets up a logging process for the logon.

b.   Guarantees the AFS client is operating correctly.

c.    Runs kinit.exe to obtain a kerberos V5 TGT using the username and password.

Note:   See "Bug in MIT's version of KINIT.EXE prevents reading passwords from stdin" under the problems section below.

d.   Runs aklog.exe to create a user token from the K5 TGT for the user logging on.

Note:   See "Can't set a user token with AKLOG.EXE from within the WinLogon process" under the problems section below.

e.   Runs aklog.exe to create a user token from the K5 TGT for the winlogon process user SYSTEM.

f.    Verifies the token obtained for user SYSTEM.

g.   Gets user's home directory path from the UNIX passwd file.

h.   Verifies user home path exists on the global drive.

i.    Verifies user is within AFS quota tolerance.

j.    Creates a U: (unix) drive that becomes the root of user's home directory.

k.    Verifies profile and folder redirection directories exist and creates them if neccessary.

l.    The "krblogon.cmd" command shell script ends, which ends the AFSLogonShell function.

 

6.  The "afslogon.dll" authenticator process ends allowing the Winlogon process to continue.

7.  The user profile is downloaded from the user's AFS home directory.

8.  Folder redirection is created using redirection setup in AD group policy.

9.  The user's desktop environment, or explorer shell, is initialized.

 

At this point the user is logged on with a token that was obtained using Kerberos V5.  The Microsoft credentials cache contains a valid trusted Kerberos V5 TGT that can be used to create a MIT credentials cache Kerberos TGT using the tool "ms2mit.exe" in a regular user logon script (not the AFSLogonShell script).

 

And, that is the story of how we process user logons in our Mosaic XP environment here in our IT shop.  Obviously there's more to the story than this, but most of the important stuff is here.

 

The AFSLogonShell glues together Kerberos V5, AFS, roaming Profiles, folder redirection, and takes care of issues such as afs service not running when user logs on, folder redirection issues, "local settings" folder problems (see below).  In fact, any problem that needs to be taken care of in a programmatic way, before the user's profile downloads, can be taken care of with the AFSLogonShell.

 

 

Futures

 

Plans for modifying the "krblogon.cmd" script to take care of checking network connectivity problems more extensively at logon (such as pinging the AFS servers).  The Windows AFS client has some stability problems that need to be addressed.  I can only hope the OpenAFS group fixes these issues.

 

 

Very Important Notes

 

"AFS Logon Authentication and AFSLogonShell can't prevent logon"

 

When a user types in their username and password into the Winlogon dialog then presses OK, the logon process is started after Microsoft workstation authentication takes place.  The logon process once started cannot normally be stopped, either by the AFS logon authenticator, or by the AFSLogonShell add-in.  The purpose of the AFS authenticator in the first place is only to get you a AFS credential (token) at logon, not to prevent logon from continuing.

 

If you are using the AFSLogonShell to create a token from a Kerberos V5 TGT and something goes wrong, you might want to stop the logon process.  There is a way to do this.

 

In a pure Microsoft environment, immediately after logon, the profile would be downloaded from the server share and the environment would be initialized.  In the case where the profile can't be downloaded the workstation normally creates a temporary profile and logon continues.  But, you can set an AD group policy that will stop the logon if the profile can't be downloaded.

 

"Log users off when roaming profile fails"             Enabled

 

When using this policy the AFSLogonShell script can just fail to provide a valid AFS token.  So in this case the workstation doesn't have rights to get into the user's AFS home directory to retrieve the profile.  The workstation can't download the profile so it logs the user off after the AFSLogonShell script ends.

 

 

            "Can't set a user token with AKLOG.EXE from within the WinLogon process"

 

Important late news:  A new version of AKLOG called AFSK5Log is now available at my website "http://www.coe.uncc.edu/~rmdyer".  The AFSK5Log performs the same function as AKLOG but has the additional capability to set a user logon token.  This is done with the "-stlgtk" option.  This means that you don't need to build your own version of AKLOG, as described here, to take care of this problem.

 

Problem description...

 

Ah, here's a sticky issue.  Assuming you've got a valid Kerberos V5 TGT and you want to create a token then you would normally just call "aklog.exe" right?  Right, well sort-of.  The problem is that "aklog.exe" will normally just create the token for the user who's process space you are executing "aklog.exe" in.  This is fine if we are a normal user, or if we are inside the AFSLogonShell and we want to get a logon user token for the user SYSTEM.  But, if you are running as user SYSTEM and you want to create a token for the user logging on, in their process space, how do you do that?  A little snooping in the normal "afslogon.c" will show you the following code line...

 

Beginning of NPLogonNotify code of "afslogon.c"

.

.

.

  code = ka_UserAuthenticateGeneral2

  (

     KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,

     uname,

     "",

     cell,

     password,

     uname,

     0,

     &pw_exp,

     0,

     &reason

  );

.

.

.

Ending of NPLogonNotify code of "afslogon.c"

 

You notice those flags to the call ka_UserAuthenticateGeneral2?  Well one of them, "KA_USERAUTH_AUTHENT_LOGON" looks a little fishy.  Looking at the OpenAFS header files finds... "kautils.h" which contains...

 

#define KA_USERAUTH_AUTHENT_LOGON     0x100000

 

Ok, so let's actually look into the ka_UserAuthenticateGeneral2 function and see what's going on.  The function exists in "user_nt.c".

 

Beginning of ka_UserAuthenticateGeneral2

.

.

.

  code = ktc_SetToken(&server, &token, &client,

     (flags & KA_USERAUTH_AUTHENT_LOGON) ? AFS_SETTOK_LOGON : 0);

.

.

.

Ending of ka_UserAuthenticateGeneral2

 

Ok, we see that the OpenAFS coders are either setting a value of 0 (zero), or the define "AFS_SETTOK_LOGON" based on the bit flag "KA_USERAUTH_AUTHENT_LOGON".  Searching for the define "AFS_SETTOK_LOGON" results in a file called "auth.h" which contains...

 

#define AFS_SETTOK_LOGON     0x2       /* invoked from integrated logon */

 

Ok, it's the value of 2, this is just what we need.  Why?  Because the "aklog_main.c" code needs to be modified so that it will do either a normal SetToken for the current user process space, or a SetToken for the user's process space who is currently logging on.

 

The following code is what I did to modify "aklog_main.c".  I didn't want to create two different versions of "aklog.exe".  I wanted one binary that could be made to perform both types of token setting.  So at the point where the token needs to be set I decided to check for a new environment variable called "SetUserToken".  If the "SetUserToken" environment variable doesn't exist, then we perform the normal token setting for the current process space.  If the "SetUserToken" environment variable exists, then we set the token for the user logging on.  You will see in one of the SetToken calls below where the value of 2 is used.  I just hardcoded the value because I didn't wan't to increase the complexity of the compile-time environment.  Here's the code.

 

Note:  You see that the single original code line is remarked out.

 

Beginning of auth_to_cell function code in "aklog_main.c"

.

.

.

#else /* WINDOWS */

  /* Note switched 2nd and 3rd args */

#ifdef PRE_AFS35

  if ((status = ktc_SetToken(&aserver, &aclient, &atoken, afssetpag))) {

#else

   

//------------------------

// original code line

//------------------------

 

// if ((status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag))) {

 

//------------------------

// begin new code

//------------------------

 

     // the next line needs to be un-remarked for OpenAFS v1.2.3 and up.

     //strcpy( aclient.smbname, username );

    

     // check if this thread is running in MprNotify child process

     if ( getenv( "SetUserToken" ) != NULL )

     {

       // this thread is running as a child process of the MprNotify

       // logon provider as user system so...

       // set token in WlMprNotifyUserName's cache

       status = ktc_SetToken(&aserver, &atoken, &aclient, 2);

     }

     else

     {

       // this thread is running as an ordinary logged on user

       // set token in current user's cache

       status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag);

     };

 

     if (status) {

 

//------------------------

// end new code

//------------------------

 

#endif

      switch(status) {

      case KTC_INVAL:

     sprintf(msgbuf, "%s: Bad ticket length", progname);

     break;

      case KTC_PIOCTLFAIL:

     sprintf(msgbuf, "%s: Unknown error contacting AFS service",

       progname);

     break;

      case KTC_NOCELL:

     sprintf(msgbuf, "%s: Cell name (%s) not recognized by AFS service",

       progname, realm_of_cell);

     break;

      case KTC_NOCM:

     sprintf(msgbuf, "%s: AFS service is unavailable", progname);

     break;

      default:

     sprintf(msgbuf, "%s: Undocumented error (%d) contacting AFS service", progname, status);

     break;

      }

      params.pstderr(msgbuf);

      status = AKLOG_TOKEN;    

  }

#endif /* !WINDOWS */

.

.

.

Ending of auth_to_cell function code in "aklog_main.c"

 

 

So, long story short, if you intend on using the AFSLogonShell to provide authentication for the user logging on to the machine, as well as provide the user SYSTEM a user token, you will need to make this change to the Windows version of "aklog.exe" and recompile.  Appendix A contains a version of our "krblogon.cmd" script that the AFSLogonShell calls.  You will see in this script that we use the "aklog.exe" call twice, once for the user, and another for user system.  Remmember here that which token you set depends on the existance or non-existance of the environment variable "SetUserToken".  If this variable exists, then a token will be set for the user logging on.  If the variable doesn't exist, then the "aklog.exe" program will operate as normal.  This is because of the change we made to the aklog.exe code above.

 

 

            "Bug in MIT's version of KINIT.EXE prevents reading passwords from stdin"

 

The version of "kinit.exe" that compiles for Windows from the MIT Kerberos source code doesn't accept input from stdin.  Of course this was a surprise to me when we wanted to authenticate users from within the AFSLogonShell.  According to MIT sources this was intentional, it is not a bug.  MIT doesn't want people to be able to send passwords to "kinit" through a pipe.  They make some valid points.  See...

 

https://lists.openafs.org/pipermail/openafs-info/2003-August/010379.html

 

However, I believe that stdin to "kinit" should be allowed, and since the source is open there's little MIT can do to prevent you from changing it.

 

We spent some time looking for the problem and found it.  What is presented here is a temporary hack to make stdin work.  It isn't the most optimal solution, but it does suffice.

 

You will need to make the following single line change to the file "prompter.c"...

 

Beginning of krb5_prompter_posix function

.

.

.

#if defined(_WIN32)

 

#include <io.h>

 

KRB5_DLLIMP krb5_error_code KRB5_CALLCONV

krb5_prompter_posix(krb5_context context,

         void *data,

         const char *name,

         const char *banner,

         int num_prompts,

         krb5_prompt prompts[])

{

    HANDLE    handle;

    DWORD   old_mode, new_mode;

    char    *ptr;

    int       scratchchar;

    krb5_error_code          errcode = 0;

    int       i;

 

    // Rodney's change for making kinit accept pipes from stdin

    //handle = GetStdHandle(STD_INPUT_HANDLE);

  handle = CreateFile( "CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );

  // end Rodney's change

 

    if (handle == INVALID_HANDLE_VALUE)

  return ENOTTY;

    if (!GetConsoleMode(handle, &old_mode))

  return ENOTTY;

 

    new_mode = old_mode;

    new_mode |=  ( ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT );

    new_mode &= ~( ENABLE_ECHO_INPUT );

 

    if (!SetConsoleMode(handle, new_mode))

  return ENOTTY;

.

.

.

Ending of krb5_prompter_posix function

 

As you can see a single line of code is remarked out, and another line is put into it's place.  Very little changed.

 

 

 

            "Shouldn't use XP command shell ECHO for sending password to KINIT.EXE"

 

For security reasons your user passwords should not contain characters that might escape a shell script.  But, even if they don't it is a good idea never to use the "echo" command to send the password to a pipe.  What we did was create a very small C program called "envout.exe" that takes the name of an environment variable as an argument.  The "envout.exe" program will then send the contents of the environment variable to stdout.  You see this in the "krblogon.cmd" script from appendix A.  Here's the line...

 

envout WlMprNotifyPassword | kinit -5 -V -l 7d %WlMprNotifyUserName%@UNCC.EDU

 

And here's the Microsoft Visual Studio 6.0 C code for the "envout.exe" program...

 

#include <stdio.h>

#include <stdlib.h>

 

int main( int argc, char *argv[] )

{

  char *ptr_char_Buffer = NULL;

 

  ptr_char_Buffer = getenv( argv[1] );

 

  if ( ptr_char_Buffer == NULL )

     return EXIT_FAILURE;

 

  fprintf( stdout, "%s", ptr_char_Buffer );

 

  return EXIT_SUCCESS;

}

 

 

            "Bug in Microsoft/MIT Kerberos V5 replay detection breaks AD server shares"

 

This problem is very annoying since Microsoft should have caught this problem in development.  It basically boils down to this...if you set your AD domain to trust a MIT Kerberos realm, then you can sometimes be denied access to Microsoft's own file shares off of the AD domain.  The problem is apparently caused by MIT and Microsoft disagreeing about just how replay detection should be done.

 

For more information see the following web pages...

 

"http://mailman.mit.edu/pipermail/kerberos/2002-May/000788.html"

"http://mailman.mit.edu/pipermail/krb5-bugs/2002-November/000349.html"

"http://mailman.mit.edu/pipermail/krb5-bugs/2002-December/000377.html"

 

The fix for this problem is avaiable now by contacting Microsoft Product Support Services, or it should be included in XP SP2 when it becomes available.

 

Here is the specific hotfix document...

 

"http://www.coe.uncc.edu/~rmdyer/Q811802_hotfix_info.htm"

 

 

            "Folder redirection bugs"

 

If you setup folder redirection to a user's AFS filespace you may find that it doesn't quite work as planned.  The problem will show up in the event viewer as error messages stating that the folders can't be created or redirected.  In our IT shop we wanted the operating system to create the redirection folders in the user's AFS filespace for us, or we thought that it would.

 

We found after experimenting that the folders were being created, just not reliably for new accounts.  For the first logon only one folder would be created.  The second logon might create another folder, and so on.  It might take a few times logging on to get all the folders created.  We didn't like what was going on.  We found that there was no good way to solve this problem except to create the folders ahead of time for new users.  We do this in the "krblogon.cmd" script we run via the AFSLogonShell.  In the "krblogon.cmd" script we check to make sure at each logon that all the folders exist in the user's account.  If any folder is missing we create it.  This effectively stopped the problem.

 

 

            "Windows XP excludes too much profile data under the "Local Settings" folder"

 

When we were using roaming profiles on Windows NT 4.0, the profile save at logout saved most everything under the user's local profile directory (c:\winnt\profiles\user) to AFS.  But, when we migrated to Windows XP we found changes that Microsoft made to the profile system.  Microsoft made these changes at the requests of administrators who were finding that user profiles could grow very large.

 

Basically the change Microsoft made to the profile system when they moved to the Win2k architecture was the following...

 

* The following directories and files are excluded from saving to the user's network profile share...

 

"Local Settings"

"Temporary Internet Files"

"History"

"Temp"

"Local Settings\Application Data\Microsoft\Outlook"*

 

* Outlook registry path added when you install Office XP.

 

This means that the user's IE web browse history isn't roamable.  This means that some application settings aren't roamable.  This especially caused our shop trouble because we setup the AD group policy so that all user profile information would be deleted from a XP machine when the user logged out.

 

We found that even certain Microsoft applications appeared not to know this change.

 

For example...

 

*  If you want to use a JPEG as background wallpaper, XP converts the wallpaper to a BMP file and stores it in "Local Settings\Application Data\Microsoft\Wallpaper1.bmp" so when you logout and move to another machine, the bitmap won't be there.  Microsoft has confessed to us through bug support that this is a problem.  Watch for a fix in SP2.

 

*  Microsoft Visual Studio 7.0 (.NET) stores some of the user's preferences under "Local Settings\Application Data\Microsoft\VisualStudio\7.0\VCComponents.dat".  So, when the users logout and move to another machine, they won't be there.  Don't know when this will be fixed.

 

*  Microsoft Outlook Email Wizard creates the user's standard mailbox in "Local Settings\Application Data\Microsoft\Outlook\Outlook.pst".  So, when the users logout and move to another machine, they won't be there.  I can't see a fix for this one on the horizon.

 

Microsoft's early response on these issues was to modify the following registry key entry...

 

[HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Winlogon]

"ExcludeProfileDirs"="Local Settings;Temporary Internet Files;History;Temp;Local Settings\\Application Data\\Microsoft\\Outlook"

 

This registry option, which is the out-of-the-box default of XP, allows administrators to exclude (or remove from being exluded) certain user profile folders.  This is to save space on the network profile share.  The problem is, if you remove the "Local Settings" folder from the exclusion list you are in for a world of trouble.  Starting with Windows 2000 Microsoft moved the "Application Data" folder into "Local Settings" folder.  This wouldn't be a problem except that now Microsoft applications store "gobs" of data into the "Application Data" directory.  If you remove it from the exclusion list you are looking at a very large profile indeed.

 

The solution to this problem involves the following...

 

At our site, we keep our user's redirected profile folders under a different folder in the user's AFS file space called "win_data".  Microsoft recommends this for good reasons.

 

For example, where the user's profile path is...

 

"n:\uncc\usr2\rmdyer\xp_profile"

 

We keep the folder redirections under...

 

"u:\pc\win_data"

 

So that we have...

 

"u:\pc\win_data\Desktop"

"u:\pc\win_data\Application Data"

"u:\pc\win_data\My Documents"

"u:\pc\win_data\My Music"

"u:\pc\win_data\My Pictures"

 

The "U:" drive is created by a "subst.exe" in the AFSLogonShell process.  So that...

 

"u:" -> "n:\uncc\usr2\rmdyer"

 

Now, when the user logs off, a user logout script "loggoff.cmd" saves the following under the "win_data" directory.

 

"Local Settings\History"

"Local Settings\Application Data\Microsoft\Wallpaper1.bmp"

"Local Settings\Application Data\Microsoft\VisualStudio\7.0\VCComponents.dat"

"Local Settings\Application Data\Microsoft\Outlook\Outlook.pst"

 

When the user logs back on we do the following in our AFSLogonShell script "krblogon.cmd"...

 

  :: move local settings profile data into place

  nlfecho Checking "Local Settings"...

  if exist "u:\pc\win_data\Local Settings" (

       echo done.

       (if exist "u:\xp_profile\Local Settings" (

            nlfecho Removing "Local Settings"...

            (rd "u:\xp_profile\Local Settings" /s /q >> "%logon_errors_log%" 2>>&1 || (

                 echo error.

                 echo Unable to remove folder.

                 goto debug

            ))

            echo done.

       ))

       nlfecho Moving "Local Settings"...

       (move "u:\pc\win_data\Local Settings" "u:\xp_profile" >> "%logon_errors_log%" 2>>&1 || (

            echo error.

            echo Unable to move folder.

            goto debug

       ))

  )

  echo done.

 

What this script code basically does is move the "Local Settings" folder into the user's profile directory before the profile is downloaded at logon.  It is written so that it only does it once, and has some checking to make sure that there will only be one copy of the "Local Settings" folder at a time.

 

You might be saying "This seems silly, why so complicated".  The answer is that this method allows us to exactly define on our own terms what files/folders from the "Local Settings" folder, under the user's "Documents and Settings" local profile, get saved into the user's AFS space.  That's a mouthfull, you might want to read that close again.  And, we don't have to change Microsoft's default settings for the excluded files and folders.

 

This is only a hack anyway until Microsoft can figure out a way to deal with these issues.  It solved the problem for us for over 4000 users.  The only reason we were able to pull this off was because we use the AFSLogonShell for scripting things before the XP machine logs user's on.

 

 

            "Windows XP Service Pack 1 breaks roaming profiles with AFS"

 

Installing Service Pack 1 for Windows XP breaks the ability to download profiles from AFS space when a user logs on.  This is caused by a change in the way Microsoft validates filesystem rights on the profile directory.  See the following web reference for the fix...

 

"Windows XP SP1 checks permissions on an existing profile folder when a roaming profile is created?"

http://www.jsifaq.com/SUBL/tip5700/rh5745.htm

 

also,

 

"Windows XP SP1 Checks for Existing Roaming User Profile Folders When a Roaming User Profile Is Created"

http://support.microsoft.com/default.aspx?scid=KB;EN-US;q327462&

 

 

 

References

 

 

            The Mosaic White Paper

            The William States Lee College of Engineering

            The University of North Carolina at Charlotte

            http://www.coe.uncc.edu/mosaic/mosaic_whitepapers/Mosaic_Overview.pdf

 

 

            Implementation of Kerberos V5 in the Mosaic Computing Environment

            Mike Mosley, Mosaic Solaris Systems Programmer

            http://www.coe.uncc.edu/mosaic/staff/Kerberos5_Jan25.pdf

 

 

            The AFSLogonShell

            Rodney Dyer, Mosaic Windows Systems Programmer

            http://www.coe.uncc.edu/~rmdyer/afslogonshell.rtf

 

 

The Windows 2000/XP Command Shell

Microsoft

http://www.microsoft.com/technet/prodtechnol/windowsserver2003/proddocs/server/ntcmds_o.asp

 

 

OpenAFS Organization

http://www.openafs.org/

 

 

IBM, Pittsburgh Lab (Transarc)

AFS Client Support

http://www.transarc.ibm.com/Support/afs/

Documentation...

http://www.transarc.ibm.com/Library/documentation/afs/3.6/unix/en_US/HTML/index.htm

 

 

MIT Kerberos

Kerberos: The Network Authentication Protocol

http://web.mit.edu/kerberos/www/

Source and Binaries...

http://web.mit.edu/network/kerberos-form.html

 

 

AFS Kerberos Migration Tool Kits

Site Managed by Ken Hornstein?

http://www.cmf.nrl.navy.mil/CCS/people/kenh/kerberos-faq.html#kerbafs

Sources for krb524, aklog, and Windows binaries...

ftp://ftp.cmf.nrl.navy.mil/pub/kerberos5/

 

 

Step-by-Step Guide to Kerberos 5 (krb5 1.0)

Microsoft Windows 2000 Web Site

http://www.microsoft.com/windows2000/techinfo/planning/security/kerbsteps.asp

More...

http://www.microsoft.com/windows2000/techinfo/howitworks/security/kerbint.asp

http://www.microsoft.com/windows2000/techinfo/howitworks/security/kerberos.asp

http://msdn.microsoft.com/msdnmag/issues/0500/security/print.asp

 

 

User Data and Settings Management for Windows XP in a Windows 2000 Environment

Microsoft Windows XP Web Site

http://www.microsoft.com/WindowsXP/pro/techinfo/administration/userdata/default.asp

 

 

Transarc Versions of Windows AFS Client

(OpenAFS appears not to support the feature(s) documented here)

Transarc Report Number:  54630

Title:  "Does AFS support roaming profiles, and how do they work?"

http://www.transarc.com/TRACS/afs/54630

 

 

Answers to Frequently Asked Kerberos Questions

Microsoft Support

http://support.microsoft.com/default.aspx?scid=KB;en-us;q266080

 

 

Windows 2000 Kerberos at University of Michigan

http://www.umich.edu/~lannos/win2000/w2k-kerberos.html

 

 

MIT's Project Pismere

http://web.mit.edu/pismere/

http://web.mit.edu/pismere/draft-documents/earlylook/earlylook.html

 

 

Paul B. Hill at MIT

http://mit.edu/pbh/www

 

 

Kerberos and Windows 2000

"Kerberos interoperability issues", Paul B. Hill, Massachusetts Institute of Technology

http://www.usenix.org/events/lisa-nt2000/hill/hill_html/

 

 

NT Gina Information

http://web.mit.edu/cartel/ntgina.html

 

 

USC/ISI Kerberos Information

http://gost.isi.edu/info/Kerberos/

 

 

The Kerberos Network Authentication Service (V5)

http://www.isi.edu/people/bcn/draft-kerberos-rev.html/krbrev1-3.html

 

 

Kerberos WG (krb-wg)

http://www.kerberos.isi.edu/

 

 

Guide to Windows 2000 Kerberos Security Settings

Systems and Network Attack Center (SNAC), NSA

http://nsa2.www.conxion.com/win2k/guides/w2k-16.pdf

 

 

AFS Consulting

Sine Nomie Associates, Inc.

http://www.sinenomine.net/afs.php

 

 

            MIT's Project Athena

            http://www-tech.mit.edu/V119/N19/history_of_athe.19f.html

 

 

 

Appendicies

 

 

A)  AFSLogonShell Script "krblogon.cmd"

 

About the script...

 

This script is an XP command shell script.  It makes extensive use of extended features within the XP command shell such as the line continuation characters "(" and ")", the expanded FOR syntax, environment variable expansions, intermediate variable expansions, subroutine creation, numeric processing, and others.  See "The Windows XP Command Shell" in the references section for more information.

 

At our IT shop, we use the double-colon "::" as a remark statement.  Microsoft has never recognized this as a standard but may be used in most situations.  You must be careful because the double-colon doesn't work within line continuations.  To remark something inside of line continuations you must use the "REM" statement.

 

Example...

 

fails...

(

            :: this won't work here

            echo Hello World!

)

 

works...

(

            REM this works here!

            echo Hello World!

)

 

This script sets up extensive logging for the entire process.  You can see some of the example logs in appendix D.  The style of logging at our site is as follows...

 

Output a line of text without line-feed explaining what is being done.  Then perform the process outputting all stdout and error information of the process to an alternate file.  If no error, then output "done." with a line feed.  If an error occured, then output the word "error." with a line feed, then on the next line display what failed as a general text message.  After that, we might go to our failure routine which then processes the result of the command file that was created by piping the command output to the file.

 

Example...

 

nlfecho Doing something here...

test.exe >> %error_file% 2>>&1 || (

   echo error.

   echo An error occured during test.

   goto failure

)

echo done.

 

Note:  The double-pipe "||" means "on failure".

 

I prefer to code batch files so that they are readable as in most languages with indentions.  You can get into a lot of trouble when you start looking at line continuation characters as if they imply "indented blocked proceedures", THEY ARE NOT.  The line continuation characters in batch simply pull everything they contain "into the main line" before execution.  To restate...the command shell language pulls everything into one-line then evaluates that string as an entire entitiy from left to right before execution.

 

Ergo, the following does not work as you expect...

 

Consider that environment variables "a" and "ab" are undefined when the script is run.

 

(

      set a=a

      set ab=%a%b

)

 

You might think that the resulant "ab" environment variable might contain the letters "a" and "b" consecutively, but it does not.  The command shell first turns the above into the string...

 

set a=a&set ab=%a%b

 

...and then performs the environment variable replacements...

 

%a% is replaced with nothing.  Remmember it isn't defined yet because the line hasn't been executed.

 

...and then it executes the line from left to right.

 

You will note in this circumstance that the "a" environment variable hasn't yet been defined before the line is executed.  The line is executed from left to right, but the environment vaiables are only evaluated once before any execution is done.

 

The latest command shell from Windows 2000 can deal with such situations.  These situations are called "delayed environment variable expansion" situations.  Go to the command shell and type set /? for more information.

 

The "nlfecho" is a special version of echo that does not output a line feed character.

The "ntregedt.exe" program just sets registry values.

The "cmdmail.exe" program is just a command line mailer.

 

We setup the AFSLogonShell so that our "krblogon.cmd" script runs hidden.  If everything works perfectly during logon then the script usually finishes within about one second and the user never sees anything but the profile download message.

 

If something goes wrong however we need to display a visual to the user that things aren't quite working.  The "logondisp.exe" program is a GUI interface to show the user what is wrong if something goes wrong during the script.  In the event something is wrong that can be fixed by the script then the visual looks like this...

 

 

You see a progress bar in the dialog that lets the user know that the problem is being fixed.  If however the problem can't be fixed then a more informative display shows up...

 

Note:  In the previous dialog the "Details" button would show the user something similar to the following also.

 

 

As you can see our "logondisp.exe" program actively monitors and displays the resulting log file generated from the "krblogon.cmd" script.  This is nice because it allows an administrator to monitor the failures that may be occuring without much complication.  These dialogs create a file called the "logondisp.lock" and they watch for it every second.  When it goes away, the dialog process ends.  When the "krblogon.cmd" script is finished showing the dialogs it will delete the "logondisp.lock" file.

 

Sometimes when we have a logon error the following error dialog is displayed overlaying the above dialogs...

 

 

We can display standard Windows error dialogs from within the AFSLogonShell with the use of our "cmdmsgbox.cmd" helper app.  This app is nice because it is able to return the user button selections to the command shell via a cmdmsgbox environment variable.  If we need to ask the user a question during the processing of the "krblogon.cmd" script, this comes in real handy.  It can also be set so that the dialog "times-out" after a certain time, if the user never answers.  You may find this utility handy also so I will document it in the Tools and Utilities in appendix E.

 

The global environment variable "ntnet" (referenced in the krblogon.cmd script) points down into our AFS global drive to a subdirectory containing our PC-centric files (non-unix).  We call this the PC system directory.

 

I could say much more but the script is pretty well commented so you might get what is going on.

 

One note about the logon subroutine is in order.  When the call is made to the logon subroutine I redirect all output to the "logon_session_log" file.  This is an NT command shell method of outputting all standard output from within a subroutine to a file.  This allows me to just use non-directed echo's within the logon subroutine.

 

For help on command shell subroutine syntax type "help call" at the command line.

 

IMPORTANT:  There are some lines that wrap around in this script because of the way it is formatted within this document.  It is best to copy the text script and save it to a file, then edit it with notepad or some other text editor.

 

The script...

 

@echo off

 

::----------------------------------------------------------------------------------------

:: KRBLOGON.CMD

:: Kerberos Logon Script

::

:: Purpose

::

:: This is a versatile script used for various purposes.  The primary purpose of this

:: script is to obtain an AFS token for the user logging onto the system by performing a

:: "kinit" then a "aklog".  A secondary purpose is to obtain an AFS token (of the user

:: logging onto the system) for user SYSTEM so that certain operations can be performed in

:: the AFS home directory of the user before the user's profile downloads from AFS.

::

::-----------------------------------------------------------------------------------------

 

:begin

 

     :: get out if this is not a MprNotify thread

     if not defined WlMprNotifyUserName goto :eof

 

     :: otherwise set credentials cache name and environment to normal

     set KRB5CCNAME=API:%WlMprNotifyUserName%.krb5cc

     ntregedt.exe ADDVALUE HKEY_LOCAL_MACHINE "System\CurrentControlSet\Control\Session Manager\Environment" "KRB5CCNAME" REG_SZ    "%KRB5CCNAME%"

 

     :: get out if not a normal user

     if /i "%WlMprNotifyUserName%"=="Administrator" goto :eof

 

     :: setup variables

     set logon_disp_lock=c:\temp\logondisp.lock

     set logon_log=c:\admin\log\logon.log

     set logon_errors_log=c:\admin\temp\~logon_errors.log

     set logon_session_log=c:\admin\temp\~logon_session.log

 

     :: remove errors_log and session_log

     if exist "%logon_errors_log%" del "%logon_errors_log%"

     if exist "%logon_session_log%" del "%logon_session_log%"

 

     :: if logon_log size is greater than 2 Meg then

     :: backup the current one and start a new one

     for %%i in (%logon_log%) do (

          if %%~zi GTR 2000000 (

              copy "%logon_log%" "%logon_log%_old"

              del "%logon_log%"

          )

     )

 

     if /i "%1"=="logon" (

          call :logon >> "%logon_session_log%"

          echo. >> "%logon_log%"

          echo ------------------------------------------------------------------------------ >> "%logon_log%"

          type "%logon_session_log%" >> "%logon_log%"

          goto :eof

     )

 

     echo Invalid argument.

     goto :eof

 

 

::-------------------------------------------------------------------------------------------------

:: logon (subroutine)

::-------------------------------------------------------------------------------------------------

 

:logon

 

     echo Logon:  %date% %time%

     echo Accnt:  %WlMprNotifyUserName%

     echo Process Information:

 

     :: remove substitution of previous logon user's u: drive

     nlfecho Removing old U: drive...

     for /f "tokens=1 delims=:" %%i in ('subst') do (

          if /i "%%i"=="U" (

              subst U: /d  >> "%logon_errors_log%" 2>>&1 || (

                   echo error.

                   echo Unable to remove U: drive substitution.

                   goto debug

              )

          )

     )

     echo done.

 

     :: make sure afs drive and service are ok

     call :afscheck

     if defined result goto debug

 

     :: get ticket granting ticket

     nlfecho Performing kinit...

     envout WlMprNotifyPassword | kinit -5 -V -l 7d %WlMprNotifyUserName%@UNCC.EDU >> "%logon_errors_log%" 2>>&1 || (

          echo error.

          echo The kinit process returned an error.

          goto debug

     )

     echo done.

 

     :: remove the error file generated by kinit (data no longer needed)

     if exist "%logon_errors_log%" del "%logon_errors_log%"

 

     :: get logon user token into cache manager

     nlfecho Performing user aklog...

     set SetUserToken=True

     aklog

     echo done.

 

     :: get user token for system account

     nlfecho Performing system aklog...

     set SetUserToken=

     aklog

     echo done.

 

     :: check for token

     set tokenname=

     nlfecho Parsing AFS token...

     for /f "skip=2 tokens=1,2 delims= " %%i in ('tokens') do (

          if /i "%%i"=="user" (

              for /f "tokens=1 delims='" %%k in ("%%j") do (

                   set tokenname=%%k

              )

          )

     )

 

     :: check to see if tokenname exists

     if not defined tokenname (

          echo error.

          echo Unable to get username from AFS token.

          tokens >> "%logon_errors_log%" 2>>&1

          goto debug

     )

     echo done.

 

     :: does username equal token name?

     nlfecho Verifying AFS token...

     if /i not "%WlMprNotifyUserName%"=="%tokenname%" (

          echo error.

          echo The token name does not match!

          tokens >> "%logon_errors_log%" 2>>&1

          goto debug

     )

     echo done.

 

     :: create drive for folder redirection

     set fr_homepath=

     nlfecho Finding AFS home directory...

     if not exist "\xxxx\passwd" (

          echo error.

          echo Unable to access AFS UNIX passwd file.

          goto debug

     )

 

     :: get user's home path

     for /f "tokens=6 delims=:" %%i in ('findstr /i /b /c:"%WlMprNotifyUserName%:" \xxxx\passwd') do set fr_homepath=%%i

 

     if not defined fr_homepath (

          echo error.

          echo User not found in passwd file.

          goto debug

     )

     echo done.

 

     :: remove the /afs and convert forward slashes to backslashes

     set fr_homepath=%fr_homepath:/afs=%

     set fr_homepath=N:%fr_homepath:/=\%

 

     echo Homedir=%fr_homepath%

 

     :: check for existance of home path

     nlfecho Checking AFS home directory...

     if not exist "%fr_homepath%" (

          echo error.

          echo The home directory doesn't exist.

          goto debug

     )

     echo done.

 

     nlfecho Checking user AFS quota...

 

     :: get entire second line of fs lq output

     for /f "skip=1 tokens=*" %%i in ('fs lq "%fr_homepath%" 2^>^> "%logon_errors_log%"') do set fs_lq_output=%%i

 

     if not defined fs_lq_output (

          echo error.

          echo Unable to process output of fs lq.

          goto debug

     )

 

     :: parse fs lq output

     for /f "tokens=1,4 delims= " %%i in ("%fs_lq_output%") do (

          set afs_volume=%%i

          set afs_quota=%%j

     )

 

     :: remove "volume." designation

     for /f "tokens=2 delims=." %%i in ("%afs_volume%") do set afs_fs_user=%%i

 

     if /i not "%afs_fs_user%"=="%WlMprNotifyUserName%" (

          echo error.

          echo Unable to parse user AFS quota.

          goto debug

     )

    

     :: strip off percent character

     for /f "delims=%%" %%i in ("%afs_quota%") do set afs_quota=%%i

 

     if not defined afs_quota (

          echo error.

          echo Unable to parse AFS quota percentage.

          goto debug

     )

     echo done.

 

     echo AFSQuotaUsedPercentage=%afs_quota%

 

     if %afs_quota% GTR 99 (

          echo The user is over quota.

          call cmdmsgbox.cmd "Logon Alert!  Please stop and read this message...\n\nYour Mosaic account file space quota has been exeeded.\n\nYour account is currently at %afs_quota% percent capacity.\n\nYou MUST remove files from your Mosaic file space to prevent system problems, or\ncontact the Mosaic Help Desk to obtain more quota.\n\nWeb browse to \"http://www.coe.uncc.edu/mosaic/procedures\" for more information.\n\nPlease correct this issue BEFORE you logout!" "AFS Logon" "Ok+IconStop+TopMost" 120 >nul

     )

 

     nlfecho Creating new U: drive...

     subst u: "%fr_homepath%" >> "%logon_errors_log%" 2>>&1 || set result=error

     if defined result (

          echo error.

          echo Unable to create U: drive for logon user.

          goto debug

     )

     echo done.

 

     :: check all required directories in user's AFS volume

 

     call :checkdir "u:\xp_profile"

     if defined result goto debug

 

     call :checkdir "u:\pc"

     if defined result goto debug

 

     call :checkdir "u:\pc\win_data"

     if defined result goto debug

 

     call :checkdir "u:\pc\win_data\Desktop"

     if defined result goto debug

 

     call :checkdir "u:\pc\win_data\Application Data"

     if defined result goto debug

 

     call :checkdir "u:\pc\win_data\My Documents"

     if defined result goto debug

 

     call :checkdir "u:\pc\win_data\My Music"

     if defined result goto debug

    

     call :checkdir "u:\pc\win_data\My Pictures"

     if defined result goto debug

 

     :: move local settings profile data into place

     nlfecho Checking "Local Settings"...

     if exist "u:\pc\win_data\Local Settings" (

          echo done.

          (if exist "u:\xp_profile\Local Settings" (

              nlfecho Removing "Local Settings"...

              (rd "u:\xp_profile\Local Settings" /s /q >> "%logon_errors_log%" 2>>&1 || (

                   echo error.

                   echo Unable to remove folder.

                   goto debug

              ))

              echo done.

          ))

          nlfecho Moving "Local Settings"...

          (move "u:\pc\win_data\Local Settings" "u:\xp_profile" >> "%logon_errors_log%" 2>>&1 || (

              echo error.

              echo Unable to move folder.

              goto debug

          ))

     )

     echo done.

 

     set fr_homepath=

 

     nlfecho Removing system AFS token...

     unlog >> nul 2>>&1

     echo done.

 

     if exist "%logon_disp_lock%" (

          sleep 3

          del "%logon_disp_lock%"

     )

 

     goto end

 

 

::-------------------------------------------------------------------------------------------------

:: afscheck (subroutine)

:: checks status of AFS service and restarts service if neccessary.

:: returns:

:: result=              (undefined) Everything's ok.

:: result=error        (defined) An uncorrectable problem exists.

::-------------------------------------------------------------------------------------------------

 

:afscheck

 

     :: clear error

     set result=

 

     nlfecho Checking AFS N: drive...

     if exist "%ntnet%" (

          echo done.

          goto :eof

     )

     echo error.

     echo The N: drive appears to be missing.

    

     :: start status dialog for user

     start logondisp.exe "%logon_session_log%"

 

     :: get pc uptime to find out if the machine was just restarted

     for /f "tokens=8 delims=," %%i in ('tasklist /V /NH /FI "IMAGENAME eq System Idle Process" /FO CSV') do set uptime=%%~i

 

     :: parse out hours, mins, and secs

     for /f "tokens=1,2,3 delims=:" %%i in ("%uptime%") do (

          set uphours=%%~i

          set upmins=%%~j

          set upsecs=%%~k

     )

 

     :: create total seconds of uptime

     :: set default to 0 in case variable creation goofed

     set /a uptotal=0

     if defined uphours set /a uptotal=%uphours% * 3600

     if defined upmins set /a uptotal+=%upmins% * 60

     if defined upsecs set /a uptotal+=%upsecs%

 

     echo Time since reboot is %uptotal% secs

 

     :: if uptotal greater than 60 seconds then

     :: something is probably wrong, go ahead and restart afs

     if %uptotal% GTR 60 goto afsrestart

 

          :: otherwise, wait on mosaicd.cmd to link-up n: drive

          :: check for n: drive every second for 40 seconds

          nlfecho Waiting for N: drive...

          for /l %%i in (1,1,40) do (

              (if exist "%ntnet%" (

                   echo done.

                   goto :eof

              ))

              nlfecho .

              sleep 1

          )

 

          echo error.

          echo There was a time-out waiting on the N: drive.

          echo The N: drive does not exist or the AFS service is unavailable.

 

     :afsrestart

 

     nlfecho Sending stop signal to AFS service...

     net stop "IBM AFS Client" >> nul 2>>&1

     echo done.

 

     nlfecho Waiting for AFS service to stop...

     for /l %%i in (1,1,16) do (

          ((for /f "tokens=*" %%i in ('tlist afsd_service') do @echo. > nul) || goto afsstopped )

          nlfecho .

          sleep 1

     )

 

          echo error.

          echo The AFS service did not stop in 16 seconds.

 

          nlfecho Forcing a kill on afs service...

          kill -f afsd_service.exe

          echo done.

 

          nlfecho Waiting for AFS service to terminate...

          for /l %%i in (1,1,16) do (

              ((for /f "tokens=*" %%i in ('tlist afsd_service') do @echo. > nul) || goto afsisdead )

              nlfecho .

              sleep 1

          )

 

              echo error.

              echo The AFS service did not terminate in 16 seconds.

              set result=error

              goto :eof

 

          :afsisdead

 

     :afsstopped

     echo done.

 

     nlfecho Deleting AFS cache...

     set /a count=0

     :delcache

 

          if exist "c:\afscache" del "c:\afscache"

 

     if exist "c:\afscache" (

          if %count% LSS 10 (

              sleep 2

              nlfecho ..

              set /a count+=1

              goto delcache

          ) else (

              echo error.

              echo Unable to remove the AFS cache file.

              echo The AFS cache file is likely still in use.

              set result=error

              goto :eof

          )

     )

     echo done.

 

     nlfecho Restarting the AFS service...

     net start "IBM AFS Client" >>nul 2>>&1 || (

          echo error.

          echo The system was unable to restart the AFS service.

          set result=error

          goto :eof

     )

     echo done.

 

     :: make sure existing N: drive is unlinked

     net use n: /d >>nul 2>>&1

 

     :: try to link n: drive to AFS

     :: if failure then retry every 3 seconds for 9 times

 

     nlfecho Reconnecting the AFS N: drive...

     for /l %%i in (1,1,9) do (

          net use n: \\%COMPUTERNAME%-afs\all >>nul 2>>&1

          (if exist "%ntnet%" (

              echo done.

              goto :eof

          ))

          nlfecho .

          sleep 3

     )

     echo error.

 

     echo The system was unable to restart AFS networking.

     set result=error

 

     goto :eof

 

 

::-------------------------------------------------------------------------------------------------

:: checkdir (subroutine)

:: checks to see if a specified logon user subdirectory exists

:: if it does it just returns

:: if it doesn't then it trys to create it and sets proper AFS acl's on it and returns.

:: if no error occurs, the routine unsets the environment variable "result" and returns.

:: if an error occurs, the routine sets the environment variable "result=error" and returns.

:: routine logs output to console

:: command output and errors logged to %logon_errors_log%

::

:: requires:

::   * argument %1 to be the directory to check.

::

:: use: