On Friday 23 June 2006 22:24, Joachim Schipper wrote:
>
> You could set up a named pipe (mkfifo(1)), and have a process
> continually drain it (cat /home/john/dev/null >/dev/null &); however,
> while this would work for the most likely use (writing to /dev/null), it
> wouldn't allow for reading.
> I'm not sure if sftp-server ever reads from /dev/null, but it is not
> impossible. Strange errors will occur if this is the case.
>
>

Im thinking it might just be easier to make a copy of the /dev/null device, 
but i need to investigate and test this... 

> Yes, make sure you also set real uid. A small part of
> /usr/src/usr.sbin/tcpdump/privsep.c:
>
>         /* Child - drop suid privileges */
>         gid = getgid();
>         uid = getuid();
>
>         if (setresgid(gid, gid, gid) == -1)
>                 err(1, "setresgid() failed");
>         if (setresuid(uid, uid, uid) == -1)
>                 err(1, "setresuid() failed");
>
> Do note that this is only necessary if the shell is suid and/or sgid;
> however, normal users don't have the rights to call chroot(2), so these
> additional priviliges are necessary.
>
> Also, you are aware that you perform chroot(), setresuid() and
> setresgid(), and only then execve()? This means that you'll need some
> binaries in the home directories...
>
> So, be aware that deleting a file or directory requires write priviliges
> on the parent directory; i.e., john can replace
> /home/john/bin/sftp-server by an arbitrary binary if john has write
> priviliges on his home directory, hence my suggestion to use /home
> (which is typically only writable by root) above.
> (An alternate solution is to make /home/john owned by root, group john,
> and with priviliges 0750; this would break too many things to be
> feasible if shells are allowed, but just might work if only considering
> sftp.)
>
> Finally, be aware of the many other options sshd allows, like various
> ways of tunneling. For the same reason as above, those cannot be
> disabled in /home/john/.ssh/authorized_keys only (disabling them there
> works iff the user cannot mess with this file, which is clearly not the
> case if the user has access to sftp). Either disable them sshd-wide or
> set AuthorizedKeysFile (see sshd_config(5)) to something like
> /home/.keys/%u/authorized_keys.
> Note that running any number of ssh daemons in parallel works just fine,
> subject to some caveats (they can, of course, not listen on the same
> ports on the same interfaces; they are quite CPU intensive; and random
> number quality may degrade if the pool is drained sufficiently fast).
>
>               Joachim

I am going to write another program which is used to setup, check, and update 
the chroot environments with the right files and permissions. Im going to 
have it chown the home dirs to root/wheel, and there will only be a single 
writeable dir owned by the user ( which will contain their website files for 
example ).

Here is a copy of the code ive got so far... its by no means finished, or 
formatted in the proper way, or even checked over properly again:




#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <syslog.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>

char home_dir[1024];
int argc;
char **argv;


void print_arguments(void);
void check_arguments(void);
void check_user(void);
char * find_end_part(char *buff);
void setup_env(void);

int
main(int _argc, char **_argv) {
    char *exec_args[2];

    argc = _argc;
    argv = _argv;

    openlog("jshell", LOG_PID | LOG_NDELAY, LOG_AUTH);

    check_arguments();
    check_user();

    if (chroot(home_dir) != 0 || chdir("/") != 0) {
        syslog(LOG_ERR, "chroot(%s) failed: %s", home_dir, strerror(errno));
        return 1;
    }

    /* drop privledges */
    if (seteuid(getuid()) != 0 || setuid(getuid()) != 0) {
        syslog(LOG_ERR, "setuid(%d) failed: %s", getuid(), strerror(errno));
        return 1;
    }

    exec_args[1] = NULL;
    exec_args[0] = find_end_part(argv[2]);
    execve(argv[2], exec_args, NULL);
    syslog(LOG_ERR, "execve failed");

    return 1;
}

/* print arguments to syslog */
void
print_arguments(void) {
    int x;
    for (x = 0; x < argc; x++) {
        syslog(LOG_ERR, "%d arg is '%s'", x, argv[x]);
    }
}

/* 
 *  for now we only allow -c /usr/libexec/sftp-server as an argument 
 */
void
check_arguments(void) {

    /* compare second argument ( should be -c ) */
    if (argc != 3 || strcmp("-c", argv[1]) != 0) {
        syslog(LOG_ERR, "invalid arguments\n");
        print_arguments();
        exit(1);
    }

    /* compare third argument */
    if (strcmp("/usr/libexec/sftp-server", argv[2]) != 0) {
        syslog(LOG_ERR, "invalid arguments\n");
        print_arguments();
        exit(1);
    }

}

/* 
 * check the user has some sane permissions and settings 
 * and what not on their home dir.
 */
void
check_user(void) {

    struct passwd *pw = NULL;

    /*
     * do we bother checking for a root login? 
     * why would root be using jshell anyway?
     */

    pw = getpwuid(getuid());
    if (pw == NULL) {
        syslog(LOG_ERR, "getpwuid failed: %s", strerror(errno));
        exit(1);
    }

    if (!pw->pw_dir || !strlen(pw->pw_dir)) {
        syslog(LOG_ERR, "user %s (uid %d) has no home directory?", 
pw->pw_name, getuid());
        endpwent();
        exit(1);
    }

    /*
     * TODO: check pw_dir is in a valid place, 
     * eg, /home, not / or /var/ or some shit like that. 
     */

    if (strlen(pw->pw_dir) >= sizeof(home_dir)) {
        syslog(LOG_ERR, "user %s (uid %d) home directory pathname too large", 
pw->pw_name, getuid());
        endpwent();
        exit(1);
    }

    strncpy(home_dir, pw->pw_dir, sizeof(home_dir));
    endpwent();

    /* 
     * TODO: check home dir permissions, eg, no .ssh is writeable
     * and all that
     */

}


/*
 * find the end part of a path, eg, 
 * /home/foo/bar would return 'bar'
 */
char *
find_end_part(char *buff) {
    char *x;
    char *y = buff;

    for (x = buff; *x != '\0'; x++) {
        if (*x == '/')
            y = x + 1;
    }
    return y;
}

void
setup_env(void) {



}

Reply via email to