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