38f74997d5f05a42181d53455046c96e78f59ea1
[openafs.git] / src / WINNT / afsd / afslogon.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
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
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <roken.h>
13
14 #include <io.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <fcntl.h>
18
19 #include <winsock2.h>
20 #include <winioctl.h>
21 #define SECURITY_WIN32
22 #include <sspi.h>
23 #include <lm.h>
24 #include <nb30.h>
25 #include <sddl.h>
26
27 #include "afslogon.h"
28
29 #include <afs/stds.h>
30 #include <afs/pioctl_nt.h>
31 #include <afs/kautils.h>
32
33 #include "afsd.h"
34 #include "cm_config.h"
35 #include "krb.h"
36 #include "afskfw.h"
37 #include "lanahelper.h"
38
39 /* Allocated in Windows Driver Kit */
40 #ifndef WNNC_NET_OPENAFS
41 #define WNNC_NET_OPENAFS     0x00390000
42 #endif
43
44 #include <WINNT\afsreg.h>
45
46 DWORD TraceOption = 0;
47 DWORD Debug = 0;
48
49 HANDLE hDLL;
50
51 #define AFS_LOGON_EVENT_NAME TEXT("AFS Logon")
52
53 void DebugEvent0(char *a)
54 {
55     HANDLE h; char *ptbuf[1];
56
57     if (!Debug && !ISLOGONTRACE(TraceOption))
58         return;
59
60     h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
61     if (h != INVALID_HANDLE_VALUE) {
62         ptbuf[0] = a;
63         ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 1008, NULL, 1, 0, (const char **)ptbuf, NULL);
64         DeregisterEventSource(h);
65     }
66 }
67
68 #define MAXBUF_ 512
69 void DebugEvent(char *b,...)
70 {
71     HANDLE h; char *ptbuf[1],buf[MAXBUF_+1];
72     va_list marker;
73
74     if (!Debug && !ISLOGONTRACE(TraceOption))
75         return;
76
77     h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
78     if (h != INVALID_HANDLE_VALUE) {
79         va_start(marker,b);
80         StringCbVPrintf(buf, MAXBUF_+1,b,marker);
81         buf[MAXBUF_] = '\0';
82         ptbuf[0] = buf;
83         ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 1008, NULL, 1, 0, (const char **)ptbuf, NULL);
84         DeregisterEventSource(h);
85         va_end(marker);
86     }
87 }
88
89 static HANDLE hInitMutex = NULL;
90 static BOOL bInit = FALSE;
91
92 BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
93 {
94     WSADATA wsaData;
95     hDLL = dll;
96
97     switch (reason) {
98     case DLL_PROCESS_ATTACH:
99         /* Initialization Mutex */
100         if (!hInitMutex)
101             hInitMutex = CreateMutex(NULL, FALSE, NULL);
102
103         WSAStartup( MAKEWORD(2,2), &wsaData );
104         break;
105
106     case DLL_PROCESS_DETACH:
107         WSACleanup();
108         CloseHandle(hInitMutex);
109         hInitMutex = NULL;
110         bInit = FALSE;
111         break;
112
113     case DLL_THREAD_ATTACH:
114     case DLL_THREAD_DETACH:
115     default:
116         /* Everything else succeeds but does nothing. */
117         break;
118     }
119
120     return TRUE;
121 }
122
123 void AfsLogonInit(void)
124 {
125     if ( bInit == FALSE ) {
126         if ( WaitForSingleObject( hInitMutex, INFINITE ) == WAIT_OBJECT_0 ) {
127             /* initAFSDirPath() initializes an array and sets a
128              * flag so that the initialization can only occur
129              * once.  No cleanup will be done when the DLL is
130              * unloaded so the initialization will not be
131              * performed again on a subsequent reload
132              */
133             initAFSDirPath();
134
135             /* ka_Init initializes a number of error tables.
136              * and then calls ka_CellConfig() which grabs
137              * an afsconf_dir structure via afsconf_Open().
138              * Upon a second attempt to call ka_CellConfig()
139              * the structure will be released with afsconf_Close()
140              * and then re-opened.  Could this corrupt memory?
141              *
142              * We only need this if we are not using KFW.
143              */
144             if (!KFW_is_available())
145                 ka_Init(0);
146             bInit = TRUE;
147         }
148         ReleaseMutex(hInitMutex);
149     }
150 }
151
152 CHAR *GenRandomName(CHAR *pbuf)
153 {
154     int i;
155     srand( (unsigned)time( NULL ) );
156     for (i=0;i<MAXRANDOMNAMELEN-1;i++)
157         pbuf[i]='a'+(rand() % 26);
158     pbuf[MAXRANDOMNAMELEN-1]=0;
159     return pbuf;
160 }
161
162 BOOLEAN AFSWillAutoStart(void)
163 {
164     SC_HANDLE scm;
165     SC_HANDLE svc;
166     BOOLEAN flag;
167     BOOLEAN result = FALSE;
168     LPQUERY_SERVICE_CONFIG pConfig = NULL;
169     DWORD BufSize;
170     LONG status;
171
172     /* Open services manager */
173     scm = OpenSCManager(NULL, NULL, GENERIC_READ);
174     if (!scm) return FALSE;
175
176     /* Open AFSD service */
177     svc = OpenService(scm, "TransarcAFSDaemon", SERVICE_QUERY_CONFIG);
178     if (!svc)
179         goto close_scm;
180
181     /* Query AFSD service config, first just to get buffer size */
182     /* Expected to fail, so don't test return value */
183     (void) QueryServiceConfig(svc, NULL, 0, &BufSize);
184     status = GetLastError();
185     if (status != ERROR_INSUFFICIENT_BUFFER)
186         goto close_svc;
187
188     /* Allocate buffer */
189     pConfig = (LPQUERY_SERVICE_CONFIG)GlobalAlloc(GMEM_FIXED,BufSize);
190     if (!pConfig)
191         goto close_svc;
192
193     /* Query AFSD service config, this time for real */
194     flag = QueryServiceConfig(svc, pConfig, BufSize, &BufSize);
195     if (!flag)
196         goto free_pConfig;
197
198     /* Is it autostart? */
199     if (pConfig->dwStartType < SERVICE_DEMAND_START)
200         result = TRUE;
201
202   free_pConfig:
203     GlobalFree(pConfig);
204   close_svc:
205     CloseServiceHandle(svc);
206   close_scm:
207     CloseServiceHandle(scm);
208
209     return result;
210 }
211
212 DWORD MapAuthError(DWORD code)
213 {
214     switch (code) {
215         /* Unfortunately, returning WN_NO_NETWORK results in the MPR abandoning
216          * logon scripts for all credential managers, although they will still
217          * receive logon notifications.  Since we don't want this, we return
218          * WN_SUCCESS.  This is highly undesirable, but we also don't want to
219          * break other network providers.
220          */
221  /* case KTC_NOCM:
222     case KTC_NOCMRPC:
223     return WN_NO_NETWORK; */
224     default: return WN_SUCCESS;
225   }
226 }
227
228 DWORD APIENTRY NPGetCaps(DWORD index)
229 {
230     switch (index) {
231     case WNNC_NET_TYPE:
232         /*
233          * The purpose of this response is to let the system
234          * know which file system the network provider is associated with
235          * Microsoft issues these values starting from 1 with the exception
236          * of WNNC_CRED_MANAGER which is 0xFFFF.  The provider type is
237          * stored in the hiword.  Pick a value that is unused.
238          */
239         return 0x1FFF0000;
240
241     case WNNC_SPEC_VERSION:
242         return WNNC_SPEC_VERSION51;
243
244     case WNNC_START:
245         /* Say we are already started, even though we might wait after we receive NPLogonNotify */
246         return 1;
247
248     default:
249         return 0;
250     }
251 }
252
253 NET_API_STATUS
254 NetUserGetProfilePath( LPCWSTR Domain, LPCWSTR UserName, char * profilePath,
255                        DWORD profilePathLen )
256 {
257     NET_API_STATUS code;
258     LPWSTR ServerName = NULL;
259     LPUSER_INFO_3 p3 = NULL;
260
261     NetGetAnyDCName(NULL, Domain, (LPBYTE *)&ServerName);
262     /* if NetGetAnyDCName fails, ServerName == NULL
263      * NetUserGetInfo will obtain local user information
264      */
265     code = NetUserGetInfo(ServerName, UserName, 3, (LPBYTE *)&p3);
266     if (code == NERR_Success)
267     {
268         code = NERR_UserNotFound;
269         if (p3) {
270             if (p3->usri3_profile) {
271                 DWORD len = lstrlenW(p3->usri3_profile);
272                 if (len > 0) {
273                     /* Convert From Unicode to ANSI (UTF-8 for future) */
274                     len = len < profilePathLen ? len : profilePathLen - 1;
275                     WideCharToMultiByte(CP_UTF8, 0, p3->usri3_profile, len, profilePath, len, NULL, NULL);
276                     profilePath[len] = '\0';
277                     code = NERR_Success;
278                 }
279             }
280             NetApiBufferFree(p3);
281         }
282     }
283     if (ServerName)
284         NetApiBufferFree(ServerName);
285     return code;
286 }
287
288 BOOL IsServiceRunning (void)
289 {
290     SERVICE_STATUS Status;
291     SC_HANDLE hManager;
292     memset (&Status, 0x00, sizeof(Status));
293     Status.dwCurrentState = SERVICE_STOPPED;
294
295     if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
296     {
297         SC_HANDLE hService;
298         if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
299         {
300             QueryServiceStatus (hService, &Status);
301             CloseServiceHandle (hService);
302         }
303
304         CloseServiceHandle (hManager);
305     }
306     DebugEvent("AFS AfsLogon - Test Service Running Return Code[%x] ?Running[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_RUNNING));
307     return (Status.dwCurrentState == SERVICE_RUNNING);
308 }
309
310 BOOL IsServiceStartPending (void)
311 {
312     SERVICE_STATUS Status;
313     SC_HANDLE hManager;
314     memset (&Status, 0x00, sizeof(Status));
315     Status.dwCurrentState = SERVICE_STOPPED;
316
317     if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
318     {
319         SC_HANDLE hService;
320         if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
321         {
322             QueryServiceStatus (hService, &Status);
323             CloseServiceHandle (hService);
324         }
325
326         CloseServiceHandle (hManager);
327     }
328     DebugEvent("AFS AfsLogon - Test Service Start Pending Return Code[%x] ?Start Pending[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_START_PENDING));
329     return (Status.dwCurrentState == SERVICE_START_PENDING);
330 }
331
332 BOOL StartTheService (void)
333 {
334     SC_HANDLE hManager;
335     DWORD gle = 0;
336
337     if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ|SERVICE_START)) != NULL)
338     {
339         SC_HANDLE hService;
340         if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ|SERVICE_START)) != NULL)
341         {
342             StartService (hService, 0, NULL);
343             gle = GetLastError();
344             CloseServiceHandle (hService);
345         } else
346             gle = GetLastError();
347
348         CloseServiceHandle (hManager);
349     }
350     DebugEvent("AFS AfsLogon - Service Start Return Code[0x%x]",gle);
351     return (gle == 0);
352 }
353
354 /* LOOKUPKEYCHAIN: macro to look up the value in the list of keys in order until it's found
355    v:variable to receive value (reference type)
356    t:type
357    d:default, in case the value isn't on any of the keys
358    n:name of value */
359 #define LOOKUPKEYCHAIN(v,t,d,n) \
360         do { \
361                 rv = ~ERROR_SUCCESS; \
362                 dwType = t; \
363                 if(hkDom) { \
364                         dwSize = sizeof(v); \
365                         rv = RegQueryValueEx(hkDom, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
366                         if(rv == ERROR_SUCCESS || rv == ERROR_MORE_DATA) \
367                             DebugEvent(#v " found in hkDom with type [%d]", dwType); \
368                 } \
369                 if(hkDoms && ((rv != ERROR_SUCCESS && rv != ERROR_MORE_DATA) || dwType != t)) { \
370                         dwSize = sizeof(v); \
371                         rv = RegQueryValueEx(hkDoms, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
372                         if(rv == ERROR_SUCCESS || rv == ERROR_MORE_DATA) \
373                             DebugEvent(#v " found in hkDoms with type [%d]", dwType); \
374                 } \
375                 if(hkNp && ((rv != ERROR_SUCCESS && rv != ERROR_MORE_DATA) || dwType != t)) { \
376                         dwSize = sizeof(v); \
377                         rv = RegQueryValueEx(hkNp, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
378                         if(rv == ERROR_SUCCESS || rv == ERROR_MORE_DATA) \
379                             DebugEvent(#v " found in hkNp with type [%d]", dwType); \
380                 } \
381                 if((rv != ERROR_SUCCESS && rv != ERROR_MORE_DATA) || dwType != t) { \
382                         v = d; \
383                         DebugEvent0(#v " being set to default"); \
384                 } \
385         } while(0)
386
387 /* Get domain specific configuration info.  We are returning void because if anything goes wrong
388    we just return defaults.
389  */
390 void
391 GetDomainLogonOptions( PLUID lpLogonId, char * username, char * domain, LogonOptions_t *opt ) {
392     HKEY hkParm = NULL; /* Service parameter */
393     HKEY hkNp = NULL;   /* network provider key */
394     HKEY hkDoms = NULL; /* domains key */
395     HKEY hkDom = NULL;  /* DOMAINS/domain key */
396     HKEY hkTemp = NULL;
397     LONG rv;
398     DWORD dwSize;
399     DWORD dwType;
400     DWORD dwDummy;
401     char computerName[MAX_COMPUTERNAME_LENGTH + 1]="";
402     char *effDomain = NULL;
403
404     memset(opt, 0, sizeof(LogonOptions_t));
405
406     DebugEvent("In GetDomainLogonOptions for user [%s] in domain [%s]", username, domain);
407     /* If the domain is the same as the Netbios computer name, we use the LOCALHOST domain name*/
408     opt->flags = LOGON_FLAG_REMOTE;
409     if(domain) {
410         dwSize = MAX_COMPUTERNAME_LENGTH + 1;
411         if(GetComputerName(computerName, &dwSize)) {
412             if(!cm_stricmp_utf8(computerName, domain)) {
413                 effDomain = "LOCALHOST";
414                 opt->flags = LOGON_FLAG_LOCAL;
415             }
416         }
417         if (effDomain == NULL)
418             effDomain = domain;
419     }
420
421     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY, 0, KEY_READ, &hkParm );
422     if(rv != ERROR_SUCCESS) {
423         hkParm = NULL;
424         DebugEvent("GetDomainLogonOption: Can't open parms key [%d]", rv);
425     }
426
427     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PROVIDER_SUBKEY, 0, KEY_READ, &hkNp );
428     if(rv != ERROR_SUCCESS) {
429         hkNp = NULL;
430         DebugEvent("GetDomainLogonOptions: Can't open NP key [%d]", rv);
431     }
432
433     if(hkNp) {
434         rv = RegOpenKeyEx( hkNp, REG_CLIENT_DOMAINS_SUBKEY, 0, KEY_READ, &hkDoms );
435         if( rv != ERROR_SUCCESS ) {
436             hkDoms = NULL;
437             DebugEvent("GetDomainLogonOptions: Can't open Domains key [%d]", rv);
438         }
439     }
440
441     if(hkDoms && effDomain) {
442         rv = RegOpenKeyEx( hkDoms, effDomain, 0, KEY_READ, &hkDom );
443         if( rv != ERROR_SUCCESS ) {
444             hkDom = NULL;
445             DebugEvent("GetDomainLogonOptions: Can't open domain key for [%s] [%d]", effDomain, rv);
446             /* If none of the domains match, we shouldn't use the domain key either */
447             RegCloseKey(hkDoms);
448             hkDoms = NULL;
449         }
450     } else
451         DebugEvent0("Not opening domain key");
452
453     /* Each individual can either be specified on the domain key, the domains key or in the
454        net provider key.  They fail over in that order.  If none is found, we just use the
455        defaults. */
456
457     /* LogonOption */
458     LOOKUPKEYCHAIN(opt->LogonOption, REG_DWORD, DEFAULT_LOGON_OPTION, REG_CLIENT_LOGON_OPTION_PARM);
459
460     /* FailLoginsSilently */
461     dwSize = sizeof(dwDummy);
462     rv = RegQueryValueEx(hkParm, REG_CLIENT_FAIL_SILENTLY_PARM, 0, &dwType, (LPBYTE) &dwDummy, &dwSize);
463     if (rv != ERROR_SUCCESS)
464         LOOKUPKEYCHAIN(dwDummy, REG_DWORD, DEFAULT_FAIL_SILENTLY, REG_CLIENT_FAIL_SILENTLY_PARM);
465     opt->failSilently = dwDummy ? 1 :0;
466
467     /* Retry interval */
468     LOOKUPKEYCHAIN(opt->retryInterval, REG_DWORD, DEFAULT_RETRY_INTERVAL, REG_CLIENT_RETRY_INTERVAL_PARM);
469
470     /* Sleep interval */
471     LOOKUPKEYCHAIN(opt->sleepInterval, REG_DWORD, DEFAULT_SLEEP_INTERVAL, REG_CLIENT_SLEEP_INTERVAL_PARM);
472
473     if(!ISLOGONINTEGRATED(opt->LogonOption)) {
474         DebugEvent0("Integrated logon disabled");
475         goto cleanup; /* no need to lookup the logon script */
476     }
477
478     /* come up with SMB username */
479     if(ISHIGHSECURITY(opt->LogonOption)) {
480         DebugEvent0("High Security Mode active");
481         opt->smbName = malloc( MAXRANDOMNAMELEN );
482         if (opt->smbName == NULL)
483             goto cleanup;
484         GenRandomName(opt->smbName);
485     } else if (lpLogonId) {
486         /* username and domain for logon session is not necessarily the same as
487            username and domain passed into network provider. */
488         PSECURITY_LOGON_SESSION_DATA plsd=NULL;
489         char lsaUsername[MAX_USERNAME_LENGTH]="";
490         char lsaDomain[MAX_DOMAIN_LENGTH]="";
491         size_t len, tlen;
492         NTSTATUS Status;
493
494         Status = LsaGetLogonSessionData(lpLogonId, &plsd);
495         if ( FAILED(Status) || plsd == NULL ) {
496             DebugEvent("LsaGetLogonSessionData failed [0x%x]", Status);
497             goto bad_strings;
498         }
499
500         if (!UnicodeStringToANSI(plsd->UserName, lsaUsername, MAX_USERNAME_LENGTH))
501             goto bad_strings;
502
503         if (!UnicodeStringToANSI(plsd->LogonDomain, lsaDomain, MAX_DOMAIN_LENGTH))
504             goto bad_strings;
505
506         DebugEvent("PLSD username[%s] domain[%s]",lsaUsername,lsaDomain);
507
508         if(SUCCEEDED(StringCbLength(lsaUsername, MAX_USERNAME_LENGTH, &tlen)))
509             len = tlen;
510         else
511             goto bad_strings;
512
513         if(SUCCEEDED(StringCbLength(lsaDomain, MAX_DOMAIN_LENGTH, &tlen)))
514             len += tlen;
515         else
516             goto bad_strings;
517
518         len += 2;
519
520         opt->smbName = malloc(len);
521         if (opt->smbName == NULL)
522             goto cleanup;
523
524         StringCbCopy(opt->smbName, len, lsaDomain);
525         StringCbCat(opt->smbName, len, "\\");
526         StringCbCat(opt->smbName, len, lsaUsername);
527
528         strlwr(opt->smbName);
529
530       bad_strings:
531         if (plsd)
532             LsaFreeReturnBuffer(plsd);
533     }
534     if (opt->smbName == NULL) {
535         size_t len;
536
537         DebugEvent("Constructing username using [%s] and [%s]",
538                    username, domain);
539
540         len = strlen(username) + strlen(domain) + 2;
541
542         opt->smbName = malloc(len);
543         if (opt->smbName == NULL)
544             goto cleanup;
545
546         StringCbCopy(opt->smbName, len, domain);
547         StringCbCat(opt->smbName, len, "\\");
548         StringCbCat(opt->smbName, len, username);
549
550         strlwr(opt->smbName);
551     }
552
553     DebugEvent0("Looking up logon script");
554     /* Logon script */
555     /* First find out where the key is */
556     hkTemp = NULL;
557     rv = ~ERROR_SUCCESS;
558     dwType = 0;
559     if(hkDom)
560         rv = RegQueryValueExW(hkDom, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
561     if(rv == ERROR_SUCCESS && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
562         hkTemp = hkDom;
563         DebugEvent0("Located logon script in hkDom");
564     }
565     else if(hkDoms) {
566         rv = RegQueryValueExW(hkDoms, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
567         if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
568             hkTemp = hkDoms;
569             DebugEvent0("Located logon script in hkDoms");
570         }
571         /* Note that the LogonScript in the NP key is only used if we are doing high security. */
572         else if(hkNp && ISHIGHSECURITY(opt->LogonOption)) {
573             rv = RegQueryValueExW(hkNp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
574             if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
575                 hkTemp = hkNp;
576                 DebugEvent0("Located logon script in hkNp");
577             }
578         }
579     }
580
581     if(hkTemp) {
582         WCHAR *regscript        = NULL;
583         WCHAR *regexscript      = NULL;
584         WCHAR *regexuscript     = NULL;
585         WCHAR *wuname           = NULL;
586         HRESULT hr;
587
588         size_t len;
589
590         StringCbLength(opt->smbName, MAX_USERNAME_LENGTH, &len);
591         len ++;
592
593         wuname = malloc(len * sizeof(WCHAR));
594         if (!wuname)
595             goto doneLogonScript;
596         MultiByteToWideChar(CP_ACP,0,opt->smbName,-1,wuname,(int)(len*sizeof(WCHAR)));
597
598         DebugEvent("Username is set for [%S]", wuname);
599
600         /* dwSize still has the size of the required buffer in bytes. */
601         regscript = malloc(dwSize);
602         if (!regscript)
603             goto doneLogonScript;
604         rv = RegQueryValueExW(hkTemp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, (LPBYTE) regscript, &dwSize);
605         if(rv != ERROR_SUCCESS) {/* what the ..? */
606             DebugEvent("Can't look up logon script rv [%d] size [%d] gle %d",rv, dwSize, GetLastError());
607             goto doneLogonScript;
608         }
609
610         DebugEvent("Found logon script [%S]", regscript);
611
612         if(dwType == REG_EXPAND_SZ) {
613             DWORD dwReq;
614
615             dwSize += MAX_PATH * sizeof(WCHAR);  /* make room for environment expansion. */
616             regexscript = malloc(dwSize);
617             if (!regexscript)
618                 goto doneLogonScript;
619             dwReq = ExpandEnvironmentStringsW(regscript, regexscript, dwSize / sizeof(WCHAR));
620             free(regscript);
621             regscript = regexscript;
622             regexscript = NULL;
623             if(dwReq > (dwSize / sizeof(WCHAR))) {
624                 DebugEvent0("Overflow while expanding environment strings.");
625                 goto doneLogonScript;
626             }
627         }
628
629         DebugEvent("After expanding env strings [%S]", regscript);
630
631         if(wcsstr(regscript, L"%s")) {
632             dwSize += (DWORD)(len * sizeof(WCHAR)); /* make room for username expansion */
633             regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
634             if (!regexuscript)
635                 goto doneLogonScript;
636             hr = StringCbPrintfW(regexuscript, dwSize, regscript, wuname);
637         } else {
638             regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
639             if (!regexuscript)
640                 goto doneLogonScript;
641             hr = StringCbCopyW(regexuscript, dwSize, regscript);
642         }
643
644         DebugEvent("After expanding username [%S]", regexuscript);
645
646         if(hr == S_OK)
647             opt->logonScript = regexuscript;
648         else
649             LocalFree(regexuscript);
650
651       doneLogonScript:
652         if(wuname) free(wuname);
653         if(regscript) free(regscript);
654         if(regexscript) free(regexscript);
655     }
656
657     DebugEvent0("Looking up TheseCells");
658     /* TheseCells */
659     /* First find out where the key is */
660     hkTemp = NULL;
661     rv = ~ERROR_SUCCESS;
662     dwSize = 0;
663     if (hkDom)
664         rv = RegQueryValueEx(hkDom, REG_CLIENT_THESE_CELLS_PARM, 0, &dwType, NULL, &dwSize);
665     if (rv == ERROR_SUCCESS && dwType == REG_MULTI_SZ) {
666         hkTemp = hkDom;
667         DebugEvent("Located TheseCells in hkDom size %d", dwSize);
668     } else if (hkDoms) {
669         rv = RegQueryValueEx(hkDoms, REG_CLIENT_THESE_CELLS_PARM, 0, &dwType, NULL, &dwSize);
670         if (rv == ERROR_SUCCESS && !hkTemp && dwType == REG_MULTI_SZ) {
671             hkTemp = hkDoms;
672             DebugEvent("Located TheseCells in hkDoms size %d", dwSize);
673         } else if (hkNp) {
674             rv = RegQueryValueEx(hkNp, REG_CLIENT_THESE_CELLS_PARM, 0, &dwType, NULL, &dwSize);
675             if (rv == ERROR_SUCCESS && !hkTemp && dwType == REG_MULTI_SZ) {
676                 hkTemp = hkNp;
677                 DebugEvent("Located TheseCells in hkNp size %d", dwSize);
678             }
679         }
680     }
681
682     if (hkTemp) {
683         CHAR * thesecells = NULL, *p;
684
685         /* dwSize still has the size of the required buffer in bytes. */
686         thesecells = malloc(dwSize*2);
687         if (!thesecells)
688             goto doneTheseCells;
689         dwSize *= 2;
690         SetLastError(0);
691         rv = RegQueryValueEx(hkTemp, REG_CLIENT_THESE_CELLS_PARM, 0, NULL, (LPBYTE) thesecells, &dwSize);
692         if(rv != ERROR_SUCCESS) {/* what the ..? */
693             DebugEvent("Can't look up TheseCells rv [%d] size [%d] gle [%d]",rv, dwSize, GetLastError());
694             goto doneTheseCells;
695         }
696
697         /* TheseCells is a REG_MULTI_SZ */
698         if ( thesecells && thesecells[0]) {
699             for ( p=thesecells; *p; p += (strlen(p) + 1)) {
700                 DebugEvent("Found TheseCells [%s]", p);
701             }
702             opt->theseCells = thesecells;
703             thesecells = NULL;
704         } else {
705             DebugEvent("TheseCells [REG_MULTI_SZ] not found");
706         }
707
708       doneTheseCells:
709         if (thesecells) free(thesecells);
710     }
711
712     DebugEvent0("Looking up Realm");
713     /* Realm */
714     /* First find out where the key is */
715     hkTemp = NULL;
716     rv = ~ERROR_SUCCESS;
717     dwSize = 0;
718     if (hkDom)
719         rv = RegQueryValueEx(hkDom, REG_CLIENT_REALM_PARM, 0, &dwType, NULL, &dwSize);
720     if (rv == ERROR_SUCCESS && dwType == REG_SZ) {
721         hkTemp = hkDom;
722         DebugEvent("Located Realm in hkDom size %d", dwSize);
723     } else if (hkDoms) {
724         rv = RegQueryValueEx(hkDoms, REG_CLIENT_REALM_PARM, 0, &dwType, NULL, &dwSize);
725         if (rv == ERROR_SUCCESS && !hkTemp && dwType == REG_SZ) {
726             hkTemp = hkDoms;
727             DebugEvent("Located Realm in hkDoms size %d", dwSize);
728         } else if (hkNp) {
729             rv = RegQueryValueEx(hkNp, REG_CLIENT_REALM_PARM, 0, &dwType, NULL, &dwSize);
730             if (rv == ERROR_SUCCESS && !hkTemp && dwType == REG_SZ) {
731                 hkTemp = hkNp;
732                 DebugEvent("Located Realm in hkNp size %d", dwSize);
733             }
734         }
735     }
736
737     if (hkTemp) {
738         CHAR * realm = NULL;
739
740         /* dwSize still has the size of the required buffer in bytes. */
741         realm = malloc(dwSize*2);
742         if (!realm)
743             goto doneRealm;
744         dwSize *=2;
745         SetLastError(0);
746         rv = RegQueryValueEx(hkTemp, REG_CLIENT_REALM_PARM, 0, NULL, (LPBYTE) realm, &dwSize);
747         if(rv != ERROR_SUCCESS) {/* what the ..? */
748             DebugEvent("Can't look up Realm rv [%d] size [%d] gle [%d]",rv, dwSize, GetLastError());
749             goto doneRealm;
750         }
751
752         DebugEvent("Found Realm [%s]", realm);
753         opt->realm = realm;
754         realm = NULL;
755
756       doneRealm:
757         if (realm) free(realm);
758     }
759
760   cleanup:
761     if(hkNp) RegCloseKey(hkNp);
762     if(hkDom) RegCloseKey(hkDom);
763     if(hkDoms) RegCloseKey(hkDoms);
764     if(hkParm) RegCloseKey(hkParm);
765 }
766
767 #undef LOOKUPKEYCHAIN
768
769 /* Try to find out which cell the given path is in.  We must retain
770    the contents of *cell in case of failure. *cell is assumed to be
771    at least cellLen chars */
772 DWORD GetFileCellName(char * path, char * cell, size_t cellLen) {
773     struct ViceIoctl blob;
774     char tcell[MAX_PATH];
775     DWORD code;
776
777     blob.in_size = 0;
778     blob.out_size = MAX_PATH;
779     blob.out = tcell;
780
781     code = pioctl(path, VIOC_FILE_CELL_NAME, &blob, 1);
782
783     if(!code) {
784         strncpy(cell, tcell, cellLen);
785         cell[cellLen - 1] = '\0';
786     }
787     return code;
788 }
789
790
791 static BOOL
792 WINAPI
793 UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen)
794 {
795     CPINFO CodePageInfo;
796
797     GetCPInfo(CP_ACP, &CodePageInfo);
798
799     if (CodePageInfo.MaxCharSize > 1)
800         // Only supporting non-Unicode strings
801         return FALSE;
802
803     if (uInputString.Buffer && ((LPBYTE) uInputString.Buffer)[1] == '\0')
804     {
805         // Looks like unicode, better translate it
806         // UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS
807         WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,
808                             lpszOutputString, nOutStringLen-1, NULL, NULL);
809         lpszOutputString[min(uInputString.Length/2,nOutStringLen-1)] = '\0';
810         return TRUE;
811     }
812
813     lpszOutputString[0] = '\0';
814     return FALSE;
815 }  // UnicodeStringToANSI
816
817 DWORD APIENTRY
818 NPLogonNotify(
819         PLUID lpLogonId,
820         LPCWSTR lpAuthentInfoType,
821         LPVOID lpAuthentInfo,
822         LPCWSTR lpPreviousAuthentInfoType,
823         LPVOID lpPreviousAuthentInfo,
824         LPWSTR lpStationName,
825         LPVOID StationHandle,
826         LPWSTR *lpLogonScript)
827 {
828     char uname[MAX_USERNAME_LENGTH]="";
829     char password[MAX_PASSWORD_LENGTH]="";
830     char logonDomain[MAX_DOMAIN_LENGTH]="";
831     char cell[256]="<non-integrated logon>";
832     char homePath[MAX_PATH]="";
833     char szLogonId[128] = "";
834
835     MSV1_0_INTERACTIVE_LOGON *IL;
836
837     DWORD code = 0, code2;
838
839     int pw_exp;
840     char *reason;
841     char *ctemp;
842
843     BOOLEAN interactive;
844     BOOLEAN flag;
845     DWORD LSPtype, LSPsize;
846     HKEY NPKey;
847
848     HWND hwndOwner = (HWND)StationHandle;
849
850     BOOLEAN afsWillAutoStart;
851
852     BOOLEAN lowercased_name = TRUE;
853
854     LogonOptions_t opt; /* domain specific logon options */
855     int retryInterval;
856     int sleepInterval;
857
858     CtxtHandle LogonContext;
859
860     /* Are we interactive? */
861     interactive = (wcsicmp(lpStationName, L"WinSta0") == 0);
862
863 #ifdef DISABLE_NON_INTERACTIVE
864     /* Do not do anything if the logon session is not interactive. */
865     if (!interactive)
866         return 0;
867 #endif
868
869     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
870                          0, KEY_QUERY_VALUE, &NPKey);
871     LSPsize=sizeof(TraceOption);
872     RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
873                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
874
875     RegCloseKey (NPKey);
876
877     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PROVIDER_SUBKEY,
878                          0, KEY_QUERY_VALUE, &NPKey);
879     LSPsize=sizeof(Debug);
880     RegQueryValueEx(NPKey, REG_CLIENT_DEBUG_PARM, NULL,
881                      &LSPtype, (LPBYTE)&Debug, &LSPsize);
882
883     RegCloseKey (NPKey);
884
885     DebugEvent("NPLogonNotify - LoginId(%d,%d)", lpLogonId->HighPart, lpLogonId->LowPart);
886
887     /* Make sure the AFS Libraries are initialized */
888     AfsLogonInit();
889
890     /* Initialize Logon Script to none */
891     *lpLogonScript=NULL;
892
893     /* MSV1_0_INTERACTIVE_LOGON and KERB_INTERACTIVE_LOGON are equivalent for
894      * our purposes */
895
896     if ( wcsicmp(lpAuthentInfoType,L"MSV1_0:Interactive") &&
897          wcsicmp(lpAuthentInfoType,L"Kerberos:Interactive") )
898     {
899         DebugEvent("Unsupported Authentication Info Type: %S",
900                    lpAuthentInfoType);
901         return 0;
902     }
903
904     IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
905
906     /* Convert from Unicode to ANSI */
907
908     /*TODO: Use SecureZeroMemory to erase passwords */
909     if (!UnicodeStringToANSI(IL->UserName, uname, MAX_USERNAME_LENGTH) ||
910          !UnicodeStringToANSI(IL->Password, password, MAX_PASSWORD_LENGTH) ||
911          !UnicodeStringToANSI(IL->LogonDomainName, logonDomain, MAX_DOMAIN_LENGTH))
912         return 0;
913
914     /* Make sure AD-DOMAINS sent from login that is sent to us is stripped */
915     ctemp = strchr(uname, '@');
916     if (ctemp) {
917         *ctemp = 0;
918         ctemp++;
919         if ( logonDomain[0] == '\0' )
920             StringCchCopy(logonDomain, MAX_DOMAIN_LENGTH, ctemp);
921     }
922
923     /* is the name all lowercase? */
924     for ( ctemp = uname; *ctemp ; ctemp++) {
925         if ( !islower(*ctemp) ) {
926             lowercased_name = FALSE;
927             break;
928         }
929     }
930
931     /*
932      * Get Logon options
933      */
934
935     GetDomainLogonOptions( lpLogonId, uname, logonDomain, &opt );
936     retryInterval = opt.retryInterval;
937     sleepInterval = opt.sleepInterval;
938     *lpLogonScript = opt.logonScript;
939
940     if (retryInterval < sleepInterval)
941         sleepInterval = retryInterval;
942
943     DebugEvent("Got logon script: [%S]", opt.logonScript);
944
945     afsWillAutoStart = AFSWillAutoStart();
946
947     DebugEvent("LogonOption[%x], Service AutoStart[%d]",
948                 opt.LogonOption,afsWillAutoStart);
949
950     /* Check for zero length password if integrated logon*/
951     if ( ISLOGONINTEGRATED(opt.LogonOption) )  {
952         if ( password[0] == 0 ) {
953             DebugEvent0("Password is the empty string");
954             code = GT_PW_NULL;
955             reason = "zero length password is illegal";
956             code=0;
957         }
958
959         /* Get cell name if doing integrated logon.
960            We might overwrite this if we are logging into an AD realm and we find out that
961            the user's home dir is in some other cell. */
962         DebugEvent0("About to call cm_GetRootCellName()");
963         code = cm_GetRootCellName(cell);
964         if (code < 0) {
965             DebugEvent0("Unable to obtain Root Cell");
966             code = KTC_NOCELL;
967             reason = "unknown cell";
968         } else {
969             DebugEvent("Default cell is %s", cell);
970             code = 0;
971         }
972
973         /* We get the user's home directory path, if applicable, though we can't lookup the
974            cell right away because the client service may not have started yet. This call
975            also sets the AD_REALM flag in opt.flags if applicable. */
976         if (ISREMOTE(opt.flags)) {
977             DebugEvent0("Is Remote");
978             GetAdHomePath(homePath,MAX_PATH,lpLogonId,&opt);
979         }
980     }
981
982     AFSCreatePAG(lpLogonId);
983
984     if (afsWillAutoStart) {
985         /*
986          * If the service is configured for auto start but hasn't started yet,
987          * give it a shove.
988          */
989         if (!(IsServiceRunning() || IsServiceStartPending()))
990             StartTheService();
991
992         /* loop until AFS is started or fails. */
993         while ( IsServiceStartPending() ) {
994             Sleep(10);
995         }
996
997         while (IsServiceRunning() && code != KTC_NOCM && code != KTC_NOCMRPC && code != KTC_NOCELL) {
998             DebugEvent("while(autostart) LogonOption[%x], Service AutoStart[%d]",
999                         opt.LogonOption,afsWillAutoStart);
1000
1001             if (ISADREALM(opt.flags)) {
1002                 code = GetFileCellName(homePath,cell,256);
1003                 if (!code) {
1004                     DebugEvent("profile path [%s] is in cell [%s]",homePath,cell);
1005                 }
1006                 /* Don't bail out if GetFileCellName failed.
1007                  * The home dir may not be in AFS after all.
1008                  */
1009             } else
1010                 code=0;
1011
1012             /* if Integrated Logon  */
1013             if (ISLOGONINTEGRATED(opt.LogonOption))
1014             {
1015                 LogonSSP(lpLogonId, &LogonContext);
1016                 ImpersonateSecurityContext(&LogonContext);
1017
1018                 if ( KFW_is_available() ) {
1019                     SetEnvironmentVariable(DO_NOT_REGISTER_VARNAME, "");
1020                     if (opt.realm) {
1021                         char * principal, *p;
1022                         size_t len, tlen;
1023
1024                         StringCchLength(opt.realm, MAX_DOMAIN_LENGTH, &tlen);
1025                         len = tlen;
1026                         StringCchLength(uname, MAX_USERNAME_LENGTH, &tlen);
1027                         len += tlen + 2;
1028
1029                         /* tlen is now the length of uname in characters */
1030                         principal = (char *)malloc(len * sizeof(char));
1031                         if ( principal ) {
1032                             StringCchCopy(principal, len, uname);
1033                             p = principal + tlen;
1034                             *p++ = '@';
1035                             StringCchCopy(p, len - tlen -1, opt.realm);
1036                             code = KFW_AFS_get_cred(principal, cell, password, 0, opt.smbName, &reason);
1037                             DebugEvent("KFW_AFS_get_cred  uname=[%s] smbname=[%s] cell=[%s] code=[%d]",
1038                                             principal,opt.smbName,cell,code);
1039                             free(principal);
1040                         }
1041                     } else {
1042                         code = KFW_AFS_get_cred(uname, cell, password, 0, opt.smbName, &reason);
1043                         DebugEvent("KFW_AFS_get_cred  uname=[%s] smbname=[%s] cell=[%s] code=[%d]",
1044                                     uname,opt.smbName,cell,code);
1045                     }
1046                     SetEnvironmentVariable(DO_NOT_REGISTER_VARNAME, NULL);
1047                     if (code == 0 && opt.theseCells) {
1048                         char * principal, *p;
1049                         size_t len, tlen;
1050
1051                         StringCchLength(opt.realm ? opt.realm : cell, MAX_DOMAIN_LENGTH, &tlen);
1052                         len = tlen;
1053                         StringCchLength(uname, MAX_USERNAME_LENGTH, &tlen);
1054                         len += tlen + 2;
1055
1056                         /* tlen is now the length of uname in characters */
1057                         principal = (char *)malloc(len * sizeof(char));
1058                         if ( principal ) {
1059                             StringCchCopy(principal, len, uname);
1060                             p = principal + tlen;
1061                             *p++ = '@';
1062                             if (opt.realm) {
1063                                 StringCchCopy(p, len - tlen -1, opt.realm);
1064                             } else {
1065                                 StringCchCopy(p, len - tlen - 1, cell);
1066                                 for ( ;*p; p++) {
1067                                     *p = toupper(*p);
1068                                 }
1069                             }
1070                             p = opt.theseCells;
1071                             while ( *p ) {
1072                                 if ( cm_stricmp_utf8(p, cell) ) {
1073                                     SetEnvironmentVariable(DO_NOT_REGISTER_VARNAME, "");
1074                                     code2 = KFW_AFS_get_cred(principal, p, password, 0, opt.smbName, &reason);
1075                                     SetEnvironmentVariable(DO_NOT_REGISTER_VARNAME, NULL);
1076                                     DebugEvent("KFW_AFS_get_cred  uname=[%s] smbname=[%s] cell=[%s] code=[%d]",
1077                                                principal,opt.smbName,p,code2);
1078                                 }
1079                                 p += strlen(p) + 1;
1080                             }
1081                             free(principal);
1082                         }
1083                     }
1084                 } else {
1085                     code = ka_UserAuthenticateGeneral2(KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
1086                                                         uname, "", cell, password, opt.smbName, 0, &pw_exp, 0,
1087                                                         &reason);
1088                     DebugEvent("AFS AfsLogon - (INTEGRATED only)ka_UserAuthenticateGeneral2 Code[%x] uname[%s] smbname=[%s] Cell[%s] PwExp=[%d] Reason=[%s]",
1089                                 code,uname,opt.smbName,cell,pw_exp,reason?reason:"");
1090                 }
1091
1092                 RevertSecurityContext(&LogonContext);
1093                 DeleteSecurityContext(&LogonContext);
1094
1095                 if ( code && code != KTC_NOCM && code != KTC_NOCMRPC && !lowercased_name ) {
1096                     for ( ctemp = uname; *ctemp ; ctemp++) {
1097                         *ctemp = tolower(*ctemp);
1098                     }
1099                     lowercased_name = TRUE;
1100                     goto sleeping;
1101                 }
1102
1103                 /* is service started yet?*/
1104
1105                 /* If we've failed because the client isn't running yet and the
1106                  * client is set to autostart (and therefore it makes sense for
1107                  * us to wait for it to start) then sleep a while and try again.
1108                  * If the error was something else, then give up. */
1109                 if (code != KTC_NOCM && code != KTC_NOCMRPC)
1110                     break;
1111             }
1112             else {
1113                 /*JUST check to see if its running*/
1114                 if (IsServiceRunning())
1115                     break;
1116                 if (!IsServiceStartPending()) {
1117                     code = KTC_NOCMRPC;
1118                     reason = "AFS Service start failed";
1119                     break;
1120                 }
1121             }
1122
1123             /* If the retry interval has expired and we still aren't
1124              * logged in, then just give up if we are not in interactive
1125              * mode or the failSilently flag is set, otherwise let the
1126              * user know we failed and give them a chance to try again. */
1127             if (retryInterval <= 0) {
1128                 reason = "AFS not running";
1129                 if (!interactive || opt.failSilently)
1130                     break;
1131                 flag = MessageBox(hwndOwner,
1132                                    "AFS is still starting.  Retry?",
1133                                    "AFS Logon",
1134                                    MB_ICONQUESTION | MB_RETRYCANCEL);
1135                 if (flag == IDCANCEL)
1136                     break;
1137
1138                 /* Wait just a little while and try again */
1139                 retryInterval = opt.retryInterval;
1140             }
1141
1142           sleeping:
1143             Sleep(sleepInterval * 1000);
1144             retryInterval -= sleepInterval;
1145         }
1146         DebugEvent0("while loop exited");
1147     }
1148
1149     /* remove any kerberos 5 tickets currently held by the SYSTEM account
1150      * for this user
1151      */
1152
1153     if (ISLOGONINTEGRATED(opt.LogonOption) && KFW_is_available()) {
1154 #ifdef KFW_LOGON
1155         sprintf(szLogonId,"%d.%d",lpLogonId->HighPart, lpLogonId->LowPart);
1156         KFW_AFS_copy_cache_to_system_file(uname, szLogonId);
1157 #endif
1158         KFW_AFS_destroy_tickets_for_principal(uname);
1159     }
1160
1161     if (code) {
1162         char msg[128];
1163         HANDLE h;
1164         char *ptbuf[1];
1165
1166         StringCbPrintf(msg, sizeof(msg), "Integrated login failed: %s", reason);
1167
1168         if (ISLOGONINTEGRATED(opt.LogonOption) && interactive && !opt.failSilently)
1169             MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
1170
1171         h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
1172         ptbuf[0] = msg;
1173         ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
1174                      1, 0, ptbuf, NULL);
1175         DeregisterEventSource(h);
1176
1177         code = MapAuthError(code);
1178         SetLastError(code);
1179
1180         if (ISLOGONINTEGRATED(opt.LogonOption) && (code!=0))
1181         {
1182             if (*lpLogonScript)
1183                 LocalFree(*lpLogonScript);
1184             *lpLogonScript = NULL;
1185             if (!afsWillAutoStart)      // its not running, so if not autostart or integrated logon then just skip
1186                 code = 0;
1187         }
1188     }
1189
1190     if (opt.theseCells) free(opt.theseCells);
1191     if (opt.smbName) free(opt.smbName);
1192     if (opt.realm) free(opt.realm);
1193
1194     DebugEvent("AFS AfsLogon - Exit","Return Code[%x]",code);
1195     return code;
1196 }
1197
1198 DWORD APIENTRY NPPasswordChangeNotify(
1199         LPCWSTR lpAuthentInfoType,
1200         LPVOID lpAuthentInfo,
1201         LPCWSTR lpPreviousAuthentInfoType,
1202         LPVOID lpPreviousAuthentInfo,
1203         LPWSTR lpStationName,
1204         LPVOID StationHandle,
1205         DWORD dwChangeInfo)
1206 {
1207     BOOLEAN interactive;
1208
1209     /* Are we interactive? */
1210     interactive = (wcsicmp(lpStationName, L"WinSta0") == 0);
1211
1212     /* Do not do anything if the logon session is not interactive. */
1213     if (!interactive)
1214         return 0;
1215
1216     /* Make sure the AFS Libraries are initialized */
1217     AfsLogonInit();
1218
1219     DebugEvent0("AFS AfsLogon - NPPasswordChangeNotify");
1220     return 0;
1221 }
1222
1223 #include <userenv.h>
1224 #include <Winwlx.h>
1225 #include <afs/vice.h>
1226 #include <afs/fs_utils.h>
1227
1228 BOOL IsPathInAfs(const CHAR *strPath)
1229 {
1230     char space[2048];
1231     struct ViceIoctl blob;
1232     int code;
1233
1234     blob.in_size = 0;
1235     blob.out_size = 2048;
1236     blob.out = space;
1237
1238     code = pioctl((LPTSTR)((LPCTSTR)strPath), VIOC_FILE_CELL_NAME, &blob, 1);
1239     if (code)
1240         return FALSE;
1241     return TRUE;
1242 }
1243
1244 #ifdef COMMENT
1245 typedef struct _WLX_NOTIFICATION_INFO {
1246     ULONG Size;
1247     ULONG Flags;
1248     PWSTR UserName;
1249     PWSTR Domain;
1250     PWSTR WindowStation;
1251     HANDLE hToken;
1252     HDESK hDesktop;
1253     PFNMSGECALLBACK pStatusCallback;
1254 } WLX_NOTIFICATION_INFO, *PWLX_NOTIFICATION_INFO;
1255 #endif
1256
1257 VOID AFS_Startup_Event( PWLX_NOTIFICATION_INFO pInfo )
1258 {
1259     DWORD LSPtype, LSPsize;
1260     HKEY NPKey;
1261
1262     /* Make sure the AFS Libraries are initialized */
1263     AfsLogonInit();
1264
1265     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
1266                         0, KEY_QUERY_VALUE, &NPKey);
1267     LSPsize=sizeof(TraceOption);
1268     RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
1269                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
1270
1271     RegCloseKey (NPKey);
1272     DebugEvent0("AFS_Startup_Event");
1273 }
1274
1275 VOID AFS_Logoff_Event( PWLX_NOTIFICATION_INFO pInfo )
1276 {
1277     DWORD code;
1278     TCHAR profileDir[1024] = TEXT("");
1279     DWORD  len = 1024;
1280     PTOKEN_USER  tokenUser = NULL;
1281     DWORD  retLen;
1282     DWORD LSPtype, LSPsize;
1283     HKEY NPKey;
1284     DWORD LogoffPreserveTokens = 0;
1285     LogonOptions_t opt;
1286
1287     /* Make sure the AFS Libraries are initialized */
1288     AfsLogonInit();
1289
1290     DebugEvent0("AFS_Logoff_Event - Start");
1291
1292     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
1293                          0, KEY_QUERY_VALUE, &NPKey);
1294     LSPsize=sizeof(LogoffPreserveTokens);
1295     RegQueryValueEx(NPKey, REG_CLIENT_LOGOFF_TOKENS_PARM, NULL,
1296                      &LSPtype, (LPBYTE)&LogoffPreserveTokens, &LSPsize);
1297     RegCloseKey (NPKey);
1298
1299     if (!LogoffPreserveTokens) {
1300         memset(&opt, 0, sizeof(LogonOptions_t));
1301
1302         if (pInfo->UserName && pInfo->Domain) {
1303             char username[MAX_USERNAME_LENGTH] = "";
1304             char domain[MAX_DOMAIN_LENGTH] = "";
1305             size_t szlen = 0;
1306
1307             StringCchLengthW(pInfo->UserName, MAX_USERNAME_LENGTH, &szlen);
1308             WideCharToMultiByte(CP_UTF8, 0, pInfo->UserName, szlen,
1309                                  username, sizeof(username), NULL, NULL);
1310
1311             StringCchLengthW(pInfo->Domain, MAX_DOMAIN_LENGTH, &szlen);
1312             WideCharToMultiByte(CP_UTF8, 0, pInfo->Domain, szlen,
1313                                  domain, sizeof(domain), NULL, NULL);
1314
1315             GetDomainLogonOptions(NULL, username, domain, &opt);
1316         }
1317
1318         if (ISREMOTE(opt.flags)) {
1319             if (!GetTokenInformation(pInfo->hToken, TokenUser, NULL, 0, &retLen))
1320             {
1321                 if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
1322                     tokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen);
1323
1324                     if (!GetTokenInformation(pInfo->hToken, TokenUser, tokenUser, retLen, &retLen))
1325                     {
1326                         DebugEvent("AFS_Logoff_Event - GetTokenInformation failed: GLE = %lX", GetLastError());
1327                     }
1328                 }
1329             }
1330
1331             /* We can't use pInfo->Domain for the domain since in the cross realm case
1332              * this is source domain and not the destination domain.
1333              */
1334             if (tokenUser && QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, pInfo->Domain)) {
1335                 WCHAR Domain[64]=L"";
1336                 GetLocalShortDomain(Domain, sizeof(Domain));
1337                 if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, Domain)) {
1338                     if (NetUserGetProfilePath(pInfo->Domain, pInfo->UserName, profileDir, len))
1339                         GetUserProfileDirectory(pInfo->hToken, profileDir, &len);
1340                 }
1341             }
1342
1343             if (strlen(profileDir)) {
1344                 DebugEvent("AFS_Logoff_Event - Profile Directory: %s", profileDir);
1345                 if (!IsPathInAfs(profileDir)) {
1346                     if (code = ktc_ForgetAllTokens())
1347                         DebugEvent("AFS_Logoff_Event - ForgetAllTokens failed [%lX]",code);
1348                     else
1349                         DebugEvent0("AFS_Logoff_Event - ForgetAllTokens succeeded");
1350                 } else {
1351                     DebugEvent0("AFS_Logoff_Event - Tokens left in place; profile in AFS");
1352                 }
1353             } else {
1354                 DebugEvent0("AFS_Logoff_Event - Unable to load profile");
1355             }
1356
1357             if ( tokenUser )
1358                 LocalFree(tokenUser);
1359         } else {
1360             DebugEvent0("AFS_Logoff_Event - Local Logon");
1361             if (code = ktc_ForgetAllTokens())
1362                 DebugEvent("AFS_Logoff_Event - ForgetAllTokens failed [%lX]",code);
1363             else
1364                 DebugEvent0("AFS_Logoff_Event - ForgetAllTokens succeeded");
1365         }
1366     } else {
1367         DebugEvent0("AFS_Logoff_Event - Preserving Tokens");
1368     }
1369
1370     DebugEvent0("AFS_Logoff_Event - End");
1371 }
1372
1373 VOID AFS_Logon_Event( PWLX_NOTIFICATION_INFO pInfo )
1374 {
1375     TCHAR profileDir[1024] = TEXT("");
1376     DWORD  len = 1024;
1377     PTOKEN_USER  tokenUser = NULL;
1378     DWORD  retLen;
1379     WCHAR szUserW[128] = L"";
1380     char  szUserA[128] = "";
1381     char  szClient[MAX_PATH];
1382     char szPath[MAX_PATH] = "";
1383     NETRESOURCE nr;
1384     DWORD res;
1385     DWORD dwSize;
1386     LogonOptions_t opt;
1387
1388     /* Make sure the AFS Libraries are initialized */
1389     AfsLogonInit();
1390
1391     DebugEvent0("AFS_Logon_Event - Start");
1392
1393     DebugEvent("AFS_Logon_Event Process ID: %d",GetCurrentProcessId());
1394
1395     memset(&opt, 0, sizeof(LogonOptions_t));
1396
1397     if (pInfo->UserName && pInfo->Domain) {
1398         char username[MAX_USERNAME_LENGTH] = "";
1399         char domain[MAX_DOMAIN_LENGTH] = "";
1400         size_t szlen = 0;
1401
1402         DebugEvent0("AFS_Logon_Event - pInfo UserName and Domain");
1403
1404         StringCchLengthW(pInfo->UserName, MAX_USERNAME_LENGTH, &szlen);
1405         WideCharToMultiByte(CP_UTF8, 0, pInfo->UserName, szlen,
1406                             username, sizeof(username), NULL, NULL);
1407
1408         StringCchLengthW(pInfo->Domain, MAX_DOMAIN_LENGTH, &szlen);
1409         WideCharToMultiByte(CP_UTF8, 0, pInfo->Domain, szlen,
1410                             domain, sizeof(domain), NULL, NULL);
1411
1412         DebugEvent0("AFS_Logon_Event - Calling GetDomainLogonOptions");
1413         GetDomainLogonOptions(NULL, username, domain, &opt);
1414     } else {
1415         if (!pInfo->UserName)
1416             DebugEvent0("AFS_Logon_Event - No pInfo->UserName");
1417         if (!pInfo->Domain)
1418             DebugEvent0("AFS_Logon_Event - No pInfo->Domain");
1419     }
1420
1421     DebugEvent("AFS_Logon_Event - opt.LogonOption = %lX opt.flags = %lX",
1422                 opt.LogonOption, opt.flags);
1423
1424     if (!ISLOGONINTEGRATED(opt.LogonOption) || !ISREMOTE(opt.flags)) {
1425         DebugEvent0("AFS_Logon_Event - Logon is not integrated or not remote");
1426         goto done_logon_event;
1427     }
1428
1429     DebugEvent0("AFS_Logon_Event - Calling GetTokenInformation");
1430
1431     if (!GetTokenInformation(pInfo->hToken, TokenUser, NULL, 0, &retLen))
1432     {
1433         if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
1434             tokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen);
1435
1436             if (!GetTokenInformation(pInfo->hToken, TokenUser, tokenUser, retLen, &retLen))
1437             {
1438                 DebugEvent("AFS_Logon_Event - GetTokenInformation failed: GLE = %lX", GetLastError());
1439             }
1440         }
1441     }
1442
1443     /* We can't use pInfo->Domain for the domain since in the cross realm case
1444      * this is source domain and not the destination domain.
1445      */
1446     if (tokenUser && QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, pInfo->Domain)) {
1447         WCHAR Domain[64]=L"";
1448         GetLocalShortDomain(Domain, sizeof(Domain));
1449         if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, Domain)) {
1450             if (NetUserGetProfilePath(pInfo->Domain, pInfo->UserName, profileDir, len))
1451                 GetUserProfileDirectory(pInfo->hToken, profileDir, &len);
1452         }
1453     }
1454
1455     if (strlen(profileDir)) {
1456         DebugEvent("AFS_Logon_Event - Profile Directory: %s", profileDir);
1457     } else {
1458         DebugEvent0("AFS_Logon_Event - Unable to load profile");
1459     }
1460
1461   done_logon_event:
1462     dwSize = sizeof(szUserA);
1463     if (!KFW_AFS_get_lsa_principal(szUserA, &dwSize)) {
1464         StringCbPrintfW(szUserW, sizeof(szUserW), L"%s\\%s", pInfo->Domain, pInfo->UserName);
1465         WideCharToMultiByte(CP_ACP, 0, szUserW, -1, szUserA, MAX_PATH, NULL, NULL);
1466     }
1467
1468     if (szUserA[0])
1469     {
1470         lana_GetNetbiosName(szClient, LANA_NETBIOS_NAME_FULL);
1471         StringCbPrintf(szPath, sizeof(szPath), "\\\\%s", szClient);
1472
1473         DebugEvent("AFS_Logon_Event - Logon Name: %s", szUserA);
1474
1475         memset (&nr, 0x00, sizeof(NETRESOURCE));
1476         nr.dwType=RESOURCETYPE_DISK;
1477         nr.lpLocalName=0;
1478         nr.lpRemoteName=szPath;
1479         res = WNetAddConnection2(&nr,NULL,szUserA,0);
1480         if (res)
1481             DebugEvent("AFS_Logon_Event - WNetAddConnection2(%s,%s) failed: 0x%X",
1482                         szPath, szUserA,res);
1483         else
1484             DebugEvent0("AFS_Logon_Event - WNetAddConnection2() succeeded");
1485     } else
1486         DebugEvent("AFS_Logon_Event - User name conversion failed: GLE = 0x%X",GetLastError());
1487
1488     if ( tokenUser )
1489         LocalFree(tokenUser);
1490
1491     DebugEvent0("AFS_Logon_Event - End");
1492 }
1493
1494 static BOOL
1495 GetSecurityLogonSessionData(HANDLE hToken, PSECURITY_LOGON_SESSION_DATA * ppSessionData)
1496 {
1497     NTSTATUS Status = 0;
1498     TOKEN_STATISTICS Stats;
1499     DWORD   ReqLen;
1500     BOOL    Success;
1501
1502     if (!ppSessionData)
1503         return FALSE;
1504     *ppSessionData = NULL;
1505
1506     Success = GetTokenInformation( hToken, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );
1507     if ( !Success )
1508         return FALSE;
1509
1510     Status = LsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );
1511     if ( FAILED(Status) || !ppSessionData )
1512         return FALSE;
1513
1514     return TRUE;
1515 }
1516
1517 VOID KFW_Logon_Event( PWLX_NOTIFICATION_INFO pInfo )
1518 {
1519 #ifdef KFW_LOGON
1520     WCHAR szUserW[128] = L"";
1521     char  szUserA[128] = "";
1522     char szPath[MAX_PATH] = "";
1523     char szLogonId[128] = "";
1524     DWORD count;
1525     char filename[MAX_PATH] = "";
1526     char newfilename[MAX_PATH] = "";
1527     char commandline[MAX_PATH+256] = "";
1528     STARTUPINFO startupinfo;
1529     PROCESS_INFORMATION procinfo;
1530     HANDLE hf = INVALID_HANDLE_VALUE;
1531
1532     LUID LogonId = {0, 0};
1533     PSECURITY_LOGON_SESSION_DATA pLogonSessionData = NULL;
1534
1535     HKEY hKey1 = NULL, hKey2 = NULL;
1536
1537     /* Make sure the KFW Libraries are initialized */
1538     AfsLogonInit();
1539
1540     DebugEvent0("KFW_Logon_Event - Start");
1541
1542     GetSecurityLogonSessionData( pInfo->hToken, &pLogonSessionData );
1543
1544     if ( pLogonSessionData ) {
1545         LogonId = pLogonSessionData->LogonId;
1546         DebugEvent("KFW_Logon_Event - LogonId(%d,%d)", LogonId.HighPart, LogonId.LowPart);
1547
1548         sprintf(szLogonId,"%d.%d",LogonId.HighPart, LogonId.LowPart);
1549         LsaFreeReturnBuffer( pLogonSessionData );
1550     } else {
1551         DebugEvent0("KFW_Logon_Event - Unable to determine LogonId");
1552         return;
1553     }
1554
1555     count = GetEnvironmentVariable("TEMP", filename, sizeof(filename));
1556     if ( count > sizeof(filename) || count == 0 ) {
1557         GetWindowsDirectory(filename, sizeof(filename));
1558     }
1559
1560     count = GetEnvironmentVariable("TEMP", filename, sizeof(filename));
1561     if ( count > sizeof(filename) || count == 0 ) {
1562         GetWindowsDirectory(filename, sizeof(filename));
1563     }
1564
1565     if ( strlen(filename) + strlen(szLogonId) + 2 > sizeof(filename) ) {
1566         DebugEvent0("KFW_Logon_Event - filename too long");
1567         return;
1568     }
1569
1570     strcat(filename, "\\");
1571     strcat(filename, szLogonId);
1572
1573     hf = CreateFile(filename, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING,
1574                      FILE_ATTRIBUTE_NORMAL, NULL);
1575     if (hf == INVALID_HANDLE_VALUE) {
1576         DebugEvent0("KFW_Logon_Event - file cannot be opened");
1577         return;
1578     }
1579     CloseHandle(hf);
1580
1581     if (KFW_AFS_set_file_cache_dacl(filename, pInfo->hToken)) {
1582         DebugEvent0("KFW_Logon_Event - unable to set dacl");
1583         DeleteFile(filename);
1584         return;
1585     }
1586
1587     if (KFW_AFS_obtain_user_temp_directory(pInfo->hToken, newfilename, sizeof(newfilename))) {
1588         DebugEvent0("KFW_Logon_Event - unable to obtain temp directory");
1589         return;
1590     }
1591
1592     if ( strlen(newfilename) + strlen(szLogonId) + 2 > sizeof(newfilename) ) {
1593         DebugEvent0("KFW_Logon_Event - new filename too long");
1594         return;
1595     }
1596
1597     strcat(newfilename, "\\");
1598     strcat(newfilename, szLogonId);
1599
1600     if (!MoveFileEx(filename, newfilename,
1601                      MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {
1602         DebugEvent("KFW_Logon_Event - MoveFileEx failed GLE = 0x%x", GetLastError());
1603         return;
1604     }
1605
1606     sprintf(commandline, "afscpcc.exe \"%s\"", newfilename);
1607
1608     GetStartupInfo(&startupinfo);
1609     if (CreateProcessAsUser( pInfo->hToken,
1610                              "afscpcc.exe",
1611                              commandline,
1612                              NULL,
1613                              NULL,
1614                              FALSE,
1615                              CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS,
1616                              NULL,
1617                              NULL,
1618                              &startupinfo,
1619                              &procinfo))
1620     {
1621         DebugEvent("KFW_Logon_Event - CommandLine %s", commandline);
1622
1623         WaitForSingleObject(procinfo.hProcess, 30000);
1624
1625         CloseHandle(procinfo.hThread);
1626         CloseHandle(procinfo.hProcess);
1627     } else {
1628         DebugEvent0("KFW_Logon_Event - CreateProcessFailed");
1629     }
1630
1631     DeleteFile(filename);
1632
1633     DebugEvent0("KFW_Logon_Event - End");
1634 #endif
1635 }
1636