Browse code

Add Interactive Service developer documentation

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.

Acked-by: Selva Nair <selva.nair@gmail.com>
Message-Id: <20180419112313.1013-1-simon@rozman.si>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg16794.html

Signed-off-by: Gert Doering <gert@greenie.muc.de>

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