OpenVPN
run_command.c
Go to the documentation of this file.
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-2017 OpenVPN Technologies, Inc. <sales@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 along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #elif defined(_MSC_VER)
27 #include "config-msvc.h"
28 #endif
29 
30 #include "syshead.h"
31 
32 #include "buffer.h"
33 #include "error.h"
34 #include "platform.h"
35 #include "win32.h"
36 
37 #include "memdbg.h"
38 
39 #include "run_command.h"
40 
41 /* contains an SSEC_x value defined in platform.h */
42 static int script_security_level = SSEC_BUILT_IN; /* GLOBAL */
43 
44 int
46 {
47  return script_security_level;
48 }
49 
50 void
52 {
53  script_security_level = level;
54 }
55 
56 /*
57  * Print an error message based on the status code returned by system().
58  */
59 static const char *
60 system_error_message(int stat, struct gc_arena *gc)
61 {
62  struct buffer out = alloc_buf_gc(256, gc);
63 #ifdef _WIN32
64  if (stat == -1)
65  {
66  buf_printf(&out, "external program did not execute -- ");
67  }
68  buf_printf(&out, "returned error code %d", stat);
69 #else /* ifdef _WIN32 */
70  if (stat == -1)
71  {
72  buf_printf(&out, "external program fork failed");
73  }
74  else if (!WIFEXITED(stat))
75  {
76  buf_printf(&out, "external program did not exit normally");
77  }
78  else
79  {
80  const int cmd_ret = WEXITSTATUS(stat);
81  if (!cmd_ret)
82  {
83  buf_printf(&out, "external program exited normally");
84  }
85  else if (cmd_ret == 127)
86  {
87  buf_printf(&out, "could not execute external program");
88  }
89  else
90  {
91  buf_printf(&out, "external program exited with error status: %d", cmd_ret);
92  }
93  }
94 #endif /* ifdef _WIN32 */
95  return (const char *)out.data;
96 }
97 
98 bool
99 openvpn_execve_allowed(const unsigned int flags)
100 {
101  if (flags & S_SCRIPT)
102  {
103  return script_security() >= SSEC_SCRIPTS;
104  }
105  else
106  {
107  return script_security() >= SSEC_BUILT_IN;
108  }
109 }
110 
111 
112 #ifndef _WIN32
113 /*
114  * Run execve() inside a fork(). Designed to replicate the semantics of system() but
115  * in a safer way that doesn't require the invocation of a shell or the risks
116  * assocated with formatting and parsing a command line.
117  */
118 int
119 openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags)
120 {
121  struct gc_arena gc = gc_new();
122  int ret = -1;
123  static bool warn_shown = false;
124 
125  if (a && a->argv[0])
126  {
127 #if defined(ENABLE_FEATURE_EXECVE)
128  if (openvpn_execve_allowed(flags))
129  {
130  const char *cmd = a->argv[0];
131  char *const *argv = a->argv;
132  char *const *envp = (char *const *)make_env_array(es, true, &gc);
133  pid_t pid;
134 
135  pid = fork();
136  if (pid == (pid_t)0) /* child side */
137  {
138  execve(cmd, argv, envp);
139  exit(127);
140  }
141  else if (pid < (pid_t)0) /* fork failed */
142  {
143  msg(M_ERR, "openvpn_execve: unable to fork");
144  }
145  else /* parent side */
146  {
147  if (waitpid(pid, &ret, 0) != pid)
148  {
149  ret = -1;
150  }
151  }
152  }
153  else if (!warn_shown && (script_security() < SSEC_SCRIPTS))
154  {
156  warn_shown = true;
157  }
158 #else /* if defined(ENABLE_FEATURE_EXECVE) */
159  msg(M_WARN, "openvpn_execve: execve function not available");
160 #endif /* if defined(ENABLE_FEATURE_EXECVE) */
161  }
162  else
163  {
164  msg(M_FATAL, "openvpn_execve: called with empty argv");
165  }
166 
167  gc_free(&gc);
168  return ret;
169 }
170 #endif /* ifndef _WIN32 */
171 
172 /*
173  * Wrapper around openvpn_execve
174  */
175 bool
176 openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message)
177 {
178  struct gc_arena gc = gc_new();
179  const int stat = openvpn_execve(a, es, flags);
180  int ret = false;
181 
182  if (platform_system_ok(stat))
183  {
184  ret = true;
185  }
186  else
187  {
188  if (error_message)
189  {
190  msg(((flags & S_FATAL) ? M_FATAL : M_WARN), "%s: %s",
191  error_message,
192  system_error_message(stat, &gc));
193  }
194  }
195  gc_free(&gc);
196  return ret;
197 }
198 
199 /*
200  * Run execve() inside a fork(), duping stdout. Designed to replicate the semantics of popen() but
201  * in a safer way that doesn't require the invocation of a shell or the risks
202  * assocated with formatting and parsing a command line.
203  */
204 int
205 openvpn_popen(const struct argv *a, const struct env_set *es)
206 {
207  struct gc_arena gc = gc_new();
208  int ret = -1;
209  static bool warn_shown = false;
210 
211  if (a && a->argv[0])
212  {
213 #if defined(ENABLE_FEATURE_EXECVE)
215  {
216  const char *cmd = a->argv[0];
217  char *const *argv = a->argv;
218  char *const *envp = (char *const *)make_env_array(es, true, &gc);
219  pid_t pid;
220  int pipe_stdout[2];
221 
222  if (pipe(pipe_stdout) == 0)
223  {
224  pid = fork();
225  if (pid == (pid_t)0) /* child side */
226  {
227  close(pipe_stdout[0]); /* Close read end */
228  dup2(pipe_stdout[1],1);
229  execve(cmd, argv, envp);
230  exit(127);
231  }
232  else if (pid > (pid_t)0) /* parent side */
233  {
234  int status = 0;
235 
236  close(pipe_stdout[1]); /* Close write end */
237  waitpid(pid, &status, 0);
238  ret = pipe_stdout[0];
239  }
240  else /* fork failed */
241  {
242  close(pipe_stdout[0]);
243  close(pipe_stdout[1]);
244  msg(M_ERR, "openvpn_popen: unable to fork %s", cmd);
245  }
246  }
247  else
248  {
249  msg(M_WARN, "openvpn_popen: unable to create stdout pipe for %s", cmd);
250  ret = -1;
251  }
252  }
253  else if (!warn_shown && (script_security() < SSEC_SCRIPTS))
254  {
256  warn_shown = true;
257  }
258 #else /* if defined(ENABLE_FEATURE_EXECVE) */
259  msg(M_WARN, "openvpn_popen: execve function not available");
260 #endif /* if defined(ENABLE_FEATURE_EXECVE) */
261  }
262  else
263  {
264  msg(M_FATAL, "openvpn_popen: called with empty argv");
265  }
266 
267  gc_free(&gc);
268  return ret;
269 }
#define S_FATAL
Definition: run_command.h:42
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1023
int script_security(void)
Definition: run_command.c:45
static int script_security_level
Definition: run_command.c:42
bool buf_printf(struct buffer *buf, const char *format,...)
Definition: buffer.c:245
#define SSEC_BUILT_IN
Definition: run_command.h:32
bool openvpn_execve_allowed(const unsigned int flags)
Definition: run_command.c:99
list flags
#define S_SCRIPT
Definition: run_command.h:41
bool platform_system_ok(int stat)
Definition: platform.c:234
const char ** make_env_array(const struct env_set *es, const bool check_allowed, struct gc_arena *gc)
Definition: env_set.c:423
#define SSEC_SCRIPTS
Definition: run_command.h:33
static struct gc_arena gc_new(void)
Definition: buffer.h:1015
char ** argv
Definition: argv.h:38
static SERVICE_STATUS status
Definition: automatic.c:43
#define msg
Definition: error.h:173
uint8_t * data
Pointer to the allocated memory.
Definition: buffer.h:68
int openvpn_popen(const struct argv *a, const struct env_set *es)
Definition: run_command.c:205
struct gc_arena * gc
Definition: route.h:108
static const char * system_error_message(int stat, struct gc_arena *gc)
Definition: run_command.c:60
void script_security_set(int level)
Definition: run_command.c:51
#define M_ERR
Definition: error.h:110
int openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags)
Definition: win32.c:1089
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
#define M_FATAL
Definition: error.h:94
#define SCRIPT_SECURITY_WARNING
Definition: common.h:100
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition: buffer.c:90
#define M_WARN
Definition: error.h:96
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
Definition: argv.h:35
bool openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message)
Definition: run_command.c:176