20e91dc030984b9456f8c6f034dbe281c6534aaa
[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 #define USE_MS2MIT 1
66
67 #include <afs/stds.h>
68 #include <krb5.h>
69 #include <rx\rxkad.h>
70 #include <afskfw.h>
71 #include "ipaddrchg.h"
72 #include "creds.h"
73 #include <iphlpapi.h>
74 #include <afs/auth.h>
75
76 #define MAXCELLCHARS   64
77
78 #ifdef USE_FSPROBE
79 // Cell Accessibility Functions
80 // based on work originally submitted to the CMU Computer Club
81 // by Jeffrey Hutzelman
82 //
83 // These would work great if the fsProbe interface had been 
84 // ported to Windows
85
86 static 
87 void probeComplete()
88 {
89     fsprobe_Cleanup(1);
90     rx_Finalize();
91 }
92
93 struct ping_params {
94     unsigned short port;            // in
95     int            retry_delay;     // in seconds
96     int            verbose;         // in
97     struct {
98         int        wait;            // in seconds
99         int        retry;           // in attempts
100     }   host;
101     int            max_hosts;       // in
102     int            hosts_attempted; // out
103 }
104
105 // the fsHandler is where we receive the answer to the probe
106 static 
107 int fsHandler(void)
108 {
109     ping_count = fsprobe_Results.probeNum;
110     if (!*fsprobe_Results.probeOK)
111     {
112         ok_count++;
113         if (waiting) complete();
114     }
115     if (ping_count == retry) 
116         complete();
117     return 0;
118 }
119
120 // ping_fs is a callback routine meant to be called from within
121 // cm_SearchCellFile() or cm_SearchCellDNS()
122 static long 
123 pingFS(void *ping_params, struct sockaddr_in *addrp, char *namep)
124 {
125     int rc;
126     struct ping_params * pp = (struct ping_params *) ping_params;
127
128     if ( pp->max_hosts && pp->hosts_attempted >= pp->max_hosts )
129         return 0;
130
131     pp->hosts_attempted++;
132
133     if (pp->port && addrp->sin_port != htons(pp->port))
134         addrp->sin_port = htons(pp->port);
135
136     rc = fsprobe_Init(1, addrp, pp->retry_delay, fsHandler, pp->verbose);
137     if (rc)
138     {
139         fprintf(stderr, "fsprobe_Init failed (%d)\n", rc);
140         fsprobe_Cleanup(1);
141         return 0;
142     }
143
144     for (;;)
145     {
146         tv.tv_sec = pp->host.wait;
147         tv.tv_usec = 0;
148         if (IOMGR_Select(0, 0, 0, 0, &tv)) 
149             break;
150     }
151     probeComplete();
152     return(0);
153 }
154
155
156 static BOOL
157 pingCell(char *cell)
158 {
159     int rc;
160     char rootcell[MAXCELLCHARS+1];
161     char newcell[MAXCELLCHARS+1];
162     struct ping_params pp;
163
164     memset(&pp, 0, sizeof(struct ping_params));
165
166     if (!cell || strlen(cell) == 0) {
167         /* WIN32 NOTE: no way to get max chars */
168         if (rc = pcm_GetRootCellName(rootcell))
169             return(FALSE);
170         cell = rootcell;
171     }
172
173     pp.port = 7000; // AFS FileServer
174     pp.retry_delay = 10;
175     pp.max_hosts = 3;
176     pp.host.wait = 30;
177     pp.host.retry = 0;
178     pp.verbose = 1;
179
180     /* WIN32: cm_SearchCellFile(cell, newcell, linkedCell, pcallback, pdata) */
181     rc = pcm_SearchCellFile(cell, newcell, pingFS, (void *)&pp);
182 }
183 #endif /* USE_FSPROBE */
184  
185 // These two items are imported from afscreds.h 
186 // but it cannot be included without causing conflicts
187 #define c100ns1SECOND        (LONGLONG)10000000
188 static void 
189 TimeToSystemTime (SYSTEMTIME *pst, time_t TimeT)
190 {
191     struct tm *pTime;
192     memset (pst, 0x00, sizeof(SYSTEMTIME));
193
194     if ((pTime = localtime (&TimeT)) != NULL)
195     {
196         pst->wYear = pTime->tm_year + 1900;
197         pst->wMonth = pTime->tm_mon + 1;
198         pst->wDayOfWeek = pTime->tm_wday;
199         pst->wDay = pTime->tm_mday;
200         pst->wHour = pTime->tm_hour;
201         pst->wMinute = pTime->tm_min;
202         pst->wSecond = pTime->tm_sec;
203         pst->wMilliseconds = 0;
204     }
205 }
206
207 static DWORD 
208 GetServiceStatus(
209     LPSTR lpszMachineName, 
210     LPSTR lpszServiceName,
211     DWORD *lpdwCurrentState) 
212
213     DWORD           hr               = NOERROR; 
214     SC_HANDLE       schSCManager     = NULL; 
215     SC_HANDLE       schService       = NULL; 
216     DWORD           fdwDesiredAccess = 0; 
217     SERVICE_STATUS  ssServiceStatus  = {0}; 
218     BOOL            fRet             = FALSE; 
219
220     *lpdwCurrentState = 0; 
221  
222     fdwDesiredAccess = GENERIC_READ; 
223  
224     schSCManager = OpenSCManager(lpszMachineName,  
225                                  NULL,
226                                  fdwDesiredAccess); 
227  
228     if(schSCManager == NULL) 
229     { 
230         hr = GetLastError();
231         goto cleanup; 
232     } 
233  
234     schService = OpenService(schSCManager,
235                              lpszServiceName,
236                              fdwDesiredAccess); 
237  
238     if(schService == NULL) 
239     { 
240         hr = GetLastError();
241         goto cleanup; 
242     } 
243  
244     fRet = QueryServiceStatus(schService,
245                               &ssServiceStatus); 
246  
247     if(fRet == FALSE) 
248     { 
249         hr = GetLastError(); 
250         goto cleanup; 
251     } 
252  
253     *lpdwCurrentState = ssServiceStatus.dwCurrentState; 
254  
255 cleanup: 
256  
257     CloseServiceHandle(schService); 
258     CloseServiceHandle(schSCManager); 
259  
260     return(hr); 
261
262
263 void
264 ObtainTokensFromUserIfNeeded(HWND hWnd)
265 {
266     char * rootcell = NULL;
267     char   cell[MAXCELLCHARS+1] = "";
268     char   password[PROBE_PASSWORD_LEN+1];
269     struct afsconf_cell cellconfig;
270     struct ktc_principal    aserver;
271     struct ktc_principal    aclient;
272     struct ktc_token    atoken;
273     krb5_timestamp now = 0;
274     BOOL serverReachable = 0;
275     int rc;
276     DWORD       CurrentState, code;
277     char        HostName[64];
278     int         use_kfw = KFW_is_available();
279
280     SYSTEMTIME stNow;
281     FILETIME ftNow;
282     LONGLONG llNow;
283     FILETIME ftExpires;
284     LONGLONG llExpires;
285     SYSTEMTIME stExpires;
286
287     CurrentState = 0;
288     memset(HostName, '\0', sizeof(HostName));
289     gethostname(HostName, sizeof(HostName));
290     if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
291         return;
292     if (CurrentState != SERVICE_RUNNING) {
293         SendMessage(hWnd, WM_START_SERVICE, FALSE, 0L);
294         return;
295     }
296
297     rootcell = (char *)GlobalAlloc(GPTR,MAXCELLCHARS+1);
298     if (!rootcell) 
299         goto cleanup;
300
301     code = KFW_AFS_get_cellconfig(cell, (void*)&cellconfig, rootcell);
302     if (code) 
303         goto cleanup;
304
305     memset(&aserver, '\0', sizeof(aserver));
306     strcpy(aserver.name, "afs");
307     strcpy(aserver.cell, rootcell);
308
309     GetLocalTime (&stNow);
310     SystemTimeToFileTime (&stNow, &ftNow);
311     llNow = (((LONGLONG)ftNow.dwHighDateTime) << 32) + (LONGLONG)(ftNow.dwLowDateTime);
312     llNow /= c100ns1SECOND;
313
314     rc = ktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
315     if ( rc == 0 ) {
316         TimeToSystemTime (&stExpires, atoken.endTime);
317         SystemTimeToFileTime (&stExpires, &ftExpires);
318         llExpires = (((LONGLONG)ftExpires.dwHighDateTime) << 32) + (LONGLONG)(ftExpires.dwLowDateTime);
319         llExpires /= c100ns1SECOND;
320
321         if (llNow < llExpires)
322             goto cleanup;
323
324         if ( IsDebuggerPresent() ) {
325             char message[256];
326             sprintf(message,"ObtainTokensFromUserIfNeeded: %d  now = %ul  endTime = %ul\n",
327                      rc, llNow, llExpires);
328             OutputDebugString(message);
329         }
330     }
331
332 #ifdef USE_FSPROBE
333     serverReachable = cellPing(NULL);
334 #else
335     if (use_kfw) {
336         // If we can't use the FSProbe interface we can attempt to forge
337         // a kinit and if we can back an invalid user error we know the
338         // kdc is at least reachable
339         serverReachable = KFW_probe_kdc(&cellconfig);
340     } else {
341         int i;
342
343         for ( i=0 ; i<PROBE_PASSWORD_LEN ; i++ )
344             password[i] = 'x';
345
346         code = ObtainNewCredentials(rootcell, PROBE_USERNAME, password, TRUE);
347         switch ( code ) {
348         case INTK_BADPW:
349         case KERB_ERR_PRINCIPAL_UNKNOWN:
350         case KERB_ERR_SERVICE_EXP:
351         case RD_AP_TIME:
352             serverReachable = TRUE;
353             break;
354         default:
355             serverReachable = FALSE;
356         }
357     }
358 #endif
359     if ( !serverReachable ) {
360         if ( IsDebuggerPresent() )
361             OutputDebugString("Server Unreachable\n");
362         goto cleanup;
363     }
364
365     if ( IsDebuggerPresent() )
366         OutputDebugString("Server Reachable\n");
367
368     if ( use_kfw ) {
369 #ifdef USE_MS2MIT
370         KFW_import_windows_lsa();
371 #endif /* USE_MS2MIT */
372         KFW_AFS_renew_expiring_tokens();
373         KFW_AFS_renew_token_for_cell(rootcell);
374
375         rc = ktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
376         if ( rc == 0 ) {
377             TimeToSystemTime (&stExpires, atoken.endTime);
378             SystemTimeToFileTime (&stExpires, &ftExpires);
379             llExpires = (((LONGLONG)ftExpires.dwHighDateTime) << 32) + (LONGLONG)(ftExpires.dwLowDateTime);
380             llExpires /= c100ns1SECOND;
381         
382             if (llNow < llExpires)
383                 goto cleanup;
384         }
385     }
386
387     SendMessage(hWnd, WM_OBTAIN_TOKENS, FALSE, (long)rootcell);
388     rootcell = NULL;    // rootcell freed by message receiver
389
390   cleanup:
391     if (rootcell)
392         GlobalFree(rootcell);
393
394     return;
395 }
396
397 DWORD
398 GetNumOfIpAddrs(void)
399 {
400     PMIB_IPADDRTABLE pIpAddrTable = NULL;
401     ULONG            dwSize;
402     DWORD            code;
403     DWORD            index;
404     DWORD            validAddrs = 0;
405
406     dwSize = 0;
407     code = GetIpAddrTable(NULL, &dwSize, 0);
408     if (code == ERROR_INSUFFICIENT_BUFFER) {
409         pIpAddrTable = malloc(dwSize);
410         code = GetIpAddrTable(pIpAddrTable, &dwSize, 0);
411         if ( code == NO_ERROR ) {
412             for ( index=0; index < pIpAddrTable->dwNumEntries; index++ ) {
413                 if (pIpAddrTable->table[index].dwAddr != 0)
414                     validAddrs++;
415             }
416         }
417         free(pIpAddrTable);
418     }
419     return validAddrs;
420 }
421
422 void
423 IpAddrChangeMonitor(void * hWnd)
424 {
425 #ifdef USE_OVERLAPPED
426     HANDLE Handle = INVALID_HANDLE_VALUE;   /* Do Not Close This Handle */
427     OVERLAPPED Ovlap;
428 #endif /* USE_OVERLAPPED */
429     DWORD Result;
430     DWORD prevNumOfAddrs = GetNumOfIpAddrs();
431     DWORD NumOfAddrs;
432     char message[256];
433
434     if ( !hWnd )
435         return;
436
437     while ( TRUE ) {
438 #ifdef USE_OVERLAPPED
439         ZeroMemory(&Ovlap, sizeof(OVERLAPPED));
440
441         Result = NotifyAddrChange(&Handle,&Ovlap);
442         if (Result != ERROR_IO_PENDING)
443         {        
444             if ( IsDebuggerPresent() ) {
445                 sprintf(message, "NotifyAddrChange() failed with error %d \n", Result);
446                 OutputDebugString(message);
447             }
448             break;
449         }
450
451         if ((Result = WaitForSingleObject(Handle,INFINITE)) != WAIT_OBJECT_0)
452         {
453             if ( IsDebuggerPresent() ) {
454                 sprintf(message, "WaitForSingleObject() failed with error %d\n",
455                         GetLastError());
456                 OutputDebugString(message);
457             }
458             continue;
459         }
460
461         if (GetOverlappedResult(Handle, &Ovlap,
462                                  &DataTransfered, TRUE) == 0)
463         {
464             if ( IsDebuggerPresent() ) {
465                 sprintf(message, "GetOverlapped result failed %d \n",
466                         GetLastError());
467                 OutputDebugString(message);
468             }
469             break;
470         }
471 #else
472         Result = NotifyAddrChange(NULL,NULL);
473         if (Result != NO_ERROR)
474         {        
475             if ( IsDebuggerPresent() ) {
476                 sprintf(message, "NotifyAddrChange() failed with error %d \n", Result);
477                 OutputDebugString(message);
478             }
479             break;
480         }
481 #endif
482         
483         NumOfAddrs = GetNumOfIpAddrs();
484
485         if ( IsDebuggerPresent() ) {
486             sprintf(message,"IPAddrChangeMonitor() NumOfAddrs: now %d was %d\n",
487                     NumOfAddrs, prevNumOfAddrs);
488             OutputDebugString(message);
489         }
490
491         if ( NumOfAddrs != prevNumOfAddrs ) {
492             // Give AFS Client Service a chance to notice and die
493             // Or for network services to startup
494             Sleep(2000);
495             // this call should probably be mutex protected
496             ObtainTokensFromUserIfNeeded(hWnd);
497         }
498         prevNumOfAddrs = NumOfAddrs;
499     }
500 }
501
502
503 DWORD 
504 IpAddrChangeMonitorInit(HWND hWnd)
505 {
506     DWORD status = ERROR_SUCCESS;
507     HANDLE thread;
508     ULONG  threadID = 0;
509
510     thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)IpAddrChangeMonitor,
511                                     hWnd, 0, &threadID);
512
513     if (thread == NULL) {
514         status = GetLastError();
515     }
516     CloseHandle(thread);
517     return status;
518 }
519