Browse code

Renamed plugin to plugins to work around strange automake issue. 2.1_beta2

git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@603 e7ae566f-a301-0410-adde-c780ea21d3b5

james authored on 2005/10/13 13:08:20
Showing 34 changed files
... ...
@@ -3,6 +3,15 @@ Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net>
3 3
 
4 4
 $Id$
5 5
 
6
+2005.10.xx -- Version 2.1-beta3
7
+
8
+* NOTE TO PACKAGE MAINTAINERS: Moved "plugin"
9
+  directory to "plugins".  This is
10
+  to work around a strange problem with the
11
+  "make dist" target in the automake-generated
12
+  makefile, where the target tries to do a
13
+  rather bogus "gcc -g -O2 -I. plugin.c -o plugin".
14
+
6 15
 2005.10.13 -- Version 2.1-beta2
7 16
 
8 17
 * Added --socket-flags directive with TCP_NODELAY
... ...
@@ -60,6 +60,18 @@ OPTIONAL (for developers only):
60 60
 
61 61
 *************************************************************************
62 62
 
63
+CHECK OUT SOURCE FROM SUBVERSION REPOSITORY:
64
+
65
+  Check out stable version:
66
+
67
+    svn checkout http://svn.openvpn.net/projects/openvpn/trunk/openvpn openvpn
68
+
69
+  Check out beta21 branch:
70
+
71
+    svn checkout http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn openvpn
72
+
73
+*************************************************************************
74
+
63 75
 BUILD COMMANDS FROM TARBALL:
64 76
 
65 77
 	./configure
... ...
@@ -68,7 +80,7 @@ BUILD COMMANDS FROM TARBALL:
68 68
 
69 69
 *************************************************************************
70 70
 
71
-BUILD COMMANDS FROM CVS:
71
+BUILD COMMANDS FROM SUBVERSION REPOSITORY CHECKOUT:
72 72
 
73 73
 	autoreconf -i -v
74 74
 	./configure
... ...
@@ -77,7 +89,7 @@ BUILD COMMANDS FROM CVS:
77 77
 
78 78
 *************************************************************************
79 79
 
80
-BUILD A TARBALL FROM CVS:
80
+BUILD A TARBALL FROM SUBVERSION REPOSITORY CHECKOUT:
81 81
 
82 82
 	autoreconf -i -v
83 83
 	./configure
... ...
@@ -121,7 +121,7 @@ EXTRA_DIST = \
121 121
 	service-win32 \
122 122
 	contrib \
123 123
 	debug \
124
-	plugin \
124
+	plugins \
125 125
         management
126 126
 
127 127
 dist-hook:
... ...
@@ -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 plugin/down-root
104
+pushd plugins/down-root
105 105
 %__make
106 106
 popd
107 107
 
108 108
 # Build auth-pam plugin
109 109
 %if %{build_auth_pam}
110
-pushd plugin/auth-pam
110
+pushd plugins/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}/plugin/lib
154
+%__mkdir_p %{buildroot}%{_datadir}/%{name}/plugins/lib
155 155
 
156 156
 for pi in auth-pam down-root; do
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
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
160 160
   fi
161 161
 done
162 162
 
163
-%__mv -f plugin/README plugin/README.plugins
163
+%__mv -f plugins/README plugins/README.plugins
164 164
 
165 165
 #
166 166
 # Clean section
... ...
@@ -220,10 +220,14 @@ fi
220 220
 %endif
221 221
 
222 222
 # Install extra %doc stuff
223
-%doc contrib/ easy-rsa/ management/ sample-*/ plugin/README.*
223
+%doc contrib/ easy-rsa/ management/ sample-*/ plugins/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
+
227 231
 * Mon Aug 2 2005 James Yonan
228 232
 - Fixed build problem with --define 'without_pam 1'
229 233
 
230 234
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.
45 1
deleted file mode 100644
... ...
@@ -1 +0,0 @@
1
-*.so
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
182 1
deleted file mode 100644
... ...
@@ -1,7 +0,0 @@
1
-#if DLOPEN_PAM
2
-#include <security/pam_appl.h>
3
-
4
-/* Dynamically load and unload the PAM library */
5
-int dlopen_pam (const char *so);
6
-void dlclose_pam (void);
7
-#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
-}
121 1
deleted file mode 100755
... ...
@@ -1,6 +0,0 @@
1
-LIBRARY   OpenVPN_PLUGIN_SAMPLE
2
-DESCRIPTION "Sample OpenVPN plug-in module."
3
-EXPORTS
4
-   openvpn_plugin_open_v1   @1
5
-   openvpn_plugin_func_v1   @2
6
-   openvpn_plugin_close_v1  @3
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
19 1
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 47
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+*.so
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 181
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+#if DLOPEN_PAM
1
+#include <security/pam_appl.h>
2
+
3
+/* Dynamically load and unload the PAM library */
4
+int dlopen_pam (const char *so);
5
+void dlclose_pam (void);
6
+#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 120
new file mode 100755
... ...
@@ -0,0 +1,6 @@
0
+LIBRARY   OpenVPN_PLUGIN_SAMPLE
1
+DESCRIPTION "Sample OpenVPN plug-in module."
2
+EXPORTS
3
+   openvpn_plugin_open_v1   @1
4
+   openvpn_plugin_func_v1   @2
5
+   openvpn_plugin_close_v1  @3
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