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: