purge first arg to doit (assume 0 and save the work),
[unix-history] / usr / src / libexec / rshd / rshd.c
/*
* Copyright (c) 1983, 1988 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1983, 1988 The Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)rshd.c 5.19 (Berkeley) %G%";
#endif /* not lint */
/*
* remote shell server:
* [port]\0
* remuser\0
* locuser\0
* command\0
* data
*/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <netdb.h>
#include <syslog.h>
int errno;
int keepalive = 1;
char *index(), *rindex(), *strncat();
/*VARARGS1*/
int error();
/*ARGSUSED*/
main(argc, argv)
int argc;
char **argv;
{
extern int opterr, optind, _check_rhosts_file;
struct linger linger;
int ch, on = 1, fromlen;
struct sockaddr_in from;
openlog("rsh", LOG_PID | LOG_ODELAY, LOG_DAEMON);
opterr = 0;
while ((ch = getopt(argc, argv, "ln")) != EOF)
switch((char)ch) {
case 'l':
_check_rhosts_file = 0;
break;
case 'n':
keepalive = 0;
break;
case '?':
default:
syslog(LOG_ERR, "usage: rshd [-l]");
break;
}
argc -= optind;
argv += optind;
fromlen = sizeof (from);
if (getpeername(0, &from, &fromlen) < 0) {
fprintf(stderr, "%s: ", argv[0]);
perror("getpeername");
_exit(1);
}
if (keepalive &&
setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
sizeof(on)) < 0)
syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
linger.l_onoff = 1;
linger.l_linger = 60; /* XXX */
if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
sizeof (linger)) < 0)
syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
doit(&from);
}
char username[20] = "USER=";
char homedir[64] = "HOME=";
char shell[64] = "SHELL=";
char *envinit[] =
{homedir, shell, "PATH=/usr/ucb:/bin:/usr/bin:", username, 0};
char **environ;
doit(fromp)
struct sockaddr_in *fromp;
{
char cmdbuf[NCARGS+1], *cp;
char locuser[16], remuser[16];
struct passwd *pwd;
int s;
struct hostent *hp;
char *hostname;
short port;
int pv[2], pid, cc;
int nfd;
fd_set ready, readfrom;
char buf[BUFSIZ], sig;
int one = 1;
char remotehost[2 * MAXHOSTNAMELEN + 1];
(void) signal(SIGINT, SIG_DFL);
(void) signal(SIGQUIT, SIG_DFL);
(void) signal(SIGTERM, SIG_DFL);
#ifdef DEBUG
{ int t = open("/dev/tty", 2);
if (t >= 0) {
ioctl(t, TIOCNOTTY, (char *)0);
(void) close(t);
}
}
#endif
fromp->sin_port = ntohs((u_short)fromp->sin_port);
if (fromp->sin_family != AF_INET) {
syslog(LOG_ERR, "malformed from address\n");
exit(1);
}
#ifdef IP_OPTIONS
{
u_char optbuf[BUFSIZ/3], *cp;
char lbuf[BUFSIZ], *lp;
int optsize = sizeof(optbuf), ipproto;
struct protoent *ip;
if ((ip = getprotobyname("ip")) != NULL)
ipproto = ip->p_proto;
else
ipproto = IPPROTO_IP;
if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) == 0 &&
optsize != 0) {
lp = lbuf;
for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
sprintf(lp, " %2.2x", *cp);
syslog(LOG_NOTICE,
"Connection received using IP options (ignored):%s", lbuf);
if (setsockopt(0, ipproto, IP_OPTIONS,
(char *)NULL, &optsize) != 0) {
syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
exit(1);
}
}
}
#endif
if (fromp->sin_port >= IPPORT_RESERVED ||
fromp->sin_port < IPPORT_RESERVED/2) {
syslog(LOG_NOTICE, "Connection from %s on illegal port",
inet_ntoa(fromp->sin_addr));
exit(1);
}
(void) alarm(60);
port = 0;
for (;;) {
char c;
if ((cc = read(0, &c, 1)) != 1) {
if (cc < 0)
syslog(LOG_NOTICE, "read: %m");
shutdown(0, 1+1);
exit(1);
}
if (c == 0)
break;
port = port * 10 + c - '0';
}
(void) alarm(0);
if (port != 0) {
int lport = IPPORT_RESERVED - 1;
s = rresvport(&lport);
if (s < 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((u_short)port);
if (connect(s, fromp, sizeof (*fromp)) < 0) {
syslog(LOG_INFO, "connect second port: %m");
exit(1);
}
}
#ifdef notdef
/* from inetd, socket is already on 0, 1, 2 */
dup2(f, 0);
dup2(f, 1);
dup2(f, 2);
#endif
hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr),
fromp->sin_family);
if (hp) {
/*
* If name returned by gethostbyaddr is in our domain,
* attempt to verify that we haven't been fooled by someone
* in a remote net; look up the name and check that this
* address corresponds to the name.
*/
if (local_domain(hp->h_name)) {
strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
remotehost[sizeof(remotehost) - 1] = 0;
hp = gethostbyname(remotehost);
if (hp == NULL) {
syslog(LOG_INFO,
"Couldn't look up address for %s",
remotehost);
error("Couldn't look up address for your host");
exit(1);
} else for (; ; hp->h_addr_list++) {
if (!bcmp(hp->h_addr_list[0],
(caddr_t)&fromp->sin_addr,
sizeof(fromp->sin_addr)))
break;
if (hp->h_addr_list[0] == NULL) {
syslog(LOG_NOTICE,
"Host addr %s not listed for host %s",
inet_ntoa(fromp->sin_addr),
hp->h_name);
error("Host address mismatch");
exit(1);
}
}
}
hostname = hp->h_name;
} else
hostname = inet_ntoa(fromp->sin_addr);
getstr(remuser, sizeof(remuser), "remuser");
getstr(locuser, sizeof(locuser), "locuser");
getstr(cmdbuf, sizeof(cmdbuf), "command");
setpwent();
pwd = getpwnam(locuser);
if (pwd == NULL) {
error("Login incorrect.\n");
exit(1);
}
endpwent();
if (chdir(pwd->pw_dir) < 0) {
(void) chdir("/");
#ifdef notdef
error("No remote directory.\n");
exit(1);
#endif
}
if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
ruserok(hostname, pwd->pw_uid == 0, remuser, locuser) < 0) {
error("Permission denied.\n");
exit(1);
}
if (pwd->pw_uid && !access("/etc/nologin", F_OK)) {
error("Logins currently disabled.\n");
exit(1);
}
(void) write(2, "\0", 1);
if (port) {
if (pipe(pv) < 0) {
error("Can't make pipe.\n");
exit(1);
}
pid = fork();
if (pid == -1) {
error("Try again.\n");
exit(1);
}
if (pv[0] > s)
nfd = pv[0];
else
nfd = s;
nfd++;
if (pid) {
(void) close(0); (void) close(1); (void) close(2);
(void) close(pv[1]);
FD_ZERO(&readfrom);
FD_SET(s, &readfrom);
FD_SET(pv[0], &readfrom);
ioctl(pv[0], FIONBIO, (char *)&one);
/* should set s nbio! */
do {
ready = readfrom;
if (select(nfd, &ready, (fd_set *)0,
(fd_set *)0, (struct timeval *)0) < 0)
break;
if (FD_ISSET(s, &ready)) {
if (read(s, &sig, 1) <= 0)
FD_CLR(s, &readfrom);
else
killpg(pid, sig);
}
if (FD_ISSET(pv[0], &ready)) {
errno = 0;
cc = read(pv[0], buf, sizeof (buf));
if (cc <= 0) {
shutdown(s, 1+1);
FD_CLR(pv[0], &readfrom);
} else
(void) write(s, buf, cc);
}
} while (FD_ISSET(s, &readfrom) ||
FD_ISSET(pv[0], &readfrom));
exit(0);
}
setpgrp(0, getpid());
(void) close(s); (void) close(pv[0]);
dup2(pv[1], 2);
close(pv[1]);
}
if (*pwd->pw_shell == '\0')
pwd->pw_shell = "/bin/sh";
(void) setgid((gid_t)pwd->pw_gid);
initgroups(pwd->pw_name, pwd->pw_gid);
(void) setuid((uid_t)pwd->pw_uid);
environ = envinit;
strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
strncat(shell, pwd->pw_shell, sizeof(shell)-7);
strncat(username, pwd->pw_name, sizeof(username)-6);
cp = rindex(pwd->pw_shell, '/');
if (cp)
cp++;
else
cp = pwd->pw_shell;
execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
perror(pwd->pw_shell);
exit(1);
}
/*VARARGS1*/
error(fmt, a1, a2, a3)
char *fmt;
int a1, a2, a3;
{
char buf[BUFSIZ];
buf[0] = 1;
(void) sprintf(buf+1, fmt, a1, a2, a3);
(void) write(2, buf, strlen(buf));
}
getstr(buf, cnt, err)
char *buf;
int cnt;
char *err;
{
char c;
do {
if (read(0, &c, 1) != 1)
exit(1);
*buf++ = c;
if (--cnt == 0) {
error("%s too long\n", err);
exit(1);
}
} while (c != 0);
}
/*
* Check whether host h is in our local domain,
* as determined by the part of the name following
* the first '.' in its name and in ours.
* If either name is unqualified (contains no '.'),
* assume that the host is local, as it will be
* interpreted as such.
*/
local_domain(h)
char *h;
{
char localhost[MAXHOSTNAMELEN];
char *p1, *p2 = index(h, '.');
(void) gethostname(localhost, sizeof(localhost));
p1 = index(localhost, '.');
if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
return(1);
return(0);
}