9d9881697ccdec5568688b96338b1250c517257a
[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
23 HANDLE hDLL;
24
25 WSADATA WSAjunk;
26
27 char NPName[] = "System\\CurrentControlSet\\Services\\TransarcAFSDaemon\\NetworkProvider";
28
29 #define REG_CLIENT_PARMS_KEY            "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters"
30 #define REG_CLIENT_RETRY_INTERVAL_PARM  "LoginRetryInterval"
31 #define REG_CLIENT_FAIL_SILENTLY_PARM   "FailLoginsSilently"
32 #define DEFAULT_RETRY_INTERVAL          30                        // seconds
33 #define DEFAULT_FAIL_SILENTLY           FALSE
34 #define DEFAULT_SLEEP_INTERVAL          5                         // seconds
35
36
37 /* Structure def copied from DDK (NTDEF.H) */
38 typedef struct UNICODE_STRING {
39         USHORT Length;          /* number of bytes of Buffer actually used */
40         USHORT MaximumLength;   /* sizeof buffer in bytes */
41         WCHAR *Buffer;          /* 16 bit characters */
42 } UNICODE_STRING;
43
44 /* Structure def copied from NP API documentation */
45 typedef struct _MSV1_0_INTERACTIVE_LOGON {
46         DWORD           MessageType;    /* Actually this is an enum; ignored */
47         UNICODE_STRING  LogonDomainName;
48         UNICODE_STRING  UserName;
49         UNICODE_STRING  Password;
50 } MSV1_0_INTERACTIVE_LOGON;
51
52 /*
53  * GetLogonScript
54  *
55  * We get a logon script pathname from the HKEY_LOCAL_MACHINE registry.
56  * I don't know what good this does; I just copied it from DFS.
57  *
58  * Returns NULL on failure.
59  */
60 WCHAR *GetLogonScript(void)
61 {
62         WCHAR *script;
63         DWORD code;
64         DWORD LSPtype, LSPsize;
65         HKEY NPKey;
66
67         /*
68          * Get Network Provider key.
69          * Assume this works or we wouldn't be here.
70          */
71         (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, NPName,
72                             0, KEY_QUERY_VALUE, &NPKey);
73
74         /*
75          * Get Logon Script pathname length
76          */
77         code = RegQueryValueEx(NPKey, "LogonScript", NULL,
78                                 &LSPtype, NULL, &LSPsize);
79
80         if (code) {
81                 RegCloseKey (NPKey);
82                 return NULL;
83         }
84
85         if (LSPtype != REG_SZ) {        /* Maybe handle REG_EXPAND_SZ? */
86                 RegCloseKey (NPKey);
87                 return NULL;
88         }
89
90         script = (WCHAR *)LocalAlloc(LMEM_FIXED, LSPsize);
91
92         /*
93          * Explicitly call UNICODE version
94          * Assume it will succeed since it did before
95          */
96         (void) RegQueryValueExW(NPKey, L"LogonScript", NULL,
97                                 &LSPtype, (LPBYTE)script, &LSPsize);
98
99         RegCloseKey (NPKey);
100         return script;
101 }
102
103 BOOLEAN AFSWillAutoStart(void)
104 {
105         SC_HANDLE scm;
106         SC_HANDLE svc;
107         BOOLEAN flag;
108         BOOLEAN result = FALSE;
109         LPQUERY_SERVICE_CONFIG pConfig = NULL;
110         DWORD BufSize;
111         LONG status;
112
113         /* Open services manager */
114         scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
115         if (!scm) return FALSE;
116
117         /* Open AFSD service */
118         svc = OpenService(scm, "TransarcAFSDaemon", SERVICE_QUERY_CONFIG);
119         if (!svc)
120                 goto close_scm;
121
122         /* Query AFSD service config, first just to get buffer size */
123         /* Expected to fail, so don't test return value */
124         (void) QueryServiceConfig(svc, NULL, 0, &BufSize);
125         status = GetLastError();
126         if (status != ERROR_INSUFFICIENT_BUFFER)
127                 goto close_svc;
128
129         /* Allocate buffer */
130         pConfig = (LPQUERY_SERVICE_CONFIG)GlobalAlloc(GMEM_FIXED, BufSize);
131         if (!pConfig)
132                 goto close_svc;
133
134         /* Query AFSD service config, this time for real */
135         flag = QueryServiceConfig(svc, pConfig, BufSize, &BufSize);
136         if (!flag)
137                 goto free_pConfig;
138
139         /* Is it autostart? */
140         if (pConfig->dwStartType < SERVICE_DEMAND_START)
141                 result = TRUE;
142
143 free_pConfig:
144         GlobalFree(pConfig);
145 close_svc:
146         CloseServiceHandle(svc);
147 close_scm:
148         CloseServiceHandle(scm);
149
150         return result;
151 }
152
153 DWORD MapAuthError(DWORD code)
154 {
155         switch (code) {
156                 case INTK_BADPW: return WN_BAD_PASSWORD;
157                 case KERB_ERR_PRINCIPAL_UNKNOWN: return WN_BAD_USER;
158                 default: return WN_NO_NETWORK;
159         }
160 }
161
162 BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
163 {
164         hDLL = dll;
165         switch (reason) {
166                 case DLL_PROCESS_ATTACH:
167                         /* Initialize AFS libraries */
168                         rx_Init(0);
169                         ka_Init(0);
170                         break;
171
172                 /* Everything else succeeds but does nothing. */
173                 case DLL_PROCESS_DETACH:
174                 case DLL_THREAD_ATTACH:
175                 case DLL_THREAD_DETACH:
176                 default:
177                         break;
178         }
179
180         return TRUE;
181 }
182
183 DWORD APIENTRY NPGetCaps(DWORD index)
184 {
185         switch (index) {
186                 case WNNC_NET_TYPE:
187                         /* Don't have our own type; use somebody else's. */
188                         return WNNC_NET_SUN_PC_NFS;
189                 default:
190                         return 0;
191         }
192 }
193
194 static void GetLoginBehavior(int *pRetryInterval, BOOLEAN *pFailSilently)
195 {
196         long result;
197         HKEY hKey;
198         DWORD dummyLen;
199                 
200         result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY, 0, KEY_QUERY_VALUE, &hKey);
201         if (result != ERROR_SUCCESS) {
202                 *pRetryInterval = DEFAULT_RETRY_INTERVAL;
203                 *pFailSilently = DEFAULT_FAIL_SILENTLY;
204                 return;
205         }
206         
207         result = RegQueryValueEx(hKey, REG_CLIENT_RETRY_INTERVAL_PARM, 0, 0, (BYTE *)pRetryInterval, &dummyLen);
208         if (result != ERROR_SUCCESS)
209                 *pRetryInterval = DEFAULT_RETRY_INTERVAL;
210                 
211         result = RegQueryValueEx(hKey, REG_CLIENT_FAIL_SILENTLY_PARM, 0, 0, (BYTE *)pFailSilently, &dummyLen);
212         if (result != ERROR_SUCCESS)
213                 *pFailSilently = DEFAULT_FAIL_SILENTLY;
214
215         // Make sure this is really a bool value in the strict sense
216         *pFailSilently = !!*pFailSilently;
217                 
218         RegCloseKey(hKey);
219 }
220
221 DWORD APIENTRY NPLogonNotify(
222         PLUID lpLogonId,
223         LPCWSTR lpAuthentInfoType,
224         LPVOID lpAuthentInfo,
225         LPCWSTR lpPreviousAuthentInfoType,
226         LPVOID lpPreviousAuthentInfo,
227         LPWSTR lpStationName,
228         LPVOID StationHandle,
229         LPWSTR *lpLogonScript)
230 {
231         char uname[256];
232         char password[256];
233         char cell[256];
234         MSV1_0_INTERACTIVE_LOGON *IL;
235         DWORD code;
236         int pw_exp;
237         char *reason;
238         BOOLEAN interactive;
239         BOOLEAN flag;
240         HWND hwndOwner = (HWND)StationHandle;
241         BOOLEAN failSilently;
242         int retryInterval;
243         int sleepInterval = DEFAULT_SLEEP_INTERVAL;        // seconds        
244         BOOLEAN afsWillAutoStart;
245         
246         IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
247
248         /* Are we interactive? */
249         interactive = (wcscmp(lpStationName, L"WinSta0") == 0);
250
251         /* Convert from Unicode to ANSI */
252         wcstombs(uname, IL->UserName.Buffer, 256);
253         wcstombs(password, IL->Password.Buffer, 256);
254
255         /* Check for zero length password */
256         if (password[0] == 0) {
257                 code = GT_PW_NULL;
258                 reason = "zero length password is illegal";
259                 goto checkauth;
260         }
261
262         /* Get cell name */
263         code = cm_GetRootCellName(cell);
264         if (code < 0) {
265                 code = KTC_NOCELL;
266                 reason = "unknown cell";
267                 goto checkauth;
268         }
269
270         /* Get user specified login behavior (or defaults) */
271         GetLoginBehavior(&retryInterval, &failSilently);
272         
273         afsWillAutoStart = AFSWillAutoStart();
274         
275         /* Possibly loop until AFS is started. */
276         while (1) {
277                 code = ka_UserAuthenticateGeneral(
278                         KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
279                         uname, "", cell, password, 0, &pw_exp, 0,
280                         &reason);
281                         
282                 /* If we've failed because the client isn't running yet and the
283                  * client is set to autostart (and therefore it makes sense for
284                  * us to wait for it to start) then sleep a while and try again. 
285                  * If the error was something else, then give up. */
286                 if (code != KTC_NOCM && code != KTC_NOCMRPC || !afsWillAutoStart)
287                         break;
288                 
289                 /* If the retry interval has expired and we still aren't
290                  * logged in, then just give up if we are not in interactive
291                  * mode or the failSilently flag is set, otherwise let the
292                  * user know we failed and give them a chance to try again. */
293                 if (retryInterval <= 0) {
294                         if (!interactive || failSilently)
295                                 break;
296
297                         flag = MessageBox(hwndOwner,
298                                 "AFS is still starting.  Retry?",
299                                 "AFS Logon",
300                                 MB_ICONQUESTION | MB_RETRYCANCEL);
301                         if (flag == IDCANCEL)
302                                 break;
303                         
304                         /* Wait just a little while and try again */
305                         retryInterval = sleepInterval = DEFAULT_SLEEP_INTERVAL;
306                 }
307                                         
308                 if (retryInterval < sleepInterval)
309                         sleepInterval = retryInterval;
310                         
311                 Sleep(sleepInterval * 1000);
312
313                 retryInterval -= sleepInterval;
314         }
315
316 checkauth:
317         if (code) {
318                 char msg[128];
319                 
320                 sprintf(msg, "Integrated login failed: %s", reason);
321                 
322                 if (interactive && !failSilently)
323                         MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
324                 else {
325                         HANDLE h;
326                         char *ptbuf[1];
327                 
328                         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
329                         ptbuf[0] = msg;
330                         ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
331                                     1, 0, ptbuf, NULL);
332                         DeregisterEventSource(h);
333                 }
334         }
335
336         /* Get logon script */
337         if (interactive)
338                 *lpLogonScript = GetLogonScript();
339
340         if (code) {
341                 code = MapAuthError(code);
342                 SetLastError(code);
343         }
344
345         return code;
346 }
347
348 DWORD APIENTRY NPPasswordChangeNotify(
349         LPCWSTR lpAuthentInfoType,
350         LPVOID lpAuthentInfo,
351         LPCWSTR lpPreviousAuthentInfoType,
352         LPVOID lpPreviousAuthentInfo,
353         LPWSTR lpStationName,
354         LPVOID StationHandle,
355         DWORD dwChangeInfo)
356 {
357         return 0;
358 }
359