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-2017 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
script_security(void)45 script_security(void)
46 {
47 return script_security_level;
48 }
49
50 void
script_security_set(int level)51 script_security_set(int level)
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 *
system_error_message(int stat,struct gc_arena * gc)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 {
66 case OPENVPN_EXECVE_NOT_ALLOWED:
67 buf_printf(&out, "disallowed by script-security setting");
68 break;
69
70 #ifdef _WIN32
71 case OPENVPN_EXECVE_ERROR:
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
80 case OPENVPN_EXECVE_ERROR:
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
openvpn_execve_allowed(const unsigned int flags)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
openvpn_execve(const struct argv * a,const struct env_set * es,const unsigned int flags)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);
154 exit(OPENVPN_EXECVE_FAILURE);
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 {
170 ret = OPENVPN_EXECVE_NOT_ALLOWED;
171 if (!warn_shown && (script_security() < SSEC_SCRIPTS))
172 {
173 msg(M_WARN, SCRIPT_SECURITY_WARNING);
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
openvpn_execve_check(const struct argv * a,const struct env_set * es,const unsigned int flags,const char * error_message)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
openvpn_popen(const struct argv * a,const struct env_set * es)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;
241 if (script_security() >= SSEC_BUILT_IN)
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);
257 exit(OPENVPN_EXECVE_FAILURE);
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 {
282 msg(M_WARN, SCRIPT_SECURITY_WARNING);
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 }
297