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) 2017  David Sommerseth <davids@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 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 
29 #include "openvpn-plugin.h"
30 
31 #define PLUGIN_NAME "base64.c"
32 
33 /* Exported plug-in v3 API functions */
34 plugin_log_t ovpn_log = NULL;                      /**< Pointer to the OpenVPN log function.  See plugin_log() */
35 plugin_vlog_t ovpn_vlog = NULL;                    /**< Pointer to the OpenVPN vlog function. See plugin_vlog() */
36 plugin_base64_encode_t ovpn_base64_encode = NULL;  /**< Pointer to the openvpn_base64_encode () function */
37 plugin_base64_decode_t ovpn_base64_decode = NULL;  /**< Pointer to the openvpn_base64_decode () function */
38 
39 /**
40  * Search the environment pointer for a specific env var name
41  *
42  * PLEASE NOTE! The result is not valid outside the local
43  * scope of the calling function.  Once the calling function
44  * returns, any returned pointers are invalid.
45  *
46  * @param name  String containing the env.var name to search for
47  * @param envp  String array pointer to the environment variable
48  *
49  * @return Returns a pointer to the value in the environment variable
50  *         table on successful match.  Otherwise NULL is returned
51  *
52  */
53 static const char *
get_env(const char * name,const char * envp[])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                 {
67                     return cp + 1;
68                 }
69             }
70         }
71     }
72     return NULL;
73 }
74 
75 
76 /**
77  * This function is called when OpenVPN loads the plug-in.
78  * The purpose is to initialize the plug-in and tell OpenVPN
79  * which plug-in hooks this plug-in wants to be involved in
80  *
81  * For the arguments, see the include/openvpn-plugin.h file
82  * for details on the function parameters
83  *
84  * @param v3structver  An integer containing the API version of
85  *                     the plug-in structs OpenVPN uses
86  * @param args         A pointer to the argument struct for
87  *                     information and features provided by
88  *                     OpenVPN to the plug-in
89  * @param ret          A pointer to the struct OpenVPN uses to
90  *                     receive information back from the plug-in
91  *
92  * @return Must return OPENVPN_PLUGIN_FUNC_SUCCESS when everything
93  *         completed successfully.  Otherwise it must be returned
94  *         OPENVPN_PLUGIN_FUNC_ERROR, which will stop OpenVPN
95  *         from running
96  *
97  */
98 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)99 openvpn_plugin_open_v3(const int v3structver,
100                        struct openvpn_plugin_args_open_in const *args,
101                        struct openvpn_plugin_args_open_return *ret)
102 {
103     /* Check that we are API compatible */
104     if (v3structver != OPENVPN_PLUGINv3_STRUCTVER)
105     {
106         printf("base64.c: ** ERROR ** Incompatible plug-in interface between this plug-in and OpenVPN\n");
107         return OPENVPN_PLUGIN_FUNC_ERROR;
108     }
109 
110     /*  Which callbacks to intercept.  */
111     ret->type_mask =
112         OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_VERIFY)
113         |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT_V2);
114 
115     /* we don't need a plug-in context in this example, but OpenVPN expects "something" */
116     ret->handle = calloc(1, 1);
117 
118     /* Hook into the exported functions from OpenVPN */
119     ovpn_log = args->callbacks->plugin_log;
120     ovpn_vlog = args->callbacks->plugin_vlog;
121     ovpn_base64_encode = args->callbacks->plugin_base64_encode;
122     ovpn_base64_decode = args->callbacks->plugin_base64_decode;
123 
124     /* Print some version information about the OpenVPN process using this plug-in */
125     ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OpenVPN %s  (Major: %i, Minor: %i, Patch: %s)\n",
126              args->ovpn_version, args->ovpn_version_major,
127              args->ovpn_version_minor, args->ovpn_version_patch);
128 
129     return OPENVPN_PLUGIN_FUNC_SUCCESS;
130 }
131 
132 
133 /**
134  * This function is called by OpenVPN each time the OpenVPN reaches
135  * a point where plug-in calls should happen.  It only happens for those
136  * plug-in hooks enabled in openvpn_plugin_open_v3().
137  *
138  * For the arguments, see the include/openvpn-plugin.h file
139  * for details on the function parameters
140  *
141  * @param args        Pointer to a struct with details about the plug-in
142  *                    call from the main OpenVPN process.
143  * @param returndata  Pointer to a struct where the plug-in can provide
144  *                    information back to OpenVPN to be processed
145  *
146  * @return  Must return OPENVPN_PLUGIN_FUNC_SUCCESS or
147  *          OPENVPN_PLUGIN_FUNC_DEFERRED on success.  Otherwise it
148  *          should return OPENVPN_FUNC_ERROR, which will stop and reject
149  *          the client session from progressing.
150  *
151  */
152 
153 OPENVPN_EXPORT int
openvpn_plugin_func_v1(openvpn_plugin_handle_t handle,const int type,const char * argv[],const char * envp[])154 openvpn_plugin_func_v1(openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
155 {
156     if (type != OPENVPN_PLUGIN_TLS_VERIFY
157         && type != OPENVPN_PLUGIN_CLIENT_CONNECT_V2)
158     {
159         ovpn_log(PLOG_ERR, PLUGIN_NAME, "Unsupported plug-in hook call attempted");
160         return OPENVPN_PLUGIN_FUNC_ERROR;
161     }
162 
163     /* get username/password from envp string array */
164     const char *clcert_cn = get_env("X509_0_CN", envp);
165     if (!clcert_cn)
166     {
167         /* Ignore certificate checks not being a client certificate */
168         return OPENVPN_PLUGIN_FUNC_SUCCESS;
169     }
170 
171     /* test the BASE64 encode function */
172     char *buf = NULL;
173     int r = ovpn_base64_encode(clcert_cn, strlen(clcert_cn), &buf);
174     ovpn_log(PLOG_NOTE, PLUGIN_NAME, "BASE64 encoded '%s' (return value %i):  '%s'",
175              clcert_cn, r, buf);
176 
177     /* test the BASE64 decode function */
178     char buf2[256] = {0};
179     r = ovpn_base64_decode(buf, &buf2, 255);
180     ovpn_log(PLOG_NOTE, PLUGIN_NAME, "BASE64 decoded '%s' (return value %i):  '%s'",
181              buf, r, buf2);
182 
183     /* Verify the result, and free the buffer allocated by ovpn_base64_encode() */
184     r = strcmp(clcert_cn, buf2);
185     free(buf);
186 
187     return (r == 0) ? OPENVPN_PLUGIN_FUNC_SUCCESS : OPENVPN_PLUGIN_FUNC_ERROR;
188 }
189 
190 
191 /**
192  * This cleans up the last part of the plug-in, allows it to
193  * shut down cleanly and release the plug-in global context buffer
194  *
195  * @param handle   Pointer to the plug-in global context buffer, which
196  *                 need to be released by this function
197  */
198 OPENVPN_EXPORT void
openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)199 openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
200 {
201     struct plugin_context *context = (struct plugin_context *) handle;
202     free(context);
203 }
204