1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
|
/* ischroot: detect if running in a chroot
*
* Debian ischroot program
* Copyright (C) 2011 Aurelien Jarno <aurel32@debian.org>
* Copyright (C) 2015 Andreas Henriksson <andreas@fatal.se>
*
* This is free software; see the GNU General Public License version 2
* or later for copying conditions. There is NO warranty.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif /* HAVE_GETOPT_H */
void version()
{
printf( "Debian ischroot, version " PACKAGE_VERSION
"\nCopyright (C) 2011 Aurelien Jarno\n"
"This is free software; see the GNU General Public License version 2\n"
"or later for copying conditions. There is NO warranty.\n");
exit(0);
}
void usage()
{
printf( "Usage: ischroot [OPTION]\n"
" -f, --default-false return false if detection fails\n"
" -t, --default-true return true if detection fails\n"
" -V, --version output version information and exit.\n"
" -h, --help display this help and exit.\n");
exit(0);
}
/* return 1 if we are operating within a fakechroot environment,
return 0 otherwise */
int isfakechroot()
{
const char *fakechroot, *ldpreload;
return ((fakechroot = getenv("FAKECHROOT")) &&
(strcmp("true", fakechroot) == 0) &&
(NULL != getenv("FAKECHROOT_BASE")) &&
(ldpreload = getenv("LD_PRELOAD")) &&
(NULL != strstr(ldpreload, "libfakechroot.so")));
}
#if defined (__linux__)
/* ischroot_mountinfo returns:
* negative value on failure,
* 0 on chroot detected,
* 1 on no chroot detected.
*/
static int ischroot_mountinfo()
{
char buf1[1024], buf2[1024];
int fd1, fd2;
ssize_t rlen1, rlen2;
int ret;
fd1 = open("/proc/1/mountinfo", O_RDONLY);
fd2 = open("/proc/self/mountinfo", O_RDONLY);
if (fd1 < 0 || fd2 < 0) {
ret = -1;
goto out;
}
do {
rlen1 = read(fd1, buf1, sizeof(buf1));
rlen2 = read(fd2, buf2, sizeof(buf2));
if (rlen1 < 0 || rlen2 < 0) {
/* TODO: could do a better job of handling errors like EAGAIN here if
* relevant. Investigate possible error codes returned for procfs by
* kernel.
*/
ret = -1;
goto out;
}
/* FIXME: we assume read always fills up the buffer if there are more
* contents to read, which is not correct. Theoretically read can return
* smaller chunks, so we should probably memcmp the minimum of the return
* values and shift both buffer contents and try to fill it up by
* appending and continue comparing.
* Check if this is a problem in practise...
*/
if (rlen1 != rlen2) {
ret = 0;
goto out;
}
if (memcmp(buf1, buf2, rlen1) != 0) {
ret = 0;
goto out;
}
} while (rlen1 > 0 && rlen2 > 0);
if (rlen1 == 0 && rlen2 == 0)
ret = 1;
else
ret = -1;
out:
if (fd1 >= 0)
close(fd1);
if (fd2 >= 0)
close(fd2);
return ret;
}
/* On Linux we can detect chroots by checking if the
* devicenumber/inode pair of / are the same as that of
* /sbin/init's. This may fail if not running as root or if
* /proc is not mounted, in which case 2 is returned.
*
* If /proc/1/root exists but can not be stated as root,
* we're running in some limited environment (eg. vserver),
* which we consider as chroot here.
*/
static int ischroot()
{
struct stat st1, st2;
int ret;
ret = ischroot_mountinfo();
if (ret >= 0)
return ret;
if (stat("/", &st1))
return 2;
if (stat("/proc/1/root", &st2)) {
/* Does /proc/1/root exist at all? */
if (lstat("/proc/1/root" , &st2))
return 2;
/* Are we root? */
if (geteuid() != 0)
return 2;
/* Root can not read /proc/1/root, assume vserver or similar */
return 0;
} else if ((st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino))
return 1;
else
return 0;
}
#elif defined (__FreeBSD_kernel__) || defined (__FreeBSD__)
#include <sys/sysctl.h>
#include <sys/user.h>
/* On FreeBSD we can detect chroot by looking for a specific
* file descriptor pointing to the location of the chroot. There
* is not need to be root, so it is unlikely to fail in normal
* cases, but return 2 if a memory failure or the like happens. */
static int ischroot()
{
int mib[4];
size_t kf_len = 0;
char *kf_buf, *kf_bufp;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_FILEDESC;
mib[3] = getpid ();
if (sysctl (mib, 4, NULL, &kf_len, NULL, 0) != 0)
return 2;
kf_buf = kf_bufp = malloc (kf_len);
if (kf_buf == NULL)
return 2;
if (sysctl (mib, 4, kf_buf, &kf_len, NULL, 0) != 0)
{
free(kf_buf);
return 2;
}
while (kf_bufp < kf_buf + kf_len)
{
struct kinfo_file *kf = (struct kinfo_file *) (uintptr_t) kf_bufp;
if (kf->kf_fd == KF_FD_TYPE_JAIL)
{
free(kf_buf);
return 0;
}
kf_bufp += kf->kf_structsize;
}
free(kf_buf);
return 1;
}
#elif defined (__GNU__)
/* On Hurd we can detect chroot by looking at the device number
* containing /. The device number of the first mounted filesystem
* equals 3, and due to bug http://savannah.gnu.org/bugs/?23213
* chroots have to be created on a different filesystem. Return 2
* if it is not possible to probe this device. */
static int ischroot()
{
struct stat st;
if (stat("/", &st))
return 2;
else if (st.st_dev == 3)
return 1;
else
return 0;
}
#else
static int ischroot()
{
return 2;
}
#warning unknown system, chroot detection will always fail
#endif
/* Process options */
int main(int argc, char *argv[])
{
int default_false = 0;
int default_true = 0;
int exit_status;
for (;;) {
int c;
int option_index = 0;
static struct option long_options[] = {
{"default-false", 0, 0, 'f'},
{"default-true", 0, 0, 't'},
{"help", 0, 0, 'h'},
{"version", 0, 0, 'V'},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "fthV", long_options, &option_index);
if (c == EOF)
break;
switch (c) {
case 'f':
default_false = 1;
break;
case 't':
default_true = 1;
break;
case 'h':
usage();
break;
case 'V':
version();
break;
default:
fprintf(stderr, "Try `ischroot --help' for more information.\n");
exit(1);
}
}
if (default_false && default_true) {
fprintf(stderr, "Can't default to both true and false!\n");
fprintf(stderr, "Try `ischroot --help' for more information.\n");
exit(1);
}
if (isfakechroot())
exit_status = 0;
else
exit_status = ischroot();
if (exit_status == 2) {
if (default_true)
exit_status = 0;
if (default_false)
exit_status = 1;
}
return exit_status;
}
|