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