2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
14 #include <sys/types.h>
19 #include <afs/param.h>
21 #include <afs/pioctl_nt.h>
22 #include <afs/kautils.h>
25 #include "cm_config.h"
29 DWORD LogonOption,TraceOption;
35 void DebugEvent0(char *a)
37 HANDLE h; char *ptbuf[1];
38 if (!ISLOGONTRACE(TraceOption))
40 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
42 ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
43 DeregisterEventSource(h);
47 void DebugEvent(char *a,char *b,...)
49 HANDLE h; char *ptbuf[1],buf[MAXBUF_+1];
52 if (!ISLOGONTRACE(TraceOption))
56 a = AFS_DAEMON_EVENT_NAME;
57 h = RegisterEventSource(NULL, a);
59 _vsnprintf(buf,MAXBUF_,b,marker);
62 ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);\
63 DeregisterEventSource(h);
67 CHAR *GenRandomName(CHAR *pbuf)
70 srand( (unsigned)time( NULL ) );
71 for (i=0;i<MAXRANDOMNAMELEN-1;i++)
72 pbuf[i]='a'+(rand() % 26);
73 pbuf[MAXRANDOMNAMELEN-1]=0;
77 BOOLEAN AFSWillAutoStart(void)
82 BOOLEAN result = FALSE;
83 LPQUERY_SERVICE_CONFIG pConfig = NULL;
87 /* Open services manager */
88 scm = OpenSCManager(NULL, NULL, GENERIC_READ);
89 if (!scm) return FALSE;
91 /* Open AFSD service */
92 svc = OpenService(scm, "TransarcAFSDaemon", SERVICE_QUERY_CONFIG);
96 /* Query AFSD service config, first just to get buffer size */
97 /* Expected to fail, so don't test return value */
98 (void) QueryServiceConfig(svc, NULL, 0, &BufSize);
99 status = GetLastError();
100 if (status != ERROR_INSUFFICIENT_BUFFER)
103 /* Allocate buffer */
104 pConfig = (LPQUERY_SERVICE_CONFIG)GlobalAlloc(GMEM_FIXED,BufSize);
108 /* Query AFSD service config, this time for real */
109 flag = QueryServiceConfig(svc, pConfig, BufSize, &BufSize);
113 /* Is it autostart? */
114 if (pConfig->dwStartType < SERVICE_DEMAND_START)
120 CloseServiceHandle(svc);
122 CloseServiceHandle(scm);
127 DWORD MapAuthError(DWORD code)
130 /* Unfortunately, returning WN_NO_NETWORK results in the MPR abandoning
131 * logon scripts for all credential managers, although they will still
132 * receive logon notifications. Since we don't want this, we return
133 * WN_SUCCESS. This is highly undesirable, but we also don't want to
134 * break other network providers.
138 return WN_NO_NETWORK; */
139 default: return WN_SUCCESS;
143 BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
147 case DLL_PROCESS_ATTACH:
148 /* Initialize AFS libraries */
154 /* Everything else succeeds but does nothing. */
155 case DLL_PROCESS_DETACH:
156 case DLL_THREAD_ATTACH:
157 case DLL_THREAD_DETACH:
165 DWORD APIENTRY NPGetCaps(DWORD index)
169 /* Don't have our own type; use somebody else's. */
170 return WNNC_NET_SUN_PC_NFS;
173 /* Say we are already started, even though we might wait after we receive NPLogonNotify */
181 BOOL IsServiceRunning (void)
183 SERVICE_STATUS Status;
185 memset (&Status, 0x00, sizeof(Status));
186 Status.dwCurrentState = SERVICE_STOPPED;
188 if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
191 if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
193 QueryServiceStatus (hService, &Status);
194 CloseServiceHandle (hService);
197 CloseServiceHandle (hManager);
199 DebugEvent("AFS AfsLogon - Test Service Running","Return Code[%x] ?Running[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_RUNNING));
200 return (Status.dwCurrentState == SERVICE_RUNNING);
203 /* LOOKUPKEYCHAIN: macro to look up the value in the list of keys in order until it's found
204 v:variable to receive value (reference type)
206 d:default, in case the value isn't on any of the keys
208 #define LOOKUPKEYCHAIN(v,t,d,n) \
210 rv = ~ERROR_SUCCESS; \
213 dwSize = sizeof(v); \
214 rv = RegQueryValueEx(hkDom, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
215 if(rv == ERROR_SUCCESS) DebugEvent(NULL, #v " found in hkDom with type [%d]", dwType); \
217 if(hkDoms && (rv != ERROR_SUCCESS || dwType != t)) { \
218 dwSize = sizeof(v); \
219 rv = RegQueryValueEx(hkDoms, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
220 if(rv == ERROR_SUCCESS) DebugEvent(NULL, #v " found in hkDoms with type [%d]", dwType); \
222 if(hkNp && (rv != ERROR_SUCCESS || dwType != t)) { \
223 dwSize = sizeof(v); \
224 rv = RegQueryValueEx(hkNp, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
225 if(rv == ERROR_SUCCESS) DebugEvent(NULL, #v " found in hkNp with type [%d]", dwType); \
227 if(rv != ERROR_SUCCESS || dwType != t) { \
229 DebugEvent(NULL, #v " being set to default"); \
233 /* Get domain specific configuration info. We are returning void because if anything goes wrong
234 we just return defaults.
236 void GetDomainLogonOptions( PLUID lpLogonId, char * username, char * domain, LogonOptions_t *opt ) {
237 HKEY hkParm = NULL; /* Service parameter */
238 HKEY hkNp = NULL; /* network provider key */
239 HKEY hkDoms = NULL; /* domains key */
240 HKEY hkDom = NULL; /* DOMAINS/domain key */
246 char computerName[MAX_COMPUTERNAME_LENGTH + 1];
249 DebugEvent(NULL,"In GetDomainLogonOptions for user [%s] in domain [%s]", username, domain);
250 /* If the domain is the same as the Netbios computer name, we use the LOCALHOST domain name*/
251 opt->flags = LOGON_FLAG_REMOTE;
253 dwSize = MAX_COMPUTERNAME_LENGTH;
254 if(GetComputerName(computerName, &dwSize)) {
255 if(!stricmp(computerName, domain)) {
256 effDomain = "LOCALHOST";
257 opt->flags = LOGON_FLAG_LOCAL;
265 rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY, 0, KEY_READ, &hkParm );
266 if(rv != ERROR_SUCCESS) {
268 DebugEvent(NULL, "GetDomainLogonOption: Can't open parms key [%d]", rv);
271 rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_CLIENT_PROVIDER_KEY, 0, KEY_READ, &hkNp );
272 if(rv != ERROR_SUCCESS) {
274 DebugEvent(NULL, "GetDomainLogonOptions: Can't open NP key [%d]", rv);
278 rv = RegOpenKeyEx( hkNp, REG_CLIENT_DOMAINS_SUBKEY, 0, KEY_READ, &hkDoms );
279 if( rv != ERROR_SUCCESS ) {
281 DebugEvent(NULL, "GetDomainLogonOptions: Can't open Domains key [%d]", rv);
285 if(hkDoms && effDomain) {
286 rv = RegOpenKeyEx( hkDoms, effDomain, 0, KEY_READ, &hkDom );
287 if( rv != ERROR_SUCCESS ) {
289 DebugEvent( NULL, "GetDomainLogonOptions: Can't open domain key for [%s] [%d]", effDomain, rv);
290 /* If none of the domains match, we shouldn't use the domain key either */
295 DebugEvent( NULL, "Not opening domain key for [%s]", effDomain);
297 /* Each individual can either be specified on the domain key, the domains key or in the
298 net provider key. They fail over in that order. If none is found, we just use the
302 LOOKUPKEYCHAIN(opt->LogonOption, REG_DWORD, DEFAULT_LOGON_OPTION, REG_CLIENT_LOGON_OPTION_PARM);
304 /* FailLoginsSilently */
305 dwSize = sizeof(dwDummy);
306 rv = RegQueryValueEx(hkParm, REG_CLIENT_FAIL_SILENTLY_PARM, 0, &dwType, (LPBYTE) &dwDummy, &dwSize);
307 if(rv != ERROR_SUCCESS)
308 LOOKUPKEYCHAIN(dwDummy, REG_DWORD, DEFAULT_FAIL_SILENTLY, REG_CLIENT_FAIL_SILENTLY_PARM);
309 opt->failSilently = !!dwDummy;
312 LOOKUPKEYCHAIN(opt->retryInterval, REG_DWORD, DEFAULT_RETRY_INTERVAL, REG_CLIENT_RETRY_INTERVAL_PARM);
315 LOOKUPKEYCHAIN(opt->sleepInterval, REG_DWORD, DEFAULT_SLEEP_INTERVAL, REG_CLIENT_SLEEP_INTERVAL_PARM);
317 opt->logonScript = NULL;
320 if(!ISLOGONINTEGRATED(opt->LogonOption)) {
321 goto cleanup; /* no need to lookup the logon script */
324 /* come up with SMB username */
325 if(ISHIGHSECURITY(opt->LogonOption)) {
326 opt->smbName = malloc( MAXRANDOMNAMELEN );
327 GenRandomName(opt->smbName);
329 /* username and domain for logon session is not necessarily the same as
330 username and domain passed into network provider. */
331 PSECURITY_LOGON_SESSION_DATA plsd;
332 char lsaUsername[MAX_USERNAME_LENGTH];
333 char lsaDomain[MAX_DOMAIN_LENGTH];
336 LsaGetLogonSessionData(lpLogonId, &plsd);
338 UnicodeStringToANSI(plsd->UserName, lsaUsername, MAX_USERNAME_LENGTH);
339 UnicodeStringToANSI(plsd->LogonDomain, lsaDomain, MAX_DOMAIN_LENGTH);
341 DebugEvent(NULL,"PLSD username[%s] domain[%s]",lsaUsername,lsaDomain);
342 DebugEvent(NULL,"PLSD Unicode username[%S] domain[%S]",plsd->UserName.Buffer,plsd->LogonDomain.Buffer);
343 DebugEvent(NULL,"PLSD lengths username[%d] domain[%d]",plsd->UserName.Length,plsd->LogonDomain.Length);
345 len = strlen(lsaUsername) + strlen(lsaDomain) + 2;
347 opt->smbName = malloc(len);
349 strcpy(opt->smbName, lsaDomain);
350 strcat(opt->smbName, "\\");
351 strcat(opt->smbName, lsaUsername);
353 strlwr(opt->smbName);
355 LsaFreeReturnBuffer(plsd);
358 DebugEvent(NULL,"Looking up logon script");
360 /* First find out where the key is */
365 rv = RegQueryValueExW(hkDom, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
366 if(rv == ERROR_SUCCESS && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
368 DebugEvent(NULL,"Located logon script in hkDom");
371 rv = RegQueryValueExW(hkDoms, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
372 if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
374 DebugEvent(NULL,"Located logon script in hkDoms");
376 /* Note that the LogonScript in the NP key is only used if we are doing high security. */
377 else if(hkNp && ISHIGHSECURITY(opt->LogonOption))
378 rv = RegQueryValueExW(hkNp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
379 if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
381 DebugEvent(NULL,"Located logon script in hkNp");
385 WCHAR *regscript = NULL;
386 WCHAR *regexscript = NULL;
387 WCHAR *regexuscript = NULL;
388 WCHAR *wuname = NULL;
391 int len = strlen(opt->smbName) + 1;
393 wuname = malloc(len * sizeof(WCHAR));
394 MultiByteToWideChar(CP_ACP,0,opt->smbName,-1,wuname,len*sizeof(WCHAR));
396 DebugEvent(NULL,"Username is set for [%S]", wuname);
398 /* dwSize still has the size of the required buffer in bytes. */
399 regscript = malloc(dwSize);
400 rv = RegQueryValueExW(hkTemp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, (LPBYTE) regscript, &dwSize);
401 if(rv != ERROR_SUCCESS) {/* what the ..? */
402 DebugEvent(NULL,"Can't look up logon script [%d]",rv);
403 goto doneLogonScript;
406 DebugEvent(NULL,"Found logon script [%S]", regscript);
408 if(dwType == REG_EXPAND_SZ) {
409 dwSize += MAX_PATH * sizeof(WCHAR); /* make room for environment expansion. */
410 regexscript = malloc(dwSize);
411 rv = ExpandEnvironmentStringsW(regscript, regexscript, dwSize / sizeof(WCHAR));
413 regscript = regexscript;
415 if(rv > (dwSize / sizeof(WCHAR))) {
416 DebugEvent(NULL,"Overflow while expanding environment strings.");
417 goto doneLogonScript;
421 DebugEvent(NULL,"After expanding env strings [%S]", regscript);
423 if(wcsstr(regscript, L"%s")) {
424 dwSize += 256 * sizeof(WCHAR); /* make room for username expansion */
425 regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
426 hr = StringCbPrintfW(regexuscript, dwSize, regscript, wuname);
428 regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
429 wcscpy(regexuscript, regscript);
433 DebugEvent(NULL,"After expanding username [%S]", regexuscript);
436 opt->logonScript = regexuscript;
438 LocalFree(regexuscript);
441 if(wuname) free(wuname);
442 if(regscript) free(regscript);
443 if(regexscript) free(regexscript);
447 if(hkNp) RegCloseKey(hkNp);
448 if(hkDom) RegCloseKey(hkDom);
449 if(hkDoms) RegCloseKey(hkDoms);
450 if(hkParm) RegCloseKey(hkParm);
453 #undef LOOKUPKEYCHAIN
455 /* Try to find out which cell the given path is in. We must retain
456 the contents of *cell in case of failure. *cell is assumed to be
457 at least cellLen chars */
458 DWORD GetFileCellName(char * path, char * cell, size_t cellLen) {
459 struct ViceIoctl blob;
460 char tcell[MAX_PATH];
464 blob.out_size = MAX_PATH;
467 code = pioctl(path, VIOC_FILE_CELL_NAME, &blob, 1);
470 strncpy(cell, tcell, cellLen);
471 cell[cellLen - 1] = '\0';
479 UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen)
483 GetCPInfo(CP_ACP, &CodePageInfo);
485 if (CodePageInfo.MaxCharSize > 1)
486 // Only supporting non-Unicode strings
489 if (uInputString.Buffer && ((LPBYTE) uInputString.Buffer)[1] == '\0')
491 // Looks like unicode, better translate it
492 // UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS
493 WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,
494 lpszOutputString, nOutStringLen-1, NULL, NULL);
495 lpszOutputString[min(uInputString.Length/2,nOutStringLen-1)] = '\0';
499 lpszOutputString[0] = '\0';
501 } // UnicodeStringToANSI
503 DWORD APIENTRY NPLogonNotify(
505 LPCWSTR lpAuthentInfoType,
506 LPVOID lpAuthentInfo,
507 LPCWSTR lpPreviousAuthentInfoType,
508 LPVOID lpPreviousAuthentInfo,
509 LPWSTR lpStationName,
510 LPVOID StationHandle,
511 LPWSTR *lpLogonScript)
513 char uname[MAX_USERNAME_LENGTH]="";
514 char password[MAX_PASSWORD_LENGTH]="";
515 char logonDomain[MAX_DOMAIN_LENGTH]="";
516 char cell[256]="<non-integrated logon>";
517 char homePath[MAX_PATH]="";
519 MSV1_0_INTERACTIVE_LOGON *IL;
530 DWORD LSPtype, LSPsize;
533 HWND hwndOwner = (HWND)StationHandle;
535 BOOLEAN afsWillAutoStart;
537 BOOLEAN uppercased_name = TRUE;
539 LogonOptions_t opt; /* domain specific logon options */
543 /* Initialize Logon Script to none */
546 /* TODO: We should check the value of lpAuthentInfoType before assuming that it is
547 MSV1_0_INTERACTIVE_LOGON though for our purposes KERB_INTERACTIVE_LOGON is
548 co-incidentally equivalent. */
549 IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
551 /* Are we interactive? */
552 interactive = (wcscmp(lpStationName, L"WinSta0") == 0);
554 /* Convert from Unicode to ANSI */
556 /*TODO: Use SecureZeroMemory to erase passwords */
557 UnicodeStringToANSI(IL->UserName, uname, 256);
558 UnicodeStringToANSI(IL->Password, password, 256);
559 UnicodeStringToANSI(IL->LogonDomainName, logonDomain, 256);
561 /* Make sure AD-DOMANS sent from login that is sent to us is striped */
562 ctemp = strchr(uname, '@');
563 if (ctemp) *ctemp = 0;
565 /* is the name all uppercase? */
566 for ( ctemp = uname; *ctemp ; ctemp++) {
567 if ( islower(*ctemp) ) {
568 uppercased_name = FALSE;
573 (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
574 0, KEY_QUERY_VALUE, &NPKey);
575 LSPsize=sizeof(TraceOption);
576 RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
577 &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
585 GetDomainLogonOptions( lpLogonId, uname, logonDomain, &opt );
586 retryInterval = opt.retryInterval;
587 sleepInterval = opt.sleepInterval;
588 *lpLogonScript = opt.logonScript;
590 DebugEvent(NULL,"Got logon script: %S",opt.logonScript);
592 afsWillAutoStart = AFSWillAutoStart();
594 DebugEvent("AFS AfsLogon - NPLogonNotify","LogonOption[%x], Service AutoStart[%d]",
595 opt.LogonOption,afsWillAutoStart);
597 /* Check for zero length password if integrated logon*/
598 if ( ISLOGONINTEGRATED(opt.LogonOption) ) {
599 if ( password[0] == 0 ) {
601 reason = "zero length password is illegal";
605 /* Get cell name if doing integrated logon.
606 We might overwrite this if we are logging into an AD realm and we find out that
607 the user's home dir is in some other cell. */
608 code = cm_GetRootCellName(cell);
611 reason = "unknown cell";
615 /* We get the user's home directory path, if applicable, though we can't lookup the
616 cell right away because the client service may not have started yet. This call
617 also sets the AD_REALM flag in opt.flags if applicable. */
618 if(ISREMOTE(opt.flags))
619 GetAdHomePath(homePath,MAX_PATH,lpLogonId,IL,&opt);
622 /* loop until AFS is started. */
624 if(ISADREALM(opt.flags)) {
625 code = GetFileCellName(homePath,cell,256);
627 DebugEvent(NULL,"profile path [%s] is in cell [%s]",homePath,cell);
629 /* Don't bail out if GetFileCellName failed.
630 * The home dir may not be in AFS after all.
635 /* if Integrated Logon */
636 if (ISLOGONINTEGRATED(opt.LogonOption))
638 if ( KFW_is_available() ) {
639 code = KFW_AFS_get_cred(uname, cell, password, 0, opt.smbName, &reason);
640 DebugEvent(NULL,"KFW_AFS_get_cred uname=[%s] smbname=[%s] cell=[%s] code=[%d]",uname,opt.smbName,cell,code);
643 code = ka_UserAuthenticateGeneral2(KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
644 uname, "", cell, password, opt.smbName, 0, &pw_exp, 0,
646 DebugEvent("AFS AfsLogon - (INTEGRATED only)ka_UserAuthenticateGeneral2","Code[%x]",
649 if ( code && code != KTC_NOCM && code != KTC_NOCMRPC && uppercased_name ) {
650 for ( ctemp = uname; *ctemp ; ctemp++) {
651 *ctemp = tolower(*ctemp);
653 uppercased_name = FALSE;
658 /*JUST check to see if its running*/
659 if (IsServiceRunning())
662 if (!afsWillAutoStart)
666 /* is service started yet?*/
667 DebugEvent("AFS AfsLogon - ka_UserAuthenticateGeneral2","Code[%x] uname[%s] Cell[%s]",
670 /* If we've failed because the client isn't running yet and the
671 * client is set to autostart (and therefore it makes sense for
672 * us to wait for it to start) then sleep a while and try again.
673 * If the error was something else, then give up. */
674 if (code != KTC_NOCM && code != KTC_NOCMRPC || !afsWillAutoStart)
677 /* If the retry interval has expired and we still aren't
678 * logged in, then just give up if we are not in interactive
679 * mode or the failSilently flag is set, otherwise let the
680 * user know we failed and give them a chance to try again. */
681 if (retryInterval <= 0) {
682 reason = "AFS not running";
683 if (!interactive || opt.failSilently)
685 flag = MessageBox(hwndOwner,
686 "AFS is still starting. Retry?",
688 MB_ICONQUESTION | MB_RETRYCANCEL);
689 if (flag == IDCANCEL)
692 /* Wait just a little while and try again */
693 retryInterval = sleepInterval = DEFAULT_SLEEP_INTERVAL;
696 if (retryInterval < sleepInterval)
697 sleepInterval = retryInterval;
699 Sleep(sleepInterval * 1000);
701 retryInterval -= sleepInterval;
704 /* remove any kerberos 5 tickets currently held by the SYSTEM account */
705 if ( KFW_is_available() )
706 KFW_AFS_destroy_tickets_for_cell(cell);
710 sprintf(msg, "Integrated login failed: %s", reason);
712 if (interactive && !opt.failSilently)
713 MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
718 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
720 ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
722 DeregisterEventSource(h);
724 code = MapAuthError(code);
727 if (ISLOGONINTEGRATED(LogonOption) && (code!=0))
730 LocalFree(*lpLogonScript);
731 *lpLogonScript = NULL;
732 if (!afsWillAutoStart) // its not running, so if not autostart or integrated logon then just skip
738 if(opt.smbName) free(opt.smbName);
740 DebugEvent("AFS AfsLogon - Exit","Return Code[%x]",code);
744 DWORD APIENTRY NPPasswordChangeNotify(
745 LPCWSTR lpAuthentInfoType,
746 LPVOID lpAuthentInfo,
747 LPCWSTR lpPreviousAuthentInfoType,
748 LPVOID lpPreviousAuthentInfo,
749 LPWSTR lpStationName,
750 LPVOID StationHandle,
753 DebugEvent0("AFS AfsLogon - NPPasswordChangeNotify");
759 VOID AFS_Logoff_Event(
760 PWLX_NOTIFICATION_INFO pInfo )
763 if (code = ktc_ForgetAllTokens())
764 DebugEvent("AFS AfsLogon - AFS_Logoff_Event - ForgetAllTokens failed [%lX]",code);
766 DebugEvent0("AFS AfsLogon - AFS_Logoff_Event - ForgetAllTokens succeeded");