OpenVPN
check_file_access.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single 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 Inc <sales@openvpn.net>
9 * Copyright (C) 2008-2026 David Sommerseth <dazo@eurephia.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2
13 * as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, see <https://www.gnu.org/licenses/>.
22 */
23
24/*
25 * Check file/directory sanity
26 *
27 */
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32/* Expect people using the stripped down version to know what they do */
33#ifndef ENABLE_SMALL
34
35#include "syshead.h"
36
37#include "check_file_access.h"
38
39#include "argv.h"
40#include "buffer.h"
41#include "error.h"
42#include "options.h"
43#include "platform.h"
44#include "ssl_common.h"
45
46#include <string.h>
47
48#define CHKACC_FILE (1 << 0)
49#define CHKACC_DIRPATH (1 << 1)
50#define CHKACC_FILEXSTWR (1 << 2)
51#define CHKACC_ACPTSTDIN (1 << 3)
52#define CHKACC_PRIVATE (1 << 4)
53#define CHKACC_ACCEPT_URI (1 << 5)
55static bool
56check_file_access(const int type, const char *file, const int mode, const char *opt)
57{
58 int errcode = 0;
59
60 /* If no file configured, no errors to look for */
61 if (!file)
62 {
63 return false;
64 }
65
66 /* If stdin is allowed and the file name is 'stdin', then do no
67 * further checks as stdin is always available
68 */
69 if ((type & CHKACC_ACPTSTDIN) && streq(file, "stdin"))
70 {
71 return false;
72 }
73
74 /* file name is a URI if its first segment has ":" (i.e., before any "/")
75 * Then no checks done if CHKACC_ACCEPT_URI is set and the URI does not start with "file:"
76 */
77 if ((type & CHKACC_ACCEPT_URI) && strchr(file, ':'))
78 {
79 if (!strncmp(file, "file:", 5))
80 {
81 file += 5;
82 }
83 else if (!strchr(file, '/') || strchr(file, '/') > strchr(file, ':'))
84 {
85 return false;
86 }
87 }
88
89 /* Is the directory path leading to the given file accessible? */
90 if (type & CHKACC_DIRPATH)
91 {
92 char *fullpath =
93 string_alloc(file, NULL); /* POSIX dirname() implementation may modify its arguments */
94 const char *dirpath = dirname(fullpath);
95
96 if (platform_access(dirpath, mode | X_OK) != 0)
97 {
98 errcode = errno;
99 }
100 free(fullpath);
101 }
102
103 /* Is the file itself accessible? */
104 if (!errcode && (type & CHKACC_FILE) && (platform_access(file, mode) != 0))
105 {
106 errcode = errno;
107 }
108
109 /* If the file exists and is accessible, is it writable? */
110 if (!errcode && (type & CHKACC_FILEXSTWR) && (platform_access(file, F_OK) == 0))
111 {
112 if (platform_access(file, W_OK) != 0)
113 {
114 errcode = errno;
115 }
116 }
117
118 /* Warn if a given private file is group/others accessible. */
119 if (type & CHKACC_PRIVATE)
120 {
122 if (platform_stat(file, &st))
123 {
124 msg(M_WARN | M_ERRNO, "WARNING: cannot stat file '%s'", file);
125 }
126#ifndef _WIN32
127 else
128 {
129 if (st.st_mode & (S_IRWXG | S_IRWXO))
130 {
131 msg(M_WARN, "WARNING: file '%s' is group or others accessible", file);
132 }
133 }
134#endif
135 }
136
137 /* Scream if an error is found */
138 if (errcode > 0)
139 {
140 msg(M_NOPREFIX | M_OPTERR | M_ERRNO, "%s fails with '%s'", opt, file);
141 }
142
143 /* Return true if an error occurred */
144 return (errcode != 0 ? true : false);
145}
146
151static bool
152check_file_access_chroot(const char *chroot, const int type, const char *file, const int mode,
153 const char *opt)
154{
155 bool ret = false;
156
157 /* If no file configured, no errors to look for */
158 if (!file)
159 {
160 return false;
161 }
162
163 /* If chroot is set, look for the file/directory inside the chroot */
164 if (chroot)
165 {
166 struct gc_arena gc = gc_new();
167 struct buffer chroot_file;
168
169 chroot_file = prepend_dir(chroot, file, &gc);
170 ret = check_file_access(type, BSTR(&chroot_file), mode, opt);
171 gc_free(&gc);
172 }
173 else
174 {
175 /* No chroot in play, just call core file check function */
176 ret = check_file_access(type, file, mode, opt);
177 }
178 return ret;
179}
180
185static bool
186check_file_access_chroot_inline(bool is_inline, const char *chroot, const int type,
187 const char *file, const int mode, const char *opt)
188{
189 if (is_inline)
190 {
191 return false;
192 }
193
194 return check_file_access_chroot(chroot, type, file, mode, opt);
195}
196
201static bool
202check_file_access_inline(bool is_inline, const int type, const char *file, const int mode,
203 const char *opt)
204{
205 if (is_inline)
206 {
207 return false;
208 }
209
210 return check_file_access(type, file, mode, opt);
211}
212
213bool
214check_cmd_access(const char *command, const char *opt, const char *chroot)
215{
216 struct argv argv;
217 bool return_code;
218
219 /* If no command was set, there are no errors to look for */
220 if (!command)
221 {
222 return false;
223 }
224
225 /* Extract executable path and arguments */
226 argv = argv_new();
227 argv_parse_cmd(&argv, command);
228
229 /* if an executable is specified then check it; otherwise, complain */
230 if (argv.argv[0])
231 {
232 /* Scripts requires R_OK as well, but that might fail on binaries which
233 * only requires X_OK to function on Unix - a scenario not unlikely to
234 * be seen on suid binaries.
235 */
236 return_code = check_file_access_chroot(chroot, CHKACC_FILE, argv.argv[0], X_OK, opt);
237 }
238 else
239 {
240 msg(M_NOPREFIX | M_OPTERR, "%s fails with '%s': No path to executable.", opt, command);
241 return_code = true;
242 }
243
244 argv_free(&argv);
245
246 return return_code;
247}
248
249void
251{
252 bool errs = false;
253
254 /* ** SSL/TLS/crypto related files ** */
256 "--dh");
257
259 {
261 R_OK, "--ca");
262 }
263
265 "--capath");
266
268 options->cert_file, R_OK, "--cert");
269
271 options->extra_certs_file, R_OK, "--extra-certs");
272
274 {
277 options->priv_key_file, R_OK, "--key");
278 }
279
281 options->pkcs12_file, R_OK, "--pkcs12");
282
284 {
286 R_OK | X_OK, "--crl-verify directory");
287 }
288 else
289 {
290 errs |=
292 CHKACC_FILE, options->crl_file, R_OK, "--crl-verify");
293 }
294
296 {
297 errs |=
299 options->tls_export_peer_cert_dir, W_OK, "--tls-export-cert");
300 }
301
303 for (int i = 0; i < options->connection_list->len; ++i)
304 {
305 const struct connection_entry *ce = options->connection_list->array[i];
306
308 ce->tls_auth_file, R_OK, "--tls-auth");
310 ce->tls_crypt_file, R_OK, "--tls-crypt");
312 ce->tls_crypt_v2_file, R_OK, "--tls-crypt-v2");
313 }
314
315 errs |=
317 options->shared_secret_file, R_OK, "--secret");
318
320 R_OK | W_OK, "--replay-persist");
321
322 /* ** Password files ** */
324 options->key_pass_file, R_OK, "--askpass");
325#ifdef ENABLE_MANAGEMENT
326 errs |=
328 options->management_user_pass, R_OK, "--management user/password file");
329#endif /* ENABLE_MANAGEMENT */
332 options->auth_user_pass_file, R_OK, "--auth-user-pass");
333 /* ** System related ** */
334 errs |= check_file_access(CHKACC_FILE, options->chroot_dir, R_OK | X_OK, "--chroot directory");
336 "--writepid");
337
338 /* ** Log related ** */
340 "--status");
341
342 /* ** Config related ** */
344 R_OK | X_OK, "--client-config-dir");
346 R_OK | W_OK | X_OK, "Temporary directory (--tmp-dir)");
347
348 if (errs)
349 {
350 msg(M_USAGE, "Please correct these errors.");
351 }
352}
353
354#endif /* !ENABLE_SMALL */
void argv_parse_cmd(struct argv *argres, const char *cmdstr)
Parses a command string, tokenizes it and puts each element into a separate struct argv argument slot...
Definition argv.c:481
void argv_free(struct argv *a)
Frees all memory allocations allocated by the struct argv related functions.
Definition argv.c:101
struct argv argv_new(void)
Allocates a new struct argv and ensures it is initialised.
Definition argv.c:87
char * string_alloc(const char *str, struct gc_arena *gc)
Duplicate a string, allocating memory under garbage collection.
Definition buffer.c:616
Buffer management functions and garbage collection.
#define BSTR(buf)
Return the buffer content pointer cast to char *.
Definition buffer.h:151
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1912
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1896
#define CHKACC_PRIVATE
Warn if this (private) file is group/others accessible.
static bool check_file_access_chroot_inline(bool is_inline, const char *chroot, const int type, const char *file, const int mode, const char *opt)
A wrapper for check_file_access_chroot() that returns false immediately if the file is inline (and th...
bool check_cmd_access(const char *command, const char *opt, const char *chroot)
Verifies that the path in the "command" that comes after certain script options (e....
static bool check_file_access_chroot(const char *chroot, const int type, const char *file, const int mode, const char *opt)
A wrapper for check_file_access() which also takes a chroot directory.
#define CHKACC_DIRPATH
Check for directory presence where a file should reside.
#define CHKACC_ACPTSTDIN
If filename is stdin, it's allowed and "exists".
void options_postprocess_filechecks(struct options *options)
Sanity check of all file/dir options.
#define CHKACC_FILEXSTWR
If file exists, is it writable?
static bool check_file_access_inline(bool is_inline, const int type, const char *file, const int mode, const char *opt)
A wrapper for check_file_access() that returns false immediately if the file is inline (and therefore...
#define CHKACC_ACCEPT_URI
Do not check URIs, unless they start with file:
static bool check_file_access(const int type, const char *file, const int mode, const char *opt)
#define CHKACC_FILE
Check for a file/directory presence.
char * dirname(char *path)
#define MF_EXTERNAL_KEY
Definition manage.h:36
struct buffer prepend_dir(const char *dir, const char *path, struct gc_arena *gc)
Prepend a directory to a path.
Definition misc.c:782
#define M_OPTERR
Definition error.h:101
#define M_NOPREFIX
Definition error.h:98
#define M_USAGE
Definition error.h:107
#define msg(flags,...)
Definition error.h:152
#define ASSERT(x)
Definition error.h:219
#define M_WARN
Definition error.h:92
#define M_ERRNO
Definition error.h:95
#define streq(x, y)
Definition options.h:726
int platform_access(const char *path, int mode)
Definition platform.c:457
int platform_stat(const char *path, platform_stat_t *buf)
Definition platform.c:526
struct _stat platform_stat_t
Definition platform.h:118
Control Channel Common Data Structures.
#define SSLF_CRL_VERIFY_DIR
Definition ssl_common.h:428
Definition argv.h:35
char ** argv
Definition argv.h:39
Wrapper structure for dynamically allocated memory.
Definition buffer.h:71
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:76
Definition options.h:109
const char * tls_crypt_file
Definition options.h:172
const char * tls_crypt_v2_file
Definition options.h:177
bool tls_crypt_file_inline
Definition options.h:173
bool tls_auth_file_inline
Definition options.h:168
const char * tls_auth_file
Definition options.h:167
bool tls_crypt_v2_file_inline
Definition options.h:178
struct connection_entry ** array
Definition options.h:206
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
struct connection_list * connection_list
Definition options.h:295
const char * tmp_dir
Definition options.h:466
const char * tls_export_peer_cert_dir
Definition options.h:612
bool crl_file_inline
Definition options.h:616
bool pkcs12_file_inline
Definition options.h:605
const char * ca_file
Definition options.h:593
bool verify_hash_no_ca
Definition options.h:624
const char * status_file
Definition options.h:406
unsigned int ssl_flags
Definition options.h:625
bool dh_file_inline
Definition options.h:597
const char * packet_id_file
Definition options.h:587
const char * writepid
Definition options.h:384
const char * pkcs12_file
Definition options.h:604
const char * key_pass_file
Definition options.h:281
unsigned int management_flags
Definition options.h:459
const char * auth_user_pass_file
Definition options.h:560
const char * extra_certs_file
Definition options.h:600
bool priv_key_file_inline
Definition options.h:603
const char * crl_file
Definition options.h:615
bool auth_user_pass_file_inline
Definition options.h:561
const char * dh_file
Definition options.h:596
bool ca_file_inline
Definition options.h:594
const char * chroot_dir
Definition options.h:379
bool shared_secret_file_inline
Definition options.h:572
const char * ca_path
Definition options.h:595
const char * priv_key_file
Definition options.h:602
const char * cert_file
Definition options.h:598
bool cert_file_inline
Definition options.h:599
const char * management_user_pass
Definition options.h:448
const char * shared_secret_file
Definition options.h:571
const char * client_config_dir
Definition options.h:505
struct gc_arena gc
Definition test_ssl.c:122