winnt-build-cleanup-20030825
[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 <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <npapi.h>
15 #include <winsock2.h>
16 #include "afsd.h"
17 #include <afs/pioctl_nt.h>
18 #include <afs/kautils.h>
19 #include "cm_config.h"
20 #include "krb.h"
21
22 #include <io.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <fcntl.h>
26
27 DWORD LogonOption,TraceOption;
28
29 HANDLE hDLL;
30
31 WSADATA WSAjunk;
32
33 #define REG_CLIENT_PARMS_KEY            "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters"
34 #define REG_CLIENT_PROVIDER_KEY                 "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\NetworkProvider"
35 #define REG_CLIENT_RETRY_INTERVAL_PARM  "LoginRetryInterval"
36 #define REG_CLIENT_FAIL_SILENTLY_PARM   "FailLoginsSilently"
37 #define DEFAULT_RETRY_INTERVAL          30                        /* seconds*/
38 #define DEFAULT_FAIL_SILENTLY           FALSE
39 #define DEFAULT_SLEEP_INTERVAL          5                         /* seconds*/
40
41 #define ISLOGONINTEGRATED(v) ( ((v) & LOGON_OPTION_INTEGRATED)==LOGON_OPTION_INTEGRATED)
42 #define ISHIGHSECURITY(v) ( ((v) & LOGON_OPTION_HIGHSECURITY)==LOGON_OPTION_HIGHSECURITY)
43
44 #define TRACE_OPTION_EVENT 1
45 #define ISLOGONTRACE(v) ( ((v) & TRACE_OPTION_EVENT)==TRACE_OPTION_EVENT)
46
47 /* Structure def copied from DDK (NTDEF.H) */
48 typedef struct UNICODE_STRING {
49         USHORT Length;          /* number of bytes of Buffer actually used */
50         USHORT MaximumLength;   /* sizeof buffer in bytes */
51         WCHAR *Buffer;          /* 16 bit characters */
52 } UNICODE_STRING;
53
54 /* Structure def copied from NP API documentation */
55 typedef struct _MSV1_0_INTERACTIVE_LOGON {
56         DWORD           MessageType;    /* Actually this is an enum; ignored */
57         UNICODE_STRING  LogonDomainName;
58         UNICODE_STRING  UserName;
59         UNICODE_STRING  Password;
60 } MSV1_0_INTERACTIVE_LOGON;
61
62 /*
63  * GetLogonScript
64  *
65  * We get a logon script pathname from the HKEY_LOCAL_MACHINE registry.
66  * I don't know what good this does; I just copied it from DFS.
67  *
68  * Returns NULL on failure.
69  */
70
71
72 void DebugEvent0(char *a) 
73 {
74         HANDLE h; char *ptbuf[1];
75         if (!ISLOGONTRACE(TraceOption))
76                 return;
77         h = RegisterEventSource(NULL, a);
78         ptbuf[0] = a;
79         ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
80         DeregisterEventSource(h);
81 }
82
83 #define MAXBUF_ 131
84 void DebugEvent(char *a,char *b,...) 
85 {
86         HANDLE h; char *ptbuf[1],buf[MAXBUF_+1];
87         va_list marker;
88         if (!ISLOGONTRACE(TraceOption))
89                 return;
90         h = RegisterEventSource(NULL, a);
91         va_start(marker,b);
92         _vsnprintf(buf,MAXBUF_,b,marker);
93         ptbuf[0] = buf;
94         ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);\
95         DeregisterEventSource(h);
96         va_end(marker);
97 }
98
99 CHAR *GenRandomName(CHAR *pbuf)
100 {
101         int i;
102         srand( (unsigned)time( NULL ) );
103         for (i=0;i<MAXRANDOMNAMELEN-1;i++)
104                 pbuf[i]='a'+(rand() % 26);
105         pbuf[MAXRANDOMNAMELEN-1]=0;
106         return pbuf;
107 }
108
109 WCHAR *GetLogonScript(CHAR *pname)
110 {
111         WCHAR *script,*buf;
112         DWORD code;
113         DWORD LSPtype, LSPsize;
114         HKEY NPKey;
115         WCHAR randomName[MAXRANDOMNAMELEN];
116
117         /*
118          * Get Network Provider key.
119          * Assume this works or we wouldn't be here.
120          */
121         (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PROVIDER_KEY,
122                             0, KEY_QUERY_VALUE, &NPKey);
123
124         /*
125          * Get Logon Script pathname length
126          */
127
128         code = RegQueryValueExW(NPKey, L"LogonScript", NULL,
129                                 &LSPtype, NULL, &LSPsize);
130
131         if (code) {
132                 RegCloseKey (NPKey);
133                 return NULL;
134         }
135
136         if (LSPtype != REG_SZ) {        /* Maybe handle REG_EXPAND_SZ? */
137                 RegCloseKey (NPKey);
138                 return NULL;
139         }
140
141         buf=(WCHAR *)LocalAlloc(LMEM_FIXED, LSPsize);
142         script=(WCHAR *)LocalAlloc(LMEM_FIXED,LSPsize+(MAXRANDOMNAMELEN)*sizeof(WCHAR));
143         /*
144          * Explicitly call UNICODE version
145          * Assume it will succeed since it did before
146          */
147         (void) RegQueryValueExW(NPKey, L"LogonScript", NULL,
148                                 &LSPtype, (LPBYTE)buf, &LSPsize);
149         MultiByteToWideChar(CP_ACP,0,pname,strlen(pname)+1,randomName,(strlen(pname)+1)*sizeof(WCHAR));
150         swprintf(script,buf,randomName);
151         free(buf);
152
153 #ifdef DEBUG_VERBOSE
154                 {
155         HANDLE h; char *ptbuf[1],buf[132],tbuf[255];
156                 WideCharToMultiByte(CP_ACP,0,script,LSPsize,tbuf,255,NULL,NULL);
157         h = RegisterEventSource(NULL, "AFS AfsLogon - GetLogonScript");
158         sprintf(buf, "Script[%s,%d] Return Code[%x]",tbuf,LSPsize,code);
159         ptbuf[0] = buf;
160         ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
161         DeregisterEventSource(h);
162                 }
163 #endif
164
165         RegCloseKey (NPKey);
166         return script;
167 }
168
169 BOOLEAN AFSWillAutoStart(void)
170 {
171         SC_HANDLE scm;
172         SC_HANDLE svc;
173         BOOLEAN flag;
174         BOOLEAN result = FALSE;
175         LPQUERY_SERVICE_CONFIG pConfig = NULL;
176         DWORD BufSize;
177         LONG status;
178
179         /* Open services manager */
180         scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
181         if (!scm) return FALSE;
182
183         /* Open AFSD service */
184         svc = OpenService(scm, "TransarcAFSDaemon", SERVICE_QUERY_CONFIG);
185         if (!svc)
186                 goto close_scm;
187
188         /* Query AFSD service config, first just to get buffer size */
189         /* Expected to fail, so don't test return value */
190         (void) QueryServiceConfig(svc, NULL, 0, &BufSize);
191         status = GetLastError();
192         if (status != ERROR_INSUFFICIENT_BUFFER)
193                 goto close_svc;
194
195         /* Allocate buffer */
196         pConfig = (LPQUERY_SERVICE_CONFIG)GlobalAlloc(GMEM_FIXED, BufSize);
197         if (!pConfig)
198                 goto close_svc;
199
200         /* Query AFSD service config, this time for real */
201         flag = QueryServiceConfig(svc, pConfig, BufSize, &BufSize);
202         if (!flag)
203                 goto free_pConfig;
204
205         /* Is it autostart? */
206         if (pConfig->dwStartType < SERVICE_DEMAND_START)
207                 result = TRUE;
208
209 free_pConfig:
210         GlobalFree(pConfig);
211 close_svc:
212         CloseServiceHandle(svc);
213 close_scm:
214         CloseServiceHandle(scm);
215
216         return result;
217 }
218
219 DWORD MapAuthError(DWORD code)
220 {
221         switch (code) {
222         case KTC_NOCM:
223         case KTC_NOCMRPC:
224                 return WN_NO_NETWORK;
225 /*      case INTK_BADPW: return WN_BAD_PASSWORD;*/
226 /*      case KERB_ERR_PRINCIPAL_UNKNOWN: return WN_BAD_USER;*/
227         default: return WN_SUCCESS;
228         }
229 }
230
231 BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
232 {
233         hDLL = dll;
234         switch (reason) {
235                 case DLL_PROCESS_ATTACH:
236                         /* Initialize AFS libraries */
237                         rx_Init(0);
238                         ka_Init(0);
239                         break;
240
241                 /* Everything else succeeds but does nothing. */
242                 case DLL_PROCESS_DETACH:
243                 case DLL_THREAD_ATTACH:
244                 case DLL_THREAD_DETACH:
245                 default:
246                         break;
247         }
248
249         return TRUE;
250 }
251
252 DWORD APIENTRY NPGetCaps(DWORD index)
253 {
254         switch (index) {
255                 case WNNC_NET_TYPE:
256                         /* Don't have our own type; use somebody else's. */
257                         return WNNC_NET_SUN_PC_NFS;
258                 default:
259                         return 0;
260         }
261 }
262
263 static void GetLoginBehavior(int *pRetryInterval, BOOLEAN *pFailSilently)
264 {
265         long result;
266         HKEY hKey;
267         DWORD dummyLen;
268                 
269         result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY, 0, KEY_QUERY_VALUE, &hKey);
270         if (result != ERROR_SUCCESS) {
271                 *pRetryInterval = DEFAULT_RETRY_INTERVAL;
272                 *pFailSilently = DEFAULT_FAIL_SILENTLY;
273                 return;
274         }
275         
276         result = RegQueryValueEx(hKey, REG_CLIENT_RETRY_INTERVAL_PARM, 0, 0, (BYTE *)pRetryInterval, &dummyLen);
277         if (result != ERROR_SUCCESS)
278                 *pRetryInterval = DEFAULT_RETRY_INTERVAL;
279                 
280         result = RegQueryValueEx(hKey, REG_CLIENT_FAIL_SILENTLY_PARM, 0, 0, (BYTE *)pFailSilently, &dummyLen);
281         if (result != ERROR_SUCCESS)
282                 *pFailSilently = DEFAULT_FAIL_SILENTLY;
283
284         /* Make sure this is really a bool value in the strict sense*/
285         *pFailSilently = !!*pFailSilently;
286                 
287         RegCloseKey(hKey);
288 }
289
290 BOOL IsServiceRunning (void)
291 {
292       SERVICE_STATUS Status;
293       SC_HANDLE hManager;
294       memset (&Status, 0x00, sizeof(Status));
295       Status.dwCurrentState = SERVICE_STOPPED;
296
297       if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
298          {
299          SC_HANDLE hService;
300          if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
301             {
302             QueryServiceStatus (hService, &Status);
303             CloseServiceHandle (hService);
304             }
305
306          CloseServiceHandle (hManager);
307          }
308                  DebugEvent("AFS AfsLogon - Test Service Running","Return Code[%x] ?Running[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_RUNNING));
309                 return (Status.dwCurrentState == SERVICE_RUNNING);
310 }
311
312 DWORD APIENTRY NPLogonNotify(
313         PLUID lpLogonId,
314         LPCWSTR lpAuthentInfoType,
315         LPVOID lpAuthentInfo,
316         LPCWSTR lpPreviousAuthentInfoType,
317         LPVOID lpPreviousAuthentInfo,
318         LPWSTR lpStationName,
319         LPVOID StationHandle,
320         LPWSTR *lpLogonScript)
321 {
322         char uname[256];
323         char *ctemp;
324         char password[256];
325         char cell[256];
326         MSV1_0_INTERACTIVE_LOGON *IL;
327         DWORD code;
328         int pw_exp;
329         char *reason;
330         BOOLEAN interactive;
331         BOOLEAN flag;
332         DWORD LSPtype, LSPsize;
333         HKEY NPKey;
334         HWND hwndOwner = (HWND)StationHandle;
335     BOOLEAN failSilently;
336     int retryInterval;
337     int sleepInterval = DEFAULT_SLEEP_INTERVAL;        /* seconds        */
338     BOOLEAN afsWillAutoStart;
339         CHAR RandomName[MAXRANDOMNAMELEN];
340         *lpLogonScript=NULL;
341         
342         IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
343
344         /* Are we interactive? */
345         interactive = (wcscmp(lpStationName, L"WinSta0") == 0);
346
347         /* Convert from Unicode to ANSI */
348         wcstombs(uname, IL->UserName.Buffer, 256);
349         wcstombs(password, IL->Password.Buffer, 256);
350
351         /* Make sure AD-DOMANS sent from login that is sent to us is striped */
352         ctemp = strchr(uname, '@');
353         if (ctemp) *ctemp = 0;
354
355         (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
356                     0, KEY_QUERY_VALUE, &NPKey);
357         LSPsize=sizeof(TraceOption);
358         RegQueryValueEx(NPKey, "TraceOption", NULL,
359                                 &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
360          RegCloseKey (NPKey);
361         
362         /*
363          * Get Logon OPTIONS
364          */
365
366         (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PROVIDER_KEY,
367                     0, KEY_QUERY_VALUE, &NPKey);
368
369         LSPsize=sizeof(LogonOption);
370         code = RegQueryValueEx(NPKey, "LogonOptions", NULL,
371                                 &LSPtype, (LPBYTE)&LogonOption, &LSPsize);
372
373         RegCloseKey (NPKey);
374         if ((code!=0) || (LSPtype!=REG_DWORD))
375                 LogonOption=LOGON_OPTION_INTEGRATED;    /*default to integrated logon only*/
376         DebugEvent("AFS AfsLogon - NPLogonNotify","LogonOption[%x], Service AutoStart[%d]",LogonOption,AFSWillAutoStart());
377         /* Check for zero length password if integrated logon*/
378         if ( ISLOGONINTEGRATED(LogonOption) && (password[0] == 0) )  {
379                 code = GT_PW_NULL;
380                 reason = "zero length password is illegal";
381                 code=0;
382         }
383
384         /* Get cell name if doing integrated logon */
385         if (ISLOGONINTEGRATED(LogonOption))
386         {
387                 code = cm_GetRootCellName(cell);
388                 if (code < 0) { 
389                         code = KTC_NOCELL;
390                         reason = "unknown cell";
391                         code=0;
392                 }
393         }
394
395     /* Get user specified login behavior (or defaults) */
396     GetLoginBehavior(&retryInterval, &failSilently);
397         
398     afsWillAutoStart = AFSWillAutoStart();
399         
400     *lpLogonScript = GetLogonScript(GenRandomName(RandomName)); /*only do if high security option is on*/
401
402
403     /* loop until AFS is started. */
404     while (TRUE) {
405         code=0;
406                 
407         /* is service started yet?*/
408         if (ISLOGONINTEGRATED(LogonOption) && !ISHIGHSECURITY(LogonOption))     /* if Integrated Logon only */
409                 {                       
410                         DebugEvent("AFS AfsLogon - ka_UserAuthenticateGeneral2","Code[%x],uame[%s] Cell[%s]",code,uname,cell);
411                         code = ka_UserAuthenticateGeneral2(
412                                 KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
413                                 uname, "", cell, password,uname, 0, &pw_exp, 0,
414                                 &reason);
415                         DebugEvent("AFS AfsLogon - (INTEGERTED only)ka_UserAuthenticateGeneral2","Code[%x]",code);
416                 } else if (ISLOGONINTEGRATED(LogonOption) && ISHIGHSECURITY(LogonOption))       /* if Integrated Logon and High Security pass random generated name*/
417                 {
418                         code = ka_UserAuthenticateGeneral2(
419                                 KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
420                                 uname, "", cell, password,RandomName, 0, &pw_exp, 0,
421                                 &reason);
422                         DebugEvent("AFS AfsLogon - (Both)ka_UserAuthenticateGeneral2","Code[%x],RandomName[%s]",code,RandomName);
423                 } else {  /*JUST check to see if its running*/
424                     if (IsServiceRunning())
425                         break;
426                     code = KTC_NOCM;
427                     if (!afsWillAutoStart)
428                         break;
429                 }
430                         
431                 /* If we've failed because the client isn't running yet and the
432                  * client is set to autostart (and therefore it makes sense for
433                  * us to wait for it to start) then sleep a while and try again. 
434                  * If the error was something else, then give up. */
435                 if (code != KTC_NOCM && code != KTC_NOCMRPC || !afsWillAutoStart)
436                         break;
437                 
438                 /* If the retry interval has expired and we still aren't
439                  * logged in, then just give up if we are not in interactive
440                  * mode or the failSilently flag is set, otherwise let the
441                  * user know we failed and give them a chance to try again. */
442         if (retryInterval <= 0) {
443              reason = "AFS not running";
444              if (!interactive || failSilently)
445                  break;
446                         flag = MessageBox(hwndOwner,
447                                 "AFS is still starting.  Retry?",
448                                 "AFS Logon",
449                                 MB_ICONQUESTION | MB_RETRYCANCEL);
450                         if (flag == IDCANCEL)
451                                         break;
452                         
453                         /* Wait just a little while and try again */
454                  retryInterval = sleepInterval = DEFAULT_SLEEP_INTERVAL;
455         }
456                                         
457         if (retryInterval < sleepInterval)
458                         sleepInterval = retryInterval;
459                         
460                 Sleep(sleepInterval * 1000);
461
462         retryInterval -= sleepInterval;
463      }
464
465         if (code) {
466                 char msg[128];
467         sprintf(msg, "Integrated login failed: %s", reason);
468                 
469                 if (interactive && !failSilently)
470                         MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
471                 else {
472                         HANDLE h;
473                         char *ptbuf[1];
474                 
475                         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
476                         ptbuf[0] = msg;
477                         ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
478                                     1, 0, ptbuf, NULL);
479                         DeregisterEventSource(h);
480                 }
481             code = MapAuthError(code);
482                 SetLastError(code);
483                 if (ISHIGHSECURITY(LogonOption) && (code!=0))
484                 {
485                         if (*lpLogonScript)
486                                 LocalFree(*lpLogonScript);
487                         *lpLogonScript = NULL;
488                         if (!(afsWillAutoStart || ISLOGONINTEGRATED(LogonOption)))      // its not running, so if not autostart or integrated logon then just skip
489                                 return 0;
490
491                 }
492         }
493         DebugEvent("AFS AfsLogon - Exit","Return Code[%x]",code);
494         return code;
495 }
496
497 DWORD APIENTRY NPPasswordChangeNotify(
498         LPCWSTR lpAuthentInfoType,
499         LPVOID lpAuthentInfo,
500         LPCWSTR lpPreviousAuthentInfoType,
501         LPVOID lpPreviousAuthentInfo,
502         LPWSTR lpStationName,
503         LPVOID StationHandle,
504         DWORD dwChangeInfo)
505 {
506         DebugEvent0("AFS AfsLogon - NPPasswordChangeNotify");
507         return 0;
508 }
509