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  * Generate an error message based on the status code returned by openvpn_execve().
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 
64  switch (stat)
65  {
67  buf_printf(&out, "disallowed by script-security setting");
68  break;
69 
70 #ifdef _WIN32
72  buf_printf(&out, "external program did not execute -- ");
73  /* fall through */
74 
75  default:
76  buf_printf(&out, "returned error code %d", stat);
77  break;
78 #else /* ifdef _WIN32 */
79 
81  buf_printf(&out, "external program fork failed");
82  break;
83 
84  default:
85  if (!WIFEXITED(stat))
86  {
87  buf_printf(&out, "external program did not exit normally");
88  }
89  else
90  {
91  const int cmd_ret = WEXITSTATUS(stat);
92  if (!cmd_ret)
93  {
94  buf_printf(&out, "external program exited normally");
95  }
96  else if (cmd_ret == OPENVPN_EXECVE_FAILURE)
97  {
98  buf_printf(&out, "could not execute external program");
99  }
100  else
101  {
102  buf_printf(&out, "external program exited with error status: %d", cmd_ret);
103  }
104  }
105  break;
106 #endif /* ifdef _WIN32 */
107  }
108  return (const char *)out.data;
109 }
110 
111 bool
112 openvpn_execve_allowed(const unsigned int flags)
113 {
114  if (flags & S_SCRIPT)
115  {
116  return script_security() >= SSEC_SCRIPTS;
117  }
118  else
119  {
120  return script_security() >= SSEC_BUILT_IN;
121  }
122 }
123 
124 
125 #ifndef _WIN32
126 /*
127  * Run execve() inside a fork(). Designed to replicate the semantics of system() but
128  * in a safer way that doesn't require the invocation of a shell or the risks
129  * associated with formatting and parsing a command line.
130  * Returns the exit status of child, OPENVPN_EXECVE_NOT_ALLOWED if openvpn_execve_allowed()
131  * returns false, or OPENVPN_EXECVE_ERROR on other errors.
132  */
133 int
134 openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags)
135 {
136  struct gc_arena gc = gc_new();
137  int ret = OPENVPN_EXECVE_ERROR;
138  static bool warn_shown = false;
139 
140  if (a && a->argv[0])
141  {
142 #if defined(ENABLE_FEATURE_EXECVE)
143  if (openvpn_execve_allowed(flags))
144  {
145  const char *cmd = a->argv[0];
146  char *const *argv = a->argv;
147  char *const *envp = (char *const *)make_env_array(es, true, &gc);
148  pid_t pid;
149 
150  pid = fork();
151  if (pid == (pid_t)0) /* child side */
152  {
153  execve(cmd, argv, envp);
155  }
156  else if (pid < (pid_t)0) /* fork failed */
157  {
158  msg(M_ERR, "openvpn_execve: unable to fork");
159  }
160  else /* parent side */
161  {
162  if (waitpid(pid, &ret, 0) != pid)
163  {
164  ret = OPENVPN_EXECVE_ERROR;
165  }
166  }
167  }
168  else
169  {
171  if (!warn_shown && (script_security() < SSEC_SCRIPTS))
172  {
174  warn_shown = true;
175  }
176  }
177 #else /* if defined(ENABLE_FEATURE_EXECVE) */
178  msg(M_WARN, "openvpn_execve: execve function not available");
179 #endif /* if defined(ENABLE_FEATURE_EXECVE) */
180  }
181  else
182  {
183  msg(M_FATAL, "openvpn_execve: called with empty argv");
184  }
185 
186  gc_free(&gc);
187  return ret;
188 }
189 #endif /* ifndef _WIN32 */
190 
191 /*
192  * Wrapper around openvpn_execve
193  */
194 bool
195 openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message)
196 {
197  struct gc_arena gc = gc_new();
198  const int stat = openvpn_execve(a, es, flags);
199  int ret = false;
200 
201  if (platform_system_ok(stat))
202  {
203  ret = true;
204  }
205  else
206  {
207  if (error_message)
208  {
209  msg(((flags & S_FATAL) ? M_FATAL : M_WARN), "%s: %s",
210  error_message,
211  system_error_message(stat, &gc));
212  }
213  }
214  gc_free(&gc);
215  return ret;
216 }
217 
218 /*
219  * Run execve() inside a fork(), duping stdout. Designed to replicate the semantics of popen() but
220  * in a safer way that doesn't require the invocation of a shell or the risks
221  * associated with formatting and parsing a command line.
222  */
223 int
224 openvpn_popen(const struct argv *a, const struct env_set *es)
225 {
226  struct gc_arena gc = gc_new();
227  int ret = -1;
228  static bool warn_shown = false;
229 
230  if (a && a->argv[0])
231  {
232 #if defined(ENABLE_FEATURE_EXECVE)
234  {
235  const char *cmd = a->argv[0];
236  char *const *argv = a->argv;
237  char *const *envp = (char *const *)make_env_array(es, true, &gc);
238  pid_t pid;
239  int pipe_stdout[2];
240 
241  if (pipe(pipe_stdout) == 0)
242  {
243  pid = fork();
244  if (pid == (pid_t)0) /* child side */
245  {
246  close(pipe_stdout[0]); /* Close read end */
247  dup2(pipe_stdout[1],1);
248  execve(cmd, argv, envp);
250  }
251  else if (pid > (pid_t)0) /* parent side */
252  {
253  int status = 0;
254 
255  close(pipe_stdout[1]); /* Close write end */
256  waitpid(pid, &status, 0);
257  ret = pipe_stdout[0];
258  }
259  else /* fork failed */
260  {
261  close(pipe_stdout[0]);
262  close(pipe_stdout[1]);
263  msg(M_ERR, "openvpn_popen: unable to fork %s", cmd);
264  }
265  }
266  else
267  {
268  msg(M_WARN, "openvpn_popen: unable to create stdout pipe for %s", cmd);
269  ret = -1;
270  }
271  }
272  else if (!warn_shown && (script_security() < SSEC_SCRIPTS))
273  {
275  warn_shown = true;
276  }
277 #else /* if defined(ENABLE_FEATURE_EXECVE) */
278  msg(M_WARN, "openvpn_popen: execve function not available");
279 #endif /* if defined(ENABLE_FEATURE_EXECVE) */
280  }
281  else
282  {
283  msg(M_FATAL, "openvpn_popen: called with empty argv");
284  }
285 
286  gc_free(&gc);
287  return ret;
288 }
#define S_FATAL
Definition: run_command.h:46
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1023
int script_security(void)
Definition: run_command.c:45
#define OPENVPN_EXECVE_FAILURE
Definition: run_command.h:38
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:112
#define OPENVPN_EXECVE_ERROR
Definition: run_command.h:36
list flags
#define S_SCRIPT
Definition: run_command.h:45
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:224
struct gc_arena * gc
Definition: route.h:109
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:1088
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
#define OPENVPN_EXECVE_NOT_ALLOWED
Definition: run_command.h:37
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:195