To build the plug-in, do ./build log_v3 in the plugin/examples directory.
This plug-in can be tested by running an OpenVPN server like this:
# ./openvpn --plugin plugin/examples/log_v3.so --dev tun \
--server 192.168.240.0 255.255.255.0 --ca sample-keys/ca.crt \
--cert sample-keys/server.crt --key sample-keys/server.key \
--dh sample-keys/dh1024.pem
The client can be started like this:
# ./openvpn --client --remote localhost --ca sample-keys/ca.crt \
--cert sample-keys/client.crt --key sample-keys/client.key \
--dev tun --nobind --auth-user-pass
This plug-in will only log arguments and environment variables it receives
during all the different plug-in phases OpenVPN currently supports. It will
also parse the X509 certificate information given during the TLS_VERIFY phase.
Signed-off-by: David Sommerseth <dazo@users.sourceforge.net>
Acked-by: James Yonan <james@openvpn.net>
1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,245 @@ |
0 |
+/* |
|
1 |
+ * OpenVPN -- An application to securely tunnel IP networks |
|
2 |
+ * over a single TCP/UDP port, with support for SSL/TLS-based |
|
3 |
+ * session authentication and key exchange, |
|
4 |
+ * packet encryption, packet authentication, and |
|
5 |
+ * packet compression. |
|
6 |
+ * |
|
7 |
+ * Copyright (C) 2002-2009 OpenVPN Technologies, Inc. <sales@openvpn.net> |
|
8 |
+ * Copyright (C) 2010 David Sommerseth <dazo@users.sourceforge.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 plugin is similar to simple.c, except it also logs extra information |
|
27 |
+ * to stdout for every plugin method called by OpenVPN. The only difference |
|
28 |
+ * between this (log_v3.c) and log.c is that this module uses the v3 plug-in |
|
29 |
+ * API. |
|
30 |
+ * |
|
31 |
+ * See the README file for build instructions. |
|
32 |
+ */ |
|
33 |
+ |
|
34 |
+#include <stdio.h> |
|
35 |
+#include <string.h> |
|
36 |
+#include <stdlib.h> |
|
37 |
+ |
|
38 |
+#include "openvpn-plugin.h" |
|
39 |
+ |
|
40 |
+/* |
|
41 |
+ * Our context, where we keep our state. |
|
42 |
+ */ |
|
43 |
+struct plugin_context { |
|
44 |
+ const char *username; |
|
45 |
+ const char *password; |
|
46 |
+}; |
|
47 |
+ |
|
48 |
+/* |
|
49 |
+ * Given an environmental variable name, search |
|
50 |
+ * the envp array for its value, returning it |
|
51 |
+ * if found or NULL otherwise. |
|
52 |
+ */ |
|
53 |
+static const char * |
|
54 |
+get_env (const char *name, const char *envp[]) |
|
55 |
+{ |
|
56 |
+ if (envp) |
|
57 |
+ { |
|
58 |
+ int i; |
|
59 |
+ const int namelen = strlen (name); |
|
60 |
+ for (i = 0; envp[i]; ++i) |
|
61 |
+ { |
|
62 |
+ if (!strncmp (envp[i], name, namelen)) |
|
63 |
+ { |
|
64 |
+ const char *cp = envp[i] + namelen; |
|
65 |
+ if (*cp == '=') |
|
66 |
+ return cp + 1; |
|
67 |
+ } |
|
68 |
+ } |
|
69 |
+ } |
|
70 |
+ return NULL; |
|
71 |
+} |
|
72 |
+ |
|
73 |
+OPENVPN_EXPORT int |
|
74 |
+openvpn_plugin_open_v3 (const int apiver, |
|
75 |
+ struct openvpn_plugin_args_open_in const *args, |
|
76 |
+ struct openvpn_plugin_args_open_return *ret) |
|
77 |
+{ |
|
78 |
+ struct plugin_context *context = NULL; |
|
79 |
+ |
|
80 |
+ /* Check that we are API compatible */ |
|
81 |
+ if( apiver != OPENVPN_PLUGIN_VERSION ) { |
|
82 |
+ return OPENVPN_PLUGIN_FUNC_ERROR; |
|
83 |
+ } |
|
84 |
+ |
|
85 |
+ /* Which callbacks to intercept. */ |
|
86 |
+ ret->type_mask = |
|
87 |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) | |
|
88 |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN) | |
|
89 |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ROUTE_UP) | |
|
90 |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_IPCHANGE) | |
|
91 |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_VERIFY) | |
|
92 |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) | |
|
93 |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_CONNECT_V2) | |
|
94 |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_DISCONNECT) | |
|
95 |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_LEARN_ADDRESS) | |
|
96 |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_FINAL); |
|
97 |
+ |
|
98 |
+ |
|
99 |
+ /* Allocate our context */ |
|
100 |
+ context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context)); |
|
101 |
+ |
|
102 |
+ /* Set the username/password we will require. */ |
|
103 |
+ context->username = "foo"; |
|
104 |
+ context->password = "bar"; |
|
105 |
+ |
|
106 |
+ /* Point the global context handle to our newly created context */ |
|
107 |
+ ret->handle = (void *) context; |
|
108 |
+ |
|
109 |
+ return OPENVPN_PLUGIN_FUNC_SUCCESS; |
|
110 |
+} |
|
111 |
+ |
|
112 |
+void |
|
113 |
+show (const int type, const char *argv[], const char *envp[]) |
|
114 |
+{ |
|
115 |
+ size_t i; |
|
116 |
+ switch (type) |
|
117 |
+ { |
|
118 |
+ case OPENVPN_PLUGIN_UP: |
|
119 |
+ printf ("OPENVPN_PLUGIN_UP\n"); |
|
120 |
+ break; |
|
121 |
+ case OPENVPN_PLUGIN_DOWN: |
|
122 |
+ printf ("OPENVPN_PLUGIN_DOWN\n"); |
|
123 |
+ break; |
|
124 |
+ case OPENVPN_PLUGIN_ROUTE_UP: |
|
125 |
+ printf ("OPENVPN_PLUGIN_ROUTE_UP\n"); |
|
126 |
+ break; |
|
127 |
+ case OPENVPN_PLUGIN_IPCHANGE: |
|
128 |
+ printf ("OPENVPN_PLUGIN_IPCHANGE\n"); |
|
129 |
+ break; |
|
130 |
+ case OPENVPN_PLUGIN_TLS_VERIFY: |
|
131 |
+ printf ("OPENVPN_PLUGIN_TLS_VERIFY\n"); |
|
132 |
+ break; |
|
133 |
+ case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY: |
|
134 |
+ printf ("OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\n"); |
|
135 |
+ break; |
|
136 |
+ case OPENVPN_PLUGIN_CLIENT_CONNECT_V2: |
|
137 |
+ printf ("OPENVPN_PLUGIN_CLIENT_CONNECT_V2\n"); |
|
138 |
+ break; |
|
139 |
+ case OPENVPN_PLUGIN_CLIENT_DISCONNECT: |
|
140 |
+ printf ("OPENVPN_PLUGIN_CLIENT_DISCONNECT\n"); |
|
141 |
+ break; |
|
142 |
+ case OPENVPN_PLUGIN_LEARN_ADDRESS: |
|
143 |
+ printf ("OPENVPN_PLUGIN_LEARN_ADDRESS\n"); |
|
144 |
+ break; |
|
145 |
+ case OPENVPN_PLUGIN_TLS_FINAL: |
|
146 |
+ printf ("OPENVPN_PLUGIN_TLS_FINAL\n"); |
|
147 |
+ break; |
|
148 |
+ default: |
|
149 |
+ printf ("OPENVPN_PLUGIN_?\n"); |
|
150 |
+ break; |
|
151 |
+ } |
|
152 |
+ |
|
153 |
+ printf ("ARGV\n"); |
|
154 |
+ for (i = 0; argv[i] != NULL; ++i) |
|
155 |
+ printf ("%d '%s'\n", (int)i, argv[i]); |
|
156 |
+ |
|
157 |
+ printf ("ENVP\n"); |
|
158 |
+ for (i = 0; envp[i] != NULL; ++i) |
|
159 |
+ printf ("%d '%s'\n", (int)i, envp[i]); |
|
160 |
+} |
|
161 |
+ |
|
162 |
+static void |
|
163 |
+x509_print_info (X509 *x509crt) |
|
164 |
+{ |
|
165 |
+ int i, n; |
|
166 |
+ int fn_nid; |
|
167 |
+ ASN1_OBJECT *fn; |
|
168 |
+ ASN1_STRING *val; |
|
169 |
+ X509_NAME *x509_name; |
|
170 |
+ X509_NAME_ENTRY *ent; |
|
171 |
+ const char *objbuf; |
|
172 |
+ unsigned char *buf; |
|
173 |
+ |
|
174 |
+ x509_name = X509_get_subject_name (x509crt); |
|
175 |
+ n = X509_NAME_entry_count (x509_name); |
|
176 |
+ for (i = 0; i < n; ++i) |
|
177 |
+ { |
|
178 |
+ ent = X509_NAME_get_entry (x509_name, i); |
|
179 |
+ if (!ent) |
|
180 |
+ continue; |
|
181 |
+ fn = X509_NAME_ENTRY_get_object (ent); |
|
182 |
+ if (!fn) |
|
183 |
+ continue; |
|
184 |
+ val = X509_NAME_ENTRY_get_data (ent); |
|
185 |
+ if (!val) |
|
186 |
+ continue; |
|
187 |
+ fn_nid = OBJ_obj2nid (fn); |
|
188 |
+ if (fn_nid == NID_undef) |
|
189 |
+ continue; |
|
190 |
+ objbuf = OBJ_nid2sn (fn_nid); |
|
191 |
+ if (!objbuf) |
|
192 |
+ continue; |
|
193 |
+ buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ |
|
194 |
+ if (ASN1_STRING_to_UTF8 (&buf, val) <= 0) |
|
195 |
+ continue; |
|
196 |
+ |
|
197 |
+ printf("X509 %s: %s\n", objbuf, (char *)buf); |
|
198 |
+ OPENSSL_free (buf); |
|
199 |
+ } |
|
200 |
+} |
|
201 |
+ |
|
202 |
+ |
|
203 |
+ |
|
204 |
+OPENVPN_EXPORT int |
|
205 |
+openvpn_plugin_func_v3 (const int version, |
|
206 |
+ struct openvpn_plugin_args_func_in const *args, |
|
207 |
+ struct openvpn_plugin_args_func_return *retptr) |
|
208 |
+{ |
|
209 |
+ struct plugin_context *context = (struct plugin_context *) args->handle; |
|
210 |
+ |
|
211 |
+ printf("\nopenvpn_plugin_func_v3() :::::>> "); |
|
212 |
+ show (args->type, args->argv, args->envp); |
|
213 |
+ |
|
214 |
+ /* Dump some X509 information if we're in the TLS_VERIFY phase */ |
|
215 |
+ if ((args->type == OPENVPN_PLUGIN_TLS_VERIFY) && args->current_cert ) { |
|
216 |
+ printf("---- X509 Subject information ----\n"); |
|
217 |
+ printf("Certificate depth: %i\n", args->current_cert_depth); |
|
218 |
+ x509_print_info(args->current_cert); |
|
219 |
+ printf("----------------------------------\n"); |
|
220 |
+ } |
|
221 |
+ |
|
222 |
+ /* check entered username/password against what we require */ |
|
223 |
+ if (args->type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) |
|
224 |
+ { |
|
225 |
+ /* get username/password from envp string array */ |
|
226 |
+ const char *username = get_env ("username", args->envp); |
|
227 |
+ const char *password = get_env ("password", args->envp); |
|
228 |
+ |
|
229 |
+ if (username && !strcmp (username, context->username) |
|
230 |
+ && password && !strcmp (password, context->password)) |
|
231 |
+ return OPENVPN_PLUGIN_FUNC_SUCCESS; |
|
232 |
+ else |
|
233 |
+ return OPENVPN_PLUGIN_FUNC_ERROR; |
|
234 |
+ } |
|
235 |
+ else |
|
236 |
+ return OPENVPN_PLUGIN_FUNC_SUCCESS; |
|
237 |
+} |
|
238 |
+ |
|
239 |
+OPENVPN_EXPORT void |
|
240 |
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) |
|
241 |
+{ |
|
242 |
+ struct plugin_context *context = (struct plugin_context *) handle; |
|
243 |
+ free (context); |
|
244 |
+} |