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-2025 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 }
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, int msglevel)
111{
112 if (pid == 0)
113 {
114 return false;
115 }
116 int status;
118 if (pidret != pid)
119 {
120 return true;
121 }
122
123 if (WIFEXITED(status))
124 {
126
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 const char *cmd = a->argv[0];
180 char *const *argv = a->argv;
181 char *const *envp = (char *const *)make_env_array(es, true, &gc);
182 pid_t pid;
183
184 pid = fork();
185 if (pid == (pid_t)0) /* child side */
186 {
187 execve(cmd, argv, envp);
189 }
190 else if (pid < (pid_t)0) /* fork failed */
191 {
192 msg(M_ERR, "openvpn_execve: unable to fork");
193 }
194 else if (flags & S_NOWAITPID)
195 {
196 ret = pid;
197 }
198 else /* parent side */
199 {
200 if (waitpid(pid, &ret, 0) != pid)
201 {
203 }
204 }
205 }
206 else
207 {
209 if (!warn_shown && (script_security() < SSEC_SCRIPTS))
210 {
212 warn_shown = true;
213 }
214 }
215#else /* if defined(ENABLE_FEATURE_EXECVE) */
216 msg(M_WARN, "openvpn_execve: execve function not available");
217#endif /* if defined(ENABLE_FEATURE_EXECVE) */
218 }
219 else
220 {
221 msg(M_FATAL, "openvpn_execve: called with empty argv");
222 }
223
224 gc_free(&gc);
225 return ret;
226}
227#endif /* ifndef _WIN32 */
228
229/*
230 * Wrapper around openvpn_execve
231 */
232int
233openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsigned int flags,
234 const char *error_message)
235{
236 struct gc_arena gc = gc_new();
237 const int stat = openvpn_execve(a, es, flags);
238 int ret = false;
239
240 if (flags & S_EXITCODE)
241 {
242 ret = platform_ret_code(stat);
243 if (ret != -1)
244 {
245 goto done;
246 }
247 }
248 else if (flags & S_NOWAITPID && (stat > 0))
249 {
250 ret = stat;
251 goto done;
252 }
253 else if (platform_system_ok(stat))
254 {
255 ret = true;
256 goto done;
257 }
258 if (error_message)
259 {
260 msg(((flags & S_FATAL) ? M_FATAL : M_WARN), "%s: %s", error_message,
261 system_error_message(stat, &gc));
262 }
263done:
264 gc_free(&gc);
265
266 return ret;
267}
268
269/*
270 * Run execve() inside a fork(), duping stdout. Designed to replicate the semantics of popen() but
271 * in a safer way that doesn't require the invocation of a shell or the risks
272 * associated with formatting and parsing a command line.
273 */
274int
275openvpn_popen(const struct argv *a, const struct env_set *es)
276{
277 struct gc_arena gc = gc_new();
278 int ret = -1;
279
280 if (a && a->argv[0])
281 {
282#if defined(ENABLE_FEATURE_EXECVE)
283 static bool warn_shown = false;
285 {
286 const char *cmd = a->argv[0];
287 char *const *argv = a->argv;
288 char *const *envp = (char *const *)make_env_array(es, true, &gc);
289 pid_t pid;
290 int pipe_stdout[2];
291
292 if (pipe(pipe_stdout) == 0)
293 {
294 pid = fork();
295 if (pid == (pid_t)0) /* child side */
296 {
297 close(pipe_stdout[0]); /* Close read end */
298 dup2(pipe_stdout[1], 1);
299 execve(cmd, argv, envp);
301 }
302 else if (pid > (pid_t)0) /* parent side */
303 {
304 int status = 0;
305
306 close(pipe_stdout[1]); /* Close write end */
307 waitpid(pid, &status, 0);
308 ret = pipe_stdout[0];
309 }
310 else /* fork failed */
311 {
312 close(pipe_stdout[0]);
313 close(pipe_stdout[1]);
314 msg(M_ERR, "openvpn_popen: unable to fork %s", cmd);
315 }
316 }
317 else
318 {
319 msg(M_WARN, "openvpn_popen: unable to create stdout pipe for %s", cmd);
320 ret = -1;
321 }
322 }
323 else if (!warn_shown && (script_security() < SSEC_SCRIPTS))
324 {
326 warn_shown = true;
327 }
328#else /* if defined(ENABLE_FEATURE_EXECVE) */
329 msg(M_WARN, "openvpn_popen: execve function not available");
330#endif /* if defined(ENABLE_FEATURE_EXECVE) */
331 }
332 else
333 {
334 msg(M_FATAL, "openvpn_popen: called with empty argv");
335 }
336
337 gc_free(&gc);
338 return ret;
339}
bool buf_printf(struct buffer *buf, const char *format,...)
Definition buffer.c:241
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition buffer.c:89
static void gc_free(struct gc_arena *a)
Definition buffer.h:1015
static struct gc_arena gc_new(void)
Definition buffer.h:1007
#define SCRIPT_SECURITY_WARNING
Definition common.h:97
const char ** make_env_array(const struct env_set *es, const bool check_allowed, struct gc_arena *gc)
Definition env_set.c:440
static SERVICE_STATUS status
Definition interactive.c:51
#define M_FATAL
Definition error.h:88
#define M_ERR
Definition error.h:104
#define msg(flags,...)
Definition error.h:150
#define M_WARN
Definition error.h:90
int platform_ret_code(int stat)
Return an exit code if valid and between 0 and 255, -1 otherwise.
Definition platform.c:428
bool platform_system_ok(int stat)
interpret the status code returned by execve()
Definition platform.c:417
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
bool openvpn_waitpid_check(pid_t pid, const char *msg_prefix, int msglevel)
Checks if a running process is still running.
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
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:60
uint8_t * data
Pointer to the allocated memory.
Definition buffer.h:67
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:65
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:116
struct env_set * es
struct gc_arena gc
Definition test_ssl.c:154
int openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags)
Definition win32.c:994