fsprobe: add fsprobe_Wait function
[openafs.git] / src / WINNT / client_creds / ipaddrchg.c
1 /*
2  * Copyright (c) 2003 SkyRope, LLC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * - Redistributions of source code must retain the above copyright notice,
9  *   this list of conditions and the following disclaimer.
10  * - Redistributions in binary form must reproduce the above copyright notice,
11  *   this list of conditions and the following disclaimer in the documentation
12  *   and/or other materials provided with the distribution.
13  * - Neither the name of Skyrope, LLC nor the names of its contributors may be
14  *   used to endorse or promote products derived from this software without
15  *   specific prior written permission from Skyrope, LLC.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * Portions of this code are derived from portions of the MIT
30  * Leash Ticket Manager and LoadFuncs utilities.  For these portions the
31  * following copyright applies.
32  *
33  * Copyright (c) 2003,2004 by the Massachusetts Institute of Technology.
34  * All rights reserved.
35  *
36  * Export of this software from the United States of America may
37  *   require a specific license from the United States Government.
38  *   It is the responsibility of any person or organization contemplating
39  *   export to obtain such a license before exporting.
40  *
41  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
42  * distribute this software and its documentation for any purpose and
43  * without fee is hereby granted, provided that the above copyright
44  * notice appear in all copies and that both that copyright notice and
45  * this permission notice appear in supporting documentation, and that
46  * the name of M.I.T. not be used in advertising or publicity pertaining
47  * to distribution of the software without specific, written prior
48  * permission.  Furthermore if you modify this software you must label
49  * your software as modified software and not distribute it in such a
50  * fashion that it might be confused with the original M.I.T. software.
51  * M.I.T. makes no representations about the suitability of
52  * this software for any purpose.  It is provided "as is" without express
53  * or implied warranty.
54  *
55  */
56
57 // IP Change Monitoring Functions
58
59 #include <windows.h>
60 #include <stdio.h>
61 #include <string.h>
62 #include <time.h>
63 #include <winsock2.h>
64
65
66 #include <afsconfig.h>
67 #include <afs/param.h>
68 #include <roken.h>
69
70 #define USE_MS2MIT 1
71
72 #include <afs/stds.h>
73 #include <krb5.h>
74 #include <rx\rxkad.h>
75 #include <afskfw.h>
76 #include "ipaddrchg.h"
77 #include "creds.h"
78 #include <iphlpapi.h>
79 #include <afs/auth.h>
80
81 #define MAXCELLCHARS   64
82
83 #ifdef USE_FSPROBE
84 // Cell Accessibility Functions
85 // based on work originally submitted to the CMU Computer Club
86 // by Jeffrey Hutzelman
87 //
88 // These would work great if the fsProbe interface had been
89 // ported to Windows
90
91 static
92 void probeComplete()
93 {
94     fsprobe_Cleanup(1);
95     rx_Finalize();
96 }
97
98 struct ping_params {
99     unsigned short port;            // in
100     int            retry_delay;     // in seconds
101     int            verbose;         // in
102     struct {
103         int        wait;            // in seconds
104         int        retry;           // in attempts
105     }   host;
106     int            max_hosts;       // in
107     int            hosts_attempted; // out
108 }
109
110 // the fsHandler is where we receive the answer to the probe
111 static
112 int fsHandler(void)
113 {
114     ping_count = fsprobe_Results.probeNum;
115     if (!*fsprobe_Results.probeOK)
116     {
117         ok_count++;
118         if (waiting) complete();
119     }
120     if (ping_count == retry)
121         complete();
122     return 0;
123 }
124
125 // ping_fs is a callback routine meant to be called from within
126 // cm_SearchCellFile() or cm_SearchCellDNS()
127 static long
128 pingFS(void *ping_params, struct sockaddr_in *addrp, char *namep)
129 {
130     int rc;
131     struct ping_params * pp = (struct ping_params *) ping_params;
132
133     if ( pp->max_hosts && pp->hosts_attempted >= pp->max_hosts )
134         return 0;
135
136     pp->hosts_attempted++;
137
138     if (pp->port && addrp->sin_port != htons(pp->port))
139         addrp->sin_port = htons(pp->port);
140
141     rc = fsprobe_Init(1, addrp, pp->retry_delay, fsHandler, pp->verbose);
142     if (rc)
143     {
144         fprintf(stderr, "fsprobe_Init failed (%d)\n", rc);
145         fsprobe_Cleanup(1);
146         return 0;
147     }
148
149     fsprobe_Wait(0);
150     probeComplete();
151     return(0);
152 }
153
154
155 static BOOL
156 pingCell(char *cell)
157 {
158     int rc;
159     char rootcell[MAXCELLCHARS+1];
160     char newcell[MAXCELLCHARS+1];
161     struct ping_params pp;
162
163     memset(&pp, 0, sizeof(struct ping_params));
164
165     if (!cell || strlen(cell) == 0) {
166         /* WIN32 NOTE: no way to get max chars */
167         if (rc = pcm_GetRootCellName(rootcell))
168             return(FALSE);
169         cell = rootcell;
170     }
171
172     pp.port = 7000; // AFS FileServer
173     pp.retry_delay = 10;
174     pp.max_hosts = 3;
175     pp.host.wait = 30;
176     pp.host.retry = 0;
177     pp.verbose = 1;
178
179     /* WIN32: cm_SearchCellFile(cell, newcell, linkedCell, pcallback, pdata) */
180     rc = pcm_SearchCellFile(cell, newcell, pingFS, (void *)&pp);
181 }
182 #endif /* USE_FSPROBE */
183
184 // These two items are imported from afscreds.h
185 // but it cannot be included without causing conflicts
186 #define c100ns1SECOND        (LONGLONG)10000000
187 static void
188 TimeToSystemTime (SYSTEMTIME *pst, time_t TimeT)
189 {
190     struct tm *pTime;
191     memset (pst, 0x00, sizeof(SYSTEMTIME));
192
193     if ((pTime = localtime (&TimeT)) != NULL)
194     {
195         pst->wYear = pTime->tm_year + 1900;
196         pst->wMonth = pTime->tm_mon + 1;
197         pst->wDayOfWeek = pTime->tm_wday;
198         pst->wDay = pTime->tm_mday;
199         pst->wHour = pTime->tm_hour;
200         pst->wMinute = pTime->tm_min;
201         pst->wSecond = pTime->tm_sec;
202         pst->wMilliseconds = 0;
203     }
204 }
205
206 static DWORD
207 GetServiceStatus(
208     LPSTR lpszMachineName,
209     LPSTR lpszServiceName,
210     DWORD *lpdwCurrentState)
211 {
212     DWORD           hr               = NOERROR;
213     SC_HANDLE       schSCManager     = NULL;
214     SC_HANDLE       schService       = NULL;
215     DWORD           fdwDesiredAccess = 0;
216     SERVICE_STATUS  ssServiceStatus  = {0};
217     BOOL            fRet             = FALSE;
218
219     *lpdwCurrentState = 0;
220
221     fdwDesiredAccess = GENERIC_READ;
222
223     schSCManager = OpenSCManager(lpszMachineName,
224                                  NULL,
225                                  fdwDesiredAccess);
226
227     if(schSCManager == NULL)
228     {
229         hr = GetLastError();
230         goto cleanup;
231     }
232
233     schService = OpenService(schSCManager,
234                              lpszServiceName,
235                              fdwDesiredAccess);
236
237     if(schService == NULL)
238     {
239         hr = GetLastError();
240         goto cleanup;
241     }
242
243     fRet = QueryServiceStatus(schService,
244                               &ssServiceStatus);
245
246     if(fRet == FALSE)
247     {
248         hr = GetLastError();
249         goto cleanup;
250     }
251
252     *lpdwCurrentState = ssServiceStatus.dwCurrentState;
253
254 cleanup:
255
256     CloseServiceHandle(schService);
257     CloseServiceHandle(schSCManager);
258
259     return(hr);
260 }
261
262 void
263 ObtainTokensFromUserIfNeeded(HWND hWnd)
264 {
265     char * rootcell = NULL;
266     char   cell[MAXCELLCHARS+1] = "";
267     char   password[PROBE_PASSWORD_LEN+1];
268     struct afsconf_cell cellconfig;
269     struct ktc_principal    aserver;
270     struct ktc_principal    aclient;
271     struct ktc_token    atoken;
272     krb5_timestamp now = 0;
273     BOOL serverReachable = 0;
274     int rc;
275     DWORD       CurrentState, code;
276     char        HostName[64];
277     int         use_kfw = KFW_is_available();
278
279     SYSTEMTIME stNow;
280     FILETIME ftNow;
281     LONGLONG llNow;
282     FILETIME ftExpires;
283     LONGLONG llExpires;
284     SYSTEMTIME stExpires;
285
286     CurrentState = 0;
287     memset(HostName, '\0', sizeof(HostName));
288     gethostname(HostName, sizeof(HostName));
289     if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
290         return;
291     if (CurrentState != SERVICE_RUNNING) {
292         SendMessage(hWnd, WM_START_SERVICE, FALSE, 0L);
293         return;
294     }
295
296     rootcell = (char *)GlobalAlloc(GPTR,MAXCELLCHARS+1);
297     if (!rootcell)
298         goto cleanup;
299
300     code = KFW_AFS_get_cellconfig(cell, (void*)&cellconfig, rootcell);
301     if (code)
302         goto cleanup;
303
304     memset(&aserver, '\0', sizeof(aserver));
305     strcpy(aserver.name, "afs");
306     strcpy(aserver.cell, rootcell);
307
308     GetLocalTime (&stNow);
309     SystemTimeToFileTime (&stNow, &ftNow);
310     llNow = (((LONGLONG)ftNow.dwHighDateTime) << 32) + (LONGLONG)(ftNow.dwLowDateTime);
311     llNow /= c100ns1SECOND;
312
313     rc = ktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
314     if ( rc == 0 ) {
315         TimeToSystemTime (&stExpires, atoken.endTime);
316         SystemTimeToFileTime (&stExpires, &ftExpires);
317         llExpires = (((LONGLONG)ftExpires.dwHighDateTime) << 32) + (LONGLONG)(ftExpires.dwLowDateTime);
318         llExpires /= c100ns1SECOND;
319
320         if (llNow < llExpires)
321             goto cleanup;
322
323         if ( IsDebuggerPresent() ) {
324             char message[256];
325             sprintf(message,"ObtainTokensFromUserIfNeeded: %d  now = %ul  endTime = %ul\n",
326                      rc, llNow, llExpires);
327             OutputDebugString(message);
328         }
329     }
330
331 #ifdef USE_FSPROBE
332     serverReachable = cellPing(NULL);
333 #else
334     if (use_kfw) {
335         // If we can't use the FSProbe interface we can attempt to forge
336         // a kinit and if we can back an invalid user error we know the
337         // kdc is at least reachable
338         serverReachable = KFW_probe_kdc(&cellconfig);
339     } else {
340         int i;
341
342         for ( i=0 ; i<PROBE_PASSWORD_LEN ; i++ )
343             password[i] = 'x';
344
345         code = ObtainNewCredentials(rootcell, PROBE_USERNAME, password, TRUE);
346         switch ( code ) {
347         case INTK_BADPW:
348         case KERB_ERR_PRINCIPAL_UNKNOWN:
349         case KERB_ERR_SERVICE_EXP:
350         case RD_AP_TIME:
351             serverReachable = TRUE;
352             break;
353         default:
354             serverReachable = FALSE;
355         }
356     }
357 #endif
358     if ( !serverReachable ) {
359         if ( IsDebuggerPresent() )
360             OutputDebugString("Server Unreachable\n");
361         goto cleanup;
362     }
363
364     if ( IsDebuggerPresent() )
365         OutputDebugString("Server Reachable\n");
366
367     if ( use_kfw ) {
368 #ifdef USE_MS2MIT
369         KFW_import_windows_lsa();
370 #endif /* USE_MS2MIT */
371         KFW_AFS_renew_expiring_tokens();
372         KFW_AFS_renew_token_for_cell(rootcell);
373
374         rc = ktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
375         if ( rc == 0 ) {
376             TimeToSystemTime (&stExpires, atoken.endTime);
377             SystemTimeToFileTime (&stExpires, &ftExpires);
378             llExpires = (((LONGLONG)ftExpires.dwHighDateTime) << 32) + (LONGLONG)(ftExpires.dwLowDateTime);
379             llExpires /= c100ns1SECOND;
380
381             if (llNow < llExpires)
382                 goto cleanup;
383         }
384     }
385
386     SendMessage(hWnd, WM_OBTAIN_TOKENS, FALSE, (long)rootcell);
387     rootcell = NULL;    // rootcell freed by message receiver
388
389   cleanup:
390     if (rootcell)
391         GlobalFree(rootcell);
392
393     return;
394 }
395
396 DWORD
397 GetNumOfIpAddrs(void)
398 {
399     PMIB_IPADDRTABLE pIpAddrTable = NULL;
400     ULONG            dwSize;
401     DWORD            code;
402     DWORD            index;
403     DWORD            validAddrs = 0;
404
405     dwSize = 0;
406     code = GetIpAddrTable(NULL, &dwSize, 0);
407     if (code == ERROR_INSUFFICIENT_BUFFER) {
408         pIpAddrTable = malloc(dwSize);
409         code = GetIpAddrTable(pIpAddrTable, &dwSize, 0);
410         if ( code == NO_ERROR ) {
411             for ( index=0; index < pIpAddrTable->dwNumEntries; index++ ) {
412                 if (pIpAddrTable->table[index].dwAddr != 0)
413                     validAddrs++;
414             }
415         }
416         free(pIpAddrTable);
417     }
418     return validAddrs;
419 }
420
421 void
422 IpAddrChangeMonitor(void * hWnd)
423 {
424 #ifdef USE_OVERLAPPED
425     HANDLE Handle = INVALID_HANDLE_VALUE;   /* Do Not Close This Handle */
426     OVERLAPPED Ovlap;
427 #endif /* USE_OVERLAPPED */
428     DWORD Result;
429     DWORD prevNumOfAddrs = GetNumOfIpAddrs();
430     DWORD NumOfAddrs;
431     char message[256];
432
433     if ( !hWnd )
434         return;
435
436     while ( TRUE ) {
437 #ifdef USE_OVERLAPPED
438         ZeroMemory(&Ovlap, sizeof(OVERLAPPED));
439
440         Result = NotifyAddrChange(&Handle,&Ovlap);
441         if (Result != ERROR_IO_PENDING)
442         {
443             if ( IsDebuggerPresent() ) {
444                 sprintf(message, "NotifyAddrChange() failed with error %d \n", Result);
445                 OutputDebugString(message);
446             }
447             break;
448         }
449
450         if ((Result = WaitForSingleObject(Handle,INFINITE)) != WAIT_OBJECT_0)
451         {
452             if ( IsDebuggerPresent() ) {
453                 sprintf(message, "WaitForSingleObject() failed with error %d\n",
454                         GetLastError());
455                 OutputDebugString(message);
456             }
457             continue;
458         }
459
460         if (GetOverlappedResult(Handle, &Ovlap,
461                                  &DataTransfered, TRUE) == 0)
462         {
463             if ( IsDebuggerPresent() ) {
464                 sprintf(message, "GetOverlapped result failed %d \n",
465                         GetLastError());
466                 OutputDebugString(message);
467             }
468             break;
469         }
470 #else
471         Result = NotifyAddrChange(NULL,NULL);
472         if (Result != NO_ERROR)
473         {
474             if ( IsDebuggerPresent() ) {
475                 sprintf(message, "NotifyAddrChange() failed with error %d \n", Result);
476                 OutputDebugString(message);
477             }
478             break;
479         }
480 #endif
481
482         NumOfAddrs = GetNumOfIpAddrs();
483
484         if ( IsDebuggerPresent() ) {
485             sprintf(message,"IPAddrChangeMonitor() NumOfAddrs: now %d was %d\n",
486                     NumOfAddrs, prevNumOfAddrs);
487             OutputDebugString(message);
488         }
489
490         if ( NumOfAddrs != prevNumOfAddrs ) {
491             // Give AFS Client Service a chance to notice and die
492             // Or for network services to startup
493             Sleep(2000);
494             // this call should probably be mutex protected
495             ObtainTokensFromUserIfNeeded(hWnd);
496         }
497         prevNumOfAddrs = NumOfAddrs;
498     }
499 }
500
501
502 DWORD
503 IpAddrChangeMonitorInit(HWND hWnd)
504 {
505     DWORD status = ERROR_SUCCESS;
506     HANDLE thread;
507     ULONG  threadID = 0;
508
509     thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)IpAddrChangeMonitor,
510                                     hWnd, 0, &threadID);
511
512     if (thread == NULL) {
513         status = GetLastError();
514     }
515     CloseHandle(thread);
516     return status;
517 }
518