interactive-service-notes.rst 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. OpenVPN Interactive Service Notes
  2. =================================
  3. Introduction
  4. ------------
  5. OpenVPN Interactive Service, also known as "iservice" or
  6. "OpenVPNServiceInteractive", is a Windows system service which allows
  7. unprivileged openvpn.exe process to do certain privileged operations, such as
  8. adding routes. This removes the need to always run OpenVPN as administrator,
  9. which was the case for a long time, and continues to be the case for OpenVPN
  10. 2.3.x.
  11. The 2.4.x release and git "master" versions of OpenVPN contain the Interactive
  12. Service code and OpenVPN-GUI is setup to use it by default. Starting from
  13. version 2.4.0, OpenVPN-GUI is expected to be started as user (do not right-click
  14. and "run as administrator" or do not set the shortcut to run as administrator).
  15. This ensures that OpenVPN and the GUI run with limited privileges.
  16. How It Works
  17. ------------
  18. Here is a brief explanation of how the Interactive Service works, based on
  19. `Gert's email`_ to openvpn-devel mailing list. The example user, *joe*, is not
  20. an administrator, and does not have any other extra privileges.
  21. - OpenVPN-GUI runs as user *joe*.
  22. - Interactive Service runs as a local Windows service with maximum privileges.
  23. - OpenVPN-GUI connects to the Interactive Service and asks it to "run
  24. openvpn.exe with the given command line options".
  25. - Interactive Service starts openvpn.exe process as user *joe*, and keeps a
  26. service pipe between Interactive Service and openvpn.exe.
  27. - When openvpn.exe wants to perform any operation that require elevation (e.g.
  28. ipconfig, route, configure DNS), it sends a request over the service pipe to
  29. the Interactive Service, which will then execute it (and clean up should
  30. openvpn.exe crash).
  31. - ``--up`` scripts are run by openvpn.exe itself, which is running as user
  32. *joe*, all privileges are nicely in place.
  33. - Scripts run by the GUI will run as user *joe*, so that automated tasks like
  34. mapping of drives work as expected.
  35. This avoids the use of scripts for privilege escalation (as was possible by
  36. running an ``--up`` script from openvpn.exe which is run as administrator).
  37. Client-Service Communication
  38. ----------------------------
  39. Connecting
  40. ~~~~~~~~~~
  41. The client (OpenVPN GUI) and the Interactive Service communicate using a named
  42. message pipe. By default, the service provides the ``\\.\pipe\openvpn\service``
  43. named pipe.
  44. The client connects to the pipe for read/write and sets the pipe state to
  45. ``PIPE_READMODE_MESSAGE``::
  46. HANDLE pipe = CreateFile(_T("\\\\.\\pipe\\openvpn\\service"),
  47. GENERIC_READ | GENERIC_WRITE,
  48. 0,
  49. NULL,
  50. OPEN_EXISTING,
  51. FILE_FLAG_OVERLAPPED,
  52. NULL);
  53. if (pipe == INVALID_HANDLE_VALUE)
  54. {
  55. // Error
  56. }
  57. DWORD dwMode = PIPE_READMODE_MESSAGE;
  58. if (!SetNamedPipeHandleState(pipe, &dwMode, NULL, NULL)
  59. {
  60. // Error
  61. }
  62. openvpn.exe Startup
  63. ~~~~~~~~~~~~~~~~~~~
  64. After the client is connected to the service, the client must send a startup
  65. message to have the service start the openvpn.exe process. The startup message
  66. is comprised of three UTF-16 strings delimited by U0000 zero characters::
  67. startupmsg = workingdir WZERO openvpnoptions WZERO stdin WZERO
  68. workingdir = WSTRING
  69. openvpnoptions = WSTRING
  70. stdin = WSTRING
  71. WSTRING = *WCHAR
  72. WCHAR = %x0001-FFFF
  73. WZERO = %x0000
  74. ``workingdir``
  75. Represents the folder openvpn.exe process should be started in.
  76. ``openvpnoptions``
  77. String contains ``--config`` and other OpenVPN command line options, without
  78. the ``argv[0]`` executable name ("openvpn" or "openvpn.exe"). When there is
  79. only one option specified, the ``--config`` option is assumed and the option
  80. is the configuration filename.
  81. Note that the interactive service validates the options. OpenVPN
  82. configuration file must reside in the configuration folder defined by
  83. ``config_dir`` registry value. The configuration file can also reside in any
  84. subfolder of the configuration folder. For all other folders the invoking
  85. user must be a member of local Administrators group, or a member of the group
  86. defined by ``ovpn_admin_group`` registry value ("OpenVPN Administrators" by
  87. default).
  88. ``stdin``
  89. The content of the ``stdin`` string is sent to the openvpn.exe process to its
  90. stdin stream after it starts.
  91. When a ``--management ... stdin`` option is present, the openvpn.exe process
  92. will prompt for the management interface password on start. In this case, the
  93. ``stdin`` must contain the password appended with an LF (U000A) to simulate
  94. the [Enter] key after the password is "typed" in.
  95. The openvpn.exe's stdout is redirected to ``NUL``. Should the client require
  96. openvpn.exe's stdout, one should specify ``--log`` option.
  97. The message must be written in a single ``WriteFile()`` call.
  98. Example::
  99. // Prepare the message.
  100. size_t msg_len =
  101. wcslen(workingdir) + 1 +
  102. wcslen(options ) + 1 +
  103. wcslen(manage_pwd) + 1;
  104. wchar_t *msg_data = (wchar_t*)malloc(msg_len*sizeof(wchar_t));
  105. _snwprintf(msg_data, msg_len, L"%s%c%s%c%s",
  106. workingdir, L'\0',
  107. options, L'\0',
  108. manage_pwd)
  109. // Send the message.
  110. DWORD dwBytesWritten;
  111. if (!WriteFile(pipe,
  112. msg_data,
  113. msg_len*sizeof(wchar_t),
  114. &dwBytesWritten,
  115. NULL))
  116. {
  117. // Error
  118. }
  119. // Sanitize memory, since the stdin component of the message
  120. // contains the management interface password.
  121. SecureZeroMemory(msg_data, msg_len*sizeof(wchar_t));
  122. free(msg_data);
  123. openvpn.exe Process ID
  124. ~~~~~~~~~~~~~~~~~~~~~~
  125. After receiving the startup message, the Interactive Service validates the user
  126. and specified options before launching the openvpn.exe process.
  127. The Interactive Service replies with a process ID message. The process ID
  128. message is comprised of three UTF-16 strings delimited by LFs (U000A)::
  129. pidmsg = L"0x00000000" WLF L"0x" pid WLF L"Process ID"
  130. pid = 8*8WHEXDIG
  131. WHEXDIG = WDIGIT / L"A" / L"B" / L"C" / L"D" / L"E" / L"F"
  132. WDIGIT = %x0030-0039
  133. WLF = %x000a
  134. ``pid``
  135. A UTF-16 eight-character hexadecimal process ID of the openvpn.exe process
  136. the Interactive Service launched on client's behalf.
  137. openvpn.exe Monitoring and Termination
  138. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  139. After the openvpn.exe process is launched, the client can disconnect the pipe to
  140. the interactive service. However, it should monitor the openvpn.exe process
  141. itself. OpenVPN Management Interface is recommended for this.
  142. The client may choose to stay connected to the pipe. When the openvpn.exe
  143. process terminates, the service disconnects the pipe. Should the openvpn.exe
  144. process terminate with an error, the service sends an error message to the
  145. client before disconnecting the pipe.
  146. Note that Interactive Service terminates all child openvpn.exe processes when
  147. the service is stopped or restarted. This allows a graceful elevation-required
  148. clean-up (e.g. restore ipconfig, route, DNS).
  149. Error Messages
  150. ~~~~~~~~~~~~~~
  151. In case of an error, the Interactive Service sends an error message to the
  152. client. Error messages are comprised of three UTF-16 strings delimited by LFs
  153. (U000A)::
  154. errmsg = L"0x" errnum WLF func WLF msg
  155. errnum = 8*8WHEXDIG
  156. func = WSTRING
  157. msg = WSTRING
  158. ``errnum``
  159. A UTF-16 eight-character hexadecimal error code. Typically, it is one of the
  160. Win32 error codes returned by ``GetLastError()``.
  161. However, it can be one of the Interactive Service specific error codes:
  162. ===================== ==========
  163. Error Code
  164. ===================== ==========
  165. ERROR_OPENVPN_STARTUP 0x20000000
  166. ERROR_STARTUP_DATA 0x20000001
  167. ERROR_MESSAGE_DATA 0x20000002
  168. ERROR_MESSAGE_TYPE 0x20000003
  169. ===================== ==========
  170. ``func``
  171. The name of the function call that failed or an error description.
  172. ``msg``
  173. The error description returned by a
  174. ``FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, errnum, ...)`` call.
  175. Interactive Service Configuration
  176. ---------------------------------
  177. The Interactive Service settings are read from the
  178. ``HKEY_LOCAL_MACHINE\SOFTWARE\OpenVPN`` registry key by default.
  179. All the following registry values are of the ``REG_SZ`` type:
  180. *Default*
  181. Installation folder (required, hereinafter ``install_dir``)
  182. ``exe_path``
  183. The absolute path to the openvpn.exe binary; defaults to
  184. ``install_dir "\bin\openvpn.exe"``.
  185. ``config_dir``
  186. The path to the configuration folder; defaults to ``install_dir "\config"``.
  187. ``priority``
  188. openvpn.exe process priority; one of the following strings:
  189. - ``"IDLE_PRIORITY_CLASS"``
  190. - ``"BELOW_NORMAL_PRIORITY_CLASS"``
  191. - ``"NORMAL_PRIORITY_CLASS"`` (default)
  192. - ``"ABOVE_NORMAL_PRIORITY_CLASS"``
  193. - ``"HIGH_PRIORITY_CLASS"``
  194. ``ovpn_admin_group``
  195. The name of the local group, whose members are authorized to use the
  196. Interactive Service unrestricted; defaults to ``"OpenVPN Administrators"``
  197. Multiple Interactive Service Instances
  198. --------------------------------------
  199. OpenVPN 2.4.5 extended the Interactive Service to support multiple side-by-side
  200. running instances. This allows clients to use different Interactive Service
  201. versions with different settings and/or openvpn.exe binary version on the same
  202. computer.
  203. OpenVPN installs the default Interactive Service instance only. The default
  204. instance is used by OpenVPN GUI client and also provides backward compatibility.
  205. Installing a Non-default Interactive Service Instance
  206. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  207. 1. Choose a unique instance name. For example: "$v2.5-test". The instance name
  208. is appended to the default registry path and service name. We choose to start
  209. it with a dollar "$" sign analogous to Microsoft SQL Server instance naming
  210. scheme. However, this is not imperative.
  211. Appending the name to the registry path and service name also implies the
  212. name cannot contain characters not allowed in Windows paths: "<", ">", double
  213. quote etc.
  214. 2. Create an ``HKEY_LOCAL_MACHINE\SOFTWARE\OpenVPN$v2.5-test`` registry key and
  215. configure the Interactive Service instance configuration appropriately.
  216. This allows using slightly or completely different settings from the default
  217. instance.
  218. See the `Interactive Service Configuration`_ section for the list of registry
  219. values.
  220. 3. Create and start the instance's Windows service from an elevated command
  221. prompt::
  222. sc create "OpenVPNServiceInteractive$v2.5-test" \
  223. start= auto \
  224. binPath= "<path to openvpnserv.exe> -instance interactive $v2.5-test" \
  225. depend= tap0901/Dhcp \
  226. DisplayName= "OpenVPN Interactive Service (v2.5-test)"
  227. sc start "OpenVPNServiceInteractive$v2.5-test"
  228. This allows using the same or a different version of openvpnserv.exe than the
  229. default instance.
  230. Note the space after "=" character in ``sc`` command line options.
  231. 4. Set your OpenVPN client to connect to the
  232. ``\\.\pipe\openvpn$v2.5-test\service``.
  233. This allows the client to select a different installed Interactive Service
  234. instance at run-time, thus allowing different OpenVPN settings and versions.
  235. At the time writing, the OpenVPN GUI client supports connecting to the
  236. default Interactive Service instance only.
  237. .. _`Gert's email`: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg00097.html