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