libc_urandom_override.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/eintr_wrapper.h"
14#include "base/logging.h"
15#include "base/rand_util.h"
16
17// Note: this file is used by the zygote and nacl_helper.
18
19namespace sandbox {
20
21// With SELinux we can carve out a precise sandbox, so we don't have to play
22// with intercepting libc calls.
23#if !defined(CHROMIUM_SELINUX)
24
25static bool g_override_urandom = false;
26
27void InitLibcUrandomOverrides() {
28  // Make sure /dev/urandom is open.
29  base::GetUrandomFD();
30  g_override_urandom = true;
31}
32
33// TODO(sergeyu): Currently this code doesn't work properly under ASAN
34// - it crashes content_unittests. Make sure it works properly and
35// enable it here. http://crbug.com/123263
36#if !defined(ADDRESS_SANITIZER)
37
38static const char kUrandomDevPath[] = "/dev/urandom";
39
40typedef FILE* (*FopenFunction)(const char* path, const char* mode);
41typedef int (*XstatFunction)(int version, const char *path, struct stat *buf);
42typedef int (*Xstat64Function)(int version, const char *path,
43                               struct stat64 *buf);
44
45static pthread_once_t g_libc_file_io_funcs_guard = PTHREAD_ONCE_INIT;
46static FopenFunction g_libc_fopen;
47static FopenFunction g_libc_fopen64;
48static XstatFunction g_libc_xstat;
49static Xstat64Function g_libc_xstat64;
50
51static void InitLibcFileIOFunctions() {
52  g_libc_fopen = reinterpret_cast<FopenFunction>(
53      dlsym(RTLD_NEXT, "fopen"));
54  g_libc_fopen64 = reinterpret_cast<FopenFunction>(
55      dlsym(RTLD_NEXT, "fopen64"));
56
57  if (!g_libc_fopen) {
58    LOG(FATAL) << "Failed to get fopen() from libc.";
59  } else if (!g_libc_fopen64) {
60#if !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
61    LOG(WARNING) << "Failed to get fopen64() from libc. Using fopen() instead.";
62#endif  // !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
63    g_libc_fopen64 = g_libc_fopen;
64  }
65
66  // TODO(sergeyu): This works only on systems with glibc. Fix it to
67  // work properly on other systems if necessary.
68  g_libc_xstat = reinterpret_cast<XstatFunction>(
69      dlsym(RTLD_NEXT, "__xstat"));
70  g_libc_xstat64 = reinterpret_cast<Xstat64Function>(
71      dlsym(RTLD_NEXT, "__xstat64"));
72
73  if (!g_libc_xstat) {
74    LOG(FATAL) << "Failed to get __xstat() from libc.";
75  }
76  if (!g_libc_xstat64) {
77    LOG(WARNING) << "Failed to get __xstat64() from libc.";
78  }
79}
80
81// fopen() and fopen64() are intercepted here so that NSS can open
82// /dev/urandom to seed its random number generator. NSS is used by
83// remoting in the sendbox.
84
85// fopen() call may be redirected to fopen64() in stdio.h using
86// __REDIRECT(), which sets asm name for fopen() to "fopen64". This
87// means that we cannot override fopen() directly here. Instead the
88// the code below defines fopen_override() function with asm name
89// "fopen", so that all references to fopen() will resolve to this
90// function.
91__attribute__ ((__visibility__("default")))
92FILE* fopen_override(const char* path, const char* mode)  __asm__ ("fopen");
93
94__attribute__ ((__visibility__("default")))
95FILE* fopen_override(const char* path, const char* mode) {
96  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
97    int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
98    if (fd < 0) {
99      PLOG(ERROR) << "dup() failed.";
100      return NULL;
101    }
102    return fdopen(fd, mode);
103  } else {
104    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
105                             InitLibcFileIOFunctions));
106    return g_libc_fopen(path, mode);
107  }
108}
109
110__attribute__ ((__visibility__("default")))
111FILE* fopen64(const char* path, const char* mode) {
112  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
113    int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
114    if (fd < 0) {
115      PLOG(ERROR) << "dup() failed.";
116      return NULL;
117    }
118    return fdopen(fd, mode);
119  } else {
120    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
121                             InitLibcFileIOFunctions));
122    return g_libc_fopen64(path, mode);
123  }
124}
125
126// stat() is subject to the same problem as fopen(), so we have to use
127// the same trick to override it.
128__attribute__ ((__visibility__("default")))
129int xstat_override(int version,
130                   const char *path,
131                   struct stat *buf)  __asm__ ("__xstat");
132
133__attribute__ ((__visibility__("default")))
134int xstat_override(int version, const char *path, struct stat *buf) {
135  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
136    int result = __fxstat(version, base::GetUrandomFD(), buf);
137    return result;
138  } else {
139    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
140                             InitLibcFileIOFunctions));
141    return g_libc_xstat(version, path, buf);
142  }
143}
144
145__attribute__ ((__visibility__("default")))
146int xstat64_override(int version,
147                     const char *path,
148                     struct stat64 *buf)  __asm__ ("__xstat64");
149
150__attribute__ ((__visibility__("default")))
151int xstat64_override(int version, const char *path, struct stat64 *buf) {
152  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
153    int result = __fxstat64(version, base::GetUrandomFD(), buf);
154    return result;
155  } else {
156    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
157                             InitLibcFileIOFunctions));
158    CHECK(g_libc_xstat64);
159    return g_libc_xstat64(version, path, buf);
160  }
161}
162
163#endif  // !ADDRESS_SANITIZER
164
165#endif  // !CHROMIUM_SELINUX
166
167}  // namespace content
168