git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@638 e7ae566f-a301-0410-adde-c780ea21d3b5
| ... | ... |
@@ -101,13 +101,13 @@ and portability to most major OS platforms. |
| 101 | 101 |
%__strip %{name}
|
| 102 | 102 |
|
| 103 | 103 |
# Build down-root plugin |
| 104 |
-pushd plugins/down-root |
|
| 104 |
+pushd plugin/down-root |
|
| 105 | 105 |
%__make |
| 106 | 106 |
popd |
| 107 | 107 |
|
| 108 | 108 |
# Build auth-pam plugin |
| 109 | 109 |
%if %{build_auth_pam}
|
| 110 |
-pushd plugins/auth-pam |
|
| 110 |
+pushd plugin/auth-pam |
|
| 111 | 111 |
%__make |
| 112 | 112 |
popd |
| 113 | 113 |
%endif |
| ... | ... |
@@ -151,16 +151,16 @@ popd |
| 151 | 151 |
# Install the plugins |
| 152 | 152 |
# |
| 153 | 153 |
|
| 154 |
-%__mkdir_p %{buildroot}%{_datadir}/%{name}/plugins/lib
|
|
| 154 |
+%__mkdir_p %{buildroot}%{_datadir}/%{name}/plugin/lib
|
|
| 155 | 155 |
|
| 156 | 156 |
for pi in auth-pam down-root; do |
| 157 |
- %__mv -f plugins/$pi/README plugins/README.$pi |
|
| 158 |
- if [ -e plugins/$pi/openvpn-$pi.so ]; then |
|
| 159 |
- %__install -c -m 755 plugins/$pi/openvpn-$pi.so %{buildroot}%{_datadir}/openvpn/plugins/lib/openvpn-$pi.so
|
|
| 157 |
+ %__mv -f plugin/$pi/README plugin/README.$pi |
|
| 158 |
+ if [ -e plugin/$pi/openvpn-$pi.so ]; then |
|
| 159 |
+ %__install -c -m 755 plugin/$pi/openvpn-$pi.so %{buildroot}%{_datadir}/openvpn/plugin/lib/openvpn-$pi.so
|
|
| 160 | 160 |
fi |
| 161 | 161 |
done |
| 162 | 162 |
|
| 163 |
-%__mv -f plugins/README plugins/README.plugins |
|
| 163 |
+%__mv -f plugin/README plugin/README.plugins |
|
| 164 | 164 |
|
| 165 | 165 |
# |
| 166 | 166 |
# Clean section |
| ... | ... |
@@ -220,14 +220,10 @@ fi |
| 220 | 220 |
%endif |
| 221 | 221 |
|
| 222 | 222 |
# Install extra %doc stuff |
| 223 |
-%doc contrib/ easy-rsa/ management/ sample-*/ plugins/README.* |
|
| 223 |
+%doc contrib/ easy-rsa/ management/ sample-*/ plugin/README.* |
|
| 224 | 224 |
|
| 225 | 225 |
%changelog |
| 226 | 226 |
|
| 227 |
-* Mon Oct 13 2005 James Yonan |
|
| 228 |
-- Renamed plugin directory to plugins to |
|
| 229 |
- work around automake issue. |
|
| 230 |
- |
|
| 231 | 227 |
* Mon Aug 2 2005 James Yonan |
| 232 | 228 |
- Fixed build problem with --define 'without_pam 1' |
| 233 | 229 |
|
| 234 | 230 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,47 @@ |
| 0 |
+OpenVPN Plugins |
|
| 1 |
+--------------- |
|
| 2 |
+ |
|
| 3 |
+Starting with OpenVPN 2.0-beta17, compiled plugin modules are |
|
| 4 |
+supported on any *nix OS which includes libdl or on Windows. |
|
| 5 |
+One or more modules may be loaded into OpenVPN using |
|
| 6 |
+the --plugin directive, and each plugin module is capable of |
|
| 7 |
+intercepting any of the script callbacks which OpenVPN supports: |
|
| 8 |
+ |
|
| 9 |
+(1) up |
|
| 10 |
+(2) down |
|
| 11 |
+(3) route-up |
|
| 12 |
+(4) ipchange |
|
| 13 |
+(5) tls-verify |
|
| 14 |
+(6) auth-user-pass-verify |
|
| 15 |
+(7) client-connect |
|
| 16 |
+(8) client-disconnect |
|
| 17 |
+(9) learn-address |
|
| 18 |
+ |
|
| 19 |
+See the openvpn-plugin.h file in the top-level directory of the |
|
| 20 |
+OpenVPN source distribution for more detailed information |
|
| 21 |
+on the plugin interface. |
|
| 22 |
+ |
|
| 23 |
+Included Plugins |
|
| 24 |
+---------------- |
|
| 25 |
+ |
|
| 26 |
+auth-pam -- Authenticate using PAM and a split privilege |
|
| 27 |
+ execution model which functions even if |
|
| 28 |
+ root privileges or the execution environment |
|
| 29 |
+ have been altered with --user/--group/--chroot. |
|
| 30 |
+ Tested on Linux only. |
|
| 31 |
+ |
|
| 32 |
+down-root -- Enable the running of down scripts with root privileges |
|
| 33 |
+ even if --user/--group/--chroot have been used |
|
| 34 |
+ to drop root privileges or change the execution |
|
| 35 |
+ environment. Not applicable on Windows. |
|
| 36 |
+ |
|
| 37 |
+examples -- A simple example that demonstrates a portable |
|
| 38 |
+ plugin, i.e. one which can be built for *nix |
|
| 39 |
+ or Windows from the same source. |
|
| 40 |
+ |
|
| 41 |
+Building Plugins |
|
| 42 |
+---------------- |
|
| 43 |
+ |
|
| 44 |
+cd to the top-level directory of a plugin, and use the |
|
| 45 |
+"make" command to build it. The examples plugin is |
|
| 46 |
+built using a build script, not a makefile. |
| 0 | 1 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,30 @@ |
| 0 |
+# |
|
| 1 |
+# Build the OpenVPN auth-pam plugin module. |
|
| 2 |
+# |
|
| 3 |
+ |
|
| 4 |
+# If PAM modules are not linked against libpam.so, set DLOPEN_PAM to 1. This |
|
| 5 |
+# must be done on SUSE 9.1, at least. |
|
| 6 |
+DLOPEN_PAM=1 |
|
| 7 |
+ |
|
| 8 |
+ifeq ($(DLOPEN_PAM),1) |
|
| 9 |
+ LIBPAM=-ldl |
|
| 10 |
+else |
|
| 11 |
+ LIBPAM=-lpam |
|
| 12 |
+endif |
|
| 13 |
+ |
|
| 14 |
+# This directory is where we will look for openvpn-plugin.h |
|
| 15 |
+INCLUDE=-I../.. |
|
| 16 |
+ |
|
| 17 |
+CC_FLAGS=-O2 -Wall -DDLOPEN_PAM=$(DLOPEN_PAM) |
|
| 18 |
+ |
|
| 19 |
+openvpn-auth-pam.so : auth-pam.o pamdl.o |
|
| 20 |
+ gcc ${CC_FLAGS} -fPIC -shared -Wl,-soname,openvpn-auth-pam.so -o openvpn-auth-pam.so auth-pam.o pamdl.o -lc $(LIBPAM)
|
|
| 21 |
+ |
|
| 22 |
+auth-pam.o : auth-pam.c pamdl.h |
|
| 23 |
+ gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} auth-pam.c
|
|
| 24 |
+ |
|
| 25 |
+pamdl.o : pamdl.c pamdl.h |
|
| 26 |
+ gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} pamdl.c
|
|
| 27 |
+ |
|
| 28 |
+clean : |
|
| 29 |
+ rm -f *.o *.so |
| 0 | 30 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,74 @@ |
| 0 |
+openvpn-auth-pam |
|
| 1 |
+ |
|
| 2 |
+SYNOPSIS |
|
| 3 |
+ |
|
| 4 |
+The openvpn-auth-pam module implements username/password |
|
| 5 |
+authentication via PAM, and essentially allows any authentication |
|
| 6 |
+method supported by PAM (such as LDAP, RADIUS, or Linux Shadow |
|
| 7 |
+passwords) to be used with OpenVPN. While PAM supports |
|
| 8 |
+username/password authentication, this can be combined with X509 |
|
| 9 |
+certificates to provide two indepedent levels of authentication. |
|
| 10 |
+ |
|
| 11 |
+This module uses a split privilege execution model which will |
|
| 12 |
+function even if you drop openvpn daemon privileges using the user, |
|
| 13 |
+group, or chroot directives. |
|
| 14 |
+ |
|
| 15 |
+BUILD |
|
| 16 |
+ |
|
| 17 |
+To build openvpn-auth-pam, you will need to have the pam-devel |
|
| 18 |
+package installed. |
|
| 19 |
+ |
|
| 20 |
+Build with the "make" command. The module will be named |
|
| 21 |
+openvpn-auth-pam.so |
|
| 22 |
+ |
|
| 23 |
+USAGE |
|
| 24 |
+ |
|
| 25 |
+To use this plugin module, add to your OpenVPN config file: |
|
| 26 |
+ |
|
| 27 |
+ plugin openvpn-auth-pam.so service-type |
|
| 28 |
+ |
|
| 29 |
+The required service-type parameter corresponds to |
|
| 30 |
+the PAM service definition file usually found |
|
| 31 |
+in /etc/pam.d. |
|
| 32 |
+ |
|
| 33 |
+This plugin also supports the usage of a list of name/value |
|
| 34 |
+pairs to answer PAM module queries. |
|
| 35 |
+ |
|
| 36 |
+For example: |
|
| 37 |
+ |
|
| 38 |
+ plugin openvpn-auth-pam.so "login login USERNAME password PASSWORD" |
|
| 39 |
+ |
|
| 40 |
+tells auth-pam to (a) use the "login" PAM module, (b) answer a |
|
| 41 |
+"login" query with the username given by the OpenVPN client, and |
|
| 42 |
+(c) answer a "password" query with the password given by the |
|
| 43 |
+OpenVPN client. This provides flexibility in dealing with the different |
|
| 44 |
+types of query strings which different PAM modules might generate. |
|
| 45 |
+For example, suppose you were using a PAM module called |
|
| 46 |
+"test" which queried for "name" rather than "login": |
|
| 47 |
+ |
|
| 48 |
+ plugin openvpn-auth-pam.so "test name USERNAME password PASSWORD" |
|
| 49 |
+ |
|
| 50 |
+While "USERNAME" and "PASSWORD" are special strings which substitute |
|
| 51 |
+to client-supplied values, it is also possible to name literal values |
|
| 52 |
+to use as PAM module query responses. For example, suppose that the |
|
| 53 |
+login module queried for a third parameter, "domain" which |
|
| 54 |
+is to be answered with the constant value "mydomain.com": |
|
| 55 |
+ |
|
| 56 |
+ plugin openvpn-auth-pam.so "login login USERNAME password PASSWORD domain mydomain.com" |
|
| 57 |
+ |
|
| 58 |
+The following OpenVPN directives can also influence |
|
| 59 |
+the operation of this plugin: |
|
| 60 |
+ |
|
| 61 |
+ client-cert-not-required |
|
| 62 |
+ username-as-common-name |
|
| 63 |
+ |
|
| 64 |
+Run OpenVPN with --verb 7 or higher to get debugging output from |
|
| 65 |
+this plugin, including the list of queries presented by the |
|
| 66 |
+underlying PAM module. This is a useful debugging tool to figure |
|
| 67 |
+out which queries a given PAM module is making, so that you can |
|
| 68 |
+craft the appropriate plugin directive to answer it. |
|
| 69 |
+ |
|
| 70 |
+CAVEATS |
|
| 71 |
+ |
|
| 72 |
+This module will only work on *nix systems which support PAM, |
|
| 73 |
+not Windows. |
| 0 | 74 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,761 @@ |
| 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-2005 OpenVPN Solutions LLC <info@openvpn.net> |
|
| 8 |
+ * |
|
| 9 |
+ * This program is free software; you can redistribute it and/or modify |
|
| 10 |
+ * it under the terms of the GNU General Public License version 2 |
|
| 11 |
+ * as published by the Free Software Foundation. |
|
| 12 |
+ * |
|
| 13 |
+ * This program is distributed in the hope that it will be useful, |
|
| 14 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 15 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 16 |
+ * GNU General Public License for more details. |
|
| 17 |
+ * |
|
| 18 |
+ * You should have received a copy of the GNU General Public License |
|
| 19 |
+ * along with this program (see the file COPYING included with this |
|
| 20 |
+ * distribution); if not, write to the Free Software Foundation, Inc., |
|
| 21 |
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 22 |
+ */ |
|
| 23 |
+ |
|
| 24 |
+/* |
|
| 25 |
+ * OpenVPN plugin module to do PAM authentication using a split |
|
| 26 |
+ * privilege model. |
|
| 27 |
+ */ |
|
| 28 |
+ |
|
| 29 |
+#if DLOPEN_PAM |
|
| 30 |
+#include <dlfcn.h> |
|
| 31 |
+#include "pamdl.h" |
|
| 32 |
+#else |
|
| 33 |
+#include <security/pam_appl.h> |
|
| 34 |
+#endif |
|
| 35 |
+ |
|
| 36 |
+#include <stdio.h> |
|
| 37 |
+#include <string.h> |
|
| 38 |
+#include <ctype.h> |
|
| 39 |
+#include <unistd.h> |
|
| 40 |
+#include <stdlib.h> |
|
| 41 |
+#include <sys/types.h> |
|
| 42 |
+#include <sys/socket.h> |
|
| 43 |
+#include <sys/wait.h> |
|
| 44 |
+#include <fcntl.h> |
|
| 45 |
+#include <signal.h> |
|
| 46 |
+#include <syslog.h> |
|
| 47 |
+ |
|
| 48 |
+#include "openvpn-plugin.h" |
|
| 49 |
+ |
|
| 50 |
+#define DEBUG(verb) ((verb) >= 4) |
|
| 51 |
+ |
|
| 52 |
+/* Command codes for foreground -> background communication */ |
|
| 53 |
+#define COMMAND_VERIFY 0 |
|
| 54 |
+#define COMMAND_EXIT 1 |
|
| 55 |
+ |
|
| 56 |
+/* Response codes for background -> foreground communication */ |
|
| 57 |
+#define RESPONSE_INIT_SUCCEEDED 10 |
|
| 58 |
+#define RESPONSE_INIT_FAILED 11 |
|
| 59 |
+#define RESPONSE_VERIFY_SUCCEEDED 12 |
|
| 60 |
+#define RESPONSE_VERIFY_FAILED 13 |
|
| 61 |
+ |
|
| 62 |
+/* |
|
| 63 |
+ * Plugin state, used by foreground |
|
| 64 |
+ */ |
|
| 65 |
+struct auth_pam_context |
|
| 66 |
+{
|
|
| 67 |
+ /* Foreground's socket to background process */ |
|
| 68 |
+ int foreground_fd; |
|
| 69 |
+ |
|
| 70 |
+ /* Process ID of background process */ |
|
| 71 |
+ pid_t background_pid; |
|
| 72 |
+ |
|
| 73 |
+ /* Verbosity level of OpenVPN */ |
|
| 74 |
+ int verb; |
|
| 75 |
+}; |
|
| 76 |
+ |
|
| 77 |
+/* |
|
| 78 |
+ * Name/Value pairs for conversation function. |
|
| 79 |
+ * Special Values: |
|
| 80 |
+ * |
|
| 81 |
+ * "USERNAME" -- substitute client-supplied username |
|
| 82 |
+ * "PASSWORD" -- substitute client-specified password |
|
| 83 |
+ */ |
|
| 84 |
+ |
|
| 85 |
+#define N_NAME_VALUE 16 |
|
| 86 |
+ |
|
| 87 |
+struct name_value {
|
|
| 88 |
+ const char *name; |
|
| 89 |
+ const char *value; |
|
| 90 |
+}; |
|
| 91 |
+ |
|
| 92 |
+struct name_value_list {
|
|
| 93 |
+ int len; |
|
| 94 |
+ struct name_value data[N_NAME_VALUE]; |
|
| 95 |
+}; |
|
| 96 |
+ |
|
| 97 |
+/* |
|
| 98 |
+ * Used to pass the username/password |
|
| 99 |
+ * to the PAM conversation function. |
|
| 100 |
+ */ |
|
| 101 |
+struct user_pass {
|
|
| 102 |
+ int verb; |
|
| 103 |
+ |
|
| 104 |
+ char username[128]; |
|
| 105 |
+ char password[128]; |
|
| 106 |
+ |
|
| 107 |
+ const struct name_value_list *name_value_list; |
|
| 108 |
+}; |
|
| 109 |
+ |
|
| 110 |
+/* Background process function */ |
|
| 111 |
+static void pam_server (int fd, const char *service, int verb, const struct name_value_list *name_value_list); |
|
| 112 |
+ |
|
| 113 |
+/* |
|
| 114 |
+ * Given an environmental variable name, search |
|
| 115 |
+ * the envp array for its value, returning it |
|
| 116 |
+ * if found or NULL otherwise. |
|
| 117 |
+ */ |
|
| 118 |
+static const char * |
|
| 119 |
+get_env (const char *name, const char *envp[]) |
|
| 120 |
+{
|
|
| 121 |
+ if (envp) |
|
| 122 |
+ {
|
|
| 123 |
+ int i; |
|
| 124 |
+ const int namelen = strlen (name); |
|
| 125 |
+ for (i = 0; envp[i]; ++i) |
|
| 126 |
+ {
|
|
| 127 |
+ if (!strncmp (envp[i], name, namelen)) |
|
| 128 |
+ {
|
|
| 129 |
+ const char *cp = envp[i] + namelen; |
|
| 130 |
+ if (*cp == '=') |
|
| 131 |
+ return cp + 1; |
|
| 132 |
+ } |
|
| 133 |
+ } |
|
| 134 |
+ } |
|
| 135 |
+ return NULL; |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+/* |
|
| 139 |
+ * Return the length of a string array |
|
| 140 |
+ */ |
|
| 141 |
+static int |
|
| 142 |
+string_array_len (const char *array[]) |
|
| 143 |
+{
|
|
| 144 |
+ int i = 0; |
|
| 145 |
+ if (array) |
|
| 146 |
+ {
|
|
| 147 |
+ while (array[i]) |
|
| 148 |
+ ++i; |
|
| 149 |
+ } |
|
| 150 |
+ return i; |
|
| 151 |
+} |
|
| 152 |
+ |
|
| 153 |
+/* |
|
| 154 |
+ * Socket read/write functions. |
|
| 155 |
+ */ |
|
| 156 |
+ |
|
| 157 |
+static int |
|
| 158 |
+recv_control (int fd) |
|
| 159 |
+{
|
|
| 160 |
+ unsigned char c; |
|
| 161 |
+ const ssize_t size = read (fd, &c, sizeof (c)); |
|
| 162 |
+ if (size == sizeof (c)) |
|
| 163 |
+ return c; |
|
| 164 |
+ else |
|
| 165 |
+ {
|
|
| 166 |
+ /*fprintf (stderr, "AUTH-PAM: DEBUG recv_control.read=%d\n", (int)size);*/ |
|
| 167 |
+ return -1; |
|
| 168 |
+ } |
|
| 169 |
+} |
|
| 170 |
+ |
|
| 171 |
+static int |
|
| 172 |
+send_control (int fd, int code) |
|
| 173 |
+{
|
|
| 174 |
+ unsigned char c = (unsigned char) code; |
|
| 175 |
+ const ssize_t size = write (fd, &c, sizeof (c)); |
|
| 176 |
+ if (size == sizeof (c)) |
|
| 177 |
+ return (int) size; |
|
| 178 |
+ else |
|
| 179 |
+ return -1; |
|
| 180 |
+} |
|
| 181 |
+ |
|
| 182 |
+static int |
|
| 183 |
+recv_string (int fd, char *buffer, int len) |
|
| 184 |
+{
|
|
| 185 |
+ if (len > 0) |
|
| 186 |
+ {
|
|
| 187 |
+ ssize_t size; |
|
| 188 |
+ memset (buffer, 0, len); |
|
| 189 |
+ size = read (fd, buffer, len); |
|
| 190 |
+ buffer[len-1] = 0; |
|
| 191 |
+ if (size >= 1) |
|
| 192 |
+ return (int)size; |
|
| 193 |
+ } |
|
| 194 |
+ return -1; |
|
| 195 |
+} |
|
| 196 |
+ |
|
| 197 |
+static int |
|
| 198 |
+send_string (int fd, const char *string) |
|
| 199 |
+{
|
|
| 200 |
+ const int len = strlen (string) + 1; |
|
| 201 |
+ const ssize_t size = write (fd, string, len); |
|
| 202 |
+ if (size == len) |
|
| 203 |
+ return (int) size; |
|
| 204 |
+ else |
|
| 205 |
+ return -1; |
|
| 206 |
+} |
|
| 207 |
+ |
|
| 208 |
+#ifdef DO_DAEMONIZE |
|
| 209 |
+ |
|
| 210 |
+/* |
|
| 211 |
+ * Daemonize if "daemon" env var is true. |
|
| 212 |
+ * Preserve stderr across daemonization if |
|
| 213 |
+ * "daemon_log_redirect" env var is true. |
|
| 214 |
+ */ |
|
| 215 |
+static void |
|
| 216 |
+daemonize (const char *envp[]) |
|
| 217 |
+{
|
|
| 218 |
+ const char *daemon_string = get_env ("daemon", envp);
|
|
| 219 |
+ if (daemon_string && daemon_string[0] == '1') |
|
| 220 |
+ {
|
|
| 221 |
+ const char *log_redirect = get_env ("daemon_log_redirect", envp);
|
|
| 222 |
+ int fd = -1; |
|
| 223 |
+ if (log_redirect && log_redirect[0] == '1') |
|
| 224 |
+ fd = dup (2); |
|
| 225 |
+ if (daemon (0, 0) < 0) |
|
| 226 |
+ {
|
|
| 227 |
+ fprintf (stderr, "AUTH-PAM: daemonization failed\n"); |
|
| 228 |
+ } |
|
| 229 |
+ else if (fd >= 3) |
|
| 230 |
+ {
|
|
| 231 |
+ dup2 (fd, 2); |
|
| 232 |
+ close (fd); |
|
| 233 |
+ } |
|
| 234 |
+ } |
|
| 235 |
+} |
|
| 236 |
+ |
|
| 237 |
+#endif |
|
| 238 |
+ |
|
| 239 |
+/* |
|
| 240 |
+ * Close most of parent's fds. |
|
| 241 |
+ * Keep stdin/stdout/stderr, plus one |
|
| 242 |
+ * other fd which is presumed to be |
|
| 243 |
+ * our pipe back to parent. |
|
| 244 |
+ * Admittedly, a bit of a kludge, |
|
| 245 |
+ * but posix doesn't give us a kind |
|
| 246 |
+ * of FD_CLOEXEC which will stop |
|
| 247 |
+ * fds from crossing a fork(). |
|
| 248 |
+ */ |
|
| 249 |
+static void |
|
| 250 |
+close_fds_except (int keep) |
|
| 251 |
+{
|
|
| 252 |
+ int i; |
|
| 253 |
+ closelog (); |
|
| 254 |
+ for (i = 3; i <= 100; ++i) |
|
| 255 |
+ {
|
|
| 256 |
+ if (i != keep) |
|
| 257 |
+ close (i); |
|
| 258 |
+ } |
|
| 259 |
+} |
|
| 260 |
+ |
|
| 261 |
+/* |
|
| 262 |
+ * Usually we ignore signals, because our parent will |
|
| 263 |
+ * deal with them. |
|
| 264 |
+ */ |
|
| 265 |
+static void |
|
| 266 |
+set_signals (void) |
|
| 267 |
+{
|
|
| 268 |
+ signal (SIGTERM, SIG_DFL); |
|
| 269 |
+ |
|
| 270 |
+ signal (SIGINT, SIG_IGN); |
|
| 271 |
+ signal (SIGHUP, SIG_IGN); |
|
| 272 |
+ signal (SIGUSR1, SIG_IGN); |
|
| 273 |
+ signal (SIGUSR2, SIG_IGN); |
|
| 274 |
+ signal (SIGPIPE, SIG_IGN); |
|
| 275 |
+} |
|
| 276 |
+ |
|
| 277 |
+/* |
|
| 278 |
+ * Return 1 if query matches match. |
|
| 279 |
+ */ |
|
| 280 |
+static int |
|
| 281 |
+name_value_match (const char *query, const char *match) |
|
| 282 |
+{
|
|
| 283 |
+ while (!isalnum (*query)) |
|
| 284 |
+ {
|
|
| 285 |
+ if (*query == '\0') |
|
| 286 |
+ return 0; |
|
| 287 |
+ ++query; |
|
| 288 |
+ } |
|
| 289 |
+ return strncasecmp (match, query, strlen (match)) == 0; |
|
| 290 |
+} |
|
| 291 |
+ |
|
| 292 |
+OPENVPN_EXPORT openvpn_plugin_handle_t |
|
| 293 |
+openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) |
|
| 294 |
+{
|
|
| 295 |
+ pid_t pid; |
|
| 296 |
+ int fd[2]; |
|
| 297 |
+ |
|
| 298 |
+ struct auth_pam_context *context; |
|
| 299 |
+ struct name_value_list name_value_list; |
|
| 300 |
+ |
|
| 301 |
+ const int base_parms = 2; |
|
| 302 |
+ |
|
| 303 |
+ /* |
|
| 304 |
+ * Allocate our context |
|
| 305 |
+ */ |
|
| 306 |
+ context = (struct auth_pam_context *) calloc (1, sizeof (struct auth_pam_context)); |
|
| 307 |
+ context->foreground_fd = -1; |
|
| 308 |
+ |
|
| 309 |
+ /* |
|
| 310 |
+ * Intercept the --auth-user-pass-verify callback. |
|
| 311 |
+ */ |
|
| 312 |
+ *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY); |
|
| 313 |
+ |
|
| 314 |
+ /* |
|
| 315 |
+ * Make sure we have two string arguments: the first is the .so name, |
|
| 316 |
+ * the second is the PAM service type. |
|
| 317 |
+ */ |
|
| 318 |
+ if (string_array_len (argv) < base_parms) |
|
| 319 |
+ {
|
|
| 320 |
+ fprintf (stderr, "AUTH-PAM: need PAM service parameter\n"); |
|
| 321 |
+ goto error; |
|
| 322 |
+ } |
|
| 323 |
+ |
|
| 324 |
+ /* |
|
| 325 |
+ * See if we have optional name/value pairs to match against |
|
| 326 |
+ * PAM module queried fields in the conversation function. |
|
| 327 |
+ */ |
|
| 328 |
+ name_value_list.len = 0; |
|
| 329 |
+ if (string_array_len (argv) > base_parms) |
|
| 330 |
+ {
|
|
| 331 |
+ const int nv_len = string_array_len (argv) - base_parms; |
|
| 332 |
+ int i; |
|
| 333 |
+ |
|
| 334 |
+ if ((nv_len & 1) == 1 || (nv_len / 2) > N_NAME_VALUE) |
|
| 335 |
+ {
|
|
| 336 |
+ fprintf (stderr, "AUTH-PAM: bad name/value list length\n"); |
|
| 337 |
+ goto error; |
|
| 338 |
+ } |
|
| 339 |
+ |
|
| 340 |
+ name_value_list.len = nv_len / 2; |
|
| 341 |
+ for (i = 0; i < name_value_list.len; ++i) |
|
| 342 |
+ {
|
|
| 343 |
+ const int base = base_parms + i * 2; |
|
| 344 |
+ name_value_list.data[i].name = argv[base]; |
|
| 345 |
+ name_value_list.data[i].value = argv[base+1]; |
|
| 346 |
+ } |
|
| 347 |
+ } |
|
| 348 |
+ |
|
| 349 |
+ /* |
|
| 350 |
+ * Get verbosity level from environment |
|
| 351 |
+ */ |
|
| 352 |
+ {
|
|
| 353 |
+ const char *verb_string = get_env ("verb", envp);
|
|
| 354 |
+ if (verb_string) |
|
| 355 |
+ context->verb = atoi (verb_string); |
|
| 356 |
+ } |
|
| 357 |
+ |
|
| 358 |
+ /* |
|
| 359 |
+ * Make a socket for foreground and background processes |
|
| 360 |
+ * to communicate. |
|
| 361 |
+ */ |
|
| 362 |
+ if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1) |
|
| 363 |
+ {
|
|
| 364 |
+ fprintf (stderr, "AUTH-PAM: socketpair call failed\n"); |
|
| 365 |
+ goto error; |
|
| 366 |
+ } |
|
| 367 |
+ |
|
| 368 |
+ /* |
|
| 369 |
+ * Fork off the privileged process. It will remain privileged |
|
| 370 |
+ * even after the foreground process drops its privileges. |
|
| 371 |
+ */ |
|
| 372 |
+ pid = fork (); |
|
| 373 |
+ |
|
| 374 |
+ if (pid) |
|
| 375 |
+ {
|
|
| 376 |
+ int status; |
|
| 377 |
+ |
|
| 378 |
+ /* |
|
| 379 |
+ * Foreground Process |
|
| 380 |
+ */ |
|
| 381 |
+ |
|
| 382 |
+ context->background_pid = pid; |
|
| 383 |
+ |
|
| 384 |
+ /* close our copy of child's socket */ |
|
| 385 |
+ close (fd[1]); |
|
| 386 |
+ |
|
| 387 |
+ /* don't let future subprocesses inherit child socket */ |
|
| 388 |
+ if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0) |
|
| 389 |
+ fprintf (stderr, "AUTH-PAM: Set FD_CLOEXEC flag on socket file descriptor failed\n"); |
|
| 390 |
+ |
|
| 391 |
+ /* wait for background child process to initialize */ |
|
| 392 |
+ status = recv_control (fd[0]); |
|
| 393 |
+ if (status == RESPONSE_INIT_SUCCEEDED) |
|
| 394 |
+ {
|
|
| 395 |
+ context->foreground_fd = fd[0]; |
|
| 396 |
+ return (openvpn_plugin_handle_t) context; |
|
| 397 |
+ } |
|
| 398 |
+ } |
|
| 399 |
+ else |
|
| 400 |
+ {
|
|
| 401 |
+ /* |
|
| 402 |
+ * Background Process |
|
| 403 |
+ */ |
|
| 404 |
+ |
|
| 405 |
+ /* close all parent fds except our socket back to parent */ |
|
| 406 |
+ close_fds_except (fd[1]); |
|
| 407 |
+ |
|
| 408 |
+ /* Ignore most signals (the parent will receive them) */ |
|
| 409 |
+ set_signals (); |
|
| 410 |
+ |
|
| 411 |
+#ifdef DO_DAEMONIZE |
|
| 412 |
+ /* Daemonize if --daemon option is set. */ |
|
| 413 |
+ daemonize (envp); |
|
| 414 |
+#endif |
|
| 415 |
+ |
|
| 416 |
+ /* execute the event loop */ |
|
| 417 |
+ pam_server (fd[1], argv[1], context->verb, &name_value_list); |
|
| 418 |
+ |
|
| 419 |
+ close (fd[1]); |
|
| 420 |
+ |
|
| 421 |
+ exit (0); |
|
| 422 |
+ return 0; /* NOTREACHED */ |
|
| 423 |
+ } |
|
| 424 |
+ |
|
| 425 |
+ error: |
|
| 426 |
+ if (context) |
|
| 427 |
+ free (context); |
|
| 428 |
+ return NULL; |
|
| 429 |
+} |
|
| 430 |
+ |
|
| 431 |
+OPENVPN_EXPORT int |
|
| 432 |
+openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) |
|
| 433 |
+{
|
|
| 434 |
+ struct auth_pam_context *context = (struct auth_pam_context *) handle; |
|
| 435 |
+ |
|
| 436 |
+ if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY && context->foreground_fd >= 0) |
|
| 437 |
+ {
|
|
| 438 |
+ /* get username/password from envp string array */ |
|
| 439 |
+ const char *username = get_env ("username", envp);
|
|
| 440 |
+ const char *password = get_env ("password", envp);
|
|
| 441 |
+ |
|
| 442 |
+ if (username && strlen (username) > 0 && password) |
|
| 443 |
+ {
|
|
| 444 |
+ if (send_control (context->foreground_fd, COMMAND_VERIFY) == -1 |
|
| 445 |
+ || send_string (context->foreground_fd, username) == -1 |
|
| 446 |
+ || send_string (context->foreground_fd, password) == -1) |
|
| 447 |
+ {
|
|
| 448 |
+ fprintf (stderr, "AUTH-PAM: Error sending auth info to background process\n"); |
|
| 449 |
+ } |
|
| 450 |
+ else |
|
| 451 |
+ {
|
|
| 452 |
+ const int status = recv_control (context->foreground_fd); |
|
| 453 |
+ if (status == RESPONSE_VERIFY_SUCCEEDED) |
|
| 454 |
+ return OPENVPN_PLUGIN_FUNC_SUCCESS; |
|
| 455 |
+ if (status == -1) |
|
| 456 |
+ fprintf (stderr, "AUTH-PAM: Error receiving auth confirmation from background process\n"); |
|
| 457 |
+ } |
|
| 458 |
+ } |
|
| 459 |
+ } |
|
| 460 |
+ return OPENVPN_PLUGIN_FUNC_ERROR; |
|
| 461 |
+} |
|
| 462 |
+ |
|
| 463 |
+OPENVPN_EXPORT void |
|
| 464 |
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) |
|
| 465 |
+{
|
|
| 466 |
+ struct auth_pam_context *context = (struct auth_pam_context *) handle; |
|
| 467 |
+ |
|
| 468 |
+ if (DEBUG (context->verb)) |
|
| 469 |
+ fprintf (stderr, "AUTH-PAM: close\n"); |
|
| 470 |
+ |
|
| 471 |
+ if (context->foreground_fd >= 0) |
|
| 472 |
+ {
|
|
| 473 |
+ /* tell background process to exit */ |
|
| 474 |
+ if (send_control (context->foreground_fd, COMMAND_EXIT) == -1) |
|
| 475 |
+ fprintf (stderr, "AUTH-PAM: Error signaling background process to exit\n"); |
|
| 476 |
+ |
|
| 477 |
+ /* wait for background process to exit */ |
|
| 478 |
+ if (context->background_pid > 0) |
|
| 479 |
+ waitpid (context->background_pid, NULL, 0); |
|
| 480 |
+ |
|
| 481 |
+ close (context->foreground_fd); |
|
| 482 |
+ context->foreground_fd = -1; |
|
| 483 |
+ } |
|
| 484 |
+ |
|
| 485 |
+ free (context); |
|
| 486 |
+} |
|
| 487 |
+ |
|
| 488 |
+OPENVPN_EXPORT void |
|
| 489 |
+openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle) |
|
| 490 |
+{
|
|
| 491 |
+ struct auth_pam_context *context = (struct auth_pam_context *) handle; |
|
| 492 |
+ |
|
| 493 |
+ /* tell background process to exit */ |
|
| 494 |
+ if (context->foreground_fd >= 0) |
|
| 495 |
+ {
|
|
| 496 |
+ send_control (context->foreground_fd, COMMAND_EXIT); |
|
| 497 |
+ close (context->foreground_fd); |
|
| 498 |
+ context->foreground_fd = -1; |
|
| 499 |
+ } |
|
| 500 |
+} |
|
| 501 |
+ |
|
| 502 |
+/* |
|
| 503 |
+ * PAM conversation function |
|
| 504 |
+ */ |
|
| 505 |
+static int |
|
| 506 |
+my_conv (int n, const struct pam_message **msg_array, |
|
| 507 |
+ struct pam_response **response_array, void *appdata_ptr) |
|
| 508 |
+{
|
|
| 509 |
+ const struct user_pass *up = ( const struct user_pass *) appdata_ptr; |
|
| 510 |
+ struct pam_response *aresp; |
|
| 511 |
+ int i; |
|
| 512 |
+ int ret = PAM_SUCCESS; |
|
| 513 |
+ |
|
| 514 |
+ *response_array = NULL; |
|
| 515 |
+ |
|
| 516 |
+ if (n <= 0 || n > PAM_MAX_NUM_MSG) |
|
| 517 |
+ return (PAM_CONV_ERR); |
|
| 518 |
+ if ((aresp = calloc (n, sizeof *aresp)) == NULL) |
|
| 519 |
+ return (PAM_BUF_ERR); |
|
| 520 |
+ |
|
| 521 |
+ /* loop through each PAM-module query */ |
|
| 522 |
+ for (i = 0; i < n; ++i) |
|
| 523 |
+ {
|
|
| 524 |
+ const struct pam_message *msg = msg_array[i]; |
|
| 525 |
+ aresp[i].resp_retcode = 0; |
|
| 526 |
+ aresp[i].resp = NULL; |
|
| 527 |
+ |
|
| 528 |
+ if (DEBUG (up->verb)) |
|
| 529 |
+ {
|
|
| 530 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: my_conv[%d] query='%s' style=%d\n", |
|
| 531 |
+ i, |
|
| 532 |
+ msg->msg ? msg->msg : "NULL", |
|
| 533 |
+ msg->msg_style); |
|
| 534 |
+ } |
|
| 535 |
+ |
|
| 536 |
+ if (up->name_value_list && up->name_value_list->len > 0) |
|
| 537 |
+ {
|
|
| 538 |
+ /* use name/value list match method */ |
|
| 539 |
+ const struct name_value_list *list = up->name_value_list; |
|
| 540 |
+ int j; |
|
| 541 |
+ |
|
| 542 |
+ /* loop through name/value pairs */ |
|
| 543 |
+ for (j = 0; j < list->len; ++j) |
|
| 544 |
+ {
|
|
| 545 |
+ const char *match_name = list->data[j].name; |
|
| 546 |
+ const char *match_value = list->data[j].value; |
|
| 547 |
+ |
|
| 548 |
+ if (name_value_match (msg->msg, match_name)) |
|
| 549 |
+ {
|
|
| 550 |
+ /* found name/value match */ |
|
| 551 |
+ const char *return_value = NULL; |
|
| 552 |
+ |
|
| 553 |
+ if (DEBUG (up->verb)) |
|
| 554 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: name match found, query/match-string ['%s', '%s'] = '%s'\n", |
|
| 555 |
+ msg->msg, |
|
| 556 |
+ match_name, |
|
| 557 |
+ match_value); |
|
| 558 |
+ |
|
| 559 |
+ if (!strcmp (match_value, "USERNAME")) |
|
| 560 |
+ return_value = up->username; |
|
| 561 |
+ else if (!strcmp (match_value, "PASSWORD")) |
|
| 562 |
+ return_value = up->password; |
|
| 563 |
+ else |
|
| 564 |
+ return_value = match_value; |
|
| 565 |
+ |
|
| 566 |
+ aresp[i].resp = strdup (return_value); |
|
| 567 |
+ if (aresp[i].resp == NULL) |
|
| 568 |
+ ret = PAM_CONV_ERR; |
|
| 569 |
+ break; |
|
| 570 |
+ } |
|
| 571 |
+ } |
|
| 572 |
+ |
|
| 573 |
+ if (j == list->len) |
|
| 574 |
+ ret = PAM_CONV_ERR; |
|
| 575 |
+ } |
|
| 576 |
+ else |
|
| 577 |
+ {
|
|
| 578 |
+ /* use PAM_PROMPT_ECHO_x hints */ |
|
| 579 |
+ switch (msg->msg_style) |
|
| 580 |
+ {
|
|
| 581 |
+ case PAM_PROMPT_ECHO_OFF: |
|
| 582 |
+ aresp[i].resp = strdup (up->password); |
|
| 583 |
+ if (aresp[i].resp == NULL) |
|
| 584 |
+ ret = PAM_CONV_ERR; |
|
| 585 |
+ break; |
|
| 586 |
+ |
|
| 587 |
+ case PAM_PROMPT_ECHO_ON: |
|
| 588 |
+ aresp[i].resp = strdup (up->username); |
|
| 589 |
+ if (aresp[i].resp == NULL) |
|
| 590 |
+ ret = PAM_CONV_ERR; |
|
| 591 |
+ break; |
|
| 592 |
+ |
|
| 593 |
+ case PAM_ERROR_MSG: |
|
| 594 |
+ case PAM_TEXT_INFO: |
|
| 595 |
+ break; |
|
| 596 |
+ |
|
| 597 |
+ default: |
|
| 598 |
+ ret = PAM_CONV_ERR; |
|
| 599 |
+ break; |
|
| 600 |
+ } |
|
| 601 |
+ } |
|
| 602 |
+ } |
|
| 603 |
+ |
|
| 604 |
+ if (ret == PAM_SUCCESS) |
|
| 605 |
+ *response_array = aresp; |
|
| 606 |
+ return ret; |
|
| 607 |
+} |
|
| 608 |
+ |
|
| 609 |
+/* |
|
| 610 |
+ * Return 1 if authenticated and 0 if failed. |
|
| 611 |
+ * Called once for every username/password |
|
| 612 |
+ * to be authenticated. |
|
| 613 |
+ */ |
|
| 614 |
+static int |
|
| 615 |
+pam_auth (const char *service, const struct user_pass *up) |
|
| 616 |
+{
|
|
| 617 |
+ struct pam_conv conv; |
|
| 618 |
+ pam_handle_t *pamh = NULL; |
|
| 619 |
+ int status = PAM_SUCCESS; |
|
| 620 |
+ int ret = 0; |
|
| 621 |
+ const int name_value_list_provided = (up->name_value_list && up->name_value_list->len > 0); |
|
| 622 |
+ |
|
| 623 |
+ /* Initialize PAM */ |
|
| 624 |
+ conv.conv = my_conv; |
|
| 625 |
+ conv.appdata_ptr = (void *)up; |
|
| 626 |
+ status = pam_start (service, name_value_list_provided ? NULL : up->username, &conv, &pamh); |
|
| 627 |
+ if (status == PAM_SUCCESS) |
|
| 628 |
+ {
|
|
| 629 |
+ /* Call PAM to verify username/password */ |
|
| 630 |
+ status = pam_authenticate(pamh, 0); |
|
| 631 |
+ if (status == PAM_SUCCESS) |
|
| 632 |
+ status = pam_acct_mgmt (pamh, 0); |
|
| 633 |
+ if (status == PAM_SUCCESS) |
|
| 634 |
+ ret = 1; |
|
| 635 |
+ |
|
| 636 |
+ /* Output error message if failed */ |
|
| 637 |
+ if (!ret) |
|
| 638 |
+ {
|
|
| 639 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: user '%s' failed to authenticate: %s\n", |
|
| 640 |
+ up->username, |
|
| 641 |
+ pam_strerror (pamh, status)); |
|
| 642 |
+ } |
|
| 643 |
+ |
|
| 644 |
+ /* Close PAM */ |
|
| 645 |
+ pam_end (pamh, status); |
|
| 646 |
+ } |
|
| 647 |
+ |
|
| 648 |
+ return ret; |
|
| 649 |
+} |
|
| 650 |
+ |
|
| 651 |
+/* |
|
| 652 |
+ * Background process -- runs with privilege. |
|
| 653 |
+ */ |
|
| 654 |
+static void |
|
| 655 |
+pam_server (int fd, const char *service, int verb, const struct name_value_list *name_value_list) |
|
| 656 |
+{
|
|
| 657 |
+ struct user_pass up; |
|
| 658 |
+ int command; |
|
| 659 |
+#if DLOPEN_PAM |
|
| 660 |
+ static const char pam_so[] = "libpam.so"; |
|
| 661 |
+#endif |
|
| 662 |
+ |
|
| 663 |
+ /* |
|
| 664 |
+ * Do initialization |
|
| 665 |
+ */ |
|
| 666 |
+ if (DEBUG (verb)) |
|
| 667 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: INIT service='%s'\n", service); |
|
| 668 |
+ |
|
| 669 |
+#if DLOPEN_PAM |
|
| 670 |
+ /* |
|
| 671 |
+ * Load PAM shared object |
|
| 672 |
+ */ |
|
| 673 |
+ if (!dlopen_pam (pam_so)) |
|
| 674 |
+ {
|
|
| 675 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: could not load PAM lib %s: %s\n", pam_so, dlerror()); |
|
| 676 |
+ send_control (fd, RESPONSE_INIT_FAILED); |
|
| 677 |
+ goto done; |
|
| 678 |
+ } |
|
| 679 |
+#endif |
|
| 680 |
+ |
|
| 681 |
+ /* |
|
| 682 |
+ * Tell foreground that we initialized successfully |
|
| 683 |
+ */ |
|
| 684 |
+ if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1) |
|
| 685 |
+ {
|
|
| 686 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [1]\n"); |
|
| 687 |
+ goto done; |
|
| 688 |
+ } |
|
| 689 |
+ |
|
| 690 |
+ /* |
|
| 691 |
+ * Event loop |
|
| 692 |
+ */ |
|
| 693 |
+ while (1) |
|
| 694 |
+ {
|
|
| 695 |
+ memset (&up, 0, sizeof (up)); |
|
| 696 |
+ up.verb = verb; |
|
| 697 |
+ up.name_value_list = name_value_list; |
|
| 698 |
+ |
|
| 699 |
+ /* get a command from foreground process */ |
|
| 700 |
+ command = recv_control (fd); |
|
| 701 |
+ |
|
| 702 |
+ if (DEBUG (verb)) |
|
| 703 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: received command code: %d\n", command); |
|
| 704 |
+ |
|
| 705 |
+ switch (command) |
|
| 706 |
+ {
|
|
| 707 |
+ case COMMAND_VERIFY: |
|
| 708 |
+ if (recv_string (fd, up.username, sizeof (up.username)) == -1 |
|
| 709 |
+ || recv_string (fd, up.password, sizeof (up.password)) == -1) |
|
| 710 |
+ {
|
|
| 711 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: read error on command channel: code=%d, exiting\n", |
|
| 712 |
+ command); |
|
| 713 |
+ goto done; |
|
| 714 |
+ } |
|
| 715 |
+ |
|
| 716 |
+ if (DEBUG (verb)) |
|
| 717 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: USER/PASS: %s/%s\n", |
|
| 718 |
+ up.username, up.password); |
|
| 719 |
+ |
|
| 720 |
+ if (pam_auth (service, &up)) /* Succeeded */ |
|
| 721 |
+ {
|
|
| 722 |
+ if (send_control (fd, RESPONSE_VERIFY_SUCCEEDED) == -1) |
|
| 723 |
+ {
|
|
| 724 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [2]\n"); |
|
| 725 |
+ goto done; |
|
| 726 |
+ } |
|
| 727 |
+ } |
|
| 728 |
+ else /* Failed */ |
|
| 729 |
+ {
|
|
| 730 |
+ if (send_control (fd, RESPONSE_VERIFY_FAILED) == -1) |
|
| 731 |
+ {
|
|
| 732 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [3]\n"); |
|
| 733 |
+ goto done; |
|
| 734 |
+ } |
|
| 735 |
+ } |
|
| 736 |
+ break; |
|
| 737 |
+ |
|
| 738 |
+ case COMMAND_EXIT: |
|
| 739 |
+ goto done; |
|
| 740 |
+ |
|
| 741 |
+ case -1: |
|
| 742 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: read error on command channel\n"); |
|
| 743 |
+ goto done; |
|
| 744 |
+ |
|
| 745 |
+ default: |
|
| 746 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: unknown command code: code=%d, exiting\n", |
|
| 747 |
+ command); |
|
| 748 |
+ goto done; |
|
| 749 |
+ } |
|
| 750 |
+ } |
|
| 751 |
+ done: |
|
| 752 |
+ |
|
| 753 |
+#if DLOPEN_PAM |
|
| 754 |
+ dlclose_pam (); |
|
| 755 |
+#endif |
|
| 756 |
+ if (DEBUG (verb)) |
|
| 757 |
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: EXIT\n"); |
|
| 758 |
+ |
|
| 759 |
+ return; |
|
| 760 |
+} |
| 0 | 761 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,181 @@ |
| 0 |
+#if DLOPEN_PAM |
|
| 1 |
+/* |
|
| 2 |
+ * If you want to dynamically load libpam using dlopen() or something, |
|
| 3 |
+ * then dlopen( ' this shared object ' ); It takes care of exporting |
|
| 4 |
+ * the right symbols to any modules loaded by libpam. |
|
| 5 |
+ * |
|
| 6 |
+ * Modified by JY for use with openvpn-pam-auth |
|
| 7 |
+ */ |
|
| 8 |
+ |
|
| 9 |
+#include <stdio.h> |
|
| 10 |
+#include <dlfcn.h> |
|
| 11 |
+#include <security/pam_appl.h> |
|
| 12 |
+#include <security/_pam_macros.h> |
|
| 13 |
+ |
|
| 14 |
+#include "pamdl.h" |
|
| 15 |
+ |
|
| 16 |
+static void *libpam_h = NULL; |
|
| 17 |
+ |
|
| 18 |
+#define RESOLVE_PAM_FUNCTION(x, y, z, err) \ |
|
| 19 |
+ { \
|
|
| 20 |
+ union { const void *tpointer; y (*fn) z ; } fptr; \
|
|
| 21 |
+ fptr.tpointer = dlsym(libpam_h, #x); real_##x = fptr.fn; \ |
|
| 22 |
+ if (real_##x == NULL) { \
|
|
| 23 |
+ fprintf (stderr, "PAMDL: unable to resolve '%s': %s\n", #x, dlerror()); \ |
|
| 24 |
+ return err; \ |
|
| 25 |
+ } \ |
|
| 26 |
+ } |
|
| 27 |
+ |
|
| 28 |
+int |
|
| 29 |
+dlopen_pam (const char *so) |
|
| 30 |
+{
|
|
| 31 |
+ if (libpam_h == NULL) |
|
| 32 |
+ {
|
|
| 33 |
+ libpam_h = dlopen(so, RTLD_GLOBAL|RTLD_NOW); |
|
| 34 |
+ } |
|
| 35 |
+ return libpam_h != NULL; |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+void |
|
| 39 |
+dlclose_pam (void) |
|
| 40 |
+{
|
|
| 41 |
+ if (libpam_h != NULL) |
|
| 42 |
+ {
|
|
| 43 |
+ dlclose(libpam_h); |
|
| 44 |
+ libpam_h = NULL; |
|
| 45 |
+ } |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+int pam_start(const char *service_name, const char *user, |
|
| 49 |
+ const struct pam_conv *pam_conversation, |
|
| 50 |
+ pam_handle_t **pamh) |
|
| 51 |
+{
|
|
| 52 |
+ int (*real_pam_start)(const char *, const char *, |
|
| 53 |
+ const struct pam_conv *, |
|
| 54 |
+ pam_handle_t **); |
|
| 55 |
+ RESOLVE_PAM_FUNCTION(pam_start, int, (const char *, const char *, |
|
| 56 |
+ const struct pam_conv *, |
|
| 57 |
+ pam_handle_t **), PAM_ABORT); |
|
| 58 |
+ return real_pam_start(service_name, user, pam_conversation, pamh); |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+int pam_end(pam_handle_t *pamh, int pam_status) |
|
| 62 |
+{
|
|
| 63 |
+ int (*real_pam_end)(pam_handle_t *, int); |
|
| 64 |
+ RESOLVE_PAM_FUNCTION(pam_end, int, (pam_handle_t *, int), PAM_ABORT); |
|
| 65 |
+ return real_pam_end(pamh, pam_status); |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+int pam_set_item(pam_handle_t *pamh, int item_type, const void *item) |
|
| 69 |
+{
|
|
| 70 |
+ int (*real_pam_set_item)(pam_handle_t *, int, const void *); |
|
| 71 |
+ RESOLVE_PAM_FUNCTION(pam_set_item, int, |
|
| 72 |
+ (pam_handle_t *, int, const void *), PAM_ABORT); |
|
| 73 |
+ return real_pam_set_item(pamh, item_type, item); |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) |
|
| 77 |
+{
|
|
| 78 |
+ int (*real_pam_get_item)(const pam_handle_t *, int, const void **); |
|
| 79 |
+ RESOLVE_PAM_FUNCTION(pam_get_item, int, |
|
| 80 |
+ (const pam_handle_t *, int, const void **), |
|
| 81 |
+ PAM_ABORT); |
|
| 82 |
+ return real_pam_get_item(pamh, item_type, item); |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+int pam_fail_delay(pam_handle_t *pamh, unsigned int musec_delay) |
|
| 86 |
+{
|
|
| 87 |
+ int (*real_pam_fail_delay)(pam_handle_t *, unsigned int); |
|
| 88 |
+ RESOLVE_PAM_FUNCTION(pam_fail_delay, int, (pam_handle_t *, unsigned int), |
|
| 89 |
+ PAM_ABORT); |
|
| 90 |
+ return real_pam_fail_delay(pamh, musec_delay); |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+typedef const char * const_char_pointer; |
|
| 94 |
+ |
|
| 95 |
+const_char_pointer pam_strerror(pam_handle_t *pamh, int errnum) |
|
| 96 |
+{
|
|
| 97 |
+ const_char_pointer (*real_pam_strerror)(pam_handle_t *, int); |
|
| 98 |
+ RESOLVE_PAM_FUNCTION(pam_strerror, const_char_pointer, |
|
| 99 |
+ (pam_handle_t *, int), NULL); |
|
| 100 |
+ return real_pam_strerror(pamh, errnum); |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+int pam_putenv(pam_handle_t *pamh, const char *name_value) |
|
| 104 |
+{
|
|
| 105 |
+ int (*real_pam_putenv)(pam_handle_t *, const char *); |
|
| 106 |
+ RESOLVE_PAM_FUNCTION(pam_putenv, int, (pam_handle_t *, const char *), |
|
| 107 |
+ PAM_ABORT); |
|
| 108 |
+ return real_pam_putenv(pamh, name_value); |
|
| 109 |
+} |
|
| 110 |
+ |
|
| 111 |
+const_char_pointer pam_getenv(pam_handle_t *pamh, const char *name) |
|
| 112 |
+{
|
|
| 113 |
+ const_char_pointer (*real_pam_getenv)(pam_handle_t *, const char *); |
|
| 114 |
+ RESOLVE_PAM_FUNCTION(pam_getenv, const_char_pointer, |
|
| 115 |
+ (pam_handle_t *, const char *), NULL); |
|
| 116 |
+ return real_pam_getenv(pamh, name); |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+typedef char ** char_ppointer; |
|
| 120 |
+char_ppointer pam_getenvlist(pam_handle_t *pamh) |
|
| 121 |
+{
|
|
| 122 |
+ char_ppointer (*real_pam_getenvlist)(pam_handle_t *); |
|
| 123 |
+ RESOLVE_PAM_FUNCTION(pam_getenvlist, char_ppointer, (pam_handle_t *), |
|
| 124 |
+ NULL); |
|
| 125 |
+ return real_pam_getenvlist(pamh); |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+/* Authentication management */ |
|
| 129 |
+ |
|
| 130 |
+int pam_authenticate(pam_handle_t *pamh, int flags) |
|
| 131 |
+{
|
|
| 132 |
+ int (*real_pam_authenticate)(pam_handle_t *, int); |
|
| 133 |
+ RESOLVE_PAM_FUNCTION(pam_authenticate, int, (pam_handle_t *, int), |
|
| 134 |
+ PAM_ABORT); |
|
| 135 |
+ return real_pam_authenticate(pamh, flags); |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+int pam_setcred(pam_handle_t *pamh, int flags) |
|
| 139 |
+{
|
|
| 140 |
+ int (*real_pam_setcred)(pam_handle_t *, int); |
|
| 141 |
+ RESOLVE_PAM_FUNCTION(pam_setcred, int, (pam_handle_t *, int), PAM_ABORT); |
|
| 142 |
+ return real_pam_setcred(pamh, flags); |
|
| 143 |
+} |
|
| 144 |
+ |
|
| 145 |
+/* Account Management API's */ |
|
| 146 |
+ |
|
| 147 |
+int pam_acct_mgmt(pam_handle_t *pamh, int flags) |
|
| 148 |
+{
|
|
| 149 |
+ int (*real_pam_acct_mgmt)(pam_handle_t *, int); |
|
| 150 |
+ RESOLVE_PAM_FUNCTION(pam_acct_mgmt, int, (pam_handle_t *, int), PAM_ABORT); |
|
| 151 |
+ return real_pam_acct_mgmt(pamh, flags); |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+/* Session Management API's */ |
|
| 155 |
+ |
|
| 156 |
+int pam_open_session(pam_handle_t *pamh, int flags) |
|
| 157 |
+{
|
|
| 158 |
+ int (*real_pam_open_session)(pam_handle_t *, int); |
|
| 159 |
+ RESOLVE_PAM_FUNCTION(pam_open_session, int, (pam_handle_t *, int), |
|
| 160 |
+ PAM_ABORT); |
|
| 161 |
+ return real_pam_open_session(pamh, flags); |
|
| 162 |
+} |
|
| 163 |
+ |
|
| 164 |
+int pam_close_session(pam_handle_t *pamh, int flags) |
|
| 165 |
+{
|
|
| 166 |
+ int (*real_pam_close_session)(pam_handle_t *, int); |
|
| 167 |
+ RESOLVE_PAM_FUNCTION(pam_close_session, int, (pam_handle_t *, int), |
|
| 168 |
+ PAM_ABORT); |
|
| 169 |
+ return real_pam_close_session(pamh, flags); |
|
| 170 |
+} |
|
| 171 |
+ |
|
| 172 |
+/* Password Management API's */ |
|
| 173 |
+ |
|
| 174 |
+int pam_chauthtok(pam_handle_t *pamh, int flags) |
|
| 175 |
+{
|
|
| 176 |
+ int (*real_pam_chauthtok)(pam_handle_t *, int); |
|
| 177 |
+ RESOLVE_PAM_FUNCTION(pam_chauthtok, int, (pam_handle_t *, int), PAM_ABORT); |
|
| 178 |
+ return real_pam_chauthtok(pamh, flags); |
|
| 179 |
+} |
|
| 180 |
+#endif |
| 0 | 7 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,17 @@ |
| 0 |
+# |
|
| 1 |
+# Build the OpenVPN down-root plugin module. |
|
| 2 |
+# |
|
| 3 |
+ |
|
| 4 |
+# This directory is where we will look for openvpn-plugin.h |
|
| 5 |
+INCLUDE=-I../.. |
|
| 6 |
+ |
|
| 7 |
+CC_FLAGS=-O2 -Wall |
|
| 8 |
+ |
|
| 9 |
+down-root.so : down-root.o |
|
| 10 |
+ gcc ${CC_FLAGS} -fPIC -shared -Wl,-soname,openvpn-down-root.so -o openvpn-down-root.so down-root.o -lc
|
|
| 11 |
+ |
|
| 12 |
+down-root.o : down-root.c |
|
| 13 |
+ gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} down-root.c
|
|
| 14 |
+ |
|
| 15 |
+clean : |
|
| 16 |
+ rm -f *.o *.so |
| 0 | 17 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,29 @@ |
| 0 |
+down-root -- an OpenVPN Plugin Module |
|
| 1 |
+ |
|
| 2 |
+SYNOPSIS |
|
| 3 |
+ |
|
| 4 |
+The down-root module allows an OpenVPN configuration to |
|
| 5 |
+call a down script with root privileges, even when privileges |
|
| 6 |
+have been dropped using --user/--group/--chroot. |
|
| 7 |
+ |
|
| 8 |
+This module uses a split privilege execution model which will |
|
| 9 |
+fork() before OpenVPN drops root privileges, at the point where |
|
| 10 |
+the --up script is usually called. The module will then remain |
|
| 11 |
+in a wait state until it receives a message from OpenVPN via |
|
| 12 |
+pipe to execute the down script. Thus, the down script will be |
|
| 13 |
+run in the same execution environment as the up script. |
|
| 14 |
+ |
|
| 15 |
+BUILD |
|
| 16 |
+ |
|
| 17 |
+Build this module with the "make" command. The plugin |
|
| 18 |
+module will be named openvpn-down-root.so |
|
| 19 |
+ |
|
| 20 |
+USAGE |
|
| 21 |
+ |
|
| 22 |
+To use this module, add to your OpenVPN config file: |
|
| 23 |
+ |
|
| 24 |
+ plugin openvpn-down-root.so "command ..." |
|
| 25 |
+ |
|
| 26 |
+CAVEATS |
|
| 27 |
+ |
|
| 28 |
+This module will only work on *nix systems, not Windows. |
| 0 | 29 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,551 @@ |
| 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-2005 OpenVPN Solutions LLC <info@openvpn.net> |
|
| 8 |
+ * |
|
| 9 |
+ * This program is free software; you can redistribute it and/or modify |
|
| 10 |
+ * it under the terms of the GNU General Public License version 2 |
|
| 11 |
+ * as published by the Free Software Foundation. |
|
| 12 |
+ * |
|
| 13 |
+ * This program is distributed in the hope that it will be useful, |
|
| 14 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 15 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 16 |
+ * GNU General Public License for more details. |
|
| 17 |
+ * |
|
| 18 |
+ * You should have received a copy of the GNU General Public License |
|
| 19 |
+ * along with this program (see the file COPYING included with this |
|
| 20 |
+ * distribution); if not, write to the Free Software Foundation, Inc., |
|
| 21 |
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 22 |
+ */ |
|
| 23 |
+ |
|
| 24 |
+/* |
|
| 25 |
+ * OpenVPN plugin module to do privileged down-script execution. |
|
| 26 |
+ */ |
|
| 27 |
+ |
|
| 28 |
+#include <stdio.h> |
|
| 29 |
+#include <string.h> |
|
| 30 |
+#include <unistd.h> |
|
| 31 |
+#include <stdlib.h> |
|
| 32 |
+#include <sys/types.h> |
|
| 33 |
+#include <sys/socket.h> |
|
| 34 |
+#include <sys/wait.h> |
|
| 35 |
+#include <fcntl.h> |
|
| 36 |
+#include <signal.h> |
|
| 37 |
+#include <syslog.h> |
|
| 38 |
+ |
|
| 39 |
+#include "openvpn-plugin.h" |
|
| 40 |
+ |
|
| 41 |
+#define DEBUG(verb) ((verb) >= 7) |
|
| 42 |
+ |
|
| 43 |
+/* Command codes for foreground -> background communication */ |
|
| 44 |
+#define COMMAND_RUN_SCRIPT 0 |
|
| 45 |
+#define COMMAND_EXIT 1 |
|
| 46 |
+ |
|
| 47 |
+/* Response codes for background -> foreground communication */ |
|
| 48 |
+#define RESPONSE_INIT_SUCCEEDED 10 |
|
| 49 |
+#define RESPONSE_INIT_FAILED 11 |
|
| 50 |
+#define RESPONSE_SCRIPT_SUCCEEDED 12 |
|
| 51 |
+#define RESPONSE_SCRIPT_FAILED 13 |
|
| 52 |
+ |
|
| 53 |
+/* Background process function */ |
|
| 54 |
+static void down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb); |
|
| 55 |
+ |
|
| 56 |
+/* |
|
| 57 |
+ * Plugin state, used by foreground |
|
| 58 |
+ */ |
|
| 59 |
+struct down_root_context |
|
| 60 |
+{
|
|
| 61 |
+ /* Foreground's socket to background process */ |
|
| 62 |
+ int foreground_fd; |
|
| 63 |
+ |
|
| 64 |
+ /* Process ID of background process */ |
|
| 65 |
+ pid_t background_pid; |
|
| 66 |
+ |
|
| 67 |
+ /* Verbosity level of OpenVPN */ |
|
| 68 |
+ int verb; |
|
| 69 |
+ |
|
| 70 |
+ /* down command */ |
|
| 71 |
+ char *command; |
|
| 72 |
+}; |
|
| 73 |
+ |
|
| 74 |
+/* |
|
| 75 |
+ * Given an environmental variable name, search |
|
| 76 |
+ * the envp array for its value, returning it |
|
| 77 |
+ * if found or NULL otherwise. |
|
| 78 |
+ */ |
|
| 79 |
+static const char * |
|
| 80 |
+get_env (const char *name, const char *envp[]) |
|
| 81 |
+{
|
|
| 82 |
+ if (envp) |
|
| 83 |
+ {
|
|
| 84 |
+ int i; |
|
| 85 |
+ const int namelen = strlen (name); |
|
| 86 |
+ for (i = 0; envp[i]; ++i) |
|
| 87 |
+ {
|
|
| 88 |
+ if (!strncmp (envp[i], name, namelen)) |
|
| 89 |
+ {
|
|
| 90 |
+ const char *cp = envp[i] + namelen; |
|
| 91 |
+ if (*cp == '=') |
|
| 92 |
+ return cp + 1; |
|
| 93 |
+ } |
|
| 94 |
+ } |
|
| 95 |
+ } |
|
| 96 |
+ return NULL; |
|
| 97 |
+} |
|
| 98 |
+ |
|
| 99 |
+/* |
|
| 100 |
+ * Return the length of a string array |
|
| 101 |
+ */ |
|
| 102 |
+static int |
|
| 103 |
+string_array_len (const char *array[]) |
|
| 104 |
+{
|
|
| 105 |
+ int i = 0; |
|
| 106 |
+ if (array) |
|
| 107 |
+ {
|
|
| 108 |
+ while (array[i]) |
|
| 109 |
+ ++i; |
|
| 110 |
+ } |
|
| 111 |
+ return i; |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+/* |
|
| 115 |
+ * Socket read/write functions. |
|
| 116 |
+ */ |
|
| 117 |
+ |
|
| 118 |
+static int |
|
| 119 |
+recv_control (int fd) |
|
| 120 |
+{
|
|
| 121 |
+ unsigned char c; |
|
| 122 |
+ const ssize_t size = read (fd, &c, sizeof (c)); |
|
| 123 |
+ if (size == sizeof (c)) |
|
| 124 |
+ return c; |
|
| 125 |
+ else |
|
| 126 |
+ return -1; |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+static int |
|
| 130 |
+send_control (int fd, int code) |
|
| 131 |
+{
|
|
| 132 |
+ unsigned char c = (unsigned char) code; |
|
| 133 |
+ const ssize_t size = write (fd, &c, sizeof (c)); |
|
| 134 |
+ if (size == sizeof (c)) |
|
| 135 |
+ return (int) size; |
|
| 136 |
+ else |
|
| 137 |
+ return -1; |
|
| 138 |
+} |
|
| 139 |
+ |
|
| 140 |
+/* |
|
| 141 |
+ * Daemonize if "daemon" env var is true. |
|
| 142 |
+ * Preserve stderr across daemonization if |
|
| 143 |
+ * "daemon_log_redirect" env var is true. |
|
| 144 |
+ */ |
|
| 145 |
+static void |
|
| 146 |
+daemonize (const char *envp[]) |
|
| 147 |
+{
|
|
| 148 |
+ const char *daemon_string = get_env ("daemon", envp);
|
|
| 149 |
+ if (daemon_string && daemon_string[0] == '1') |
|
| 150 |
+ {
|
|
| 151 |
+ const char *log_redirect = get_env ("daemon_log_redirect", envp);
|
|
| 152 |
+ int fd = -1; |
|
| 153 |
+ if (log_redirect && log_redirect[0] == '1') |
|
| 154 |
+ fd = dup (2); |
|
| 155 |
+ if (daemon (0, 0) < 0) |
|
| 156 |
+ {
|
|
| 157 |
+ fprintf (stderr, "DOWN-ROOT: daemonization failed\n"); |
|
| 158 |
+ } |
|
| 159 |
+ else if (fd >= 3) |
|
| 160 |
+ {
|
|
| 161 |
+ dup2 (fd, 2); |
|
| 162 |
+ close (fd); |
|
| 163 |
+ } |
|
| 164 |
+ } |
|
| 165 |
+} |
|
| 166 |
+ |
|
| 167 |
+/* |
|
| 168 |
+ * Close most of parent's fds. |
|
| 169 |
+ * Keep stdin/stdout/stderr, plus one |
|
| 170 |
+ * other fd which is presumed to be |
|
| 171 |
+ * our pipe back to parent. |
|
| 172 |
+ * Admittedly, a bit of a kludge, |
|
| 173 |
+ * but posix doesn't give us a kind |
|
| 174 |
+ * of FD_CLOEXEC which will stop |
|
| 175 |
+ * fds from crossing a fork(). |
|
| 176 |
+ */ |
|
| 177 |
+static void |
|
| 178 |
+close_fds_except (int keep) |
|
| 179 |
+{
|
|
| 180 |
+ int i; |
|
| 181 |
+ closelog (); |
|
| 182 |
+ for (i = 3; i <= 100; ++i) |
|
| 183 |
+ {
|
|
| 184 |
+ if (i != keep) |
|
| 185 |
+ close (i); |
|
| 186 |
+ } |
|
| 187 |
+} |
|
| 188 |
+ |
|
| 189 |
+/* |
|
| 190 |
+ * Usually we ignore signals, because our parent will |
|
| 191 |
+ * deal with them. |
|
| 192 |
+ */ |
|
| 193 |
+static void |
|
| 194 |
+set_signals (void) |
|
| 195 |
+{
|
|
| 196 |
+ signal (SIGTERM, SIG_DFL); |
|
| 197 |
+ |
|
| 198 |
+ signal (SIGINT, SIG_IGN); |
|
| 199 |
+ signal (SIGHUP, SIG_IGN); |
|
| 200 |
+ signal (SIGUSR1, SIG_IGN); |
|
| 201 |
+ signal (SIGUSR2, SIG_IGN); |
|
| 202 |
+ signal (SIGPIPE, SIG_IGN); |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 205 |
+/* |
|
| 206 |
+ * convert system() return into a success/failure value |
|
| 207 |
+ */ |
|
| 208 |
+int |
|
| 209 |
+system_ok (int stat) |
|
| 210 |
+{
|
|
| 211 |
+#ifdef WIN32 |
|
| 212 |
+ return stat == 0; |
|
| 213 |
+#else |
|
| 214 |
+ return stat != -1 && WIFEXITED (stat) && WEXITSTATUS (stat) == 0; |
|
| 215 |
+#endif |
|
| 216 |
+} |
|
| 217 |
+ |
|
| 218 |
+static char * |
|
| 219 |
+build_command_line (const char *argv[]) |
|
| 220 |
+{
|
|
| 221 |
+ int size = 0; |
|
| 222 |
+ int n = 0; |
|
| 223 |
+ int i; |
|
| 224 |
+ char *string; |
|
| 225 |
+ |
|
| 226 |
+ /* precompute size */ |
|
| 227 |
+ if (argv) |
|
| 228 |
+ {
|
|
| 229 |
+ for (i = 0; argv[i]; ++i) |
|
| 230 |
+ {
|
|
| 231 |
+ size += (strlen (argv[i]) + 1); /* string length plus trailing space */ |
|
| 232 |
+ ++n; |
|
| 233 |
+ } |
|
| 234 |
+ } |
|
| 235 |
+ ++size; /* for null terminator */ |
|
| 236 |
+ |
|
| 237 |
+ /* allocate memory */ |
|
| 238 |
+ string = (char *) malloc (size); |
|
| 239 |
+ if (!string) |
|
| 240 |
+ {
|
|
| 241 |
+ fprintf (stderr, "DOWN-ROOT: out of memory\n"); |
|
| 242 |
+ exit (1); |
|
| 243 |
+ } |
|
| 244 |
+ string[0] = '\0'; |
|
| 245 |
+ |
|
| 246 |
+ /* build string */ |
|
| 247 |
+ for (i = 0; i < n; ++i) |
|
| 248 |
+ {
|
|
| 249 |
+ strcat (string, argv[i]); |
|
| 250 |
+ if (i + 1 < n) |
|
| 251 |
+ strcat (string, " "); |
|
| 252 |
+ } |
|
| 253 |
+ return string; |
|
| 254 |
+} |
|
| 255 |
+ |
|
| 256 |
+static void |
|
| 257 |
+free_context (struct down_root_context *context) |
|
| 258 |
+{
|
|
| 259 |
+ if (context) |
|
| 260 |
+ {
|
|
| 261 |
+ if (context->command) |
|
| 262 |
+ free (context->command); |
|
| 263 |
+ free (context); |
|
| 264 |
+ } |
|
| 265 |
+} |
|
| 266 |
+ |
|
| 267 |
+OPENVPN_EXPORT openvpn_plugin_handle_t |
|
| 268 |
+openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) |
|
| 269 |
+{
|
|
| 270 |
+ struct down_root_context *context; |
|
| 271 |
+ |
|
| 272 |
+ /* |
|
| 273 |
+ * Allocate our context |
|
| 274 |
+ */ |
|
| 275 |
+ context = (struct down_root_context *) calloc (1, sizeof (struct down_root_context)); |
|
| 276 |
+ context->foreground_fd = -1; |
|
| 277 |
+ |
|
| 278 |
+ /* |
|
| 279 |
+ * Intercept the --up and --down callbacks |
|
| 280 |
+ */ |
|
| 281 |
+ *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) | OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN); |
|
| 282 |
+ |
|
| 283 |
+ /* |
|
| 284 |
+ * Make sure we have two string arguments: the first is the .so name, |
|
| 285 |
+ * the second is the script command. |
|
| 286 |
+ */ |
|
| 287 |
+ if (string_array_len (argv) < 2) |
|
| 288 |
+ {
|
|
| 289 |
+ fprintf (stderr, "DOWN-ROOT: need down script command\n"); |
|
| 290 |
+ goto error; |
|
| 291 |
+ } |
|
| 292 |
+ |
|
| 293 |
+ /* |
|
| 294 |
+ * Save our argument in context |
|
| 295 |
+ */ |
|
| 296 |
+ context->command = build_command_line (&argv[1]); |
|
| 297 |
+ |
|
| 298 |
+ /* |
|
| 299 |
+ * Get verbosity level from environment |
|
| 300 |
+ */ |
|
| 301 |
+ {
|
|
| 302 |
+ const char *verb_string = get_env ("verb", envp);
|
|
| 303 |
+ if (verb_string) |
|
| 304 |
+ context->verb = atoi (verb_string); |
|
| 305 |
+ } |
|
| 306 |
+ |
|
| 307 |
+ return (openvpn_plugin_handle_t) context; |
|
| 308 |
+ |
|
| 309 |
+ error: |
|
| 310 |
+ free_context (context); |
|
| 311 |
+ return NULL; |
|
| 312 |
+} |
|
| 313 |
+ |
|
| 314 |
+OPENVPN_EXPORT int |
|
| 315 |
+openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) |
|
| 316 |
+{
|
|
| 317 |
+ struct down_root_context *context = (struct down_root_context *) handle; |
|
| 318 |
+ |
|
| 319 |
+ if (type == OPENVPN_PLUGIN_UP && context->foreground_fd == -1) /* fork off a process to hold onto root */ |
|
| 320 |
+ {
|
|
| 321 |
+ pid_t pid; |
|
| 322 |
+ int fd[2]; |
|
| 323 |
+ |
|
| 324 |
+ /* |
|
| 325 |
+ * Make a socket for foreground and background processes |
|
| 326 |
+ * to communicate. |
|
| 327 |
+ */ |
|
| 328 |
+ if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1) |
|
| 329 |
+ {
|
|
| 330 |
+ fprintf (stderr, "DOWN-ROOT: socketpair call failed\n"); |
|
| 331 |
+ return OPENVPN_PLUGIN_FUNC_ERROR; |
|
| 332 |
+ } |
|
| 333 |
+ |
|
| 334 |
+ /* |
|
| 335 |
+ * Fork off the privileged process. It will remain privileged |
|
| 336 |
+ * even after the foreground process drops its privileges. |
|
| 337 |
+ */ |
|
| 338 |
+ pid = fork (); |
|
| 339 |
+ |
|
| 340 |
+ if (pid) |
|
| 341 |
+ {
|
|
| 342 |
+ int status; |
|
| 343 |
+ |
|
| 344 |
+ /* |
|
| 345 |
+ * Foreground Process |
|
| 346 |
+ */ |
|
| 347 |
+ |
|
| 348 |
+ context->background_pid = pid; |
|
| 349 |
+ |
|
| 350 |
+ /* close our copy of child's socket */ |
|
| 351 |
+ close (fd[1]); |
|
| 352 |
+ |
|
| 353 |
+ /* don't let future subprocesses inherit child socket */ |
|
| 354 |
+ if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0) |
|
| 355 |
+ fprintf (stderr, "DOWN-ROOT: Set FD_CLOEXEC flag on socket file descriptor failed\n"); |
|
| 356 |
+ |
|
| 357 |
+ /* wait for background child process to initialize */ |
|
| 358 |
+ status = recv_control (fd[0]); |
|
| 359 |
+ if (status == RESPONSE_INIT_SUCCEEDED) |
|
| 360 |
+ {
|
|
| 361 |
+ context->foreground_fd = fd[0]; |
|
| 362 |
+ return OPENVPN_PLUGIN_FUNC_SUCCESS; |
|
| 363 |
+ } |
|
| 364 |
+ } |
|
| 365 |
+ else |
|
| 366 |
+ {
|
|
| 367 |
+ /* |
|
| 368 |
+ * Background Process |
|
| 369 |
+ */ |
|
| 370 |
+ |
|
| 371 |
+ /* close all parent fds except our socket back to parent */ |
|
| 372 |
+ close_fds_except (fd[1]); |
|
| 373 |
+ |
|
| 374 |
+ /* Ignore most signals (the parent will receive them) */ |
|
| 375 |
+ set_signals (); |
|
| 376 |
+ |
|
| 377 |
+ /* Daemonize if --daemon option is set. */ |
|
| 378 |
+ daemonize (envp); |
|
| 379 |
+ |
|
| 380 |
+ /* execute the event loop */ |
|
| 381 |
+ down_root_server (fd[1], context->command, argv, envp, context->verb); |
|
| 382 |
+ |
|
| 383 |
+ close (fd[1]); |
|
| 384 |
+ exit (0); |
|
| 385 |
+ return 0; /* NOTREACHED */ |
|
| 386 |
+ } |
|
| 387 |
+ } |
|
| 388 |
+ else if (type == OPENVPN_PLUGIN_DOWN && context->foreground_fd >= 0) |
|
| 389 |
+ {
|
|
| 390 |
+ if (send_control (context->foreground_fd, COMMAND_RUN_SCRIPT) == -1) |
|
| 391 |
+ {
|
|
| 392 |
+ fprintf (stderr, "DOWN-ROOT: Error sending script execution signal to background process\n"); |
|
| 393 |
+ } |
|
| 394 |
+ else |
|
| 395 |
+ {
|
|
| 396 |
+ const int status = recv_control (context->foreground_fd); |
|
| 397 |
+ if (status == RESPONSE_SCRIPT_SUCCEEDED) |
|
| 398 |
+ return OPENVPN_PLUGIN_FUNC_SUCCESS; |
|
| 399 |
+ if (status == -1) |
|
| 400 |
+ fprintf (stderr, "DOWN-ROOT: Error receiving script execution confirmation from background process\n"); |
|
| 401 |
+ } |
|
| 402 |
+ } |
|
| 403 |
+ return OPENVPN_PLUGIN_FUNC_ERROR; |
|
| 404 |
+} |
|
| 405 |
+ |
|
| 406 |
+OPENVPN_EXPORT void |
|
| 407 |
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) |
|
| 408 |
+{
|
|
| 409 |
+ struct down_root_context *context = (struct down_root_context *) handle; |
|
| 410 |
+ |
|
| 411 |
+ if (DEBUG (context->verb)) |
|
| 412 |
+ fprintf (stderr, "DOWN-ROOT: close\n"); |
|
| 413 |
+ |
|
| 414 |
+ if (context->foreground_fd >= 0) |
|
| 415 |
+ {
|
|
| 416 |
+ /* tell background process to exit */ |
|
| 417 |
+ if (send_control (context->foreground_fd, COMMAND_EXIT) == -1) |
|
| 418 |
+ fprintf (stderr, "DOWN-ROOT: Error signaling background process to exit\n"); |
|
| 419 |
+ |
|
| 420 |
+ /* wait for background process to exit */ |
|
| 421 |
+ if (context->background_pid > 0) |
|
| 422 |
+ waitpid (context->background_pid, NULL, 0); |
|
| 423 |
+ |
|
| 424 |
+ close (context->foreground_fd); |
|
| 425 |
+ context->foreground_fd = -1; |
|
| 426 |
+ } |
|
| 427 |
+ |
|
| 428 |
+ free_context (context); |
|
| 429 |
+} |
|
| 430 |
+ |
|
| 431 |
+OPENVPN_EXPORT void |
|
| 432 |
+openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle) |
|
| 433 |
+{
|
|
| 434 |
+ struct down_root_context *context = (struct down_root_context *) handle; |
|
| 435 |
+ |
|
| 436 |
+ if (context->foreground_fd >= 0) |
|
| 437 |
+ {
|
|
| 438 |
+ /* tell background process to exit */ |
|
| 439 |
+ send_control (context->foreground_fd, COMMAND_EXIT); |
|
| 440 |
+ close (context->foreground_fd); |
|
| 441 |
+ context->foreground_fd = -1; |
|
| 442 |
+ } |
|
| 443 |
+} |
|
| 444 |
+ |
|
| 445 |
+/* |
|
| 446 |
+ * Background process -- runs with privilege. |
|
| 447 |
+ */ |
|
| 448 |
+static void |
|
| 449 |
+down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb) |
|
| 450 |
+{
|
|
| 451 |
+ const char *p[3]; |
|
| 452 |
+ char *command_line = NULL; |
|
| 453 |
+ char *argv_cat = NULL; |
|
| 454 |
+ int i; |
|
| 455 |
+ |
|
| 456 |
+ /* |
|
| 457 |
+ * Do initialization |
|
| 458 |
+ */ |
|
| 459 |
+ if (DEBUG (verb)) |
|
| 460 |
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: INIT command='%s'\n", command); |
|
| 461 |
+ |
|
| 462 |
+ /* |
|
| 463 |
+ * Tell foreground that we initialized successfully |
|
| 464 |
+ */ |
|
| 465 |
+ if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1) |
|
| 466 |
+ {
|
|
| 467 |
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [1]\n"); |
|
| 468 |
+ goto done; |
|
| 469 |
+ } |
|
| 470 |
+ |
|
| 471 |
+ /* |
|
| 472 |
+ * Build command line |
|
| 473 |
+ */ |
|
| 474 |
+ if (string_array_len (argv) >= 2) |
|
| 475 |
+ argv_cat = build_command_line (&argv[1]); |
|
| 476 |
+ else |
|
| 477 |
+ argv_cat = build_command_line (NULL); |
|
| 478 |
+ p[0] = command; |
|
| 479 |
+ p[1] = argv_cat; |
|
| 480 |
+ p[2] = NULL; |
|
| 481 |
+ command_line = build_command_line (p); |
|
| 482 |
+ |
|
| 483 |
+ /* |
|
| 484 |
+ * Save envp in environment |
|
| 485 |
+ */ |
|
| 486 |
+ for (i = 0; envp[i]; ++i) |
|
| 487 |
+ {
|
|
| 488 |
+ putenv ((char *)envp[i]); |
|
| 489 |
+ } |
|
| 490 |
+ |
|
| 491 |
+ /* |
|
| 492 |
+ * Event loop |
|
| 493 |
+ */ |
|
| 494 |
+ while (1) |
|
| 495 |
+ {
|
|
| 496 |
+ int command_code; |
|
| 497 |
+ int status; |
|
| 498 |
+ |
|
| 499 |
+ /* get a command from foreground process */ |
|
| 500 |
+ command_code = recv_control (fd); |
|
| 501 |
+ |
|
| 502 |
+ if (DEBUG (verb)) |
|
| 503 |
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: received command code: %d\n", command_code); |
|
| 504 |
+ |
|
| 505 |
+ switch (command_code) |
|
| 506 |
+ {
|
|
| 507 |
+ case COMMAND_RUN_SCRIPT: |
|
| 508 |
+ status = system (command_line); |
|
| 509 |
+ if (system_ok (status)) /* Succeeded */ |
|
| 510 |
+ {
|
|
| 511 |
+ if (send_control (fd, RESPONSE_SCRIPT_SUCCEEDED) == -1) |
|
| 512 |
+ {
|
|
| 513 |
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [2]\n"); |
|
| 514 |
+ goto done; |
|
| 515 |
+ } |
|
| 516 |
+ } |
|
| 517 |
+ else /* Failed */ |
|
| 518 |
+ {
|
|
| 519 |
+ if (send_control (fd, RESPONSE_SCRIPT_FAILED) == -1) |
|
| 520 |
+ {
|
|
| 521 |
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [3]\n"); |
|
| 522 |
+ goto done; |
|
| 523 |
+ } |
|
| 524 |
+ } |
|
| 525 |
+ break; |
|
| 526 |
+ |
|
| 527 |
+ case COMMAND_EXIT: |
|
| 528 |
+ goto done; |
|
| 529 |
+ |
|
| 530 |
+ case -1: |
|
| 531 |
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: read error on command channel\n"); |
|
| 532 |
+ goto done; |
|
| 533 |
+ |
|
| 534 |
+ default: |
|
| 535 |
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: unknown command code: code=%d, exiting\n", |
|
| 536 |
+ command_code); |
|
| 537 |
+ goto done; |
|
| 538 |
+ } |
|
| 539 |
+ } |
|
| 540 |
+ |
|
| 541 |
+ done: |
|
| 542 |
+ if (argv_cat) |
|
| 543 |
+ free (argv_cat); |
|
| 544 |
+ if (command_line) |
|
| 545 |
+ free (command_line); |
|
| 546 |
+ if (DEBUG (verb)) |
|
| 547 |
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: EXIT\n"); |
|
| 548 |
+ |
|
| 549 |
+ return; |
|
| 550 |
+} |
| 0 | 551 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,16 @@ |
| 0 |
+OpenVPN plugin examples. |
|
| 1 |
+ |
|
| 2 |
+Examples provided: |
|
| 3 |
+ |
|
| 4 |
+simple.c -- using the --auth-user-pass-verify callback, verify |
|
| 5 |
+ that the username/password is "foo"/"bar". |
|
| 6 |
+ |
|
| 7 |
+To build: |
|
| 8 |
+ |
|
| 9 |
+ ./build simple (Linux/BSD/etc.) |
|
| 10 |
+ ./winbuild simple (MinGW on Windows) |
|
| 11 |
+ |
|
| 12 |
+To use in OpenVPN, add to config file: |
|
| 13 |
+ |
|
| 14 |
+ plugin simple.so (Linux/BSD/etc.) |
|
| 15 |
+ plugin simple.dll (MinGW on Windows) |
| 0 | 16 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,14 @@ |
| 0 |
+#!/bin/sh |
|
| 1 |
+ |
|
| 2 |
+# |
|
| 3 |
+# Build an OpenVPN plugin module on *nix. The argument should |
|
| 4 |
+# be the base name of the C source file (without the .c). |
|
| 5 |
+# |
|
| 6 |
+ |
|
| 7 |
+# This directory is where we will look for openvpn-plugin.h |
|
| 8 |
+INCLUDE="-I../.." |
|
| 9 |
+ |
|
| 10 |
+CC_FLAGS="-O2 -Wall" |
|
| 11 |
+ |
|
| 12 |
+gcc $CC_FLAGS -fPIC -c $INCLUDE $1.c && \ |
|
| 13 |
+gcc -fPIC -shared -Wl,-soname,$1.so -o $1.so $1.o -lc |
| 0 | 14 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,120 @@ |
| 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-2005 OpenVPN Solutions LLC <info@openvpn.net> |
|
| 8 |
+ * |
|
| 9 |
+ * This program is free software; you can redistribute it and/or modify |
|
| 10 |
+ * it under the terms of the GNU General Public License version 2 |
|
| 11 |
+ * as published by the Free Software Foundation. |
|
| 12 |
+ * |
|
| 13 |
+ * This program is distributed in the hope that it will be useful, |
|
| 14 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 15 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 16 |
+ * GNU General Public License for more details. |
|
| 17 |
+ * |
|
| 18 |
+ * You should have received a copy of the GNU General Public License |
|
| 19 |
+ * along with this program (see the file COPYING included with this |
|
| 20 |
+ * distribution); if not, write to the Free Software Foundation, Inc., |
|
| 21 |
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 22 |
+ */ |
|
| 23 |
+ |
|
| 24 |
+/* |
|
| 25 |
+ * This file implements a simple OpenVPN plugin module which |
|
| 26 |
+ * will examine the username/password provided by a client, |
|
| 27 |
+ * and make an accept/deny determination. Will run |
|
| 28 |
+ * on Windows or *nix. |
|
| 29 |
+ * |
|
| 30 |
+ * See the README file for build instructions. |
|
| 31 |
+ */ |
|
| 32 |
+ |
|
| 33 |
+#include <stdio.h> |
|
| 34 |
+#include <string.h> |
|
| 35 |
+#include <stdlib.h> |
|
| 36 |
+ |
|
| 37 |
+#include "openvpn-plugin.h" |
|
| 38 |
+ |
|
| 39 |
+/* |
|
| 40 |
+ * Our context, where we keep our state. |
|
| 41 |
+ */ |
|
| 42 |
+struct plugin_context {
|
|
| 43 |
+ const char *username; |
|
| 44 |
+ const char *password; |
|
| 45 |
+}; |
|
| 46 |
+ |
|
| 47 |
+/* |
|
| 48 |
+ * Given an environmental variable name, search |
|
| 49 |
+ * the envp array for its value, returning it |
|
| 50 |
+ * if found or NULL otherwise. |
|
| 51 |
+ */ |
|
| 52 |
+static const char * |
|
| 53 |
+get_env (const char *name, const char *envp[]) |
|
| 54 |
+{
|
|
| 55 |
+ if (envp) |
|
| 56 |
+ {
|
|
| 57 |
+ int i; |
|
| 58 |
+ const int namelen = strlen (name); |
|
| 59 |
+ for (i = 0; envp[i]; ++i) |
|
| 60 |
+ {
|
|
| 61 |
+ if (!strncmp (envp[i], name, namelen)) |
|
| 62 |
+ {
|
|
| 63 |
+ const char *cp = envp[i] + namelen; |
|
| 64 |
+ if (*cp == '=') |
|
| 65 |
+ return cp + 1; |
|
| 66 |
+ } |
|
| 67 |
+ } |
|
| 68 |
+ } |
|
| 69 |
+ return NULL; |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+OPENVPN_EXPORT openvpn_plugin_handle_t |
|
| 73 |
+openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) |
|
| 74 |
+{
|
|
| 75 |
+ struct plugin_context *context; |
|
| 76 |
+ |
|
| 77 |
+ /* |
|
| 78 |
+ * Allocate our context |
|
| 79 |
+ */ |
|
| 80 |
+ context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context)); |
|
| 81 |
+ |
|
| 82 |
+ /* |
|
| 83 |
+ * Set the username/password we will require. |
|
| 84 |
+ */ |
|
| 85 |
+ context->username = "foo"; |
|
| 86 |
+ context->password = "bar"; |
|
| 87 |
+ |
|
| 88 |
+ /* |
|
| 89 |
+ * We are only interested in intercepting the |
|
| 90 |
+ * --auth-user-pass-verify callback. |
|
| 91 |
+ */ |
|
| 92 |
+ *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY); |
|
| 93 |
+ |
|
| 94 |
+ return (openvpn_plugin_handle_t) context; |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+OPENVPN_EXPORT int |
|
| 98 |
+openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) |
|
| 99 |
+{
|
|
| 100 |
+ struct plugin_context *context = (struct plugin_context *) handle; |
|
| 101 |
+ |
|
| 102 |
+ /* get username/password from envp string array */ |
|
| 103 |
+ const char *username = get_env ("username", envp);
|
|
| 104 |
+ const char *password = get_env ("password", envp);
|
|
| 105 |
+ |
|
| 106 |
+ /* check entered username/password against what we require */ |
|
| 107 |
+ if (username && !strcmp (username, context->username) |
|
| 108 |
+ && password && !strcmp (password, context->password)) |
|
| 109 |
+ return OPENVPN_PLUGIN_FUNC_SUCCESS; |
|
| 110 |
+ else |
|
| 111 |
+ return OPENVPN_PLUGIN_FUNC_ERROR; |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+OPENVPN_EXPORT void |
|
| 115 |
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) |
|
| 116 |
+{
|
|
| 117 |
+ struct plugin_context *context = (struct plugin_context *) handle; |
|
| 118 |
+ free (context); |
|
| 119 |
+} |
| 0 | 6 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,18 @@ |
| 0 |
+# |
|
| 1 |
+# Build an OpenVPN plugin module on Windows/MinGW. |
|
| 2 |
+# The argument should be the base name of the C source file |
|
| 3 |
+# (without the .c). |
|
| 4 |
+# |
|
| 5 |
+ |
|
| 6 |
+# This directory is where we will look for openvpn-plugin.h |
|
| 7 |
+INCLUDE="-I.." |
|
| 8 |
+ |
|
| 9 |
+CC_FLAGS="-O2 -Wall" |
|
| 10 |
+ |
|
| 11 |
+gcc -DBUILD_DLL $CC_FLAGS $INCLUDE -c $1.c |
|
| 12 |
+gcc --disable-stdcall-fixup -mdll -DBUILD_DLL -o junk.tmp -Wl,--base-file,base.tmp $1.o |
|
| 13 |
+rm junk.tmp |
|
| 14 |
+dlltool --dllname $1.dll --base-file base.tmp --output-exp temp.exp --input-def $1.def |
|
| 15 |
+rm base.tmp |
|
| 16 |
+gcc --enable-stdcall-fixup -mdll -DBUILD_DLL -o $1.dll $1.o -Wl,temp.exp |
|
| 17 |
+rm temp.exp |
| 0 | 18 |
deleted file mode 100644 |
| ... | ... |
@@ -1,47 +0,0 @@ |
| 1 |
-OpenVPN Plugins |
|
| 2 |
- |
|
| 3 |
-Starting with OpenVPN 2.0-beta17, compiled plugin modules are |
|
| 4 |
-supported on any *nix OS which includes libdl or on Windows. |
|
| 5 |
-One or more modules may be loaded into OpenVPN using |
|
| 6 |
-the --plugin directive, and each plugin module is capable of |
|
| 7 |
-intercepting any of the script callbacks which OpenVPN supports: |
|
| 8 |
- |
|
| 9 |
-(1) up |
|
| 10 |
-(2) down |
|
| 11 |
-(3) route-up |
|
| 12 |
-(4) ipchange |
|
| 13 |
-(5) tls-verify |
|
| 14 |
-(6) auth-user-pass-verify |
|
| 15 |
-(7) client-connect |
|
| 16 |
-(8) client-disconnect |
|
| 17 |
-(9) learn-address |
|
| 18 |
- |
|
| 19 |
-See the openvpn-plugin.h file in the top-level directory of the |
|
| 20 |
-OpenVPN source distribution for more detailed information |
|
| 21 |
-on the plugin interface. |
|
| 22 |
- |
|
| 23 |
-Included Plugins |
|
| 24 |
- |
|
| 25 |
-auth-pam -- Authenticate using PAM and a split privilege |
|
| 26 |
- execution model which functions even if |
|
| 27 |
- root privileges or the execution environment |
|
| 28 |
- have been altered with --user/--group/--chroot. |
|
| 29 |
- Tested on Linux only. |
|
| 30 |
- |
|
| 31 |
-down-root -- Enable the running of down scripts with root privileges |
|
| 32 |
- even if --user/--group/--chroot have been used |
|
| 33 |
- to drop root privileges or change the execution |
|
| 34 |
- environment. Not applicable on Windows. |
|
| 35 |
- |
|
| 36 |
-examples -- A simple example that demonstrates a portable |
|
| 37 |
- plugin, i.e. one which can be built for *nix |
|
| 38 |
- or Windows from the same source. |
|
| 39 |
- |
|
| 40 |
-Building Plugins |
|
| 41 |
- |
|
| 42 |
-cd to the top-level directory of a plugin, and use the |
|
| 43 |
-"make" command to build it. The examples plugin is |
|
| 44 |
-built using a build script, not a makefile. |
| 2 | 1 |
deleted file mode 100755 |
| ... | ... |
@@ -1,30 +0,0 @@ |
| 1 |
-# |
|
| 2 |
-# Build the OpenVPN auth-pam plugin module. |
|
| 3 |
-# |
|
| 4 |
- |
|
| 5 |
-# If PAM modules are not linked against libpam.so, set DLOPEN_PAM to 1. This |
|
| 6 |
-# must be done on SUSE 9.1, at least. |
|
| 7 |
-DLOPEN_PAM=1 |
|
| 8 |
- |
|
| 9 |
-ifeq ($(DLOPEN_PAM),1) |
|
| 10 |
- LIBPAM=-ldl |
|
| 11 |
-else |
|
| 12 |
- LIBPAM=-lpam |
|
| 13 |
-endif |
|
| 14 |
- |
|
| 15 |
-# This directory is where we will look for openvpn-plugin.h |
|
| 16 |
-INCLUDE=-I../.. |
|
| 17 |
- |
|
| 18 |
-CC_FLAGS=-O2 -Wall -DDLOPEN_PAM=$(DLOPEN_PAM) |
|
| 19 |
- |
|
| 20 |
-openvpn-auth-pam.so : auth-pam.o pamdl.o |
|
| 21 |
- gcc ${CC_FLAGS} -fPIC -shared -Wl,-soname,openvpn-auth-pam.so -o openvpn-auth-pam.so auth-pam.o pamdl.o -lc $(LIBPAM)
|
|
| 22 |
- |
|
| 23 |
-auth-pam.o : auth-pam.c pamdl.h |
|
| 24 |
- gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} auth-pam.c
|
|
| 25 |
- |
|
| 26 |
-pamdl.o : pamdl.c pamdl.h |
|
| 27 |
- gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} pamdl.c
|
|
| 28 |
- |
|
| 29 |
-clean : |
|
| 30 |
- rm -f *.o *.so |
| 31 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,74 +0,0 @@ |
| 1 |
-openvpn-auth-pam |
|
| 2 |
- |
|
| 3 |
-SYNOPSIS |
|
| 4 |
- |
|
| 5 |
-The openvpn-auth-pam module implements username/password |
|
| 6 |
-authentication via PAM, and essentially allows any authentication |
|
| 7 |
-method supported by PAM (such as LDAP, RADIUS, or Linux Shadow |
|
| 8 |
-passwords) to be used with OpenVPN. While PAM supports |
|
| 9 |
-username/password authentication, this can be combined with X509 |
|
| 10 |
-certificates to provide two indepedent levels of authentication. |
|
| 11 |
- |
|
| 12 |
-This module uses a split privilege execution model which will |
|
| 13 |
-function even if you drop openvpn daemon privileges using the user, |
|
| 14 |
-group, or chroot directives. |
|
| 15 |
- |
|
| 16 |
-BUILD |
|
| 17 |
- |
|
| 18 |
-To build openvpn-auth-pam, you will need to have the pam-devel |
|
| 19 |
-package installed. |
|
| 20 |
- |
|
| 21 |
-Build with the "make" command. The module will be named |
|
| 22 |
-openvpn-auth-pam.so |
|
| 23 |
- |
|
| 24 |
-USAGE |
|
| 25 |
- |
|
| 26 |
-To use this plugin module, add to your OpenVPN config file: |
|
| 27 |
- |
|
| 28 |
- plugin openvpn-auth-pam.so service-type |
|
| 29 |
- |
|
| 30 |
-The required service-type parameter corresponds to |
|
| 31 |
-the PAM service definition file usually found |
|
| 32 |
-in /etc/pam.d. |
|
| 33 |
- |
|
| 34 |
-This plugin also supports the usage of a list of name/value |
|
| 35 |
-pairs to answer PAM module queries. |
|
| 36 |
- |
|
| 37 |
-For example: |
|
| 38 |
- |
|
| 39 |
- plugin openvpn-auth-pam.so "login login USERNAME password PASSWORD" |
|
| 40 |
- |
|
| 41 |
-tells auth-pam to (a) use the "login" PAM module, (b) answer a |
|
| 42 |
-"login" query with the username given by the OpenVPN client, and |
|
| 43 |
-(c) answer a "password" query with the password given by the |
|
| 44 |
-OpenVPN client. This provides flexibility in dealing with the different |
|
| 45 |
-types of query strings which different PAM modules might generate. |
|
| 46 |
-For example, suppose you were using a PAM module called |
|
| 47 |
-"test" which queried for "name" rather than "login": |
|
| 48 |
- |
|
| 49 |
- plugin openvpn-auth-pam.so "test name USERNAME password PASSWORD" |
|
| 50 |
- |
|
| 51 |
-While "USERNAME" and "PASSWORD" are special strings which substitute |
|
| 52 |
-to client-supplied values, it is also possible to name literal values |
|
| 53 |
-to use as PAM module query responses. For example, suppose that the |
|
| 54 |
-login module queried for a third parameter, "domain" which |
|
| 55 |
-is to be answered with the constant value "mydomain.com": |
|
| 56 |
- |
|
| 57 |
- plugin openvpn-auth-pam.so "login login USERNAME password PASSWORD domain mydomain.com" |
|
| 58 |
- |
|
| 59 |
-The following OpenVPN directives can also influence |
|
| 60 |
-the operation of this plugin: |
|
| 61 |
- |
|
| 62 |
- client-cert-not-required |
|
| 63 |
- username-as-common-name |
|
| 64 |
- |
|
| 65 |
-Run OpenVPN with --verb 7 or higher to get debugging output from |
|
| 66 |
-this plugin, including the list of queries presented by the |
|
| 67 |
-underlying PAM module. This is a useful debugging tool to figure |
|
| 68 |
-out which queries a given PAM module is making, so that you can |
|
| 69 |
-craft the appropriate plugin directive to answer it. |
|
| 70 |
- |
|
| 71 |
-CAVEATS |
|
| 72 |
- |
|
| 73 |
-This module will only work on *nix systems which support PAM, |
|
| 74 |
-not Windows. |
| 75 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,761 +0,0 @@ |
| 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-2005 OpenVPN Solutions LLC <info@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 |
- * OpenVPN plugin module to do PAM authentication using a split |
|
| 27 |
- * privilege model. |
|
| 28 |
- */ |
|
| 29 |
- |
|
| 30 |
-#if DLOPEN_PAM |
|
| 31 |
-#include <dlfcn.h> |
|
| 32 |
-#include "pamdl.h" |
|
| 33 |
-#else |
|
| 34 |
-#include <security/pam_appl.h> |
|
| 35 |
-#endif |
|
| 36 |
- |
|
| 37 |
-#include <stdio.h> |
|
| 38 |
-#include <string.h> |
|
| 39 |
-#include <ctype.h> |
|
| 40 |
-#include <unistd.h> |
|
| 41 |
-#include <stdlib.h> |
|
| 42 |
-#include <sys/types.h> |
|
| 43 |
-#include <sys/socket.h> |
|
| 44 |
-#include <sys/wait.h> |
|
| 45 |
-#include <fcntl.h> |
|
| 46 |
-#include <signal.h> |
|
| 47 |
-#include <syslog.h> |
|
| 48 |
- |
|
| 49 |
-#include "openvpn-plugin.h" |
|
| 50 |
- |
|
| 51 |
-#define DEBUG(verb) ((verb) >= 4) |
|
| 52 |
- |
|
| 53 |
-/* Command codes for foreground -> background communication */ |
|
| 54 |
-#define COMMAND_VERIFY 0 |
|
| 55 |
-#define COMMAND_EXIT 1 |
|
| 56 |
- |
|
| 57 |
-/* Response codes for background -> foreground communication */ |
|
| 58 |
-#define RESPONSE_INIT_SUCCEEDED 10 |
|
| 59 |
-#define RESPONSE_INIT_FAILED 11 |
|
| 60 |
-#define RESPONSE_VERIFY_SUCCEEDED 12 |
|
| 61 |
-#define RESPONSE_VERIFY_FAILED 13 |
|
| 62 |
- |
|
| 63 |
-/* |
|
| 64 |
- * Plugin state, used by foreground |
|
| 65 |
- */ |
|
| 66 |
-struct auth_pam_context |
|
| 67 |
-{
|
|
| 68 |
- /* Foreground's socket to background process */ |
|
| 69 |
- int foreground_fd; |
|
| 70 |
- |
|
| 71 |
- /* Process ID of background process */ |
|
| 72 |
- pid_t background_pid; |
|
| 73 |
- |
|
| 74 |
- /* Verbosity level of OpenVPN */ |
|
| 75 |
- int verb; |
|
| 76 |
-}; |
|
| 77 |
- |
|
| 78 |
-/* |
|
| 79 |
- * Name/Value pairs for conversation function. |
|
| 80 |
- * Special Values: |
|
| 81 |
- * |
|
| 82 |
- * "USERNAME" -- substitute client-supplied username |
|
| 83 |
- * "PASSWORD" -- substitute client-specified password |
|
| 84 |
- */ |
|
| 85 |
- |
|
| 86 |
-#define N_NAME_VALUE 16 |
|
| 87 |
- |
|
| 88 |
-struct name_value {
|
|
| 89 |
- const char *name; |
|
| 90 |
- const char *value; |
|
| 91 |
-}; |
|
| 92 |
- |
|
| 93 |
-struct name_value_list {
|
|
| 94 |
- int len; |
|
| 95 |
- struct name_value data[N_NAME_VALUE]; |
|
| 96 |
-}; |
|
| 97 |
- |
|
| 98 |
-/* |
|
| 99 |
- * Used to pass the username/password |
|
| 100 |
- * to the PAM conversation function. |
|
| 101 |
- */ |
|
| 102 |
-struct user_pass {
|
|
| 103 |
- int verb; |
|
| 104 |
- |
|
| 105 |
- char username[128]; |
|
| 106 |
- char password[128]; |
|
| 107 |
- |
|
| 108 |
- const struct name_value_list *name_value_list; |
|
| 109 |
-}; |
|
| 110 |
- |
|
| 111 |
-/* Background process function */ |
|
| 112 |
-static void pam_server (int fd, const char *service, int verb, const struct name_value_list *name_value_list); |
|
| 113 |
- |
|
| 114 |
-/* |
|
| 115 |
- * Given an environmental variable name, search |
|
| 116 |
- * the envp array for its value, returning it |
|
| 117 |
- * if found or NULL otherwise. |
|
| 118 |
- */ |
|
| 119 |
-static const char * |
|
| 120 |
-get_env (const char *name, const char *envp[]) |
|
| 121 |
-{
|
|
| 122 |
- if (envp) |
|
| 123 |
- {
|
|
| 124 |
- int i; |
|
| 125 |
- const int namelen = strlen (name); |
|
| 126 |
- for (i = 0; envp[i]; ++i) |
|
| 127 |
- {
|
|
| 128 |
- if (!strncmp (envp[i], name, namelen)) |
|
| 129 |
- {
|
|
| 130 |
- const char *cp = envp[i] + namelen; |
|
| 131 |
- if (*cp == '=') |
|
| 132 |
- return cp + 1; |
|
| 133 |
- } |
|
| 134 |
- } |
|
| 135 |
- } |
|
| 136 |
- return NULL; |
|
| 137 |
-} |
|
| 138 |
- |
|
| 139 |
-/* |
|
| 140 |
- * Return the length of a string array |
|
| 141 |
- */ |
|
| 142 |
-static int |
|
| 143 |
-string_array_len (const char *array[]) |
|
| 144 |
-{
|
|
| 145 |
- int i = 0; |
|
| 146 |
- if (array) |
|
| 147 |
- {
|
|
| 148 |
- while (array[i]) |
|
| 149 |
- ++i; |
|
| 150 |
- } |
|
| 151 |
- return i; |
|
| 152 |
-} |
|
| 153 |
- |
|
| 154 |
-/* |
|
| 155 |
- * Socket read/write functions. |
|
| 156 |
- */ |
|
| 157 |
- |
|
| 158 |
-static int |
|
| 159 |
-recv_control (int fd) |
|
| 160 |
-{
|
|
| 161 |
- unsigned char c; |
|
| 162 |
- const ssize_t size = read (fd, &c, sizeof (c)); |
|
| 163 |
- if (size == sizeof (c)) |
|
| 164 |
- return c; |
|
| 165 |
- else |
|
| 166 |
- {
|
|
| 167 |
- /*fprintf (stderr, "AUTH-PAM: DEBUG recv_control.read=%d\n", (int)size);*/ |
|
| 168 |
- return -1; |
|
| 169 |
- } |
|
| 170 |
-} |
|
| 171 |
- |
|
| 172 |
-static int |
|
| 173 |
-send_control (int fd, int code) |
|
| 174 |
-{
|
|
| 175 |
- unsigned char c = (unsigned char) code; |
|
| 176 |
- const ssize_t size = write (fd, &c, sizeof (c)); |
|
| 177 |
- if (size == sizeof (c)) |
|
| 178 |
- return (int) size; |
|
| 179 |
- else |
|
| 180 |
- return -1; |
|
| 181 |
-} |
|
| 182 |
- |
|
| 183 |
-static int |
|
| 184 |
-recv_string (int fd, char *buffer, int len) |
|
| 185 |
-{
|
|
| 186 |
- if (len > 0) |
|
| 187 |
- {
|
|
| 188 |
- ssize_t size; |
|
| 189 |
- memset (buffer, 0, len); |
|
| 190 |
- size = read (fd, buffer, len); |
|
| 191 |
- buffer[len-1] = 0; |
|
| 192 |
- if (size >= 1) |
|
| 193 |
- return (int)size; |
|
| 194 |
- } |
|
| 195 |
- return -1; |
|
| 196 |
-} |
|
| 197 |
- |
|
| 198 |
-static int |
|
| 199 |
-send_string (int fd, const char *string) |
|
| 200 |
-{
|
|
| 201 |
- const int len = strlen (string) + 1; |
|
| 202 |
- const ssize_t size = write (fd, string, len); |
|
| 203 |
- if (size == len) |
|
| 204 |
- return (int) size; |
|
| 205 |
- else |
|
| 206 |
- return -1; |
|
| 207 |
-} |
|
| 208 |
- |
|
| 209 |
-#ifdef DO_DAEMONIZE |
|
| 210 |
- |
|
| 211 |
-/* |
|
| 212 |
- * Daemonize if "daemon" env var is true. |
|
| 213 |
- * Preserve stderr across daemonization if |
|
| 214 |
- * "daemon_log_redirect" env var is true. |
|
| 215 |
- */ |
|
| 216 |
-static void |
|
| 217 |
-daemonize (const char *envp[]) |
|
| 218 |
-{
|
|
| 219 |
- const char *daemon_string = get_env ("daemon", envp);
|
|
| 220 |
- if (daemon_string && daemon_string[0] == '1') |
|
| 221 |
- {
|
|
| 222 |
- const char *log_redirect = get_env ("daemon_log_redirect", envp);
|
|
| 223 |
- int fd = -1; |
|
| 224 |
- if (log_redirect && log_redirect[0] == '1') |
|
| 225 |
- fd = dup (2); |
|
| 226 |
- if (daemon (0, 0) < 0) |
|
| 227 |
- {
|
|
| 228 |
- fprintf (stderr, "AUTH-PAM: daemonization failed\n"); |
|
| 229 |
- } |
|
| 230 |
- else if (fd >= 3) |
|
| 231 |
- {
|
|
| 232 |
- dup2 (fd, 2); |
|
| 233 |
- close (fd); |
|
| 234 |
- } |
|
| 235 |
- } |
|
| 236 |
-} |
|
| 237 |
- |
|
| 238 |
-#endif |
|
| 239 |
- |
|
| 240 |
-/* |
|
| 241 |
- * Close most of parent's fds. |
|
| 242 |
- * Keep stdin/stdout/stderr, plus one |
|
| 243 |
- * other fd which is presumed to be |
|
| 244 |
- * our pipe back to parent. |
|
| 245 |
- * Admittedly, a bit of a kludge, |
|
| 246 |
- * but posix doesn't give us a kind |
|
| 247 |
- * of FD_CLOEXEC which will stop |
|
| 248 |
- * fds from crossing a fork(). |
|
| 249 |
- */ |
|
| 250 |
-static void |
|
| 251 |
-close_fds_except (int keep) |
|
| 252 |
-{
|
|
| 253 |
- int i; |
|
| 254 |
- closelog (); |
|
| 255 |
- for (i = 3; i <= 100; ++i) |
|
| 256 |
- {
|
|
| 257 |
- if (i != keep) |
|
| 258 |
- close (i); |
|
| 259 |
- } |
|
| 260 |
-} |
|
| 261 |
- |
|
| 262 |
-/* |
|
| 263 |
- * Usually we ignore signals, because our parent will |
|
| 264 |
- * deal with them. |
|
| 265 |
- */ |
|
| 266 |
-static void |
|
| 267 |
-set_signals (void) |
|
| 268 |
-{
|
|
| 269 |
- signal (SIGTERM, SIG_DFL); |
|
| 270 |
- |
|
| 271 |
- signal (SIGINT, SIG_IGN); |
|
| 272 |
- signal (SIGHUP, SIG_IGN); |
|
| 273 |
- signal (SIGUSR1, SIG_IGN); |
|
| 274 |
- signal (SIGUSR2, SIG_IGN); |
|
| 275 |
- signal (SIGPIPE, SIG_IGN); |
|
| 276 |
-} |
|
| 277 |
- |
|
| 278 |
-/* |
|
| 279 |
- * Return 1 if query matches match. |
|
| 280 |
- */ |
|
| 281 |
-static int |
|
| 282 |
-name_value_match (const char *query, const char *match) |
|
| 283 |
-{
|
|
| 284 |
- while (!isalnum (*query)) |
|
| 285 |
- {
|
|
| 286 |
- if (*query == '\0') |
|
| 287 |
- return 0; |
|
| 288 |
- ++query; |
|
| 289 |
- } |
|
| 290 |
- return strncasecmp (match, query, strlen (match)) == 0; |
|
| 291 |
-} |
|
| 292 |
- |
|
| 293 |
-OPENVPN_EXPORT openvpn_plugin_handle_t |
|
| 294 |
-openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) |
|
| 295 |
-{
|
|
| 296 |
- pid_t pid; |
|
| 297 |
- int fd[2]; |
|
| 298 |
- |
|
| 299 |
- struct auth_pam_context *context; |
|
| 300 |
- struct name_value_list name_value_list; |
|
| 301 |
- |
|
| 302 |
- const int base_parms = 2; |
|
| 303 |
- |
|
| 304 |
- /* |
|
| 305 |
- * Allocate our context |
|
| 306 |
- */ |
|
| 307 |
- context = (struct auth_pam_context *) calloc (1, sizeof (struct auth_pam_context)); |
|
| 308 |
- context->foreground_fd = -1; |
|
| 309 |
- |
|
| 310 |
- /* |
|
| 311 |
- * Intercept the --auth-user-pass-verify callback. |
|
| 312 |
- */ |
|
| 313 |
- *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY); |
|
| 314 |
- |
|
| 315 |
- /* |
|
| 316 |
- * Make sure we have two string arguments: the first is the .so name, |
|
| 317 |
- * the second is the PAM service type. |
|
| 318 |
- */ |
|
| 319 |
- if (string_array_len (argv) < base_parms) |
|
| 320 |
- {
|
|
| 321 |
- fprintf (stderr, "AUTH-PAM: need PAM service parameter\n"); |
|
| 322 |
- goto error; |
|
| 323 |
- } |
|
| 324 |
- |
|
| 325 |
- /* |
|
| 326 |
- * See if we have optional name/value pairs to match against |
|
| 327 |
- * PAM module queried fields in the conversation function. |
|
| 328 |
- */ |
|
| 329 |
- name_value_list.len = 0; |
|
| 330 |
- if (string_array_len (argv) > base_parms) |
|
| 331 |
- {
|
|
| 332 |
- const int nv_len = string_array_len (argv) - base_parms; |
|
| 333 |
- int i; |
|
| 334 |
- |
|
| 335 |
- if ((nv_len & 1) == 1 || (nv_len / 2) > N_NAME_VALUE) |
|
| 336 |
- {
|
|
| 337 |
- fprintf (stderr, "AUTH-PAM: bad name/value list length\n"); |
|
| 338 |
- goto error; |
|
| 339 |
- } |
|
| 340 |
- |
|
| 341 |
- name_value_list.len = nv_len / 2; |
|
| 342 |
- for (i = 0; i < name_value_list.len; ++i) |
|
| 343 |
- {
|
|
| 344 |
- const int base = base_parms + i * 2; |
|
| 345 |
- name_value_list.data[i].name = argv[base]; |
|
| 346 |
- name_value_list.data[i].value = argv[base+1]; |
|
| 347 |
- } |
|
| 348 |
- } |
|
| 349 |
- |
|
| 350 |
- /* |
|
| 351 |
- * Get verbosity level from environment |
|
| 352 |
- */ |
|
| 353 |
- {
|
|
| 354 |
- const char *verb_string = get_env ("verb", envp);
|
|
| 355 |
- if (verb_string) |
|
| 356 |
- context->verb = atoi (verb_string); |
|
| 357 |
- } |
|
| 358 |
- |
|
| 359 |
- /* |
|
| 360 |
- * Make a socket for foreground and background processes |
|
| 361 |
- * to communicate. |
|
| 362 |
- */ |
|
| 363 |
- if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1) |
|
| 364 |
- {
|
|
| 365 |
- fprintf (stderr, "AUTH-PAM: socketpair call failed\n"); |
|
| 366 |
- goto error; |
|
| 367 |
- } |
|
| 368 |
- |
|
| 369 |
- /* |
|
| 370 |
- * Fork off the privileged process. It will remain privileged |
|
| 371 |
- * even after the foreground process drops its privileges. |
|
| 372 |
- */ |
|
| 373 |
- pid = fork (); |
|
| 374 |
- |
|
| 375 |
- if (pid) |
|
| 376 |
- {
|
|
| 377 |
- int status; |
|
| 378 |
- |
|
| 379 |
- /* |
|
| 380 |
- * Foreground Process |
|
| 381 |
- */ |
|
| 382 |
- |
|
| 383 |
- context->background_pid = pid; |
|
| 384 |
- |
|
| 385 |
- /* close our copy of child's socket */ |
|
| 386 |
- close (fd[1]); |
|
| 387 |
- |
|
| 388 |
- /* don't let future subprocesses inherit child socket */ |
|
| 389 |
- if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0) |
|
| 390 |
- fprintf (stderr, "AUTH-PAM: Set FD_CLOEXEC flag on socket file descriptor failed\n"); |
|
| 391 |
- |
|
| 392 |
- /* wait for background child process to initialize */ |
|
| 393 |
- status = recv_control (fd[0]); |
|
| 394 |
- if (status == RESPONSE_INIT_SUCCEEDED) |
|
| 395 |
- {
|
|
| 396 |
- context->foreground_fd = fd[0]; |
|
| 397 |
- return (openvpn_plugin_handle_t) context; |
|
| 398 |
- } |
|
| 399 |
- } |
|
| 400 |
- else |
|
| 401 |
- {
|
|
| 402 |
- /* |
|
| 403 |
- * Background Process |
|
| 404 |
- */ |
|
| 405 |
- |
|
| 406 |
- /* close all parent fds except our socket back to parent */ |
|
| 407 |
- close_fds_except (fd[1]); |
|
| 408 |
- |
|
| 409 |
- /* Ignore most signals (the parent will receive them) */ |
|
| 410 |
- set_signals (); |
|
| 411 |
- |
|
| 412 |
-#ifdef DO_DAEMONIZE |
|
| 413 |
- /* Daemonize if --daemon option is set. */ |
|
| 414 |
- daemonize (envp); |
|
| 415 |
-#endif |
|
| 416 |
- |
|
| 417 |
- /* execute the event loop */ |
|
| 418 |
- pam_server (fd[1], argv[1], context->verb, &name_value_list); |
|
| 419 |
- |
|
| 420 |
- close (fd[1]); |
|
| 421 |
- |
|
| 422 |
- exit (0); |
|
| 423 |
- return 0; /* NOTREACHED */ |
|
| 424 |
- } |
|
| 425 |
- |
|
| 426 |
- error: |
|
| 427 |
- if (context) |
|
| 428 |
- free (context); |
|
| 429 |
- return NULL; |
|
| 430 |
-} |
|
| 431 |
- |
|
| 432 |
-OPENVPN_EXPORT int |
|
| 433 |
-openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) |
|
| 434 |
-{
|
|
| 435 |
- struct auth_pam_context *context = (struct auth_pam_context *) handle; |
|
| 436 |
- |
|
| 437 |
- if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY && context->foreground_fd >= 0) |
|
| 438 |
- {
|
|
| 439 |
- /* get username/password from envp string array */ |
|
| 440 |
- const char *username = get_env ("username", envp);
|
|
| 441 |
- const char *password = get_env ("password", envp);
|
|
| 442 |
- |
|
| 443 |
- if (username && strlen (username) > 0 && password) |
|
| 444 |
- {
|
|
| 445 |
- if (send_control (context->foreground_fd, COMMAND_VERIFY) == -1 |
|
| 446 |
- || send_string (context->foreground_fd, username) == -1 |
|
| 447 |
- || send_string (context->foreground_fd, password) == -1) |
|
| 448 |
- {
|
|
| 449 |
- fprintf (stderr, "AUTH-PAM: Error sending auth info to background process\n"); |
|
| 450 |
- } |
|
| 451 |
- else |
|
| 452 |
- {
|
|
| 453 |
- const int status = recv_control (context->foreground_fd); |
|
| 454 |
- if (status == RESPONSE_VERIFY_SUCCEEDED) |
|
| 455 |
- return OPENVPN_PLUGIN_FUNC_SUCCESS; |
|
| 456 |
- if (status == -1) |
|
| 457 |
- fprintf (stderr, "AUTH-PAM: Error receiving auth confirmation from background process\n"); |
|
| 458 |
- } |
|
| 459 |
- } |
|
| 460 |
- } |
|
| 461 |
- return OPENVPN_PLUGIN_FUNC_ERROR; |
|
| 462 |
-} |
|
| 463 |
- |
|
| 464 |
-OPENVPN_EXPORT void |
|
| 465 |
-openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) |
|
| 466 |
-{
|
|
| 467 |
- struct auth_pam_context *context = (struct auth_pam_context *) handle; |
|
| 468 |
- |
|
| 469 |
- if (DEBUG (context->verb)) |
|
| 470 |
- fprintf (stderr, "AUTH-PAM: close\n"); |
|
| 471 |
- |
|
| 472 |
- if (context->foreground_fd >= 0) |
|
| 473 |
- {
|
|
| 474 |
- /* tell background process to exit */ |
|
| 475 |
- if (send_control (context->foreground_fd, COMMAND_EXIT) == -1) |
|
| 476 |
- fprintf (stderr, "AUTH-PAM: Error signaling background process to exit\n"); |
|
| 477 |
- |
|
| 478 |
- /* wait for background process to exit */ |
|
| 479 |
- if (context->background_pid > 0) |
|
| 480 |
- waitpid (context->background_pid, NULL, 0); |
|
| 481 |
- |
|
| 482 |
- close (context->foreground_fd); |
|
| 483 |
- context->foreground_fd = -1; |
|
| 484 |
- } |
|
| 485 |
- |
|
| 486 |
- free (context); |
|
| 487 |
-} |
|
| 488 |
- |
|
| 489 |
-OPENVPN_EXPORT void |
|
| 490 |
-openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle) |
|
| 491 |
-{
|
|
| 492 |
- struct auth_pam_context *context = (struct auth_pam_context *) handle; |
|
| 493 |
- |
|
| 494 |
- /* tell background process to exit */ |
|
| 495 |
- if (context->foreground_fd >= 0) |
|
| 496 |
- {
|
|
| 497 |
- send_control (context->foreground_fd, COMMAND_EXIT); |
|
| 498 |
- close (context->foreground_fd); |
|
| 499 |
- context->foreground_fd = -1; |
|
| 500 |
- } |
|
| 501 |
-} |
|
| 502 |
- |
|
| 503 |
-/* |
|
| 504 |
- * PAM conversation function |
|
| 505 |
- */ |
|
| 506 |
-static int |
|
| 507 |
-my_conv (int n, const struct pam_message **msg_array, |
|
| 508 |
- struct pam_response **response_array, void *appdata_ptr) |
|
| 509 |
-{
|
|
| 510 |
- const struct user_pass *up = ( const struct user_pass *) appdata_ptr; |
|
| 511 |
- struct pam_response *aresp; |
|
| 512 |
- int i; |
|
| 513 |
- int ret = PAM_SUCCESS; |
|
| 514 |
- |
|
| 515 |
- *response_array = NULL; |
|
| 516 |
- |
|
| 517 |
- if (n <= 0 || n > PAM_MAX_NUM_MSG) |
|
| 518 |
- return (PAM_CONV_ERR); |
|
| 519 |
- if ((aresp = calloc (n, sizeof *aresp)) == NULL) |
|
| 520 |
- return (PAM_BUF_ERR); |
|
| 521 |
- |
|
| 522 |
- /* loop through each PAM-module query */ |
|
| 523 |
- for (i = 0; i < n; ++i) |
|
| 524 |
- {
|
|
| 525 |
- const struct pam_message *msg = msg_array[i]; |
|
| 526 |
- aresp[i].resp_retcode = 0; |
|
| 527 |
- aresp[i].resp = NULL; |
|
| 528 |
- |
|
| 529 |
- if (DEBUG (up->verb)) |
|
| 530 |
- {
|
|
| 531 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: my_conv[%d] query='%s' style=%d\n", |
|
| 532 |
- i, |
|
| 533 |
- msg->msg ? msg->msg : "NULL", |
|
| 534 |
- msg->msg_style); |
|
| 535 |
- } |
|
| 536 |
- |
|
| 537 |
- if (up->name_value_list && up->name_value_list->len > 0) |
|
| 538 |
- {
|
|
| 539 |
- /* use name/value list match method */ |
|
| 540 |
- const struct name_value_list *list = up->name_value_list; |
|
| 541 |
- int j; |
|
| 542 |
- |
|
| 543 |
- /* loop through name/value pairs */ |
|
| 544 |
- for (j = 0; j < list->len; ++j) |
|
| 545 |
- {
|
|
| 546 |
- const char *match_name = list->data[j].name; |
|
| 547 |
- const char *match_value = list->data[j].value; |
|
| 548 |
- |
|
| 549 |
- if (name_value_match (msg->msg, match_name)) |
|
| 550 |
- {
|
|
| 551 |
- /* found name/value match */ |
|
| 552 |
- const char *return_value = NULL; |
|
| 553 |
- |
|
| 554 |
- if (DEBUG (up->verb)) |
|
| 555 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: name match found, query/match-string ['%s', '%s'] = '%s'\n", |
|
| 556 |
- msg->msg, |
|
| 557 |
- match_name, |
|
| 558 |
- match_value); |
|
| 559 |
- |
|
| 560 |
- if (!strcmp (match_value, "USERNAME")) |
|
| 561 |
- return_value = up->username; |
|
| 562 |
- else if (!strcmp (match_value, "PASSWORD")) |
|
| 563 |
- return_value = up->password; |
|
| 564 |
- else |
|
| 565 |
- return_value = match_value; |
|
| 566 |
- |
|
| 567 |
- aresp[i].resp = strdup (return_value); |
|
| 568 |
- if (aresp[i].resp == NULL) |
|
| 569 |
- ret = PAM_CONV_ERR; |
|
| 570 |
- break; |
|
| 571 |
- } |
|
| 572 |
- } |
|
| 573 |
- |
|
| 574 |
- if (j == list->len) |
|
| 575 |
- ret = PAM_CONV_ERR; |
|
| 576 |
- } |
|
| 577 |
- else |
|
| 578 |
- {
|
|
| 579 |
- /* use PAM_PROMPT_ECHO_x hints */ |
|
| 580 |
- switch (msg->msg_style) |
|
| 581 |
- {
|
|
| 582 |
- case PAM_PROMPT_ECHO_OFF: |
|
| 583 |
- aresp[i].resp = strdup (up->password); |
|
| 584 |
- if (aresp[i].resp == NULL) |
|
| 585 |
- ret = PAM_CONV_ERR; |
|
| 586 |
- break; |
|
| 587 |
- |
|
| 588 |
- case PAM_PROMPT_ECHO_ON: |
|
| 589 |
- aresp[i].resp = strdup (up->username); |
|
| 590 |
- if (aresp[i].resp == NULL) |
|
| 591 |
- ret = PAM_CONV_ERR; |
|
| 592 |
- break; |
|
| 593 |
- |
|
| 594 |
- case PAM_ERROR_MSG: |
|
| 595 |
- case PAM_TEXT_INFO: |
|
| 596 |
- break; |
|
| 597 |
- |
|
| 598 |
- default: |
|
| 599 |
- ret = PAM_CONV_ERR; |
|
| 600 |
- break; |
|
| 601 |
- } |
|
| 602 |
- } |
|
| 603 |
- } |
|
| 604 |
- |
|
| 605 |
- if (ret == PAM_SUCCESS) |
|
| 606 |
- *response_array = aresp; |
|
| 607 |
- return ret; |
|
| 608 |
-} |
|
| 609 |
- |
|
| 610 |
-/* |
|
| 611 |
- * Return 1 if authenticated and 0 if failed. |
|
| 612 |
- * Called once for every username/password |
|
| 613 |
- * to be authenticated. |
|
| 614 |
- */ |
|
| 615 |
-static int |
|
| 616 |
-pam_auth (const char *service, const struct user_pass *up) |
|
| 617 |
-{
|
|
| 618 |
- struct pam_conv conv; |
|
| 619 |
- pam_handle_t *pamh = NULL; |
|
| 620 |
- int status = PAM_SUCCESS; |
|
| 621 |
- int ret = 0; |
|
| 622 |
- const int name_value_list_provided = (up->name_value_list && up->name_value_list->len > 0); |
|
| 623 |
- |
|
| 624 |
- /* Initialize PAM */ |
|
| 625 |
- conv.conv = my_conv; |
|
| 626 |
- conv.appdata_ptr = (void *)up; |
|
| 627 |
- status = pam_start (service, name_value_list_provided ? NULL : up->username, &conv, &pamh); |
|
| 628 |
- if (status == PAM_SUCCESS) |
|
| 629 |
- {
|
|
| 630 |
- /* Call PAM to verify username/password */ |
|
| 631 |
- status = pam_authenticate(pamh, 0); |
|
| 632 |
- if (status == PAM_SUCCESS) |
|
| 633 |
- status = pam_acct_mgmt (pamh, 0); |
|
| 634 |
- if (status == PAM_SUCCESS) |
|
| 635 |
- ret = 1; |
|
| 636 |
- |
|
| 637 |
- /* Output error message if failed */ |
|
| 638 |
- if (!ret) |
|
| 639 |
- {
|
|
| 640 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: user '%s' failed to authenticate: %s\n", |
|
| 641 |
- up->username, |
|
| 642 |
- pam_strerror (pamh, status)); |
|
| 643 |
- } |
|
| 644 |
- |
|
| 645 |
- /* Close PAM */ |
|
| 646 |
- pam_end (pamh, status); |
|
| 647 |
- } |
|
| 648 |
- |
|
| 649 |
- return ret; |
|
| 650 |
-} |
|
| 651 |
- |
|
| 652 |
-/* |
|
| 653 |
- * Background process -- runs with privilege. |
|
| 654 |
- */ |
|
| 655 |
-static void |
|
| 656 |
-pam_server (int fd, const char *service, int verb, const struct name_value_list *name_value_list) |
|
| 657 |
-{
|
|
| 658 |
- struct user_pass up; |
|
| 659 |
- int command; |
|
| 660 |
-#if DLOPEN_PAM |
|
| 661 |
- static const char pam_so[] = "libpam.so"; |
|
| 662 |
-#endif |
|
| 663 |
- |
|
| 664 |
- /* |
|
| 665 |
- * Do initialization |
|
| 666 |
- */ |
|
| 667 |
- if (DEBUG (verb)) |
|
| 668 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: INIT service='%s'\n", service); |
|
| 669 |
- |
|
| 670 |
-#if DLOPEN_PAM |
|
| 671 |
- /* |
|
| 672 |
- * Load PAM shared object |
|
| 673 |
- */ |
|
| 674 |
- if (!dlopen_pam (pam_so)) |
|
| 675 |
- {
|
|
| 676 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: could not load PAM lib %s: %s\n", pam_so, dlerror()); |
|
| 677 |
- send_control (fd, RESPONSE_INIT_FAILED); |
|
| 678 |
- goto done; |
|
| 679 |
- } |
|
| 680 |
-#endif |
|
| 681 |
- |
|
| 682 |
- /* |
|
| 683 |
- * Tell foreground that we initialized successfully |
|
| 684 |
- */ |
|
| 685 |
- if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1) |
|
| 686 |
- {
|
|
| 687 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [1]\n"); |
|
| 688 |
- goto done; |
|
| 689 |
- } |
|
| 690 |
- |
|
| 691 |
- /* |
|
| 692 |
- * Event loop |
|
| 693 |
- */ |
|
| 694 |
- while (1) |
|
| 695 |
- {
|
|
| 696 |
- memset (&up, 0, sizeof (up)); |
|
| 697 |
- up.verb = verb; |
|
| 698 |
- up.name_value_list = name_value_list; |
|
| 699 |
- |
|
| 700 |
- /* get a command from foreground process */ |
|
| 701 |
- command = recv_control (fd); |
|
| 702 |
- |
|
| 703 |
- if (DEBUG (verb)) |
|
| 704 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: received command code: %d\n", command); |
|
| 705 |
- |
|
| 706 |
- switch (command) |
|
| 707 |
- {
|
|
| 708 |
- case COMMAND_VERIFY: |
|
| 709 |
- if (recv_string (fd, up.username, sizeof (up.username)) == -1 |
|
| 710 |
- || recv_string (fd, up.password, sizeof (up.password)) == -1) |
|
| 711 |
- {
|
|
| 712 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: read error on command channel: code=%d, exiting\n", |
|
| 713 |
- command); |
|
| 714 |
- goto done; |
|
| 715 |
- } |
|
| 716 |
- |
|
| 717 |
- if (DEBUG (verb)) |
|
| 718 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: USER/PASS: %s/%s\n", |
|
| 719 |
- up.username, up.password); |
|
| 720 |
- |
|
| 721 |
- if (pam_auth (service, &up)) /* Succeeded */ |
|
| 722 |
- {
|
|
| 723 |
- if (send_control (fd, RESPONSE_VERIFY_SUCCEEDED) == -1) |
|
| 724 |
- {
|
|
| 725 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [2]\n"); |
|
| 726 |
- goto done; |
|
| 727 |
- } |
|
| 728 |
- } |
|
| 729 |
- else /* Failed */ |
|
| 730 |
- {
|
|
| 731 |
- if (send_control (fd, RESPONSE_VERIFY_FAILED) == -1) |
|
| 732 |
- {
|
|
| 733 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [3]\n"); |
|
| 734 |
- goto done; |
|
| 735 |
- } |
|
| 736 |
- } |
|
| 737 |
- break; |
|
| 738 |
- |
|
| 739 |
- case COMMAND_EXIT: |
|
| 740 |
- goto done; |
|
| 741 |
- |
|
| 742 |
- case -1: |
|
| 743 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: read error on command channel\n"); |
|
| 744 |
- goto done; |
|
| 745 |
- |
|
| 746 |
- default: |
|
| 747 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: unknown command code: code=%d, exiting\n", |
|
| 748 |
- command); |
|
| 749 |
- goto done; |
|
| 750 |
- } |
|
| 751 |
- } |
|
| 752 |
- done: |
|
| 753 |
- |
|
| 754 |
-#if DLOPEN_PAM |
|
| 755 |
- dlclose_pam (); |
|
| 756 |
-#endif |
|
| 757 |
- if (DEBUG (verb)) |
|
| 758 |
- fprintf (stderr, "AUTH-PAM: BACKGROUND: EXIT\n"); |
|
| 759 |
- |
|
| 760 |
- return; |
|
| 761 |
-} |
| 762 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,181 +0,0 @@ |
| 1 |
-#if DLOPEN_PAM |
|
| 2 |
-/* |
|
| 3 |
- * If you want to dynamically load libpam using dlopen() or something, |
|
| 4 |
- * then dlopen( ' this shared object ' ); It takes care of exporting |
|
| 5 |
- * the right symbols to any modules loaded by libpam. |
|
| 6 |
- * |
|
| 7 |
- * Modified by JY for use with openvpn-pam-auth |
|
| 8 |
- */ |
|
| 9 |
- |
|
| 10 |
-#include <stdio.h> |
|
| 11 |
-#include <dlfcn.h> |
|
| 12 |
-#include <security/pam_appl.h> |
|
| 13 |
-#include <security/_pam_macros.h> |
|
| 14 |
- |
|
| 15 |
-#include "pamdl.h" |
|
| 16 |
- |
|
| 17 |
-static void *libpam_h = NULL; |
|
| 18 |
- |
|
| 19 |
-#define RESOLVE_PAM_FUNCTION(x, y, z, err) \ |
|
| 20 |
- { \
|
|
| 21 |
- union { const void *tpointer; y (*fn) z ; } fptr; \
|
|
| 22 |
- fptr.tpointer = dlsym(libpam_h, #x); real_##x = fptr.fn; \ |
|
| 23 |
- if (real_##x == NULL) { \
|
|
| 24 |
- fprintf (stderr, "PAMDL: unable to resolve '%s': %s\n", #x, dlerror()); \ |
|
| 25 |
- return err; \ |
|
| 26 |
- } \ |
|
| 27 |
- } |
|
| 28 |
- |
|
| 29 |
-int |
|
| 30 |
-dlopen_pam (const char *so) |
|
| 31 |
-{
|
|
| 32 |
- if (libpam_h == NULL) |
|
| 33 |
- {
|
|
| 34 |
- libpam_h = dlopen(so, RTLD_GLOBAL|RTLD_NOW); |
|
| 35 |
- } |
|
| 36 |
- return libpam_h != NULL; |
|
| 37 |
-} |
|
| 38 |
- |
|
| 39 |
-void |
|
| 40 |
-dlclose_pam (void) |
|
| 41 |
-{
|
|
| 42 |
- if (libpam_h != NULL) |
|
| 43 |
- {
|
|
| 44 |
- dlclose(libpam_h); |
|
| 45 |
- libpam_h = NULL; |
|
| 46 |
- } |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-int pam_start(const char *service_name, const char *user, |
|
| 50 |
- const struct pam_conv *pam_conversation, |
|
| 51 |
- pam_handle_t **pamh) |
|
| 52 |
-{
|
|
| 53 |
- int (*real_pam_start)(const char *, const char *, |
|
| 54 |
- const struct pam_conv *, |
|
| 55 |
- pam_handle_t **); |
|
| 56 |
- RESOLVE_PAM_FUNCTION(pam_start, int, (const char *, const char *, |
|
| 57 |
- const struct pam_conv *, |
|
| 58 |
- pam_handle_t **), PAM_ABORT); |
|
| 59 |
- return real_pam_start(service_name, user, pam_conversation, pamh); |
|
| 60 |
-} |
|
| 61 |
- |
|
| 62 |
-int pam_end(pam_handle_t *pamh, int pam_status) |
|
| 63 |
-{
|
|
| 64 |
- int (*real_pam_end)(pam_handle_t *, int); |
|
| 65 |
- RESOLVE_PAM_FUNCTION(pam_end, int, (pam_handle_t *, int), PAM_ABORT); |
|
| 66 |
- return real_pam_end(pamh, pam_status); |
|
| 67 |
-} |
|
| 68 |
- |
|
| 69 |
-int pam_set_item(pam_handle_t *pamh, int item_type, const void *item) |
|
| 70 |
-{
|
|
| 71 |
- int (*real_pam_set_item)(pam_handle_t *, int, const void *); |
|
| 72 |
- RESOLVE_PAM_FUNCTION(pam_set_item, int, |
|
| 73 |
- (pam_handle_t *, int, const void *), PAM_ABORT); |
|
| 74 |
- return real_pam_set_item(pamh, item_type, item); |
|
| 75 |
-} |
|
| 76 |
- |
|
| 77 |
-int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) |
|
| 78 |
-{
|
|
| 79 |
- int (*real_pam_get_item)(const pam_handle_t *, int, const void **); |
|
| 80 |
- RESOLVE_PAM_FUNCTION(pam_get_item, int, |
|
| 81 |
- (const pam_handle_t *, int, const void **), |
|
| 82 |
- PAM_ABORT); |
|
| 83 |
- return real_pam_get_item(pamh, item_type, item); |
|
| 84 |
-} |
|
| 85 |
- |
|
| 86 |
-int pam_fail_delay(pam_handle_t *pamh, unsigned int musec_delay) |
|
| 87 |
-{
|
|
| 88 |
- int (*real_pam_fail_delay)(pam_handle_t *, unsigned int); |
|
| 89 |
- RESOLVE_PAM_FUNCTION(pam_fail_delay, int, (pam_handle_t *, unsigned int), |
|
| 90 |
- PAM_ABORT); |
|
| 91 |
- return real_pam_fail_delay(pamh, musec_delay); |
|
| 92 |
-} |
|
| 93 |
- |
|
| 94 |
-typedef const char * const_char_pointer; |
|
| 95 |
- |
|
| 96 |
-const_char_pointer pam_strerror(pam_handle_t *pamh, int errnum) |
|
| 97 |
-{
|
|
| 98 |
- const_char_pointer (*real_pam_strerror)(pam_handle_t *, int); |
|
| 99 |
- RESOLVE_PAM_FUNCTION(pam_strerror, const_char_pointer, |
|
| 100 |
- (pam_handle_t *, int), NULL); |
|
| 101 |
- return real_pam_strerror(pamh, errnum); |
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-int pam_putenv(pam_handle_t *pamh, const char *name_value) |
|
| 105 |
-{
|
|
| 106 |
- int (*real_pam_putenv)(pam_handle_t *, const char *); |
|
| 107 |
- RESOLVE_PAM_FUNCTION(pam_putenv, int, (pam_handle_t *, const char *), |
|
| 108 |
- PAM_ABORT); |
|
| 109 |
- return real_pam_putenv(pamh, name_value); |
|
| 110 |
-} |
|
| 111 |
- |
|
| 112 |
-const_char_pointer pam_getenv(pam_handle_t *pamh, const char *name) |
|
| 113 |
-{
|
|
| 114 |
- const_char_pointer (*real_pam_getenv)(pam_handle_t *, const char *); |
|
| 115 |
- RESOLVE_PAM_FUNCTION(pam_getenv, const_char_pointer, |
|
| 116 |
- (pam_handle_t *, const char *), NULL); |
|
| 117 |
- return real_pam_getenv(pamh, name); |
|
| 118 |
-} |
|
| 119 |
- |
|
| 120 |
-typedef char ** char_ppointer; |
|
| 121 |
-char_ppointer pam_getenvlist(pam_handle_t *pamh) |
|
| 122 |
-{
|
|
| 123 |
- char_ppointer (*real_pam_getenvlist)(pam_handle_t *); |
|
| 124 |
- RESOLVE_PAM_FUNCTION(pam_getenvlist, char_ppointer, (pam_handle_t *), |
|
| 125 |
- NULL); |
|
| 126 |
- return real_pam_getenvlist(pamh); |
|
| 127 |
-} |
|
| 128 |
- |
|
| 129 |
-/* Authentication management */ |
|
| 130 |
- |
|
| 131 |
-int pam_authenticate(pam_handle_t *pamh, int flags) |
|
| 132 |
-{
|
|
| 133 |
- int (*real_pam_authenticate)(pam_handle_t *, int); |
|
| 134 |
- RESOLVE_PAM_FUNCTION(pam_authenticate, int, (pam_handle_t *, int), |
|
| 135 |
- PAM_ABORT); |
|
| 136 |
- return real_pam_authenticate(pamh, flags); |
|
| 137 |
-} |
|
| 138 |
- |
|
| 139 |
-int pam_setcred(pam_handle_t *pamh, int flags) |
|
| 140 |
-{
|
|
| 141 |
- int (*real_pam_setcred)(pam_handle_t *, int); |
|
| 142 |
- RESOLVE_PAM_FUNCTION(pam_setcred, int, (pam_handle_t *, int), PAM_ABORT); |
|
| 143 |
- return real_pam_setcred(pamh, flags); |
|
| 144 |
-} |
|
| 145 |
- |
|
| 146 |
-/* Account Management API's */ |
|
| 147 |
- |
|
| 148 |
-int pam_acct_mgmt(pam_handle_t *pamh, int flags) |
|
| 149 |
-{
|
|
| 150 |
- int (*real_pam_acct_mgmt)(pam_handle_t *, int); |
|
| 151 |
- RESOLVE_PAM_FUNCTION(pam_acct_mgmt, int, (pam_handle_t *, int), PAM_ABORT); |
|
| 152 |
- return real_pam_acct_mgmt(pamh, flags); |
|
| 153 |
-} |
|
| 154 |
- |
|
| 155 |
-/* Session Management API's */ |
|
| 156 |
- |
|
| 157 |
-int pam_open_session(pam_handle_t *pamh, int flags) |
|
| 158 |
-{
|
|
| 159 |
- int (*real_pam_open_session)(pam_handle_t *, int); |
|
| 160 |
- RESOLVE_PAM_FUNCTION(pam_open_session, int, (pam_handle_t *, int), |
|
| 161 |
- PAM_ABORT); |
|
| 162 |
- return real_pam_open_session(pamh, flags); |
|
| 163 |
-} |
|
| 164 |
- |
|
| 165 |
-int pam_close_session(pam_handle_t *pamh, int flags) |
|
| 166 |
-{
|
|
| 167 |
- int (*real_pam_close_session)(pam_handle_t *, int); |
|
| 168 |
- RESOLVE_PAM_FUNCTION(pam_close_session, int, (pam_handle_t *, int), |
|
| 169 |
- PAM_ABORT); |
|
| 170 |
- return real_pam_close_session(pamh, flags); |
|
| 171 |
-} |
|
| 172 |
- |
|
| 173 |
-/* Password Management API's */ |
|
| 174 |
- |
|
| 175 |
-int pam_chauthtok(pam_handle_t *pamh, int flags) |
|
| 176 |
-{
|
|
| 177 |
- int (*real_pam_chauthtok)(pam_handle_t *, int); |
|
| 178 |
- RESOLVE_PAM_FUNCTION(pam_chauthtok, int, (pam_handle_t *, int), PAM_ABORT); |
|
| 179 |
- return real_pam_chauthtok(pamh, flags); |
|
| 180 |
-} |
|
| 181 |
-#endif |
| 8 | 1 |
deleted file mode 100755 |
| ... | ... |
@@ -1,17 +0,0 @@ |
| 1 |
-# |
|
| 2 |
-# Build the OpenVPN down-root plugin module. |
|
| 3 |
-# |
|
| 4 |
- |
|
| 5 |
-# This directory is where we will look for openvpn-plugin.h |
|
| 6 |
-INCLUDE=-I../.. |
|
| 7 |
- |
|
| 8 |
-CC_FLAGS=-O2 -Wall |
|
| 9 |
- |
|
| 10 |
-down-root.so : down-root.o |
|
| 11 |
- gcc ${CC_FLAGS} -fPIC -shared -Wl,-soname,openvpn-down-root.so -o openvpn-down-root.so down-root.o -lc
|
|
| 12 |
- |
|
| 13 |
-down-root.o : down-root.c |
|
| 14 |
- gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} down-root.c
|
|
| 15 |
- |
|
| 16 |
-clean : |
|
| 17 |
- rm -f *.o *.so |
| 18 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,29 +0,0 @@ |
| 1 |
-down-root -- an OpenVPN Plugin Module |
|
| 2 |
- |
|
| 3 |
-SYNOPSIS |
|
| 4 |
- |
|
| 5 |
-The down-root module allows an OpenVPN configuration to |
|
| 6 |
-call a down script with root privileges, even when privileges |
|
| 7 |
-have been dropped using --user/--group/--chroot. |
|
| 8 |
- |
|
| 9 |
-This module uses a split privilege execution model which will |
|
| 10 |
-fork() before OpenVPN drops root privileges, at the point where |
|
| 11 |
-the --up script is usually called. The module will then remain |
|
| 12 |
-in a wait state until it receives a message from OpenVPN via |
|
| 13 |
-pipe to execute the down script. Thus, the down script will be |
|
| 14 |
-run in the same execution environment as the up script. |
|
| 15 |
- |
|
| 16 |
-BUILD |
|
| 17 |
- |
|
| 18 |
-Build this module with the "make" command. The plugin |
|
| 19 |
-module will be named openvpn-down-root.so |
|
| 20 |
- |
|
| 21 |
-USAGE |
|
| 22 |
- |
|
| 23 |
-To use this module, add to your OpenVPN config file: |
|
| 24 |
- |
|
| 25 |
- plugin openvpn-down-root.so "command ..." |
|
| 26 |
- |
|
| 27 |
-CAVEATS |
|
| 28 |
- |
|
| 29 |
-This module will only work on *nix systems, not Windows. |
| 30 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,551 +0,0 @@ |
| 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-2005 OpenVPN Solutions LLC <info@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 |
- * OpenVPN plugin module to do privileged down-script execution. |
|
| 27 |
- */ |
|
| 28 |
- |
|
| 29 |
-#include <stdio.h> |
|
| 30 |
-#include <string.h> |
|
| 31 |
-#include <unistd.h> |
|
| 32 |
-#include <stdlib.h> |
|
| 33 |
-#include <sys/types.h> |
|
| 34 |
-#include <sys/socket.h> |
|
| 35 |
-#include <sys/wait.h> |
|
| 36 |
-#include <fcntl.h> |
|
| 37 |
-#include <signal.h> |
|
| 38 |
-#include <syslog.h> |
|
| 39 |
- |
|
| 40 |
-#include "openvpn-plugin.h" |
|
| 41 |
- |
|
| 42 |
-#define DEBUG(verb) ((verb) >= 7) |
|
| 43 |
- |
|
| 44 |
-/* Command codes for foreground -> background communication */ |
|
| 45 |
-#define COMMAND_RUN_SCRIPT 0 |
|
| 46 |
-#define COMMAND_EXIT 1 |
|
| 47 |
- |
|
| 48 |
-/* Response codes for background -> foreground communication */ |
|
| 49 |
-#define RESPONSE_INIT_SUCCEEDED 10 |
|
| 50 |
-#define RESPONSE_INIT_FAILED 11 |
|
| 51 |
-#define RESPONSE_SCRIPT_SUCCEEDED 12 |
|
| 52 |
-#define RESPONSE_SCRIPT_FAILED 13 |
|
| 53 |
- |
|
| 54 |
-/* Background process function */ |
|
| 55 |
-static void down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb); |
|
| 56 |
- |
|
| 57 |
-/* |
|
| 58 |
- * Plugin state, used by foreground |
|
| 59 |
- */ |
|
| 60 |
-struct down_root_context |
|
| 61 |
-{
|
|
| 62 |
- /* Foreground's socket to background process */ |
|
| 63 |
- int foreground_fd; |
|
| 64 |
- |
|
| 65 |
- /* Process ID of background process */ |
|
| 66 |
- pid_t background_pid; |
|
| 67 |
- |
|
| 68 |
- /* Verbosity level of OpenVPN */ |
|
| 69 |
- int verb; |
|
| 70 |
- |
|
| 71 |
- /* down command */ |
|
| 72 |
- char *command; |
|
| 73 |
-}; |
|
| 74 |
- |
|
| 75 |
-/* |
|
| 76 |
- * Given an environmental variable name, search |
|
| 77 |
- * the envp array for its value, returning it |
|
| 78 |
- * if found or NULL otherwise. |
|
| 79 |
- */ |
|
| 80 |
-static const char * |
|
| 81 |
-get_env (const char *name, const char *envp[]) |
|
| 82 |
-{
|
|
| 83 |
- if (envp) |
|
| 84 |
- {
|
|
| 85 |
- int i; |
|
| 86 |
- const int namelen = strlen (name); |
|
| 87 |
- for (i = 0; envp[i]; ++i) |
|
| 88 |
- {
|
|
| 89 |
- if (!strncmp (envp[i], name, namelen)) |
|
| 90 |
- {
|
|
| 91 |
- const char *cp = envp[i] + namelen; |
|
| 92 |
- if (*cp == '=') |
|
| 93 |
- return cp + 1; |
|
| 94 |
- } |
|
| 95 |
- } |
|
| 96 |
- } |
|
| 97 |
- return NULL; |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-/* |
|
| 101 |
- * Return the length of a string array |
|
| 102 |
- */ |
|
| 103 |
-static int |
|
| 104 |
-string_array_len (const char *array[]) |
|
| 105 |
-{
|
|
| 106 |
- int i = 0; |
|
| 107 |
- if (array) |
|
| 108 |
- {
|
|
| 109 |
- while (array[i]) |
|
| 110 |
- ++i; |
|
| 111 |
- } |
|
| 112 |
- return i; |
|
| 113 |
-} |
|
| 114 |
- |
|
| 115 |
-/* |
|
| 116 |
- * Socket read/write functions. |
|
| 117 |
- */ |
|
| 118 |
- |
|
| 119 |
-static int |
|
| 120 |
-recv_control (int fd) |
|
| 121 |
-{
|
|
| 122 |
- unsigned char c; |
|
| 123 |
- const ssize_t size = read (fd, &c, sizeof (c)); |
|
| 124 |
- if (size == sizeof (c)) |
|
| 125 |
- return c; |
|
| 126 |
- else |
|
| 127 |
- return -1; |
|
| 128 |
-} |
|
| 129 |
- |
|
| 130 |
-static int |
|
| 131 |
-send_control (int fd, int code) |
|
| 132 |
-{
|
|
| 133 |
- unsigned char c = (unsigned char) code; |
|
| 134 |
- const ssize_t size = write (fd, &c, sizeof (c)); |
|
| 135 |
- if (size == sizeof (c)) |
|
| 136 |
- return (int) size; |
|
| 137 |
- else |
|
| 138 |
- return -1; |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-/* |
|
| 142 |
- * Daemonize if "daemon" env var is true. |
|
| 143 |
- * Preserve stderr across daemonization if |
|
| 144 |
- * "daemon_log_redirect" env var is true. |
|
| 145 |
- */ |
|
| 146 |
-static void |
|
| 147 |
-daemonize (const char *envp[]) |
|
| 148 |
-{
|
|
| 149 |
- const char *daemon_string = get_env ("daemon", envp);
|
|
| 150 |
- if (daemon_string && daemon_string[0] == '1') |
|
| 151 |
- {
|
|
| 152 |
- const char *log_redirect = get_env ("daemon_log_redirect", envp);
|
|
| 153 |
- int fd = -1; |
|
| 154 |
- if (log_redirect && log_redirect[0] == '1') |
|
| 155 |
- fd = dup (2); |
|
| 156 |
- if (daemon (0, 0) < 0) |
|
| 157 |
- {
|
|
| 158 |
- fprintf (stderr, "DOWN-ROOT: daemonization failed\n"); |
|
| 159 |
- } |
|
| 160 |
- else if (fd >= 3) |
|
| 161 |
- {
|
|
| 162 |
- dup2 (fd, 2); |
|
| 163 |
- close (fd); |
|
| 164 |
- } |
|
| 165 |
- } |
|
| 166 |
-} |
|
| 167 |
- |
|
| 168 |
-/* |
|
| 169 |
- * Close most of parent's fds. |
|
| 170 |
- * Keep stdin/stdout/stderr, plus one |
|
| 171 |
- * other fd which is presumed to be |
|
| 172 |
- * our pipe back to parent. |
|
| 173 |
- * Admittedly, a bit of a kludge, |
|
| 174 |
- * but posix doesn't give us a kind |
|
| 175 |
- * of FD_CLOEXEC which will stop |
|
| 176 |
- * fds from crossing a fork(). |
|
| 177 |
- */ |
|
| 178 |
-static void |
|
| 179 |
-close_fds_except (int keep) |
|
| 180 |
-{
|
|
| 181 |
- int i; |
|
| 182 |
- closelog (); |
|
| 183 |
- for (i = 3; i <= 100; ++i) |
|
| 184 |
- {
|
|
| 185 |
- if (i != keep) |
|
| 186 |
- close (i); |
|
| 187 |
- } |
|
| 188 |
-} |
|
| 189 |
- |
|
| 190 |
-/* |
|
| 191 |
- * Usually we ignore signals, because our parent will |
|
| 192 |
- * deal with them. |
|
| 193 |
- */ |
|
| 194 |
-static void |
|
| 195 |
-set_signals (void) |
|
| 196 |
-{
|
|
| 197 |
- signal (SIGTERM, SIG_DFL); |
|
| 198 |
- |
|
| 199 |
- signal (SIGINT, SIG_IGN); |
|
| 200 |
- signal (SIGHUP, SIG_IGN); |
|
| 201 |
- signal (SIGUSR1, SIG_IGN); |
|
| 202 |
- signal (SIGUSR2, SIG_IGN); |
|
| 203 |
- signal (SIGPIPE, SIG_IGN); |
|
| 204 |
-} |
|
| 205 |
- |
|
| 206 |
-/* |
|
| 207 |
- * convert system() return into a success/failure value |
|
| 208 |
- */ |
|
| 209 |
-int |
|
| 210 |
-system_ok (int stat) |
|
| 211 |
-{
|
|
| 212 |
-#ifdef WIN32 |
|
| 213 |
- return stat == 0; |
|
| 214 |
-#else |
|
| 215 |
- return stat != -1 && WIFEXITED (stat) && WEXITSTATUS (stat) == 0; |
|
| 216 |
-#endif |
|
| 217 |
-} |
|
| 218 |
- |
|
| 219 |
-static char * |
|
| 220 |
-build_command_line (const char *argv[]) |
|
| 221 |
-{
|
|
| 222 |
- int size = 0; |
|
| 223 |
- int n = 0; |
|
| 224 |
- int i; |
|
| 225 |
- char *string; |
|
| 226 |
- |
|
| 227 |
- /* precompute size */ |
|
| 228 |
- if (argv) |
|
| 229 |
- {
|
|
| 230 |
- for (i = 0; argv[i]; ++i) |
|
| 231 |
- {
|
|
| 232 |
- size += (strlen (argv[i]) + 1); /* string length plus trailing space */ |
|
| 233 |
- ++n; |
|
| 234 |
- } |
|
| 235 |
- } |
|
| 236 |
- ++size; /* for null terminator */ |
|
| 237 |
- |
|
| 238 |
- /* allocate memory */ |
|
| 239 |
- string = (char *) malloc (size); |
|
| 240 |
- if (!string) |
|
| 241 |
- {
|
|
| 242 |
- fprintf (stderr, "DOWN-ROOT: out of memory\n"); |
|
| 243 |
- exit (1); |
|
| 244 |
- } |
|
| 245 |
- string[0] = '\0'; |
|
| 246 |
- |
|
| 247 |
- /* build string */ |
|
| 248 |
- for (i = 0; i < n; ++i) |
|
| 249 |
- {
|
|
| 250 |
- strcat (string, argv[i]); |
|
| 251 |
- if (i + 1 < n) |
|
| 252 |
- strcat (string, " "); |
|
| 253 |
- } |
|
| 254 |
- return string; |
|
| 255 |
-} |
|
| 256 |
- |
|
| 257 |
-static void |
|
| 258 |
-free_context (struct down_root_context *context) |
|
| 259 |
-{
|
|
| 260 |
- if (context) |
|
| 261 |
- {
|
|
| 262 |
- if (context->command) |
|
| 263 |
- free (context->command); |
|
| 264 |
- free (context); |
|
| 265 |
- } |
|
| 266 |
-} |
|
| 267 |
- |
|
| 268 |
-OPENVPN_EXPORT openvpn_plugin_handle_t |
|
| 269 |
-openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) |
|
| 270 |
-{
|
|
| 271 |
- struct down_root_context *context; |
|
| 272 |
- |
|
| 273 |
- /* |
|
| 274 |
- * Allocate our context |
|
| 275 |
- */ |
|
| 276 |
- context = (struct down_root_context *) calloc (1, sizeof (struct down_root_context)); |
|
| 277 |
- context->foreground_fd = -1; |
|
| 278 |
- |
|
| 279 |
- /* |
|
| 280 |
- * Intercept the --up and --down callbacks |
|
| 281 |
- */ |
|
| 282 |
- *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) | OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN); |
|
| 283 |
- |
|
| 284 |
- /* |
|
| 285 |
- * Make sure we have two string arguments: the first is the .so name, |
|
| 286 |
- * the second is the script command. |
|
| 287 |
- */ |
|
| 288 |
- if (string_array_len (argv) < 2) |
|
| 289 |
- {
|
|
| 290 |
- fprintf (stderr, "DOWN-ROOT: need down script command\n"); |
|
| 291 |
- goto error; |
|
| 292 |
- } |
|
| 293 |
- |
|
| 294 |
- /* |
|
| 295 |
- * Save our argument in context |
|
| 296 |
- */ |
|
| 297 |
- context->command = build_command_line (&argv[1]); |
|
| 298 |
- |
|
| 299 |
- /* |
|
| 300 |
- * Get verbosity level from environment |
|
| 301 |
- */ |
|
| 302 |
- {
|
|
| 303 |
- const char *verb_string = get_env ("verb", envp);
|
|
| 304 |
- if (verb_string) |
|
| 305 |
- context->verb = atoi (verb_string); |
|
| 306 |
- } |
|
| 307 |
- |
|
| 308 |
- return (openvpn_plugin_handle_t) context; |
|
| 309 |
- |
|
| 310 |
- error: |
|
| 311 |
- free_context (context); |
|
| 312 |
- return NULL; |
|
| 313 |
-} |
|
| 314 |
- |
|
| 315 |
-OPENVPN_EXPORT int |
|
| 316 |
-openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) |
|
| 317 |
-{
|
|
| 318 |
- struct down_root_context *context = (struct down_root_context *) handle; |
|
| 319 |
- |
|
| 320 |
- if (type == OPENVPN_PLUGIN_UP && context->foreground_fd == -1) /* fork off a process to hold onto root */ |
|
| 321 |
- {
|
|
| 322 |
- pid_t pid; |
|
| 323 |
- int fd[2]; |
|
| 324 |
- |
|
| 325 |
- /* |
|
| 326 |
- * Make a socket for foreground and background processes |
|
| 327 |
- * to communicate. |
|
| 328 |
- */ |
|
| 329 |
- if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1) |
|
| 330 |
- {
|
|
| 331 |
- fprintf (stderr, "DOWN-ROOT: socketpair call failed\n"); |
|
| 332 |
- return OPENVPN_PLUGIN_FUNC_ERROR; |
|
| 333 |
- } |
|
| 334 |
- |
|
| 335 |
- /* |
|
| 336 |
- * Fork off the privileged process. It will remain privileged |
|
| 337 |
- * even after the foreground process drops its privileges. |
|
| 338 |
- */ |
|
| 339 |
- pid = fork (); |
|
| 340 |
- |
|
| 341 |
- if (pid) |
|
| 342 |
- {
|
|
| 343 |
- int status; |
|
| 344 |
- |
|
| 345 |
- /* |
|
| 346 |
- * Foreground Process |
|
| 347 |
- */ |
|
| 348 |
- |
|
| 349 |
- context->background_pid = pid; |
|
| 350 |
- |
|
| 351 |
- /* close our copy of child's socket */ |
|
| 352 |
- close (fd[1]); |
|
| 353 |
- |
|
| 354 |
- /* don't let future subprocesses inherit child socket */ |
|
| 355 |
- if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0) |
|
| 356 |
- fprintf (stderr, "DOWN-ROOT: Set FD_CLOEXEC flag on socket file descriptor failed\n"); |
|
| 357 |
- |
|
| 358 |
- /* wait for background child process to initialize */ |
|
| 359 |
- status = recv_control (fd[0]); |
|
| 360 |
- if (status == RESPONSE_INIT_SUCCEEDED) |
|
| 361 |
- {
|
|
| 362 |
- context->foreground_fd = fd[0]; |
|
| 363 |
- return OPENVPN_PLUGIN_FUNC_SUCCESS; |
|
| 364 |
- } |
|
| 365 |
- } |
|
| 366 |
- else |
|
| 367 |
- {
|
|
| 368 |
- /* |
|
| 369 |
- * Background Process |
|
| 370 |
- */ |
|
| 371 |
- |
|
| 372 |
- /* close all parent fds except our socket back to parent */ |
|
| 373 |
- close_fds_except (fd[1]); |
|
| 374 |
- |
|
| 375 |
- /* Ignore most signals (the parent will receive them) */ |
|
| 376 |
- set_signals (); |
|
| 377 |
- |
|
| 378 |
- /* Daemonize if --daemon option is set. */ |
|
| 379 |
- daemonize (envp); |
|
| 380 |
- |
|
| 381 |
- /* execute the event loop */ |
|
| 382 |
- down_root_server (fd[1], context->command, argv, envp, context->verb); |
|
| 383 |
- |
|
| 384 |
- close (fd[1]); |
|
| 385 |
- exit (0); |
|
| 386 |
- return 0; /* NOTREACHED */ |
|
| 387 |
- } |
|
| 388 |
- } |
|
| 389 |
- else if (type == OPENVPN_PLUGIN_DOWN && context->foreground_fd >= 0) |
|
| 390 |
- {
|
|
| 391 |
- if (send_control (context->foreground_fd, COMMAND_RUN_SCRIPT) == -1) |
|
| 392 |
- {
|
|
| 393 |
- fprintf (stderr, "DOWN-ROOT: Error sending script execution signal to background process\n"); |
|
| 394 |
- } |
|
| 395 |
- else |
|
| 396 |
- {
|
|
| 397 |
- const int status = recv_control (context->foreground_fd); |
|
| 398 |
- if (status == RESPONSE_SCRIPT_SUCCEEDED) |
|
| 399 |
- return OPENVPN_PLUGIN_FUNC_SUCCESS; |
|
| 400 |
- if (status == -1) |
|
| 401 |
- fprintf (stderr, "DOWN-ROOT: Error receiving script execution confirmation from background process\n"); |
|
| 402 |
- } |
|
| 403 |
- } |
|
| 404 |
- return OPENVPN_PLUGIN_FUNC_ERROR; |
|
| 405 |
-} |
|
| 406 |
- |
|
| 407 |
-OPENVPN_EXPORT void |
|
| 408 |
-openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) |
|
| 409 |
-{
|
|
| 410 |
- struct down_root_context *context = (struct down_root_context *) handle; |
|
| 411 |
- |
|
| 412 |
- if (DEBUG (context->verb)) |
|
| 413 |
- fprintf (stderr, "DOWN-ROOT: close\n"); |
|
| 414 |
- |
|
| 415 |
- if (context->foreground_fd >= 0) |
|
| 416 |
- {
|
|
| 417 |
- /* tell background process to exit */ |
|
| 418 |
- if (send_control (context->foreground_fd, COMMAND_EXIT) == -1) |
|
| 419 |
- fprintf (stderr, "DOWN-ROOT: Error signaling background process to exit\n"); |
|
| 420 |
- |
|
| 421 |
- /* wait for background process to exit */ |
|
| 422 |
- if (context->background_pid > 0) |
|
| 423 |
- waitpid (context->background_pid, NULL, 0); |
|
| 424 |
- |
|
| 425 |
- close (context->foreground_fd); |
|
| 426 |
- context->foreground_fd = -1; |
|
| 427 |
- } |
|
| 428 |
- |
|
| 429 |
- free_context (context); |
|
| 430 |
-} |
|
| 431 |
- |
|
| 432 |
-OPENVPN_EXPORT void |
|
| 433 |
-openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle) |
|
| 434 |
-{
|
|
| 435 |
- struct down_root_context *context = (struct down_root_context *) handle; |
|
| 436 |
- |
|
| 437 |
- if (context->foreground_fd >= 0) |
|
| 438 |
- {
|
|
| 439 |
- /* tell background process to exit */ |
|
| 440 |
- send_control (context->foreground_fd, COMMAND_EXIT); |
|
| 441 |
- close (context->foreground_fd); |
|
| 442 |
- context->foreground_fd = -1; |
|
| 443 |
- } |
|
| 444 |
-} |
|
| 445 |
- |
|
| 446 |
-/* |
|
| 447 |
- * Background process -- runs with privilege. |
|
| 448 |
- */ |
|
| 449 |
-static void |
|
| 450 |
-down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb) |
|
| 451 |
-{
|
|
| 452 |
- const char *p[3]; |
|
| 453 |
- char *command_line = NULL; |
|
| 454 |
- char *argv_cat = NULL; |
|
| 455 |
- int i; |
|
| 456 |
- |
|
| 457 |
- /* |
|
| 458 |
- * Do initialization |
|
| 459 |
- */ |
|
| 460 |
- if (DEBUG (verb)) |
|
| 461 |
- fprintf (stderr, "DOWN-ROOT: BACKGROUND: INIT command='%s'\n", command); |
|
| 462 |
- |
|
| 463 |
- /* |
|
| 464 |
- * Tell foreground that we initialized successfully |
|
| 465 |
- */ |
|
| 466 |
- if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1) |
|
| 467 |
- {
|
|
| 468 |
- fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [1]\n"); |
|
| 469 |
- goto done; |
|
| 470 |
- } |
|
| 471 |
- |
|
| 472 |
- /* |
|
| 473 |
- * Build command line |
|
| 474 |
- */ |
|
| 475 |
- if (string_array_len (argv) >= 2) |
|
| 476 |
- argv_cat = build_command_line (&argv[1]); |
|
| 477 |
- else |
|
| 478 |
- argv_cat = build_command_line (NULL); |
|
| 479 |
- p[0] = command; |
|
| 480 |
- p[1] = argv_cat; |
|
| 481 |
- p[2] = NULL; |
|
| 482 |
- command_line = build_command_line (p); |
|
| 483 |
- |
|
| 484 |
- /* |
|
| 485 |
- * Save envp in environment |
|
| 486 |
- */ |
|
| 487 |
- for (i = 0; envp[i]; ++i) |
|
| 488 |
- {
|
|
| 489 |
- putenv ((char *)envp[i]); |
|
| 490 |
- } |
|
| 491 |
- |
|
| 492 |
- /* |
|
| 493 |
- * Event loop |
|
| 494 |
- */ |
|
| 495 |
- while (1) |
|
| 496 |
- {
|
|
| 497 |
- int command_code; |
|
| 498 |
- int status; |
|
| 499 |
- |
|
| 500 |
- /* get a command from foreground process */ |
|
| 501 |
- command_code = recv_control (fd); |
|
| 502 |
- |
|
| 503 |
- if (DEBUG (verb)) |
|
| 504 |
- fprintf (stderr, "DOWN-ROOT: BACKGROUND: received command code: %d\n", command_code); |
|
| 505 |
- |
|
| 506 |
- switch (command_code) |
|
| 507 |
- {
|
|
| 508 |
- case COMMAND_RUN_SCRIPT: |
|
| 509 |
- status = system (command_line); |
|
| 510 |
- if (system_ok (status)) /* Succeeded */ |
|
| 511 |
- {
|
|
| 512 |
- if (send_control (fd, RESPONSE_SCRIPT_SUCCEEDED) == -1) |
|
| 513 |
- {
|
|
| 514 |
- fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [2]\n"); |
|
| 515 |
- goto done; |
|
| 516 |
- } |
|
| 517 |
- } |
|
| 518 |
- else /* Failed */ |
|
| 519 |
- {
|
|
| 520 |
- if (send_control (fd, RESPONSE_SCRIPT_FAILED) == -1) |
|
| 521 |
- {
|
|
| 522 |
- fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [3]\n"); |
|
| 523 |
- goto done; |
|
| 524 |
- } |
|
| 525 |
- } |
|
| 526 |
- break; |
|
| 527 |
- |
|
| 528 |
- case COMMAND_EXIT: |
|
| 529 |
- goto done; |
|
| 530 |
- |
|
| 531 |
- case -1: |
|
| 532 |
- fprintf (stderr, "DOWN-ROOT: BACKGROUND: read error on command channel\n"); |
|
| 533 |
- goto done; |
|
| 534 |
- |
|
| 535 |
- default: |
|
| 536 |
- fprintf (stderr, "DOWN-ROOT: BACKGROUND: unknown command code: code=%d, exiting\n", |
|
| 537 |
- command_code); |
|
| 538 |
- goto done; |
|
| 539 |
- } |
|
| 540 |
- } |
|
| 541 |
- |
|
| 542 |
- done: |
|
| 543 |
- if (argv_cat) |
|
| 544 |
- free (argv_cat); |
|
| 545 |
- if (command_line) |
|
| 546 |
- free (command_line); |
|
| 547 |
- if (DEBUG (verb)) |
|
| 548 |
- fprintf (stderr, "DOWN-ROOT: BACKGROUND: EXIT\n"); |
|
| 549 |
- |
|
| 550 |
- return; |
|
| 551 |
-} |
| 552 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,16 +0,0 @@ |
| 1 |
-OpenVPN plugin examples. |
|
| 2 |
- |
|
| 3 |
-Examples provided: |
|
| 4 |
- |
|
| 5 |
-simple.c -- using the --auth-user-pass-verify callback, verify |
|
| 6 |
- that the username/password is "foo"/"bar". |
|
| 7 |
- |
|
| 8 |
-To build: |
|
| 9 |
- |
|
| 10 |
- ./build simple (Linux/BSD/etc.) |
|
| 11 |
- ./winbuild simple (MinGW on Windows) |
|
| 12 |
- |
|
| 13 |
-To use in OpenVPN, add to config file: |
|
| 14 |
- |
|
| 15 |
- plugin simple.so (Linux/BSD/etc.) |
|
| 16 |
- plugin simple.dll (MinGW on Windows) |
| 17 | 1 |
deleted file mode 100755 |
| ... | ... |
@@ -1,14 +0,0 @@ |
| 1 |
-#!/bin/sh |
|
| 2 |
- |
|
| 3 |
-# |
|
| 4 |
-# Build an OpenVPN plugin module on *nix. The argument should |
|
| 5 |
-# be the base name of the C source file (without the .c). |
|
| 6 |
-# |
|
| 7 |
- |
|
| 8 |
-# This directory is where we will look for openvpn-plugin.h |
|
| 9 |
-INCLUDE="-I../.." |
|
| 10 |
- |
|
| 11 |
-CC_FLAGS="-O2 -Wall" |
|
| 12 |
- |
|
| 13 |
-gcc $CC_FLAGS -fPIC -c $INCLUDE $1.c && \ |
|
| 14 |
-gcc -fPIC -shared -Wl,-soname,$1.so -o $1.so $1.o -lc |
| 15 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,120 +0,0 @@ |
| 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-2005 OpenVPN Solutions LLC <info@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 examine the username/password provided by a client, |
|
| 28 |
- * and make an accept/deny determination. Will run |
|
| 29 |
- * on Windows or *nix. |
|
| 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 openvpn_plugin_handle_t |
|
| 74 |
-openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) |
|
| 75 |
-{
|
|
| 76 |
- struct plugin_context *context; |
|
| 77 |
- |
|
| 78 |
- /* |
|
| 79 |
- * Allocate our context |
|
| 80 |
- */ |
|
| 81 |
- context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context)); |
|
| 82 |
- |
|
| 83 |
- /* |
|
| 84 |
- * Set the username/password we will require. |
|
| 85 |
- */ |
|
| 86 |
- context->username = "foo"; |
|
| 87 |
- context->password = "bar"; |
|
| 88 |
- |
|
| 89 |
- /* |
|
| 90 |
- * We are only interested in intercepting the |
|
| 91 |
- * --auth-user-pass-verify callback. |
|
| 92 |
- */ |
|
| 93 |
- *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY); |
|
| 94 |
- |
|
| 95 |
- return (openvpn_plugin_handle_t) context; |
|
| 96 |
-} |
|
| 97 |
- |
|
| 98 |
-OPENVPN_EXPORT int |
|
| 99 |
-openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) |
|
| 100 |
-{
|
|
| 101 |
- struct plugin_context *context = (struct plugin_context *) handle; |
|
| 102 |
- |
|
| 103 |
- /* get username/password from envp string array */ |
|
| 104 |
- const char *username = get_env ("username", envp);
|
|
| 105 |
- const char *password = get_env ("password", envp);
|
|
| 106 |
- |
|
| 107 |
- /* check entered username/password against what we require */ |
|
| 108 |
- if (username && !strcmp (username, context->username) |
|
| 109 |
- && password && !strcmp (password, context->password)) |
|
| 110 |
- return OPENVPN_PLUGIN_FUNC_SUCCESS; |
|
| 111 |
- else |
|
| 112 |
- return OPENVPN_PLUGIN_FUNC_ERROR; |
|
| 113 |
-} |
|
| 114 |
- |
|
| 115 |
-OPENVPN_EXPORT void |
|
| 116 |
-openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) |
|
| 117 |
-{
|
|
| 118 |
- struct plugin_context *context = (struct plugin_context *) handle; |
|
| 119 |
- free (context); |
|
| 120 |
-} |
| 7 | 1 |
deleted file mode 100755 |
| ... | ... |
@@ -1,18 +0,0 @@ |
| 1 |
-# |
|
| 2 |
-# Build an OpenVPN plugin module on Windows/MinGW. |
|
| 3 |
-# The argument should be the base name of the C source file |
|
| 4 |
-# (without the .c). |
|
| 5 |
-# |
|
| 6 |
- |
|
| 7 |
-# This directory is where we will look for openvpn-plugin.h |
|
| 8 |
-INCLUDE="-I.." |
|
| 9 |
- |
|
| 10 |
-CC_FLAGS="-O2 -Wall" |
|
| 11 |
- |
|
| 12 |
-gcc -DBUILD_DLL $CC_FLAGS $INCLUDE -c $1.c |
|
| 13 |
-gcc --disable-stdcall-fixup -mdll -DBUILD_DLL -o junk.tmp -Wl,--base-file,base.tmp $1.o |
|
| 14 |
-rm junk.tmp |
|
| 15 |
-dlltool --dllname $1.dll --base-file base.tmp --output-exp temp.exp --input-def $1.def |
|
| 16 |
-rm base.tmp |
|
| 17 |
-gcc --enable-stdcall-fixup -mdll -DBUILD_DLL -o $1.dll $1.o -Wl,temp.exp |
|
| 18 |
-rm temp.exp |