[Openvpn-devel,v2,openvpn] Register/unregister trusted custom dialer DLL when installing/uninstalling service

Message ID 20180725195016.22456-1-kkane@microsoft.com
State Deferred
Headers show
Series [Openvpn-devel,v2,openvpn] Register/unregister trusted custom dialer DLL when installing/uninstalling service | expand

Commit Message

Kristof Provost via Openvpn-devel July 25, 2018, 9:50 a.m. UTC
Add copyright notice as required where Microsoft has contributed code

Signed-off-by: Kevin Kane <kkane@microsoft.com>
---
 src/openvpnserv/service.c | 241 +++++++++++++++++++++++++++++++++++---
 1 file changed, 222 insertions(+), 19 deletions(-)

Patch

diff --git a/src/openvpnserv/service.c b/src/openvpnserv/service.c
index 7157bea2..edb21b0d 100644
--- a/src/openvpnserv/service.c
+++ b/src/openvpnserv/service.c
@@ -6,6 +6,7 @@ 
  *
  * Copyright (C) 1993 - 2000.  Microsoft Corporation.  All rights reserved.
  *                      2013 Heiko Hund <heiko.hund@sophos.com>
+ * Portions Copyright (C) 2018 Microsoft Corporation
  */
 
 #include "service.h"
@@ -53,6 +54,195 @@  ReportStatusToSCMgr(SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status)
     return res;
 }
 
+static const TCHAR DialerDllName[] = TEXT("libopenvpndialer-0.dll");
+static const TCHAR RegValueName[] = TEXT("CustomDLL");
+
+static int
+HandleDialerRegistration(int uninstall)
+{
+    TCHAR path[512], customDllString[1024];
+    HKEY parametersKey;
+    LONG result;
+    DWORD customDllSize = sizeof(customDllString);
+
+    /* Assumption is that the dialer DLL is installed to the same bin directory as everything else. */
+    if (GetModuleFileName(NULL, path, sizeof(path)) == 0)
+    {
+        _tprintf(TEXT("Unable to get module path - %lu\n"), GetLastError());
+        return 1;
+    }
+
+    /* The version of NSIS we use to create the installer doesn't yet have support for 
+     * writing multi-string registry entries, which we need to do in order to register our
+     * custom dialer DLL. Instead, we'll update this registry entry on install/uninstall.
+     */
+
+    TCHAR *lastBackslash = _tcsrchr(path, TEXT('\\'));
+    if (NULL == lastBackslash)
+    {
+        _tprintf(TEXT("Could not locate last backslash in path: %s\n"), path);
+        return 1;
+    }
+
+    lastBackslash[1] = TEXT('\0');
+
+    /* Bounds checking. */
+    if ((_tcslen(path) + _countof(DialerDllName)) > _countof(path))
+    {
+        _tprintf(TEXT("Out of buffer adding dialer filename to path"));
+        return 1;
+    }
+
+    _tcscat(path, DialerDllName);
+
+    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
+        TEXT("SYSTEM\\CurrentControlSet\\Services\\RasMan\\Parameters"), 
+        0, 
+        KEY_ALL_ACCESS,
+        &parametersKey);
+    if (ERROR_SUCCESS != result)
+    {
+        _tprintf(TEXT("Could not open RasMan parameters key: %ld\n"), result);
+        return 1;
+    }
+
+    /* Must RegCloseKey(parametersKey) from this point */
+
+    result = RegGetValue(parametersKey,
+        NULL,
+        RegValueName,
+        RRF_RT_REG_MULTI_SZ,
+        NULL,
+        customDllString,
+        &customDllSize);
+
+    /* If we're installing, the key being absent is okay. Any other error is fatal. */
+    if (ERROR_FILE_NOT_FOUND == result)
+    {
+        if (0 != uninstall)
+        {
+            _tprintf(TEXT("CustomDLL value was not found; skipping unregistration step\n"));
+            RegCloseKey(parametersKey);
+            return 0; /* Not a fatal error but nothing to do */
+        }
+        else
+        {
+            result = ERROR_SUCCESS;
+            customDllString[0] = TEXT('\0');
+            customDllSize = 0;
+        }
+    }
+    else if (ERROR_SUCCESS != result)
+    {
+        _tprintf(TEXT("Could not open CustomDLL value: %ld\n"), result);
+        RegCloseKey(parametersKey);
+        return 1;
+    }
+
+    /* Determine if the custom dialer DLL is present in the registry setting. This is a multi-string so we can't 
+     * use _tcsstr.
+     */
+    TCHAR* p;
+    for (p = customDllString; *p != TEXT('\0'); p += _tcslen(p) + 1)
+    {
+        if (0 == _tcsicmp(path, p))
+        {
+            /* It is. p points to where it begins. */
+            break;
+        }
+    }
+    /* If it's not present, p points at a NULL terminator. */
+
+    /* If we're installing and the DLL is present in the registry, do nothing. */
+    /* If we're uninstalling and the DLL is not present in the registry, do nothing. */
+    if ( ((0 == uninstall) && (*p != TEXT('\0'))) ||
+         ((0 != uninstall) && (*p == TEXT('\0'))) )
+    {
+        RegCloseKey(parametersKey);
+        return 0;
+    }
+
+    /* If we're installing and the DLL is not present in the registry, append it at point p. */
+    if ((0 == uninstall) && (*p == TEXT('\0')))
+    {
+        /* Make sure the string buffer has enough space left. p points at the second in the double-terminating null
+         * (or the sole NULL if the registry entry was empty or absent).
+         */
+        if (((p - customDllString) + _tcslen(path) + 2) > _countof(customDllString))
+        {
+            _tprintf(TEXT("Not enough buffer to create new CustomDLL string\n"));
+            RegCloseKey(parametersKey);
+            return 1;
+        }
+        _tcscpy(p, path);
+        /* Add second terminating NULL we overwrote with the copy. */
+        p += _tcslen(p) + 1;
+        *p++ = TEXT('\0');
+
+        /* Set in the registry. */
+        result = RegSetValueEx(parametersKey,
+            RegValueName,
+            0,
+            REG_MULTI_SZ,
+            (const BYTE*)customDllString,
+            (DWORD)(sizeof(TCHAR) * (p - customDllString)));
+
+        RegCloseKey(parametersKey);
+        if (ERROR_SUCCESS != result)
+        {
+            _tprintf(TEXT("Failed to RegSetValueEx: %ld\n"), result);
+            return 1;
+        }
+    }
+    /* Else if we're uninstalling and the DLL is present, copy everything except strings matching path. */
+    else if ((0 != uninstall) && (*p != TEXT('\0')))
+    {
+        TCHAR newCustomDllString[1024];
+        TCHAR *n, *c;
+
+        /* newCustomDllString is the same size as customDllString, and we're only removing, so no
+         * way to overflow the buffer.
+         */
+        for (c = customDllString, n = newCustomDllString; *c != TEXT('\0'); c += _tcslen(c) + 1)
+        {
+            if (0 != _tcsicmp(path, c))
+            {
+                _tcscpy(n, c);
+                n += _tcslen(n) + 1;
+            }
+        }
+
+        /* Add double-terminating NULL as is required for a multi-string. */
+        *n++ = TEXT('\0');
+
+        /* If the new string is nonempty, set in the registry. Otherwise delete the value. */
+        if (TEXT('\0') != *newCustomDllString)
+        {
+            result = RegSetValueEx(parametersKey,
+                RegValueName,
+                0,
+                REG_MULTI_SZ,
+                (const BYTE*)newCustomDllString,
+                (DWORD)(sizeof(TCHAR) * (n - newCustomDllString)));
+        }
+        else
+        {
+            result = RegDeleteValue(parametersKey, RegValueName);
+        }
+
+        RegCloseKey(parametersKey);
+        if (ERROR_SUCCESS != result)
+        {
+            _tprintf(TEXT("Failed to %s new registry value: %ld\n"), 
+                (TEXT('\0') != *newCustomDllString)?TEXT("set"):TEXT("delete"),
+                result);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
 static int
 CmdInstallServices()
 {
@@ -77,27 +267,35 @@  CmdInstallServices()
         return 1;
     }
 
-    for (i = 0; i < _service_max; i++)
+    if (0 != HandleDialerRegistration(0))
     {
-        service = CreateService(svc_ctl_mgr,
-                                openvpn_service[i].name,
-                                openvpn_service[i].display_name,
-                                SERVICE_QUERY_STATUS,
-                                SERVICE_WIN32_SHARE_PROCESS,
-                                openvpn_service[i].start_type,
-                                SERVICE_ERROR_NORMAL,
-                                path, NULL, NULL,
-                                openvpn_service[i].dependencies,
-                                NULL, NULL);
-        if (service)
-        {
-            _tprintf(TEXT("%s installed.\n"), openvpn_service[i].display_name);
-            CloseServiceHandle(service);
-            --ret;
-        }
-        else
+        _tprintf(TEXT("HandleDialerRegistration failed\n"));
+        ret = 1;
+    }
+    else
+    {
+        for (i = 0; i < _service_max; i++)
         {
-            _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText());
+            service = CreateService(svc_ctl_mgr,
+                                    openvpn_service[i].name,
+                                    openvpn_service[i].display_name,
+                                    SERVICE_QUERY_STATUS,
+                                    SERVICE_WIN32_SHARE_PROCESS,
+                                    openvpn_service[i].start_type,
+                                    SERVICE_ERROR_NORMAL,
+                                    path, NULL, NULL,
+                                    openvpn_service[i].dependencies,
+                                    NULL, NULL);
+            if (service)
+            {
+                _tprintf(TEXT("%s installed.\n"), openvpn_service[i].display_name);
+                CloseServiceHandle(service);
+                --ret;
+            }
+            else
+            {
+                _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText());
+            }
         }
     }
 
@@ -160,6 +358,11 @@  CmdRemoveServices()
         return 1;
     }
 
+    if (0 != HandleDialerRegistration(1))
+    {
+        _tprintf(TEXT("HandleDialerRegistration uninstall failed; ignoring\n"));
+    }
+
     for (i = 0; i < _service_max; i++)
     {
         openvpn_service_t *ovpn_svc = &openvpn_service[i];