windows-misc-20050207
[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 BOOL IsServiceStartPending (void)
258 {
259     SERVICE_STATUS Status;
260     SC_HANDLE hManager;
261     memset (&Status, 0x00, sizeof(Status));
262     Status.dwCurrentState = SERVICE_STOPPED;
263
264     if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
265     {
266         SC_HANDLE hService;
267         if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
268         {
269             QueryServiceStatus (hService, &Status);
270             CloseServiceHandle (hService);
271         }
272
273         CloseServiceHandle (hManager);
274     }
275     DebugEvent("AFS AfsLogon - Test Service Start Pending","Return Code[%x] ?Start Pending[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_START_PENDING));
276     return (Status.dwCurrentState == SERVICE_RUNNING);
277 }   
278
279 /* LOOKUPKEYCHAIN: macro to look up the value in the list of keys in order until it's found
280    v:variable to receive value (reference type)
281    t:type
282    d:default, in case the value isn't on any of the keys
283    n:name of value */
284 #define LOOKUPKEYCHAIN(v,t,d,n) \
285         do { \
286                 rv = ~ERROR_SUCCESS; \
287                 dwType = t; \
288                 if(hkDom) { \
289                         dwSize = sizeof(v); \
290                         rv = RegQueryValueEx(hkDom, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
291                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkDom with type [%d]", dwType); \
292                 } \
293                 if(hkDoms && (rv != ERROR_SUCCESS || dwType != t)) { \
294                         dwSize = sizeof(v); \
295                         rv = RegQueryValueEx(hkDoms, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
296                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkDoms with type [%d]", dwType); \
297                 } \
298                 if(hkNp && (rv != ERROR_SUCCESS || dwType != t)) { \
299                         dwSize = sizeof(v); \
300                         rv = RegQueryValueEx(hkNp, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
301                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkNp with type [%d]", dwType); \
302                 } \
303                 if(rv != ERROR_SUCCESS || dwType != t) { \
304                         v = d; \
305                         DebugEvent(#v " being set to default"); \
306                 } \
307         } while(0)
308
309 /* Get domain specific configuration info.  We are returning void because if anything goes wrong
310    we just return defaults.
311  */
312 void 
313 GetDomainLogonOptions( PLUID lpLogonId, char * username, char * domain, LogonOptions_t *opt ) {
314     HKEY hkParm = NULL; /* Service parameter */
315     HKEY hkNp = NULL;   /* network provider key */
316     HKEY hkDoms = NULL; /* domains key */
317     HKEY hkDom = NULL;  /* DOMAINS/domain key */
318     HKEY hkTemp = NULL;
319     LONG rv;
320     DWORD dwSize;
321     DWORD dwType;
322     DWORD dwDummy;
323     char computerName[MAX_COMPUTERNAME_LENGTH + 1];
324     char *effDomain;
325
326     DebugEvent("In GetDomainLogonOptions for user [%s] in domain [%s]", username, domain);
327     /* If the domain is the same as the Netbios computer name, we use the LOCALHOST domain name*/
328     opt->flags = LOGON_FLAG_REMOTE;
329     if(domain) {
330         dwSize = MAX_COMPUTERNAME_LENGTH;
331         if(GetComputerName(computerName, &dwSize)) {
332             if(!stricmp(computerName, domain)) {
333                 effDomain = "LOCALHOST";
334                 opt->flags = LOGON_FLAG_LOCAL;
335             }
336             else
337                 effDomain = domain;
338         }
339     } else
340         effDomain = NULL;
341
342     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY, 0, KEY_READ, &hkParm );
343     if(rv != ERROR_SUCCESS) {
344         hkParm = NULL;
345         DebugEvent("GetDomainLogonOption: Can't open parms key [%d]", rv);
346     }
347
348     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_CLIENT_PROVIDER_KEY, 0, KEY_READ, &hkNp );
349     if(rv != ERROR_SUCCESS) {
350         hkNp = NULL;
351         DebugEvent("GetDomainLogonOptions: Can't open NP key [%d]", rv);
352     }
353
354     if(hkNp) {
355         rv = RegOpenKeyEx( hkNp, REG_CLIENT_DOMAINS_SUBKEY, 0, KEY_READ, &hkDoms );
356         if( rv != ERROR_SUCCESS ) {
357             hkDoms = NULL;
358             DebugEvent("GetDomainLogonOptions: Can't open Domains key [%d]", rv);
359         }
360     }
361
362     if(hkDoms && effDomain) {
363         rv = RegOpenKeyEx( hkDoms, effDomain, 0, KEY_READ, &hkDom );
364         if( rv != ERROR_SUCCESS ) {
365             hkDom = NULL;
366             DebugEvent("GetDomainLogonOptions: Can't open domain key for [%s] [%d]", effDomain, rv);
367             /* If none of the domains match, we shouldn't use the domain key either */
368             RegCloseKey(hkDoms);
369             hkDoms = NULL;
370         }
371     } else
372         DebugEvent("Not opening domain key for [%s]", effDomain);
373
374     /* Each individual can either be specified on the domain key, the domains key or in the
375        net provider key.  They fail over in that order.  If none is found, we just use the 
376        defaults. */
377
378     /* LogonOption */
379     LOOKUPKEYCHAIN(opt->LogonOption, REG_DWORD, DEFAULT_LOGON_OPTION, REG_CLIENT_LOGON_OPTION_PARM);
380
381     /* FailLoginsSilently */
382     dwSize = sizeof(dwDummy);
383     rv = RegQueryValueEx(hkParm, REG_CLIENT_FAIL_SILENTLY_PARM, 0, &dwType, (LPBYTE) &dwDummy, &dwSize);
384     if (rv != ERROR_SUCCESS)
385         LOOKUPKEYCHAIN(dwDummy, REG_DWORD, DEFAULT_FAIL_SILENTLY, REG_CLIENT_FAIL_SILENTLY_PARM);
386     opt->failSilently = !!dwDummy;
387
388     /* Retry interval */
389     LOOKUPKEYCHAIN(opt->retryInterval, REG_DWORD, DEFAULT_RETRY_INTERVAL, REG_CLIENT_RETRY_INTERVAL_PARM);
390
391     /* Sleep interval */
392     LOOKUPKEYCHAIN(opt->sleepInterval, REG_DWORD, DEFAULT_SLEEP_INTERVAL, REG_CLIENT_SLEEP_INTERVAL_PARM);
393
394     opt->logonScript = NULL;
395     opt->smbName = NULL;
396
397     if(!ISLOGONINTEGRATED(opt->LogonOption)) {
398         goto cleanup; /* no need to lookup the logon script */
399     }
400
401     /* come up with SMB username */
402     if(ISHIGHSECURITY(opt->LogonOption)) {
403         opt->smbName = malloc( MAXRANDOMNAMELEN );
404         GenRandomName(opt->smbName);
405     } else {
406         /* username and domain for logon session is not necessarily the same as
407            username and domain passed into network provider. */
408         PSECURITY_LOGON_SESSION_DATA plsd;
409         char lsaUsername[MAX_USERNAME_LENGTH];
410         char lsaDomain[MAX_DOMAIN_LENGTH];
411         size_t len, tlen;
412
413         LsaGetLogonSessionData(lpLogonId, &plsd);
414         
415         UnicodeStringToANSI(plsd->UserName, lsaUsername, MAX_USERNAME_LENGTH);
416         UnicodeStringToANSI(plsd->LogonDomain, lsaDomain, MAX_DOMAIN_LENGTH);
417
418         DebugEvent("PLSD username[%s] domain[%s]",lsaUsername,lsaDomain);
419
420         if(SUCCEEDED(StringCbLength(lsaUsername, MAX_USERNAME_LENGTH, &tlen)))
421             len = tlen;
422         else
423             goto bad_strings;
424
425         if(SUCCEEDED(StringCbLength(lsaDomain, MAX_DOMAIN_LENGTH, &tlen)))
426             len += tlen;
427         else
428             goto bad_strings;
429
430         len += 2;
431
432         opt->smbName = malloc(len);
433
434         StringCbCopy(opt->smbName, len, lsaDomain);
435         StringCbCat(opt->smbName, len, "\\");
436         StringCbCat(opt->smbName, len, lsaUsername);
437
438         strlwr(opt->smbName);
439
440       bad_strings:
441         LsaFreeReturnBuffer(plsd);
442     }
443
444     DebugEvent("Looking up logon script");
445     /* Logon script */
446     /* First find out where the key is */
447     hkTemp = NULL;
448     rv = ~ERROR_SUCCESS;
449     dwType = 0;
450     if(hkDom)
451         rv = RegQueryValueExW(hkDom, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
452     if(rv == ERROR_SUCCESS && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
453         hkTemp = hkDom;
454         DebugEvent("Located logon script in hkDom");
455     }
456     else if(hkDoms)
457         rv = RegQueryValueExW(hkDoms, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
458     if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
459         hkTemp = hkDoms;
460         DebugEvent("Located logon script in hkDoms");
461     }
462     /* Note that the LogonScript in the NP key is only used if we are doing high security. */
463     else if(hkNp && ISHIGHSECURITY(opt->LogonOption))
464         rv = RegQueryValueExW(hkNp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
465     if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
466         hkTemp = hkNp;
467         DebugEvent("Located logon script in hkNp");
468     }
469
470     if(hkTemp) {
471         WCHAR *regscript        = NULL;
472         WCHAR *regexscript      = NULL;
473         WCHAR *regexuscript     = NULL;
474         WCHAR *wuname           = NULL;
475         HRESULT hr;
476
477         size_t len;
478
479         StringCbLength(opt->smbName, MAX_USERNAME_LENGTH, &len);
480         len ++;
481
482         wuname = malloc(len * sizeof(WCHAR));
483         MultiByteToWideChar(CP_ACP,0,opt->smbName,-1,wuname,len*sizeof(WCHAR));
484
485         DebugEvent("Username is set for [%S]", wuname);
486
487         /* dwSize still has the size of the required buffer in bytes. */
488         regscript = malloc(dwSize);
489         rv = RegQueryValueExW(hkTemp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, (LPBYTE) regscript, &dwSize);
490         if(rv != ERROR_SUCCESS) {/* what the ..? */
491             DebugEvent("Can't look up logon script [%d]",rv);
492             goto doneLogonScript;
493         }
494
495         DebugEvent("Found logon script [%S]", regscript);
496
497         if(dwType == REG_EXPAND_SZ) {
498             DWORD dwReq;
499
500             dwSize += MAX_PATH * sizeof(WCHAR);  /* make room for environment expansion. */
501             regexscript = malloc(dwSize);
502             dwReq = ExpandEnvironmentStringsW(regscript, regexscript, dwSize / sizeof(WCHAR));
503             free(regscript);
504             regscript = regexscript;
505             regexscript = NULL;
506             if(dwReq > (dwSize / sizeof(WCHAR))) {
507                 DebugEvent("Overflow while expanding environment strings.");
508                 goto doneLogonScript;
509             }
510         }
511
512         DebugEvent("After expanding env strings [%S]", regscript);
513
514         if(wcsstr(regscript, L"%s")) {
515             dwSize += len * sizeof(WCHAR); /* make room for username expansion */
516             regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
517             hr = StringCbPrintfW(regexuscript, dwSize, regscript, wuname);
518         } else {
519             regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
520             hr = StringCbCopyW(regexuscript, dwSize, regscript);
521         }
522
523         DebugEvent("After expanding username [%S]", regexuscript);
524
525         if(hr == S_OK)
526             opt->logonScript = regexuscript;
527         else
528             LocalFree(regexuscript);
529
530       doneLogonScript:
531         if(wuname) free(wuname);
532         if(regscript) free(regscript);
533         if(regexscript) free(regexscript);
534     }
535
536   cleanup:
537     if(hkNp) RegCloseKey(hkNp);
538     if(hkDom) RegCloseKey(hkDom);
539     if(hkDoms) RegCloseKey(hkDoms);
540     if(hkParm) RegCloseKey(hkParm);
541 }       
542
543 #undef LOOKUPKEYCHAIN
544
545 /* Try to find out which cell the given path is in.  We must retain
546    the contents of *cell in case of failure. *cell is assumed to be
547    at least cellLen chars */
548 DWORD GetFileCellName(char * path, char * cell, size_t cellLen) {
549     struct ViceIoctl blob;
550     char tcell[MAX_PATH];
551     DWORD code;
552
553     blob.in_size = 0;
554     blob.out_size = MAX_PATH;
555     blob.out = tcell;
556
557     code = pioctl(path, VIOC_FILE_CELL_NAME, &blob, 1);
558
559     if(!code) {
560         strncpy(cell, tcell, cellLen);
561         cell[cellLen - 1] = '\0';
562     }
563     return code;
564 }       
565
566
567 static BOOL
568 WINAPI
569 UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen)
570 {
571     CPINFO CodePageInfo;
572
573     GetCPInfo(CP_ACP, &CodePageInfo);
574
575     if (CodePageInfo.MaxCharSize > 1)
576         // Only supporting non-Unicode strings
577         return FALSE;
578     
579     if (uInputString.Buffer && ((LPBYTE) uInputString.Buffer)[1] == '\0')
580     {
581         // Looks like unicode, better translate it
582         // UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS
583         WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,
584                             lpszOutputString, nOutStringLen-1, NULL, NULL);
585         lpszOutputString[min(uInputString.Length/2,nOutStringLen-1)] = '\0';
586         return TRUE;
587     }
588     else
589         lpszOutputString[0] = '\0';
590     return FALSE;
591 }  // UnicodeStringToANSI
592
593 DWORD APIENTRY NPLogonNotify(
594         PLUID lpLogonId,
595         LPCWSTR lpAuthentInfoType,
596         LPVOID lpAuthentInfo,
597         LPCWSTR lpPreviousAuthentInfoType,
598         LPVOID lpPreviousAuthentInfo,
599         LPWSTR lpStationName,
600         LPVOID StationHandle,
601         LPWSTR *lpLogonScript)
602 {
603     char uname[MAX_USERNAME_LENGTH]="";
604     char password[MAX_PASSWORD_LENGTH]="";
605     char logonDomain[MAX_DOMAIN_LENGTH]="";
606     char cell[256]="<non-integrated logon>";
607     char homePath[MAX_PATH]="";
608
609     MSV1_0_INTERACTIVE_LOGON *IL;
610
611     DWORD code;
612
613     int pw_exp;
614     char *reason = 0;
615     char *ctemp;
616
617     BOOLEAN interactive;
618     BOOLEAN flag;
619     DWORD LSPtype, LSPsize;
620     HKEY NPKey;
621
622     HWND hwndOwner = (HWND)StationHandle;
623
624     BOOLEAN afsWillAutoStart;
625
626     BOOLEAN lowercased_name = TRUE;
627
628     LogonOptions_t opt; /* domain specific logon options */
629     int retryInterval;
630     int sleepInterval;
631
632     /* Make sure the AFS Libraries are initialized */
633     AfsLogonInit();
634
635     /* Initialize Logon Script to none */
636     *lpLogonScript=NULL;
637     
638     /* TODO: We should check the value of lpAuthentInfoType before assuming that it is
639        MSV1_0_INTERACTIVE_LOGON though for our purposes KERB_INTERACTIVE_LOGON is
640        co-incidentally equivalent. */
641     IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
642
643     /* Are we interactive? */
644     interactive = (wcscmp(lpStationName, L"WinSta0") == 0);
645
646     /* Convert from Unicode to ANSI */
647
648     /*TODO: Use SecureZeroMemory to erase passwords */
649     UnicodeStringToANSI(IL->UserName, uname, MAX_USERNAME_LENGTH);
650     UnicodeStringToANSI(IL->Password, password, MAX_PASSWORD_LENGTH);
651     UnicodeStringToANSI(IL->LogonDomainName, logonDomain, MAX_DOMAIN_LENGTH);
652
653     /* Make sure AD-DOMANS sent from login that is sent to us is striped */
654     ctemp = strchr(uname, '@');
655     if (ctemp) *ctemp = 0;
656
657     /* is the name all lowercase? */
658     for ( ctemp = uname; *ctemp ; ctemp++) {
659         if ( !islower(*ctemp) ) {
660             lowercased_name = FALSE;
661             break;
662         }
663     }
664
665     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
666                          0, KEY_QUERY_VALUE, &NPKey);
667     LSPsize=sizeof(TraceOption);
668     RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
669                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
670
671     RegCloseKey (NPKey);
672
673     /*
674      * Get Logon options
675      */
676
677     GetDomainLogonOptions( lpLogonId, uname, logonDomain, &opt );
678     retryInterval = opt.retryInterval;
679     sleepInterval = opt.sleepInterval;
680     *lpLogonScript = opt.logonScript;
681
682     if (retryInterval < sleepInterval)
683         sleepInterval = retryInterval;
684
685     DebugEvent("Got logon script: %S",opt.logonScript);
686
687     afsWillAutoStart = AFSWillAutoStart();
688
689     DebugEvent("LogonOption[%x], Service AutoStart[%d]",
690                 opt.LogonOption,afsWillAutoStart);
691     
692     /* Check for zero length password if integrated logon*/
693     if ( ISLOGONINTEGRATED(opt.LogonOption) )  {
694         if ( password[0] == 0 ) {
695             DebugEvent("Password is the empty string");
696             code = GT_PW_NULL;
697             reason = "zero length password is illegal";
698             code=0;
699         }       
700
701         /* Get cell name if doing integrated logon.  
702            We might overwrite this if we are logging into an AD realm and we find out that
703            the user's home dir is in some other cell. */
704         DebugEvent("About to call cm_GetRootCellName(%s)",cell);
705         code = cm_GetRootCellName(cell);
706         if (code < 0) { 
707             DebugEvent("Unable to obtain Root Cell");
708             code = KTC_NOCELL;
709             reason = "unknown cell";
710             code=0;
711         } else {
712             DebugEvent("Cell is %s",cell);
713         }       
714
715         /* We get the user's home directory path, if applicable, though we can't lookup the
716            cell right away because the client service may not have started yet. This call
717            also sets the AD_REALM flag in opt.flags if applicable. */
718         if (ISREMOTE(opt.flags)) {
719             DebugEvent("Is Remote");
720             GetAdHomePath(homePath,MAX_PATH,lpLogonId,&opt);
721         }       
722     }
723
724     /* loop until AFS is started. */
725     while (TRUE) {
726         DebugEvent("while(TRUE) LogonOption[%x], Service AutoStart[%d]",
727                     opt.LogonOption,afsWillAutoStart);
728
729         if (ISADREALM(opt.flags)) {
730             code = GetFileCellName(homePath,cell,256);
731             if (!code) {
732                 DebugEvent("profile path [%s] is in cell [%s]",homePath,cell);
733             }
734             /* Don't bail out if GetFileCellName failed.
735              * The home dir may not be in AFS after all. 
736              */
737         } else
738             code=0;
739                 
740         /* if Integrated Logon  */
741         if (ISLOGONINTEGRATED(opt.LogonOption))
742         {       
743             if ( KFW_is_available() ) {
744                 code = KFW_AFS_get_cred(uname, cell, password, 0, opt.smbName, &reason);
745                 DebugEvent("KFW_AFS_get_cred  uname=[%s] smbname=[%s] cell=[%s] code=[%d]",
746                            uname,opt.smbName,cell,code);
747             } else {
748                 code = ka_UserAuthenticateGeneral2(KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
749                                                     uname, "", cell, password, opt.smbName, 0, &pw_exp, 0,
750                                                     &reason);
751
752                 DebugEvent("AFS AfsLogon - ka_UserAuthenticateGeneral2","Code[%x] uname[%s] Cell[%s] Reason[%s]",
753                             code,uname,cell,reason ? reason : "<none>");
754                 {
755                     char msg[2048];
756                     sprintf(msg, "Code[%x] uname[%s] Cell[%s] Reason[%s]",
757                             code,uname,cell,reason ? reason : "<none>");
758                     MessageBox(hwndOwner,
759                                 msg,
760                                 "AFS Logon",
761                                 MB_ICONINFORMATION | MB_OK);
762                 }
763             }       
764             if ( code && code != KTC_NOCM && code != KTC_NOCMRPC && !lowercased_name ) {
765                 for ( ctemp = uname; *ctemp ; ctemp++) {
766                     *ctemp = tolower(*ctemp);
767                 }
768                 lowercased_name = TRUE;
769                 goto sleeping;
770             }
771
772             /* is service started yet?*/
773             /* If we've failed because the client isn't running yet and the
774             * client is set to autostart (and therefore it makes sense for
775             * us to wait for it to start) then sleep a while and try again. 
776             * If the error was something else, then give up. */
777             if (code != KTC_NOCM && code != KTC_NOCMRPC || !afsWillAutoStart)
778                 break;
779         } else {  
780             /*JUST check to see if its running*/
781             if (IsServiceRunning())
782                 break;
783             if (afsWillAutoStart && !IsServiceStartPending()) {
784                 code = KTC_NOCMRPC;
785                 reason = "AFS Service start failed";
786                 break;
787             }
788         }
789
790         /* If the retry interval has expired and we still aren't
791          * logged in, then just give up if we are not in interactive
792          * mode or the failSilently flag is set, otherwise let the
793          * user know we failed and give them a chance to try again. */
794         if (retryInterval <= 0) {
795             reason = "AFS not running";
796             if (!interactive || opt.failSilently)
797                 break;
798             flag = MessageBox(hwndOwner,
799                                "AFS is still starting.  Retry?",
800                                "AFS Logon",
801                                MB_ICONQUESTION | MB_RETRYCANCEL);
802             if (flag == IDCANCEL)
803                 break;
804
805             /* Wait just a little while and try again */
806             retryInterval = opt.retryInterval;
807         }
808
809       sleeping:
810         Sleep(sleepInterval * 1000);
811         retryInterval -= sleepInterval;
812     }
813
814     DebugEvent("while loop exited");
815     /* remove any kerberos 5 tickets currently held by the SYSTEM account */
816     if ( KFW_is_available() )
817         KFW_AFS_destroy_tickets_for_cell(cell);
818
819     if (code) {
820         char msg[128];
821         HANDLE h;
822         char *ptbuf[1];
823
824         StringCbPrintf(msg, sizeof(msg), "Integrated login failed: %s", reason);
825
826         if (ISLOGONINTEGRATED(opt.LogonOption) && interactive && !opt.failSilently)
827             MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
828
829         h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
830         ptbuf[0] = msg;
831         ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
832                      1, 0, ptbuf, NULL);
833         DeregisterEventSource(h);
834             
835         code = MapAuthError(code);
836         SetLastError(code);
837
838         if (ISLOGONINTEGRATED(opt.LogonOption) && (code!=0))
839         {
840             if (*lpLogonScript)
841                 LocalFree(*lpLogonScript);
842             *lpLogonScript = NULL;
843             if (!afsWillAutoStart)      // its not running, so if not autostart or integrated logon then just skip
844                 code = 0;
845         }
846     }
847
848     if(opt.smbName) free(opt.smbName);
849
850     DebugEvent("AFS AfsLogon - Exit","Return Code[%x]",code);
851     return code;
852 }       
853
854 DWORD APIENTRY NPPasswordChangeNotify(
855         LPCWSTR lpAuthentInfoType,
856         LPVOID lpAuthentInfo,
857         LPCWSTR lpPreviousAuthentInfoType,
858         LPVOID lpPreviousAuthentInfo,
859         LPWSTR lpStationName,
860         LPVOID StationHandle,
861         DWORD dwChangeInfo)
862 {
863     /* Make sure the AFS Libraries are initialized */
864     AfsLogonInit();
865
866     DebugEvent0("AFS AfsLogon - NPPasswordChangeNotify");
867     return 0;
868 }
869
870 #include <userenv.h>
871 #include <Winwlx.h>
872 #include <afs/vice.h>
873 #include <afs/fs_utils.h>
874
875 BOOL IsPathInAfs(const CHAR *strPath)
876 {
877     char space[2048];
878     struct ViceIoctl blob;
879     int code;
880
881     blob.in_size = 0;
882     blob.out_size = 2048;
883     blob.out = space;
884
885     code = pioctl((LPTSTR)((LPCTSTR)strPath), VIOC_FILE_CELL_NAME, &blob, 1);
886     if (code)
887         return FALSE;
888     return TRUE;
889 }
890
891 #ifdef COMMENT
892 typedef struct _WLX_NOTIFICATION_INFO {  
893     ULONG Size;  
894     ULONG Flags;  
895     PWSTR UserName;  
896     PWSTR Domain;  
897     PWSTR WindowStation;  
898     HANDLE hToken;  
899     HDESK hDesktop;  
900     PFNMSGECALLBACK pStatusCallback;
901 } WLX_NOTIFICATION_INFO, *PWLX_NOTIFICATION_INFO;
902 #endif
903
904 VOID AFS_Startup_Event( PWLX_NOTIFICATION_INFO pInfo )
905 {
906     DWORD LSPtype, LSPsize;
907     HKEY NPKey;
908
909     /* Make sure the AFS Libraries are initialized */
910     AfsLogonInit();
911
912     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
913                         0, KEY_QUERY_VALUE, &NPKey);
914     LSPsize=sizeof(TraceOption);
915     RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
916                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
917
918     RegCloseKey (NPKey);
919     DebugEvent0("AFS_Startup_Event");
920 }
921
922 VOID AFS_Logoff_Event( PWLX_NOTIFICATION_INFO pInfo )
923 {
924     DWORD code;
925     TCHAR profileDir[1024] = TEXT("");
926     DWORD  len = 1024;
927     PTOKEN_USER  tokenUser = NULL;
928     DWORD  retLen;
929     HANDLE hToken;
930
931     /* Make sure the AFS Libraries are initialized */
932     AfsLogonInit();
933
934     DebugEvent0("AFS_Logoff_Event - Starting");
935
936     if (!GetTokenInformation(pInfo->hToken, TokenUser, NULL, 0, &retLen))
937     {
938         if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
939             tokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen);
940
941             if (!GetTokenInformation(pInfo->hToken, TokenUser, tokenUser, retLen, &retLen))
942             {
943                 DebugEvent("GetTokenInformation failed: GLE = %lX", GetLastError());
944             }
945         }
946     }
947
948     /* We can't use pInfo->Domain for the domain since in the cross realm case 
949      * this is source domain and not the destination domain.
950      */
951     if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, pInfo->Domain)) {
952         WCHAR Domain[64]=L"";
953         GetLocalShortDomain(Domain, sizeof(Domain));
954         if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, Domain)) {
955             if (NetUserGetProfilePath(pInfo->Domain, pInfo->UserName, profileDir, len))
956                 GetUserProfileDirectory(pInfo->hToken, profileDir, &len);
957         }
958     }
959     
960     if (strlen(profileDir)) {
961         DebugEvent("Profile Directory: %s", profileDir);
962         if (!IsPathInAfs(profileDir)) {
963             if (code = ktc_ForgetAllTokens())
964                 DebugEvent("AFS_Logoff_Event - ForgetAllTokens failed [%lX]",code);
965             else
966                 DebugEvent0("AFS_Logoff_Event - ForgetAllTokens succeeded");
967         } else {
968             DebugEvent0("AFS_Logoff_Event - Tokens left in place; profile in AFS");
969         }
970     } else {
971         DebugEvent0("AFS_Logoff_Event - Unable to load profile");
972     }
973
974     if ( tokenUser )
975         LocalFree(tokenUser);
976 }   
977