解释dort()函数在rshd.c中的工作原理

时间:2013-03-16 10:12:00

标签: linux c system-calls rsh

我想知道rsh如何运行任何命令。我正在使用 netkit-rsh-0.17 包。我的操作系统 centOS

在rshd目录中,rshd.c执行任务以在服务器上运行任何命令。 在此文件中,doit()是执行所有任务的主要功能。

问题

  1. 此代码中pwd->pw_dirpwd->pw_uidpwd->pw_shell的含义是什么?
  2. pv做了什么。
  3. 使用rsh localhost ulimit -n命令解释我。

    度特()

    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);
    }
    

1 个答案:

答案 0 :(得分:5)

struct passwd记录在pwd.h中的POSIX中。它是用于存储给定用户的/etc/passwd条目的结构。你提到的三个是这些:

  
      
  • uid_t pw_uid
      数字用户ID。
  •   
  • char *pw_dir
      初始工作目录。 (主目录。)
  •   
  • char *pw_shell
      程序用作shell。 (用户的默认shell。)
  •   

上面代码中引用的函数doauth可能会调用getpwent或模拟它以在远程系统上填写用户的相应值。

pv是一对文件描述符,表示由pipe()设置的连接管道。 pv[0]是“阅读方”,pv[1]是“写方”。写入pv[1]的任何内容都可以从pv[0]中读取。

在上面的代码中,父进程执行:

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

关闭写入端,并且,我猜测,将读取端连接到用于在主机之间通信的(一个)套接字。

另一方面,子进程就是这样做的:

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

基本上,孩子的stderr流现已连接到网络流回客户端。

其余代码基本上清理环境(环境变量和工作目录),检查权限,设置适当的uid / gid,最后执行用户想要运行的命令{ {3}}通过shell。在远程系统上运行的实际命令类似于/bin/sh -c <user command string> 因此,以您的示例为例,假设您的用户在/etc/passwd中的shell为/bin/bashexecl调用将导致运行此操作:

/bin/bash -c 'ulimit -n'

(引用,因为用户命令是execl调用中的单个参数,因此不会被标记化。)