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-2018 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 {
87     int test_deferred_auth;
88     int test_packet_filter;
89 };
90 
91 struct plugin_per_client_context {
92     int n_calls;
93     bool generated_pf_file;
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 *
get_env(const char * name,const char * envp[])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 *
np(const char * str)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
atoi_null0(const char * str)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
openvpn_plugin_min_version_required_v1()155 openvpn_plugin_min_version_required_v1()
156 {
157     return OPENVPN_PLUGIN_VERSION_MIN;
158 }
159 
160 /* use v3 functions so we can use openvpn's logging and base64 etc. */
161 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)162 openvpn_plugin_open_v3(const int v3structver,
163                        struct openvpn_plugin_args_open_in const *args,
164                        struct openvpn_plugin_args_open_return *ret)
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);
172         return OPENVPN_PLUGIN_FUNC_ERROR;
173     }
174 
175     /* Save global pointers to functions exported from openvpn */
176     plugin_log = args->callbacks->plugin_log;
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 =
199         OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_UP)
200         |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_DOWN)
201         |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_ROUTE_UP)
202         |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_IPCHANGE)
203         |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_VERIFY)
204         |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)
205         |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT_V2)
206         |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT)
207         |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_LEARN_ADDRESS)
208         |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_FINAL);
209 
210     /* ENABLE_PF should only be called if we're actually willing to do PF */
211     if (context->test_packet_filter)
212     {
213         ret->type_mask |= OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_ENABLE_PF);
214     }
215 
216     ret->handle = (openvpn_plugin_handle_t *) context;
217     plugin_log(PLOG_NOTE, MODULE, "initialization succeeded");
218     return OPENVPN_PLUGIN_FUNC_SUCCESS;
219 
220 error:
221     if (context)
222     {
223         free(context);
224     }
225     plugin_log(PLOG_NOTE, MODULE, "initialization failed");
226     return OPENVPN_PLUGIN_FUNC_ERROR;
227 }
228 
229 static int
auth_user_pass_verify(struct plugin_context * context,struct plugin_per_client_context * pcc,const char * argv[],const char * envp[])230 auth_user_pass_verify(struct plugin_context *context,
231                       struct plugin_per_client_context *pcc,
232                       const char *argv[], const char *envp[])
233 {
234     if (!context->test_deferred_auth)
235     {
236         return OPENVPN_PLUGIN_FUNC_SUCCESS;
237     }
238 
239     /* get username/password from envp string array */
240     const char *username = get_env("username", envp);
241     const char *password = get_env("password", envp);
242 
243     /* get auth_control_file filename from envp string array*/
244     const char *auth_control_file = get_env("auth_control_file", envp);
245 
246     plugin_log(PLOG_NOTE, MODULE, "DEFER u='%s' p='%s' acf='%s'",
247                np(username),
248                np(password),
249                np(auth_control_file));
250 
251     /* Authenticate asynchronously in n seconds */
252     if (!auth_control_file)
253     {
254         return OPENVPN_PLUGIN_FUNC_ERROR;
255     }
256 
257     /* we do not want to complicate our lives with having to wait()
258      * for child processes (so they are not zombiefied) *and* we MUST NOT
259      * fiddle with signal handlers (= shared with openvpn main), so
260      * we use double-fork() trick.
261      */
262 
263     /* fork, sleep, succeed (no "real" auth done = always succeed) */
264     pid_t p1 = fork();
265     if (p1 < 0)                 /* Fork failed */
266     {
267         return OPENVPN_PLUGIN_FUNC_ERROR;
268     }
269     if (p1 > 0)                 /* parent process */
270     {
271         waitpid(p1, NULL, 0);
272         return OPENVPN_PLUGIN_FUNC_DEFERRED;
273     }
274 
275     /* first gen child process, fork() again and exit() right away */
276     pid_t p2 = fork();
277     if (p2 < 0)
278     {
279         plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: fork(2) failed");
280         exit(1);
281     }
282 
283     if (p2 != 0)                            /* new parent: exit right away */
284     {
285         exit(0);
286     }
287 
288     /* (grand-)child process
289      *  - never call "return" now (would mess up openvpn)
290      *  - return status is communicated by file
291      *  - then exit()
292      */
293 
294     /* do mighty complicated work that will really take time here... */
295     plugin_log(PLOG_NOTE, MODULE, "in async/deferred handler, sleep(%d)", context->test_deferred_auth);
296     sleep(context->test_deferred_auth);
297 
298     /* now signal success state to openvpn */
299     int fd = open(auth_control_file, O_WRONLY);
300     if (fd < 0)
301     {
302         plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "open('%s') failed", auth_control_file);
303         exit(1);
304     }
305 
306     plugin_log(PLOG_NOTE, MODULE, "auth_user_pass_verify: done" );
307 
308     if (write(fd, "1", 1) != 1)
309     {
310         plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "write to '%s' failed", auth_control_file );
311     }
312     close(fd);
313 
314     exit(0);
315 }
316 
317 static int
tls_final(struct plugin_context * context,struct plugin_per_client_context * pcc,const char * argv[],const char * envp[])318 tls_final(struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[])
319 {
320     if (!context->test_packet_filter)   /* no PF testing, nothing to do */
321     {
322         return OPENVPN_PLUGIN_FUNC_SUCCESS;
323     }
324 
325     if (pcc->generated_pf_file)         /* we already have created a file */
326     {
327         return OPENVPN_PLUGIN_FUNC_ERROR;
328     }
329 
330     const char *pff = get_env("pf_file", envp);
331     const char *cn = get_env("username", envp);
332     if (!pff || !cn)                    /* required vars missing */
333     {
334         return OPENVPN_PLUGIN_FUNC_ERROR;
335     }
336 
337     pcc->generated_pf_file = true;
338 
339     /* the PF API is, basically
340      *  - OpenVPN sends a filename (pf_file) to the plugin
341      *  - OpenVPN main loop will check every second if that file shows up
342      *  - when it does, it will be read & used for the pf config
343      * the pre-created file needs to be removed in ...ENABLE_PF
344      * to make deferred PF setup work
345      *
346      * the regular PF hook does not know the client username or CN, so
347      * this is deferred to the TLS_FINAL hook which knows these things
348      */
349 
350     /* do the double fork dance (see above for more verbose comments)
351      */
352     pid_t p1 = fork();
353     if (p1 < 0)                 /* Fork failed */
354     {
355         return OPENVPN_PLUGIN_FUNC_ERROR;
356     }
357     if (p1 > 0)                 /* parent process */
358     {
359         waitpid(p1, NULL, 0);
360         return OPENVPN_PLUGIN_FUNC_SUCCESS;     /* no _DEFERRED here! */
361     }
362 
363     /* first gen child process, fork() again and exit() right away */
364     pid_t p2 = fork();
365     if (p2 < 0)
366     {
367         plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: fork(2) failed");
368         exit(1);
369     }
370 
371     if (p2 != 0)                            /* new parent: exit right away */
372     {
373         exit(0);
374     }
375 
376     /* (grand-)child process
377      *  - never call "return" now (would mess up openvpn)
378      *  - return status is communicated by file
379      *  - then exit()
380      */
381 
382     /* at this point, the plugin can take its time, because OpenVPN will
383      * no longer block waiting for the call to finish
384      *
385      * in this example, we build a PF file by copying over a file
386      * named "<username>.pf" to the OpenVPN-provided pf file name
387      *
388      * a real example could do a LDAP lookup, a REST call, ...
389      */
390     plugin_log(PLOG_NOTE, MODULE, "in async/deferred tls_final handler, sleep(%d)", context->test_packet_filter);
391     sleep(context->test_packet_filter);
392 
393     char buf[256];
394     snprintf(buf, sizeof(buf), "%s.pf", cn );
395 
396     /* there is a small race condition here - OpenVPN could detect our
397      * file while we have only written half of it.  So "perfect" code
398      * needs to create this with a temp file name, and then rename() it
399      * after it has been written.  But I am lazy.
400      */
401 
402     int w_fd = open( pff, O_WRONLY|O_CREAT, 0600 );
403     if (w_fd < 0)
404     {
405         plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "can't write to '%s'", pff);
406         exit(0);
407     }
408 
409     int r_fd = open( buf, O_RDONLY );
410     if (r_fd < 0)
411     {
412         plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "can't read '%s', creating empty pf file", buf);
413         close(w_fd);
414         exit(0);
415     }
416 
417     char data[1024];
418 
419     int r;
420     do
421     {
422         r = read(r_fd, data, sizeof(data));
423         if (r < 0)
424         {
425             plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "error reading '%s'", buf);
426             close(r_fd);
427             close(w_fd);
428             exit(0);
429         }
430         int w = write(w_fd, data, r);
431         if (w < 0 || w != r)
432         {
433             plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "error writing %d bytes to '%s'", r, pff);
434             close(r_fd);
435             close(w_fd);
436             exit(0);
437         }
438     }
439     while(r > 0);
440 
441     plugin_log(PLOG_NOTE, MODULE, "copied PF config from '%s' to '%s', job done", buf, pff);
442     exit(0);
443 }
444 
445 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)446 openvpn_plugin_func_v3(const int v3structver,
447                        struct openvpn_plugin_args_func_in const *args,
448                        struct openvpn_plugin_args_func_return *ret)
449 {
450     if (v3structver < OPENVPN_PLUGIN_STRUCTVER_MIN)
451     {
452         fprintf(stderr, "%s: this plugin is incompatible with the running version of OpenVPN\n", MODULE);
453         return OPENVPN_PLUGIN_FUNC_ERROR;
454     }
455     const char **argv = args->argv;
456     const char **envp = args->envp;
457     struct plugin_context *context = (struct plugin_context *) args->handle;
458     struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) args->per_client_context;
459     switch (args->type)
460     {
461         case OPENVPN_PLUGIN_UP:
462             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_UP");
463             return OPENVPN_PLUGIN_FUNC_SUCCESS;
464 
465         case OPENVPN_PLUGIN_DOWN:
466             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_DOWN");
467             return OPENVPN_PLUGIN_FUNC_SUCCESS;
468 
469         case OPENVPN_PLUGIN_ROUTE_UP:
470             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_ROUTE_UP");
471             return OPENVPN_PLUGIN_FUNC_SUCCESS;
472 
473         case OPENVPN_PLUGIN_IPCHANGE:
474             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_IPCHANGE");
475             return OPENVPN_PLUGIN_FUNC_SUCCESS;
476 
477         case OPENVPN_PLUGIN_TLS_VERIFY:
478             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_TLS_VERIFY");
479             return OPENVPN_PLUGIN_FUNC_SUCCESS;
480 
481         case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
482             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY");
483             return auth_user_pass_verify(context, pcc, argv, envp);
484 
485         case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
486             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_CLIENT_CONNECT_V2");
487             return OPENVPN_PLUGIN_FUNC_SUCCESS;
488 
489         case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
490             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_CLIENT_DISCONNECT");
491             return OPENVPN_PLUGIN_FUNC_SUCCESS;
492 
493         case OPENVPN_PLUGIN_LEARN_ADDRESS:
494             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_LEARN_ADDRESS");
495             return OPENVPN_PLUGIN_FUNC_SUCCESS;
496 
497         case OPENVPN_PLUGIN_TLS_FINAL:
498             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_TLS_FINAL");
499             return tls_final(context, pcc, argv, envp);
500 
501         case OPENVPN_PLUGIN_ENABLE_PF:
502             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_ENABLE_PF");
503 
504             /* OpenVPN pre-creates the file, which gets in the way of
505              * deferred pf setup - so remove it here, and re-create
506              * it in the background handler (in tls_final()) when ready
507              */
508             const char *pff = get_env("pf_file", envp);
509             if (pff)
510             {
511                 (void) unlink(pff);
512             }
513             return OPENVPN_PLUGIN_FUNC_SUCCESS;           /* must succeed */
514 
515         default:
516             plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_?");
517             return OPENVPN_PLUGIN_FUNC_ERROR;
518     }
519 }
520 
521 OPENVPN_EXPORT void *
openvpn_plugin_client_constructor_v1(openvpn_plugin_handle_t handle)522 openvpn_plugin_client_constructor_v1(openvpn_plugin_handle_t handle)
523 {
524     plugin_log(PLOG_NOTE, MODULE, "FUNC: openvpn_plugin_client_constructor_v1");
525     return calloc(1, sizeof(struct plugin_per_client_context));
526 }
527 
528 OPENVPN_EXPORT void
openvpn_plugin_client_destructor_v1(openvpn_plugin_handle_t handle,void * per_client_context)529 openvpn_plugin_client_destructor_v1(openvpn_plugin_handle_t handle, void *per_client_context)
530 {
531     plugin_log(PLOG_NOTE, MODULE, "FUNC: openvpn_plugin_client_destructor_v1");
532     free(per_client_context);
533 }
534 
535 OPENVPN_EXPORT void
openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)536 openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
537 {
538     struct plugin_context *context = (struct plugin_context *) handle;
539     plugin_log(PLOG_NOTE, MODULE, "FUNC: openvpn_plugin_close_v1");
540     free(context);
541 }
542