diff options
Diffstat (limited to 'Documentation/lguest/lguest.c')
| -rw-r--r-- | Documentation/lguest/lguest.c | 73 |
1 files changed, 65 insertions, 8 deletions
diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index dc73bc54cc4e..d9da7e148538 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c | |||
| @@ -39,6 +39,9 @@ | |||
| 39 | #include <limits.h> | 39 | #include <limits.h> |
| 40 | #include <stddef.h> | 40 | #include <stddef.h> |
| 41 | #include <signal.h> | 41 | #include <signal.h> |
| 42 | #include <pwd.h> | ||
| 43 | #include <grp.h> | ||
| 44 | |||
| 42 | #include <linux/virtio_config.h> | 45 | #include <linux/virtio_config.h> |
| 43 | #include <linux/virtio_net.h> | 46 | #include <linux/virtio_net.h> |
| 44 | #include <linux/virtio_blk.h> | 47 | #include <linux/virtio_blk.h> |
| @@ -298,20 +301,27 @@ static void *map_zeroed_pages(unsigned int num) | |||
| 298 | 301 | ||
| 299 | /* | 302 | /* |
| 300 | * We use a private mapping (ie. if we write to the page, it will be | 303 | * We use a private mapping (ie. if we write to the page, it will be |
| 301 | * copied). | 304 | * copied). We allocate an extra two pages PROT_NONE to act as guard |
| 305 | * pages against read/write attempts that exceed allocated space. | ||
| 302 | */ | 306 | */ |
| 303 | addr = mmap(NULL, getpagesize() * num, | 307 | addr = mmap(NULL, getpagesize() * (num+2), |
| 304 | PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0); | 308 | PROT_NONE, MAP_PRIVATE, fd, 0); |
| 309 | |||
| 305 | if (addr == MAP_FAILED) | 310 | if (addr == MAP_FAILED) |
| 306 | err(1, "Mmapping %u pages of /dev/zero", num); | 311 | err(1, "Mmapping %u pages of /dev/zero", num); |
| 307 | 312 | ||
| 313 | if (mprotect(addr + getpagesize(), getpagesize() * num, | ||
| 314 | PROT_READ|PROT_WRITE) == -1) | ||
| 315 | err(1, "mprotect rw %u pages failed", num); | ||
| 316 | |||
| 308 | /* | 317 | /* |
| 309 | * One neat mmap feature is that you can close the fd, and it | 318 | * One neat mmap feature is that you can close the fd, and it |
| 310 | * stays mapped. | 319 | * stays mapped. |
| 311 | */ | 320 | */ |
| 312 | close(fd); | 321 | close(fd); |
| 313 | 322 | ||
| 314 | return addr; | 323 | /* Return address after PROT_NONE page */ |
| 324 | return addr + getpagesize(); | ||
| 315 | } | 325 | } |
| 316 | 326 | ||
| 317 | /* Get some more pages for a device. */ | 327 | /* Get some more pages for a device. */ |
| @@ -343,7 +353,7 @@ static void map_at(int fd, void *addr, unsigned long offset, unsigned long len) | |||
| 343 | * done to it. This allows us to share untouched memory between | 353 | * done to it. This allows us to share untouched memory between |
| 344 | * Guests. | 354 | * Guests. |
| 345 | */ | 355 | */ |
| 346 | if (mmap(addr, len, PROT_READ|PROT_WRITE|PROT_EXEC, | 356 | if (mmap(addr, len, PROT_READ|PROT_WRITE, |
| 347 | MAP_FIXED|MAP_PRIVATE, fd, offset) != MAP_FAILED) | 357 | MAP_FIXED|MAP_PRIVATE, fd, offset) != MAP_FAILED) |
| 348 | return; | 358 | return; |
| 349 | 359 | ||
| @@ -573,10 +583,10 @@ static void *_check_pointer(unsigned long addr, unsigned int size, | |||
| 573 | unsigned int line) | 583 | unsigned int line) |
| 574 | { | 584 | { |
| 575 | /* | 585 | /* |
| 576 | * We have to separately check addr and addr+size, because size could | 586 | * Check if the requested address and size exceeds the allocated memory, |
| 577 | * be huge and addr + size might wrap around. | 587 | * or addr + size wraps around. |
| 578 | */ | 588 | */ |
| 579 | if (addr >= guest_limit || addr + size >= guest_limit) | 589 | if ((addr + size) > guest_limit || (addr + size) < addr) |
| 580 | errx(1, "%s:%i: Invalid address %#lx", __FILE__, line, addr); | 590 | errx(1, "%s:%i: Invalid address %#lx", __FILE__, line, addr); |
| 581 | /* | 591 | /* |
| 582 | * We return a pointer for the caller's convenience, now we know it's | 592 | * We return a pointer for the caller's convenience, now we know it's |
| @@ -1872,6 +1882,8 @@ static struct option opts[] = { | |||
| 1872 | { "block", 1, NULL, 'b' }, | 1882 | { "block", 1, NULL, 'b' }, |
| 1873 | { "rng", 0, NULL, 'r' }, | 1883 | { "rng", 0, NULL, 'r' }, |
| 1874 | { "initrd", 1, NULL, 'i' }, | 1884 | { "initrd", 1, NULL, 'i' }, |
| 1885 | { "username", 1, NULL, 'u' }, | ||
| 1886 | { "chroot", 1, NULL, 'c' }, | ||
| 1875 | { NULL }, | 1887 | { NULL }, |
| 1876 | }; | 1888 | }; |
| 1877 | static void usage(void) | 1889 | static void usage(void) |
| @@ -1894,6 +1906,12 @@ int main(int argc, char *argv[]) | |||
| 1894 | /* If they specify an initrd file to load. */ | 1906 | /* If they specify an initrd file to load. */ |
| 1895 | const char *initrd_name = NULL; | 1907 | const char *initrd_name = NULL; |
| 1896 | 1908 | ||
| 1909 | /* Password structure for initgroups/setres[gu]id */ | ||
| 1910 | struct passwd *user_details = NULL; | ||
| 1911 | |||
| 1912 | /* Directory to chroot to */ | ||
| 1913 | char *chroot_path = NULL; | ||
| 1914 | |||
| 1897 | /* Save the args: we "reboot" by execing ourselves again. */ | 1915 | /* Save the args: we "reboot" by execing ourselves again. */ |
| 1898 | main_args = argv; | 1916 | main_args = argv; |
| 1899 | 1917 | ||
| @@ -1950,6 +1968,14 @@ int main(int argc, char *argv[]) | |||
| 1950 | case 'i': | 1968 | case 'i': |
| 1951 | initrd_name = optarg; | 1969 | initrd_name = optarg; |
| 1952 | break; | 1970 | break; |
| 1971 | case 'u': | ||
| 1972 | user_details = getpwnam(optarg); | ||
| 1973 | if (!user_details) | ||
| 1974 | err(1, "getpwnam failed, incorrect username?"); | ||
| 1975 | break; | ||
| 1976 | case 'c': | ||
| 1977 | chroot_path = optarg; | ||
| 1978 | break; | ||
| 1953 | default: | 1979 | default: |
| 1954 | warnx("Unknown argument %s", argv[optind]); | 1980 | warnx("Unknown argument %s", argv[optind]); |
| 1955 | usage(); | 1981 | usage(); |
| @@ -2021,6 +2047,37 @@ int main(int argc, char *argv[]) | |||
| 2021 | /* If we exit via err(), this kills all the threads, restores tty. */ | 2047 | /* If we exit via err(), this kills all the threads, restores tty. */ |
| 2022 | atexit(cleanup_devices); | 2048 | atexit(cleanup_devices); |
| 2023 | 2049 | ||
| 2050 | /* If requested, chroot to a directory */ | ||
| 2051 | if (chroot_path) { | ||
| 2052 | if (chroot(chroot_path) != 0) | ||
| 2053 | err(1, "chroot(\"%s\") failed", chroot_path); | ||
| 2054 | |||
| 2055 | if (chdir("/") != 0) | ||
| 2056 | err(1, "chdir(\"/\") failed"); | ||
| 2057 | |||
| 2058 | verbose("chroot done\n"); | ||
| 2059 | } | ||
| 2060 | |||
| 2061 | /* If requested, drop privileges */ | ||
| 2062 | if (user_details) { | ||
| 2063 | uid_t u; | ||
| 2064 | gid_t g; | ||
| 2065 | |||
| 2066 | u = user_details->pw_uid; | ||
| 2067 | g = user_details->pw_gid; | ||
| 2068 | |||
| 2069 | if (initgroups(user_details->pw_name, g) != 0) | ||
| 2070 | err(1, "initgroups failed"); | ||
| 2071 | |||
| 2072 | if (setresgid(g, g, g) != 0) | ||
| 2073 | err(1, "setresgid failed"); | ||
| 2074 | |||
| 2075 | if (setresuid(u, u, u) != 0) | ||
| 2076 | err(1, "setresuid failed"); | ||
| 2077 | |||
| 2078 | verbose("Dropping privileges completed\n"); | ||
| 2079 | } | ||
| 2080 | |||
| 2024 | /* Finally, run the Guest. This doesn't return. */ | 2081 | /* Finally, run the Guest. This doesn't return. */ |
| 2025 | run_guest(); | 2082 | run_guest(); |
| 2026 | } | 2083 | } |
