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