121176Sdist /* 236629Skarels * Copyright (c) 1983, 1988 The Regents of the University of California. 335485Sbostic * All rights reserved. 435485Sbostic * 535485Sbostic * Redistribution and use in source and binary forms are permitted 635485Sbostic * provided that the above copyright notice and this paragraph are 735485Sbostic * duplicated in all such forms and that any documentation, 835485Sbostic * advertising materials, and other materials related to such 935485Sbostic * distribution and use acknowledge that the software was developed 1035485Sbostic * by the University of California, Berkeley. The name of the 1135485Sbostic * University may not be used to endorse or promote products derived 1235485Sbostic * from this software without specific prior written permission. 1335485Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1435485Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1535485Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1621176Sdist */ 1721176Sdist 186454Swnj #ifndef lint 1921176Sdist char copyright[] = 2036629Skarels "@(#) Copyright (c) 1983, 1988 The Regents of the University of California.\n\ 2121176Sdist All rights reserved.\n"; 2235485Sbostic #endif /* not lint */ 236454Swnj 2421176Sdist #ifndef lint 25*36700Skarels static char sccsid[] = "@(#)rshd.c 5.19 (Berkeley) 02/07/89"; 2635485Sbostic #endif /* not lint */ 2721176Sdist 2816370Skarels /* 2916370Skarels * remote shell server: 3036629Skarels * [port]\0 3116370Skarels * remuser\0 3216370Skarels * locuser\0 3316370Skarels * command\0 3416370Skarels * data 3516370Skarels */ 3636629Skarels #include <sys/param.h> 376454Swnj #include <sys/ioctl.h> 386454Swnj #include <sys/socket.h> 3931025Sbostic #include <sys/file.h> 4027901Slepreau #include <sys/time.h> 419212Ssam 429212Ssam #include <netinet/in.h> 439212Ssam 4424903Sbloom #include <arpa/inet.h> 4524903Sbloom 469212Ssam #include <stdio.h> 476454Swnj #include <errno.h> 486454Swnj #include <pwd.h> 496454Swnj #include <signal.h> 508380Ssam #include <netdb.h> 5117187Sralph #include <syslog.h> 526454Swnj 536454Swnj int errno; 5436600Sbostic int keepalive = 1; 5527901Slepreau char *index(), *rindex(), *strncat(); 5627901Slepreau /*VARARGS1*/ 576454Swnj int error(); 5816370Skarels 5936645Skfall #ifdef KERBEROS 6036645Skfall #include <kerberos/krb.h> 6136645Skfall #define VERSION_SIZE 9 6236645Skfall #define OPTIONS "lnkv" 6336645Skfall char *strsave(); 6436645Skfall char authbuf[sizeof(AUTH_DAT)]; 6536645Skfall char tickbuf[sizeof(KTEXT_ST)]; 6636645Skfall int use_kerberos = 0, vacuous = 0; 6736645Skfall 6836645Skfall #define OLD_RCMD 0x00 6936645Skfall #define KERB_RCMD 0x00 7036645Skfall #define KERB_RCMD_MUTUAL 0x03 7136645Skfall 7236645Skfall int encrypt = 0; 7336645Skfall #else 7436645Skfall #define OPTIONS "ln" 7536645Skfall #endif 7636645Skfall 7727901Slepreau /*ARGSUSED*/ 786454Swnj main(argc, argv) 796454Swnj int argc; 806454Swnj char **argv; 816454Swnj { 8236318Sbostic extern int opterr, optind, _check_rhosts_file; 8317156Ssam struct linger linger; 8436318Sbostic int ch, on = 1, fromlen; 856454Swnj struct sockaddr_in from; 866454Swnj 8724851Seric openlog("rsh", LOG_PID | LOG_ODELAY, LOG_DAEMON); 8836318Sbostic 8936318Sbostic opterr = 0; 9036645Skfall while ((ch = getopt(argc, argv, OPTIONS)) != EOF) 9136318Sbostic switch((char)ch) { 9236318Sbostic case 'l': 9336318Sbostic _check_rhosts_file = 0; 9436318Sbostic break; 9536600Sbostic case 'n': 9636600Sbostic keepalive = 0; 9736600Sbostic break; 9836645Skfall #ifdef KERBEROS 9936645Skfall case 'k': 10036645Skfall use_kerberos = 1; 10136645Skfall break; 10236645Skfall 10336645Skfall case 'v': 10436645Skfall vacuous = 1; 10536645Skfall break; 10636645Skfall #endif 10736318Sbostic case '?': 10836318Sbostic default: 10936318Sbostic syslog(LOG_ERR, "usage: rshd [-l]"); 11036318Sbostic break; 11136318Sbostic } 11236611Skfall 11336318Sbostic argc -= optind; 11436318Sbostic argv += optind; 11536318Sbostic 11636645Skfall #ifdef KERBEROS 11736645Skfall if (use_kerberos && vacuous) { 11836645Skfall syslog(LOG_ERR, "only one of -k and -v allowed"); 11936645Skfall exit(1); 12036645Skfall } 12136645Skfall #endif 12236611Skfall 12316370Skarels fromlen = sizeof (from); 12416370Skarels if (getpeername(0, &from, &fromlen) < 0) { 12516370Skarels fprintf(stderr, "%s: ", argv[0]); 12616370Skarels perror("getpeername"); 12716370Skarels _exit(1); 1288380Ssam } 12936600Sbostic if (keepalive && 13036600Sbostic setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, 13136600Sbostic sizeof(on)) < 0) 13217187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 13317156Ssam linger.l_onoff = 1; 13417156Ssam linger.l_linger = 60; /* XXX */ 13527901Slepreau if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger, 13627901Slepreau sizeof (linger)) < 0) 13717187Sralph syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); 138*36700Skarels doit(&from); 1396454Swnj } 1406454Swnj 1416454Swnj char username[20] = "USER="; 1426454Swnj char homedir[64] = "HOME="; 1436454Swnj char shell[64] = "SHELL="; 1446454Swnj char *envinit[] = 14536629Skarels {homedir, shell, "PATH=/usr/ucb:/bin:/usr/bin:", username, 0}; 1466454Swnj char **environ; 1476454Swnj 148*36700Skarels doit(fromp) 1496454Swnj struct sockaddr_in *fromp; 1506454Swnj { 1516454Swnj char cmdbuf[NCARGS+1], *cp; 1526454Swnj char locuser[16], remuser[16]; 1536454Swnj struct passwd *pwd; 15427901Slepreau int s; 1558380Ssam struct hostent *hp; 15625985Skarels char *hostname; 1576454Swnj short port; 15835485Sbostic int pv[2], pid, cc; 15936629Skarels int nfd; 16036629Skarels fd_set ready, readfrom; 1616454Swnj char buf[BUFSIZ], sig; 1626454Swnj int one = 1; 16336629Skarels char remotehost[2 * MAXHOSTNAMELEN + 1]; 1646454Swnj 16536645Skfall #ifdef KERBEROS 16636645Skfall AUTH_DAT *kdata = (AUTH_DAT *) NULL; 16736645Skfall KTEXT ticket = (KTEXT) NULL; 16836645Skfall char instance[INST_SZ], version[VERSION_SIZE]; 16936645Skfall char *h_name; 17036645Skfall struct sockaddr_in fromaddr; 17136645Skfall int rc; 17236645Skfall long authopts; 17336645Skfall 17436645Skfall fromaddr = *fromp; 17536645Skfall #endif 17636645Skfall 1776454Swnj (void) signal(SIGINT, SIG_DFL); 1786454Swnj (void) signal(SIGQUIT, SIG_DFL); 1796454Swnj (void) signal(SIGTERM, SIG_DFL); 18010883Ssam #ifdef DEBUG 1816454Swnj { int t = open("/dev/tty", 2); 1826454Swnj if (t >= 0) { 1836454Swnj ioctl(t, TIOCNOTTY, (char *)0); 1846454Swnj (void) close(t); 1856454Swnj } 1866454Swnj } 1876454Swnj #endif 1886454Swnj fromp->sin_port = ntohs((u_short)fromp->sin_port); 18932108Skarels if (fromp->sin_family != AF_INET) { 19017187Sralph syslog(LOG_ERR, "malformed from address\n"); 1916454Swnj exit(1); 19210596Ssam } 193*36700Skarels #ifdef IP_OPTIONS 194*36700Skarels { 195*36700Skarels u_char optbuf[BUFSIZ/3], *cp; 196*36700Skarels char lbuf[BUFSIZ], *lp; 197*36700Skarels int optsize = sizeof(optbuf), ipproto; 198*36700Skarels struct protoent *ip; 19936611Skfall 200*36700Skarels if ((ip = getprotobyname("ip")) != NULL) 201*36700Skarels ipproto = ip->p_proto; 202*36700Skarels else 203*36700Skarels ipproto = IPPROTO_IP; 204*36700Skarels if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) == 0 && 205*36700Skarels optsize != 0) { 206*36700Skarels lp = lbuf; 207*36700Skarels for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) 208*36700Skarels sprintf(lp, " %2.2x", *cp); 209*36700Skarels syslog(LOG_NOTICE, 210*36700Skarels "Connection received using IP options (ignored):%s", lbuf); 211*36700Skarels if (setsockopt(0, ipproto, IP_OPTIONS, 212*36700Skarels (char *)NULL, &optsize) != 0) { 213*36700Skarels syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); 214*36700Skarels exit(1); 215*36700Skarels } 216*36700Skarels } 217*36700Skarels } 218*36700Skarels #endif 219*36700Skarels 22036645Skfall #ifndef KERBEROS 22132108Skarels if (fromp->sin_port >= IPPORT_RESERVED || 22232108Skarels fromp->sin_port < IPPORT_RESERVED/2) { 22336611Skfall syslog(LOG_NOTICE, "Connection from %s on illegal port", 22436611Skfall inet_ntoa(fromp->sin_addr)); 22532108Skarels exit(1); 22632108Skarels } 22736645Skfall #endif 22836611Skfall 2296454Swnj (void) alarm(60); 2306454Swnj port = 0; 2316454Swnj for (;;) { 2326454Swnj char c; 233*36700Skarels if ((cc = read(0, &c, 1)) != 1) { 23432108Skarels if (cc < 0) 23532108Skarels syslog(LOG_NOTICE, "read: %m"); 236*36700Skarels shutdown(0, 1+1); 2376454Swnj exit(1); 23810596Ssam } 23936645Skfall #ifdef KERBEROS 24036645Skfall if (c == OLD_RCMD || c == KERB_RCMD) 24136645Skfall break; 24236645Skfall 24336645Skfall if (c == KERB_RCMD_MUTUAL) { 24436645Skfall encrypt = 1; 24536645Skfall break; 24636645Skfall } 24736645Skfall #else 2486454Swnj if (c == 0) 2496454Swnj break; 25036645Skfall #endif 2516454Swnj port = port * 10 + c - '0'; 2526454Swnj } 25336611Skfall 2546454Swnj (void) alarm(0); 2556454Swnj if (port != 0) { 25627901Slepreau int lport = IPPORT_RESERVED - 1; 25710273Ssam s = rresvport(&lport); 25810596Ssam if (s < 0) { 25917187Sralph syslog(LOG_ERR, "can't get stderr port: %m"); 2606454Swnj exit(1); 26110596Ssam } 26236645Skfall #ifndef KERBEROS 26310596Ssam if (port >= IPPORT_RESERVED) { 26417187Sralph syslog(LOG_ERR, "2nd port not reserved\n"); 26510596Ssam exit(1); 26610596Ssam } 26736645Skfall #endif 2689243Ssam fromp->sin_port = htons((u_short)port); 26927901Slepreau if (connect(s, fromp, sizeof (*fromp)) < 0) { 27025985Skarels syslog(LOG_INFO, "connect second port: %m"); 2716454Swnj exit(1); 27210596Ssam } 2736454Swnj } 27436611Skfall 27536645Skfall #ifdef KERBEROS 27636645Skfall if (vacuous) { 27736645Skfall error("rshd: remote host requires Kerberos authentication\n"); 27836645Skfall exit(1); 27936645Skfall } 28036645Skfall #endif 28136645Skfall 28236629Skarels #ifdef notdef 283*36700Skarels /* from inetd, socket is already on 0, 1, 2 */ 28410596Ssam dup2(f, 0); 28510596Ssam dup2(f, 1); 28610596Ssam dup2(f, 2); 28736629Skarels #endif 28827901Slepreau hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr), 2898380Ssam fromp->sin_family); 29036611Skfall if (hp) { 29136629Skarels /* 29236629Skarels * If name returned by gethostbyaddr is in our domain, 29336629Skarels * attempt to verify that we haven't been fooled by someone 29436629Skarels * in a remote net; look up the name and check that this 29536629Skarels * address corresponds to the name. 29636629Skarels */ 29736629Skarels if (local_domain(hp->h_name)) { 29836629Skarels strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1); 29936629Skarels remotehost[sizeof(remotehost) - 1] = 0; 30036611Skfall hp = gethostbyname(remotehost); 30136629Skarels if (hp == NULL) { 30236629Skarels syslog(LOG_INFO, 30336629Skarels "Couldn't look up address for %s", 30436629Skarels remotehost); 30536629Skarels error("Couldn't look up address for your host"); 30636611Skfall exit(1); 30736629Skarels } else for (; ; hp->h_addr_list++) { 30836629Skarels if (!bcmp(hp->h_addr_list[0], 30936629Skarels (caddr_t)&fromp->sin_addr, 31036629Skarels sizeof(fromp->sin_addr))) 31136629Skarels break; 31236629Skarels if (hp->h_addr_list[0] == NULL) { 31336629Skarels syslog(LOG_NOTICE, 31436629Skarels "Host addr %s not listed for host %s", 31536629Skarels inet_ntoa(fromp->sin_addr), 31636629Skarels hp->h_name); 31736611Skfall error("Host address mismatch"); 31836611Skfall exit(1); 31936611Skfall } 32036611Skfall } 32136611Skfall } 32225985Skarels hostname = hp->h_name; 32336629Skarels } else 32425985Skarels hostname = inet_ntoa(fromp->sin_addr); 32536611Skfall 32636645Skfall #ifdef KERBEROS 32736645Skfall if (use_kerberos) { 32836645Skfall h_name = strsave(hp->h_name); 32936645Skfall kdata = (AUTH_DAT *) authbuf; 33036645Skfall ticket = (KTEXT) tickbuf; 33136645Skfall authopts = 0L; 33236645Skfall strcpy(instance, "*"); 33336645Skfall version[VERSION_SIZE - 1] = '\0'; 334*36700Skarels if (rc = krb_recvauth(authopts, 0, ticket, "rcmd", 33536645Skfall instance, &fromaddr, 33636645Skfall (struct sockaddr_in *) 0, 33736645Skfall kdata, "", (bit_64 *) 0, version)) { 33836645Skfall fprintf(stderr, 33936645Skfall "Kerberos authentication failure: %s\r\n", 34036645Skfall krb_err_txt[rc]); 34136645Skfall exit(1); 34236645Skfall } 34336645Skfall free(h_name); 34436645Skfall h_name = NULL; 34536645Skfall } else 34636645Skfall #endif 34736645Skfall getstr(remuser, sizeof(remuser), "remuser"); 34836645Skfall 3496454Swnj getstr(locuser, sizeof(locuser), "locuser"); 3506454Swnj getstr(cmdbuf, sizeof(cmdbuf), "command"); 3516454Swnj setpwent(); 3526454Swnj pwd = getpwnam(locuser); 3536454Swnj if (pwd == NULL) { 3546454Swnj error("Login incorrect.\n"); 3556454Swnj exit(1); 3566454Swnj } 3576454Swnj endpwent(); 3586454Swnj if (chdir(pwd->pw_dir) < 0) { 35927901Slepreau (void) chdir("/"); 36016370Skarels #ifdef notdef 3616454Swnj error("No remote directory.\n"); 3626454Swnj exit(1); 36316370Skarels #endif 3646454Swnj } 36536611Skfall 36636645Skfall #ifdef KERBEROS 36736645Skfall if (use_kerberos) { 36836645Skfall if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0') { 36936645Skfall if (krb_kntoln(kdata, remuser) != KSUCCESS) { 37036645Skfall error("Permission denied.\n"); 37136645Skfall exit(1); 37236645Skfall } 37336645Skfall if (kuserok(kdata, locuser) != 0) { 37436645Skfall syslog(LOG_NOTICE, "Kerberos rlogin denied to %s.%s@%s", 37536645Skfall kdata->pname, kdata->pinst, kdata->prealm); 37636645Skfall error("Permission denied.\n"); 37736645Skfall exit(1); 37836645Skfall } 37936645Skfall } 38036645Skfall } else 38136645Skfall #endif 38236611Skfall 38336645Skfall if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && 38436645Skfall ruserok(hostname, pwd->pw_uid == 0, remuser, locuser) < 0) { 38536645Skfall error("Permission denied.\n"); 38636645Skfall exit(1); 38736645Skfall } 38836645Skfall 38934011Sbostic if (pwd->pw_uid && !access("/etc/nologin", F_OK)) { 39031025Sbostic error("Logins currently disabled.\n"); 39131025Sbostic exit(1); 39231025Sbostic } 39336645Skfall #ifdef KERBEROS 39436645Skfall if (encrypt) { 39536645Skfall char c = KERB_RCMD_MUTUAL; 39636645Skfall (void) write(2, &c, 1); 39736645Skfall } else { 39836645Skfall char c = KERB_RCMD; 39936645Skfall (void) write(2, &c, 1); 40036645Skfall } 40136645Skfall #else 4026454Swnj (void) write(2, "\0", 1); 40336645Skfall #endif 40436611Skfall 4056454Swnj if (port) { 4066454Swnj if (pipe(pv) < 0) { 4076454Swnj error("Can't make pipe.\n"); 4086454Swnj exit(1); 4096454Swnj } 4106454Swnj pid = fork(); 4116454Swnj if (pid == -1) { 4126454Swnj error("Try again.\n"); 4136454Swnj exit(1); 4146454Swnj } 41536629Skarels if (pv[0] > s) 41636629Skarels nfd = pv[0]; 41736629Skarels else 41836629Skarels nfd = s; 41936629Skarels nfd++; 4206454Swnj if (pid) { 4216454Swnj (void) close(0); (void) close(1); (void) close(2); 422*36700Skarels (void) close(pv[1]); 42336629Skarels FD_ZERO(&readfrom); 42436629Skarels FD_SET(s, &readfrom); 42536629Skarels FD_SET(pv[0], &readfrom); 42630265Smckusick ioctl(pv[0], FIONBIO, (char *)&one); 4276454Swnj /* should set s nbio! */ 4286454Swnj do { 4296454Swnj ready = readfrom; 43036629Skarels if (select(nfd, &ready, (fd_set *)0, 43127901Slepreau (fd_set *)0, (struct timeval *)0) < 0) 4329212Ssam break; 43336629Skarels if (FD_ISSET(s, &ready)) { 4346454Swnj if (read(s, &sig, 1) <= 0) 43536629Skarels FD_CLR(s, &readfrom); 4366454Swnj else 4376454Swnj killpg(pid, sig); 4386454Swnj } 43936629Skarels if (FD_ISSET(pv[0], &ready)) { 44011238Ssam errno = 0; 4416454Swnj cc = read(pv[0], buf, sizeof (buf)); 4426454Swnj if (cc <= 0) { 44310194Ssam shutdown(s, 1+1); 44436629Skarels FD_CLR(pv[0], &readfrom); 4456454Swnj } else 4466454Swnj (void) write(s, buf, cc); 4476454Swnj } 44836629Skarels } while (FD_ISSET(s, &readfrom) || 44936629Skarels FD_ISSET(pv[0], &readfrom)); 4506454Swnj exit(0); 4516454Swnj } 4526454Swnj setpgrp(0, getpid()); 4536454Swnj (void) close(s); (void) close(pv[0]); 4546454Swnj dup2(pv[1], 2); 45536645Skfall close(pv[1]); 4566454Swnj } 4576454Swnj if (*pwd->pw_shell == '\0') 4586454Swnj pwd->pw_shell = "/bin/sh"; 45927901Slepreau (void) setgid((gid_t)pwd->pw_gid); 4609212Ssam initgroups(pwd->pw_name, pwd->pw_gid); 46127901Slepreau (void) setuid((uid_t)pwd->pw_uid); 4626454Swnj environ = envinit; 4636454Swnj strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); 4646454Swnj strncat(shell, pwd->pw_shell, sizeof(shell)-7); 4656454Swnj strncat(username, pwd->pw_name, sizeof(username)-6); 4666454Swnj cp = rindex(pwd->pw_shell, '/'); 4676454Swnj if (cp) 4686454Swnj cp++; 4696454Swnj else 4706454Swnj cp = pwd->pw_shell; 4716454Swnj execl(pwd->pw_shell, cp, "-c", cmdbuf, 0); 4726454Swnj perror(pwd->pw_shell); 4736454Swnj exit(1); 4746454Swnj } 4756454Swnj 47627901Slepreau /*VARARGS1*/ 47725663Smckusick error(fmt, a1, a2, a3) 4786454Swnj char *fmt; 47925663Smckusick int a1, a2, a3; 4806454Swnj { 4816454Swnj char buf[BUFSIZ]; 4826454Swnj 4836454Swnj buf[0] = 1; 48425663Smckusick (void) sprintf(buf+1, fmt, a1, a2, a3); 4856454Swnj (void) write(2, buf, strlen(buf)); 4866454Swnj } 4876454Swnj 4886454Swnj getstr(buf, cnt, err) 4896454Swnj char *buf; 4906454Swnj int cnt; 4916454Swnj char *err; 4926454Swnj { 4936454Swnj char c; 4946454Swnj 4956454Swnj do { 4966454Swnj if (read(0, &c, 1) != 1) 4976454Swnj exit(1); 4986454Swnj *buf++ = c; 4996454Swnj if (--cnt == 0) { 5006454Swnj error("%s too long\n", err); 5016454Swnj exit(1); 5026454Swnj } 5036454Swnj } while (c != 0); 5046454Swnj } 50536611Skfall 50636629Skarels /* 50736629Skarels * Check whether host h is in our local domain, 50836629Skarels * as determined by the part of the name following 50936629Skarels * the first '.' in its name and in ours. 51036629Skarels * If either name is unqualified (contains no '.'), 51136629Skarels * assume that the host is local, as it will be 51236629Skarels * interpreted as such. 51336629Skarels */ 51436629Skarels local_domain(h) 51536629Skarels char *h; 51636611Skfall { 51736629Skarels char localhost[MAXHOSTNAMELEN]; 51836629Skarels char *p1, *p2 = index(h, '.'); 51936629Skarels 52036629Skarels (void) gethostname(localhost, sizeof(localhost)); 52136629Skarels p1 = index(localhost, '.'); 52236629Skarels if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 52336611Skfall return(1); 52436611Skfall return(0); 52536611Skfall } 526