7f6b4e1a12a840ef78af2ae1e427d5972e2a4d2e
[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 (CodePageInfo.MaxCharSize > 1)
554         // Only supporting non-Unicode strings
555         return FALSE;
556     
557     if (uInputString.Buffer && ((LPBYTE) uInputString.Buffer)[1] == '\0')
558     {
559         // Looks like unicode, better translate it
560         // UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS
561         WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,
562                             lpszOutputString, nOutStringLen-1, NULL, NULL);
563         lpszOutputString[min(uInputString.Length/2,nOutStringLen-1)] = '\0';
564         return TRUE;
565     }
566     else
567         lpszOutputString[0] = '\0';
568     return FALSE;
569 }  // UnicodeStringToANSI
570
571 DWORD APIENTRY NPLogonNotify(
572         PLUID lpLogonId,
573         LPCWSTR lpAuthentInfoType,
574         LPVOID lpAuthentInfo,
575         LPCWSTR lpPreviousAuthentInfoType,
576         LPVOID lpPreviousAuthentInfo,
577         LPWSTR lpStationName,
578         LPVOID StationHandle,
579         LPWSTR *lpLogonScript)
580 {
581     char uname[MAX_USERNAME_LENGTH]="";
582     char password[MAX_PASSWORD_LENGTH]="";
583     char logonDomain[MAX_DOMAIN_LENGTH]="";
584     char cell[256]="<non-integrated logon>";
585     char homePath[MAX_PATH]="";
586
587     MSV1_0_INTERACTIVE_LOGON *IL;
588
589     DWORD code;
590
591     int pw_exp;
592     char *reason;
593     char *ctemp;
594
595     BOOLEAN interactive;
596     BOOLEAN flag;
597     DWORD LSPtype, LSPsize;
598     HKEY NPKey;
599
600     HWND hwndOwner = (HWND)StationHandle;
601
602     BOOLEAN afsWillAutoStart;
603
604     BOOLEAN uppercased_name = TRUE;
605
606     LogonOptions_t opt; /* domain specific logon options */
607     int retryInterval;
608     int sleepInterval;
609
610     /* Make sure the AFS Libraries are initialized */
611     AfsLogonInit();
612
613     /* Initialize Logon Script to none */
614     *lpLogonScript=NULL;
615     
616     /* TODO: We should check the value of lpAuthentInfoType before assuming that it is
617        MSV1_0_INTERACTIVE_LOGON though for our purposes KERB_INTERACTIVE_LOGON is
618        co-incidentally equivalent. */
619     IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
620
621     /* Are we interactive? */
622     interactive = (wcscmp(lpStationName, L"WinSta0") == 0);
623
624     /* Convert from Unicode to ANSI */
625
626     /*TODO: Use SecureZeroMemory to erase passwords */
627     UnicodeStringToANSI(IL->UserName, uname, MAX_USERNAME_LENGTH);
628     UnicodeStringToANSI(IL->Password, password, MAX_PASSWORD_LENGTH);
629     UnicodeStringToANSI(IL->LogonDomainName, logonDomain, MAX_DOMAIN_LENGTH);
630
631     /* Make sure AD-DOMANS sent from login that is sent to us is striped */
632     ctemp = strchr(uname, '@');
633     if (ctemp) *ctemp = 0;
634
635     /* is the name all uppercase? */
636     for ( ctemp = uname; *ctemp ; ctemp++) {
637         if ( islower(*ctemp) ) {
638             uppercased_name = FALSE;
639             break;
640         }
641     }
642
643     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
644                          0, KEY_QUERY_VALUE, &NPKey);
645     LSPsize=sizeof(TraceOption);
646     RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
647                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
648
649     RegCloseKey (NPKey);
650
651     /*
652      * Get Logon options
653      */
654
655     GetDomainLogonOptions( lpLogonId, uname, logonDomain, &opt );
656     retryInterval = opt.retryInterval;
657     sleepInterval = opt.sleepInterval;
658     *lpLogonScript = opt.logonScript;
659
660     DebugEvent("Got logon script: %S",opt.logonScript);
661
662     afsWillAutoStart = AFSWillAutoStart();
663
664     DebugEvent("LogonOption[%x], Service AutoStart[%d]",
665                 opt.LogonOption,afsWillAutoStart);
666     
667     /* Check for zero length password if integrated logon*/
668     if ( ISLOGONINTEGRATED(opt.LogonOption) )  {
669         if ( password[0] == 0 ) {
670             DebugEvent("Password is the empty string");
671             code = GT_PW_NULL;
672             reason = "zero length password is illegal";
673             code=0;
674         }       
675
676         /* Get cell name if doing integrated logon.  
677            We might overwrite this if we are logging into an AD realm and we find out that
678            the user's home dir is in some other cell. */
679         DebugEvent("About to call cm_GetRootCellName(%s)",cell);
680         code = cm_GetRootCellName(cell);
681         if (code < 0) { 
682             DebugEvent("Unable to obtain Root Cell");
683             code = KTC_NOCELL;
684             reason = "unknown cell";
685             code=0;
686         } else {
687             DebugEvent("Cell is %s",cell);
688         }       
689
690         /* We get the user's home directory path, if applicable, though we can't lookup the
691            cell right away because the client service may not have started yet. This call
692            also sets the AD_REALM flag in opt.flags if applicable. */
693         if(ISREMOTE(opt.flags)) {
694             DebugEvent("Is Remote");
695             GetAdHomePath(homePath,MAX_PATH,lpLogonId,&opt);
696         }       
697     }
698
699     /* loop until AFS is started. */
700     while (TRUE) {
701         DebugEvent("while(TRUE) LogonOption[%x], Service AutoStart[%d]",
702                     opt.LogonOption,afsWillAutoStart);
703
704         if(ISADREALM(opt.flags)) {
705             code = GetFileCellName(homePath,cell,256);
706             if(!code) {
707                 DebugEvent("profile path [%s] is in cell [%s]",homePath,cell);
708             }
709             /* Don't bail out if GetFileCellName failed.
710              * The home dir may not be in AFS after all. 
711              */
712         } else
713             code=0;
714                 
715         /* if Integrated Logon  */
716         if (ISLOGONINTEGRATED(opt.LogonOption))
717         {                       
718             if ( KFW_is_available() ) {
719                 code = KFW_AFS_get_cred(uname, cell, password, 0, opt.smbName, &reason);
720                 DebugEvent("KFW_AFS_get_cred  uname=[%s] smbname=[%s] cell=[%s] code=[%d]",uname,opt.smbName,cell,code);
721             }
722             else {
723                 code = ka_UserAuthenticateGeneral2(KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
724                                                     uname, "", cell, password, opt.smbName, 0, &pw_exp, 0,
725                                                     &reason);
726                 DebugEvent("AFS AfsLogon - (INTEGRATED only)ka_UserAuthenticateGeneral2","Code[%x]",
727                             code);
728             }       
729             if ( code && code != KTC_NOCM && code != KTC_NOCMRPC && uppercased_name ) {
730                 for ( ctemp = uname; *ctemp ; ctemp++) {
731                     *ctemp = tolower(*ctemp);
732                 }
733                 uppercased_name = FALSE;
734                 continue;
735             }
736         }
737         else {  
738             /*JUST check to see if its running*/
739             if (IsServiceRunning())
740                 break;
741             code = KTC_NOCM;
742             if (!afsWillAutoStart)
743                 break;
744         }
745
746         /* is service started yet?*/
747         DebugEvent("AFS AfsLogon - ka_UserAuthenticateGeneral2","Code[%x] uname[%s] Cell[%s]",
748                     code,uname,cell);
749
750         /* If we've failed because the client isn't running yet and the
751          * client is set to autostart (and therefore it makes sense for
752          * us to wait for it to start) then sleep a while and try again. 
753          * If the error was something else, then give up. */
754         if (code != KTC_NOCM && code != KTC_NOCMRPC || !afsWillAutoStart)
755             break;
756
757         /* If the retry interval has expired and we still aren't
758          * logged in, then just give up if we are not in interactive
759          * mode or the failSilently flag is set, otherwise let the
760          * user know we failed and give them a chance to try again. */
761         if (retryInterval <= 0) {
762             reason = "AFS not running";
763             if (!interactive || opt.failSilently)
764                 break;
765             flag = MessageBox(hwndOwner,
766                                "AFS is still starting.  Retry?",
767                                "AFS Logon",
768                                MB_ICONQUESTION | MB_RETRYCANCEL);
769             if (flag == IDCANCEL)
770                 break;
771
772             /* Wait just a little while and try again */
773             retryInterval = sleepInterval = DEFAULT_SLEEP_INTERVAL;
774         }
775
776         if (retryInterval < sleepInterval)
777             sleepInterval = retryInterval;
778
779         Sleep(sleepInterval * 1000);
780
781         retryInterval -= sleepInterval;
782     }
783
784     DebugEvent("while loop exited");
785     /* remove any kerberos 5 tickets currently held by the SYSTEM account */
786     if ( KFW_is_available() )
787         KFW_AFS_destroy_tickets_for_cell(cell);
788
789     if (code) {
790         char msg[128];
791         HANDLE h;
792         char *ptbuf[1];
793
794         StringCbPrintf(msg, sizeof(msg), "Integrated login failed: %s", reason);
795
796         if (ISLOGONINTEGRATED(opt.LogonOption) && interactive && !opt.failSilently)
797             MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
798
799         h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
800         ptbuf[0] = msg;
801         ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
802                      1, 0, ptbuf, NULL);
803         DeregisterEventSource(h);
804             
805         code = MapAuthError(code);
806         SetLastError(code);
807
808         if (ISLOGONINTEGRATED(opt.LogonOption) && (code!=0))
809         {
810             if (*lpLogonScript)
811                 LocalFree(*lpLogonScript);
812             *lpLogonScript = NULL;
813             if (!afsWillAutoStart)      // its not running, so if not autostart or integrated logon then just skip
814                 code = 0;
815
816         }
817     }
818
819     if(opt.smbName) free(opt.smbName);
820
821     DebugEvent("AFS AfsLogon - Exit","Return Code[%x]",code);
822     return code;
823 }       
824
825 DWORD APIENTRY NPPasswordChangeNotify(
826         LPCWSTR lpAuthentInfoType,
827         LPVOID lpAuthentInfo,
828         LPCWSTR lpPreviousAuthentInfoType,
829         LPVOID lpPreviousAuthentInfo,
830         LPWSTR lpStationName,
831         LPVOID StationHandle,
832         DWORD dwChangeInfo)
833 {
834     /* Make sure the AFS Libraries are initialized */
835     AfsLogonInit();
836
837     DebugEvent0("AFS AfsLogon - NPPasswordChangeNotify");
838     return 0;
839 }
840
841 #include <userenv.h>
842 #include <Winwlx.h>
843 #include <afs/vice.h>
844 #include <afs/fs_utils.h>
845
846 BOOL IsPathInAfs(const CHAR *strPath)
847 {
848     char space[2048];
849     struct ViceIoctl blob;
850     int code;
851
852     blob.in_size = 0;
853     blob.out_size = 2048;
854     blob.out = space;
855
856     code = pioctl((LPTSTR)((LPCTSTR)strPath), VIOC_FILE_CELL_NAME, &blob, 1);
857     if (code)
858         return FALSE;
859     return TRUE;
860 }
861
862 #ifdef COMMENT
863 typedef struct _WLX_NOTIFICATION_INFO {  
864     ULONG Size;  
865     ULONG Flags;  
866     PWSTR UserName;  
867     PWSTR Domain;  
868     PWSTR WindowStation;  
869     HANDLE hToken;  
870     HDESK hDesktop;  
871     PFNMSGECALLBACK pStatusCallback;
872 } WLX_NOTIFICATION_INFO, *PWLX_NOTIFICATION_INFO;
873 #endif
874
875 VOID AFS_Startup_Event( PWLX_NOTIFICATION_INFO pInfo )
876 {
877     DWORD LSPtype, LSPsize;
878     HKEY NPKey;
879
880     /* Make sure the AFS Libraries are initialized */
881     AfsLogonInit();
882
883     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
884                         0, KEY_QUERY_VALUE, &NPKey);
885     LSPsize=sizeof(TraceOption);
886     RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
887                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
888
889     RegCloseKey (NPKey);
890     DebugEvent0("AFS_Startup_Event");
891 }
892
893 VOID AFS_Logoff_Event( PWLX_NOTIFICATION_INFO pInfo )
894 {
895     DWORD code;
896     TCHAR profileDir[1024] = TEXT("");
897     DWORD  len = 1024;
898     PTOKEN_USER  tokenUser = NULL;
899     DWORD  retLen;
900     HANDLE hToken;
901
902     /* Make sure the AFS Libraries are initialized */
903     AfsLogonInit();
904
905     DebugEvent0("AFS_Logoff_Event - Starting");
906
907     if (!GetTokenInformation(pInfo->hToken, TokenUser, NULL, 0, &retLen))
908     {
909         if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
910             tokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen);
911
912             if (!GetTokenInformation(pInfo->hToken, TokenUser, tokenUser, retLen, &retLen))
913             {
914                 DebugEvent("GetTokenInformation failed: GLE = %lX", GetLastError());
915             }
916         }
917     }
918
919     /* We can't use pInfo->Domain for the domain since in the cross realm case 
920      * this is source domain and not the destination domain.
921      */
922     if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, pInfo->Domain)) {
923         WCHAR Domain[64]=L"";
924         GetLocalShortDomain(Domain, sizeof(Domain));
925         if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, Domain)) {
926             if (NetUserGetProfilePath(pInfo->Domain, pInfo->UserName, profileDir, len))
927                 GetUserProfileDirectory(pInfo->hToken, profileDir, &len);
928         }
929     }
930     
931     if (strlen(profileDir)) {
932         DebugEvent("Profile Directory: %s", profileDir);
933         if (!IsPathInAfs(profileDir)) {
934             if (code = ktc_ForgetAllTokens())
935                 DebugEvent("AFS_Logoff_Event - ForgetAllTokens failed [%lX]",code);
936             else
937                 DebugEvent0("AFS_Logoff_Event - ForgetAllTokens succeeded");
938         } else {
939             DebugEvent0("AFS_Logoff_Event - Tokens left in place; profile in AFS");
940         }
941     } else {
942         DebugEvent0("AFS_Logoff_Event - Unable to load profile");
943     }
944
945     if ( tokenUser )
946         LocalFree(tokenUser);
947 }   
948