OpenVPN
multi-auth.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-2024 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  * can do either an instant authentication or a deferred auth.
27  * The purpose of this plug-in is to test multiple auth plugins
28  * in the same configuration file
29  *
30  * Plugin arguments:
31  *
32  * multi-auth.so LOG_ID DEFER_TIME USERNAME PASSWORD
33  *
34  * LOG_ID is just an ID string used to separate auth results in the log
35  * DEFER_TIME is the time to defer the auth. Set to 0 to return immediately
36  * USERNAME is the username for a valid authentication
37  * PASSWORD is the password for a valid authentication
38  *
39  * The DEFER_TIME time unit is in ms.
40  *
41  * Sample usage:
42  *
43  * plugin multi-auth.so MA_1 0 foo bar # Instant reply user:foo pass:bar
44  * plugin multi-auth.so MA_2 5000 fux bax # Defer 5 sec, user:fux pass: bax
45  *
46  */
47 #include "config.h"
48 #include <stdio.h>
49 #include <string.h>
50 #include <stdlib.h>
51 #include <stdarg.h>
52 #include <unistd.h>
53 #include <stdbool.h>
54 #include <fcntl.h>
55 #include <sys/types.h>
56 #include <sys/wait.h>
57 
58 #include "openvpn-plugin.h"
59 
60 static char *MODULE = "multi-auth";
61 
62 /*
63  * Our context, where we keep our state.
64  */
65 
66 struct plugin_context {
68  char *authid;
71 };
72 
73 /* local wrapping of the log function, to add more details */
75 static void
76 plog(const struct plugin_context *ctx, int flags, char *fmt, ...)
77 {
78  char logid[129];
79 
80  if (ctx && ctx->authid)
81  {
82  snprintf(logid, 128, "%s[%s]", MODULE, ctx->authid);
83  }
84  else
85  {
86  snprintf(logid, 128, "%s", MODULE);
87  }
88 
89  va_list arglist;
90  va_start(arglist, fmt);
91  _plugin_vlog_func(flags, logid, fmt, arglist);
92  va_end(arglist);
93 }
94 
95 
96 /*
97  * Constants indicating minimum API and struct versions by the functions
98  * in this plugin. Consult openvpn-plugin.h, look for:
99  * OPENVPN_PLUGIN_VERSION and OPENVPN_PLUGINv3_STRUCTVER
100  *
101  * Strictly speaking, this sample code only requires plugin_log, a feature
102  * of structver version 1. However, '1' lines up with ancient versions
103  * of openvpn that are past end-of-support. As such, we are requiring
104  * structver '5' here to indicate a desire for modern openvpn, rather
105  * than a need for any particular feature found in structver beyond '1'.
106  */
107 #define OPENVPN_PLUGIN_VERSION_MIN 3
108 #define OPENVPN_PLUGIN_STRUCTVER_MIN 5
109 
110 
112  int n_calls;
114 };
115 
116 
117 /*
118  * Given an environmental variable name, search
119  * the envp array for its value, returning it
120  * if found or NULL otherwise.
121  */
122 static const char *
123 get_env(const char *name, const char *envp[])
124 {
125  if (envp)
126  {
127  int i;
128  const int namelen = strlen(name);
129  for (i = 0; envp[i]; ++i)
130  {
131  if (!strncmp(envp[i], name, namelen))
132  {
133  const char *cp = envp[i] + namelen;
134  if (*cp == '=')
135  {
136  return cp + 1;
137  }
138  }
139  }
140  }
141  return NULL;
142 }
143 
144 /* used for safe printf of possible NULL strings */
145 static const char *
146 np(const char *str)
147 {
148  if (str)
149  {
150  return str;
151  }
152  else
153  {
154  return "[NULL]";
155  }
156 }
157 
158 static int
159 atoi_null0(const char *str)
160 {
161  if (str)
162  {
163  return atoi(str);
164  }
165  else
166  {
167  return 0;
168  }
169 }
170 
171 /* Require a minimum OpenVPN Plugin API */
172 OPENVPN_EXPORT int
174 {
176 }
177 
178 /* use v3 functions so we can use openvpn's logging and base64 etc. */
179 OPENVPN_EXPORT int
180 openvpn_plugin_open_v3(const int v3structver,
181  struct openvpn_plugin_args_open_in const *args,
183 {
184  if (v3structver < OPENVPN_PLUGIN_STRUCTVER_MIN)
185  {
186  fprintf(stderr, "%s: this plugin is incompatible with the running version of OpenVPN\n", MODULE);
188  }
189 
190  /* Save global pointers to functions exported from openvpn */
192 
193  plog(NULL, PLOG_NOTE, "FUNC: openvpn_plugin_open_v3");
194 
195  /*
196  * Allocate our context
197  */
198  struct plugin_context *context = NULL;
199  context = (struct plugin_context *) calloc(1, sizeof(struct plugin_context));
200  if (!context)
201  {
202  goto error;
203  }
204 
205  /* simple module argument parsing */
206  if ((args->argv[4]) && !args->argv[5])
207  {
208  context->authid = strdup(args->argv[1]);
209  context->test_deferred_auth = atoi_null0(args->argv[2]);
210  context->test_valid_user = strdup(args->argv[3]);
211  context->test_valid_pass = strdup(args->argv[4]);
212  }
213  else
214  {
215  plog(context, PLOG_ERR, "Too many arguments provided");
216  goto error;
217  }
218 
219  if (context->test_deferred_auth > 0)
220  {
221  plog(context, PLOG_NOTE, "TEST_DEFERRED_AUTH %d", context->test_deferred_auth);
222  }
223 
224  /*
225  * Which callbacks to intercept.
226  */
229 
230  plog(context, PLOG_NOTE, "initialization succeeded");
232 
233 error:
234  plog(context, PLOG_NOTE, "initialization failed");
235  if (context)
236  {
237  free(context);
238  }
240 }
241 
242 static bool
244  const char *username, const char *password)
245 {
247  "expect_user=%s, received_user=%s, expect_passw=%s, received_passw=%s",
248  np(context->test_valid_user),
249  np(username),
250  np(context->test_valid_pass),
251  np(password));
252 
253  if (context->test_valid_user && context->test_valid_pass)
254  {
255  if ((strcmp(context->test_valid_user, username) != 0)
256  || (strcmp(context->test_valid_pass, password) != 0))
257  {
259  "User/Password auth result: FAIL");
260  return false;
261  }
262  else
263  {
265  "User/Password auth result: PASS");
266  return true;
267  }
268  }
269  return false;
270 }
271 
272 
273 static int
275  struct plugin_per_client_context *pcc,
276  const char *argv[], const char *envp[])
277 {
278  /* get username/password from envp string array */
279  const char *username = get_env("username", envp);
280  const char *password = get_env("password", envp);
281 
282  if (!context->test_deferred_auth)
283  {
284  plog(context, PLOG_NOTE, "Direct authentication");
287  }
288 
289  /* get auth_control_file filename from envp string array*/
290  const char *auth_control_file = get_env("auth_control_file", envp);
291  plog(context, PLOG_NOTE, "auth_control_file=%s", auth_control_file);
292 
293  /* Authenticate asynchronously in n seconds */
294  if (!auth_control_file)
295  {
297  }
298 
299  /* we do not want to complicate our lives with having to wait()
300  * for child processes (so they are not zombiefied) *and* we MUST NOT
301  * fiddle with signal handlers (= shared with openvpn main), so
302  * we use double-fork() trick.
303  */
304 
305  /* fork, sleep, succeed (no "real" auth done = always succeed) */
306  pid_t p1 = fork();
307  if (p1 < 0) /* Fork failed */
308  {
310  }
311  if (p1 > 0) /* parent process */
312  {
313  waitpid(p1, NULL, 0);
315  }
316 
317  /* first gen child process, fork() again and exit() right away */
318  pid_t p2 = fork();
319  if (p2 < 0)
320  {
321  plog(context, PLOG_ERR|PLOG_ERRNO, "BACKGROUND: fork(2) failed");
322  exit(1);
323  }
324 
325  if (p2 != 0) /* new parent: exit right away */
326  {
327  exit(0);
328  }
329 
330  /* (grand-)child process
331  * - never call "return" now (would mess up openvpn)
332  * - return status is communicated by file
333  * - then exit()
334  */
335 
336  /* do mighty complicated work that will really take time here... */
337  plog(context, PLOG_NOTE, "in async/deferred handler, usleep(%d)",
338  context->test_deferred_auth*1000);
339  usleep(context->test_deferred_auth*1000);
340 
341  /* now signal success state to openvpn */
342  int fd = open(auth_control_file, O_WRONLY);
343  if (fd < 0)
344  {
346  "open('%s') failed", auth_control_file);
347  exit(1);
348  }
349 
350  char result[2] = "0\0";
352  {
353  result[0] = '1';
354  }
355 
356  if (write(fd, result, 1) != 1)
357  {
358  plog(context, PLOG_ERR|PLOG_ERRNO, "write to '%s' failed", auth_control_file );
359  }
360  close(fd);
361 
362  exit(0);
363 }
364 
365 
366 OPENVPN_EXPORT int
367 openvpn_plugin_func_v3(const int v3structver,
368  struct openvpn_plugin_args_func_in const *args,
370 {
371  if (v3structver < OPENVPN_PLUGIN_STRUCTVER_MIN)
372  {
373  fprintf(stderr, "%s: this plugin is incompatible with the running version of OpenVPN\n", MODULE);
375  }
376  const char **argv = args->argv;
377  const char **envp = args->envp;
378  struct plugin_context *context = (struct plugin_context *) args->handle;
380  switch (args->type)
381  {
383  plog(context, PLOG_NOTE, "OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY");
384  return auth_user_pass_verify(context, pcc, argv, envp);
385 
386  default:
387  plog(context, PLOG_NOTE, "OPENVPN_PLUGIN_?");
389  }
390 }
391 
392 OPENVPN_EXPORT void *
394 {
395  struct plugin_context *context = (struct plugin_context *) handle;
396  plog(context, PLOG_NOTE, "FUNC: openvpn_plugin_client_constructor_v1");
397  return calloc(1, sizeof(struct plugin_per_client_context));
398 }
399 
400 OPENVPN_EXPORT void
402 {
403  struct plugin_context *context = (struct plugin_context *) handle;
404  plog(context, PLOG_NOTE, "FUNC: openvpn_plugin_client_destructor_v1");
405  free(per_client_context);
406 }
407 
408 OPENVPN_EXPORT void
410 {
411  struct plugin_context *context = (struct plugin_context *) handle;
412  plog(context, PLOG_NOTE, "FUNC: openvpn_plugin_close_v1");
413  free(context);
414 }
plugin_context::test_valid_user
char * test_valid_user
Definition: multi-auth.c:69
PLOG_NOTE
@ PLOG_NOTE
Definition: openvpn-plugin.h:235
plugin_per_client_context::generated_pf_file
bool generated_pf_file
Definition: multi-auth.c:113
plugin_per_client_context::n_calls
int n_calls
Definition: multi-auth.c:112
openvpn_plugin_args_func_in::handle
openvpn_plugin_handle_t handle
Definition: openvpn-plugin.h:434
openvpn_plugin_client_destructor_v1
OPENVPN_EXPORT void openvpn_plugin_client_destructor_v1(openvpn_plugin_handle_t handle, void *per_client_context)
Definition: multi-auth.c:401
argv
Definition: argv.h:35
openvpn_plugin_args_open_in
Arguments used to transport variables to the plug-in.
Definition: openvpn-plugin.h:359
context
Contains all state information for one tunnel.
Definition: openvpn.h:476
plugin_context::test_valid_pass
char * test_valid_pass
Definition: multi-auth.c:70
plugin_context
Definition: sample-client-connect.c:62
OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
#define OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
Definition: openvpn-plugin.h:122
openvpn_plugin_args_open_return::type_mask
int type_mask
Definition: openvpn-plugin.h:396
OPENVPN_PLUGIN_STRUCTVER_MIN
#define OPENVPN_PLUGIN_STRUCTVER_MIN
Definition: multi-auth.c:108
plog
static void plog(const struct plugin_context *ctx, int flags, char *fmt,...)
Definition: multi-auth.c:76
OPENVPN_PLUGIN_MASK
#define OPENVPN_PLUGIN_MASK(x)
Definition: openvpn-plugin.h:137
openvpn_plugin_client_constructor_v1
OPENVPN_EXPORT void * openvpn_plugin_client_constructor_v1(openvpn_plugin_handle_t handle)
Definition: multi-auth.c:393
np
static const char * np(const char *str)
Definition: multi-auth.c:146
openvpn_plugin_args_func_in::type
const int type
Definition: openvpn-plugin.h:431
OPENVPN_PLUGIN_FUNC_ERROR
#define OPENVPN_PLUGIN_FUNC_ERROR
Definition: openvpn-plugin.h:149
PLOG_ERRNO
@ PLOG_ERRNO
Definition: openvpn-plugin.h:238
openvpn_plugin_open_v3
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: multi-auth.c:180
OPENVPN_PLUGIN_VERSION_MIN
#define OPENVPN_PLUGIN_VERSION_MIN
Definition: multi-auth.c:107
write
@ write
Definition: interactive.c:219
do_auth_user_pass
static bool do_auth_user_pass(struct plugin_context *context, const char *username, const char *password)
Definition: multi-auth.c:243
plugin_per_client_context
Definition: sample-client-connect.c:72
OPENVPN_EXPORT
#define OPENVPN_EXPORT
Definition: openvpn-plugin.h:156
atoi_null0
static int atoi_null0(const char *str)
Definition: multi-auth.c:159
OPENVPN_PLUGIN_FUNC_DEFERRED
#define OPENVPN_PLUGIN_FUNC_DEFERRED
Definition: openvpn-plugin.h:150
plugin_vlog_t
void(*) typedef void(* plugin_vlog_t)(openvpn_plugin_log_flags_t flags, const char *plugin_name, const char *format, va_list arglist) _ovpn_chk_fmt(3
Definition: openvpn-plugin.h:258
MODULE
static char * MODULE
Definition: multi-auth.c:60
get_env
static const char * get_env(const char *name, const char *envp[])
Definition: multi-auth.c:123
openvpn_plugin_args_func_in::per_client_context
void * per_client_context
Definition: openvpn-plugin.h:435
openvpn_plugin_args_func_in::envp
const char **const envp
Definition: openvpn-plugin.h:433
auth_user_pass_verify
static int auth_user_pass_verify(struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[])
Definition: multi-auth.c:274
openvpn_plugin_callbacks::plugin_vlog
plugin_vlog_t plugin_vlog
Definition: openvpn-plugin.h:320
PLOG_ERR
@ PLOG_ERR
Definition: openvpn-plugin.h:233
plugin_context::test_deferred_auth
int test_deferred_auth
Definition: multi-auth.c:67
openvpn_plugin_args_func_return
Arguments used to transport variables to and from the plug-in.
Definition: openvpn-plugin.h:452
openvpn_plugin_args_open_in::argv
const char **const argv
Definition: openvpn-plugin.h:362
openvpn_plugin_func_v3
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: multi-auth.c:367
plugin_context::password
const char * password
Definition: log.c:42
openvpn_plugin_args_open_return
Arguments used to transport variables from the plug-in back to the OpenVPN process.
Definition: openvpn-plugin.h:394
openvpn_plugin_args_func_in
Arguments used to transport variables to and from the plug-in.
Definition: openvpn-plugin.h:429
openvpn_plugin_args_open_return::handle
openvpn_plugin_handle_t handle
Definition: openvpn-plugin.h:397
OPENVPN_PLUGIN_FUNC_SUCCESS
#define OPENVPN_PLUGIN_FUNC_SUCCESS
Definition: openvpn-plugin.h:148
config.h
plugin_context::authid
char * authid
Definition: multi-auth.c:68
openvpn_plugin_args_func_in::argv
const char **const argv
Definition: openvpn-plugin.h:432
plugin_context::username
const char * username
Definition: log.c:41
openvpn-plugin.h
_plugin_vlog_func
static plugin_vlog_t _plugin_vlog_func
Definition: multi-auth.c:74
openvpn_plugin_handle_t
void * openvpn_plugin_handle_t
Definition: openvpn-plugin.h:143
openvpn_plugin_close_v1
OPENVPN_EXPORT void openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
Definition: multi-auth.c:409
openvpn_plugin_args_open_in::callbacks
struct openvpn_plugin_callbacks * callbacks
Definition: openvpn-plugin.h:364
openvpn_plugin_min_version_required_v1
OPENVPN_EXPORT int openvpn_plugin_min_version_required_v1()
Definition: multi-auth.c:173