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