/* $Id: uchroot.c,v 1.2 2001/10/26 21:14:51 mjt Exp $ * Combines chroot and sort of su into one executable. * Public domain * * Changes (last at top): * * Mon Jul 30 2001 Michael Tokarev * removed getopt_long (for portability with FreeBSD) * implemented -d dir (initial directory in chroot jail) option */ #include #include #include #include #include #include #include #include static char *progname; static void err(int code, const char *fmt, ...) { va_list ap; fprintf(stderr, "%s: ", progname); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); if (code) /* it is safe to use errno here -- if it was changed by fprintf() below then it was error printing error message... */ fprintf(stderr, ": %s", strerror(code)); putc('\n', stderr); fflush(stderr); exit(1); } int main(int argc, char **argv) { struct passwd *pw = NULL; struct group *gr = NULL; const char *dir = "/"; char *p; int c; if ((progname = strrchr(argv[0], '/')) != NULL) argv[0] = ++progname; else progname = argv[0]; while((c = getopt(argc, argv, "+hu:d:")) != EOF) switch(c) { case 'u': if ((p = strchr(optarg, ':')) != NULL) *p++ = '\0'; if ((pw = getpwnam(optarg)) == NULL) err(0, "unable to find user `%s'", optarg); if (p && (gr = getgrnam(p)) == NULL) err(0, "unable to find group `%s'", p); break; case 'd': dir = optarg; break; case 'h': printf("%s: change root\n\ Usage: %s [-u user[:group]] [-d dir] newroot [command args...]\n", progname, progname); return 0; default: return 1; } if (argc == optind) err(0, "new root not specified"); if (chroot(argv[optind]) != 0) err(1, "unable to change root to `%s'", argv[optind]); if (chdir(dir) != 0) err(1, "unable to chdir to `%s'"); if (pw) { if (setgid(gr ? gr->gr_gid : pw->pw_gid) != 0) err(1, "unable to set group"); if (setuid(pw->pw_uid) != 0) err(1, "unable to set uid"); } argv += optind + 1; argc -= optind + 1; if (argc) { execv(argv[0], argv); p = argv[0]; } else { if (pw) p = pw->pw_shell; else if ((p = getenv("SHELL")) == NULL) p = "/bin/sh"; execlp(p, p, NULL); } err(1, "unable to execute %s", p); return 1; }