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-2026 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, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include "buffer.h"
30#include "error.h"
31#include "platform.h"
32#include "win32.h"
33
34#include "memdbg.h"
35
36#include "run_command.h"
37
38/* contains an SSEC_x value defined in platform.h */
39static int script_security_level = SSEC_BUILT_IN; /* GLOBAL */
40
41int
43{
45}
46
47void
49{
51}
52
53/*
54 * Generate an error message based on the status code returned by openvpn_execve().
55 */
56static const char *
58{
59 struct buffer out = alloc_buf_gc(256, gc);
60
61 switch (stat)
62 {
64 buf_printf(&out, "disallowed by script-security setting");
65 break;
66
67#ifdef _WIN32
69 buf_printf(&out, "external program did not execute -- ");
70 /* fall through */
71
72 default:
73 buf_printf(&out, "returned error code %d", stat);
74 break;
75#else /* ifdef _WIN32 */
76
78 buf_printf(&out, "external program fork failed");
79 break;
80
81 default:
82 if (!WIFEXITED(stat))
83 {
84 buf_printf(&out, "external program did not exit normally");
85 }
86 else
87 {
88 const int cmd_ret = WEXITSTATUS(stat);
89 if (!cmd_ret)
90 {
91 buf_printf(&out, "external program exited normally");
92 }
93 else if (cmd_ret == OPENVPN_EXECVE_FAILURE)
94 {
95 buf_printf(&out, "could not execute external program");
96 }
97 else
98 {
99 buf_printf(&out, "external program exited with error status: %d", cmd_ret);
100 }
101 }
102 break;
103#endif /* ifdef _WIN32 */
104 }
105 return (const char *)out.data;
106}
107
108#ifndef WIN32
109bool
110openvpn_waitpid_check(pid_t pid, const char *msg_prefix, msglvl_t msglevel)
111{
112 if (pid == 0)
113 {
114 return false;
115 }
116 int status;
117 pid_t pidret = waitpid(pid, &status, WNOHANG);
118 if (pidret != pid)
119 {
120 return true;
121 }
122
123 if (WIFEXITED(status))
124 {
125 int exitcode = WEXITSTATUS(status);
126
127 if (exitcode == OPENVPN_EXECVE_FAILURE)
128 {
129 msg(msglevel, "%scould not execute external program (exit code 127)", msg_prefix);
130 }
131 else
132 {
133 msg(msglevel, "%sexternal program exited with error status: %d", msg_prefix, exitcode);
134 }
135 }
136 else if (WIFSIGNALED(status))
137 {
138 msg(msglevel, "%sexternal program received signal %d", msg_prefix, WTERMSIG(status));
139 }
140
141 return false;
142}
143#endif /* ifndef WIN32 */
144
145bool
146openvpn_execve_allowed(const unsigned int flags)
147{
148 if (flags & S_SCRIPT)
149 {
150 return script_security() >= SSEC_SCRIPTS;
151 }
152 else
153 {
154 return script_security() >= SSEC_BUILT_IN;
155 }
156}
157
158
159#ifndef _WIN32
160/*
161 * Run execve() inside a fork(). Designed to replicate the semantics of system() but
162 * in a safer way that doesn't require the invocation of a shell or the risks
163 * associated with formatting and parsing a command line.
164 * Returns the exit status of child, OPENVPN_EXECVE_NOT_ALLOWED if openvpn_execve_allowed()
165 * returns false, or OPENVPN_EXECVE_ERROR on other errors.
166 */
167int
168openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags)
169{
170 struct gc_arena gc = gc_new();
171 int ret = OPENVPN_EXECVE_ERROR;
172 static bool warn_shown = false;
173
174 if (a && a->argv[0])
175 {
176#if defined(ENABLE_FEATURE_EXECVE)
177 if (openvpn_execve_allowed(flags))
178 {
179 char *const *envp = (char *const *)make_env_array(es, true, &gc);
180
181 const pid_t pid = fork();
182 if (pid == (pid_t)0) /* child side */
183 {
184 const char *cmd = a->argv[0];
185 char *const *argv = a->argv;
186 execve(cmd, argv, envp);
188 }
189 else if (pid < (pid_t)0) /* fork failed */
190 {
191 msg(M_ERR, "openvpn_execve: unable to fork");
192 }
193 else if (flags & S_NOWAITPID)
194 {
195 ret = pid;
196 }
197 else /* parent side */
198 {
199 if (waitpid(pid, &ret, 0) != pid)
200 {
202 }
203 }
204 }
205 else
206 {
208 if (!warn_shown && (script_security() < SSEC_SCRIPTS))
209 {
211 warn_shown = true;
212 }
213 }
214#else /* if defined(ENABLE_FEATURE_EXECVE) */
215 msg(M_WARN, "openvpn_execve: execve function not available");
216#endif /* if defined(ENABLE_FEATURE_EXECVE) */
217 }
218 else
219 {
220 msg(M_FATAL, "openvpn_execve: called with empty argv");
221 }
222
223 gc_free(&gc);
224 return ret;
225}
226#endif /* ifndef _WIN32 */
227
228/*
229 * Wrapper around openvpn_execve
230 */
231int
232openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsigned int flags,
233 const char *error_message)
234{
235 struct gc_arena gc = gc_new();
236 const int stat = openvpn_execve(a, es, flags);
237 int ret = false;
238
239 if (flags & S_EXITCODE)
240 {
241 ret = platform_ret_code(stat);
242 if (ret != -1)
243 {
244 goto done;
245 }
246 }
247 else if (flags & S_NOWAITPID && (stat > 0))
248 {
249 ret = stat;
250 goto done;
251 }
252 else if (platform_system_ok(stat))
253 {
254 ret = true;
255 goto done;
256 }
257 if (error_message)
258 {
259 msg(((flags & S_FATAL) ? M_FATAL : M_WARN), "%s: %s", error_message,
260 system_error_message(stat, &gc));
261 }
262done:
263 gc_free(&gc);
264
265 return ret;
266}
267
268/*
269 * Run execve() inside a fork(), duping stdout. Designed to replicate the semantics of popen() but
270 * in a safer way that doesn't require the invocation of a shell or the risks
271 * associated with formatting and parsing a command line.
272 */
273int
274openvpn_popen(const struct argv *a, const struct env_set *es)
275{
276 struct gc_arena gc = gc_new();
277 int ret = -1;
278
279 if (a && a->argv[0])
280 {
281#if defined(ENABLE_FEATURE_EXECVE)
282 static bool warn_shown = false;
284 {
285 char *const *envp = (char *const *)make_env_array(es, true, &gc);
286 const char *cmd = a->argv[0];
287 int pipe_stdout[2];
288
289 if (pipe(pipe_stdout) == 0)
290 {
291 const pid_t pid = fork();
292 if (pid == (pid_t)0) /* child side */
293 {
294 char *const *argv = a->argv;
295
296 close(pipe_stdout[0]); /* Close read end */
297 dup2(pipe_stdout[1], 1);
298 execve(cmd, argv, envp);
300 }
301 else if (pid > (pid_t)0) /* parent side */
302 {
303 int status = 0;
304
305 close(pipe_stdout[1]); /* Close write end */
306 waitpid(pid, &status, 0);
307 ret = pipe_stdout[0];
308 }
309 else /* fork failed */
310 {
311 close(pipe_stdout[0]);
312 close(pipe_stdout[1]);
313 msg(M_ERR, "openvpn_popen: unable to fork %s", cmd);
314 }
315 }
316 else
317 {
318 msg(M_WARN, "openvpn_popen: unable to create stdout pipe for %s", cmd);
319 ret = -1;
320 }
321 }
322 else if (!warn_shown && (script_security() < SSEC_SCRIPTS))
323 {
325 warn_shown = true;
326 }
327#else /* if defined(ENABLE_FEATURE_EXECVE) */
328 msg(M_WARN, "openvpn_popen: execve function not available");
329#endif /* if defined(ENABLE_FEATURE_EXECVE) */
330 }
331 else
332 {
333 msg(M_FATAL, "openvpn_popen: called with empty argv");
334 }
335
336 gc_free(&gc);
337 return ret;
338}
bool buf_printf(struct buffer *buf, const char *format,...)
printf-style append to a buffer with overflow check.
Definition buffer.c:226
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Allocate a buffer of the given size under garbage collection.
Definition buffer.c:77
Buffer management functions and garbage collection.
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1976
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1960
#define SCRIPT_SECURITY_WARNING
Definition common.h:99
const char ** make_env_array(const struct env_set *es, const bool check_allowed, struct gc_arena *gc)
Definition env_set.c:438
static SERVICE_STATUS status
Definition interactive.c:52
#define M_FATAL
Definition error.h:90
#define M_ERR
Definition error.h:106
#define msg(flags,...)
Definition error.h:152
unsigned int msglvl_t
Definition error.h:77
#define M_WARN
Definition error.h:92
int platform_ret_code(int stat)
Return an exit code if valid and between 0 and 255, -1 otherwise.
Definition platform.c:424
bool platform_system_ok(int stat)
interpret the status code returned by execve()
Definition platform.c:413
bool openvpn_execve_allowed(const unsigned int flags)
void script_security_set(int level)
Definition run_command.c:48
int openvpn_popen(const struct argv *a, const struct env_set *es)
static const char * system_error_message(int stat, struct gc_arena *gc)
Definition run_command.c:57
int openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message)
int script_security(void)
Definition run_command.c:42
bool openvpn_waitpid_check(pid_t pid, const char *msg_prefix, msglvl_t msglevel)
Checks if a running process is still running.
static int script_security_level
Definition run_command.c:39
#define S_FATAL
Definition run_command.h:50
#define SSEC_SCRIPTS
allow calling of built-in programs and user-defined scripts
Definition run_command.h:35
#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:53
#define OPENVPN_EXECVE_ERROR
Definition run_command.h:40
#define S_SCRIPT
Definition run_command.h:49
#define OPENVPN_EXECVE_NOT_ALLOWED
Definition run_command.h:41
#define S_NOWAITPID
instead of waiting for child process to exit and report the status, return the pid of the child proce...
Definition run_command.h:56
#define SSEC_BUILT_IN
only call built-in programs such as ifconfig, route, netsh, etc.
Definition run_command.h:33
#define OPENVPN_EXECVE_FAILURE
Definition run_command.h:42
Definition argv.h:35
char ** argv
Definition argv.h:39
Wrapper structure for dynamically allocated memory.
Definition buffer.h:71
uint8_t * data
Pointer to the allocated memory.
Definition buffer.h:78
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
struct gc_arena gc
Definition test_ssl.c:122
int openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags)
Definition win32.c:1042