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-2023 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 int
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 (flags & S_EXITCODE)
202  {
203  ret = platform_ret_code(stat);
204  if (ret != -1)
205  {
206  goto done;
207  }
208  }
209  else if (platform_system_ok(stat))
210  {
211  ret = true;
212  goto done;
213  }
214  if (error_message)
215  {
216  msg(((flags & S_FATAL) ? M_FATAL : M_WARN), "%s: %s",
217  error_message,
218  system_error_message(stat, &gc));
219  }
220 done:
221  gc_free(&gc);
222 
223  return ret;
224 }
225 
226 /*
227  * Run execve() inside a fork(), duping stdout. Designed to replicate the semantics of popen() but
228  * in a safer way that doesn't require the invocation of a shell or the risks
229  * associated with formatting and parsing a command line.
230  */
231 int
232 openvpn_popen(const struct argv *a, const struct env_set *es)
233 {
234  struct gc_arena gc = gc_new();
235  int ret = -1;
236 
237  if (a && a->argv[0])
238  {
239 #if defined(ENABLE_FEATURE_EXECVE)
240  static bool warn_shown = false;
242  {
243  const char *cmd = a->argv[0];
244  char *const *argv = a->argv;
245  char *const *envp = (char *const *)make_env_array(es, true, &gc);
246  pid_t pid;
247  int pipe_stdout[2];
248 
249  if (pipe(pipe_stdout) == 0)
250  {
251  pid = fork();
252  if (pid == (pid_t)0) /* child side */
253  {
254  close(pipe_stdout[0]); /* Close read end */
255  dup2(pipe_stdout[1], 1);
256  execve(cmd, argv, envp);
258  }
259  else if (pid > (pid_t)0) /* parent side */
260  {
261  int status = 0;
262 
263  close(pipe_stdout[1]); /* Close write end */
264  waitpid(pid, &status, 0);
265  ret = pipe_stdout[0];
266  }
267  else /* fork failed */
268  {
269  close(pipe_stdout[0]);
270  close(pipe_stdout[1]);
271  msg(M_ERR, "openvpn_popen: unable to fork %s", cmd);
272  }
273  }
274  else
275  {
276  msg(M_WARN, "openvpn_popen: unable to create stdout pipe for %s", cmd);
277  ret = -1;
278  }
279  }
280  else if (!warn_shown && (script_security() < SSEC_SCRIPTS))
281  {
283  warn_shown = true;
284  }
285 #else /* if defined(ENABLE_FEATURE_EXECVE) */
286  msg(M_WARN, "openvpn_popen: execve function not available");
287 #endif /* if defined(ENABLE_FEATURE_EXECVE) */
288  }
289  else
290  {
291  msg(M_FATAL, "openvpn_popen: called with empty argv");
292  }
293 
294  gc_free(&gc);
295  return ret;
296 }
error.h
gc_new
static struct gc_arena gc_new(void)
Definition: buffer.h:1011
run_command.h
platform_ret_code
int platform_ret_code(int stat)
Return an exit code if valid and between 0 and 255, -1 otherwise.
Definition: platform.c:427
M_FATAL
#define M_FATAL
Definition: error.h:95
win32.h
argv
Definition: argv.h:35
script_security_set
void script_security_set(int level)
Definition: run_command.c:51
es
struct env_set * es
Definition: test_pkcs11.c:122
OPENVPN_EXECVE_ERROR
#define OPENVPN_EXECVE_ERROR
Definition: run_command.h:36
SSEC_SCRIPTS
#define SSEC_SCRIPTS
Definition: run_command.h:33
alloc_buf_gc
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition: buffer.c:90
config-msvc.h
platform_system_ok
bool platform_system_ok(int stat)
interpret the status code returned by execve()
Definition: platform.c:416
openvpn_execve_check
int 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
argv::argv
char ** argv
Definition: argv.h:39
system_error_message
static const char * system_error_message(int stat, struct gc_arena *gc)
Definition: run_command.c:60
OPENVPN_EXECVE_NOT_ALLOWED
#define OPENVPN_EXECVE_NOT_ALLOWED
Definition: run_command.h:37
M_WARN
#define M_WARN
Definition: error.h:97
M_ERR
#define M_ERR
Definition: error.h:111
script_security_level
static int script_security_level
Definition: run_command.c:42
buffer
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
SSEC_BUILT_IN
#define SSEC_BUILT_IN
Definition: run_command.h:32
buffer.h
syshead.h
platform.h
gc_arena
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
env_set
Definition: env_set.h:42
openvpn_execve_allowed
bool openvpn_execve_allowed(const unsigned int flags)
Definition: run_command.c:112
S_EXITCODE
#define S_EXITCODE
Instead of returning 1/0 for success/fail, return exit code when between 0 and 255 and -1 otherwise.
Definition: run_command.h:49
status
static SERVICE_STATUS status
Definition: interactive.c:56
gc_free
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1019
openvpn_popen
int openvpn_popen(const struct argv *a, const struct env_set *es)
Definition: run_command.c:232
config.h
S_FATAL
#define S_FATAL
Definition: run_command.h:46
S_SCRIPT
#define S_SCRIPT
Definition: run_command.h:45
SCRIPT_SECURITY_WARNING
#define SCRIPT_SECURITY_WARNING
Definition: common.h:98
OPENVPN_EXECVE_FAILURE
#define OPENVPN_EXECVE_FAILURE
Definition: run_command.h:38
memdbg.h
msg
#define msg(flags,...)
Definition: error.h:150
make_env_array
const char ** make_env_array(const struct env_set *es, const bool check_allowed, struct gc_arena *gc)
Definition: env_set.c:423
buf_printf
bool buf_printf(struct buffer *buf, const char *format,...)
Definition: buffer.c:242
openvpn_execve
int openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags)
Definition: win32.c:1004
script_security
int script_security(void)
Definition: run_command.c:45
route_ipv6_option_list::gc
struct gc_arena * gc
Definition: route.h:109
buffer::data
uint8_t * data
Pointer to the allocated memory.
Definition: buffer.h:68