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
8
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
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.
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.
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
26
* OpenVPN plugin module to do privileged down-script execution.
33
#include <sys/types.h>
34
#include <sys/socket.h>
40
#include "openvpn-plugin.h"
42
#define DEBUG(verb) ((verb) >= 7)
44
/* Command codes for foreground -> background communication */
45
#define COMMAND_RUN_SCRIPT 0
46
#define COMMAND_EXIT 1
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
54
/* Background process function */
55
static void down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb);
58
* Plugin state, used by foreground
60
struct down_root_context
62
/* Foreground's socket to background process */
65
/* Process ID of background process */
68
/* Verbosity level of OpenVPN */
76
* Given an environmental variable name, search
77
* the envp array for its value, returning it
78
* if found or NULL otherwise.
81
get_env (const char *name, const char *envp[])
86
const int namelen = strlen (name);
87
for (i = 0; envp[i]; ++i)
89
if (!strncmp (envp[i], name, namelen))
91
const char *cp = envp[i] + namelen;
101
* Return the length of a string array
104
string_array_len (const char *array[])
116
* Socket read/write functions.
120
recv_control (int fd)
123
const ssize_t size = read (fd, &c, sizeof (c));
124
if (size == sizeof (c))
131
send_control (int fd, int code)
133
unsigned char c = (unsigned char) code;
134
const ssize_t size = write (fd, &c, sizeof (c));
135
if (size == sizeof (c))
142
* Daemonize if "daemon" env var is true.
143
* Preserve stderr across daemonization if
144
* "daemon_log_redirect" env var is true.
147
daemonize (const char *envp[])
149
const char *daemon_string = get_env ("daemon", envp);
150
if (daemon_string && daemon_string[0] == '1')
152
const char *log_redirect = get_env ("daemon_log_redirect", envp);
154
if (log_redirect && log_redirect[0] == '1')
156
if (daemon (0, 0) < 0)
158
fprintf (stderr, "DOWN-ROOT: daemonization failed\n");
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().
179
close_fds_except (int keep)
183
for (i = 3; i <= 100; ++i)
191
* Usually we ignore signals, because our parent will
197
signal (SIGTERM, SIG_DFL);
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);
207
* convert system() return into a success/failure value
215
return stat != -1 && WIFEXITED (stat) && WEXITSTATUS (stat) == 0;
220
build_command_line (const char *argv[])
227
/* precompute size */
230
for (i = 0; argv[i]; ++i)
232
size += (strlen (argv[i]) + 1); /* string length plus trailing space */
236
++size; /* for null terminator */
238
/* allocate memory */
239
string = (char *) malloc (size);
242
fprintf (stderr, "DOWN-ROOT: out of memory\n");
248
for (i = 0; i < n; ++i)
250
strcat (string, argv[i]);
252
strcat (string, " ");
258
free_context (struct down_root_context *context)
262
if (context->command)
263
free (context->command);
268
OPENVPN_EXPORT openvpn_plugin_handle_t
269
openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
271
struct down_root_context *context;
274
* Allocate our context
276
context = (struct down_root_context *) calloc (1, sizeof (struct down_root_context));
279
context->foreground_fd = -1;
282
* Intercept the --up and --down callbacks
284
*type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) | OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN);
287
* Make sure we have two string arguments: the first is the .so name,
288
* the second is the script command.
290
if (string_array_len (argv) < 2)
292
fprintf (stderr, "DOWN-ROOT: need down script command\n");
297
* Save our argument in context
299
context->command = build_command_line (&argv[1]);
302
* Get verbosity level from environment
305
const char *verb_string = get_env ("verb", envp);
307
context->verb = atoi (verb_string);
310
return (openvpn_plugin_handle_t) context;
313
free_context (context);
318
openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
320
struct down_root_context *context = (struct down_root_context *) handle;
322
if (type == OPENVPN_PLUGIN_UP && context->foreground_fd == -1) /* fork off a process to hold onto root */
328
* Make a socket for foreground and background processes
331
if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1)
333
fprintf (stderr, "DOWN-ROOT: socketpair call failed\n");
334
return OPENVPN_PLUGIN_FUNC_ERROR;
338
* Fork off the privileged process. It will remain privileged
339
* even after the foreground process drops its privileges.
351
context->background_pid = pid;
353
/* close our copy of child's socket */
356
/* don't let future subprocesses inherit child socket */
357
if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0)
358
fprintf (stderr, "DOWN-ROOT: Set FD_CLOEXEC flag on socket file descriptor failed\n");
360
/* wait for background child process to initialize */
361
status = recv_control (fd[0]);
362
if (status == RESPONSE_INIT_SUCCEEDED)
364
context->foreground_fd = fd[0];
365
return OPENVPN_PLUGIN_FUNC_SUCCESS;
374
/* close all parent fds except our socket back to parent */
375
close_fds_except (fd[1]);
377
/* Ignore most signals (the parent will receive them) */
380
/* Daemonize if --daemon option is set. */
383
/* execute the event loop */
384
down_root_server (fd[1], context->command, argv, envp, context->verb);
388
return 0; /* NOTREACHED */
391
else if (type == OPENVPN_PLUGIN_DOWN && context->foreground_fd >= 0)
393
if (send_control (context->foreground_fd, COMMAND_RUN_SCRIPT) == -1)
395
fprintf (stderr, "DOWN-ROOT: Error sending script execution signal to background process\n");
399
const int status = recv_control (context->foreground_fd);
400
if (status == RESPONSE_SCRIPT_SUCCEEDED)
401
return OPENVPN_PLUGIN_FUNC_SUCCESS;
403
fprintf (stderr, "DOWN-ROOT: Error receiving script execution confirmation from background process\n");
406
return OPENVPN_PLUGIN_FUNC_ERROR;
410
openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
412
struct down_root_context *context = (struct down_root_context *) handle;
414
if (DEBUG (context->verb))
415
fprintf (stderr, "DOWN-ROOT: close\n");
417
if (context->foreground_fd >= 0)
419
/* tell background process to exit */
420
if (send_control (context->foreground_fd, COMMAND_EXIT) == -1)
421
fprintf (stderr, "DOWN-ROOT: Error signaling background process to exit\n");
423
/* wait for background process to exit */
424
if (context->background_pid > 0)
425
waitpid (context->background_pid, NULL, 0);
427
close (context->foreground_fd);
428
context->foreground_fd = -1;
431
free_context (context);
435
openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle)
437
struct down_root_context *context = (struct down_root_context *) handle;
439
if (context && context->foreground_fd >= 0)
441
/* tell background process to exit */
442
send_control (context->foreground_fd, COMMAND_EXIT);
443
close (context->foreground_fd);
444
context->foreground_fd = -1;
449
* Background process -- runs with privilege.
452
down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb)
455
char *command_line = NULL;
456
char *argv_cat = NULL;
463
fprintf (stderr, "DOWN-ROOT: BACKGROUND: INIT command='%s'\n", command);
466
* Tell foreground that we initialized successfully
468
if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1)
470
fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [1]\n");
477
if (string_array_len (argv) >= 2)
478
argv_cat = build_command_line (&argv[1]);
480
argv_cat = build_command_line (NULL);
484
command_line = build_command_line (p);
487
* Save envp in environment
489
for (i = 0; envp[i]; ++i)
491
putenv ((char *)envp[i]);
502
/* get a command from foreground process */
503
command_code = recv_control (fd);
506
fprintf (stderr, "DOWN-ROOT: BACKGROUND: received command code: %d\n", command_code);
508
switch (command_code)
510
case COMMAND_RUN_SCRIPT:
511
status = system (command_line);
512
if (system_ok (status)) /* Succeeded */
514
if (send_control (fd, RESPONSE_SCRIPT_SUCCEEDED) == -1)
516
fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [2]\n");
522
if (send_control (fd, RESPONSE_SCRIPT_FAILED) == -1)
524
fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [3]\n");
534
fprintf (stderr, "DOWN-ROOT: BACKGROUND: read error on command channel\n");
538
fprintf (stderr, "DOWN-ROOT: BACKGROUND: unknown command code: code=%d, exiting\n",
550
fprintf (stderr, "DOWN-ROOT: BACKGROUND: EXIT\n");