0446fb4989442b997c78ce4ebabb677240bced11
[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 "afslogon.h"
11
12 #include <io.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <fcntl.h>
16
17 #include <winsock2.h>
18 #include <lm.h>
19
20 #include <afs/param.h>
21 #include <afs/stds.h>
22 #include <afs/pioctl_nt.h>
23 #include <afs/kautils.h>
24
25 #include "afsd.h"
26 #include "cm_config.h"
27 #include "krb.h"
28 #include "afskfw.h"
29
30 DWORD TraceOption = 0;
31
32 HANDLE hDLL;
33
34 WSADATA WSAjunk;
35 #define AFS_LOGON_EVENT_NAME TEXT("AFS Logon")
36
37 void DebugEvent0(char *a) 
38 {
39     HANDLE h; char *ptbuf[1];
40     if (!ISLOGONTRACE(TraceOption))
41         return;
42     h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
43     ptbuf[0] = a;
44     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
45     DeregisterEventSource(h);
46 }
47
48 #define MAXBUF_ 512
49 void DebugEvent(char *b,...) 
50 {
51     HANDLE h; char *ptbuf[1],buf[MAXBUF_+1];
52     va_list marker;
53
54     if (!ISLOGONTRACE(TraceOption))
55         return;
56
57     h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
58     va_start(marker,b);
59     StringCbVPrintf(buf, MAXBUF_+1,b,marker);
60     buf[MAXBUF_] = '\0';
61     ptbuf[0] = buf;
62     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
63     DeregisterEventSource(h);
64     va_end(marker);
65 }
66
67 static HANDLE hInitMutex = NULL;
68 static BOOL bInit = FALSE;
69
70 BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
71 {
72     hDLL = dll;
73     switch (reason) {
74     case DLL_PROCESS_ATTACH:
75         /* Initialization Mutex */
76         hInitMutex = CreateMutex(NULL, FALSE, NULL);
77         break;
78
79     case DLL_PROCESS_DETACH:
80         CloseHandle(hInitMutex);
81         break;
82
83     case DLL_THREAD_ATTACH:
84     case DLL_THREAD_DETACH:
85     default:
86         /* Everything else succeeds but does nothing. */
87         break;
88     }
89
90     return TRUE;
91 }
92
93 void AfsLogonInit(void)
94 {
95     if ( bInit == FALSE ) {
96         if ( WaitForSingleObject( hInitMutex, INFINITE ) == WAIT_OBJECT_0 ) {
97             if ( bInit == FALSE ) {
98                 rx_Init(0);
99                 initAFSDirPath();
100                 ka_Init(0);
101                 bInit = TRUE;
102             }
103             ReleaseMutex(hInitMutex);
104         }
105     }
106 }
107
108 CHAR *GenRandomName(CHAR *pbuf)
109 {
110     int i;
111     srand( (unsigned)time( NULL ) );
112     for (i=0;i<MAXRANDOMNAMELEN-1;i++)
113         pbuf[i]='a'+(rand() % 26);
114     pbuf[MAXRANDOMNAMELEN-1]=0;
115     return pbuf;
116 }
117
118 BOOLEAN AFSWillAutoStart(void)
119 {
120     SC_HANDLE scm;
121     SC_HANDLE svc;
122     BOOLEAN flag;
123     BOOLEAN result = FALSE;
124     LPQUERY_SERVICE_CONFIG pConfig = NULL;
125     DWORD BufSize;
126     LONG status;
127
128     /* Open services manager */
129     scm = OpenSCManager(NULL, NULL, GENERIC_READ);
130     if (!scm) return FALSE;
131
132     /* Open AFSD service */
133     svc = OpenService(scm, "TransarcAFSDaemon", SERVICE_QUERY_CONFIG);
134     if (!svc)
135         goto close_scm;
136
137     /* Query AFSD service config, first just to get buffer size */
138     /* Expected to fail, so don't test return value */
139     (void) QueryServiceConfig(svc, NULL, 0, &BufSize);
140     status = GetLastError();
141     if (status != ERROR_INSUFFICIENT_BUFFER)
142         goto close_svc;
143
144     /* Allocate buffer */
145     pConfig = (LPQUERY_SERVICE_CONFIG)GlobalAlloc(GMEM_FIXED,BufSize);
146     if (!pConfig)
147         goto close_svc;
148
149     /* Query AFSD service config, this time for real */
150     flag = QueryServiceConfig(svc, pConfig, BufSize, &BufSize);
151     if (!flag)
152         goto free_pConfig;
153
154     /* Is it autostart? */
155     if (pConfig->dwStartType < SERVICE_DEMAND_START)
156         result = TRUE;
157
158   free_pConfig:
159     GlobalFree(pConfig);
160   close_svc:
161     CloseServiceHandle(svc);
162   close_scm:
163     CloseServiceHandle(scm);
164
165     return result;
166 }
167
168 DWORD MapAuthError(DWORD code)
169 {
170     switch (code) {
171         /* Unfortunately, returning WN_NO_NETWORK results in the MPR abandoning
172          * logon scripts for all credential managers, although they will still
173          * receive logon notifications.  Since we don't want this, we return
174          * WN_SUCCESS.  This is highly undesirable, but we also don't want to
175          * break other network providers.
176          */
177  /* case KTC_NOCM:
178     case KTC_NOCMRPC:
179     return WN_NO_NETWORK; */
180     default: return WN_SUCCESS;
181   }
182 }
183
184 DWORD APIENTRY NPGetCaps(DWORD index)
185 {
186     switch (index) {
187     case WNNC_NET_TYPE:
188         /* Don't have our own type; use somebody else's. */
189         return WNNC_NET_SUN_PC_NFS;
190
191     case WNNC_START:
192         /* Say we are already started, even though we might wait after we receive NPLogonNotify */
193         return 1;
194
195     default:
196         return 0;
197     }
198 }       
199
200 NET_API_STATUS 
201 NetUserGetProfilePath( LPCWSTR Domain, LPCWSTR UserName, char * profilePath, 
202                        DWORD profilePathLen )
203 {
204     NET_API_STATUS code;
205     LPWSTR ServerName = NULL;
206     LPUSER_INFO_3 p3 = NULL;
207
208     NetGetAnyDCName(NULL, Domain, (LPBYTE *)&ServerName);
209     /* if NetGetAnyDCName fails, ServerName == NULL
210      * NetUserGetInfo will obtain local user information 
211      */
212     code = NetUserGetInfo(ServerName, UserName, 3, (LPBYTE *)&p3);
213     if (code == NERR_Success)
214     {
215         code = NERR_UserNotFound;
216         if (p3) {
217             if (p3->usri3_profile) {
218                 DWORD len = lstrlenW(p3->usri3_profile);
219                 if (len > 0) {
220                     /* Convert From Unicode to ANSI (UTF-8 for future) */
221                     len = len < profilePathLen ? len : profilePathLen - 1;
222                     WideCharToMultiByte(CP_UTF8, 0, p3->usri3_profile, len, profilePath, len, NULL, NULL);
223                     profilePath[len] = '\0';
224                     code = NERR_Success;
225                 }
226             }
227             NetApiBufferFree(p3);
228         }
229     }
230     if (ServerName) 
231         NetApiBufferFree(ServerName);
232     return code;
233 }
234
235 BOOL IsServiceRunning (void)
236 {
237     SERVICE_STATUS Status;
238     SC_HANDLE hManager;
239     memset (&Status, 0x00, sizeof(Status));
240     Status.dwCurrentState = SERVICE_STOPPED;
241
242     if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
243     {
244         SC_HANDLE hService;
245         if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
246         {
247             QueryServiceStatus (hService, &Status);
248             CloseServiceHandle (hService);
249         }
250
251         CloseServiceHandle (hManager);
252     }
253     DebugEvent("AFS AfsLogon - Test Service Running","Return Code[%x] ?Running[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_RUNNING));
254     return (Status.dwCurrentState == SERVICE_RUNNING);
255 }   
256
257 /* LOOKUPKEYCHAIN: macro to look up the value in the list of keys in order until it's found
258    v:variable to receive value (reference type)
259    t:type
260    d:default, in case the value isn't on any of the keys
261    n:name of value */
262 #define LOOKUPKEYCHAIN(v,t,d,n) \
263         do { \
264                 rv = ~ERROR_SUCCESS; \
265                 dwType = t; \
266                 if(hkDom) { \
267                         dwSize = sizeof(v); \
268                         rv = RegQueryValueEx(hkDom, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
269                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkDom with type [%d]", dwType); \
270                 } \
271                 if(hkDoms && (rv != ERROR_SUCCESS || dwType != t)) { \
272                         dwSize = sizeof(v); \
273                         rv = RegQueryValueEx(hkDoms, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
274                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkDoms with type [%d]", dwType); \
275                 } \
276                 if(hkNp && (rv != ERROR_SUCCESS || dwType != t)) { \
277                         dwSize = sizeof(v); \
278                         rv = RegQueryValueEx(hkNp, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
279                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkNp with type [%d]", dwType); \
280                 } \
281                 if(rv != ERROR_SUCCESS || dwType != t) { \
282                         v = d; \
283                         DebugEvent(#v " being set to default"); \
284                 } \
285         } while(0)
286
287 /* Get domain specific configuration info.  We are returning void because if anything goes wrong
288    we just return defaults.
289  */
290 void 
291 GetDomainLogonOptions( PLUID lpLogonId, char * username, char * domain, LogonOptions_t *opt ) {
292     HKEY hkParm = NULL; /* Service parameter */
293     HKEY hkNp = NULL;   /* network provider key */
294     HKEY hkDoms = NULL; /* domains key */
295     HKEY hkDom = NULL;  /* DOMAINS/domain key */
296     HKEY hkTemp = NULL;
297     LONG rv;
298     DWORD dwSize;
299     DWORD dwType;
300     DWORD dwDummy;
301     char computerName[MAX_COMPUTERNAME_LENGTH + 1];
302     char *effDomain;
303
304     DebugEvent("In GetDomainLogonOptions for user [%s] in domain [%s]", username, domain);
305     /* If the domain is the same as the Netbios computer name, we use the LOCALHOST domain name*/
306     opt->flags = LOGON_FLAG_REMOTE;
307     if(domain) {
308         dwSize = MAX_COMPUTERNAME_LENGTH;
309         if(GetComputerName(computerName, &dwSize)) {
310             if(!stricmp(computerName, domain)) {
311                 effDomain = "LOCALHOST";
312                 opt->flags = LOGON_FLAG_LOCAL;
313             }
314             else
315                 effDomain = domain;
316         }
317     } else
318         effDomain = NULL;
319
320     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY, 0, KEY_READ, &hkParm );
321     if(rv != ERROR_SUCCESS) {
322         hkParm = NULL;
323         DebugEvent("GetDomainLogonOption: Can't open parms key [%d]", rv);
324     }
325
326     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_CLIENT_PROVIDER_KEY, 0, KEY_READ, &hkNp );
327     if(rv != ERROR_SUCCESS) {
328         hkNp = NULL;
329         DebugEvent("GetDomainLogonOptions: Can't open NP key [%d]", rv);
330     }
331
332     if(hkNp) {
333         rv = RegOpenKeyEx( hkNp, REG_CLIENT_DOMAINS_SUBKEY, 0, KEY_READ, &hkDoms );
334         if( rv != ERROR_SUCCESS ) {
335             hkDoms = NULL;
336             DebugEvent("GetDomainLogonOptions: Can't open Domains key [%d]", rv);
337         }
338     }
339
340     if(hkDoms && effDomain) {
341         rv = RegOpenKeyEx( hkDoms, effDomain, 0, KEY_READ, &hkDom );
342         if( rv != ERROR_SUCCESS ) {
343             hkDom = NULL;
344             DebugEvent("GetDomainLogonOptions: Can't open domain key for [%s] [%d]", effDomain, rv);
345             /* If none of the domains match, we shouldn't use the domain key either */
346             RegCloseKey(hkDoms);
347             hkDoms = NULL;
348         }
349     } else
350         DebugEvent("Not opening domain key for [%s]", effDomain);
351
352     /* Each individual can either be specified on the domain key, the domains key or in the
353        net provider key.  They fail over in that order.  If none is found, we just use the 
354        defaults. */
355
356     /* LogonOption */
357     LOOKUPKEYCHAIN(opt->LogonOption, REG_DWORD, DEFAULT_LOGON_OPTION, REG_CLIENT_LOGON_OPTION_PARM);
358
359     /* FailLoginsSilently */
360     dwSize = sizeof(dwDummy);
361     rv = RegQueryValueEx(hkParm, REG_CLIENT_FAIL_SILENTLY_PARM, 0, &dwType, (LPBYTE) &dwDummy, &dwSize);
362     if (rv != ERROR_SUCCESS)
363         LOOKUPKEYCHAIN(dwDummy, REG_DWORD, DEFAULT_FAIL_SILENTLY, REG_CLIENT_FAIL_SILENTLY_PARM);
364     opt->failSilently = !!dwDummy;
365
366     /* Retry interval */
367     LOOKUPKEYCHAIN(opt->retryInterval, REG_DWORD, DEFAULT_RETRY_INTERVAL, REG_CLIENT_RETRY_INTERVAL_PARM);
368
369     /* Sleep interval */
370     LOOKUPKEYCHAIN(opt->sleepInterval, REG_DWORD, DEFAULT_SLEEP_INTERVAL, REG_CLIENT_SLEEP_INTERVAL_PARM);
371
372     opt->logonScript = NULL;
373     opt->smbName = NULL;
374
375     if(!ISLOGONINTEGRATED(opt->LogonOption)) {
376         goto cleanup; /* no need to lookup the logon script */
377     }
378
379     /* come up with SMB username */
380     if(ISHIGHSECURITY(opt->LogonOption)) {
381         opt->smbName = malloc( MAXRANDOMNAMELEN );
382         GenRandomName(opt->smbName);
383     } else {
384         /* username and domain for logon session is not necessarily the same as
385            username and domain passed into network provider. */
386         PSECURITY_LOGON_SESSION_DATA plsd;
387         char lsaUsername[MAX_USERNAME_LENGTH];
388         char lsaDomain[MAX_DOMAIN_LENGTH];
389         size_t len, tlen;
390
391         LsaGetLogonSessionData(lpLogonId, &plsd);
392         
393         UnicodeStringToANSI(plsd->UserName, lsaUsername, MAX_USERNAME_LENGTH);
394         UnicodeStringToANSI(plsd->LogonDomain, lsaDomain, MAX_DOMAIN_LENGTH);
395
396         DebugEvent("PLSD username[%s] domain[%s]",lsaUsername,lsaDomain);
397
398         if(SUCCEEDED(StringCbLength(lsaUsername, MAX_USERNAME_LENGTH, &tlen)))
399             len = tlen;
400         else
401             goto bad_strings;
402
403         if(SUCCEEDED(StringCbLength(lsaDomain, MAX_DOMAIN_LENGTH, &tlen)))
404             len += tlen;
405         else
406             goto bad_strings;
407
408         len += 2;
409
410         opt->smbName = malloc(len);
411
412         StringCbCopy(opt->smbName, len, lsaDomain);
413         StringCbCat(opt->smbName, len, "\\");
414         StringCbCat(opt->smbName, len, lsaUsername);
415
416         strlwr(opt->smbName);
417
418       bad_strings:
419         LsaFreeReturnBuffer(plsd);
420     }
421
422     DebugEvent("Looking up logon script");
423     /* Logon script */
424     /* First find out where the key is */
425     hkTemp = NULL;
426     rv = ~ERROR_SUCCESS;
427     dwType = 0;
428     if(hkDom)
429         rv = RegQueryValueExW(hkDom, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
430     if(rv == ERROR_SUCCESS && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
431         hkTemp = hkDom;
432         DebugEvent("Located logon script in hkDom");
433     }
434     else if(hkDoms)
435         rv = RegQueryValueExW(hkDoms, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
436     if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
437         hkTemp = hkDoms;
438         DebugEvent("Located logon script in hkDoms");
439     }
440     /* Note that the LogonScript in the NP key is only used if we are doing high security. */
441     else if(hkNp && ISHIGHSECURITY(opt->LogonOption))
442         rv = RegQueryValueExW(hkNp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
443     if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
444         hkTemp = hkNp;
445         DebugEvent("Located logon script in hkNp");
446     }
447
448     if(hkTemp) {
449         WCHAR *regscript        = NULL;
450         WCHAR *regexscript      = NULL;
451         WCHAR *regexuscript     = NULL;
452         WCHAR *wuname           = NULL;
453         HRESULT hr;
454
455         size_t len;
456
457         StringCbLength(opt->smbName, MAX_USERNAME_LENGTH, &len);
458         len ++;
459
460         wuname = malloc(len * sizeof(WCHAR));
461         MultiByteToWideChar(CP_ACP,0,opt->smbName,-1,wuname,len*sizeof(WCHAR));
462
463         DebugEvent("Username is set for [%S]", wuname);
464
465         /* dwSize still has the size of the required buffer in bytes. */
466         regscript = malloc(dwSize);
467         rv = RegQueryValueExW(hkTemp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, (LPBYTE) regscript, &dwSize);
468         if(rv != ERROR_SUCCESS) {/* what the ..? */
469             DebugEvent("Can't look up logon script [%d]",rv);
470             goto doneLogonScript;
471         }
472
473         DebugEvent("Found logon script [%S]", regscript);
474
475         if(dwType == REG_EXPAND_SZ) {
476             DWORD dwReq;
477
478             dwSize += MAX_PATH * sizeof(WCHAR);  /* make room for environment expansion. */
479             regexscript = malloc(dwSize);
480             dwReq = ExpandEnvironmentStringsW(regscript, regexscript, dwSize / sizeof(WCHAR));
481             free(regscript);
482             regscript = regexscript;
483             regexscript = NULL;
484             if(dwReq > (dwSize / sizeof(WCHAR))) {
485                 DebugEvent("Overflow while expanding environment strings.");
486                 goto doneLogonScript;
487             }
488         }
489
490         DebugEvent("After expanding env strings [%S]", regscript);
491
492         if(wcsstr(regscript, L"%s")) {
493             dwSize += len * sizeof(WCHAR); /* make room for username expansion */
494             regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
495             hr = StringCbPrintfW(regexuscript, dwSize, regscript, wuname);
496         } else {
497             regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
498             hr = StringCbCopyW(regexuscript, dwSize, regscript);
499         }
500
501         DebugEvent("After expanding username [%S]", regexuscript);
502
503         if(hr == S_OK)
504             opt->logonScript = regexuscript;
505         else
506             LocalFree(regexuscript);
507
508       doneLogonScript:
509         if(wuname) free(wuname);
510         if(regscript) free(regscript);
511         if(regexscript) free(regexscript);
512     }
513
514   cleanup:
515     if(hkNp) RegCloseKey(hkNp);
516     if(hkDom) RegCloseKey(hkDom);
517     if(hkDoms) RegCloseKey(hkDoms);
518     if(hkParm) RegCloseKey(hkParm);
519 }       
520
521 #undef LOOKUPKEYCHAIN
522
523 /* Try to find out which cell the given path is in.  We must retain
524    the contents of *cell in case of failure. *cell is assumed to be
525    at least cellLen chars */
526 DWORD GetFileCellName(char * path, char * cell, size_t cellLen) {
527     struct ViceIoctl blob;
528     char tcell[MAX_PATH];
529     DWORD code;
530
531     blob.in_size = 0;
532     blob.out_size = MAX_PATH;
533     blob.out = tcell;
534
535     code = pioctl(path, VIOC_FILE_CELL_NAME, &blob, 1);
536
537     if(!code) {
538         strncpy(cell, tcell, cellLen);
539         cell[cellLen - 1] = '\0';
540     }
541     return code;
542 }       
543
544
545 static BOOL
546 WINAPI
547 UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen)
548 {
549     CPINFO CodePageInfo;
550
551     GetCPInfo(CP_ACP, &CodePageInfo);
552
553     if (uInputString.Buffer && ((LPBYTE) uInputString.Buffer)[1] == '\0')
554     {
555         // Looks like unicode, better translate it
556         // UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS
557         WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,
558                             lpszOutputString, nOutStringLen-1, NULL, NULL);
559         lpszOutputString[min(uInputString.Length/2,nOutStringLen-1)] = '\0';
560         return TRUE;
561     }
562     else
563         lpszOutputString[0] = '\0';
564
565     return FALSE;
566 }  // UnicodeStringToANSI
567
568 DWORD APIENTRY NPLogonNotify(
569         PLUID lpLogonId,
570         LPCWSTR lpAuthentInfoType,
571         LPVOID lpAuthentInfo,
572         LPCWSTR lpPreviousAuthentInfoType,
573         LPVOID lpPreviousAuthentInfo,
574         LPWSTR lpStationName,
575         LPVOID StationHandle,
576         LPWSTR *lpLogonScript)
577 {
578     char uname[MAX_USERNAME_LENGTH]="";
579     char password[MAX_PASSWORD_LENGTH]="";
580     char logonDomain[MAX_DOMAIN_LENGTH]="";
581     char cell[256]="<non-integrated logon>";
582     char homePath[MAX_PATH]="";
583
584     MSV1_0_INTERACTIVE_LOGON *IL;
585
586     DWORD code;
587
588     int pw_exp;
589     char *reason;
590     char *ctemp;
591
592     BOOLEAN interactive;
593     BOOLEAN flag;
594     DWORD LSPtype, LSPsize;
595     HKEY NPKey;
596
597     HWND hwndOwner = (HWND)StationHandle;
598
599     BOOLEAN afsWillAutoStart;
600
601     BOOLEAN uppercased_name = TRUE;
602
603     LogonOptions_t opt; /* domain specific logon options */
604     int retryInterval;
605     int sleepInterval;
606
607     /* Make sure the AFS Libraries are initialized */
608     AfsLogonInit();
609
610     /* Initialize Logon Script to none */
611     *lpLogonScript=NULL;
612     
613     /* TODO: We should check the value of lpAuthentInfoType before assuming that it is
614        MSV1_0_INTERACTIVE_LOGON though for our purposes KERB_INTERACTIVE_LOGON is
615        co-incidentally equivalent. */
616     IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
617
618     /* Are we interactive? */
619     interactive = (wcscmp(lpStationName, L"WinSta0") == 0);
620
621     /* Convert from Unicode to ANSI */
622
623     /*TODO: Use SecureZeroMemory to erase passwords */
624     UnicodeStringToANSI(IL->UserName, uname, MAX_USERNAME_LENGTH);
625     UnicodeStringToANSI(IL->Password, password, MAX_PASSWORD_LENGTH);
626     UnicodeStringToANSI(IL->LogonDomainName, logonDomain, MAX_DOMAIN_LENGTH);
627
628     /* Make sure AD-DOMANS sent from login that is sent to us is striped */
629     ctemp = strchr(uname, '@');
630     if (ctemp) *ctemp = 0;
631
632     /* is the name all uppercase? */
633     for ( ctemp = uname; *ctemp ; ctemp++) {
634         if ( islower(*ctemp) ) {
635             uppercased_name = FALSE;
636             break;
637         }
638     }
639
640     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
641                          0, KEY_QUERY_VALUE, &NPKey);
642     LSPsize=sizeof(TraceOption);
643     RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
644                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
645
646     RegCloseKey (NPKey);
647
648     /*
649      * Get Logon options
650      */
651
652     GetDomainLogonOptions( lpLogonId, uname, logonDomain, &opt );
653     retryInterval = opt.retryInterval;
654     sleepInterval = opt.sleepInterval;
655     *lpLogonScript = opt.logonScript;
656
657     DebugEvent("Got logon script: %S",opt.logonScript);
658
659     afsWillAutoStart = AFSWillAutoStart();
660
661     DebugEvent("LogonOption[%x], Service AutoStart[%d]",
662                 opt.LogonOption,afsWillAutoStart);
663     
664     /* Check for zero length password if integrated logon*/
665     if ( ISLOGONINTEGRATED(opt.LogonOption) )  {
666         if ( password[0] == 0 ) {
667             DebugEvent("Password is the empty string");
668             code = GT_PW_NULL;
669             reason = "zero length password is illegal";
670             code=0;
671         }       
672
673         /* Get cell name if doing integrated logon.  
674            We might overwrite this if we are logging into an AD realm and we find out that
675            the user's home dir is in some other cell. */
676         DebugEvent("About to call cm_GetRootCellName(%s)",cell);
677         code = cm_GetRootCellName(cell);
678         if (code < 0) { 
679             DebugEvent("Unable to obtain Root Cell");
680             code = KTC_NOCELL;
681             reason = "unknown cell";
682             code=0;
683         } else {
684             DebugEvent("Cell is %s",cell);
685         }       
686
687         /* We get the user's home directory path, if applicable, though we can't lookup the
688            cell right away because the client service may not have started yet. This call
689            also sets the AD_REALM flag in opt.flags if applicable. */
690         if(ISREMOTE(opt.flags)) {
691             DebugEvent("Is Remote");
692             GetAdHomePath(homePath,MAX_PATH,lpLogonId,&opt);
693         }       
694     }
695
696     /* loop until AFS is started. */
697     while (TRUE) {
698         DebugEvent("while(TRUE) LogonOption[%x], Service AutoStart[%d]",
699                     opt.LogonOption,afsWillAutoStart);
700
701         if(ISADREALM(opt.flags)) {
702             code = GetFileCellName(homePath,cell,256);
703             if(!code) {
704                 DebugEvent("profile path [%s] is in cell [%s]",homePath,cell);
705             }
706             /* Don't bail out if GetFileCellName failed.
707              * The home dir may not be in AFS after all. 
708              */
709         } else
710             code=0;
711                 
712         /* if Integrated Logon  */
713         if (ISLOGONINTEGRATED(opt.LogonOption))
714         {                       
715             if ( KFW_is_available() ) {
716                 code = KFW_AFS_get_cred(uname, cell, password, 0, opt.smbName, &reason);
717                 DebugEvent("KFW_AFS_get_cred  uname=[%s] smbname=[%s] cell=[%s] code=[%d]",uname,opt.smbName,cell,code);
718             }
719             else {
720                 code = ka_UserAuthenticateGeneral2(KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
721                                                     uname, "", cell, password, opt.smbName, 0, &pw_exp, 0,
722                                                     &reason);
723                 DebugEvent("AFS AfsLogon - (INTEGRATED only)ka_UserAuthenticateGeneral2","Code[%x]",
724                             code);
725             }       
726             if ( code && code != KTC_NOCM && code != KTC_NOCMRPC && uppercased_name ) {
727                 for ( ctemp = uname; *ctemp ; ctemp++) {
728                     *ctemp = tolower(*ctemp);
729                 }
730                 uppercased_name = FALSE;
731                 continue;
732             }
733         }
734         else {  
735             /*JUST check to see if its running*/
736             if (IsServiceRunning())
737                 break;
738             code = KTC_NOCM;
739             if (!afsWillAutoStart)
740                 break;
741         }
742
743         /* is service started yet?*/
744         DebugEvent("AFS AfsLogon - ka_UserAuthenticateGeneral2","Code[%x] uname[%s] Cell[%s]",
745                     code,uname,cell);
746
747         /* If we've failed because the client isn't running yet and the
748          * client is set to autostart (and therefore it makes sense for
749          * us to wait for it to start) then sleep a while and try again. 
750          * If the error was something else, then give up. */
751         if (code != KTC_NOCM && code != KTC_NOCMRPC || !afsWillAutoStart)
752             break;
753
754         /* If the retry interval has expired and we still aren't
755          * logged in, then just give up if we are not in interactive
756          * mode or the failSilently flag is set, otherwise let the
757          * user know we failed and give them a chance to try again. */
758         if (retryInterval <= 0) {
759             reason = "AFS not running";
760             if (!interactive || opt.failSilently)
761                 break;
762             flag = MessageBox(hwndOwner,
763                                "AFS is still starting.  Retry?",
764                                "AFS Logon",
765                                MB_ICONQUESTION | MB_RETRYCANCEL);
766             if (flag == IDCANCEL)
767                 break;
768
769             /* Wait just a little while and try again */
770             retryInterval = sleepInterval = DEFAULT_SLEEP_INTERVAL;
771         }
772
773         if (retryInterval < sleepInterval)
774             sleepInterval = retryInterval;
775
776         Sleep(sleepInterval * 1000);
777
778         retryInterval -= sleepInterval;
779     }
780
781     DebugEvent("while loop exited");
782     /* remove any kerberos 5 tickets currently held by the SYSTEM account */
783     if ( KFW_is_available() )
784         KFW_AFS_destroy_tickets_for_cell(cell);
785
786     if (code) {
787         char msg[128];
788         HANDLE h;
789         char *ptbuf[1];
790
791         StringCbPrintf(msg, sizeof(msg), "Integrated login failed: %s", reason);
792
793         if (ISLOGONINTEGRATED(opt.LogonOption) && interactive && !opt.failSilently)
794             MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
795
796         h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
797         ptbuf[0] = msg;
798         ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
799                      1, 0, ptbuf, NULL);
800         DeregisterEventSource(h);
801             
802         code = MapAuthError(code);
803         SetLastError(code);
804
805         if (ISLOGONINTEGRATED(opt.LogonOption) && (code!=0))
806         {
807             if (*lpLogonScript)
808                 LocalFree(*lpLogonScript);
809             *lpLogonScript = NULL;
810             if (!afsWillAutoStart)      // its not running, so if not autostart or integrated logon then just skip
811                 code = 0;
812
813         }
814     }
815
816     if(opt.smbName) free(opt.smbName);
817
818     DebugEvent("AFS AfsLogon - Exit","Return Code[%x]",code);
819     return code;
820 }       
821
822 DWORD APIENTRY NPPasswordChangeNotify(
823         LPCWSTR lpAuthentInfoType,
824         LPVOID lpAuthentInfo,
825         LPCWSTR lpPreviousAuthentInfoType,
826         LPVOID lpPreviousAuthentInfo,
827         LPWSTR lpStationName,
828         LPVOID StationHandle,
829         DWORD dwChangeInfo)
830 {
831     /* Make sure the AFS Libraries are initialized */
832     AfsLogonInit();
833
834     DebugEvent0("AFS AfsLogon - NPPasswordChangeNotify");
835     return 0;
836 }
837
838 #include <userenv.h>
839 #include <Winwlx.h>
840 #include <afs/vice.h>
841 #include <afs/fs_utils.h>
842
843 BOOL IsPathInAfs(const CHAR *strPath)
844 {
845     char space[2048];
846     struct ViceIoctl blob;
847     int code;
848
849     blob.in_size = 0;
850     blob.out_size = 2048;
851     blob.out = space;
852
853     code = pioctl((LPTSTR)((LPCTSTR)strPath), VIOC_FILE_CELL_NAME, &blob, 1);
854     if (code)
855         return FALSE;
856     return TRUE;
857 }
858
859 #ifdef COMMENT
860 typedef struct _WLX_NOTIFICATION_INFO {  
861     ULONG Size;  
862     ULONG Flags;  
863     PWSTR UserName;  
864     PWSTR Domain;  
865     PWSTR WindowStation;  
866     HANDLE hToken;  
867     HDESK hDesktop;  
868     PFNMSGECALLBACK pStatusCallback;
869 } WLX_NOTIFICATION_INFO, *PWLX_NOTIFICATION_INFO;
870 #endif
871
872 VOID AFS_Startup_Event( PWLX_NOTIFICATION_INFO pInfo )
873 {
874     DWORD LSPtype, LSPsize;
875     HKEY NPKey;
876
877     /* Make sure the AFS Libraries are initialized */
878     AfsLogonInit();
879
880     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
881                         0, KEY_QUERY_VALUE, &NPKey);
882     LSPsize=sizeof(TraceOption);
883     RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
884                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
885
886     RegCloseKey (NPKey);
887     DebugEvent0("AFS_Startup_Event");
888 }
889
890 VOID AFS_Logoff_Event( PWLX_NOTIFICATION_INFO pInfo )
891 {
892     DWORD code;
893     TCHAR profileDir[1024] = TEXT("");
894     DWORD  len = 1024;
895     PTOKEN_USER  tokenUser = NULL;
896     DWORD  retLen;
897     HANDLE hToken;
898
899     /* Make sure the AFS Libraries are initialized */
900     AfsLogonInit();
901
902     DebugEvent0("AFS_Logoff_Event - Starting");
903
904     if (!GetTokenInformation(pInfo->hToken, TokenUser, NULL, 0, &retLen))
905     {
906         if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
907             tokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen);
908
909             if (!GetTokenInformation(pInfo->hToken, TokenUser, tokenUser, retLen, &retLen))
910             {
911                 DebugEvent("GetTokenInformation failed: GLE = %lX", GetLastError());
912             }
913         }
914     }
915
916     /* We can't use pInfo->Domain for the domain since in the cross realm case 
917      * this is source domain and not the destination domain.
918      */
919     if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, pInfo->Domain)) {
920         WCHAR Domain[64]=L"";
921         GetLocalShortDomain(Domain, sizeof(Domain));
922         if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, Domain)) {
923             if (NetUserGetProfilePath(pInfo->Domain, pInfo->UserName, profileDir, len))
924                 GetUserProfileDirectory(pInfo->hToken, profileDir, &len);
925         }
926     }
927     
928     if (strlen(profileDir)) {
929         DebugEvent("Profile Directory: %s", profileDir);
930         if (!IsPathInAfs(profileDir)) {
931             if (code = ktc_ForgetAllTokens())
932                 DebugEvent("AFS_Logoff_Event - ForgetAllTokens failed [%lX]",code);
933             else
934                 DebugEvent0("AFS_Logoff_Event - ForgetAllTokens succeeded");
935         } else {
936             DebugEvent0("AFS_Logoff_Event - Tokens left in place; profile in AFS");
937         }
938     } else {
939         DebugEvent0("AFS_Logoff_Event - Unable to load profile");
940     }
941
942     if ( tokenUser )
943         LocalFree(tokenUser);
944 }   
945