OpenVPN
keyingmaterialexporter.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-2025 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, see <https://www.gnu.org/licenses/>.
21 */
22
23/*
24 * This file implements a Sample (HTTP) SSO OpenVPN plugin module
25 *
26 * See the README file for build instructions.
27 */
28
29#include <stdio.h>
30#include <string.h>
31#include <strings.h>
32#include <stdlib.h>
33
34#include "openvpn-plugin.h"
35
36#ifndef MAXPATH
37#define MAXPATH 1024
38#endif
39
40#define ovpn_err(fmt, ...) plugin->log(PLOG_ERR, "SSO", fmt, ##__VA_ARGS__)
41#define ovpn_dbg(fmt, ...) plugin->log(PLOG_DEBUG, "SSO", fmt, ##__VA_ARGS__)
42#define ovpn_note(fmt, ...) plugin->log(PLOG_NOTE, "SSO", fmt, ##__VA_ARGS__)
43
45{
46 CLIENT = 1,
47 SERVER = 2
48};
49
50struct plugin
51{
52 plugin_log_t log;
54 int mask;
55};
56
57struct session
58{
59 char user[48];
60 char key[48];
61};
62
63/*
64 * Given an environmental variable name, search
65 * the envp array for its value, returning it
66 * if found or NULL otherwise.
67 */
68
69static const char *
70get_env(const char *name, const char *envp[])
71{
72 if (envp)
73 {
74 const size_t namelen = strlen(name);
75 for (int i = 0; envp[i]; ++i)
76 {
77 if (!strncmp(envp[i], name, namelen))
78 {
79 const char *cp = envp[i] + namelen;
80 if (*cp == '=')
81 {
82 return cp + 1;
83 }
84 }
85 }
86 }
87 return NULL;
88}
89
90OPENVPN_EXPORT int
91openvpn_plugin_open_v3(const int version, struct openvpn_plugin_args_open_in const *args,
92 struct openvpn_plugin_args_open_return *rv)
93{
94 struct plugin *plugin = calloc(1, sizeof(*plugin));
95
96 if (plugin == NULL)
97 {
98 printf("PLUGIN: allocating memory for context failed\n");
99 return OPENVPN_PLUGIN_FUNC_ERROR;
100 }
101
102 plugin->type = get_env("remote_1", args->envp) ? CLIENT : SERVER;
103 plugin->log = args->callbacks->plugin_log;
104
105 plugin->mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_FINAL);
106 plugin->mask |= OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_VERIFY);
107
108 ovpn_note("vpn endpoint type=%s", plugin->type == CLIENT ? "client" : "server");
109
110 rv->type_mask = plugin->mask;
111 rv->handle = (void *)plugin;
112
113 return OPENVPN_PLUGIN_FUNC_SUCCESS;
114}
115
116static void
117session_user_set(struct session *sess, X509 *x509)
118{
119 int fn_nid;
120 ASN1_OBJECT *fn;
121 ASN1_STRING *val;
122 X509_NAME *x509_name;
123 X509_NAME_ENTRY *ent;
124 const char *objbuf;
125
126 x509_name = X509_get_subject_name(x509);
127 int i, n = X509_NAME_entry_count(x509_name);
128 for (i = 0; i < n; ++i)
129 {
130 if (!(ent = X509_NAME_get_entry(x509_name, i)))
131 {
132 continue;
133 }
134 if (!(fn = X509_NAME_ENTRY_get_object(ent)))
135 {
136 continue;
137 }
138 if (!(val = X509_NAME_ENTRY_get_data(ent)))
139 {
140 continue;
141 }
142 if ((fn_nid = OBJ_obj2nid(fn)) == NID_undef)
143 {
144 continue;
145 }
146 if (!(objbuf = OBJ_nid2sn(fn_nid)))
147 {
148 continue;
149 }
150 unsigned char *buf = NULL;
151 if (ASN1_STRING_to_UTF8(&buf, val) < 0)
152 {
153 continue;
154 }
155
156 if (!strncasecmp(objbuf, "CN", 2))
157 {
158 strncpy(sess->user, (char *)buf, sizeof(sess->user) - 1);
159 }
160
161 OPENSSL_free(buf);
162 }
163}
164
165static int
166tls_verify(struct openvpn_plugin_args_func_in const *args)
167{
168 struct plugin *plugin = (struct plugin *)args->handle;
169 struct session *sess = (struct session *)args->per_client_context;
170
171 /* we store cert subject for the server end point only */
172 if (plugin->type != SERVER)
173 {
174 return OPENVPN_PLUGIN_FUNC_SUCCESS;
175 }
176
177 if (!args->current_cert)
178 {
179 ovpn_err("this example plugin requires client certificate");
180 return OPENVPN_PLUGIN_FUNC_ERROR;
181 }
182
183 session_user_set(sess, args->current_cert);
184
185 return OPENVPN_PLUGIN_FUNC_SUCCESS;
186}
187
188static void
189file_store(char *file, char *content)
190{
191 FILE *f;
192 if (!(f = fopen(file, "w+")))
193 {
194 return;
195 }
196
197 fprintf(f, "%s", content);
198 fclose(f);
199}
200
201static void
202server_store(struct openvpn_plugin_args_func_in const *args)
203{
204 struct plugin *plugin = (struct plugin *)args->handle;
205 struct session *sess = (struct session *)args->per_client_context;
206
207 char file[MAXPATH];
208 snprintf(file, sizeof(file) - 1, "/tmp/openvpn_sso_%s", sess->key);
209 ovpn_note("app session file: %s", file);
210 file_store(file, sess->user);
211}
212
213static void
214client_store(struct openvpn_plugin_args_func_in const *args)
215{
216 struct plugin *plugin = (struct plugin *)args->handle;
217 struct session *sess = (struct session *)args->per_client_context;
218
219 char *file = "/tmp/openvpn_sso_user";
220 ovpn_note("app session file: %s", file);
221 file_store(file, sess->key);
222}
223
224static int
225tls_final(struct openvpn_plugin_args_func_in const *args,
226 struct openvpn_plugin_args_func_return *rv)
227{
228 struct plugin *plugin = (struct plugin *)args->handle;
229 struct session *sess = (struct session *)args->per_client_context;
230
231 const char *key;
232 if (!(key = get_env("exported_keying_material", args->envp)))
233 {
234 return OPENVPN_PLUGIN_FUNC_ERROR;
235 }
236
237 strncpy(sess->key, key, sizeof(sess->key) - 1);
238 ovpn_note("app session key: %s", sess->key);
239
240 switch (plugin->type)
241 {
242 case SERVER:
243 server_store(args);
244 break;
245
246 case CLIENT:
247 client_store(args);
248 return OPENVPN_PLUGIN_FUNC_SUCCESS;
249 }
250
251 ovpn_note("app session user: %s", sess->user);
252 return OPENVPN_PLUGIN_FUNC_SUCCESS;
253}
254
255OPENVPN_EXPORT int
256openvpn_plugin_func_v3(const int version, struct openvpn_plugin_args_func_in const *args,
257 struct openvpn_plugin_args_func_return *rv)
258{
259 switch (args->type)
260 {
261 case OPENVPN_PLUGIN_TLS_VERIFY:
262 return tls_verify(args);
263
264 case OPENVPN_PLUGIN_TLS_FINAL:
265 return tls_final(args, rv);
266 }
267 return OPENVPN_PLUGIN_FUNC_SUCCESS;
268}
269
270OPENVPN_EXPORT void *
271openvpn_plugin_client_constructor_v1(openvpn_plugin_handle_t handle)
272{
273 struct plugin *plugin = (struct plugin *)handle;
274 struct session *sess = calloc(1, sizeof(*sess));
275
276 ovpn_note("app session created");
277
278 return (void *)sess;
279}
280
281OPENVPN_EXPORT void
282openvpn_plugin_client_destructor_v1(openvpn_plugin_handle_t handle, void *ctx)
283{
284 struct plugin *plugin = (struct plugin *)handle;
285 struct session *sess = (struct session *)ctx;
286
287 ovpn_note("app session key: %s", sess->key);
288 ovpn_note("app session destroyed");
289
290 free(sess);
291}
292
293OPENVPN_EXPORT void
294openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
295{
296 struct plugin *plugin = (struct plugin *)handle;
297 free(plugin);
298}
static void server_store(struct openvpn_plugin_args_func_in const *args)
OPENVPN_EXPORT void openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
static int tls_verify(struct openvpn_plugin_args_func_in const *args)
#define MAXPATH
static void client_store(struct openvpn_plugin_args_func_in const *args)
OPENVPN_EXPORT void openvpn_plugin_client_destructor_v1(openvpn_plugin_handle_t handle, void *ctx)
OPENVPN_EXPORT int openvpn_plugin_open_v3(const int version, struct openvpn_plugin_args_open_in const *args, struct openvpn_plugin_args_open_return *rv)
OPENVPN_EXPORT int openvpn_plugin_func_v3(const int version, struct openvpn_plugin_args_func_in const *args, struct openvpn_plugin_args_func_return *rv)
static void file_store(char *file, char *content)
static const char * get_env(const char *name, const char *envp[])
static int tls_final(struct openvpn_plugin_args_func_in const *args, struct openvpn_plugin_args_func_return *rv)
OPENVPN_EXPORT void * openvpn_plugin_client_constructor_v1(openvpn_plugin_handle_t handle)
static void session_user_set(struct session *sess, X509 *x509)
#define ovpn_err(fmt,...)
#define ovpn_note(fmt,...)
Container for unidirectional cipher and HMAC key material.
Definition crypto.h:152
plugin_log_t log
enum endpoint type