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 StringCbVPrintf(buf, MAXBUF_+1,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);
343 if(SUCCEEDED(StringCbLength(lsaUsername, MAX_USERNAME_LENGTH, &tlen)))
348 if(SUCCEEDED(StringCbLength(lsaDomain, MAX_DOMAIN_LENGTH, &tlen)))
355 opt->smbName = malloc(len);
357 StringCbCopy(opt->smbName, len, lsaDomain);
358 StringCbCat(opt->smbName, len, "\\");
359 StringCbCat(opt->smbName, len, lsaUsername);
361 strlwr(opt->smbName);
364 LsaFreeReturnBuffer(plsd);
367 DebugEvent(NULL,"Looking up logon script");
369 /* First find out where the key is */
374 rv = RegQueryValueExW(hkDom, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
375 if(rv == ERROR_SUCCESS && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
377 DebugEvent(NULL,"Located logon script in hkDom");
380 rv = RegQueryValueExW(hkDoms, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
381 if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
383 DebugEvent(NULL,"Located logon script in hkDoms");
385 /* Note that the LogonScript in the NP key is only used if we are doing high security. */
386 else if(hkNp && ISHIGHSECURITY(opt->LogonOption))
387 rv = RegQueryValueExW(hkNp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
388 if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
390 DebugEvent(NULL,"Located logon script in hkNp");
394 WCHAR *regscript = NULL;
395 WCHAR *regexscript = NULL;
396 WCHAR *regexuscript = NULL;
397 WCHAR *wuname = NULL;
402 StringCbLength(opt->smbName, MAX_USERNAME_LENGTH, &len);
405 wuname = malloc(len * sizeof(WCHAR));
406 MultiByteToWideChar(CP_ACP,0,opt->smbName,-1,wuname,len*sizeof(WCHAR));
408 DebugEvent(NULL,"Username is set for [%S]", wuname);
410 /* dwSize still has the size of the required buffer in bytes. */
411 regscript = malloc(dwSize);
412 rv = RegQueryValueExW(hkTemp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, (LPBYTE) regscript, &dwSize);
413 if(rv != ERROR_SUCCESS) {/* what the ..? */
414 DebugEvent(NULL,"Can't look up logon script [%d]",rv);
415 goto doneLogonScript;
418 DebugEvent(NULL,"Found logon script [%S]", regscript);
420 if(dwType == REG_EXPAND_SZ) {
423 dwSize += MAX_PATH * sizeof(WCHAR); /* make room for environment expansion. */
424 regexscript = malloc(dwSize);
425 dwReq = ExpandEnvironmentStringsW(regscript, regexscript, dwSize / sizeof(WCHAR));
427 regscript = regexscript;
429 if(dwReq > (dwSize / sizeof(WCHAR))) {
430 DebugEvent(NULL,"Overflow while expanding environment strings.");
431 goto doneLogonScript;
435 DebugEvent(NULL,"After expanding env strings [%S]", regscript);
437 if(wcsstr(regscript, L"%s")) {
438 dwSize += len * sizeof(WCHAR); /* make room for username expansion */
439 regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
440 hr = StringCbPrintfW(regexuscript, dwSize, regscript, wuname);
442 regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
443 hr = StringCbCopyW(regexuscript, dwSize, regscript);
446 DebugEvent(NULL,"After expanding username [%S]", regexuscript);
449 opt->logonScript = regexuscript;
451 LocalFree(regexuscript);
454 if(wuname) free(wuname);
455 if(regscript) free(regscript);
456 if(regexscript) free(regexscript);
460 if(hkNp) RegCloseKey(hkNp);
461 if(hkDom) RegCloseKey(hkDom);
462 if(hkDoms) RegCloseKey(hkDoms);
463 if(hkParm) RegCloseKey(hkParm);
466 #undef LOOKUPKEYCHAIN
468 /* Try to find out which cell the given path is in. We must retain
469 the contents of *cell in case of failure. *cell is assumed to be
470 at least cellLen chars */
471 DWORD GetFileCellName(char * path, char * cell, size_t cellLen) {
472 struct ViceIoctl blob;
473 char tcell[MAX_PATH];
477 blob.out_size = MAX_PATH;
480 code = pioctl(path, VIOC_FILE_CELL_NAME, &blob, 1);
483 strncpy(cell, tcell, cellLen);
484 cell[cellLen - 1] = '\0';
492 UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen)
496 GetCPInfo(CP_ACP, &CodePageInfo);
498 if (CodePageInfo.MaxCharSize > 1)
499 // Only supporting non-Unicode strings
502 if (uInputString.Buffer && ((LPBYTE) uInputString.Buffer)[1] == '\0')
504 // Looks like unicode, better translate it
505 // UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS
506 WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,
507 lpszOutputString, nOutStringLen-1, NULL, NULL);
508 lpszOutputString[min(uInputString.Length/2,nOutStringLen-1)] = '\0';
512 lpszOutputString[0] = '\0';
514 } // UnicodeStringToANSI
516 DWORD APIENTRY NPLogonNotify(
518 LPCWSTR lpAuthentInfoType,
519 LPVOID lpAuthentInfo,
520 LPCWSTR lpPreviousAuthentInfoType,
521 LPVOID lpPreviousAuthentInfo,
522 LPWSTR lpStationName,
523 LPVOID StationHandle,
524 LPWSTR *lpLogonScript)
526 char uname[MAX_USERNAME_LENGTH]="";
527 char password[MAX_PASSWORD_LENGTH]="";
528 char logonDomain[MAX_DOMAIN_LENGTH]="";
529 char cell[256]="<non-integrated logon>";
530 char homePath[MAX_PATH]="";
532 MSV1_0_INTERACTIVE_LOGON *IL;
542 DWORD LSPtype, LSPsize;
545 HWND hwndOwner = (HWND)StationHandle;
547 BOOLEAN afsWillAutoStart;
549 BOOLEAN uppercased_name = TRUE;
551 LogonOptions_t opt; /* domain specific logon options */
555 /* Initialize Logon Script to none */
558 /* TODO: We should check the value of lpAuthentInfoType before assuming that it is
559 MSV1_0_INTERACTIVE_LOGON though for our purposes KERB_INTERACTIVE_LOGON is
560 co-incidentally equivalent. */
561 IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
563 /* Are we interactive? */
564 interactive = (wcscmp(lpStationName, L"WinSta0") == 0);
566 /* Convert from Unicode to ANSI */
568 /*TODO: Use SecureZeroMemory to erase passwords */
569 UnicodeStringToANSI(IL->UserName, uname, 256);
570 UnicodeStringToANSI(IL->Password, password, 256);
571 UnicodeStringToANSI(IL->LogonDomainName, logonDomain, 256);
573 /* Make sure AD-DOMANS sent from login that is sent to us is striped */
574 ctemp = strchr(uname, '@');
575 if (ctemp) *ctemp = 0;
577 /* is the name all uppercase? */
578 for ( ctemp = uname; *ctemp ; ctemp++) {
579 if ( islower(*ctemp) ) {
580 uppercased_name = FALSE;
585 (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
586 0, KEY_QUERY_VALUE, &NPKey);
587 LSPsize=sizeof(TraceOption);
588 RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
589 &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
597 GetDomainLogonOptions( lpLogonId, uname, logonDomain, &opt );
598 retryInterval = opt.retryInterval;
599 sleepInterval = opt.sleepInterval;
600 *lpLogonScript = opt.logonScript;
602 DebugEvent(NULL,"Got logon script: %S",opt.logonScript);
604 afsWillAutoStart = AFSWillAutoStart();
606 DebugEvent("AFS AfsLogon - NPLogonNotify","LogonOption[%x], Service AutoStart[%d]",
607 opt.LogonOption,afsWillAutoStart);
609 /* Check for zero length password if integrated logon*/
610 if ( ISLOGONINTEGRATED(opt.LogonOption) ) {
611 if ( password[0] == 0 ) {
613 reason = "zero length password is illegal";
617 /* Get cell name if doing integrated logon.
618 We might overwrite this if we are logging into an AD realm and we find out that
619 the user's home dir is in some other cell. */
620 code = cm_GetRootCellName(cell);
623 reason = "unknown cell";
627 /* We get the user's home directory path, if applicable, though we can't lookup the
628 cell right away because the client service may not have started yet. This call
629 also sets the AD_REALM flag in opt.flags if applicable. */
630 if(ISREMOTE(opt.flags))
631 GetAdHomePath(homePath,MAX_PATH,lpLogonId,&opt);
634 /* loop until AFS is started. */
636 if(ISADREALM(opt.flags)) {
637 code = GetFileCellName(homePath,cell,256);
639 DebugEvent(NULL,"profile path [%s] is in cell [%s]",homePath,cell);
641 /* Don't bail out if GetFileCellName failed.
642 * The home dir may not be in AFS after all.
647 /* if Integrated Logon */
648 if (ISLOGONINTEGRATED(opt.LogonOption))
650 if ( KFW_is_available() ) {
651 code = KFW_AFS_get_cred(uname, cell, password, 0, opt.smbName, &reason);
652 DebugEvent(NULL,"KFW_AFS_get_cred uname=[%s] smbname=[%s] cell=[%s] code=[%d]",uname,opt.smbName,cell,code);
655 code = ka_UserAuthenticateGeneral2(KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
656 uname, "", cell, password, opt.smbName, 0, &pw_exp, 0,
658 DebugEvent("AFS AfsLogon - (INTEGRATED only)ka_UserAuthenticateGeneral2","Code[%x]",
661 if ( code && code != KTC_NOCM && code != KTC_NOCMRPC && uppercased_name ) {
662 for ( ctemp = uname; *ctemp ; ctemp++) {
663 *ctemp = tolower(*ctemp);
665 uppercased_name = FALSE;
670 /*JUST check to see if its running*/
671 if (IsServiceRunning())
674 if (!afsWillAutoStart)
678 /* is service started yet?*/
679 DebugEvent("AFS AfsLogon - ka_UserAuthenticateGeneral2","Code[%x] uname[%s] Cell[%s]",
682 /* If we've failed because the client isn't running yet and the
683 * client is set to autostart (and therefore it makes sense for
684 * us to wait for it to start) then sleep a while and try again.
685 * If the error was something else, then give up. */
686 if (code != KTC_NOCM && code != KTC_NOCMRPC || !afsWillAutoStart)
689 /* If the retry interval has expired and we still aren't
690 * logged in, then just give up if we are not in interactive
691 * mode or the failSilently flag is set, otherwise let the
692 * user know we failed and give them a chance to try again. */
693 if (retryInterval <= 0) {
694 reason = "AFS not running";
695 if (!interactive || opt.failSilently)
697 flag = MessageBox(hwndOwner,
698 "AFS is still starting. Retry?",
700 MB_ICONQUESTION | MB_RETRYCANCEL);
701 if (flag == IDCANCEL)
704 /* Wait just a little while and try again */
705 retryInterval = sleepInterval = DEFAULT_SLEEP_INTERVAL;
708 if (retryInterval < sleepInterval)
709 sleepInterval = retryInterval;
711 Sleep(sleepInterval * 1000);
713 retryInterval -= sleepInterval;
716 /* remove any kerberos 5 tickets currently held by the SYSTEM account */
717 if ( KFW_is_available() )
718 KFW_AFS_destroy_tickets_for_cell(cell);
723 StringCbPrintf(msg, sizeof(msg), "Integrated login failed: %s", reason);
725 if (interactive && !opt.failSilently)
726 MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
731 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
733 ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
735 DeregisterEventSource(h);
737 code = MapAuthError(code);
740 if (ISLOGONINTEGRATED(LogonOption) && (code!=0))
743 LocalFree(*lpLogonScript);
744 *lpLogonScript = NULL;
745 if (!afsWillAutoStart) // its not running, so if not autostart or integrated logon then just skip
751 if(opt.smbName) free(opt.smbName);
753 DebugEvent("AFS AfsLogon - Exit","Return Code[%x]",code);
757 DWORD APIENTRY NPPasswordChangeNotify(
758 LPCWSTR lpAuthentInfoType,
759 LPVOID lpAuthentInfo,
760 LPCWSTR lpPreviousAuthentInfoType,
761 LPVOID lpPreviousAuthentInfo,
762 LPWSTR lpStationName,
763 LPVOID StationHandle,
766 DebugEvent0("AFS AfsLogon - NPPasswordChangeNotify");
772 VOID AFS_Logoff_Event(
773 PWLX_NOTIFICATION_INFO pInfo )
776 if (code = ktc_ForgetAllTokens())
777 DebugEvent(NULL,"AFS AfsLogon - AFS_Logoff_Event - ForgetAllTokens failed [%lX]",code);
779 DebugEvent0("AFS AfsLogon - AFS_Logoff_Event - ForgetAllTokens succeeded");