OpenVPN
simple.c
Go to the documentation of this file.
1 /*
2  * OpenVPN -- An application to securely tunnel IP networks
3  * over a single TCP/UDP port, with support for SSL/TLS-based
4  * session authentication and key exchange,
5  * packet encryption, packet authentication, and
6  * packet compression.
7  *
8  * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2
12  * as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 /*
25  * This file implements a simple OpenVPN plugin module which
26  * will test deferred authentication and packet filtering.
27  *
28  * Will run on Windows or *nix.
29  *
30  * Sample usage:
31  *
32  * setenv test_deferred_auth 20
33  * setenv test_packet_filter 10
34  * plugin plugin/defer/simple.so
35  *
36  * This will enable deferred authentication to occur 20
37  * seconds after the normal TLS authentication process,
38  * and will cause a packet filter file to be generated 10
39  * seconds after the initial TLS negotiation, using
40  * {common-name}.pf as the source.
41  *
42  * Sample packet filter configuration:
43  *
44  * [CLIENTS DROP]
45  * +otherclient
46  * [SUBNETS DROP]
47  * +10.0.0.0/8
48  * -10.10.0.8
49  * [END]
50  *
51  * See the README file for build instructions.
52  */
53 
54 #include <stdio.h>
55 #include <string.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58 #include <stdbool.h>
59 #include <fcntl.h>
60 #include <sys/types.h>
61 #include <sys/wait.h>
62 
63 #include "openvpn-plugin.h"
64 
65 /* Pointers to functions exported from openvpn */
66 static plugin_log_t plugin_log = NULL;
67 
68 /*
69  * Constants indicating minimum API and struct versions by the functions
70  * in this plugin. Consult openvpn-plugin.h, look for:
71  * OPENVPN_PLUGIN_VERSION and OPENVPN_PLUGINv3_STRUCTVER
72  *
73  * Strictly speaking, this sample code only requires plugin_log, a feature
74  * of structver version 1. However, '1' lines up with ancient versions
75  * of openvpn that are past end-of-support. As such, we are requiring
76  * structver '5' here to indicate a desire for modern openvpn, rather
77  * than a need for any particular feature found in structver beyond '1'.
78  */
79 #define OPENVPN_PLUGIN_VERSION_MIN 3
80 #define OPENVPN_PLUGIN_STRUCTVER_MIN 5
81 
82 /*
83  * Our context, where we keep our state.
84  */
85 
86 struct plugin_context {
89 };
90 
92  int n_calls;
94 };
95 
96 /* module name for plugin_log() */
97 static char *MODULE = "defer/simple";
98 
99 /*
100  * Given an environmental variable name, search
101  * the envp array for its value, returning it
102  * if found or NULL otherwise.
103  */
104 static const char *
105 get_env(const char *name, const char *envp[])
106 {
107  if (envp)
108  {
109  int i;
110  const int namelen = strlen(name);
111  for (i = 0; envp[i]; ++i)
112  {
113  if (!strncmp(envp[i], name, namelen))
114  {
115  const char *cp = envp[i] + namelen;
116  if (*cp == '=')
117  {
118  return cp + 1;
119  }
120  }
121  }
122  }
123  return NULL;
124 }
125 
126 /* used for safe printf of possible NULL strings */
127 static const char *
128 np(const char *str)
129 {
130  if (str)
131  {
132  return str;
133  }
134  else
135  {
136  return "[NULL]";
137  }
138 }
139 
140 static int
141 atoi_null0(const char *str)
142 {
143  if (str)
144  {
145  return atoi(str);
146  }
147  else
148  {
149  return 0;
150  }
151 }
152 
153 /* Require a minimum OpenVPN Plugin API */
154 OPENVPN_EXPORT int
156 {
158 }
159 
160 /* use v3 functions so we can use openvpn's logging and base64 etc. */
161 OPENVPN_EXPORT int
162 openvpn_plugin_open_v3(const int v3structver,
163  struct openvpn_plugin_args_open_in const *args,
165 {
166  const char **envp = args->envp; /* environment variables */
167  struct plugin_context *context;
168 
169  if (v3structver < OPENVPN_PLUGIN_STRUCTVER_MIN)
170  {
171  fprintf(stderr, "%s: this plugin is incompatible with the running version of OpenVPN\n", MODULE);
173  }
174 
175  /* Save global pointers to functions exported from openvpn */
177 
178  plugin_log(PLOG_NOTE, MODULE, "FUNC: openvpn_plugin_open_v3");
179 
180  /*
181  * Allocate our context
182  */
183  context = (struct plugin_context *) calloc(1, sizeof(struct plugin_context));
184  if (!context)
185  {
186  goto error;
187  }
188 
189  context->test_deferred_auth = atoi_null0(get_env("test_deferred_auth", envp));
190  plugin_log(PLOG_NOTE, MODULE, "TEST_DEFERRED_AUTH %d", context->test_deferred_auth);
191 
192  context->test_packet_filter = atoi_null0(get_env("test_packet_filter", envp));
193  plugin_log(PLOG_NOTE, MODULE, "TEST_PACKET_FILTER %d", context->test_packet_filter);
194 
195  /*
196  * Which callbacks to intercept.
197  */
198  ret->type_mask =
209 
210  ret->handle = (openvpn_plugin_handle_t *) context;
211  plugin_log(PLOG_NOTE, MODULE, "initialization succeeded");
213 
214 error:
215  if (context)
216  {
217  free(context);
218  }
219  plugin_log(PLOG_NOTE, MODULE, "initialization failed");
221 }
222 
223 static int
225  struct plugin_per_client_context *pcc,
226  const char *argv[], const char *envp[])
227 {
228  if (!context->test_deferred_auth)
229  {
231  }
232 
233  /* get username/password from envp string array */
234  const char *username = get_env("username", envp);
235  const char *password = get_env("password", envp);
236 
237  /* get auth_control_file filename from envp string array*/
238  const char *auth_control_file = get_env("auth_control_file", envp);
239 
240  plugin_log(PLOG_NOTE, MODULE, "DEFER u='%s' p='%s' acf='%s'",
241  np(username),
242  np(password),
243  np(auth_control_file));
244 
245  /* Authenticate asynchronously in n seconds */
246  if (!auth_control_file)
247  {
249  }
250 
251  /* we do not want to complicate our lives with having to wait()
252  * for child processes (so they are not zombiefied) *and* we MUST NOT
253  * fiddle with signal handlers (= shared with openvpn main), so
254  * we use double-fork() trick.
255  */
256 
257  /* fork, sleep, succeed (no "real" auth done = always succeed) */
258  pid_t p1 = fork();
259  if (p1 < 0) /* Fork failed */
260  {
262  }
263  if (p1 > 0) /* parent process */
264  {
265  waitpid(p1, NULL, 0);
267  }
268 
269  /* first gen child process, fork() again and exit() right away */
270  pid_t p2 = fork();
271  if (p2 < 0)
272  {
273  plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: fork(2) failed");
274  exit(1);
275  }
276 
277  if (p2 != 0) /* new parent: exit right away */
278  {
279  exit(0);
280  }
281 
282  /* (grand-)child process
283  * - never call "return" now (would mess up openvpn)
284  * - return status is communicated by file
285  * - then exit()
286  */
287 
288  /* do mighty complicated work that will really take time here... */
289  plugin_log(PLOG_NOTE, MODULE, "in async/deferred handler, sleep(%d)", context->test_deferred_auth);
290  sleep(context->test_deferred_auth);
291 
292  /* now signal success state to openvpn */
293  int fd = open(auth_control_file, O_WRONLY);
294  if (fd < 0)
295  {
296  plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "open('%s') failed", auth_control_file);
297  exit(1);
298  }
299 
300  plugin_log(PLOG_NOTE, MODULE, "auth_user_pass_verify: done" );
301 
302  if (write(fd, "1", 1) != 1)
303  {
304  plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "write to '%s' failed", auth_control_file );
305  }
306  close(fd);
307 
308  exit(0);
309 }
310 
311 OPENVPN_EXPORT int
312 openvpn_plugin_func_v3(const int v3structver,
313  struct openvpn_plugin_args_func_in const *args,
315 {
316  if (v3structver < OPENVPN_PLUGIN_STRUCTVER_MIN)
317  {
318  fprintf(stderr, "%s: this plugin is incompatible with the running version of OpenVPN\n", MODULE);
320  }
321  const char **argv = args->argv;
322  const char **envp = args->envp;
323  struct plugin_context *context = (struct plugin_context *) args->handle;
325  switch (args->type)
326  {
327  case OPENVPN_PLUGIN_UP:
328  plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_UP");
330 
331  case OPENVPN_PLUGIN_DOWN:
332  plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_DOWN");
334 
336  plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_ROUTE_UP");
338 
340  plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_IPCHANGE");
342 
344  plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_TLS_VERIFY");
346 
348  plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY");
349  return auth_user_pass_verify(context, pcc, argv, envp);
350 
352  plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_CLIENT_CONNECT_V2");
354 
356  plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_CLIENT_DISCONNECT");
358 
360  plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_LEARN_ADDRESS");
362 
364  plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_TLS_FINAL");
366 
367  default:
368  plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_?");
370  }
371 }
372 
373 OPENVPN_EXPORT void *
375 {
376  plugin_log(PLOG_NOTE, MODULE, "FUNC: openvpn_plugin_client_constructor_v1");
377  return calloc(1, sizeof(struct plugin_per_client_context));
378 }
379 
380 OPENVPN_EXPORT void
382 {
383  plugin_log(PLOG_NOTE, MODULE, "FUNC: openvpn_plugin_client_destructor_v1");
384  free(per_client_context);
385 }
386 
387 OPENVPN_EXPORT void
389 {
390  struct plugin_context *context = (struct plugin_context *) handle;
391  plugin_log(PLOG_NOTE, MODULE, "FUNC: openvpn_plugin_close_v1");
392  free(context);
393 }
Arguments used to transport variables to the plug-in.
OPENVPN_EXPORT void openvpn_plugin_client_destructor_v1(openvpn_plugin_handle_t handle, void *per_client_context)
Definition: simple.c:381
static const char * get_env(const char *name, const char *envp[])
Definition: simple.c:105
static int auth_user_pass_verify(struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[])
Definition: simple.c:224
const char * password
Definition: log.c:42
Contains all state information for one tunnel.
Definition: openvpn.h:461
#define OPENVPN_PLUGIN_VERSION_MIN
Definition: simple.c:79
openvpn_plugin_handle_t * handle
#define OPENVPN_PLUGIN_DOWN
#define OPENVPN_PLUGIN_ROUTE_UP
int test_deferred_auth
Definition: simple.c:87
#define OPENVPN_EXPORT
#define OPENVPN_PLUGIN_FUNC_SUCCESS
OPENVPN_EXPORT int openvpn_plugin_min_version_required_v1()
Definition: simple.c:155
#define OPENVPN_PLUGIN_LEARN_ADDRESS
const char * username
Definition: log.c:41
#define OPENVPN_PLUGIN_IPCHANGE
OPENVPN_EXPORT int openvpn_plugin_open_v3(const int v3structver, struct openvpn_plugin_args_open_in const *args, struct openvpn_plugin_args_open_return *ret)
Definition: simple.c:162
#define OPENVPN_PLUGIN_FUNC_DEFERRED
#define OPENVPN_PLUGIN_TLS_VERIFY
#define OPENVPN_PLUGIN_FUNC_ERROR
static const char * np(const char *str)
Definition: simple.c:128
OPENVPN_EXPORT int openvpn_plugin_func_v3(const int v3structver, struct openvpn_plugin_args_func_in const *args, struct openvpn_plugin_args_func_return *ret)
Definition: simple.c:312
#define sleep(x)
Definition: syshead.h:43
Arguments used to transport variables to and from the plug-in.
static plugin_log_t plugin_log
Definition: simple.c:66
#define OPENVPN_PLUGIN_TLS_FINAL
openvpn_plugin_handle_t handle
void(* plugin_log_t)(openvpn_plugin_log_flags_t flags, const char *plugin_name, const char *format,...) _ovpn_chk_fmt(3
#define OPENVPN_PLUGIN_CLIENT_DISCONNECT
void * openvpn_plugin_handle_t
OPENVPN_EXPORT void openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
Definition: simple.c:388
static int atoi_null0(const char *str)
Definition: simple.c:141
Arguments used to transport variables to and from the plug-in.
#define OPENVPN_PLUGIN_STRUCTVER_MIN
Definition: simple.c:80
#define OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
static char * MODULE
Definition: simple.c:97
struct openvpn_plugin_callbacks * callbacks
OPENVPN_EXPORT void * openvpn_plugin_client_constructor_v1(openvpn_plugin_handle_t handle)
Definition: simple.c:374
int test_packet_filter
Definition: simple.c:88
#define free
Definition: cmocka.c:1850
Definition: argv.h:35
Arguments used to transport variables from the plug-in back to the OpenVPN process.
#define OPENVPN_PLUGIN_CLIENT_CONNECT_V2
#define OPENVPN_PLUGIN_UP
#define OPENVPN_PLUGIN_MASK(x)