OpenVPN
platform.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-2018 OpenVPN 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 "crypto.h"
34 #include "error.h"
35 #include "misc.h"
36 #include "win32.h"
37 
38 #include "memdbg.h"
39 
40 #include "platform.h"
41 
42 /* Redefine the top level directory of the filesystem
43  * to restrict access to files for security */
44 void
45 platform_chroot(const char *path)
46 {
47  if (path)
48  {
49 #ifdef HAVE_CHROOT
50  const char *top = "/";
51  if (chroot(path))
52  {
53  msg(M_ERR, "chroot to '%s' failed", path);
54  }
55  if (platform_chdir(top))
56  {
57  msg(M_ERR, "cd to '%s' failed", top);
58  }
59  msg(M_INFO, "chroot to '%s' and cd to '%s' succeeded", path, top);
60 #else /* ifdef HAVE_CHROOT */
61  msg(M_FATAL, "Sorry but I can't chroot to '%s' because this operating system doesn't appear to support the chroot() system call", path);
62 #endif
63  }
64 }
65 
66 /* Get/Set UID of process */
67 
68 bool
69 platform_user_get(const char *username, struct platform_state_user *state)
70 {
71  bool ret = false;
72  CLEAR(*state);
73  if (username)
74  {
75 #if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID)
76  state->pw = getpwnam(username);
77  if (!state->pw)
78  {
79  msg(M_ERR, "failed to find UID for user %s", username);
80  }
81  state->username = username;
82  ret = true;
83 #else /* if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID) */
84  msg(M_FATAL, "cannot get UID for user %s -- platform lacks getpwname() or setuid() system calls", username);
85 #endif
86  }
87  return ret;
88 }
89 
90 void
92 {
93 #if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID)
94  if (state->username && state->pw)
95  {
96  if (setuid(state->pw->pw_uid))
97  {
98  msg(M_ERR, "setuid('%s') failed", state->username);
99  }
100  msg(M_INFO, "UID set to %s", state->username);
101  }
102 #endif
103 }
104 
105 /* Get/Set GID of process */
106 
107 bool
108 platform_group_get(const char *groupname, struct platform_state_group *state)
109 {
110  bool ret = false;
111  CLEAR(*state);
112  if (groupname)
113  {
114 #if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID)
115  state->gr = getgrnam(groupname);
116  if (!state->gr)
117  {
118  msg(M_ERR, "failed to find GID for group %s", groupname);
119  }
120  state->groupname = groupname;
121  ret = true;
122 #else /* if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID) */
123  msg(M_FATAL, "cannot get GID for group %s -- platform lacks getgrnam() or setgid() system calls", groupname);
124 #endif
125  }
126  return ret;
127 }
128 
129 void
131 {
132 #if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID)
133  if (state->groupname && state->gr)
134  {
135  if (setgid(state->gr->gr_gid))
136  {
137  msg(M_ERR, "setgid('%s') failed", state->groupname);
138  }
139  msg(M_INFO, "GID set to %s", state->groupname);
140 #ifdef HAVE_SETGROUPS
141  {
142  gid_t gr_list[1];
143  gr_list[0] = state->gr->gr_gid;
144  if (setgroups(1, gr_list))
145  {
146  msg(M_ERR, "setgroups('%s') failed", state->groupname);
147  }
148  }
149 #endif
150  }
151 #endif
152 }
153 
154 /* Change process priority */
155 void
156 platform_nice(int niceval)
157 {
158  if (niceval)
159  {
160 #ifdef HAVE_NICE
161  errno = 0;
162  if (nice(niceval) < 0 && errno != 0)
163  {
164  msg(M_WARN | M_ERRNO, "WARNING: nice %d failed", niceval);
165  }
166  else
167  {
168  msg(M_INFO, "nice %d succeeded", niceval);
169  }
170 #else /* ifdef HAVE_NICE */
171  msg(M_WARN, "WARNING: nice %d failed (function not implemented)", niceval);
172 #endif
173  }
174 }
175 
176 /* Get current PID */
177 unsigned int
179 {
180 #ifdef _WIN32
181  return (unsigned int) GetCurrentProcessId();
182 #else
183 #ifdef HAVE_GETPID
184  return (unsigned int) getpid();
185 #else
186  return 0;
187 #endif
188 #endif
189 }
190 
191 /* Disable paging */
192 void
193 platform_mlockall(bool print_msg)
194 {
195 #ifdef HAVE_MLOCKALL
196  if (mlockall(MCL_CURRENT | MCL_FUTURE))
197  {
198  msg(M_WARN | M_ERRNO, "WARNING: mlockall call failed");
199  }
200  else if (print_msg)
201  {
202  msg(M_INFO, "mlockall call succeeded");
203  }
204 #else /* ifdef HAVE_MLOCKALL */
205  msg(M_WARN, "WARNING: mlockall call failed (function not implemented)");
206 #endif
207 }
208 
209 /*
210  * Wrapper for chdir library function
211  */
212 int
213 platform_chdir(const char *dir)
214 {
215 #ifdef HAVE_CHDIR
216 #ifdef _WIN32
217  int res;
218  struct gc_arena gc = gc_new();
219  res = _wchdir(wide_string(dir, &gc));
220  gc_free(&gc);
221  return res;
222 #else /* ifdef _WIN32 */
223  return chdir(dir);
224 #endif
225 #else /* ifdef HAVE_CHDIR */
226  return -1;
227 #endif
228 }
229 
230 /*
231  * convert execve() return into a success/failure value
232  */
233 bool
235 {
236 #ifdef _WIN32
237  return stat == 0;
238 #else
239  return stat != -1 && WIFEXITED(stat) && WEXITSTATUS(stat) == 0;
240 #endif
241 }
242 
243 int
244 platform_access(const char *path, int mode)
245 {
246 #ifdef _WIN32
247  struct gc_arena gc = gc_new();
248  int ret = _waccess(wide_string(path, &gc), mode & ~X_OK);
249  gc_free(&gc);
250  return ret;
251 #else
252  return access(path, mode);
253 #endif
254 }
255 
256 /*
257  * Go to sleep for n milliseconds.
258  */
259 void
261 {
262 #ifdef _WIN32
263  Sleep(n);
264 #else
265  struct timeval tv;
266  tv.tv_sec = n / 1000;
267  tv.tv_usec = (n % 1000) * 1000;
268  select(0, NULL, NULL, NULL, &tv);
269 #endif
270 }
271 
272 /*
273  * Go to sleep indefinitely.
274  */
275 void
277 {
278 #ifdef _WIN32
279  ASSERT(0);
280 #else
281  select(0, NULL, NULL, NULL, NULL);
282 #endif
283 }
284 
285 /* delete a file, return true if succeeded */
286 bool
287 platform_unlink(const char *filename)
288 {
289 #if defined(_WIN32)
290  struct gc_arena gc = gc_new();
291  BOOL ret = DeleteFileW(wide_string(filename, &gc));
292  gc_free(&gc);
293  return (ret != 0);
294 #elif defined(HAVE_UNLINK)
295  return (unlink(filename) == 0);
296 #else /* if defined(_WIN32) */
297  return false;
298 #endif
299 }
300 
301 FILE *
302 platform_fopen(const char *path, const char *mode)
303 {
304 #ifdef _WIN32
305  struct gc_arena gc = gc_new();
306  FILE *f = _wfopen(wide_string(path, &gc), wide_string(mode, &gc));
307  gc_free(&gc);
308  return f;
309 #else
310  return fopen(path, mode);
311 #endif
312 }
313 
314 int
315 platform_open(const char *path, int flags, int mode)
316 {
317 #ifdef _WIN32
318  struct gc_arena gc = gc_new();
319  int fd = _wopen(wide_string(path, &gc), flags, mode);
320  gc_free(&gc);
321  return fd;
322 #else
323  return open(path, flags, mode);
324 #endif
325 }
326 
327 int
328 platform_stat(const char *path, platform_stat_t *buf)
329 {
330 #ifdef _WIN32
331  struct gc_arena gc = gc_new();
332  int res = _wstat(wide_string(path, &gc), buf);
333  gc_free(&gc);
334  return res;
335 #else
336  return stat(path, buf);
337 #endif
338 }
339 
340 /* create a temporary filename in directory */
341 const char *
342 platform_create_temp_file(const char *directory, const char *prefix, struct gc_arena *gc)
343 {
344  int fd;
345  const char *retfname = NULL;
346  unsigned int attempts = 0;
347  char fname[256] = { 0 };
348  const char *fname_fmt = PACKAGE "_%.*s_%08lx%08lx.tmp";
349  const int max_prefix_len = sizeof(fname) - (sizeof(PACKAGE) + 7 + (2 * 8));
350 
351  while (attempts < 6)
352  {
353  ++attempts;
354 
355  if (!openvpn_snprintf(fname, sizeof(fname), fname_fmt, max_prefix_len,
356  prefix, (unsigned long) get_random(),
357  (unsigned long) get_random()))
358  {
359  msg(M_WARN, "ERROR: temporary filename too long");
360  return NULL;
361  }
362 
363  retfname = platform_gen_path(directory, fname, gc);
364  if (!retfname)
365  {
366  msg(M_WARN, "Failed to create temporary filename and path");
367  return NULL;
368  }
369 
370  /* Atomically create the file. Errors out if the file already
371  * exists. */
372  fd = platform_open(retfname, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
373  if (fd != -1)
374  {
375  close(fd);
376  return retfname;
377  }
378  else if (fd == -1 && errno != EEXIST)
379  {
380  /* Something else went wrong, no need to retry. */
381  msg(M_WARN | M_ERRNO, "Could not create temporary file '%s'",
382  retfname);
383  return NULL;
384  }
385  }
386 
387  msg(M_WARN, "Failed to create temporary file after %i attempts", attempts);
388  return NULL;
389 }
390 
391 /*
392  * Put a directory and filename together.
393  */
394 const char *
395 platform_gen_path(const char *directory, const char *filename,
396  struct gc_arena *gc)
397 {
398 #ifdef _WIN32
399  const int CC_PATH_RESERVED = CC_LESS_THAN|CC_GREATER_THAN|CC_COLON
401 #else
402  const int CC_PATH_RESERVED = CC_SLASH;
403 #endif
404 
405  if (!gc)
406  {
407  return NULL; /* Would leak memory otherwise */
408  }
409 
410  const char *safe_filename = string_mod_const(filename, CC_PRINT, CC_PATH_RESERVED, '_', gc);
411 
412  if (safe_filename
413  && strcmp(safe_filename, ".")
414  && strcmp(safe_filename, "..")
415 #ifdef _WIN32
416  && win_safe_filename(safe_filename)
417 #endif
418  )
419  {
420  const size_t outsize = strlen(safe_filename) + (directory ? strlen(directory) : 0) + 16;
421  struct buffer out = alloc_buf_gc(outsize, gc);
422  char dirsep[2];
423 
424  dirsep[0] = OS_SPECIFIC_DIRSEP;
425  dirsep[1] = '\0';
426 
427  if (directory)
428  {
429  buf_printf(&out, "%s%s", directory, dirsep);
430  }
431  buf_printf(&out, "%s", safe_filename);
432 
433  return BSTR(&out);
434  }
435  else
436  {
437  return NULL;
438  }
439 }
440 
441 bool
442 platform_absolute_pathname(const char *pathname)
443 {
444  if (pathname)
445  {
446  const int c = pathname[0];
447 #ifdef _WIN32
448  return c == '\\' || (isalpha(c) && pathname[1] == ':' && pathname[2] == '\\');
449 #else
450  return c == '/';
451 #endif
452  }
453  else
454  {
455  return false;
456  }
457 }
458 
459 /* return true if filename can be opened for read */
460 bool
461 platform_test_file(const char *filename)
462 {
463  bool ret = false;
464  if (filename)
465  {
466  FILE *fp = platform_fopen(filename, "r");
467  if (fp)
468  {
469  fclose(fp);
470  ret = true;
471  }
472  else
473  {
474  if (openvpn_errno() == EACCES)
475  {
476  msg( M_WARN | M_ERRNO, "Could not access file '%s'", filename);
477  }
478  }
479  }
480 
481  dmsg(D_TEST_FILE, "TEST FILE '%s' [%d]",
482  filename ? filename : "UNDEF",
483  ret);
484 
485  return ret;
486 }
unsigned int platform_getpid(void)
Definition: platform.c:178
WCHAR * wide_string(const char *utf8, struct gc_arena *gc)
Definition: win32.c:1154
void platform_group_set(const struct platform_state_group *state)
Definition: platform.c:130
bool platform_group_get(const char *groupname, struct platform_state_group *state)
Definition: platform.c:108
void platform_nice(int niceval)
Definition: platform.c:156
#define D_TEST_FILE
Definition: errlevel.h:133
#define OS_SPECIFIC_DIRSEP
Definition: syshead.h:478
#define M_INFO
Definition: errlevel.h:55
void platform_sleep_until_signal(void)
Definition: platform.c:276
#define CC_LESS_THAN
Definition: buffer.h:912
static void gc_free(struct gc_arena *a)
Definition: buffer.h:999
bool platform_user_get(const char *username, struct platform_state_user *state)
Definition: platform.c:69
#define CC_PRINT
Definition: buffer.h:891
#define ASSERT(x)
Definition: error.h:221
#define M_FATAL
Definition: error.h:94
bool buf_printf(struct buffer *buf, const char *format,...)
Definition: buffer.c:243
#define CLEAR(x)
Definition: basic.h:33
#define X_OK
Definition: config-msvc.h:111
#define CC_BACKSLASH
Definition: buffer.h:900
void platform_user_set(const struct platform_state_user *state)
Definition: platform.c:91
bool platform_unlink(const char *filename)
Definition: platform.c:287
#define openvpn_errno()
Definition: error.h:74
#define S_IWUSR
Definition: config-msvc.h:108
void platform_mlockall(bool print_msg)
Definition: platform.c:193
list flags
bool platform_system_ok(int stat)
Definition: platform.c:234
FILE * platform_fopen(const char *path, const char *mode)
Definition: platform.c:302
bool openvpn_snprintf(char *str, size_t size, const char *format,...)
Definition: buffer.c:297
#define CC_ASTERISK
Definition: buffer.h:916
int platform_open(const char *path, int flags, int mode)
Definition: platform.c:315
void platform_sleep_milliseconds(unsigned int n)
Definition: platform.c:260
string f
Definition: http-client.py:6
static struct gc_arena gc_new(void)
Definition: buffer.h:991
bool platform_test_file(const char *filename)
Return true if filename can be opened for read.
Definition: platform.c:461
#define M_ERR
Definition: error.h:110
#define S_IRUSR
Definition: config-msvc.h:107
const char * platform_gen_path(const char *directory, const char *filename, struct gc_arena *gc)
Put a directory and filename together.
Definition: platform.c:395
#define dmsg
Definition: error.h:174
int platform_stat(const char *path, platform_stat_t *buf)
Definition: platform.c:328
#define CC_QUESTION_MARK
Definition: buffer.h:915
const char * string_mod_const(const char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace, struct gc_arena *gc)
Definition: buffer.c:1088
#define CC_GREATER_THAN
Definition: buffer.h:913
#define CC_SLASH
Definition: buffer.h:906
#define CC_PIPE
Definition: buffer.h:914
#define msg
Definition: error.h:173
int platform_chdir(const char *dir)
Definition: platform.c:213
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition: buffer.c:88
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
const char * platform_create_temp_file(const char *directory, const char *prefix, struct gc_arena *gc)
Create a temporary file in directory, returns the filename of the created file.
Definition: platform.c:342
#define BSTR(buf)
Definition: buffer.h:129
#define PACKAGE
Definition: config.h:724
long int get_random(void)
Definition: crypto.c:1761
void platform_chroot(const char *path)
Definition: platform.c:45
#define M_ERRNO
Definition: error.h:99
#define CC_COLON
Definition: buffer.h:905
bool platform_absolute_pathname(const char *pathname)
Return true if pathname is absolute.
Definition: platform.c:442
struct _stat platform_stat_t
Definition: platform.h:141
#define M_WARN
Definition: error.h:96
#define CC_DOUBLE_QUOTE
Definition: buffer.h:908
bool win_safe_filename(const char *fn)
Definition: win32.c:936
int platform_access(const char *path, int mode)
Definition: platform.c:244