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-2010 OpenVPN Technologies, 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
20 *  along with this program (see the file COPYING included with this
21 *  distribution); if not, write to the Free Software Foundation, Inc.,
22 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25/*
26 * This file implements a simple OpenVPN plugin module which
27 * will test deferred authentication and packet filtering.
28 *
29 * Will run on Windows or *nix.
30 *
31 * Sample usage:
32 *
33 * setenv test_deferred_auth 20
34 * setenv test_packet_filter 10
35 * plugin plugin/defer/simple.so
36 *
37 * This will enable deferred authentication to occur 20
38 * seconds after the normal TLS authentication process,
39 * and will cause a packet filter file to be generated 10
40 * seconds after the initial TLS negotiation, using
41 * {common-name}.pf as the source.
42 *
43 * Sample packet filter configuration:
44 *
45 * [CLIENTS DROP]
46 * +otherclient
47 * [SUBNETS DROP]
48 * +10.0.0.0/8
49 * -10.10.0.8
50 * [END]
51 *
52 * See the README file for build instructions.
53 */
54
55#include <stdio.h>
56#include <string.h>
57#include <stdlib.h>
58
59#include "openvpn-plugin.h"
60
61/* bool definitions */
62#define bool int
63#define true 1
64#define false 0
65
66/*
67 * Our context, where we keep our state.
68 */
69
70struct plugin_context {
71  int test_deferred_auth;
72  int test_packet_filter;
73};
74
75struct plugin_per_client_context {
76  int n_calls;
77  bool generated_pf_file;
78};
79
80/*
81 * Given an environmental variable name, search
82 * the envp array for its value, returning it
83 * if found or NULL otherwise.
84 */
85static const char *
86get_env (const char *name, const char *envp[])
87{
88  if (envp)
89    {
90      int i;
91      const int namelen = strlen (name);
92      for (i = 0; envp[i]; ++i)
93	{
94	  if (!strncmp (envp[i], name, namelen))
95	    {
96	      const char *cp = envp[i] + namelen;
97	      if (*cp == '=')
98		return cp + 1;
99	    }
100	}
101    }
102  return NULL;
103}
104
105/* used for safe printf of possible NULL strings */
106static const char *
107np (const char *str)
108{
109  if (str)
110    return str;
111  else
112    return "[NULL]";
113}
114
115static int
116atoi_null0 (const char *str)
117{
118  if (str)
119    return atoi (str);
120  else
121    return 0;
122}
123
124OPENVPN_EXPORT openvpn_plugin_handle_t
125openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
126{
127  struct plugin_context *context;
128
129  printf ("FUNC: openvpn_plugin_open_v1\n");
130
131  /*
132   * Allocate our context
133   */
134  context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context));
135
136  context->test_deferred_auth = atoi_null0 (get_env ("test_deferred_auth", envp));
137  printf ("TEST_DEFERRED_AUTH %d\n", context->test_deferred_auth);
138
139  context->test_packet_filter = atoi_null0 (get_env ("test_packet_filter", envp));
140  printf ("TEST_PACKET_FILTER %d\n", context->test_packet_filter);
141
142  /*
143   * Which callbacks to intercept.
144   */
145  *type_mask =
146    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) |
147    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN) |
148    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ROUTE_UP) |
149    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_IPCHANGE) |
150    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_VERIFY) |
151    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) |
152    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_CONNECT_V2) |
153    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_DISCONNECT) |
154    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_LEARN_ADDRESS) |
155    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_FINAL) |
156    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ENABLE_PF);
157
158  return (openvpn_plugin_handle_t) context;
159}
160
161static int
162auth_user_pass_verify (struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[])
163{
164  if (context->test_deferred_auth)
165    {
166      /* get username/password from envp string array */
167      const char *username = get_env ("username", envp);
168      const char *password = get_env ("password", envp);
169
170      /* get auth_control_file filename from envp string array*/
171      const char *auth_control_file = get_env ("auth_control_file", envp);
172
173      printf ("DEFER u='%s' p='%s' acf='%s'\n",
174	      np(username),
175	      np(password),
176	      np(auth_control_file));
177
178      /* Authenticate asynchronously in n seconds */
179      if (auth_control_file)
180	{
181	  char buf[256];
182	  int auth = 2;
183	  sscanf (username, "%d", &auth);
184	  snprintf (buf, sizeof(buf), "( sleep %d ; echo AUTH %s %d ; echo %d >%s ) &",
185		    context->test_deferred_auth,
186		    auth_control_file,
187		    auth,
188		    pcc->n_calls < auth,
189		    auth_control_file);
190	  printf ("%s\n", buf);
191	  system (buf);
192	  pcc->n_calls++;
193	  return OPENVPN_PLUGIN_FUNC_DEFERRED;
194	}
195      else
196	return OPENVPN_PLUGIN_FUNC_ERROR;
197    }
198  else
199    return OPENVPN_PLUGIN_FUNC_SUCCESS;
200}
201
202static int
203tls_final (struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[])
204{
205  if (context->test_packet_filter)
206    {
207      if (!pcc->generated_pf_file)
208	{
209	  const char *pff = get_env ("pf_file", envp);
210	  const char *cn = get_env ("username", envp);
211	  if (pff && cn)
212	    {
213	      char buf[256];
214	      snprintf (buf, sizeof(buf), "( sleep %d ; echo PF %s/%s ; cp \"%s.pf\" \"%s\" ) &",
215			context->test_packet_filter, cn, pff, cn, pff);
216	      printf ("%s\n", buf);
217	      system (buf);
218	      pcc->generated_pf_file = true;
219	      return OPENVPN_PLUGIN_FUNC_SUCCESS;
220	    }
221	  else
222	    return OPENVPN_PLUGIN_FUNC_ERROR;
223	}
224      else
225	return OPENVPN_PLUGIN_FUNC_ERROR;
226    }
227  else
228    return OPENVPN_PLUGIN_FUNC_SUCCESS;
229}
230
231OPENVPN_EXPORT int
232openvpn_plugin_func_v2 (openvpn_plugin_handle_t handle,
233			const int type,
234			const char *argv[],
235			const char *envp[],
236			void *per_client_context,
237			struct openvpn_plugin_string_list **return_list)
238{
239  struct plugin_context *context = (struct plugin_context *) handle;
240  struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) per_client_context;
241  switch (type)
242    {
243    case OPENVPN_PLUGIN_UP:
244      printf ("OPENVPN_PLUGIN_UP\n");
245      return OPENVPN_PLUGIN_FUNC_SUCCESS;
246    case OPENVPN_PLUGIN_DOWN:
247      printf ("OPENVPN_PLUGIN_DOWN\n");
248      return OPENVPN_PLUGIN_FUNC_SUCCESS;
249    case OPENVPN_PLUGIN_ROUTE_UP:
250      printf ("OPENVPN_PLUGIN_ROUTE_UP\n");
251      return OPENVPN_PLUGIN_FUNC_SUCCESS;
252    case OPENVPN_PLUGIN_IPCHANGE:
253      printf ("OPENVPN_PLUGIN_IPCHANGE\n");
254      return OPENVPN_PLUGIN_FUNC_SUCCESS;
255    case OPENVPN_PLUGIN_TLS_VERIFY:
256      printf ("OPENVPN_PLUGIN_TLS_VERIFY\n");
257      return OPENVPN_PLUGIN_FUNC_SUCCESS;
258    case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
259      printf ("OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\n");
260      return auth_user_pass_verify (context, pcc, argv, envp);
261    case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
262      printf ("OPENVPN_PLUGIN_CLIENT_CONNECT_V2\n");
263      return OPENVPN_PLUGIN_FUNC_SUCCESS;
264    case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
265      printf ("OPENVPN_PLUGIN_CLIENT_DISCONNECT\n");
266      return OPENVPN_PLUGIN_FUNC_SUCCESS;
267    case OPENVPN_PLUGIN_LEARN_ADDRESS:
268      printf ("OPENVPN_PLUGIN_LEARN_ADDRESS\n");
269      return OPENVPN_PLUGIN_FUNC_SUCCESS;
270    case OPENVPN_PLUGIN_TLS_FINAL:
271      printf ("OPENVPN_PLUGIN_TLS_FINAL\n");
272      return tls_final (context, pcc, argv, envp);
273    case OPENVPN_PLUGIN_ENABLE_PF:
274      printf ("OPENVPN_PLUGIN_ENABLE_PF\n");
275      if (context->test_packet_filter)
276	return OPENVPN_PLUGIN_FUNC_SUCCESS;
277      else
278	return OPENVPN_PLUGIN_FUNC_ERROR;
279    default:
280      printf ("OPENVPN_PLUGIN_?\n");
281      return OPENVPN_PLUGIN_FUNC_ERROR;
282    }
283}
284
285OPENVPN_EXPORT void *
286openvpn_plugin_client_constructor_v1 (openvpn_plugin_handle_t handle)
287{
288  printf ("FUNC: openvpn_plugin_client_constructor_v1\n");
289  return calloc (1, sizeof (struct plugin_per_client_context));
290}
291
292OPENVPN_EXPORT void
293openvpn_plugin_client_destructor_v1 (openvpn_plugin_handle_t handle, void *per_client_context)
294{
295  printf ("FUNC: openvpn_plugin_client_destructor_v1\n");
296  free (per_client_context);
297}
298
299OPENVPN_EXPORT void
300openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
301{
302  struct plugin_context *context = (struct plugin_context *) handle;
303  printf ("FUNC: openvpn_plugin_close_v1\n");
304  free (context);
305}
306