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
|
/*
* This program creates a new filesystem namespace, bind mounts /bin/bash
* over /bin/sh and runs its arguments.
* It needs to be installed setuid root and will drop the extra privileges
* before running the argument.
*
* gcc -O2 -s switchsh.c -o switchsh
* chown root:root switchsh
* chmod u+s switchsh
* ./switchsh /bin/sh --version
*
* Written by Marco d'Itri <md@linux.it>, released to the public domain.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <sys/syscall.h> /* syscall */
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h> /* waitpid */
#include <sys/mount.h> /* mount */
# include <sched.h> /* unshare */
#ifndef USE_CLONE
/* old kernel headers may lack the system call number */
# if defined(__NR_unshare)
# elif defined(__i386__)
# define __NR_unshare 310
# elif defined(__x86_64__)
# define __NR_unshare 272
# elif defined(__powerpc__) || defined(__powerpc64__)
# define __NR_unshare 282
# else
/* update the kernel headers or add the number for the architecture */
# error "unshare unsupported on this architecture!"
# endif
# ifndef SYS_unshare
# define SYS_unshare __NR_unshare
# endif
# if !(__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 4)
static inline int unshare(int flags)
{
return syscall(SYS_unshare, flags);
}
# endif
#endif
/* Prototypes */
void err_quit(const char *, ...)
__attribute__ ((noreturn, format(printf, 1, 2)));
void err_sys(const char *, ...)
__attribute__ ((noreturn, format(printf, 1, 2)));
int main(int argc, char *argv[])
{
int pid;
if (!*++argv)
err_quit("Usage: switchsh PROGRAM [ARG]...");
#ifdef USE_CLONE
/* think about this as fork(2)... */
pid = syscall(SYS_clone, CLONE_NEWNS | SIGCHLD, 0);
#else
pid = fork();
#endif
if (pid < 0) { /* error */
if (errno == EPERM)
err_quit("This program must be setuid root!");
err_sys("fork");
}
if (pid > 0) {
/* parent */
int status;
/* wait for the child */
waitpid(pid, &status, 0);
exit(WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status) + 128);
}
/* child */
#ifndef USE_CLONE
if (unshare(CLONE_NEWNS) < 0) {
if (errno == ENOSYS)
err_quit("unshare requires a kernel >= 2.6.16");
else
err_sys("unshare");
}
#endif
if (mount("/bin/bash", "/bin/sh", NULL, MS_BIND, NULL) < 0) {
if (errno == EPERM)
err_quit("This program must be setuid root!");
err_sys("mount");
}
/* permanently drop privileges */
if (setgid(getgid()) < 0)
err_sys("cannot drop GID 0");
if (setuid(getuid()) < 0)
err_sys("cannot drop UID 0");
execv(*argv, argv);
err_sys("Can't exec %s", argv[0]);
}
/* Error routines */
void err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, ": %s\n", strerror(errno));
va_end(ap);
exit(1);
}
void err_quit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fputs("\n", stderr);
va_end(ap);
exit(1);
}
|