views:

51

answers:

1

After not getting an answer I liked for this question about chroot, I went and rolled my own solution:

#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <stdio.h>
extern char **environ;

int main(int argc, char** argv, char** envp) {
  char* path = "/";
  char* name = "nobody";
  char* exe = "/bin/false";
  struct passwd *pass;

  if(argc < 4) { printf("Need more args: username chroot exe args...\n"); return 1; }
  name = argv[1];
  path = argv[2];
  exe = argv[3];

  if(!(pass = getpwnam(name))) { printf("Unknown user %s", name); return 2; }

  if(chroot(path)) {
    if(errno == EPERM) { printf("chroot not allowed\n"); return 3; }
    printf("chroot failed\n");
    return 4;
  }
  chdir("/");

  if(setgid(pass->pw_gid)) { printf("setgid failed: %d\n", pass->pw_gid); return 5; }
  if(setuid(pass->pw_uid)) { printf("setuid failed: %d\n", pass->pw_uid); return 6; }

  environ = envp;
  execvp(exe, argv + 3);

  printf("exec of %s failed\n", exe);
  return 7;
}

Does anyone see any bugs in that (or even better, know of a tool that makes it redundant)?

+2  A: 
  1. Why do you assign defaults to path, name, exe, if you overwrite them anyway?
  2. You should not return negative values from within main(). It makes the actual return value unclear, as POSIX uses only the 8 least significant bits of it (i.e. -1 is returned as 255, etc.).
  3. You shouldn't rely on getuid(); chroot() would work CAP_SYS_CHROOT capability too. Instead, you could try to chroot() and check if errno == EPERM.
  4. chroot() doesn't change the current working directory; I think you should call chdir() too.
  5. What does environ = envp assignment exactly do? It seems hacky.
  6. In any case, you could add better error reporting.

And finally, chrootuid is probably the tool you were looking for.

Michał Górny
1) sane defaults avoid a whole class of bugs. 5) `environ` is used by `execvp` and I don't remember if it's set by default. 6) :b
BCS
As for chrootuid, it's not a standard tool and assumes that I want to use syslog (and I don't, so I can't use it as-is). Given that I was looking to get away from a) having to install something and b) writing my own, it has no advantage to me.
BCS
environ is set by default, AFAIK it is POSIX equivalent of envp. Thus, you're mixing two coding standards. And I don't think you could be able to find anything better. Especially that you want to acquire the UID/GID before entering chroot.
Michał Górny