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