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-2009 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");