mirror of
https://github.com/OpenVPN/openvpn.git
synced 2026-05-28 04:03:29 -04:00
sample-plugin: TLS Keying Material Exporter [RFC-5705] demonstration plug-in
A simple plug-in with a corresponding HTTP server and client which can authenticate
an HTTP user based on the authentication already done via an established OpenVPN
connection
[DS: Renamed the module at commit time from sso to keyingmaterialexporter to
avoid confusion with other Single-Sign-On solutions. Updated documentation
and commits accordingly. Added --pull to the client config]
Signed-off-by: Daniel Kubec <niel@rtfm.cz>
Signed-off-by: David Sommerseth <davids@redhat.com>
Acked-by: David Sommerseth <davids@redhat.com>
This commit is contained in:
parent
84604e0bae
commit
f7ef7522f5
Notes:
David Sommerseth
2015-10-10 10:46:13 +02:00
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Forgot to add Message-ID and URLs to the commit message. Message-Id: CAEW7GUuRt5Tuw+n4CHie+pTrr3FhH2kM0hQ20y3f_LXS8yvDoQ@mail.gmail.com URL: http://article.gmane.org/gmane.network.openvpn.devel/9581 -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iEYEARECAAYFAlYYzNMACgkQDC186MBRfrrvYwCdFCjovO6dGxhmbuXa+L66a9X4 +rYAnR0AplVKpExLnOjxhjVEDMsU2cS+ =6PPU -----END PGP SIGNATURE-----
7 changed files with 449 additions and 0 deletions
68
sample/sample-plugins/keying-material-exporter-demo/README
Normal file
68
sample/sample-plugins/keying-material-exporter-demo/README
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
OpenVPN plugin examples. Daniel Kubec <niel@rtfm.cz>
|
||||
|
||||
Examples provided:
|
||||
|
||||
keyingmaterialexporter.c -- Example based on TLS Keying Material Exporters over HTTP [RFC-5705]
|
||||
(openvpn/doc/keying-material-exporter.txt)
|
||||
|
||||
This example demonstrates authenticating a user over HTTP who have already
|
||||
established an OpenVPN connecting using the --keying-material-exporter
|
||||
feature.
|
||||
|
||||
Requires:
|
||||
OpenVPN RFC-5705 Support, OpenSSL >= 1.0.1
|
||||
|
||||
Files:
|
||||
http-server.py -- Example HTTP Server listen 0.0.0.0:8080
|
||||
http-client.py -- Example HTTP Client connect 10.8.0.1:8080 [GET /$SESSIONID]
|
||||
|
||||
server.ovpn -- Example HTTP SSO VPN Server configuration
|
||||
client.ovpn -- Example HTTP SSO VPN Client configuration
|
||||
|
||||
keyingmaterialexporter.c,
|
||||
keyingmaterialexporter.so -- Example OpenVPN Client and Server plugin
|
||||
|
||||
To build:
|
||||
./build keyingmaterialexporter
|
||||
|
||||
To use in OpenVPN:
|
||||
|
||||
Enter openvpn/sample/sample-plugins/keyingmaterialexporter directory
|
||||
and in separate terminals, start these four processes:
|
||||
|
||||
$ openvpn --config ./server.ovpn
|
||||
$ openvpn --config ./client.ovpn
|
||||
$ ./http-server.py
|
||||
$ ./http-client.py
|
||||
|
||||
Test:
|
||||
|
||||
openvpn --config ./server.ovpn
|
||||
##############################
|
||||
|
||||
PLUGIN SSO: app session created
|
||||
PLUGIN_CALL: POST ./keyingmaterialexporter.so/PLUGIN_TLS_VERIFY status=0
|
||||
PLUGIN SSO: app session key: a5885abc84d361803f58ede1ef9c0adf99e720cd
|
||||
PLUGIN SSO: app session file: /tmp/openvpn_sso_a5885abc84d361803f58ede1ef9c0adf99e720cd
|
||||
PLUGIN SSO: app session user: Test-Client
|
||||
|
||||
openvpn --config ./client.ovpn
|
||||
##############################
|
||||
PLUGIN SSO: app session created
|
||||
PLUGIN_CALL: POST ./keyingmaterialexporter.so/PLUGIN_TLS_VERIFY status=0
|
||||
PLUGIN SSO: app session key: a5885abc84d361803f58ede1ef9c0adf99e720cd
|
||||
PLUGIN SSO: app session file: /tmp/openvpn_sso_user
|
||||
PLUGIN_CALL: POST ./keyingmaterialexporter.so/PLUGIN_TLS_FINAL status=0
|
||||
|
||||
HTTP_SERVER:
|
||||
http-server.py
|
||||
################
|
||||
http server started
|
||||
session file: /tmp/openvpn_sso_a5885abc84d361803f58ede1ef9c0adf99e720cd
|
||||
10.8.0.1 - - [02/Apr/2015 15:03:33] "GET /a5885abc84d361803f58ede1ef9c0adf99e720cd HTTP/1.1" 200 -
|
||||
session user: Test-Client
|
||||
session key: a5885abc84d361803f58ede1ef9c0adf99e720cd
|
||||
|
||||
HTTP_SERVER:
|
||||
http-client.py
|
||||
<html><body><h1>Greetings Test-Client. You are authorized</h1></body></html>
|
||||
15
sample/sample-plugins/keying-material-exporter-demo/build
Executable file
15
sample/sample-plugins/keying-material-exporter-demo/build
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Build an OpenVPN plugin module on *nix. The argument should
|
||||
# be the base name of the C source file (without the .c).
|
||||
#
|
||||
|
||||
# This directory is where we will look for openvpn-plugin.h
|
||||
CPPFLAGS="${CPPFLAGS:--I../../..}"
|
||||
|
||||
CC="${CC:-gcc}"
|
||||
CFLAGS="${CFLAGS:--O2 -Wall -g}"
|
||||
|
||||
$CC $CPPFLAGS $CFLAGS -fPIC -c $1.c && \
|
||||
$CC $CFLAGS -fPIC -shared $LDFLAGS -Wl,-soname,$1.so -o $1.so $1.o -lc
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
tls-client
|
||||
pull
|
||||
|
||||
keying-material-exporter "EXPORTER_SSO_TEST" 16
|
||||
reneg-sec 0
|
||||
|
||||
ca ../../sample-keys/ca.crt
|
||||
cert ../../sample-keys/client.crt
|
||||
key ../../sample-keys/client.key
|
||||
|
||||
plugin ./keyingmaterialexporter.so
|
||||
|
||||
remote 127.0.0.1 1194
|
||||
proto udp
|
||||
dev tun
|
||||
nobind
|
||||
|
||||
verb 4
|
||||
20
sample/sample-plugins/keying-material-exporter-demo/http-client.py
Executable file
20
sample/sample-plugins/keying-material-exporter-demo/http-client.py
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import os
|
||||
import httplib
|
||||
|
||||
f = '/tmp/openvpn_sso_user'
|
||||
with open (f, "r") as myfile:
|
||||
session_key = myfile.read().replace('\n', '')
|
||||
|
||||
conn = httplib.HTTPConnection("10.8.0.1:8080")
|
||||
conn.request("GET", "/" + session_key)
|
||||
r1 = conn.getresponse()
|
||||
|
||||
if r1.status == 200:
|
||||
body = r1.read().rstrip()
|
||||
print body
|
||||
elif r1.status == 404:
|
||||
print "Authentication failed"
|
||||
else:
|
||||
print r1.status, r1.reason
|
||||
41
sample/sample-plugins/keying-material-exporter-demo/http-server.py
Executable file
41
sample/sample-plugins/keying-material-exporter-demo/http-server.py
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/python
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
import os
|
||||
|
||||
class ExampleHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_GET(self):
|
||||
session_key = os.path.basename(self.path)
|
||||
file = '/tmp/openvpn_sso_' + session_key
|
||||
print 'session file: ' + file
|
||||
try:
|
||||
f = open(file)
|
||||
#send code 200 response
|
||||
self.send_response(200)
|
||||
#send header first
|
||||
self.send_header('Content-type','text-html')
|
||||
self.end_headers()
|
||||
#send file content to client
|
||||
user = f.read().rstrip()
|
||||
print 'session user: ' + user
|
||||
print 'session key: ' + session_key
|
||||
self.wfile.write('<html><body><h1>Greetings ' + user \
|
||||
+ '. You are authorized' \
|
||||
'</h1>' \
|
||||
'</body></html>')
|
||||
f.close()
|
||||
return
|
||||
except IOError:
|
||||
self.send_error(404, 'authentication failed')
|
||||
|
||||
def run():
|
||||
#ip and port of servr
|
||||
#by default http server port is 80
|
||||
server_address = ('0.0.0.0', 8080)
|
||||
httpd = HTTPServer(server_address, ExampleHTTPRequestHandler)
|
||||
print('http server started')
|
||||
httpd.serve_forever()
|
||||
print('http server stopped')
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* OpenVPN -- An application to securely tunnel IP networks
|
||||
* over a single TCP/UDP port, with support for SSL/TLS-based
|
||||
* session authentication and key exchange,
|
||||
* packet encryption, packet authentication, and
|
||||
* packet compression.
|
||||
*
|
||||
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (see the file COPYING included with this
|
||||
* distribution); if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file implements a Sample (HTTP) SSO OpenVPN plugin module
|
||||
*
|
||||
* See the README file for build instructions.
|
||||
*/
|
||||
|
||||
#define ENABLE_CRYPTO
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "openvpn-plugin.h"
|
||||
|
||||
#ifndef MAXPATH
|
||||
#define MAXPATH 1024
|
||||
#endif
|
||||
|
||||
#define ovpn_err(fmt, ...) \
|
||||
plugin->log(PLOG_ERR, "SSO", fmt , ## __VA_ARGS__)
|
||||
#define ovpn_dbg(fmt, ...) \
|
||||
plugin->log(PLOG_DEBUG, "SSO", fmt , ## __VA_ARGS__)
|
||||
#define ovpn_note(fmt, ...) \
|
||||
plugin->log(PLOG_NOTE, "SSO", fmt , ## __VA_ARGS__)
|
||||
|
||||
enum endpoint { CLIENT = 1, SERVER = 2 };
|
||||
|
||||
struct plugin {
|
||||
plugin_log_t log;
|
||||
enum endpoint type;
|
||||
int mask;
|
||||
};
|
||||
|
||||
struct session {
|
||||
char user[48];
|
||||
char key [48];
|
||||
};
|
||||
|
||||
/*
|
||||
* Given an environmental variable name, search
|
||||
* the envp array for its value, returning it
|
||||
* if found or NULL otherwise.
|
||||
*/
|
||||
|
||||
static const char *
|
||||
get_env(const char *name, const char *envp[])
|
||||
{
|
||||
if (envp)
|
||||
{
|
||||
int i;
|
||||
const int namelen = strlen (name);
|
||||
for (i = 0; envp[i]; ++i)
|
||||
{
|
||||
if (!strncmp (envp[i], name, namelen))
|
||||
{
|
||||
const char *cp = envp[i] + namelen;
|
||||
if (*cp == '=')
|
||||
return cp + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct plugin *plugin = calloc (1, sizeof(*plugin));
|
||||
|
||||
plugin->type = get_env ("remote_1", args->envp) ? CLIENT : SERVER;
|
||||
plugin->log = args->callbacks->plugin_log;
|
||||
|
||||
plugin->mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_FINAL);
|
||||
plugin->mask |= OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_VERIFY);
|
||||
|
||||
ovpn_note("vpn endpoint type=%s",plugin->type == CLIENT ? "client":"server");
|
||||
|
||||
rv->type_mask = plugin->mask;
|
||||
rv->handle = (void *)plugin;
|
||||
|
||||
return OPENVPN_PLUGIN_FUNC_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
session_user_set(struct session *sess, X509 *x509)
|
||||
{
|
||||
int fn_nid;
|
||||
ASN1_OBJECT *fn;
|
||||
ASN1_STRING *val;
|
||||
X509_NAME *x509_name;
|
||||
X509_NAME_ENTRY *ent;
|
||||
const char *objbuf;
|
||||
|
||||
x509_name = X509_get_subject_name (x509);
|
||||
int i, n = X509_NAME_entry_count (x509_name);
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
if (!(ent = X509_NAME_get_entry (x509_name, i)))
|
||||
continue;
|
||||
if (!(fn = X509_NAME_ENTRY_get_object (ent)))
|
||||
continue;
|
||||
if (!(val = X509_NAME_ENTRY_get_data (ent)))
|
||||
continue;
|
||||
if ((fn_nid = OBJ_obj2nid (fn)) == NID_undef)
|
||||
continue;
|
||||
if (!(objbuf = OBJ_nid2sn (fn_nid)))
|
||||
continue;
|
||||
/* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */
|
||||
unsigned char *buf = (unsigned char *)1;
|
||||
if (ASN1_STRING_to_UTF8 (&buf, val) <= 0)
|
||||
continue;
|
||||
|
||||
if (!strncasecmp(objbuf, "CN", 2))
|
||||
snprintf(sess->user, sizeof(sess->user) - 1, (char *)buf);
|
||||
|
||||
OPENSSL_free (buf);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tls_verify(struct openvpn_plugin_args_func_in const *args)
|
||||
{
|
||||
struct plugin *plugin = (struct plugin *)args->handle;
|
||||
struct session *sess = (struct session *)args->per_client_context;
|
||||
|
||||
/* we store cert subject for the server end point only */
|
||||
if (plugin->type != SERVER)
|
||||
return OPENVPN_PLUGIN_FUNC_SUCCESS;
|
||||
|
||||
if (!args->current_cert) {
|
||||
ovpn_err("this example plugin requires client certificate");
|
||||
return OPENVPN_PLUGIN_FUNC_ERROR;
|
||||
}
|
||||
|
||||
session_user_set(sess, args->current_cert);
|
||||
|
||||
return OPENVPN_PLUGIN_FUNC_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
file_store(char *file, char *content)
|
||||
{
|
||||
FILE *f;
|
||||
if (!(f = fopen(file, "w+")))
|
||||
return;
|
||||
|
||||
fprintf(f, "%s", content);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void
|
||||
server_store(struct openvpn_plugin_args_func_in const *args)
|
||||
{
|
||||
struct plugin *plugin = (struct plugin *)args->handle;
|
||||
struct session *sess = (struct session *)args->per_client_context;
|
||||
|
||||
char file[MAXPATH];
|
||||
snprintf(file, sizeof(file) - 1, "/tmp/openvpn_sso_%s", sess->key);
|
||||
ovpn_note("app session file: %s", file);
|
||||
file_store(file, sess->user);
|
||||
}
|
||||
|
||||
static void
|
||||
client_store(struct openvpn_plugin_args_func_in const *args)
|
||||
{
|
||||
struct plugin *plugin = (struct plugin *)args->handle;
|
||||
struct session *sess = (struct session *)args->per_client_context;
|
||||
|
||||
char *file = "/tmp/openvpn_sso_user";
|
||||
ovpn_note("app session file: %s", file);
|
||||
file_store(file, sess->key);
|
||||
}
|
||||
|
||||
static int
|
||||
tls_final(struct openvpn_plugin_args_func_in const *args,
|
||||
struct openvpn_plugin_args_func_return *rv)
|
||||
{
|
||||
struct plugin *plugin = (struct plugin *)args->handle;
|
||||
struct session *sess = (struct session *)args->per_client_context;
|
||||
|
||||
const char *key;
|
||||
if (!(key = get_env ("exported_keying_material", args->envp)))
|
||||
return OPENVPN_PLUGIN_FUNC_ERROR;
|
||||
|
||||
snprintf(sess->key, sizeof(sess->key) - 1, "%s", key);
|
||||
ovpn_note("app session key: %s", sess->key);
|
||||
|
||||
switch (plugin->type) {
|
||||
case SERVER:
|
||||
server_store(args);
|
||||
break;
|
||||
case CLIENT:
|
||||
client_store(args);
|
||||
return OPENVPN_PLUGIN_FUNC_SUCCESS;
|
||||
}
|
||||
|
||||
ovpn_note("app session user: %s", sess->user);
|
||||
return OPENVPN_PLUGIN_FUNC_SUCCESS;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
switch(args->type) {
|
||||
case OPENVPN_PLUGIN_TLS_VERIFY:
|
||||
return tls_verify(args);
|
||||
case OPENVPN_PLUGIN_TLS_FINAL:
|
||||
return tls_final(args, rv);
|
||||
}
|
||||
return OPENVPN_PLUGIN_FUNC_SUCCESS;
|
||||
}
|
||||
|
||||
OPENVPN_EXPORT void *
|
||||
openvpn_plugin_client_constructor_v1(openvpn_plugin_handle_t handle)
|
||||
{
|
||||
struct plugin *plugin = (struct plugin *)handle;
|
||||
struct session *sess = calloc (1, sizeof(*sess));
|
||||
|
||||
ovpn_note("app session created");
|
||||
|
||||
return (void *)sess;
|
||||
}
|
||||
|
||||
OPENVPN_EXPORT void
|
||||
openvpn_plugin_client_destructor_v1(openvpn_plugin_handle_t handle, void *ctx)
|
||||
{
|
||||
struct plugin *plugin = (struct plugin *)handle;
|
||||
struct session *sess = (struct session *)ctx;
|
||||
|
||||
ovpn_note("app session key: %s", sess->key);
|
||||
ovpn_note("app session destroyed");
|
||||
|
||||
free (sess);
|
||||
}
|
||||
|
||||
OPENVPN_EXPORT void
|
||||
openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
|
||||
{
|
||||
struct plugin *plugin = (struct plugin *)handle;
|
||||
free (plugin);
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
tls-server
|
||||
reneg-sec 0
|
||||
|
||||
keying-material-exporter "EXPORTER_SSO_TEST" 16
|
||||
duplicate-cn
|
||||
|
||||
plugin ./keyingmaterialexporter.so
|
||||
ca ../../sample-keys/ca.crt
|
||||
cert ../../sample-keys/server.crt
|
||||
key ../../sample-keys/server.key
|
||||
dh ../../sample-keys/dh2048.pem
|
||||
|
||||
server 10.8.0.0 255.255.255.0
|
||||
port 1194
|
||||
proto udp
|
||||
dev tun
|
||||
|
||||
verb 4
|
||||
Loading…
Reference in a new issue