Message ID | 20180412184845.1175-1-simon@rozman.si |
---|---|
State | Superseded |
Headers | show |
Series | [Openvpn-devel] Add Interactive Service developer documentation | expand |
Hi, On Thu, Apr 12, 2018 at 2:48 PM, Simon Rozman <simon@rozman.si> wrote: Moving up the most important part: Hi, > I'm back. :) > Welcome back! > I took the short Interactive Service introduction found at > https://community.openvpn.net/openvpn/wiki/OpenVPNInteractiveService and > extended it with my experience while developing the eduVPN client. > This document is not up to the RFC standards nor it intends to be. > It's written using reStructuredText markup as suggested by Iliya, but > keeping > the filename and ".txt" extension as suggested by Samuli. > I suggest replacing the > https://community.openvpn.net/openvpn/wiki/OpenVPNInteractiveService wiki > page +1 to that and to keep it up to date. > with this document analogous to the Management Interface Notes document > published online at > https://openvpn.net/index.php/open-source/documentation/misc > ellaneous/79-management-interface.html. This one too occasionally needs to be kept in sync with the source Your comments are welcome. Thanks for writing this up. Some minor comments below: The OpenVPN Interactive Service documentation from > https://community.openvpn.net/openvpn/wiki/OpenVPNInteractiveService was > upgraded with a description of the client-service communication flow, > service registry configuration, and non-default instance installation. > --- > doc/interactive-service-notes.txt | 316 ++++++++++++++++++++++++++++++ > ++++++++ > 1 file changed, 316 insertions(+) > create mode 100644 doc/interactive-service-notes.txt > > diff --git a/doc/interactive-service-notes.txt > b/doc/interactive-service-notes.txt > new file mode 100644 > index 00000000..9b3b8f6c > --- /dev/null > +++ b/doc/interactive-service-notes.txt > @@ -0,0 +1,316 @@ > +================================= > +OpenVPN Interactive Service Notes > +================================= > + > + > +Introduction > +============ > + > +OpenVPN Interactive Service, also known as "iservice" or > +"OpenVPNServiceInteractive", is a Windows system service which allows > +unprivileged users to do certain privileged operations required by > OpenVPN, such > +as adding routes. This removes the need to always run OpenVPN as > administrator, > +which was the case for a long time, and continues to be the case for > OpenVPN > +2.3.x. > + > +The 2.4.x release and git "master" versions of OpenVPN contain the > Interactive > +Service code and OpenVPN-GUI is setup to use it by default. Starting from > +version 2.4.0, OpenVPN-GUI is expected to be started as user (do not > right-click > +and "run as administrator" or do not set the shortcut to run as > administrator). > +This ensures that OpenVPN and the GUI run with limited privileges. > + > + > +How It Works > +============ > + > +Here is a brief explanation of how the Interactive Service works, based on > +`Gert's email`_ to openvpn-devel mailing list. The example user, *joe*, > is not > +an administrator, and does not have any other extra privileges. > + > +- OpenVPN-GUI runs as a *joe* > +- Interactive Service runs as a local Windows service with maximum > privileges > +- OpenVPN-GUI connects to the Interactive Service and asks it "run > openvpn.exe > + with the following arguments, using the *joe*'s credentials" - Windows > can do > + this - pass credentials across a pipe, which you can't fake > +- Interactive Service forks openvpn.exe, and runs this as the user *joe*, > and > + keeps a "service pipe" between Interactive Service and openvpn.exe > +- If openvpn.exe wants to do ipconfig/route/dns stuff, it sends these as > + requests over the service pipe to the Interactive Service, which will > then > + execute them (and clean up should openvpn.exe crash) > +- ``--up`` scripts are run by openvpn.exe itself, which is already > running as > + *joe*, all privileges are nicely in place > +- Scripts run by the GUI will run as user *joe*, so that automated tasks > like > + mapping of drives work as expected + > +This also avoids the use of scripts for privilege escalation to admin (as > was > +possible by running an ``--up`` script from openvpn.exe which is run as > admin). + > Considering the more formal style used below, the above paragraph could be rewritten too. IMO, its not necessary to preserve the "original" description of "How it works". Just saying.. > + > +Client-Service Communication > +============================ > + > +Connecting > +---------- > + > +The client (OpenVPN GUI) and the Interactive Service communicate using a > named > +message pipe. By default, the service provides the > ``\\.\pipe\openvpn\service`` > +named pipe. > + > +The client connects to the pipe for read/write and sets the pipe state to > the > state to the --> state to > +``PIPE_READMODE_MESSAGE``:: > + > + HANDLE pipe = CreateFile(_T("\\\\.\\pipe\\openvpn\\service"), > + GENERIC_READ | GENERIC_WRITE, > + 0, > + NULL, > + OPEN_EXISTING, > + FILE_FLAG_OVERLAPPED, > + NULL); > + > + if (pipe == INVALID_HANDLE_VALUE) > + { > + // Error > + } > + > + DWORD dwMode = PIPE_READMODE_MESSAGE; > + if (!SetNamedPipeHandleState(pipe, &dwMode, NULL, NULL) > + { > + // Error > + } > + > + > +openvpn.exe Startup > +------------------- > + > +After the client is connected to the service, the client must send a > startup > +message to have the service start the openvpn.exe process. The startup > message > +is comprised of three UTF-16 strings delimited by U0000 zero characters:: > + > + startupmsg = workingdir WZERO openvpnoptions WZERO stdin WZERO > + > + workingdir = WSTRING > + openvpnoptions = WSTRING > + stdin = WSTRING > + > + WSTRING = *WCHAR > + WCHAR = %x0001-FFFF > + WZERO = %x0000 > + > +``workingdir`` > + Represents the folder openvpn.exe process should be started in. > + > +``openvpnoptions`` > + String contains ``--config`` and other OpenVPN command line options, > without > + the ``argv[0]`` executable name ("openvpn" or "openvpn.exe"). When > there is > + only one option specified, the ``--config`` option is assumed and the > option > + is the configuration filename. > + > + Please, note that the interactive service validates the options (e.g. > OpenVPN > Please, note --> Please note or just "Note" considering the overall style + config file must reside in one of the approved folders, or the invoking > user Actually only one approved folder (and its subfolders) is supported. That is, the one defined by the global_config_dir. For all other folders the user must be authorized. Even when the config file is in the approved folder, additional options added to the command line are restricted to a limited set unless the user is authorized. > + must be a member of local Administrators group, or a member of the + "OpenVPN Administrators" group). > More precisely, that would be the group pointed to by the registry key HKLM\Software\OpenVPN\ovpn_admin_group which defaults to "OpenVPN Administrators". > + > +``stdin`` > + The content of the ``stdin`` string is sent to the openvpn.exe process > to its > + stdin stream after it starts. > + > + When a ``--management ... stdin`` option is present, the openvpn.exe > process > + will prompt for the management interface password on start. In this > case, the > + ``stdin`` must contain the password appended with an LF (U000A) to > simulate > + the [Enter] key after the password is "typed" in. > + > + The openvpn.exe's stdout is redirected to ``NUL``. Should the client > require > + openvpn.exe's stdout, one should specify ``--log`` option. > + > +The message must be written in a single ``WriteFile()`` call. +Example:: > + > + // Prepare the message. > + size_t msg_len = > + wcslen(workingdir) + 1 + > + wcslen(options ) + 1 + > + wcslen(manage_pwd) + 1; > + wchar_t *msg_data = (wchar_t*)malloc(msg_len*sizeof(wchar_t)); > + _snwprintf(msg_data, msg_len, L"%s%c%s%c%s", > + workingdir, L'\0', > + options, L'\0', > + manage_pwd) > + > + // Send the message. > + DWORD dwBytesWritten; > + if (!WriteFile(pipe, > + msg_data, > + msg_len*sizeof(wchar_t), > + &dwBytesWritten, > + NULL)) > + { > + // Error > + } > + > + // Sanitize memory, since the stdin component of the message > + // contains the management interface password. > + SecureZeroMemory(msg_data, msg_len*sizeof(wchar_t)); > + free(msg_data); > + > + > +openvpn.exe Process ID > +---------------------- > + > +After receiving the startup message, the Interactive Service validates > the user > +and specified options before launching the openvpn.exe process. > + > +The Interactive Service replies with a process ID message. The process ID > +message is comprised of three UTF-16 strings delimited by LFs (U000A):: > + > + pidmsg = L"0x00000000" WLF L"0x" pid WLF L"Process ID" > + > + pid = 8*8WHEXDIG > + > + WHEXDIG = WDIGIT / L"A" / L"B" / L"C" / L"D" / L"E" / L"F" > + WDIGIT = %x0030-0039 > + WLF = %x000a > + > +``pid`` > + An UTF-16 eight-character hexadecimal process ID of the openvpn.exe > process > A UTF-16 > + the Interactive Service launched on client's behalf. > + > + > +openvpn.exe Termination > +----------------------- > + > +The pipe between the client and the service is kept open until the > openvpn.exe > +process terminates. > + > +Should the openvpn.exe process terminate with an error, the Interactive > Service > +sends an error message to the client before disconnecting the pipe. > May be also mention that the service never terminates openvpn.exe process -- not even when it is stopped or restarted. So its up to the client to monitor the process and terminate it. > + > + > +Error Messages > +-------------- > + > +In case of an error, the Interactive Service sends an error message to the > +client. Error messages are comprised of three UTF-16 strings delimited by > LFs > +(U000A):: > + > + errmsg = L"0x" errnum WLF func WLF msg > + > + errnum = 8*8WHEXDIG > + func = WSTRING > + msg = WSTRING > + > +``errnum`` > + > + An UTF-16 eight-character hexadecimal error code. Typically, it is one > of the > An --> A > + Win32 error codes returned by ``GetLastError()``. > + > + However, it can be one of the Interactive Service specific error codes: > + > + ===================== ========== > + Error Code > + ===================== ========== > + ERROR_OPENVPN_STARTUP 0x20000000 > + ERROR_STARTUP_DATA 0x20000001 > + ERROR_MESSAGE_DATA 0x20000002 > + ERROR_MESSAGE_TYPE 0x20000003 > + ===================== ========== > + > +``func`` > + The name of the function call that failed or an error description. > + > +``msg`` > + The error description returned by a > + ``FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, errnum, ...)`` call. > + > + > +Interactive Service Configuration > +================================= > + > +The Interactive Service settings are read from the > +``HKEY_LOCAL_MACHINE\SOFTWARE\OpenVPN`` registry key by default. > + > +All the following registry values are of the ``REG_SZ`` type: > + > +*Default* > + Installation folder (required) > install_dir used below is undefined. Define it here? > + > +``exe_path`` > + The absolute path to the openvpn.exe binary; defaults to > + ``install_dir "\bin\openvpn.exe"``. > + > +``config_dir`` > + The path to the configuration folder; defaults to ``install_dir > "\config"``. > + > +``priority`` > + openvpn.exe process priority; one of the following strings: > + > + - ``"IDLE_PRIORITY_CLASS"`` > + - ``"BELOW_NORMAL_PRIORITY_CLASS"`` > + - ``"NORMAL_PRIORITY_CLASS"`` (default) > + - ``"ABOVE_NORMAL_PRIORITY_CLASS"`` > + - ``"HIGH_PRIORITY_CLASS"`` > + > +``ovpn_admin_group`` > + The name of the local group, whose members are authorized to use the > + Interactive Service unrestricted; defaults to ``"OpenVPN > Administrators"`` > + > + > +Multiple Interactive Service Instances > +====================================== > + > +OpenVPN 2.4.5 extended the Interactive Service to support multiple > side-by-side > +running instances. This allows clients to use different Interactive > Service > +versions with different settings and/or openvpn.exe binary version on the > same > +computer. > + > +OpenVPN installs the default Interactive Service instance only. The > default > +instance is used by OpenVPN GUI client and also provides backward > compatibility. > + > + > +Installing a Non-default Interactive Service Instance > +----------------------------------------------------- > + > +1. Choose a unique instance name. For example: "$v2.5-test". The instance > name > + is appended to the default registry path and service name. We choose > to start > + it with a dollar "$" sign analogous to Microsoft SQL Server instance > naming > + scheme. However, this is not imperative. + > + Appending the name to the registry path and service name also implies > the > + name cannot contain characters not allowed in Windows paths: "<", ">", > double > + quote etc. > + > +2. Create an ``HKEY_LOCAL_MACHINE\SOFTWARE\OpenVPN$v2.5-test`` registry > key and > + configure the Interactive Service instance configuration appropriately. > + > + This allows using slightly or completely different settings from the > default > + instance. > + > + See the `Interactive Service Configuration`_ chapter for the list of > registry > chapter --> section ? And is that "_" a part of markup or a stale character? > + values. > + > +3. Create and start the instance's Windows service from an elevated > command > + prompt:: > + > + sc create "OpenVPNServiceInteractive$v2.5-test" \ > + start= auto \ > + binPath= "<path to openvpnserv.exe> -instance interactive > $v2.5-test" \ > + depend= tap0901/Dhcp \ > + DisplayName= "OpenVPN Interactive Service (v2.5-test)" > + > + sc start "OpenVPNServiceInteractive$v2.5-test" > + > + This allows using the same or a different version of openvpnserv.exe > than the > + default instance. > + > + Please, mind the spacing around "=" character in ``sc`` command line > options. > Please, mind the spacing around "=" --> Notice the space after "=" + > +4. Set your OpenVPN client to connect to the > + ``\\.\pipe\openvpn$v2.5-test\service``. > + > + This allows the client to select different installed Interactive > Service > different --> a different > + instance at run-time, thus allowing different OpenVPN settings and > versions. > + > + At the time writing, the OpenVPN GUI client supports connecting to the > + default Interactive Service instance only. > + > +.. _`Gert's email`: https://www.mail-archive.com/o > penvpn-devel@lists.sourceforge.net/msg00097.html > -- > 2.15.1 (Apple Git-101) > > Do you also plan to document the service <--> openvpn.exe communication? Or is that beyond the scope of this file? Anyway, could be a separate patch :) Thanks, Selva <div dir="ltr">Hi,<div><br><div class="gmail_extra"><div class="gmail_quote">On Thu, Apr 12, 2018 at 2:48 PM, Simon Rozman <span dir="ltr"><<a href="mailto:simon@rozman.si" target="_blank">simon@rozman.si</a>></span> wrote:</div><div class="gmail_quote"><br></div><div class="gmail_quote">Moving up the most important part:</div><div class="gmail_quote"><div class="gmail_quote"><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi,<br>I'm back. :)<br></blockquote><div><br></div><div>Welcome back!</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">I took the short Interactive Service introduction found at<br><a href="https://community.openvpn.net/openvpn/wiki/OpenVPNInteractiveService" target="_blank">https://community.openvpn.net/<wbr>openvpn/wiki/OpenVPNInteractiv<wbr>eService</a> and<br>extended it with my experience while developing the eduVPN client.<br>This document is not up to the RFC standards nor it intends to be.<br>It's written using reStructuredText markup as suggested by Iliya, but keeping<br>the filename and ".txt" extension as suggested by Samuli.<br>I suggest replacing the<br><a href="https://community.openvpn.net/openvpn/wiki/OpenVPNInteractiveService" target="_blank">https://community.openvpn.net/<wbr>openvpn/wiki/OpenVPNInteractiv<wbr>eService</a> wiki page</blockquote><div class="gmail_quote"><br></div><div class="gmail_quote">+1 to that and to keep it up to date.</div><div class="gmail_quote"> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">with this document analogous to the Management Interface Notes document<br>published online at<br><a href="https://openvpn.net/index.php/open-source/documentation/miscellaneous/79-management-interface.html" target="_blank">https://openvpn.net/index.php/<wbr>open-source/documentation/misc<wbr>ellaneous/79-management-interf<wbr>ace.html</a>.</blockquote><div class="gmail_quote"><br></div><div class="gmail_quote">This one too occasionally needs to be kept in sync with the source</div><div class="gmail_quote"><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Your comments are welcome.</blockquote><div class="gmail_quote"><br></div><div class="gmail_quote">Thanks for writing this up. Some minor comments below:</div></div><div class="gmail_quote"><br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">The OpenVPN Interactive Service documentation from<br> <a href="https://community.openvpn.net/openvpn/wiki/OpenVPNInteractiveService" rel="noreferrer" target="_blank">https://community.openvpn.net/<wbr>openvpn/wiki/OpenVPNInteractiv<wbr>eService</a> was<br> upgraded with a description of the client-service communication flow,<br> service registry configuration, and non-default instance installation.<br> ---<br> doc/interactive-service-notes<wbr>.txt | 316 ++++++++++++++++++++++++++++++<wbr>++++++++<br> 1 file changed, 316 insertions(+)<br> create mode 100644 doc/interactive-service-notes.<wbr>txt<br> <br> diff --git a/doc/interactive-service-note<wbr>s.txt b/doc/interactive-service-note<wbr>s.txt<br> new file mode 100644<br> index 00000000..9b3b8f6c<br> --- /dev/null<br> +++ b/doc/interactive-service-note<wbr>s.txt<br> @@ -0,0 +1,316 @@<br> +=============================<wbr>====<br> +OpenVPN Interactive Service Notes<br> +=============================<wbr>====<br> +<br> +<br> +Introduction<br> +============<br> +<br> +OpenVPN Interactive Service, also known as "iservice" or<br> +"OpenVPNServiceInteractive", is a Windows system service which allows<br> +unprivileged users to do certain privileged operations required by OpenVPN, such<br> +as adding routes. This removes the need to always run OpenVPN as administrator,<br> +which was the case for a long time, and continues to be the case for OpenVPN<br> +2.3.x.<br> +<br> +The 2.4.x release and git "master" versions of OpenVPN contain the Interactive<br> +Service code and OpenVPN-GUI is setup to use it by default. Starting from<br> +version 2.4.0, OpenVPN-GUI is expected to be started as user (do not right-click<br> +and "run as administrator" or do not set the shortcut to run as administrator).<br> +This ensures that OpenVPN and the GUI run with limited privileges.<br> +<br> +<br> +How It Works<br> +============<br> +<br> +Here is a brief explanation of how the Interactive Service works, based on<br> +`Gert's email`_ to openvpn-devel mailing list. The example user, *joe*, is not<br> +an administrator, and does not have any other extra privileges.<br> +<br> +- OpenVPN-GUI runs as a *joe*<br> +- Interactive Service runs as a local Windows service with maximum privileges<br> +- OpenVPN-GUI connects to the Interactive Service and asks it "run openvpn.exe<br> + with the following arguments, using the *joe*'s credentials" - Windows can do<br> + this - pass credentials across a pipe, which you can't fake<br> +- Interactive Service forks openvpn.exe, and runs this as the user *joe*, and<br> + keeps a "service pipe" between Interactive Service and openvpn.exe<br> +- If openvpn.exe wants to do ipconfig/route/dns stuff, it sends these as<br> + requests over the service pipe to the Interactive Service, which will then<br> + execute them (and clean up should openvpn.exe crash)<br> +- ``--up`` scripts are run by openvpn.exe itself, which is already running as<br> + *joe*, all privileges are nicely in place<br> +- Scripts run by the GUI will run as user *joe*, so that automated tasks like<br> + mapping of drives work as expected</blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> +<br> +This also avoids the use of scripts for privilege escalation to admin (as was<br> +possible by running an ``--up`` script from openvpn.exe which is run as admin).</blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">+<br></blockquote><div><br></div><div>Considering the more formal style used below, the above paragraph could be</div><div>rewritten too. IMO, its not necessary to preserve the "original" description of</div><div>"How it works". Just saying..</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> +<br> +Client-Service Communication<br> +============================<br> +<br> +Connecting<br> +----------<br> +<br> +The client (OpenVPN GUI) and the Interactive Service communicate using a named<br> +message pipe. By default, the service provides the ``\\.\pipe\openvpn\service``<br> +named pipe.<br> +<br> +The client connects to the pipe for read/write and sets the pipe state to the<br></blockquote><div><br></div><div>state to the --> state to</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> +``PIPE_READMODE_MESSAGE``::<br> +<br> + HANDLE pipe = CreateFile(_T("\\\\.\\pipe\\op<wbr>envpn\\service"),<br> + GENERIC_READ | GENERIC_WRITE,<br> + 0,<br> + NULL,<br> + OPEN_EXISTING,<br> + FILE_FLAG_OVERLAPPED,<br> + NULL);<br> +<br> + if (pipe == INVALID_HANDLE_VALUE)<br> + {<br> + // Error<br> + }<br> +<br> + DWORD dwMode = PIPE_READMODE_MESSAGE;<br> + if (!SetNamedPipeHandleState(pipe<wbr>, &dwMode, NULL, NULL)<br> + {<br> + // Error<br> + }<br> +<br> +<br> +openvpn.exe Startup<br> +-------------------<br> +<br> +After the client is connected to the service, the client must send a startup<br> +message to have the service start the openvpn.exe process. The startup message<br> +is comprised of three UTF-16 strings delimited by U0000 zero characters::<br> +<br> + startupmsg = workingdir WZERO openvpnoptions WZERO stdin WZERO<br> +<br> + workingdir = WSTRING<br> + openvpnoptions = WSTRING<br> + stdin = WSTRING<br> +<br> + WSTRING = *WCHAR<br> + WCHAR = %x0001-FFFF<br> + WZERO = %x0000<br> +<br> +``workingdir``<br> + Represents the folder openvpn.exe process should be started in.<br> +<br> +``openvpnoptions``<br> + String contains ``--config`` and other OpenVPN command line options, without<br> + the ``argv[0]`` executable name ("openvpn" or "openvpn.exe"). When there is<br> + only one option specified, the ``--config`` option is assumed and the option<br> + is the configuration filename.<br> +<br> + Please, note that the interactive service validates the options (e.g. OpenVPN<br></blockquote><div><br></div><div>Please, note --> Please note or just "Note" considering the overall style</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> + config file must reside in one of the approved folders, or the invoking user</blockquote><div><br></div><div>Actually only one approved folder (and its subfolders) is supported. That is, the</div><div>one defined by the global_config_dir. For all other folders the user must be </div><div>authorized.</div><div><br></div><div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)">Even when the config file is in the approved folder, additional options added to</div><div style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)">the command line are restricted to a limited set unless the user is authorized.</div></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> + must be a member of local Administrators group, or a member of the </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">+ "OpenVPN Administrators" group).<br></blockquote><div><br></div><div><div>More precisely, that would be the group pointed to by the registry key </div><div>HKLM\Software\OpenVPN\ovpn_adm<wbr>in_group which defaults to</div><div>"OpenVPN Administrators".</div></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> +<br> +``stdin``<br> + The content of the ``stdin`` string is sent to the openvpn.exe process to its<br> + stdin stream after it starts.<br> +<br> + When a ``--management ... stdin`` option is present, the openvpn.exe process<br> + will prompt for the management interface password on start. In this case, the<br> + ``stdin`` must contain the password appended with an LF (U000A) to simulate<br> + the [Enter] key after the password is "typed" in.<br> +<br> + The openvpn.exe's stdout is redirected to ``NUL``. Should the client require<br> + openvpn.exe's stdout, one should specify ``--log`` option.<br> +<br> +The message must be written in a single ``WriteFile()`` call.</blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> +Example::<br> +<br> + // Prepare the message.<br> + size_t msg_len =<br> + wcslen(workingdir) + 1 +<br> + wcslen(options ) + 1 +<br> + wcslen(manage_pwd) + 1;<br> + wchar_t *msg_data = (wchar_t*)malloc(msg_len*sizeo<wbr>f(wchar_t));<br> + _snwprintf(msg_data, msg_len, L"%s%c%s%c%s",<br> + workingdir, L'\0',<br> + options, L'\0',<br> + manage_pwd)<br> +<br> + // Send the message.<br> + DWORD dwBytesWritten;<br> + if (!WriteFile(pipe,<br> + msg_data,<br> + msg_len*sizeof(wchar_t),<br> + &dwBytesWritten,<br> + NULL))<br> + {<br> + // Error<br> + }<br> +<br> + // Sanitize memory, since the stdin component of the message<br> + // contains the management interface password.<br> + SecureZeroMemory(msg_data, msg_len*sizeof(wchar_t));<br> + free(msg_data);<br> +<br> +<br> +openvpn.exe Process ID<br> +----------------------<br> +<br> +After receiving the startup message, the Interactive Service validates the user<br> +and specified options before launching the openvpn.exe process.<br> +<br> +The Interactive Service replies with a process ID message. The process ID<br> +message is comprised of three UTF-16 strings delimited by LFs (U000A)::<br> +<br> + pidmsg = L"0x00000000" WLF L"0x" pid WLF L"Process ID"<br> +<br> + pid = 8*8WHEXDIG<br> +<br> + WHEXDIG = WDIGIT / L"A" / L"B" / L"C" / L"D" / L"E" / L"F"<br> + WDIGIT = %x0030-0039<br> + WLF = %x000a<br> +<br> +``pid``<br> + An UTF-16 eight-character hexadecimal process ID of the openvpn.exe process<br> </blockquote><div><br></div><div>A UTF-16</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">+ the Interactive Service launched on client's behalf.<br> +<br> +<br> +openvpn.exe Termination<br> +-----------------------<br> +<br> +The pipe between the client and the service is kept open until the openvpn.exe<br> +process terminates.<br> +<br> +Should the openvpn.exe process terminate with an error, the Interactive Service<br> +sends an error message to the client before disconnecting the pipe.<br> </blockquote><div><br></div><div>May be also mention that the service never terminates openvpn.exe process -- not even</div><div>when it is stopped or restarted. So its up to the client to monitor the process</div><div>and terminate it.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">+<br> +<br> +Error Messages<br> +--------------<br> +<br> +In case of an error, the Interactive Service sends an error message to the<br> +client. Error messages are comprised of three UTF-16 strings delimited by LFs<br> +(U000A)::<br> +<br> + errmsg = L"0x" errnum WLF func WLF msg<br> +<br> + errnum = 8*8WHEXDIG<br> + func = WSTRING<br> + msg = WSTRING<br> +<br> +``errnum``<br> +<br> + An UTF-16 eight-character hexadecimal error code. Typically, it is one of the<br></blockquote><div><br></div><div>An --> A </div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> + Win32 error codes returned by ``GetLastError()``.<br> +<br> + However, it can be one of the Interactive Service specific error codes:<br> +<br> + ===================== ==========<br> + Error Code<br> + ===================== ==========<br> + ERROR_OPENVPN_STARTUP 0x20000000<br> + ERROR_STARTUP_DATA 0x20000001<br> + ERROR_MESSAGE_DATA 0x20000002<br> + ERROR_MESSAGE_TYPE 0x20000003<br> + ===================== ==========<br> +<br> +``func``<br> + The name of the function call that failed or an error description.<br> +<br> +``msg``<br> + The error description returned by a<br> + ``FormatMessageW(FORMAT_MESSAG<wbr>E_FROM_SYSTEM, 0, errnum, ...)`` call.<br> +<br> +<br> +Interactive Service Configuration<br> +=============================<wbr>====<br> +<br> +The Interactive Service settings are read from the<br> +``HKEY_LOCAL_MACHINE\SOFTWARE<wbr>\OpenVPN`` registry key by default.<br> +<br> +All the following registry values are of the ``REG_SZ`` type:<br> +<br> +*Default*<br> + Installation folder (required)<br></blockquote><div><br></div><div>install_dir used below is undefined. Define it here?</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> +<br> +``exe_path``<br> + The absolute path to the openvpn.exe binary; defaults to<br> + ``install_dir "\bin\openvpn.exe"``.<br> +<br> +``config_dir``<br> + The path to the configuration folder; defaults to ``install_dir "\config"``.<br> +<br> +``priority``<br> + openvpn.exe process priority; one of the following strings:<br> +<br> + - ``"IDLE_PRIORITY_CLASS"``<br> + - ``"BELOW_NORMAL_PRIORITY_CLASS<wbr>"``<br> + - ``"NORMAL_PRIORITY_CLASS"`` (default)<br> + - ``"ABOVE_NORMAL_PRIORITY_CLASS<wbr>"``<br> + - ``"HIGH_PRIORITY_CLASS"``<br> +<br> +``ovpn_admin_group``<br> + The name of the local group, whose members are authorized to use the<br> + Interactive Service unrestricted; defaults to ``"OpenVPN Administrators"``<br> +<br> +<br> +Multiple Interactive Service Instances<br> +=============================<wbr>=========<br> +<br> +OpenVPN 2.4.5 extended the Interactive Service to support multiple side-by-side<br> +running instances. This allows clients to use different Interactive Service<br> +versions with different settings and/or openvpn.exe binary version on the same<br> +computer.<br> +<br> +OpenVPN installs the default Interactive Service instance only. The default<br> +instance is used by OpenVPN GUI client and also provides backward compatibility.<br> +<br> +<br> +Installing a Non-default Interactive Service Instance<br> +-----------------------------<wbr>------------------------<br> +<br> +1. Choose a unique instance name. For example: "$v2.5-test". The instance name<br> + is appended to the default registry path and service name. We choose to start<br> + it with a dollar "$" sign analogous to Microsoft SQL Server instance naming<br> + scheme. However, this is not imperative. </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> +<br> + Appending the name to the registry path and service name also implies the<br> + name cannot contain characters not allowed in Windows paths: "<", ">", double<br> + quote etc.<br> +<br> +2. Create an ``HKEY_LOCAL_MACHINE\SOFTWARE\<wbr>OpenVPN$v2.5-test`` registry key and<br> + configure the Interactive Service instance configuration appropriately.<br> +<br> + This allows using slightly or completely different settings from the default<br> + instance.<br> +<br> + See the `Interactive Service Configuration`_ chapter for the list of registry<br></blockquote><div><br></div><div>chapter --> section ? And is that "_" a part of markup or a stale character?</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> + values.<br> +<br> +3. Create and start the instance's Windows service from an elevated command<br> + prompt::<br> +<br> + sc create "OpenVPNServiceInteractive$v2.<wbr>5-test" \<br> + start= auto \<br> + binPath= "<path to openvpnserv.exe> -instance interactive $v2.5-test" \<br> + depend= tap0901/Dhcp \<br> + DisplayName= "OpenVPN Interactive Service (v2.5-test)"<br> +<br> + sc start "OpenVPNServiceInteractive$v2.<wbr>5-test"<br> +<br> + This allows using the same or a different version of openvpnserv.exe than the<br> + default instance.<br> +<br> + Please, mind the spacing around "=" character in ``sc`` command line options.<br></blockquote><div><br></div><div>Please, mind the spacing around "=" --> Notice the space after "=" </div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> +<br> +4. Set your OpenVPN client to connect to the<br> + ``\\.\pipe\openvpn$v2.5-test\<wbr>service``.<br> +<br> + This allows the client to select different installed Interactive Service<br></blockquote><div><br></div><div>different --> a different</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> + instance at run-time, thus allowing different OpenVPN settings and versions.<br> +<br> + At the time writing, the OpenVPN GUI client supports connecting to the<br> + default Interactive Service instance only.<br> +<br> +.. _`Gert's email`: <a href="https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg00097.html" rel="noreferrer" target="_blank">https://www.mail-archive.com/o<wbr>penvpn-devel@lists.sourceforge<wbr>.net/msg00097.html</a><br> --<br> 2.15.1 (Apple Git-101)<br><br></blockquote><div><br></div><div>Do you also plan to document the service <--> openvpn.exe communication?</div><div>Or is that beyond the scope of this file? Anyway, could be a separate patch :)</div><div><br></div><div>Thanks,</div><div><br></div><div>Selva </div></div></div></div></div> ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot
diff --git a/doc/interactive-service-notes.txt b/doc/interactive-service-notes.txt new file mode 100644 index 00000000..9b3b8f6c --- /dev/null +++ b/doc/interactive-service-notes.txt @@ -0,0 +1,316 @@ +================================= +OpenVPN Interactive Service Notes +================================= + + +Introduction +============ + +OpenVPN Interactive Service, also known as "iservice" or +"OpenVPNServiceInteractive", is a Windows system service which allows +unprivileged users to do certain privileged operations required by OpenVPN, such +as adding routes. This removes the need to always run OpenVPN as administrator, +which was the case for a long time, and continues to be the case for OpenVPN +2.3.x. + +The 2.4.x release and git "master" versions of OpenVPN contain the Interactive +Service code and OpenVPN-GUI is setup to use it by default. Starting from +version 2.4.0, OpenVPN-GUI is expected to be started as user (do not right-click +and "run as administrator" or do not set the shortcut to run as administrator). +This ensures that OpenVPN and the GUI run with limited privileges. + + +How It Works +============ + +Here is a brief explanation of how the Interactive Service works, based on +`Gert's email`_ to openvpn-devel mailing list. The example user, *joe*, is not +an administrator, and does not have any other extra privileges. + +- OpenVPN-GUI runs as a *joe* +- Interactive Service runs as a local Windows service with maximum privileges +- OpenVPN-GUI connects to the Interactive Service and asks it "run openvpn.exe + with the following arguments, using the *joe*'s credentials" - Windows can do + this - pass credentials across a pipe, which you can't fake +- Interactive Service forks openvpn.exe, and runs this as the user *joe*, and + keeps a "service pipe" between Interactive Service and openvpn.exe +- If openvpn.exe wants to do ipconfig/route/dns stuff, it sends these as + requests over the service pipe to the Interactive Service, which will then + execute them (and clean up should openvpn.exe crash) +- ``--up`` scripts are run by openvpn.exe itself, which is already running as + *joe*, all privileges are nicely in place +- Scripts run by the GUI will run as user *joe*, so that automated tasks like + mapping of drives work as expected + +This also avoids the use of scripts for privilege escalation to admin (as was +possible by running an ``--up`` script from openvpn.exe which is run as admin). + + +Client-Service Communication +============================ + +Connecting +---------- + +The client (OpenVPN GUI) and the Interactive Service communicate using a named +message pipe. By default, the service provides the ``\\.\pipe\openvpn\service`` +named pipe. + +The client connects to the pipe for read/write and sets the pipe state to the +``PIPE_READMODE_MESSAGE``:: + + HANDLE pipe = CreateFile(_T("\\\\.\\pipe\\openvpn\\service"), + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + if (pipe == INVALID_HANDLE_VALUE) + { + // Error + } + + DWORD dwMode = PIPE_READMODE_MESSAGE; + if (!SetNamedPipeHandleState(pipe, &dwMode, NULL, NULL) + { + // Error + } + + +openvpn.exe Startup +------------------- + +After the client is connected to the service, the client must send a startup +message to have the service start the openvpn.exe process. The startup message +is comprised of three UTF-16 strings delimited by U0000 zero characters:: + + startupmsg = workingdir WZERO openvpnoptions WZERO stdin WZERO + + workingdir = WSTRING + openvpnoptions = WSTRING + stdin = WSTRING + + WSTRING = *WCHAR + WCHAR = %x0001-FFFF + WZERO = %x0000 + +``workingdir`` + Represents the folder openvpn.exe process should be started in. + +``openvpnoptions`` + String contains ``--config`` and other OpenVPN command line options, without + the ``argv[0]`` executable name ("openvpn" or "openvpn.exe"). When there is + only one option specified, the ``--config`` option is assumed and the option + is the configuration filename. + + Please, note that the interactive service validates the options (e.g. OpenVPN + config file must reside in one of the approved folders, or the invoking user + must be a member of local Administrators group, or a member of the + "OpenVPN Administrators" group). + +``stdin`` + The content of the ``stdin`` string is sent to the openvpn.exe process to its + stdin stream after it starts. + + When a ``--management ... stdin`` option is present, the openvpn.exe process + will prompt for the management interface password on start. In this case, the + ``stdin`` must contain the password appended with an LF (U000A) to simulate + the [Enter] key after the password is "typed" in. + + The openvpn.exe's stdout is redirected to ``NUL``. Should the client require + openvpn.exe's stdout, one should specify ``--log`` option. + +The message must be written in a single ``WriteFile()`` call. + +Example:: + + // Prepare the message. + size_t msg_len = + wcslen(workingdir) + 1 + + wcslen(options ) + 1 + + wcslen(manage_pwd) + 1; + wchar_t *msg_data = (wchar_t*)malloc(msg_len*sizeof(wchar_t)); + _snwprintf(msg_data, msg_len, L"%s%c%s%c%s", + workingdir, L'\0', + options, L'\0', + manage_pwd) + + // Send the message. + DWORD dwBytesWritten; + if (!WriteFile(pipe, + msg_data, + msg_len*sizeof(wchar_t), + &dwBytesWritten, + NULL)) + { + // Error + } + + // Sanitize memory, since the stdin component of the message + // contains the management interface password. + SecureZeroMemory(msg_data, msg_len*sizeof(wchar_t)); + free(msg_data); + + +openvpn.exe Process ID +---------------------- + +After receiving the startup message, the Interactive Service validates the user +and specified options before launching the openvpn.exe process. + +The Interactive Service replies with a process ID message. The process ID +message is comprised of three UTF-16 strings delimited by LFs (U000A):: + + pidmsg = L"0x00000000" WLF L"0x" pid WLF L"Process ID" + + pid = 8*8WHEXDIG + + WHEXDIG = WDIGIT / L"A" / L"B" / L"C" / L"D" / L"E" / L"F" + WDIGIT = %x0030-0039 + WLF = %x000a + +``pid`` + An UTF-16 eight-character hexadecimal process ID of the openvpn.exe process + the Interactive Service launched on client's behalf. + + +openvpn.exe Termination +----------------------- + +The pipe between the client and the service is kept open until the openvpn.exe +process terminates. + +Should the openvpn.exe process terminate with an error, the Interactive Service +sends an error message to the client before disconnecting the pipe. + + +Error Messages +-------------- + +In case of an error, the Interactive Service sends an error message to the +client. Error messages are comprised of three UTF-16 strings delimited by LFs +(U000A):: + + errmsg = L"0x" errnum WLF func WLF msg + + errnum = 8*8WHEXDIG + func = WSTRING + msg = WSTRING + +``errnum`` + + An UTF-16 eight-character hexadecimal error code. Typically, it is one of the + Win32 error codes returned by ``GetLastError()``. + + However, it can be one of the Interactive Service specific error codes: + + ===================== ========== + Error Code + ===================== ========== + ERROR_OPENVPN_STARTUP 0x20000000 + ERROR_STARTUP_DATA 0x20000001 + ERROR_MESSAGE_DATA 0x20000002 + ERROR_MESSAGE_TYPE 0x20000003 + ===================== ========== + +``func`` + The name of the function call that failed or an error description. + +``msg`` + The error description returned by a + ``FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, errnum, ...)`` call. + + +Interactive Service Configuration +================================= + +The Interactive Service settings are read from the +``HKEY_LOCAL_MACHINE\SOFTWARE\OpenVPN`` registry key by default. + +All the following registry values are of the ``REG_SZ`` type: + +*Default* + Installation folder (required) + +``exe_path`` + The absolute path to the openvpn.exe binary; defaults to + ``install_dir "\bin\openvpn.exe"``. + +``config_dir`` + The path to the configuration folder; defaults to ``install_dir "\config"``. + +``priority`` + openvpn.exe process priority; one of the following strings: + + - ``"IDLE_PRIORITY_CLASS"`` + - ``"BELOW_NORMAL_PRIORITY_CLASS"`` + - ``"NORMAL_PRIORITY_CLASS"`` (default) + - ``"ABOVE_NORMAL_PRIORITY_CLASS"`` + - ``"HIGH_PRIORITY_CLASS"`` + +``ovpn_admin_group`` + The name of the local group, whose members are authorized to use the + Interactive Service unrestricted; defaults to ``"OpenVPN Administrators"`` + + +Multiple Interactive Service Instances +====================================== + +OpenVPN 2.4.5 extended the Interactive Service to support multiple side-by-side +running instances. This allows clients to use different Interactive Service +versions with different settings and/or openvpn.exe binary version on the same +computer. + +OpenVPN installs the default Interactive Service instance only. The default +instance is used by OpenVPN GUI client and also provides backward compatibility. + + +Installing a Non-default Interactive Service Instance +----------------------------------------------------- + +1. Choose a unique instance name. For example: "$v2.5-test". The instance name + is appended to the default registry path and service name. We choose to start + it with a dollar "$" sign analogous to Microsoft SQL Server instance naming + scheme. However, this is not imperative. + + Appending the name to the registry path and service name also implies the + name cannot contain characters not allowed in Windows paths: "<", ">", double + quote etc. + +2. Create an ``HKEY_LOCAL_MACHINE\SOFTWARE\OpenVPN$v2.5-test`` registry key and + configure the Interactive Service instance configuration appropriately. + + This allows using slightly or completely different settings from the default + instance. + + See the `Interactive Service Configuration`_ chapter for the list of registry + values. + +3. Create and start the instance's Windows service from an elevated command + prompt:: + + sc create "OpenVPNServiceInteractive$v2.5-test" \ + start= auto \ + binPath= "<path to openvpnserv.exe> -instance interactive $v2.5-test" \ + depend= tap0901/Dhcp \ + DisplayName= "OpenVPN Interactive Service (v2.5-test)" + + sc start "OpenVPNServiceInteractive$v2.5-test" + + This allows using the same or a different version of openvpnserv.exe than the + default instance. + + Please, mind the spacing around "=" character in ``sc`` command line options. + +4. Set your OpenVPN client to connect to the + ``\\.\pipe\openvpn$v2.5-test\service``. + + This allows the client to select different installed Interactive Service + instance at run-time, thus allowing different OpenVPN settings and versions. + + At the time writing, the OpenVPN GUI client supports connecting to the + default Interactive Service instance only. + +.. _`Gert's email`: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg00097.html