ipaddrchg-20040506
authorJeffrey Altman <jaltman@mit.edu>
Thu, 6 May 2004 15:21:44 +0000 (15:21 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Thu, 6 May 2004 15:21:44 +0000 (15:21 +0000)
ipaddrchg.[ch]: contains the ip address change monitor thread functionality
                which was previously in afskfw.[ch] but which is now a
                library in src/WINNT/afsd

src/WINNT/client_creds/ipaddrchg.c [new file with mode: 0644]
src/WINNT/client_creds/ipaddrchg.h [new file with mode: 0644]

diff --git a/src/WINNT/client_creds/ipaddrchg.c b/src/WINNT/client_creds/ipaddrchg.c
new file mode 100644 (file)
index 0000000..d1a45a7
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ * Copyright (c) 2003 SkyRope, LLC
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice, 
+ *   this list of conditions and the following disclaimer in the documentation 
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of Skyrope, LLC nor the names of its contributors may be 
+ *   used to endorse or promote products derived from this software without 
+ *   specific prior written permission from Skyrope, LLC.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this code are derived from portions of the MIT
+ * Leash Ticket Manager and LoadFuncs utilities.  For these portions the
+ * following copyright applies.
+ *
+ * Copyright (c) 2003,2004 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+// IP Change Monitoring Functions
+
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <winsock2.h>
+
+#define USE_MS2MIT
+#define USE_KRB4
+#include <afs/stds.h>
+#include <krb5.h>
+#include <rxkad.h>
+#include <afskfw.h>
+#include "ipaddrchg.h"
+#include "creds.h"
+#include <iphlpapi.h>
+#include <afs/auth.h>
+
+#define MAXCELLCHARS   64
+
+#ifdef USE_FSPROBE
+// Cell Accessibility Functions
+// based on work originally submitted to the CMU Computer Club
+// by Jeffrey Hutzelman
+//
+// These would work great if the fsProbe interface had been 
+// ported to Windows
+
+static 
+void probeComplete()
+{
+    fsprobe_Cleanup(1);
+    rx_Finalize();
+}
+
+struct ping_params {
+    unsigned short port;            // in
+    int            retry_delay;     // in seconds
+    int            verbose;         // in
+    struct {
+        int        wait;            // in seconds
+        int        retry;           // in attempts
+    }   host;
+    int            max_hosts;       // in
+    int            hosts_attempted; // out
+}
+
+// the fsHandler is where we receive the answer to the probe
+static 
+int fsHandler(void)
+{
+    ping_count = fsprobe_Results.probeNum;
+    if (!*fsprobe_Results.probeOK)
+    {
+        ok_count++;
+        if (waiting) complete();
+    }
+    if (ping_count == retry) 
+        complete();
+    return 0;
+}
+
+// ping_fs is a callback routine meant to be called from within
+// cm_SearchCellFile() or cm_SearchCellDNS()
+static long 
+pingFS(void *ping_params, struct sockaddr_in *addrp, char *namep)
+{
+    int rc;
+    struct ping_params * pp = (struct ping_params *) ping_params;
+
+    if ( pp->max_hosts && pp->hosts_attempted >= pp->max_hosts )
+        return 0;
+
+    pp->hosts_attempted++;
+
+    if (pp->port && addrp->sin_port != htons(pp->port))
+        addrp->sin_port = htons(pp->port);
+
+    rc = fsprobe_Init(1, addrp, pp->retry_delay, fsHandler, pp->verbose);
+    if (rc)
+    {
+        fprintf(stderr, "fsprobe_Init failed (%d)\n", rc);
+        fsprobe_Cleanup(1);
+        return 0;
+    }
+
+    for (;;)
+    {
+        tv.tv_sec = pp->host.wait;
+        tv.tv_usec = 0;
+        if (IOMGR_Select(0, 0, 0, 0, &tv)) 
+            break;
+    }
+    probeComplete();
+    return(0);
+}
+
+
+static BOOL
+pingCell(char *cell)
+{
+    int        rc;
+    char rootcell[MAXCELLCHARS+1];
+    char newcell[MAXCELLCHARS+1];
+    struct ping_params pp;
+
+    memset(&pp, 0, sizeof(struct ping_params));
+
+    if (!cell || strlen(cell) == 0) {
+        /* WIN32 NOTE: no way to get max chars */
+        if (rc = pcm_GetRootCellName(rootcell))
+            return(FALSE);
+        cell = rootcell;
+    }
+
+    pp.port = 7000; // AFS FileServer
+    pp.retry_delay = 10;
+    pp.max_hosts = 3;
+    pp.host.wait = 30;
+    pp.host.retry = 0;
+    pp.verbose = 1;
+
+    /* WIN32: cm_SearchCellFile(cell, pcallback, pdata) */
+    rc = pcm_SearchCellFile(cell, newcell, pingFS, (void *)&pp);
+}
+#endif /* USE_FSPROBE */
+// These two items are imported from afscreds.h 
+// but it cannot be included without causing conflicts
+#define c100ns1SECOND        (LONGLONG)10000000
+static void 
+TimeToSystemTime (SYSTEMTIME *pst, time_t TimeT)
+{
+    struct tm *pTime;
+    memset (pst, 0x00, sizeof(SYSTEMTIME));
+
+    if ((pTime = localtime (&TimeT)) != NULL)
+    {
+        pst->wYear = pTime->tm_year + 1900;
+        pst->wMonth = pTime->tm_mon + 1;
+        pst->wDayOfWeek = pTime->tm_wday;
+        pst->wDay = pTime->tm_mday;
+        pst->wHour = pTime->tm_hour;
+        pst->wMinute = pTime->tm_min;
+        pst->wSecond = pTime->tm_sec;
+        pst->wMilliseconds = 0;
+    }
+}
+
+static DWORD 
+GetServiceStatus(
+    LPSTR lpszMachineName, 
+    LPSTR lpszServiceName,
+    DWORD *lpdwCurrentState) 
+{ 
+    DWORD           hr               = NOERROR; 
+    SC_HANDLE       schSCManager     = NULL; 
+    SC_HANDLE       schService       = NULL; 
+    DWORD           fdwDesiredAccess = 0; 
+    SERVICE_STATUS  ssServiceStatus  = {0}; 
+    BOOL            fRet             = FALSE; 
+
+    *lpdwCurrentState = 0; 
+    fdwDesiredAccess = GENERIC_READ; 
+    schSCManager = OpenSCManager(lpszMachineName,  
+                                 NULL,
+                                 fdwDesiredAccess); 
+    if(schSCManager == NULL) 
+    { 
+        hr = GetLastError();
+        goto cleanup; 
+    } 
+    schService = OpenService(schSCManager,
+                             lpszServiceName,
+                             fdwDesiredAccess); 
+    if(schService == NULL) 
+    { 
+        hr = GetLastError();
+        goto cleanup; 
+    } 
+    fRet = QueryServiceStatus(schService,
+                              &ssServiceStatus); 
+    if(fRet == FALSE) 
+    { 
+        hr = GetLastError(); 
+        goto cleanup; 
+    } 
+    *lpdwCurrentState = ssServiceStatus.dwCurrentState; 
+cleanup: 
+    CloseServiceHandle(schService); 
+    CloseServiceHandle(schSCManager); 
+    return(hr); 
+} 
+
+void
+ObtainTokensFromUserIfNeeded(HWND hWnd)
+{
+    char * rootcell = NULL;
+    char   cell[MAXCELLCHARS+1] = "";
+    char   password[PROBE_PASSWORD_LEN+1];
+    struct afsconf_cell cellconfig;
+    struct ktc_principal    aserver;
+    struct ktc_principal    aclient;
+    struct ktc_token   atoken;
+    krb5_timestamp now = 0;
+    BOOL serverReachable = 0;
+    int rc;
+    DWORD       CurrentState, code;
+    char        HostName[64];
+    int         use_kfw = KFW_is_available();
+
+    SYSTEMTIME stNow;
+    FILETIME ftNow;
+    LONGLONG llNow;
+    FILETIME ftExpires;
+    LONGLONG llExpires;
+    SYSTEMTIME stExpires;
+
+    CurrentState = 0;
+    memset(HostName, '\0', sizeof(HostName));
+    gethostname(HostName, sizeof(HostName));
+    if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
+        return;
+    if (CurrentState != SERVICE_RUNNING) {
+        SendMessage(hWnd, WM_START_SERVICE, FALSE, 0L);
+        return;
+    }
+
+    rootcell = (char *)GlobalAlloc(GPTR,MAXCELLCHARS+1);
+    if ( !rootcell ) goto cleanup;
+
+    code = KFW_AFS_get_cellconfig(cell, (void*)&cellconfig, rootcell);
+    if ( code ) goto cleanup;
+
+    memset(&aserver, '\0', sizeof(aserver));
+    strcpy(aserver.name, "afs");
+    strcpy(aserver.cell, rootcell);
+
+    rc = ktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
+
+    GetLocalTime (&stNow);
+    SystemTimeToFileTime (&stNow, &ftNow);
+    llNow = (((LONGLONG)ftNow.dwHighDateTime) << 32) + (LONGLONG)(ftNow.dwLowDateTime);
+    llNow /= c100ns1SECOND;
+
+    TimeToSystemTime (&stExpires, atoken.endTime);
+    SystemTimeToFileTime (&stExpires, &ftExpires);
+    llExpires = (((LONGLONG)ftExpires.dwHighDateTime) << 32) + (LONGLONG)(ftExpires.dwLowDateTime);
+    llExpires /= c100ns1SECOND;
+
+    if (!rc && (llNow < llExpires))
+        goto cleanup;
+
+    if ( IsDebuggerPresent() ) {
+        char message[256];
+        sprintf(message,"ObtainTokensFromUserIfNeeded: %d  now = %ul  endTime = %ul\n",
+                 rc, llNow, llExpires);
+        OutputDebugString(message);
+    }
+
+#ifdef USE_FSPROBE
+    serverReachable = cellPing(NULL);
+#else
+    if ( use_kfw ) {
+        // If we can't use the FSProbe interface we can attempt to forge
+        // a kinit and if we can back an invalid user error we know the
+        // kdc is at least reachable
+        serverReachable = KFW_probe_kdc(&cellconfig);
+    } else {
+        int i;
+
+        for ( i=0 ; i<PROBE_PASSWORD_LEN ; i++ )
+            password[i] = 'x';
+
+        code = ObtainNewCredentials(rootcell, PROBE_USERNAME, password, TRUE);
+        switch ( code ) {
+        case INTK_BADPW:
+        case KERB_ERR_PRINCIPAL_UNKNOWN:
+        case KERB_ERR_SERVICE_EXP:
+        case RD_AP_TIME:
+            serverReachable = TRUE;
+            break;
+        default:
+            serverReachable = FALSE;
+        }
+    }
+#endif
+    if ( !serverReachable ) {
+        if ( IsDebuggerPresent() )
+            OutputDebugString("Server Unreachable\n");
+        goto cleanup;
+    }
+
+    if ( IsDebuggerPresent() )
+        OutputDebugString("Server Reachable\n");
+
+    if ( use_kfw ) {
+#ifdef USE_MS2MIT
+        KFW_import_windows_lsa();
+#endif /* USE_MS2MIT */
+        KFW_AFS_renew_expiring_tokens();
+        KFW_AFS_renew_token_for_cell(rootcell);
+
+        rc = ktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
+
+        TimeToSystemTime (&stExpires, atoken.endTime);
+        SystemTimeToFileTime (&stExpires, &ftExpires);
+        llExpires = (((LONGLONG)ftExpires.dwHighDateTime) << 32) + (LONGLONG)(ftExpires.dwLowDateTime);
+        llExpires /= c100ns1SECOND;
+        
+        if (!rc && (llNow < llExpires))
+            goto cleanup;
+    }
+
+    SendMessage(hWnd, WM_OBTAIN_TOKENS, FALSE, (long)rootcell);
+    rootcell = NULL;    // rootcell freed by message receiver
+
+  cleanup:
+    if (rootcell)
+        GlobalFree(rootcell);
+
+    return;
+}
+
+DWORD
+GetNumOfIpAddrs(void)
+{
+    PMIB_IPADDRTABLE pIpAddrTable = NULL;
+    ULONG            dwSize;
+    DWORD            code;
+    DWORD            index;
+    DWORD            validAddrs = 0;
+
+    dwSize = 0;
+    code = GetIpAddrTable(NULL, &dwSize, 0);
+    if (code == ERROR_INSUFFICIENT_BUFFER) {
+        pIpAddrTable = malloc(dwSize);
+        code = GetIpAddrTable(pIpAddrTable, &dwSize, 0);
+        if ( code == NO_ERROR ) {
+            for ( index=0; index < pIpAddrTable->dwNumEntries; index++ ) {
+                if (pIpAddrTable->table[index].dwAddr != 0)
+                    validAddrs++;
+            }
+        }
+        free(pIpAddrTable);
+    }
+    return validAddrs;
+}
+
+void
+IpAddrChangeMonitor(void * hWnd)
+{
+#ifdef USE_OVERLAPPED
+    HANDLE Handle = INVALID_HANDLE_VALUE;   /* Do Not Close This Handle */
+    OVERLAPPED Ovlap;
+#endif /* USE_OVERLAPPED */
+    DWORD Result;
+    DWORD prevNumOfAddrs = GetNumOfIpAddrs();
+    DWORD NumOfAddrs;
+    char message[256];
+
+    if ( !hWnd )
+        return;
+
+    while ( TRUE ) {
+#ifdef USE_OVERLAPPED
+        ZeroMemory(&Ovlap, sizeof(OVERLAPPED));
+
+        Result = NotifyAddrChange(&Handle,&Ovlap);
+        if (Result != ERROR_IO_PENDING)
+        {        
+            if ( IsDebuggerPresent() ) {
+                sprintf(message, "NotifyAddrChange() failed with error %d \n", Result);
+                OutputDebugString(message);
+            }
+            break;
+        }
+
+        if ((Result = WaitForSingleObject(Handle,INFINITE)) != WAIT_OBJECT_0)
+        {
+            if ( IsDebuggerPresent() ) {
+                sprintf(message, "WaitForSingleObject() failed with error %d\n",
+                        GetLastError());
+                OutputDebugString(message);
+            }
+            continue;
+        }
+
+        if (GetOverlappedResult(Handle, &Ovlap,
+                                 &DataTransfered, TRUE) == 0)
+        {
+            if ( IsDebuggerPresent() ) {
+                sprintf(message, "GetOverlapped result failed %d \n",
+                        GetLastError());
+                OutputDebugString(message);
+            }
+            break;
+        }
+#else
+        Result = NotifyAddrChange(NULL,NULL);
+        if (Result != NO_ERROR)
+        {        
+            if ( IsDebuggerPresent() ) {
+                sprintf(message, "NotifyAddrChange() failed with error %d \n", Result);
+                OutputDebugString(message);
+            }
+            break;
+        }
+#endif
+        
+        NumOfAddrs = GetNumOfIpAddrs();
+
+        if ( IsDebuggerPresent() ) {
+            sprintf(message,"IPAddrChangeMonitor() NumOfAddrs: now %d was %d\n",
+                    NumOfAddrs, prevNumOfAddrs);
+            OutputDebugString(message);
+        }
+
+        if ( NumOfAddrs != prevNumOfAddrs ) {
+            // Give AFS Client Service a chance to notice and die
+            // Or for network services to startup
+            Sleep(2000);
+            // this call should probably be mutex protected
+            ObtainTokensFromUserIfNeeded(hWnd);
+        }
+        prevNumOfAddrs = NumOfAddrs;
+    }
+}
+
+
+DWORD 
+IpAddrChangeMonitorInit(HWND hWnd)
+{
+    DWORD status = ERROR_SUCCESS;
+    HANDLE thread;
+    ULONG  threadID = 0;
+
+    thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)IpAddrChangeMonitor,
+                                    hWnd, 0, &threadID);
+
+    if (thread == NULL) {
+        status = GetLastError();
+    }
+    CloseHandle(thread);
+    return status;
+}
+
diff --git a/src/WINNT/client_creds/ipaddrchg.h b/src/WINNT/client_creds/ipaddrchg.h
new file mode 100644 (file)
index 0000000..09e506b
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2003 SkyRope, LLC
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice, 
+ *   this list of conditions and the following disclaimer in the documentation 
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of Skyrope, LLC nor the names of its contributors may be 
+ *   used to endorse or promote products derived from this software without 
+ *   specific prior written permission from Skyrope, LLC.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef IPADDRCHG_H
+#define IPADDRCHG_H
+#ifdef  __cplusplus
+extern "C" {
+#endif
+#define WM_OBTAIN_TOKENS (WM_USER+77)
+#define WM_START_SERVICE (WM_USER+78)
+void  ObtainTokensFromUserIfNeeded(HWND hWnd);
+DWORD IpAddrChangeMonitorInit(HWND hWnd);
+
+#ifdef  __cplusplus
+}
+#endif
+#endif /* IPADDRCHG_H */