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 */
40static int script_security_level = SSEC_BUILT_IN; /* GLOBAL */
41
42int
44{
46}
47
48void
50{
52}
53
54/*
55 * Generate an error message based on the status code returned by openvpn_execve().
56 */
57static const char *
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 }
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#ifndef WIN32
110bool
111openvpn_waitpid_check(pid_t pid, const char *msg_prefix, int msglevel)
112{
113 if (pid == 0)
114 {
115 return false;
116 }
117 int status;
119 if (pidret != pid)
120 {
121 return true;
122 }
123
124 if (WIFEXITED(status))
125 {
127
129 {
130 msg(msglevel, "%scould not execute external program (exit code 127)",
131 msg_prefix);
132 }
133 else
134 {
135 msg(msglevel, "%sexternal program exited with error status: %d",
136 msg_prefix, exitcode);
137 }
138
139 }
140 else if (WIFSIGNALED(status))
141 {
142 msg(msglevel, "%sexternal program received signal %d",
143 msg_prefix, WTERMSIG(status));
144 }
145
146 return false;
147}
148#endif /* ifndef WIN32 */
149
150bool
151openvpn_execve_allowed(const unsigned int flags)
152{
153 if (flags & S_SCRIPT)
154 {
155 return script_security() >= SSEC_SCRIPTS;
156 }
157 else
158 {
159 return script_security() >= SSEC_BUILT_IN;
160 }
161}
162
163
164#ifndef _WIN32
165/*
166 * Run execve() inside a fork(). Designed to replicate the semantics of system() but
167 * in a safer way that doesn't require the invocation of a shell or the risks
168 * associated with formatting and parsing a command line.
169 * Returns the exit status of child, OPENVPN_EXECVE_NOT_ALLOWED if openvpn_execve_allowed()
170 * returns false, or OPENVPN_EXECVE_ERROR on other errors.
171 */
172int
173openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags)
174{
175 struct gc_arena gc = gc_new();
176 int ret = OPENVPN_EXECVE_ERROR;
177 static bool warn_shown = false;
178
179 if (a && a->argv[0])
180 {
181#if defined(ENABLE_FEATURE_EXECVE)
182 if (openvpn_execve_allowed(flags))
183 {
184 const char *cmd = a->argv[0];
185 char *const *argv = a->argv;
186 char *const *envp = (char *const *)make_env_array(es, true, &gc);
187 pid_t pid;
188
189 pid = fork();
190 if (pid == (pid_t)0) /* child side */
191 {
192 execve(cmd, argv, envp);
194 }
195 else if (pid < (pid_t)0) /* fork failed */
196 {
197 msg(M_ERR, "openvpn_execve: unable to fork");
198 }
199 else if (flags & S_NOWAITPID)
200 {
201 ret = pid;
202 }
203 else /* parent side */
204 {
205 if (waitpid(pid, &ret, 0) != pid)
206 {
208 }
209 }
210 }
211 else
212 {
214 if (!warn_shown && (script_security() < SSEC_SCRIPTS))
215 {
217 warn_shown = true;
218 }
219 }
220#else /* if defined(ENABLE_FEATURE_EXECVE) */
221 msg(M_WARN, "openvpn_execve: execve function not available");
222#endif /* if defined(ENABLE_FEATURE_EXECVE) */
223 }
224 else
225 {
226 msg(M_FATAL, "openvpn_execve: called with empty argv");
227 }
228
229 gc_free(&gc);
230 return ret;
231}
232#endif /* ifndef _WIN32 */
233
234/*
235 * Wrapper around openvpn_execve
236 */
237int
238openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message)
239{
240 struct gc_arena gc = gc_new();
241 const int stat = openvpn_execve(a, es, flags);
242 int ret = false;
243
244 if (flags & S_EXITCODE)
245 {
246 ret = platform_ret_code(stat);
247 if (ret != -1)
248 {
249 goto done;
250 }
251 }
252 else if (flags & S_NOWAITPID && (stat > 0))
253 {
254 ret = stat;
255 goto done;
256 }
257 else if (platform_system_ok(stat))
258 {
259 ret = true;
260 goto done;
261 }
262 if (error_message)
263 {
264 msg(((flags & S_FATAL) ? M_FATAL : M_WARN), "%s: %s",
265 error_message,
266 system_error_message(stat, &gc));
267 }
268done:
269 gc_free(&gc);
270
271 return ret;
272}
273
274/*
275 * Run execve() inside a fork(), duping stdout. Designed to replicate the semantics of popen() but
276 * in a safer way that doesn't require the invocation of a shell or the risks
277 * associated with formatting and parsing a command line.
278 */
279int
280openvpn_popen(const struct argv *a, const struct env_set *es)
281{
282 struct gc_arena gc = gc_new();
283 int ret = -1;
284
285 if (a && a->argv[0])
286 {
287#if defined(ENABLE_FEATURE_EXECVE)
288 static bool warn_shown = false;
290 {
291 const char *cmd = a->argv[0];
292 char *const *argv = a->argv;
293 char *const *envp = (char *const *)make_env_array(es, true, &gc);
294 pid_t pid;
295 int pipe_stdout[2];
296
297 if (pipe(pipe_stdout) == 0)
298 {
299 pid = fork();
300 if (pid == (pid_t)0) /* child side */
301 {
302 close(pipe_stdout[0]); /* Close read end */
303 dup2(pipe_stdout[1], 1);
304 execve(cmd, argv, envp);
306 }
307 else if (pid > (pid_t)0) /* parent side */
308 {
309 int status = 0;
310
311 close(pipe_stdout[1]); /* Close write end */
312 waitpid(pid, &status, 0);
313 ret = pipe_stdout[0];
314 }
315 else /* fork failed */
316 {
317 close(pipe_stdout[0]);
318 close(pipe_stdout[1]);
319 msg(M_ERR, "openvpn_popen: unable to fork %s", cmd);
320 }
321 }
322 else
323 {
324 msg(M_WARN, "openvpn_popen: unable to create stdout pipe for %s", cmd);
325 ret = -1;
326 }
327 }
328 else if (!warn_shown && (script_security() < SSEC_SCRIPTS))
329 {
331 warn_shown = true;
332 }
333#else /* if defined(ENABLE_FEATURE_EXECVE) */
334 msg(M_WARN, "openvpn_popen: execve function not available");
335#endif /* if defined(ENABLE_FEATURE_EXECVE) */
336 }
337 else
338 {
339 msg(M_FATAL, "openvpn_popen: called with empty argv");
340 }
341
342 gc_free(&gc);
343 return ret;
344}
bool buf_printf(struct buffer *buf, const char *format,...)
Definition buffer.c:240
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition buffer.c:88
static void gc_free(struct gc_arena *a)
Definition buffer.h:1033
static struct gc_arena gc_new(void)
Definition buffer.h:1025
#define SCRIPT_SECURITY_WARNING
Definition common.h:98
const char ** make_env_array(const struct env_set *es, const bool check_allowed, struct gc_arena *gc)
Definition env_set.c:421
static SERVICE_STATUS status
Definition interactive.c:53
#define M_FATAL
Definition error.h:89
#define M_ERR
Definition error.h:105
#define msg(flags,...)
Definition error.h:144
#define M_WARN
Definition error.h:91
int platform_ret_code(int stat)
Return an exit code if valid and between 0 and 255, -1 otherwise.
Definition platform.c:425
bool platform_system_ok(int stat)
interpret the status code returned by execve()
Definition platform.c:414
bool openvpn_execve_allowed(const unsigned int flags)
void script_security_set(int level)
Definition run_command.c:49
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:58
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:43
static int script_security_level
Definition run_command.c:40
#define S_FATAL
Definition run_command.h:46
#define SSEC_SCRIPTS
Definition run_command.h:33
#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
#define OPENVPN_EXECVE_ERROR
Definition run_command.h:36
#define S_SCRIPT
Definition run_command.h:45
#define OPENVPN_EXECVE_NOT_ALLOWED
Definition run_command.h:37
#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:52
#define SSEC_BUILT_IN
Definition run_command.h:32
#define OPENVPN_EXECVE_FAILURE
Definition run_command.h:38
Definition argv.h:35
char ** argv
Definition argv.h:39
Wrapper structure for dynamically allocated memory.
Definition buffer.h:61
uint8_t * data
Pointer to the allocated memory.
Definition buffer.h:68
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:66
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:117
struct env_set * es
struct gc_arena gc
Definition test_ssl.c:155
int openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags)
Definition win32.c:1001