From 6149d88c5a2c58a9cc943ca02c36e8ee4e5d1751 Mon Sep 17 00:00:00 2001 From: Alberto Gonzalez Iniesta Date: Tue, 30 Jun 2015 08:22:29 +0200 Subject: Imported Upstream version 2.3.7 --- src/plugins/down-root/down-root.c | 696 +++++++++++++++++++------------------- 1 file changed, 343 insertions(+), 353 deletions(-) (limited to 'src/plugins/down-root/down-root.c') diff --git a/src/plugins/down-root/down-root.c b/src/plugins/down-root/down-root.c index d51d0e5..6931bec 100644 --- a/src/plugins/down-root/down-root.c +++ b/src/plugins/down-root/down-root.c @@ -5,7 +5,8 @@ * packet encryption, packet authentication, and * packet compression. * - * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. + * Copyright (C) 2002-2013 OpenVPN Technologies, Inc. + * Copyright (C) 2013 David Sommerseth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -40,14 +41,16 @@ #include #include #include +#include +#include #include #define DEBUG(verb) ((verb) >= 7) /* Command codes for foreground -> background communication */ -#define COMMAND_RUN_SCRIPT 0 -#define COMMAND_EXIT 1 +#define COMMAND_RUN_SCRIPT 1 +#define COMMAND_EXIT 2 /* Response codes for background -> foreground communication */ #define RESPONSE_INIT_SUCCEEDED 10 @@ -56,24 +59,24 @@ #define RESPONSE_SCRIPT_FAILED 13 /* Background process function */ -static void down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb); +static void down_root_server (const int fd, char * const * argv, char * const *envp, const int verb); /* * Plugin state, used by foreground */ struct down_root_context { - /* Foreground's socket to background process */ - int foreground_fd; + /* Foreground's socket to background process */ + int foreground_fd; - /* Process ID of background process */ - pid_t background_pid; + /* Process ID of background process */ + pid_t background_pid; - /* Verbosity level of OpenVPN */ - int verb; + /* Verbosity level of OpenVPN */ + int verb; - /* down command */ - char *command; + /* down command */ + char **command; }; /* @@ -84,21 +87,21 @@ struct down_root_context static const char * get_env (const char *name, const char *envp[]) { - if (envp) + if (envp) { - int i; - const int namelen = strlen (name); - for (i = 0; envp[i]; ++i) - { - if (!strncmp (envp[i], name, namelen)) - { - const char *cp = envp[i] + namelen; - if (*cp == '=') - return cp + 1; - } - } + int i; + const int namelen = strlen (name); + for (i = 0; envp[i]; ++i) + { + if (!strncmp (envp[i], name, namelen)) + { + const char *cp = envp[i] + namelen; + if (*cp == '=') + return cp + 1; + } + } } - return NULL; + return NULL; } /* @@ -107,13 +110,13 @@ get_env (const char *name, const char *envp[]) static int string_array_len (const char *array[]) { - int i = 0; - if (array) + int i = 0; + if (array) { - while (array[i]) - ++i; + while (array[i]) + ++i; } - return i; + return i; } /* @@ -123,23 +126,23 @@ string_array_len (const char *array[]) static int recv_control (int fd) { - unsigned char c; - const ssize_t size = read (fd, &c, sizeof (c)); - if (size == sizeof (c)) - return c; - else - return -1; + unsigned char c; + const ssize_t size = read (fd, &c, sizeof (c)); + if (size == sizeof (c)) + return c; + else + return -1; } static int send_control (int fd, int code) { - unsigned char c = (unsigned char) code; - const ssize_t size = write (fd, &c, sizeof (c)); - if (size == sizeof (c)) - return (int) size; - else - return -1; + unsigned char c = (unsigned char) code; + const ssize_t size = write (fd, &c, sizeof (c)); + if (size == sizeof (c)) + return (int) size; + else + return -1; } /* @@ -150,22 +153,22 @@ send_control (int fd, int code) static void daemonize (const char *envp[]) { - const char *daemon_string = get_env ("daemon", envp); - if (daemon_string && daemon_string[0] == '1') + const char *daemon_string = get_env ("daemon", envp); + if (daemon_string && daemon_string[0] == '1') { - const char *log_redirect = get_env ("daemon_log_redirect", envp); - int fd = -1; - if (log_redirect && log_redirect[0] == '1') - fd = dup (2); - if (daemon (0, 0) < 0) - { - fprintf (stderr, "DOWN-ROOT: daemonization failed\n"); - } - else if (fd >= 3) - { - dup2 (fd, 2); - close (fd); - } + const char *log_redirect = get_env ("daemon_log_redirect", envp); + int fd = -1; + if (log_redirect && log_redirect[0] == '1') + fd = dup (2); + if (daemon (0, 0) < 0) + { + warn ("DOWN-ROOT: daemonization failed"); + } + else if (fd >= 3) + { + dup2 (fd, 2); + close (fd); + } } } @@ -182,12 +185,12 @@ daemonize (const char *envp[]) static void close_fds_except (int keep) { - int i; - closelog (); - for (i = 3; i <= 100; ++i) + int i; + closelog (); + for (i = 3; i <= 100; ++i) { - if (i != keep) - close (i); + if (i != keep) + close (i); } } @@ -198,254 +201,261 @@ close_fds_except (int keep) static void set_signals (void) { - signal (SIGTERM, SIG_DFL); + signal (SIGTERM, SIG_DFL); - signal (SIGINT, SIG_IGN); - signal (SIGHUP, SIG_IGN); - signal (SIGUSR1, SIG_IGN); - signal (SIGUSR2, SIG_IGN); - signal (SIGPIPE, SIG_IGN); + signal (SIGINT, SIG_IGN); + signal (SIGHUP, SIG_IGN); + signal (SIGUSR1, SIG_IGN); + signal (SIGUSR2, SIG_IGN); + signal (SIGPIPE, SIG_IGN); } -/* - * convert system() return into a success/failure value - */ -int -system_ok (int stat) + +static void +free_context (struct down_root_context *context) { -#ifdef WIN32 - return stat == 0; -#else - return stat != -1 && WIFEXITED (stat) && WEXITSTATUS (stat) == 0; -#endif + if (context) + { + if (context->command) + { + free (context->command); + } + free (context); + } } -static char * -build_command_line (const char *argv[]) +/* Run the script using execve(). As execve() replaces the + * current process with the new one, do a fork first before + * calling execve() + */ +static int +run_script(char * const *argv, char * const *envp) { - int size = 0; - int n = 0; - int i; - char *string; + pid_t pid; + int ret = 0; - /* precompute size */ - if (argv) + pid = fork(); + if (pid == (pid_t)0) /* child side */ { - for (i = 0; argv[i]; ++i) - { - size += (strlen (argv[i]) + 1); /* string length plus trailing space */ - ++n; - } + execve(argv[0], argv, envp); + /* If execve() fails to run, exit child with exit code 127 */ + err(127, "DOWN-ROOT: Failed execute: %s", argv[0]); } - ++size; /* for null terminator */ - - /* allocate memory */ - string = (char *) malloc (size); - if (!string) + else if (pid < (pid_t)0 ) { - fprintf (stderr, "DOWN-ROOT: out of memory\n"); - exit (1); + warn ("DOWN-ROOT: Failed to fork child to run %s", argv[0]); + return -1; } - string[0] = '\0'; - - /* build string */ - for (i = 0; i < n; ++i) + else /* parent side */ { - strcat (string, argv[i]); - if (i + 1 < n) - strcat (string, " "); + if( waitpid (pid, &ret, 0) != pid ) + { + /* waitpid does not return error information via errno */ + fprintf(stderr, "DOWN-ROOT: waitpid() failed, don't know exit code of child (%s)\n", argv[0]); + return -1; + } } - return string; + return ret; } -static void -free_context (struct down_root_context *context) +OPENVPN_EXPORT openvpn_plugin_handle_t +openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) { - if (context) + struct down_root_context *context; + int i = 0; + + /* + * Allocate our context + */ + context = (struct down_root_context *) calloc (1, sizeof (struct down_root_context)); + if (!context) { - if (context->command) - free (context->command); - free (context); + warn ("DOWN-ROOT: Could not allocate memory for plug-in context"); + goto error; + } + context->foreground_fd = -1; + + /* + * Intercept the --up and --down callbacks + */ + *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) | OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN); + + /* + * Make sure we have two string arguments: the first is the .so name, + * the second is the script command. + */ + if (string_array_len (argv) < 2) + { + fprintf (stderr, "DOWN-ROOT: need down script command\n"); + goto error; } -} -OPENVPN_EXPORT openvpn_plugin_handle_t -openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) -{ - struct down_root_context *context; - - /* - * Allocate our context - */ - context = (struct down_root_context *) calloc (1, sizeof (struct down_root_context)); - if (!context) - goto error; - context->foreground_fd = -1; - - /* - * Intercept the --up and --down callbacks - */ - *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) | OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN); - - /* - * Make sure we have two string arguments: the first is the .so name, - * the second is the script command. - */ - if (string_array_len (argv) < 2) + /* + * Save the arguments in our context + */ + context->command = calloc(string_array_len(argv), sizeof(char *)); + if (!context->command) + { + warn ("DOWN-ROOT: Could not allocate memory for command array"); + goto error; + } + + /* Ignore argv[0], as it contains just the plug-in file name */ + for (i = 1; i < string_array_len(argv); i++) + { + context->command[i-1] = (char *) argv[i]; + } + + /* + * Get verbosity level from environment + */ { - fprintf (stderr, "DOWN-ROOT: need down script command\n"); - goto error; + const char *verb_string = get_env ("verb", envp); + if (verb_string) + context->verb = atoi (verb_string); } - /* - * Save our argument in context - */ - context->command = build_command_line (&argv[1]); - - /* - * Get verbosity level from environment - */ - { - const char *verb_string = get_env ("verb", envp); - if (verb_string) - context->verb = atoi (verb_string); - } - - return (openvpn_plugin_handle_t) context; - - error: - free_context (context); - return NULL; + return (openvpn_plugin_handle_t) context; + +error: + free_context (context); + return NULL; } OPENVPN_EXPORT int openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) { - struct down_root_context *context = (struct down_root_context *) handle; + struct down_root_context *context = (struct down_root_context *) handle; - if (type == OPENVPN_PLUGIN_UP && context->foreground_fd == -1) /* fork off a process to hold onto root */ + if (type == OPENVPN_PLUGIN_UP && context->foreground_fd == -1) /* fork off a process to hold onto root */ { - pid_t pid; - int fd[2]; - - /* - * Make a socket for foreground and background processes - * to communicate. - */ - if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1) - { - fprintf (stderr, "DOWN-ROOT: socketpair call failed\n"); - return OPENVPN_PLUGIN_FUNC_ERROR; - } - - /* - * Fork off the privileged process. It will remain privileged - * even after the foreground process drops its privileges. - */ - pid = fork (); - - if (pid) - { - int status; - - /* - * Foreground Process - */ - - context->background_pid = pid; - - /* close our copy of child's socket */ - close (fd[1]); - - /* don't let future subprocesses inherit child socket */ - if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0) - fprintf (stderr, "DOWN-ROOT: Set FD_CLOEXEC flag on socket file descriptor failed\n"); - - /* wait for background child process to initialize */ - status = recv_control (fd[0]); - if (status == RESPONSE_INIT_SUCCEEDED) - { - context->foreground_fd = fd[0]; - return OPENVPN_PLUGIN_FUNC_SUCCESS; - } - } - else - { - /* - * Background Process - */ - - /* close all parent fds except our socket back to parent */ - close_fds_except (fd[1]); - - /* Ignore most signals (the parent will receive them) */ - set_signals (); - - /* Daemonize if --daemon option is set. */ - daemonize (envp); - - /* execute the event loop */ - down_root_server (fd[1], context->command, argv, envp, context->verb); - - close (fd[1]); - exit (0); - return 0; /* NOTREACHED */ - } + pid_t pid; + int fd[2]; + + /* + * Make a socket for foreground and background processes + * to communicate. + */ + if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1) + { + warn ("DOWN-ROOT: socketpair call failed"); + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + /* + * Fork off the privileged process. It will remain privileged + * even after the foreground process drops its privileges. + */ + pid = fork (); + + if (pid) + { + int status; + + /* + * Foreground Process + */ + + context->background_pid = pid; + + /* close our copy of child's socket */ + close (fd[1]); + + /* don't let future subprocesses inherit child socket */ + if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0) + { + warn ("DOWN-ROOT: Set FD_CLOEXEC flag on socket file descriptor failed"); + } + + /* wait for background child process to initialize */ + status = recv_control (fd[0]); + if (status == RESPONSE_INIT_SUCCEEDED) + { + context->foreground_fd = fd[0]; + return OPENVPN_PLUGIN_FUNC_SUCCESS; + } + } + else + { + /* + * Background Process + */ + + /* close all parent fds except our socket back to parent */ + close_fds_except (fd[1]); + + /* Ignore most signals (the parent will receive them) */ + set_signals (); + + /* Daemonize if --daemon option is set. */ + daemonize (envp); + + /* execute the event loop */ + down_root_server (fd[1], context->command, (char * const *) envp, context->verb); + + close (fd[1]); + exit (0); + return 0; /* NOTREACHED */ + } } - else if (type == OPENVPN_PLUGIN_DOWN && context->foreground_fd >= 0) + else if (type == OPENVPN_PLUGIN_DOWN && context->foreground_fd >= 0) { - if (send_control (context->foreground_fd, COMMAND_RUN_SCRIPT) == -1) - { - fprintf (stderr, "DOWN-ROOT: Error sending script execution signal to background process\n"); - } - else - { - const int status = recv_control (context->foreground_fd); - if (status == RESPONSE_SCRIPT_SUCCEEDED) - return OPENVPN_PLUGIN_FUNC_SUCCESS; - if (status == -1) - fprintf (stderr, "DOWN-ROOT: Error receiving script execution confirmation from background process\n"); - } + if (send_control (context->foreground_fd, COMMAND_RUN_SCRIPT) == -1) + { + warn ("DOWN-ROOT: Error sending script execution signal to background process"); + } + else + { + const int status = recv_control (context->foreground_fd); + if (status == RESPONSE_SCRIPT_SUCCEEDED) + return OPENVPN_PLUGIN_FUNC_SUCCESS; + if (status == -1) + { + warn ("DOWN-ROOT: Error receiving script execution confirmation from background process"); + } + } } - return OPENVPN_PLUGIN_FUNC_ERROR; + return OPENVPN_PLUGIN_FUNC_ERROR; } OPENVPN_EXPORT void openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) { - struct down_root_context *context = (struct down_root_context *) handle; + struct down_root_context *context = (struct down_root_context *) handle; - if (DEBUG (context->verb)) - fprintf (stderr, "DOWN-ROOT: close\n"); + if (DEBUG (context->verb)) + fprintf (stderr, "DOWN-ROOT: close\n"); - if (context->foreground_fd >= 0) + if (context->foreground_fd >= 0) { - /* tell background process to exit */ - if (send_control (context->foreground_fd, COMMAND_EXIT) == -1) - fprintf (stderr, "DOWN-ROOT: Error signaling background process to exit\n"); - - /* wait for background process to exit */ - if (context->background_pid > 0) - waitpid (context->background_pid, NULL, 0); - - close (context->foreground_fd); - context->foreground_fd = -1; + /* tell background process to exit */ + if (send_control (context->foreground_fd, COMMAND_EXIT) == -1) + { + warn ("DOWN-ROOT: Error signalling background process to exit"); + } + + /* wait for background process to exit */ + if (context->background_pid > 0) + waitpid (context->background_pid, NULL, 0); + + close (context->foreground_fd); + context->foreground_fd = -1; } - free_context (context); + free_context (context); } OPENVPN_EXPORT void openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle) { - struct down_root_context *context = (struct down_root_context *) handle; + struct down_root_context *context = (struct down_root_context *) handle; - if (context && context->foreground_fd >= 0) + if (context && context->foreground_fd >= 0) { - /* tell background process to exit */ - send_control (context->foreground_fd, COMMAND_EXIT); - close (context->foreground_fd); - context->foreground_fd = -1; + /* tell background process to exit */ + send_control (context->foreground_fd, COMMAND_EXIT); + close (context->foreground_fd); + context->foreground_fd = -1; } } @@ -453,105 +463,85 @@ openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle) * Background process -- runs with privilege. */ static void -down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb) +down_root_server (const int fd, char * const *argv, char * const *envp, const int verb) { - const char *p[3]; - char *command_line = NULL; - char *argv_cat = NULL; - int i; - - /* - * Do initialization - */ - if (DEBUG (verb)) - fprintf (stderr, "DOWN-ROOT: BACKGROUND: INIT command='%s'\n", command); - - /* - * Tell foreground that we initialized successfully - */ - if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1) + /* + * Do initialization + */ + if (DEBUG (verb)) + fprintf (stderr, "DOWN-ROOT: BACKGROUND: INIT command='%s'\n", argv[0]); + + /* + * Tell foreground that we initialized successfully + */ + if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1) { - fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [1]\n"); - goto done; + warn ("DOWN-ROOT: BACKGROUND: write error on response socket [1]"); + goto done; } - /* - * Build command line - */ - if (string_array_len (argv) >= 2) - argv_cat = build_command_line (&argv[1]); - else - argv_cat = build_command_line (NULL); - p[0] = command; - p[1] = argv_cat; - p[2] = NULL; - command_line = build_command_line (p); - - /* - * Save envp in environment - */ - for (i = 0; envp[i]; ++i) + /* + * Event loop + */ + while (1) { - putenv ((char *)envp[i]); + int command_code; + int exit_code = -1; + + /* get a command from foreground process */ + command_code = recv_control (fd); + + if (DEBUG (verb)) + fprintf (stderr, "DOWN-ROOT: BACKGROUND: received command code: %d\n", command_code); + + switch (command_code) + { + case COMMAND_RUN_SCRIPT: + if ( (exit_code = run_script(argv, envp)) == 0 ) /* Succeeded */ + { + if (send_control (fd, RESPONSE_SCRIPT_SUCCEEDED) == -1) + { + warn ("DOWN-ROOT: BACKGROUND: write error on response socket [2]"); + goto done; + } + } + else /* Failed */ + { + fprintf(stderr, "DOWN-ROOT: BACKGROUND: %s exited with exit code %i\n", argv[0], exit_code); + if (send_control (fd, RESPONSE_SCRIPT_FAILED) == -1) + { + warn ("DOWN-ROOT: BACKGROUND: write error on response socket [3]"); + goto done; + } + } + break; + + case COMMAND_EXIT: + goto done; + + case -1: + warn ("DOWN-ROOT: BACKGROUND: read error on command channel"); + goto done; + + default: + fprintf (stderr, "DOWN-ROOT: BACKGROUND: unknown command code: code=%d, exiting\n", + command_code); + goto done; + } } - /* - * Event loop - */ - while (1) - { - int command_code; - int status; - - /* get a command from foreground process */ - command_code = recv_control (fd); - - if (DEBUG (verb)) - fprintf (stderr, "DOWN-ROOT: BACKGROUND: received command code: %d\n", command_code); - - switch (command_code) - { - case COMMAND_RUN_SCRIPT: - status = system (command_line); - if (system_ok (status)) /* Succeeded */ - { - if (send_control (fd, RESPONSE_SCRIPT_SUCCEEDED) == -1) - { - fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [2]\n"); - goto done; - } - } - else /* Failed */ - { - if (send_control (fd, RESPONSE_SCRIPT_FAILED) == -1) - { - fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [3]\n"); - goto done; - } - } - break; - - case COMMAND_EXIT: - goto done; - - case -1: - fprintf (stderr, "DOWN-ROOT: BACKGROUND: read error on command channel\n"); - goto done; - - default: - fprintf (stderr, "DOWN-ROOT: BACKGROUND: unknown command code: code=%d, exiting\n", - command_code); - goto done; - } - } +done: + if (DEBUG (verb)) + fprintf (stderr, "DOWN-ROOT: BACKGROUND: EXIT\n"); - done: - if (argv_cat) - free (argv_cat); - if (command_line) - free (command_line); - if (DEBUG (verb)) - fprintf (stderr, "DOWN-ROOT: BACKGROUND: EXIT\n"); - - return; + return; } + + +/* +Local variables: +c-file-style: "bsd" +c-basic-offset: 4 +indent-tabs-mode: nil +End: +*/ -- cgit v1.2.3