AFSLogonShell
Rodney M. Dyer
x86 Systems Programmer
Mosaic Computing Group
William States Lee College of Engineering
University of North Carolina at Charlotte
Email: rmdyer@uncc.edu
Phone: 704-687-3518
Fax: 704-687-2352
February 26, 2003
Draft 1.0
What is
it?
AFSLogonShell
provides an augmentation of the traditional AFS client authentication process
under the Windows operating system.
AFSLogonShell allows an administrator to write their own child process
in almost any compiled or scripted language that will be executed during the
AFS authentication process at logon. The
child shell process will run as the SYSTEM user and will have available to it
the username and password of the user logging on to the system. The shell can also be made hidden so that the
user never sees it. Many things can be
accomplished in the child shell that may help administrators in their
administration of Windows machines that are using the AFS file system.
AFSLogonShell is
specifically a single C function that can be applied to the C source file
called "afslogon.c". The
"afslogon.c" file is provided in the OpenAFS Windows client source
code. The AFSLogonShell function was
made to be added into the file so that it is statically linked in. No libraries or DLLs are required other than
those already used by the AFS client.
Traditional
AFS Logon Authentication
When AFS logon
authentication is enabled the Windows logon process calls upon the AFS logon
authenticator for obtaining an AFS token for the user. The AFS logon authenticator is simply a file
called "afslogon.dll" that adheres to Microsoft's network provider
interface standard and which should publicly export the function
"NPLogonNotify". Under normal
circumstances the routines that reside in the "NPLogonNotify"
function will use the user's username and password to obtain a Kerberos 4
ticket from the AFS Kerberos Authentication Server. The routines then convert the ticket to a
token and store that token into the user's token cache. The "NPLogonNotify" function then
exits allowing the Windows logon process to continue. When the Windows logon process is completed,
the "userinit.exe" program is called, which then sets up the user's
environment.
It is important
to note that the user token obtained through Kerberos 4 authentication is
placed into the token cache that is a part of the user's process space. No other user can access that user's token
(in theory). In this case user SYSTEM
does not have access to the user's token.
This is one reason that the AFSLogonShell function was written.
The AFSLogonShell
function can allow procedures to be written that run as user SYSTEM with a
user's token. We might need to do this
for example to prepare the user's AFS file space before the user's profile is
downloaded, or before folder redirection is done. We also might want to reject the Kerberos 4
authentication outright and obtain for the user a token that was derived from
Kerberos V. There are plenty of uses for
the AFSLogonShell function that have yet to be conceived.
It is within the
"NPLogonNotify" function that the AFSLogonShell function call is
added.
Here is a link to
the Network Provider API...
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/security/network_provider_api.asp
You can also just
search on the MSDN site for "Network Provider".
How
AFSLogonShell Works
After several
design changes it was decided that the best way to implement the AFSLogonShell
function was so that it had little to no impact on the regular processing of
the AFS authentication routines. The
theory is to allow the normal authentication process to take place then perform
the child shell process before any error checking is done for that
authentication. So we have...
1. Perform normal Kerberos 4 authentication.
2. Call child shell process.
3. Check for error returned from 1.
The nice thing
about the way the AFSLogonShell is written is that we can control whether or
not any error dialogs show up for any authentication problems encountered with
regular authentication. The reason we
would want to do this is if we are performing our own authentication within our
child shell. If the child shell is
executed successfully we may want to ignore the error reported from regular
authentication. The AFSLogonShell
function is written so that the administrator can decide on how the error code
from regular authentication is processed.
I ended up with a
function that requires only one line to add to the regular authentication code
in the "NPLogonNotify" routine of "afslogon.c". Here is the function line that needs to be
added in...
code
= dword_AFSLogonShell( hwndOwner, code, interactive, failSilently,
define_AFSLogonShell_Mode_UseINIModes );
If you've looked
at the source code within the "NPLogonNotify" routine of
"afslogon.c", you'll notice that one variable named "code",
is used near the end for processing the result of the regular authentication. Near the bottom of the
"NPLogonNotify" routine we see that the "code" variable
simply takes on a value of 0 (zero) in the case of success, or some other
possible failure value.
The AFSLogonShell
function was written so that it can be run in several modes. The simplest mode called
"PassThrough" prevents the AFSLogonShell function from interfering
with any normal authentication result.
In this manner the AFSLogonShell function will be "invisible"
to the normal flow of processing. The
other modes of operation for the AFSLogonShell function allow the
"code" value to be "controlled" by the administrator based
on the execution or failure of the child process.
Looking at the
AFSLogonShell function line presented previously you see 5 parameters. The first four parameters are regular
variables used in the normal "NPLogonNotify" routine. The AFSLogonShell function does not modify
any of these variables within the function.
The fifth parameter is by default set to the define
"define_AFSLogonShell_Mode_UseINIModes". This is but one of 5 different defines that
can be used as the fifth parameter of the AFSLogonShell function. The fifth parameter of the AFSLogonShell
function controls what the return value of the function will be. For most circumstances the default value is
fine and unless you are a programmer, should not be changed. The define "define_AFSLogonShell_Mode_UseINIModes"
allows the administrator to use a config setting at run-time to control what
happens to the return value of the function.
The AFSLogonShell
function first tries to detect whether or not a system wide environment
variable exists called "AFSLogonINI".
The "AFSLogonINI" environment variable points to a Windows INI
file that contains the settings for executing the child shell process. For example...
AFSLogonINI=c:\admin\afslogon.ini
The
"afslogon.ini" file contains...
[Config]
Mode=PassThrough
ExecString=c:\winnt\system32\cmd.exe
/c c:\admin\hello_world.cmd
ExecDirectory=c:\admin
ExecTimeOut=40
ExecShow=SW_SHOW
As you can see, the "afslogon.ini" file directs the
AFSLogonShell function to execute a file called "cmd.exe" with the
arguments "/c" and "hello_world.cmd". The startup directory will be
"c:\admin". The AFSLogonShell
function will then try to execute the file and wait 40 seconds for it to
complete.
The "ExecTimeOut" is a value in seconds for
"afslogon.dll" to wait for your child process to complete. If the time goes beyond this value then it is
assumed the child has locked-up or will never return. In that case it is assumed the child has
failed. The AFSLogonShell function
within the "afslogon.dll" is then allowed to continue with normal
processing based on the "Mode" set.
Typically your shell process should take well less than 40 seconds,
especially due to the following...
Important note: There is
also another time-out involved here.
When you first logon to the Windows machine the WinLogon process calls
the "NPLogonNotify" routine within "afslogon.dll". The Winlogon process gives the
"afslogon.dll" a maximum time to complete. If the "afslogon.dll" itself
doesn't complete then the Winlogon process continues on with regular
processing. The WinLogon time-out is set
default to 60 seconds unless overridden by a specific network provider. You can override the max time by using...
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\NetworkProvider\RestoreTimeout
= REG_DWORD n
See the following for more information...
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/security/npgetcaps.asp
Specifically the WNNC_START member.
The "ExecShow" setting is for debugging. Most of the time it will be set to
"SW_HIDE", but for debugging, you can set it to "SW_SHOW".
The "Mode" setting directs the AFSLogonShell function to
just pass the result of the regular AFS Kerberos 4 authentication on to the
failure detection routines. You can use
two other options for "Mode" called "SuccessAlways" and
"SuccessOnly".
The "SuccessAlways" option will always prevent the
regular internal authentication from error'ing.
You might use this option if you are going to perform all of you own
authentication within the child process that you create.
The "SuccessOnly" option is similar to the
"SuccessAlways" option except that if the child process fails in any
way, the regular authentication result will be passed on to the failure
detection routines. In the case that the
child process completes successfully, the result will always prevent the
regular internal processing from error'ing.
The AFSLogonShell function was written so that a INI file could be
used instead of recompiling every time a change needed to be made to the
code. You are free as an administrator
to change any of the settings in the INI file for executing your child process.
How to add
the AFSLogonShell function to the "afslogon.c" source...
Note: I have a pre-compiled binary of the latest
version of "afslogon.dll" from OpenAFS with the AFSLogonShell
functioning added in. I can't publicly
release this since it would probably violate some license somewhere. But, if you ask me nicely through personal
email, I can probably send you a copy.
Assuming you
don't trust pre-compiled binaries and you understand C programming then we'll
keep going...
We'll start by
testing the real version of the "afslogon.dll" that comes from the
OpenAFS source code. You need to first
setup a working test PC that has the binary distribution of the OpenAFS client
installed. Make sure everything works as
you expect it to. You will note that the
binary distribution of OpenAFS installs a working copy of the
"afslogon.dll" into...
"c:\program
files\ibm\afs\client\program\afslogon.dll"
After you have
run your tests copy the "afslogon.dll" to
"afslogon.dll_orig" to make a backup of it.
Now, download the
latest version of the OpenAFS source code and compile it for Windows. After it is compiled copy the
"afslogon.dll" that was created from the compile process to the test
machine with the OpenAFS client on it.
Test the binary you just compiled on the test PC. It should work as normal out-of-the box, if
it doesn't, then something is probably wrong with your compile environment, or
you are not using the same version of the binary distribution and source.
Assuming you have
everything tested and working now with the "real" OpenAFS source
code, you can start by making a copy of the "afslogon.c" source. I would copy it to "afslogon.c_orig"
just to make a backup. Now open up the
real "afslogon.c" source and add the "begining
through ending" of the "AFSLogonShell defs
and function code" (at the bottom of this document) into the text near the
top of the code, in an area just under all the variable declarations and
defines, but before the function declarations begin. Once you've done that you are ready to add
the single line that calls the AFSLogonShell function. It goes near the bottom of the
"NPLogonNotify" routine. Look
for a section and add it in like this...
Beginning
of NPLogonNotify code of "afslogon.c"
.
.
.
/* Wait just a little
while and try again */
retryInterval
= sleepInterval = DEFAULT_SLEEP_INTERVAL;
}
if (retryInterval
< sleepInterval)
sleepInterval
= retryInterval;
Sleep(sleepInterval
* 1000);
retryInterval
-= sleepInterval;
}
//------------------------------------------------------------------------
// call AFSLogonShell
//------------------------------------------------------------------------
code = dword_AFSLogonShell( hwndOwner, code,
interactive, failSilently, define_AFSLogonShell_Mode_UseINIModes );
if (code) {
char msg[128];
sprintf(msg, "Integrated login failed: %s", reason);
if (interactive && !failSilently)
MessageBox(hwndOwner,
msg, "AFS Logon", MB_OK);
else {
.
.
.
End
of NPLogonNotify code
Once you've added
in this code, compile it. It should
compile successfully, if not you may have done something wrong. Assuming it did compile, grab the new
"afslogon.dll" from the compile tree and copy it over to the PC you
are using for testing. Btw, the
"afslogon.dll" is only "in-use" at logon, you should not
have any trouble replacing it by using a regular copy.
Now that you have
it installed, you need to create the system-wide environment variable that
points to the INI config file that tells the AFSLogonShell what to do. You need to create a INI file similar to the
one shown above that directs the processing of the AFSLogonShell function. You might even create the "hello_world.cmd" script as above with the following in
it for testing...
hello_world.cmd file contents...
@echo off
echo Hello World!
pause
Assuming you've
got everything ready to go. You can
logon as administrator and test the results.
With the "PassThough" mode enabled
you should see a command window open up with the "Hello World!" text
and a "Press any key to continue. . ." in the window.
When you press a
key the window will close and you might see the result of your trying to
authenticate as admin which fails unless you've got an
"Administrator" account setup for your AFS file system.
The following
environment variables are available for use from within your child process...
WlMprNotifyDesktop=Winlogon
WlMprNotifyDomain=DOMAIN
WlMprNotifyLogonFlag=1
WlMprNotifyLogonId=0:79047
WlMprNotifyOldPasswordValid=0
WlMprNotifyPassword=xxxxxxx
WlMprNotifyStationHandle=0
WlMprNotifyStationName=WinSta0
WlMprNotifyUserName=abcdef
WlMprNotifyWinlogonWindow=100072
Obviously the
"WlMprNotifyUserName" and "WlMprNotifyPassword" environment variables are very
important for things we need to do in the shell. We might want to get access to the user's
home space before the profile downloads to make changes. We might want to setup for folder redirection. We might want to get a KRB V TGT and set the
user a token based on that. We can do
WHATEVER WE WANT TO DO!
To see the power
in this change your "hello_world.cmd" code
to read...
hello_world.cmd file contents...
@echo off
echo Username=%WlMprNotifyUserName%
echo Password=%WlMprNotifyPassword%
pause
Security
It is very
important to understand that any child process you create will be run in the
context of the SYSTEM account. The
SYSTEM account is the operating system itself, it's the account that services
run as. As the SYSTEM account you will
be able to perform almost anything.
Because of this there are things that need to be protected...
* You need to make sure no one can change the
AFSLogonINI system-wide environment variable to point to something different.
* You need to make sure that no one can modify
the INI file that contains the config settings.
* You need to make sure that no one has access
to your child process binary or script.
* You need to make sure your child process is
"fool-proof", or at least "good-enough".
* You need to make sure your child process
doesn't "lock-up" even though there is a time-out protection
mechanism built into the AFSLogonShell function.
* You need to protect the
"afslogon.dll" file. That file
could conceivably be changed by a user without your knowledge if it isn't
protected.
* You need to make sure if you are using
scripts to not "echo" the environment variable contents to a pipe
except when testing or debugging. I've
written a special app called "envout.exe" to spit the contents of an
environment variable to stdout just to handle this
situation.
* You need to make sure that once you go
production it is wise to only use SW_HIDE as the "ExecShow"
setting of the INI file.
* If you are using a scripting language such as
command shell (cmd.exe) or perl, you need to protect
the interpreter from being changed.
* You need to protect anything else you can
think of that I've forgotten.
* You need to make sure you understand how all
this works!
The
AFSLogonShell Definitions and Function Code
//------------------------------------------------------------------------------------------------------------
//
begin - AFSLogonShell defs and function
//------------------------------------------------------------------------------------------------------------
//
standard AFSLogonShell Function Mode values
#define
define_AFSLogonShell_Mode_FunctionValues 0
#define
define_AFSLogonShell_Mode_PassThrough 1
#define
define_AFSLogonShell_Mode_SuccessAlways 2
#define
define_AFSLogonShell_Mode_SuccessOnly 3
#define
define_AFSLogonShell_Mode_UseINIModes
4
//
standard AFSLogonShell INI Mode values
#define
define_AFSLogonShell_INIMode_Undefined 0
#define
define_AFSLogonShell_INIMode_PassThrough 1
#define
define_AFSLogonShell_INIMode_SuccessAlways 2
#define
define_AFSLogonShell_INIMode_SuccessOnly 3
//
standard AFSLogonShell return values
#define
define_AFSLogonShell_Ret_SUCCESS 0
#define
define_AFSLogonShell_Ret_FAILURE 1
#define
define_AFSLogonShell_Ret_NOENV 2
#define
define_AFSLogonShell_Ret_EXECERR 3
#define
define_AFSLogonShell_Ret_TIMEOUT 4
#define
define_AFSLogonShell_Ret_UNDEFINED 5
//
Important Notes:
//
//
The "dword_Mode" variable controls whether
the value of
//
the "code" variable that is passed into the function will
//
be passed right through regardless of function processing.
//
This controls whether the AFSLogonShell function exists in
//
the "afslogon.c" code as if it were "invisible".
//
//
Using the define "define_AFSLogonShell_Mode_PassThrough"
//
for the value of "dword_Mode" means that
the "code"
//
variable is passed through the function regardless of
//
what happens in the function. In this
manner the function
//
does not effect normal "afslogon.dll" processing. The
//
function just executes the shell, that's all, all errors
//
are ignored. If the shell didn't execute
there's no way
//
to know other than a possilby slower execution at
runtime.
//
//
Using the define "define_AFSLogonShell_Mode_FunctionValues"
//
for the value of "dword_Mode" means that
the function
//
will return the standard AFSLogonShell return values
//
based on the function process results. A
programmer
//
must add more code in "afslogon.c" to process those
//
results after the call is made to prevent interference
//
with the regular "afslogon.c" "code" variable values.
//
//
Using the define "define_AFSLogonShell_Mode_SuccessAlways"
//
for the value of "dword_Mode" means that the
function
//
returns "define_AFSLogonShell_Ret_SUCCESS"
under any
//
circumstance. This will prevent the
normal "afslogon.c"
//
procedures from producing an error. You
may want to do
//
this if you are using the shell for all authentication
//
such as in the case when you are using kinit for KRB
V.
//
//
Using the define "define_AFSLogonShell_Mode_SuccessOnly"
//
for the value of "dword_Mode" means that if
the function
//
successfully executes the child shell process the "code"
//
value will be forced to be "define_AFSLogonShell_Ret_SUCCESS".
//
This will prevent the "afslogon.c" code from displaying
//
an error message from the default authentication routines
//
(if it previously occured). This means that all
//
authentication will be handled by the AFSLogonShell child
//
process. If the shell does not execute
properly any value
//
that "code" currently has will pass right through the
//
function.
//
Using the define "define_AFSLogonShell_Mode_UseINIModes"
//
for the value of "dword_Mode" means that
the value
//
returned will depend on the "Mode" setting from the INI
//
file. In the case where the function
cannot find or
//
open the INI file and obtain that mode setting, then
//
the default mode will be "PassThrough".
DWORD
dword_AFSLogonShell( HWND hwndOwner, DWORD code, BOOL interactive, BOOL
failSilently, DWORD dword_Mode )
{
// declare some variables
DWORD dword_ReturnCode; // variable holding the function
return value
BOOL bool_Return; // general Win32 function
return variable
DWORD dword_Return; // general Win32 function
return variable
PROCESS_INFORMATION process_information_Process; // process information for created process
STARTUPINFO startup_info_StartInfo; // startup information for created
process
CHAR char_Print[2048]; // string used for formatting
dialog box
CHAR char_ExecString[256]; // string holding executable process
pathname
CHAR char_ExecDirectory[256]; // string holding executable process
directory
CHAR char_ExecTimeOut[256]; // string holding executable process
time-out
CHAR char_ExecShow[256]; // string holding executable
process show mode
INT int_ExecTimeoutCount; // variable holding time-out count
for process (in secs)
CHAR *ptr_char_AFSLogonINIVar; // pointer to AFSLogonINI environment
variable contents
DWORD dword_ExitCode; // variable holding the exit
code of the child process
BOOL bool_GetExitCodeProcess_Return;
// variable holding the GetExitCodeProcess return
value
DWORD dword_GetLastError; // variable to hold the Win32 GetLastError value
CHAR char_GetLastErrorString[256]; // variable to hold the Win32 GetLastError string
CHAR char_INIMode[256]; // string to hold the INI Mode
setting
DWORD dword_INIMode; // variable to hold the value
of the INI Mode setting
// init some values
char_ExecString[0] =
0;
char_ExecDirectory[0]
= 0;
char_ExecTimeOut[0]
= 0;
char_ExecShow[0] =
0;
char_INIMode[0] = 0;
int_ExecTimeoutCount
= 0;
ptr_char_AFSLogonINIVar
= NULL;
dword_GetLastError =
0;
char_GetLastErrorString[0]
= 0;
dword_ExitCode = 0;
dword_INIMode = define_AFSLogonShell_INIMode_Undefined;
dword_ReturnCode = define_AFSLogonShell_Ret_UNDEFINED;
// try to get the AFSLogonINI environment
variable contents
ptr_char_AFSLogonINIVar
= getenv("AFSLogonINI");
// check for error
if ( ptr_char_AFSLogonINIVar
== NULL )
{
if (interactive && !failSilently)
MessageBox(
hwndOwner, "The \"AFSLogonINI\" environment variable was not
found.", "AFS Logon Shell", MB_ICONSTOP | MB_OK );
dword_ReturnCode
= define_AFSLogonShell_Ret_NOENV;
goto lable_AFSLogonShell_END;
};
// get the config information from the
afslogon.ini file
GetPrivateProfileString(
"Config", "ExecString",
"\0", char_ExecString, 255, ptr_char_AFSLogonINIVar );
GetPrivateProfileString(
"Config", "ExecDirectory",
"\0", char_ExecDirectory, 255, ptr_char_AFSLogonINIVar );
GetPrivateProfileString(
"Config", "ExecTimeOut",
"\0", char_ExecTimeOut, 255, ptr_char_AFSLogonINIVar );
GetPrivateProfileString(
"Config", "ExecShow",
"\0", char_ExecShow, 255, ptr_char_AFSLogonINIVar );
GetPrivateProfileString(
"Config", "Mode", "\0", char_INIMode,
255, ptr_char_AFSLogonINIVar );
// convert time-out time to integer
int_ExecTimeoutCount
= atoi( char_ExecTimeOut );
// get INI mode if it exists
strupr( char_INIMode );
if ( strcmp( char_INIMode, "PASSTHROUGH" ) == 0 )
dword_INIMode =
define_AFSLogonShell_INIMode_PassThrough;
else
if ( strcmp( char_INIMode, "SUCCESSALWAYS" ) == 0 )
dword_INIMode
= define_AFSLogonShell_INIMode_SuccessAlways;
else
if ( strcmp(
char_INIMode, "SUCCESSONLY" ) == 0 )
dword_INIMode
= define_AFSLogonShell_INIMode_SuccessOnly;
// clean structure
ZeroMemory( &process_information_Process, sizeof(PROCESS_INFORMATION)
);
// setup startup information
GetStartupInfo(
&startup_info_StartInfo );
startup_info_StartInfo.dwFlags
= STARTF_USESHOWWINDOW;
strupr( char_ExecShow );
if ( strcmp( char_ExecShow, "SW_SHOW" ) == 0 )
startup_info_StartInfo.wShowWindow
= SW_SHOW;
else
startup_info_StartInfo.wShowWindow
= SW_HIDE;
// execute child shell process
bool_Return = CreateProcess
(
NULL,
char_ExecString,
NULL,
NULL,
0x01,
NORMAL_PRIORITY_CLASS,
NULL,
char_ExecDirectory,
&startup_info_StartInfo,
&process_information_Process
);
// check if started ok
if ( bool_Return !=
TRUE )
{
if (interactive && !failSilently)
{
dword_GetLastError
= GetLastError();
strcpy( char_GetLastErrorString, "Unknown" );
dword_Return
= FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dword_GetLastError, MAKELANGID(LANG_NEUTRAL,
SUBLANG_DEFAULT), char_GetLastErrorString, 255, NULL
);
sprintf( char_Print, "Unable to start the AFS logon shell
process.\n\nExecString=\"%s\"\n\nWin32
error = %d, %s", char_ExecString, dword_GetLastError, char_GetLastErrorString
);
MessageBox(
hwndOwner, char_Print, "AFS Logon Shell",
MB_ICONSTOP | MB_OK );
}
dword_ReturnCode
= define_AFSLogonShell_Ret_EXECERR;
goto lable_AFSLogonShell_END;
}
// wait on child execution to complete
do
{
dword_Return = WaitForSingleObject( process_information_Process.hProcess,
1000 );
int_ExecTimeoutCount--;
} while ( dword_Return
== WAIT_TIMEOUT && int_ExecTimeoutCount >
0 );
// get termination code
bool_GetExitCodeProcess_Return
= GetExitCodeProcess( process_information_Process.hProcess,
&dword_ExitCode );
// now close the handle(s)
CloseHandle( process_information_Process.hProcess );
CloseHandle( process_information_Process.hThread );
// did the process time-out?
if ( dword_Return ==
WAIT_TIMEOUT )
{
if
(interactive && !failSilently)
MessageBox(
hwndOwner, "A time-out occured waiting for
execution of the AFS logon shell process to complete.", "AFS Logon
Shell", MB_ICONSTOP | MB_OK );
dword_ReturnCode
= define_AFSLogonShell_Ret_TIMEOUT;
goto lable_AFSLogonShell_END;
}
// process termination code
if ( dword_ExitCode
!= 0 || bool_GetExitCodeProcess_Return == 0 )
{
dword_ReturnCode
= define_AFSLogonShell_Ret_FAILURE;
goto lable_AFSLogonShell_END;
}
dword_ReturnCode = define_AFSLogonShell_Ret_SUCCESS;
lable_AFSLogonShell_END:
// INI return modes
switch ( dword_INIMode
)
{
case define_AFSLogonShell_INIMode_SuccessAlways:
dword_Mode
= define_AFSLogonShell_Mode_SuccessAlways;
break;
case define_AFSLogonShell_INIMode_SuccessOnly:
dword_Mode
= define_AFSLogonShell_Mode_SuccessOnly;
break;
case define_AFSLogonShell_INIMode_Undefined:
if ( dword_Mode
== define_AFSLogonShell_Mode_UseINIModes )
dword_Mode
= define_AFSLogonShell_Mode_PassThrough;
break;
default:
dword_Mode
= define_AFSLogonShell_Mode_PassThrough;
}
// function return modes
switch ( dword_Mode
)
{
case define_AFSLogonShell_Mode_FunctionValues:
return dword_ReturnCode;
case define_AFSLogonShell_Mode_SuccessAlways:
return define_AFSLogonShell_Ret_SUCCESS;
case define_AFSLogonShell_Mode_SuccessOnly:
if ( dword_ReturnCode
== define_AFSLogonShell_Ret_SUCCESS )
return define_AFSLogonShell_Ret_SUCCESS;
else
return code;
default:
return code;
}
}
//------------------------------------------------------------------------------------------------------------
//
end - AFSLogonShell defs and function
//------------------------------------------------------------------------------------------------------------
The
AFSLogonShell Function Call
//------------------------------------------------------------------------
// call AFSLogonShell
//------------------------------------------------------------------------
code = dword_AFSLogonShell(
hwndOwner, code, interactive, failSilently,
define_AFSLogonShell_Mode_UseINIModes );