Domanda

I want to know how rsh runs any command. I am using netkit-rsh-0.17 package. My OS is centOS.

In rshd directory, rshd.c performs the task to run any command on server. In this file, doit() is the main function who performs all the task.

Questions,

  1. What pwd->pw_dir, pwd->pw_uid, pwd->pw_shell means in this code?
  2. What pv does in this.

Explain me by using rsh localhost ulimit -n command.

doit()

static void
doit(struct sockaddr_in *fromp)
{
    char cmdbuf[ARG_MAX+1];
    const char *theshell, *shellname;
    char locuser[16], remuser[16];
    struct passwd *pwd;
    int sock = -1;
    const char *hostname;
    u_short port;
    int pv[2], pid, ifd;

    signal(SIGINT, SIG_DFL);
    signal(SIGQUIT, SIG_DFL);
    signal(SIGTERM, SIG_DFL);

    alarm(60);
    port = getint();
    alarm(0);

    if (port != 0) {
        int lport = IPPORT_RESERVED - 1;
        sock = rresvport(&lport);
        if (sock < 0) {
            syslog(LOG_ERR, "can't get stderr port: %m");
            exit(1);
        }
        if (port >= IPPORT_RESERVED) {
            syslog(LOG_ERR, "2nd port not reserved\n");
            exit(1);
        }
        fromp->sin_port = htons(port);
        if (connect(sock, (struct sockaddr *)fromp,
                sizeof(*fromp)) < 0) {
            syslog(LOG_INFO, "connect second port: %m");
            exit(1);
        }
    }

#if 0
    /* We're running from inetd; socket is already on 0, 1, 2 */
    dup2(f, 0);
    dup2(f, 1);
    dup2(f, 2);
#endif

    getstr(remuser, sizeof(remuser), "remuser");
    getstr(locuser, sizeof(locuser), "locuser");
    getstr(cmdbuf, sizeof(cmdbuf), "command");
    if (!strcmp(locuser, "root")) paranoid = 1;

    hostname = findhostname(fromp, remuser, locuser, cmdbuf);

    setpwent();
    pwd = doauth(remuser, hostname, locuser);
    if (pwd == NULL) {
        fail("Permission denied.\n", 
             remuser, hostname, locuser, cmdbuf);
    }

    if (chdir(pwd->pw_dir) < 0) {
        chdir("/");
        /*
         * error("No remote directory.\n");
         * exit(1);
         */
    }


    if (pwd->pw_uid != 0 && !access(_PATH_NOLOGIN, F_OK)) {
        error("Logins currently disabled.\n");
        exit(1);
    }

    (void) write(2, "\0", 1);
    sent_null = 1;

    if (port) {
        if (pipe(pv) < 0) {
            error("Can't make pipe.\n");
            exit(1);
        }
        pid = fork();
        if (pid == -1)  {
            error("Can't fork; try again.\n");
            exit(1);
        }
        if (pid) {
            close(0); 
            close(1);
            close(2); 
            close(pv[1]);
            stderr_parent(sock, pv[0], pid);
            /* NOTREACHED */
        }
        setpgrp();
        close(sock); 
        close(pv[0]);
        dup2(pv[1], 2);
        close(pv[1]);
    }
    theshell = pwd->pw_shell;
    if (!theshell || !*theshell) {
        /* shouldn't we deny access? */
        theshell = _PATH_BSHELL;
    }

#if BSD > 43
    if (setlogin(pwd->pw_name) < 0) {
        syslog(LOG_ERR, "setlogin() failed: %m");
    }
#endif
#ifndef USE_PAM
    /* if PAM, already done */
    if (setgid(pwd->pw_gid)) {
        syslog(LOG_ERR, "setgid: %m");
        exit(1);
    }
    if (initgroups(pwd->pw_name, pwd->pw_gid)) {
        syslog(LOG_ERR, "initgroups: %m");
        exit(1);
    }
#endif
    if (setuid(pwd->pw_uid)) {
        syslog(LOG_ERR, "setuid: %m");
        exit(1);
    }
    environ = envinit;

    strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
    homedir[sizeof(homedir)-1] = 0;

    strcat(path, _PATH_DEFPATH);

    strncat(shell, theshell, sizeof(shell)-7);
    shell[sizeof(shell)-1] = 0;

    strncat(username, pwd->pw_name, sizeof(username)-6);
    username[sizeof(username)-1] = 0;

    shellname = strrchr(theshell, '/');
    if (shellname) shellname++;
    else shellname = theshell;

    endpwent();
    if (paranoid) {
        syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%s'",
           remuser, hostname, locuser, cmdbuf);
    }

    /*
     * Close all fds, in case libc has left fun stuff like 
     * /etc/shadow open.
     */
    for (ifd = getdtablesize()-1; ifd > 2; ifd--) close(ifd);

    execl(theshell, shellname, "-c", cmdbuf, 0);
    perror(theshell);
    exit(1);
}
È stato utile?

Soluzione

struct passwd is documented in POSIX, in pwd.h. It is a structure used to store the /etc/passwd entries for a given user. The three you mention are these:

  • uid_t pw_uid
    Numerical user ID.
  • char *pw_dir
    Initial working directory. (Home directory.)
  • char *pw_shell
    Program to use as shell. (Default shell for the user.)

The function doauth referenced in the code above probably either calls getpwent or simulates that to fill in the appropriate values for the user on the remote system.

pv is pair of file descriptors representing connected pipes, set up by pipe(). pv[0] is the "read side", pv[1] the "write side". Anything written to pv[1] can be read from pv[0].

In the code above, the parent process does:

close(pv[1]);
stderr_parent(sock, pv[0], pid);

which closes the write side, and, I'm guessing, wires the read side to (one of) the sockets used to communicate between the hosts.

The child process on the other hand does this:

close(pv[0]);    // close the read side
dup2(pv[1], 2);  // clone the write side to fd n° 2 (stderr)
close(pv[1]);    // close the original write side (now only
                 // writable through fd n° 2

So basically, the child's stderr stream is now connected to a network stream back to the client.

The rest of the code essentially sanitizes the environment (environment variables and working directory), checks permissions, sets the appropriate uid/gid and finally executes the command that the user wanted to run using execl() via a shell. The actual command run on the remote system will be something like /bin/sh -c <user command string>.
So with your example, assuming for example that your user's shell in /etc/passwd is /bin/bash, the execl call will result in running this:

/bin/bash -c 'ulimit -n'

(Quotes since the user command is a single argument in the execl call, it is not tokenized.)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top