windows-misc-20050102
[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;
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 uppercased_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 uppercase? */
658     for ( ctemp = uname; *ctemp ; ctemp++) {
659         if ( islower(*ctemp) ) {
660             uppercased_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]",uname,opt.smbName,cell,code);
746             }
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                 DebugEvent("AFS AfsLogon - (INTEGRATED only)ka_UserAuthenticateGeneral2","Code[%x]",
752                             code);
753             }       
754             if ( code && code != KTC_NOCM && code != KTC_NOCMRPC && uppercased_name ) {
755                 for ( ctemp = uname; *ctemp ; ctemp++) {
756                     *ctemp = tolower(*ctemp);
757                 }
758                 uppercased_name = FALSE;
759                 goto sleeping;
760             }
761
762             /* is service started yet?*/
763             DebugEvent("AFS AfsLogon - ka_UserAuthenticateGeneral2","Code[%x] uname[%s] Cell[%s]",
764                         code,uname,cell);
765
766             /* If we've failed because the client isn't running yet and the
767             * client is set to autostart (and therefore it makes sense for
768             * us to wait for it to start) then sleep a while and try again. 
769             * If the error was something else, then give up. */
770             if (code != KTC_NOCM && code != KTC_NOCMRPC || !afsWillAutoStart)
771                 break;
772         }
773         else {  
774             /*JUST check to see if its running*/
775             if (IsServiceRunning())
776                 break;
777             if (afsWillAutoStart && !IsServiceStartPending()) {
778                 code = KTC_NOCMRPC;
779                 reason = "AFS Service start failed";
780                 break;
781             }
782         }
783
784         /* If the retry interval has expired and we still aren't
785          * logged in, then just give up if we are not in interactive
786          * mode or the failSilently flag is set, otherwise let the
787          * user know we failed and give them a chance to try again. */
788         if (retryInterval <= 0) {
789             reason = "AFS not running";
790             if (!interactive || opt.failSilently)
791                 break;
792             flag = MessageBox(hwndOwner,
793                                "AFS is still starting.  Retry?",
794                                "AFS Logon",
795                                MB_ICONQUESTION | MB_RETRYCANCEL);
796             if (flag == IDCANCEL)
797                 break;
798
799             /* Wait just a little while and try again */
800             retryInterval = opt.retryInterval;
801         }
802
803       sleeping:
804         Sleep(sleepInterval * 1000);
805         retryInterval -= sleepInterval;
806     }
807
808     DebugEvent("while loop exited");
809     /* remove any kerberos 5 tickets currently held by the SYSTEM account */
810     if ( KFW_is_available() )
811         KFW_AFS_destroy_tickets_for_cell(cell);
812
813     if (code) {
814         char msg[128];
815         HANDLE h;
816         char *ptbuf[1];
817
818         StringCbPrintf(msg, sizeof(msg), "Integrated login failed: %s", reason);
819
820         if (ISLOGONINTEGRATED(opt.LogonOption) && interactive && !opt.failSilently)
821             MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
822
823         h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
824         ptbuf[0] = msg;
825         ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
826                      1, 0, ptbuf, NULL);
827         DeregisterEventSource(h);
828             
829         code = MapAuthError(code);
830         SetLastError(code);
831
832         if (ISLOGONINTEGRATED(opt.LogonOption) && (code!=0))
833         {
834             if (*lpLogonScript)
835                 LocalFree(*lpLogonScript);
836             *lpLogonScript = NULL;
837             if (!afsWillAutoStart)      // its not running, so if not autostart or integrated logon then just skip
838                 code = 0;
839         }
840     }
841
842     if(opt.smbName) free(opt.smbName);
843
844     DebugEvent("AFS AfsLogon - Exit","Return Code[%x]",code);
845     return code;
846 }       
847
848 DWORD APIENTRY NPPasswordChangeNotify(
849         LPCWSTR lpAuthentInfoType,
850         LPVOID lpAuthentInfo,
851         LPCWSTR lpPreviousAuthentInfoType,
852         LPVOID lpPreviousAuthentInfo,
853         LPWSTR lpStationName,
854         LPVOID StationHandle,
855         DWORD dwChangeInfo)
856 {
857     /* Make sure the AFS Libraries are initialized */
858     AfsLogonInit();
859
860     DebugEvent0("AFS AfsLogon - NPPasswordChangeNotify");
861     return 0;
862 }
863
864 #include <userenv.h>
865 #include <Winwlx.h>
866 #include <afs/vice.h>
867 #include <afs/fs_utils.h>
868
869 BOOL IsPathInAfs(const CHAR *strPath)
870 {
871     char space[2048];
872     struct ViceIoctl blob;
873     int code;
874
875     blob.in_size = 0;
876     blob.out_size = 2048;
877     blob.out = space;
878
879     code = pioctl((LPTSTR)((LPCTSTR)strPath), VIOC_FILE_CELL_NAME, &blob, 1);
880     if (code)
881         return FALSE;
882     return TRUE;
883 }
884
885 #ifdef COMMENT
886 typedef struct _WLX_NOTIFICATION_INFO {  
887     ULONG Size;  
888     ULONG Flags;  
889     PWSTR UserName;  
890     PWSTR Domain;  
891     PWSTR WindowStation;  
892     HANDLE hToken;  
893     HDESK hDesktop;  
894     PFNMSGECALLBACK pStatusCallback;
895 } WLX_NOTIFICATION_INFO, *PWLX_NOTIFICATION_INFO;
896 #endif
897
898 VOID AFS_Startup_Event( PWLX_NOTIFICATION_INFO pInfo )
899 {
900     DWORD LSPtype, LSPsize;
901     HKEY NPKey;
902
903     /* Make sure the AFS Libraries are initialized */
904     AfsLogonInit();
905
906     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
907                         0, KEY_QUERY_VALUE, &NPKey);
908     LSPsize=sizeof(TraceOption);
909     RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
910                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
911
912     RegCloseKey (NPKey);
913     DebugEvent0("AFS_Startup_Event");
914 }
915
916 VOID AFS_Logoff_Event( PWLX_NOTIFICATION_INFO pInfo )
917 {
918     DWORD code;
919     TCHAR profileDir[1024] = TEXT("");
920     DWORD  len = 1024;
921     PTOKEN_USER  tokenUser = NULL;
922     DWORD  retLen;
923     HANDLE hToken;
924
925     /* Make sure the AFS Libraries are initialized */
926     AfsLogonInit();
927
928     DebugEvent0("AFS_Logoff_Event - Starting");
929
930     if (!GetTokenInformation(pInfo->hToken, TokenUser, NULL, 0, &retLen))
931     {
932         if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
933             tokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen);
934
935             if (!GetTokenInformation(pInfo->hToken, TokenUser, tokenUser, retLen, &retLen))
936             {
937                 DebugEvent("GetTokenInformation failed: GLE = %lX", GetLastError());
938             }
939         }
940     }
941
942     /* We can't use pInfo->Domain for the domain since in the cross realm case 
943      * this is source domain and not the destination domain.
944      */
945     if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, pInfo->Domain)) {
946         WCHAR Domain[64]=L"";
947         GetLocalShortDomain(Domain, sizeof(Domain));
948         if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, Domain)) {
949             if (NetUserGetProfilePath(pInfo->Domain, pInfo->UserName, profileDir, len))
950                 GetUserProfileDirectory(pInfo->hToken, profileDir, &len);
951         }
952     }
953     
954     if (strlen(profileDir)) {
955         DebugEvent("Profile Directory: %s", profileDir);
956         if (!IsPathInAfs(profileDir)) {
957             if (code = ktc_ForgetAllTokens())
958                 DebugEvent("AFS_Logoff_Event - ForgetAllTokens failed [%lX]",code);
959             else
960                 DebugEvent0("AFS_Logoff_Event - ForgetAllTokens succeeded");
961         } else {
962             DebugEvent0("AFS_Logoff_Event - Tokens left in place; profile in AFS");
963         }
964     } else {
965         DebugEvent0("AFS_Logoff_Event - Unable to load profile");
966     }
967
968     if ( tokenUser )
969         LocalFree(tokenUser);
970 }   
971