patch-from-shadow-to-jaltman-bkbox-20031120
[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         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_DAEMON_EVENT_NAME);
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             initAFSDirPath();
239                         ka_Init(0);
240                         break;
241
242                 /* Everything else succeeds but does nothing. */
243                 case DLL_PROCESS_DETACH:
244                 case DLL_THREAD_ATTACH:
245                 case DLL_THREAD_DETACH:
246                 default:
247                         break;
248         }
249
250         return TRUE;
251 }
252
253 DWORD APIENTRY NPGetCaps(DWORD index)
254 {
255         switch (index) {
256                 case WNNC_NET_TYPE:
257                         /* Don't have our own type; use somebody else's. */
258                         return WNNC_NET_SUN_PC_NFS;
259                 default:
260                         return 0;
261         }
262 }
263
264 static void GetLoginBehavior(int *pRetryInterval, BOOLEAN *pFailSilently)
265 {
266         long result;
267         HKEY hKey;
268         DWORD dummyLen;
269                 
270         result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY, 0, KEY_QUERY_VALUE, &hKey);
271         if (result != ERROR_SUCCESS) {
272                 *pRetryInterval = DEFAULT_RETRY_INTERVAL;
273                 *pFailSilently = DEFAULT_FAIL_SILENTLY;
274                 return;
275         }
276         
277         result = RegQueryValueEx(hKey, REG_CLIENT_RETRY_INTERVAL_PARM, 0, 0, (BYTE *)pRetryInterval, &dummyLen);
278         if (result != ERROR_SUCCESS)
279                 *pRetryInterval = DEFAULT_RETRY_INTERVAL;
280                 
281         result = RegQueryValueEx(hKey, REG_CLIENT_FAIL_SILENTLY_PARM, 0, 0, (BYTE *)pFailSilently, &dummyLen);
282         if (result != ERROR_SUCCESS)
283                 *pFailSilently = DEFAULT_FAIL_SILENTLY;
284
285         /* Make sure this is really a bool value in the strict sense*/
286         *pFailSilently = !!*pFailSilently;
287                 
288         RegCloseKey(hKey);
289 }
290
291 BOOL IsServiceRunning (void)
292 {
293       SERVICE_STATUS Status;
294       SC_HANDLE hManager;
295       memset (&Status, 0x00, sizeof(Status));
296       Status.dwCurrentState = SERVICE_STOPPED;
297
298       if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
299          {
300          SC_HANDLE hService;
301          if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
302             {
303             QueryServiceStatus (hService, &Status);
304             CloseServiceHandle (hService);
305             }
306
307          CloseServiceHandle (hManager);
308          }
309                  DebugEvent("AFS AfsLogon - Test Service Running","Return Code[%x] ?Running[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_RUNNING));
310                 return (Status.dwCurrentState == SERVICE_RUNNING);
311 }
312
313 DWORD APIENTRY NPLogonNotify(
314         PLUID lpLogonId,
315         LPCWSTR lpAuthentInfoType,
316         LPVOID lpAuthentInfo,
317         LPCWSTR lpPreviousAuthentInfoType,
318         LPVOID lpPreviousAuthentInfo,
319         LPWSTR lpStationName,
320         LPVOID StationHandle,
321         LPWSTR *lpLogonScript)
322 {
323         char uname[256];
324         char *ctemp;
325         char password[256];
326         char cell[256];
327         MSV1_0_INTERACTIVE_LOGON *IL;
328         DWORD code;
329         int pw_exp;
330         char *reason;
331         BOOLEAN interactive;
332         BOOLEAN flag;
333         DWORD LSPtype, LSPsize;
334         HKEY NPKey;
335         HWND hwndOwner = (HWND)StationHandle;
336     BOOLEAN failSilently;
337     int retryInterval;
338     int sleepInterval = DEFAULT_SLEEP_INTERVAL;        /* seconds        */
339     BOOLEAN afsWillAutoStart;
340         CHAR RandomName[MAXRANDOMNAMELEN];
341         *lpLogonScript=NULL;
342         
343         IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
344
345         /* Are we interactive? */
346         interactive = (wcscmp(lpStationName, L"WinSta0") == 0);
347
348         /* Convert from Unicode to ANSI */
349         wcstombs(uname, IL->UserName.Buffer, 256);
350         wcstombs(password, IL->Password.Buffer, 256);
351
352         /* Make sure AD-DOMANS sent from login that is sent to us is striped */
353         ctemp = strchr(uname, '@');
354         if (ctemp) *ctemp = 0;
355
356         (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
357                     0, KEY_QUERY_VALUE, &NPKey);
358         LSPsize=sizeof(TraceOption);
359         RegQueryValueEx(NPKey, "TraceOption", NULL,
360                                 &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
361          RegCloseKey (NPKey);
362         
363         /*
364          * Get Logon OPTIONS
365          */
366
367         (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PROVIDER_KEY,
368                     0, KEY_QUERY_VALUE, &NPKey);
369
370         LSPsize=sizeof(LogonOption);
371         code = RegQueryValueEx(NPKey, "LogonOptions", NULL,
372                                 &LSPtype, (LPBYTE)&LogonOption, &LSPsize);
373
374         RegCloseKey (NPKey);
375         if ((code!=0) || (LSPtype!=REG_DWORD))
376                 LogonOption=LOGON_OPTION_INTEGRATED;    /*default to integrated logon only*/
377         DebugEvent("AFS AfsLogon - NPLogonNotify","LogonOption[%x], Service AutoStart[%d]",LogonOption,AFSWillAutoStart());
378         /* Check for zero length password if integrated logon*/
379         if ( ISLOGONINTEGRATED(LogonOption) && (password[0] == 0) )  {
380                 code = GT_PW_NULL;
381                 reason = "zero length password is illegal";
382                 code=0;
383         }
384
385         /* Get cell name if doing integrated logon */
386         if (ISLOGONINTEGRATED(LogonOption))
387         {
388                 code = cm_GetRootCellName(cell);
389                 if (code < 0) { 
390                         code = KTC_NOCELL;
391                         reason = "unknown cell";
392                         code=0;
393                 }
394         }
395
396     /* Get user specified login behavior (or defaults) */
397     GetLoginBehavior(&retryInterval, &failSilently);
398         
399     afsWillAutoStart = AFSWillAutoStart();
400         
401     *lpLogonScript = GetLogonScript(GenRandomName(RandomName)); /*only do if high security option is on*/
402
403
404     /* loop until AFS is started. */
405     while (TRUE) {
406         code=0;
407                 
408         /* is service started yet?*/
409         if (ISLOGONINTEGRATED(LogonOption) && !ISHIGHSECURITY(LogonOption))     /* if Integrated Logon only */
410                 {                       
411                         DebugEvent("AFS AfsLogon - ka_UserAuthenticateGeneral2","Code[%x],uame[%s] Cell[%s]",code,uname,cell);
412                         code = ka_UserAuthenticateGeneral2(
413                                 KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
414                                 uname, "", cell, password,uname, 0, &pw_exp, 0,
415                                 &reason);
416                         DebugEvent("AFS AfsLogon - (INTEGERTED only)ka_UserAuthenticateGeneral2","Code[%x]",code);
417                 } else if (ISLOGONINTEGRATED(LogonOption) && ISHIGHSECURITY(LogonOption))       /* if Integrated Logon and High Security pass random generated name*/
418                 {
419                         code = ka_UserAuthenticateGeneral2(
420                                 KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
421                                 uname, "", cell, password,RandomName, 0, &pw_exp, 0,
422                                 &reason);
423                         DebugEvent("AFS AfsLogon - (Both)ka_UserAuthenticateGeneral2","Code[%x],RandomName[%s]",code,RandomName);
424                 } else {  /*JUST check to see if its running*/
425                     if (IsServiceRunning())
426                         break;
427                     code = KTC_NOCM;
428                     if (!afsWillAutoStart)
429                         break;
430                 }
431                         
432                 /* If we've failed because the client isn't running yet and the
433                  * client is set to autostart (and therefore it makes sense for
434                  * us to wait for it to start) then sleep a while and try again. 
435                  * If the error was something else, then give up. */
436                 if (code != KTC_NOCM && code != KTC_NOCMRPC || !afsWillAutoStart)
437                         break;
438                 
439                 /* If the retry interval has expired and we still aren't
440                  * logged in, then just give up if we are not in interactive
441                  * mode or the failSilently flag is set, otherwise let the
442                  * user know we failed and give them a chance to try again. */
443         if (retryInterval <= 0) {
444              reason = "AFS not running";
445              if (!interactive || failSilently)
446                  break;
447                         flag = MessageBox(hwndOwner,
448                                 "AFS is still starting.  Retry?",
449                                 "AFS Logon",
450                                 MB_ICONQUESTION | MB_RETRYCANCEL);
451                         if (flag == IDCANCEL)
452                                         break;
453                         
454                         /* Wait just a little while and try again */
455                  retryInterval = sleepInterval = DEFAULT_SLEEP_INTERVAL;
456         }
457                                         
458         if (retryInterval < sleepInterval)
459                         sleepInterval = retryInterval;
460                         
461                 Sleep(sleepInterval * 1000);
462
463         retryInterval -= sleepInterval;
464      }
465
466         if (code) {
467                 char msg[128];
468         sprintf(msg, "Integrated login failed: %s", reason);
469                 
470                 if (interactive && !failSilently)
471                         MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
472                 else {
473                         HANDLE h;
474                         char *ptbuf[1];
475                 
476                         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
477                         ptbuf[0] = msg;
478                         ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
479                                     1, 0, ptbuf, NULL);
480                         DeregisterEventSource(h);
481                 }
482             code = MapAuthError(code);
483                 SetLastError(code);
484                 if (ISHIGHSECURITY(LogonOption) && (code!=0))
485                 {
486                         if (*lpLogonScript)
487                                 LocalFree(*lpLogonScript);
488                         *lpLogonScript = NULL;
489                         if (!(afsWillAutoStart || ISLOGONINTEGRATED(LogonOption)))      // its not running, so if not autostart or integrated logon then just skip
490                                 return 0;
491
492                 }
493         }
494         DebugEvent("AFS AfsLogon - Exit","Return Code[%x]",code);
495         return code;
496 }
497
498 DWORD APIENTRY NPPasswordChangeNotify(
499         LPCWSTR lpAuthentInfoType,
500         LPVOID lpAuthentInfo,
501         LPCWSTR lpPreviousAuthentInfoType,
502         LPVOID lpPreviousAuthentInfo,
503         LPWSTR lpStationName,
504         LPVOID StationHandle,
505         DWORD dwChangeInfo)
506 {
507         DebugEvent0("AFS AfsLogon - NPPasswordChangeNotify");
508         return 0;
509 }
510