1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "sandbox/linux/services/libc_urandom_override.h"
6
7#include <dlfcn.h>
8#include <pthread.h>
9#include <stdio.h>
10#include <sys/stat.h>
11#include <unistd.h>
12
13#include "base/logging.h"
14#include "base/posix/eintr_wrapper.h"
15#include "base/rand_util.h"
16
17// Note: this file is used by the zygote and nacl_helper.
18
19#if !defined(HAVE_XSTAT) && defined(LIBC_GLIBC)
20#define HAVE_XSTAT 1
21#endif
22
23namespace sandbox {
24
25static bool g_override_urandom = false;
26
27// TODO(sergeyu): Currently InitLibcUrandomOverrides() doesn't work properly
28// under ASAN - it crashes content_unittests. Make sure it works properly and
29// enable it here. http://crbug.com/123263
30#if !defined(ADDRESS_SANITIZER)
31static void InitLibcFileIOFunctions();
32static pthread_once_t g_libc_file_io_funcs_guard = PTHREAD_ONCE_INIT;
33#endif
34
35void InitLibcUrandomOverrides() {
36  // Make sure /dev/urandom is open.
37  base::GetUrandomFD();
38  g_override_urandom = true;
39
40#if !defined(ADDRESS_SANITIZER)
41  CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
42                           InitLibcFileIOFunctions));
43#endif
44}
45
46#if !defined(ADDRESS_SANITIZER)
47
48static const char kUrandomDevPath[] = "/dev/urandom";
49
50typedef FILE* (*FopenFunction)(const char* path, const char* mode);
51
52static FopenFunction g_libc_fopen = NULL;
53static FopenFunction g_libc_fopen64 = NULL;
54
55#if HAVE_XSTAT
56typedef int (*XstatFunction)(int version, const char *path, struct stat *buf);
57typedef int (*Xstat64Function)(int version, const char *path,
58                               struct stat64 *buf);
59
60static XstatFunction g_libc_xstat = NULL;
61static Xstat64Function g_libc_xstat64 = NULL;
62#else
63typedef int (*StatFunction)(const char *path, struct stat *buf);
64typedef int (*Stat64Function)(const char *path, struct stat64 *buf);
65
66static StatFunction g_libc_stat = NULL;
67static Stat64Function g_libc_stat64 = NULL;
68#endif  // HAVE_XSTAT
69
70// Find the libc's real fopen* and *stat* functions. This should only be
71// called once, and should be guarded by g_libc_file_io_funcs_guard.
72static void InitLibcFileIOFunctions() {
73  g_libc_fopen = reinterpret_cast<FopenFunction>(
74      dlsym(RTLD_NEXT, "fopen"));
75  g_libc_fopen64 = reinterpret_cast<FopenFunction>(
76      dlsym(RTLD_NEXT, "fopen64"));
77
78  if (!g_libc_fopen) {
79    LOG(FATAL) << "Failed to get fopen() from libc.";
80  } else if (!g_libc_fopen64) {
81#if !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
82    LOG(WARNING) << "Failed to get fopen64() from libc. Using fopen() instead.";
83#endif  // !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
84    g_libc_fopen64 = g_libc_fopen;
85  }
86
87#if HAVE_XSTAT
88  g_libc_xstat = reinterpret_cast<XstatFunction>(
89      dlsym(RTLD_NEXT, "__xstat"));
90  g_libc_xstat64 = reinterpret_cast<Xstat64Function>(
91      dlsym(RTLD_NEXT, "__xstat64"));
92
93  if (!g_libc_xstat) {
94    LOG(FATAL) << "Failed to get __xstat() from libc.";
95  }
96  if (!g_libc_xstat64) {
97    LOG(FATAL) << "Failed to get __xstat64() from libc.";
98  }
99#else
100  g_libc_stat = reinterpret_cast<StatFunction>(
101      dlsym(RTLD_NEXT, "stat"));
102  g_libc_stat64 = reinterpret_cast<Stat64Function>(
103      dlsym(RTLD_NEXT, "stat64"));
104
105  if (!g_libc_stat) {
106    LOG(FATAL) << "Failed to get stat() from libc.";
107  }
108  if (!g_libc_stat64) {
109    LOG(FATAL) << "Failed to get stat64() from libc.";
110  }
111#endif  // HAVE_XSTAT
112}
113
114// fopen() and fopen64() are intercepted here so that NSS can open
115// /dev/urandom to seed its random number generator. NSS is used by
116// remoting in the sendbox.
117
118// fopen() call may be redirected to fopen64() in stdio.h using
119// __REDIRECT(), which sets asm name for fopen() to "fopen64". This
120// means that we cannot override fopen() directly here. Instead the
121// the code below defines fopen_override() function with asm name
122// "fopen", so that all references to fopen() will resolve to this
123// function.
124__attribute__ ((__visibility__("default")))
125FILE* fopen_override(const char* path, const char* mode)  __asm__ ("fopen");
126
127__attribute__ ((__visibility__("default")))
128FILE* fopen_override(const char* path, const char* mode) {
129  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
130    int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
131    if (fd < 0) {
132      PLOG(ERROR) << "dup() failed.";
133      return NULL;
134    }
135    return fdopen(fd, mode);
136  } else {
137    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
138                             InitLibcFileIOFunctions));
139    return g_libc_fopen(path, mode);
140  }
141}
142
143__attribute__ ((__visibility__("default")))
144FILE* fopen64(const char* path, const char* mode) {
145  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
146    int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
147    if (fd < 0) {
148      PLOG(ERROR) << "dup() failed.";
149      return NULL;
150    }
151    return fdopen(fd, mode);
152  } else {
153    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
154                             InitLibcFileIOFunctions));
155    return g_libc_fopen64(path, mode);
156  }
157}
158
159// The stat() family of functions are subject to the same problem as
160// fopen(), so we have to use the same trick to override them.
161
162#if HAVE_XSTAT
163
164__attribute__ ((__visibility__("default")))
165int xstat_override(int version,
166                   const char *path,
167                   struct stat *buf)  __asm__ ("__xstat");
168
169__attribute__ ((__visibility__("default")))
170int xstat_override(int version, const char *path, struct stat *buf) {
171  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
172    int result = __fxstat(version, base::GetUrandomFD(), buf);
173    return result;
174  } else {
175    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
176                             InitLibcFileIOFunctions));
177    return g_libc_xstat(version, path, buf);
178  }
179}
180
181__attribute__ ((__visibility__("default")))
182int xstat64_override(int version,
183                     const char *path,
184                     struct stat64 *buf)  __asm__ ("__xstat64");
185
186__attribute__ ((__visibility__("default")))
187int xstat64_override(int version, const char *path, struct stat64 *buf) {
188  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
189    int result = __fxstat64(version, base::GetUrandomFD(), buf);
190    return result;
191  } else {
192    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
193                             InitLibcFileIOFunctions));
194    return g_libc_xstat64(version, path, buf);
195  }
196}
197
198#else
199
200__attribute__ ((__visibility__("default")))
201int stat_override(const char *path,
202                  struct stat *buf)  __asm__ ("stat");
203
204__attribute__ ((__visibility__("default")))
205int stat_override(const char *path, struct stat *buf) {
206  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
207    int result = fstat(base::GetUrandomFD(), buf);
208    return result;
209  } else {
210    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
211                             InitLibcFileIOFunctions));
212    return g_libc_stat(path, buf);
213  }
214}
215
216__attribute__ ((__visibility__("default")))
217int stat64_override(const char *path,
218                    struct stat64 *buf)  __asm__ ("stat64");
219
220__attribute__ ((__visibility__("default")))
221int stat64_override(const char *path, struct stat64 *buf) {
222  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
223    int result = fstat64(base::GetUrandomFD(), buf);
224    return result;
225  } else {
226    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
227                             InitLibcFileIOFunctions));
228    return g_libc_stat64(path, buf);
229  }
230}
231
232#endif  // HAVE_XSTAT
233
234#endif  // !defined(ADDRESS_SANITIZER)
235
236}  // namespace content
237