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-2005 OpenVPN Solutions LLC <info@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
));
277 context
->foreground_fd
= -1;
280 * Intercept the --up and --down callbacks
282 *type_mask
= OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP
) | OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN
);
285 * Make sure we have two string arguments: the first is the .so name,
286 * the second is the script command.
288 if (string_array_len (argv
) < 2)
290 fprintf (stderr
, "DOWN-ROOT: need down script command\n");
295 * Save our argument in context
297 context
->command
= build_command_line (&argv
[1]);
300 * Get verbosity level from environment
303 const char *verb_string
= get_env ("verb", envp
);
305 context
->verb
= atoi (verb_string
);
308 return (openvpn_plugin_handle_t
) context
;
311 free_context (context
);
316 openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle
, const int type
, const char *argv
[], const char *envp
[])
318 struct down_root_context
*context
= (struct down_root_context
*) handle
;
320 if (type
== OPENVPN_PLUGIN_UP
&& context
->foreground_fd
== -1) /* fork off a process to hold onto root */
326 * Make a socket for foreground and background processes
329 if (socketpair (PF_UNIX
, SOCK_DGRAM
, 0, fd
) == -1)
331 fprintf (stderr
, "DOWN-ROOT: socketpair call failed\n");
332 return OPENVPN_PLUGIN_FUNC_ERROR
;
336 * Fork off the privileged process. It will remain privileged
337 * even after the foreground process drops its privileges.
349 context
->background_pid
= pid
;
351 /* close our copy of child's socket */
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");
358 /* wait for background child process to initialize */
359 status
= recv_control (fd
[0]);
360 if (status
== RESPONSE_INIT_SUCCEEDED
)
362 context
->foreground_fd
= fd
[0];
363 return OPENVPN_PLUGIN_FUNC_SUCCESS
;
372 /* close all parent fds except our socket back to parent */
373 close_fds_except (fd
[1]);
375 /* Ignore most signals (the parent will receive them) */
378 /* Daemonize if --daemon option is set. */
381 /* execute the event loop */
382 down_root_server (fd
[1], context
->command
, argv
, envp
, context
->verb
);
386 return 0; /* NOTREACHED */
389 else if (type
== OPENVPN_PLUGIN_DOWN
&& context
->foreground_fd
>= 0)
391 if (send_control (context
->foreground_fd
, COMMAND_RUN_SCRIPT
) == -1)
393 fprintf (stderr
, "DOWN-ROOT: Error sending script execution signal to background process\n");
397 const int status
= recv_control (context
->foreground_fd
);
398 if (status
== RESPONSE_SCRIPT_SUCCEEDED
)
399 return OPENVPN_PLUGIN_FUNC_SUCCESS
;
401 fprintf (stderr
, "DOWN-ROOT: Error receiving script execution confirmation from background process\n");
404 return OPENVPN_PLUGIN_FUNC_ERROR
;
408 openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle
)
410 struct down_root_context
*context
= (struct down_root_context
*) handle
;
412 if (DEBUG (context
->verb
))
413 fprintf (stderr
, "DOWN-ROOT: close\n");
415 if (context
->foreground_fd
>= 0)
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");
421 /* wait for background process to exit */
422 if (context
->background_pid
> 0)
423 waitpid (context
->background_pid
, NULL
, 0);
425 close (context
->foreground_fd
);
426 context
->foreground_fd
= -1;
429 free_context (context
);
433 openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle
)
435 struct down_root_context
*context
= (struct down_root_context
*) handle
;
437 if (context
->foreground_fd
>= 0)
439 /* tell background process to exit */
440 send_control (context
->foreground_fd
, COMMAND_EXIT
);
441 close (context
->foreground_fd
);
442 context
->foreground_fd
= -1;
447 * Background process -- runs with privilege.
450 down_root_server (const int fd
, char *command
, const char *argv
[], const char *envp
[], const int verb
)
453 char *command_line
= NULL
;
454 char *argv_cat
= NULL
;
461 fprintf (stderr
, "DOWN-ROOT: BACKGROUND: INIT command='%s'\n", command
);
464 * Tell foreground that we initialized successfully
466 if (send_control (fd
, RESPONSE_INIT_SUCCEEDED
) == -1)
468 fprintf (stderr
, "DOWN-ROOT: BACKGROUND: write error on response socket [1]\n");
475 if (string_array_len (argv
) >= 2)
476 argv_cat
= build_command_line (&argv
[1]);
478 argv_cat
= build_command_line (NULL
);
482 command_line
= build_command_line (p
);
485 * Save envp in environment
487 for (i
= 0; envp
[i
]; ++i
)
489 putenv ((char *)envp
[i
]);
500 /* get a command from foreground process */
501 command_code
= recv_control (fd
);
504 fprintf (stderr
, "DOWN-ROOT: BACKGROUND: received command code: %d\n", command_code
);
506 switch (command_code
)
508 case COMMAND_RUN_SCRIPT
:
509 status
= system (command_line
);
510 if (system_ok (status
)) /* Succeeded */
512 if (send_control (fd
, RESPONSE_SCRIPT_SUCCEEDED
) == -1)
514 fprintf (stderr
, "DOWN-ROOT: BACKGROUND: write error on response socket [2]\n");
520 if (send_control (fd
, RESPONSE_SCRIPT_FAILED
) == -1)
522 fprintf (stderr
, "DOWN-ROOT: BACKGROUND: write error on response socket [3]\n");
532 fprintf (stderr
, "DOWN-ROOT: BACKGROUND: read error on command channel\n");
536 fprintf (stderr
, "DOWN-ROOT: BACKGROUND: unknown command code: code=%d, exiting\n",
548 fprintf (stderr
, "DOWN-ROOT: BACKGROUND: EXIT\n");