📄 deslogind.c
字号:
} } while ((ch = getopt(argc, argv, "w:k:Pl:t:i:f:p:dvnc")) != EOF) switch (ch) { case 'c': /* user compile'd userFile key */ userPhrase = (char *) -1; break; case 'k': /* user file cipher key */ userPhrase = optarg; break; case 'n': /* no user file cipher key */ noUserKey++; break; case 'P': pty = 0; /* pipeline instead of pty */ break; case 'l': logName = optarg; break; case 'i': /* idle timeout */ count = sscanf(optarg, "%u", &connTimeout); /* in seconds */ if (count != 1) goto usage; connTimeout *= 1000; break; case 't': /* passphrase timeout */ count = sscanf(optarg, "%u", &loginTimeout); /* in seconds */ if (count != 1) goto usage; loginTimeout *= 1000; break; case 'f': /* set userfile */ userFile = optarg; break; case 'p': /* bind to port # */ count = sscanf(optarg, "%u", &port); if (count != 1) goto usage; break; case 'w': /* function as wrapper for login */ generic = optarg; /* name in userfile with system-wide passphrase */ break; case 'v': verbose++; break; case 'd': debug++; break; default:usage: log("usage: %s [-dn] [-w name ] [-i inactiveSecs ] [-t loginSecs ] [-l logfile ] [-f userfile ] [-k userfileCipherKey ] [-p port ]\n", progName); return 1; } argc -= optind; argv += optind; if (debug > 1) { log("%s: issocket(0) returned %d\n", progName, stype); } userKey = getUserFileKey(userFile, userPhrase, noUserKey); if (userKey == (keyType) -1) { return 1; } /* * Open log *after* options parsed and some simple error checking, * and *before* closing fd's 0,1, and 2. */ res = openLog(logName); /* leaves existing log if failed */ if (res < 0) { log("%s: couldn't open logfile \"%s\"--%s\n", progName, logName, ERRMSG); return 1; } setProgName(); res = handleSignals(); /* handle quit attempts */ if (res < 0) { return 1; } /* * The remote host name is not trustworthy for several reasons. * We don't use it (or the remote IP address) for authentication. * Relying on the Domain Name System, or the remote IP address * is not something I'm willing to trust. Here, we use it strictly * for logging, and informational purposes (who, RHOSTNAME, etc). */ if (getRemoteAddr(0, rhostName, sizeof rhostName, &rport) == 0) { nfd = 0; /* From inetd. 0 and 1 is our socket. */ close(2); /* inetd leaves it open */ } else { /* Interactive invocation */ if (port == 0) { port = getServicePort(SERVICE_NAME); if ((int) port < 0) { port = DESLOGIN_PORT; log( "%s: WARNING--no \"%s\" service; using port %u\n", progName, SERVICE_NAME, port); } } log("%s: Starting daemon pid %ld port %d\n", progName, (unsigned long) getpid(), port); /* * Must close fds 0,1, and 2 *before* openServer and becomeDaemon */ close(0); close(1); close(2); pid = becomeDaemon(); if (pid < 0) { return 1; /* messages already logged */ } nfd = openServer(port, rhostName, sizeof rhostName, &rport, (debug < 2)); if (nfd < 0) { log("%s: open network port %d failed--%s\n", progName, port, ERRMSG); return 1; } setProgName(); /* Openserver did a fork when it returned successfully */ /* * Openserver handed us nfd as fd 1, 0 is now available */ } log("%s: connect %s:%d\n", progName, rhostName, rport); protocol = serverHandshake(nfd, rhostName, rport, SETUP_TIMEOUT); if (!protocol) { return 1; /* log already done */ } count = getString(nfd, ruser, USERNAME_SIZE-1, SETUP_TIMEOUT); if (count == 0) { log("%s: NORUSER %s:%d\n", progName, rhostName, rport); return 1; } count = getString(nfd, userName, USERNAME_SIZE-1, SETUP_TIMEOUT); if (count == 0) { log("%s: NOLUSER %s@%s:%d\n", progName, ruser, rhostName, rport); return 1; } /* * Minimize the the time the phrase must me visible in memory. */ if (generic) { chp = generic; } else { chp = userName; } res = getUserPhrase(userFile, passPhrase, PHRASE_SIZE, chp, userKey); if (res == 0) { log("%s: NOPHRASE %s@%s:%d->\"%s\"\n", progName, ruser, rhostName, rport, chp); return 1; } if (res == -1) { log("%s: cannot open \"%s\" for user database\n", progName, userFile); return 1; } if (res == -2) { log("%s: incorrect decryption key for user database \"%s\"\n", progName, userFile); return 1; } key = challenge(nfd, passPhrase, loginTimeout); if (key == (keyType) 0) { log("%s: BADLOGIN %s@%s:%d->\"%s\"\n", progName, ruser, rhostName, rport, userName); return 1; } memset(passPhrase, '\0', PHRASE_SIZE); if (!generic) { res = getUserInfo(userName, &shell, &cwd, &uid, &gid); if (res < 0) { log("%s: NOUSER %s@%s:%d->\"%s\"\n", progName, ruser, rhostName, rport, userName); return 1; } } else { shell = loginProg; cwd = loginCwd; /* not used except for log message */ uid = getuid(); /* cannot fail */ gid = getgid(); /* cannot fail */ } /* * If /etc/nologin exists, just copy it to the port and terminate. */#if defined(NOLOGIN_FILE) res = open(NOLOGIN_FILE, O_RDONLY, 0); if (res >= 0) { log("%s: NOLOGIN %s@%s:%d->%s\n", progName, ruser, rhostName, rport, userName); cipherCopy(nfd, res, 2, key); /* two seconds max to blast it out */ close(res); exit(1); }#endif /* * We really should log the tty assigned, and pid of the shell as well */ log("%s: Login %s@%s:%d as %s(%u:%u) %s %s\n", progName, ruser, rhostName, rport, userName, uid, gid, cwd, shell); time(&loginTime); if (pty) { /* * We only need to do all this pty stuff and forking, if we want * the local process to use a the slave tty interface. This will * not always be the case. We should add a feature to eliminate it * for remote commands that do not need a tty as stdin, stdout & stderr. * * The openPty below make the slave the controlling terminal for * this process, unless we use O_NOCTTY. */ masterpty = openPty(O_RDWR | O_NOCTTY, sname, mname, PTY_NAMESIZE, &slavepty); if (masterpty < 0) { log("%s: no pty's available\n", progName); exit(1); } /* * Better make sure this close relinquishes the slavepty as the * controlling terminal if it was. Otherwise, the child will be unable * to reacquire it as the controlling terminal and exit. * * DEC Ultrix 4.3 definitely ignores O_NOCTTY, but this code makes * sure that is harmless by closing the slave before the fork below. * * In fact, I can't figure out a way to force Ultrix to give me * a controlling terminal. It claims I got one, but then when I * try to relinquish it, it fails ENOTTY. tcgetpgrp is really busted * on this machine. */ pid_res = tcgetpgrp(slavepty); if (debug > 1) { log("%s: tcgetpgrp(%d) returned pid %d\n", progName, slavepty, pid_res); } if (debug && (pid_res >= (pid_t) 0)) { log("%s: WARNING--%s fd %d is controlling terminal of daemon!\n", progName, sname, slavepty); res = mkCtrlTty(slavepty, 0); if (debug > 1) { log("%s: mkCtrlTty(%d) returned %d\n", progName, slavepty, res); } if (res < 0) { log("%s: WARNING--couldn't relinquish control terminal--%s\n", progName, ERRMSG); } } close(slavepty); /* * The next three calls can fail when running as a normal user */ res = chmod(sname, TTY_MODE); if (res < 0) { log("%s: WARNING--chmod(\"%s\",0%o) failed--%s\n", progName, sname, TTY_MODE, ERRMSG); } res = chown(sname, uid, gid); if (res < 0) { log("%s: WARNING--chown(\"%s\",%d,%d) failed--%s\n", progName, sname, uid, gid, ERRMSG); } if (debug) { log("%s: \"%s\" %d->%d \"%s\"\n", progName, mname, masterpty, slavepty, sname); } } else { /* pipe */ /* * We must make sure these are not fd's 0, 1, or 2, so this first * call is just to use them up if necessary. */ res = pipe(pipe2); if (res < 0) { log("%s: pipe failed--%s\n", progName, ERRMSG); exit(1); } res = pipe(pipe1); if (res < 0) { log("%s: pipe failed--%s\n", progName, ERRMSG); exit(1); } res = pipe(pipe0); if (res < 0) { log("%s: pipe failed--%s\n", progName, ERRMSG); exit(1); } close(pipe2[0]); close(pipe2[1]); if (debug) { log("%s: pipe (%d>%d) (%d<%d)\n", progName, pipe0[1], pipe0[1], pipe1[0], pipe1[1]); } } /* * POSIX states default is not to receive signal when child dies (or stops) * We want to know when it happens. We must do this before the fork * to prevent a race condition. We must catch this signal to force * the select call in txfr to interrupt when the child dies. Note thhat * this may do nothing if on systems where sysconf(_SC_JOB_CONTROL) * returns 0 (although System V may implement SIGCHLD as SIGCLD.). We'll * just have to assume that this will allow us to detect when the child * dies. If not, the symptom is that exiting the remote shell will just * hang until the INACTIVITY_TIMEOUT (connTimeout) expires. */ chp = (char *) posignal(SIGCHLD, sigHandler); if (chp == (char *) SIG_ERR) { log("%s: sigaction SIGCHLD failed--%s\n", progName, ERRMSG); return 1; } /* * This fork is necessary to reset much of the process state and * to ensure that setsid won't fail because we're a process group leader * It is also needed to transport data from the pty to the socket * (or control terminal) from the remote shell. * * Sect3:15: fork() ensures that child pid != any active pgid */ pid = fork(); if (pid != 0) { /* parent */ if (pid < 0) { /* fork failure */ log("%s: fork failed--%s\n", progName, ERRMSG); exit(1); } setProgName(); if (debug) { log("%s: parent pid %d; child pid %ld\n", progName, getpid(), (long) pid); } if (pty) { /* * This must be done after we have a pty, and before we do a setuid. * It updates the utmp file so getlogin and who will work corretly. * It should be done only by the parent. * It's required on hp-ux even for -w (wrapper) option. */ res = setlogin(sname, userName, rhostName, 0); if (res < 0) { log("%s: WARNING--setlogin %s to %s@%s failed--%s\n", progName, sname, userName, rhostName, ERRMSG); } res = atexit(eraselogin); if (res < 0) { log("%s: WARNING--atexit of eraselogin failed--%s\n", progName, ERRMSG); } /* * There is a race condition between the parent and the child. * On DEC, if the parent executes txfr before the child opens the * slave, we'll abort with an I/O error. Wait until pty is ready. */ res = waitReady(masterpty, buf, 1, SETUP_TIMEOUT); if (debug > 1) { log("%s: waitReady(%d,%u)=%d\n", progName, masterpty, SETUP_TIMEOUT, res); } if (res <= 0) { if (res < 0 && errno != EINTR) { /* fail, and not SIGCHLD */ log("%s: master pty handshake failed--%s\n", progName, res, ERRMSG); } else if (res == 0) { log("%s: master pty handshake failed\n", progName, res); } } else { if (debug) { log("%s: txfr %d-decrypt->%d\n", progName, nfd, masterpty); } res = txfr(nfd, masterpty, masterpty, bufSize, connTimeout, key); } } else { /* pipe */ close(pipe0[1]); close(pipe1[0]); if (debug) { log("%s: txfr pipe %d->%d<-%d\n", progName, pipe0[0], nfd, pipe1[1]); } res = txfr(nfd, pipe0[0], pipe1[1], bufSize, connTimeout, key); } /* * Really should be registered with atexit, but since it's just * a session key anyway, it's not insecure to let's its carcass * rot in inaccessible bits of free space. */ destroyKey(&key); /* * Reap all terminated children so we don't leave zombie processes * and make sure our child CPU times are updated. Waitpid returns * -1 when all children have been reaped, and 0 if still have * unterminated children. */ do { pid = waitpid(-1, &chldStat, WNOHANG); if (debug) { log("%s: waitpid returned %ld\n", progName, (long) pid); } } while (pid > 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -