4.4BSD snapshot (revision 8.1); add 1993 to copyright
[unix-history] / usr / src / libexec / rshd / rshd.c
CommitLineData
ccb7ffcd 1/*-
2e9feff0
KB
2 * Copyright (c) 1988, 1989, 1992, 1993
3 * The Regents of the University of California. All rights reserved.
780d12ad 4 *
836fe169 5 * %sccs.include.redist.c%
8c5eec2f
DF
6 */
7
8#ifndef lint
2e9feff0
KB
9static char copyright[] =
10"@(#) Copyright (c) 1988, 1989, 1992, 1993\n\
11 The Regents of the University of California. All rights reserved.\n";
780d12ad 12#endif /* not lint */
8c5eec2f 13
1d68cba5 14#ifndef lint
2e9feff0 15static char sccsid[] = "@(#)rshd.c 8.1 (Berkeley) %G%";
780d12ad 16#endif /* not lint */
1d68cba5 17
bb933cc2
MK
18/*
19 * remote shell server:
046a4121 20 * [port]\0
bb933cc2
MK
21 * remuser\0
22 * locuser\0
23 * command\0
24 * data
25 */
1d68cba5 26#include <sys/param.h>
046a4121 27#include <sys/ioctl.h>
d4413a13 28#include <sys/time.h>
ccb7ffcd 29#include <sys/socket.h>
5492e445 30
e7d6d17a 31#include <netinet/in.h>
4c99408e 32#include <arpa/inet.h>
ccb7ffcd 33#include <netdb.h>
4c99408e 34
5492e445
AC
35#include <fcntl.h>
36#include <signal.h>
1d68cba5 37#include <pwd.h>
3f99c0f7 38#include <syslog.h>
17ccb086 39#include "pathnames.h"
ccb7ffcd
KB
40#include <unistd.h>
41#include <errno.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <paths.h>
1d68cba5 46
e0ffdf97 47int keepalive = 1;
56940953 48int check_all = 0;
5492e445
AC
49int check_all;
50int log_success; /* If TRUE, log all successful accesses */
56940953 51int sent_null;
beaa2897 52int sent_null;
bb933cc2 53
5492e445
AC
54void doit __P((struct sockaddr_in *));
55void error __P((const char *, ...));
56void getstr __P((char *, int, char *));
57int local_domain __P((char *));
58char *topdomain __P((char *));
59void usage __P((void));
60
61int
1d68cba5
BJ
62main(argc, argv)
63 int argc;
5492e445 64 char *argv[];
1d68cba5 65{
5492e445 66 extern int __check_rhosts_file;
bcb894cb 67 struct linger linger;
10869b2d 68 int ch, on = 1, fromlen;
1d68cba5 69 struct sockaddr_in from;
3f5b52bc 70
0d097e0d 71 openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
10869b2d
KB
72
73 opterr = 0;
56940953
MK
74 switch (ch) {
75 case 'a':
76 check_all = 1;
77 break;
10869b2d 78 case 'l':
5492e445 79 __check_rhosts_file = 0;
10869b2d 80 break;
e0ffdf97
KB
81 case 'n':
82 keepalive = 0;
83 break;
10869b2d
KB
84 case '?':
85 default:
0d097e0d 86 usage();
1e9b0900 87 exit(2);
10869b2d 88 }
26c5bd5e 89
10869b2d
KB
90 argc -= optind;
91 argv += optind;
92
bb933cc2 93 fromlen = sizeof (from);
ccb7ffcd 94 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
56940953 95 syslog(LOG_ERR, "getpeername: %m");
bb933cc2 96 _exit(1);
3f5b52bc 97 }
e0ffdf97
KB
98 if (keepalive &&
99 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
100 sizeof(on)) < 0)
3f99c0f7 101 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
bcb894cb
SL
102 linger.l_onoff = 1;
103 linger.l_linger = 60; /* XXX */
d4413a13
JL
104 if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
105 sizeof (linger)) < 0)
3f99c0f7 106 syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
4c1b6a2b 107 doit(&from);
5492e445 108 /* NOTREACHED */
a8e3386b
SL
109}
110
1d68cba5
BJ
111char username[20] = "USER=";
112char homedir[64] = "HOME=";
113char shell[64] = "SHELL=";
e515a157 114char path[100] = "PATH=";
1d68cba5 115char *envinit[] =
e515a157 116 {homedir, shell, path, username, 0};
1d68cba5
BJ
117char **environ;
118
5492e445 119void
4c1b6a2b 120doit(fromp)
1d68cba5
BJ
121 struct sockaddr_in *fromp;
122{
5492e445 123 extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */
3f5b52bc 124 struct hostent *hp;
5492e445 125 struct passwd *pwd;
dec47a22 126 u_short port;
046a4121 127 fd_set ready, readfrom;
5492e445 128 int cc, nfd, pv[2], pid, s;
1d68cba5 129 int one = 1;
5492e445
AC
130 char *hostname, *errorstr, *errorhost;
131 char *cp, sig, buf[BUFSIZ];
132 char cmdbuf[NCARGS+1], locuser[16], remuser[16];
046a4121 133 char remotehost[2 * MAXHOSTNAMELEN + 1];
26c5bd5e 134
1d68cba5
BJ
135 (void) signal(SIGINT, SIG_DFL);
136 (void) signal(SIGQUIT, SIG_DFL);
137 (void) signal(SIGTERM, SIG_DFL);
27890867 138#ifdef DEBUG
b52175dc 139 { int t = open(_PATH_TTY, 2);
1d68cba5
BJ
140 if (t >= 0) {
141 ioctl(t, TIOCNOTTY, (char *)0);
142 (void) close(t);
143 }
144 }
145#endif
1d68cba5 146 fromp->sin_port = ntohs((u_short)fromp->sin_port);
c7299f34 147 if (fromp->sin_family != AF_INET) {
1e9b0900
MK
148 syslog(LOG_ERR, "malformed \"from\" address (af %d)\n",
149 fromp->sin_family);
1d68cba5 150 exit(1);
d173f55d 151 }
4c1b6a2b
MK
152#ifdef IP_OPTIONS
153 {
154 u_char optbuf[BUFSIZ/3], *cp;
155 char lbuf[BUFSIZ], *lp;
156 int optsize = sizeof(optbuf), ipproto;
157 struct protoent *ip;
cf9611a9
MK
158#ifdef IP_OPTIONS
159 {
160 u_char optbuf[BUFSIZ/3], *cp;
161 char lbuf[BUFSIZ], *lp;
162 int optsize = sizeof(optbuf), ipproto;
163 struct protoent *ip;
164
165 if ((ip = getprotobyname("ip")) != NULL)
166 ipproto = ip->p_proto;
167 else
168 ipproto = IPPROTO_IP;
169 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) == 0 &&
170 optsize != 0) {
171 lp = lbuf;
172 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
173 sprintf(lp, " %2.2x", *cp);
174 syslog(LOG_NOTICE,
175 "Connection received using IP options (ignored):%s", lbuf);
176 if (setsockopt(0, ipproto, IP_OPTIONS,
177 (char *)NULL, &optsize) != 0) {
178 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
179 exit(1);
180 }
181 }
182 }
183#endif
4c1b6a2b
MK
184
185 if ((ip = getprotobyname("ip")) != NULL)
186 ipproto = ip->p_proto;
187 else
188 ipproto = IPPROTO_IP;
5416610d 189 if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
4c1b6a2b
MK
190 optsize != 0) {
191 lp = lbuf;
192 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
193 sprintf(lp, " %2.2x", *cp);
194 syslog(LOG_NOTICE,
1e9b0900
MK
195 "Connection received from %s using IP options (ignored):%s",
196 inet_ntoa(fromp->sin_addr), lbuf);
4c1b6a2b 197 if (setsockopt(0, ipproto, IP_OPTIONS,
ccb7ffcd 198 (char *)NULL, optsize) != 0) {
4c1b6a2b
MK
199 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
200 exit(1);
201 }
202 }
203 }
204#endif
26c5bd5e 205
cee3e39b
KF
206#ifdef KERBEROS
207 if (!use_kerberos)
26c5bd5e 208
1d68cba5
BJ
209 (void) alarm(60);
210 port = 0;
211 for (;;) {
212 char c;
cf5b24dd 213 if ((cc = read(STDIN_FILENO, &c, 1)) != 1) {
c7299f34
MK
214 if (cc < 0)
215 syslog(LOG_NOTICE, "read: %m");
4c1b6a2b 216 shutdown(0, 1+1);
1d68cba5 217 exit(1);
d173f55d 218 }
1d68cba5
BJ
219 port = port * 10 + c - '0';
220 }
26c5bd5e 221
1d68cba5
BJ
222 (void) alarm(0);
223 if (port != 0) {
d4413a13 224 int lport = IPPORT_RESERVED - 1;
3b0e7dec 225 s = rresvport(&lport);
d173f55d 226 if (s < 0) {
3f99c0f7 227 syslog(LOG_ERR, "can't get stderr port: %m");
d173f55d
SL
228 exit(1);
229 }
cee3e39b
KF
230#ifdef KERBEROS
231 if (!use_kerberos)
dec47a22 232 fromp->sin_port = htons(port);
ccb7ffcd 233 if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) {
5492e445 234 syslog(LOG_INFO, "connect second port %d: %m", port);
1d68cba5 235 exit(1);
d173f55d 236 }
1d68cba5 237 }
26c5bd5e 238
046a4121 239#ifdef notdef
4c1b6a2b 240 /* from inetd, socket is already on 0, 1, 2 */
d173f55d
SL
241 dup2(f, 0);
242 dup2(f, 1);
243 dup2(f, 2);
046a4121 244#endif
5492e445 245 errorstr = NULL;
d4413a13 246 hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr),
3f5b52bc 247 fromp->sin_family);
26c5bd5e 248 if (hp) {
046a4121
MK
249 /*
250 * If name returned by gethostbyaddr is in our domain,
251 * attempt to verify that we haven't been fooled by someone
252 * in a remote net; look up the name and check that this
253 * address corresponds to the name.
254 */
25a61f03 255 hostname = hp->h_name;
1e9b0900
MK
256#ifdef KERBEROS
257 if (!use_kerberos)
258#endif
259 if (check_all || local_domain(hp->h_name)) {
046a4121
MK
260 strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
261 remotehost[sizeof(remotehost) - 1] = 0;
1e9b0900 262 errorhost = remotehost;
26c5bd5e 263 hp = gethostbyname(remotehost);
046a4121
MK
264 if (hp == NULL) {
265 syslog(LOG_INFO,
266 "Couldn't look up address for %s",
267 remotehost);
dec47a22 268 errorstr =
1e9b0900
MK
269 "Couldn't look up address for your host (%s)\n";
270 hostname = inet_ntoa(fromp->sin_addr);
beaa2897
MK
271 }
272#ifdef h_addr /* 4.2 hack */
273 for (; ; hp->h_addr_list++) {
046a4121
MK
274 if (hp->h_addr_list[0] == NULL) {
275 syslog(LOG_NOTICE,
276 "Host addr %s not listed for host %s",
277 inet_ntoa(fromp->sin_addr),
278 hp->h_name);
1e9b0900
MK
279 errorstr =
280 "Host address mismatch for %s\n";
281 hostname = inet_ntoa(fromp->sin_addr);
282 break;
26c5bd5e 283 }
5416610d
KB
284 if (!bcmp(hp->h_addr_list[0],
285 (caddr_t)&fromp->sin_addr,
1e9b0900
MK
286 sizeof(fromp->sin_addr))) {
287 hostname = hp->h_name;
5416610d 288 break;
1e9b0900 289 }
26c5bd5e 290 }
beaa2897
MK
291#else
292 if (bcmp(hp->h_addr, (caddr_t)&fromp->sin_addr,
293 sizeof(fromp->sin_addr))) {
294 syslog(LOG_NOTICE,
295 "Host addr %s not listed for host %s",
296 inet_ntoa(fromp->sin_addr),
297 hp->h_name);
298 error("Host address mismatch\n");
299 exit(1);
300 }
301#endif
26c5bd5e 302 }
046a4121 303 } else
1e9b0900 304 errorhost = hostname = inet_ntoa(fromp->sin_addr);
26c5bd5e 305
0d23e75d 306 getstr(remuser, sizeof(remuser), "remuser");
1d68cba5
BJ
307 getstr(locuser, sizeof(locuser), "locuser");
308 getstr(cmdbuf, sizeof(cmdbuf), "command");
309 setpwent();
310 pwd = getpwnam(locuser);
311 if (pwd == NULL) {
5492e445
AC
312 syslog(LOG_INFO|LOG_AUTH,
313 "%s@%s as %s: unknown login. cmd='%.80s'",
314 remuser, hostname, locuser, cmdbuf);
1e9b0900
MK
315 if (errorstr == NULL)
316 errorstr = "Login incorrect.\n";
317 goto fail;
1d68cba5 318 }
1d68cba5 319 if (chdir(pwd->pw_dir) < 0) {
d4413a13 320 (void) chdir("/");
bb933cc2 321#ifdef notdef
5492e445
AC
322 syslog(LOG_INFO|LOG_AUTH,
323 "%s@%s as %s: no home directory. cmd='%.80s'",
324 remuser, hostname, locuser, cmdbuf);
1d68cba5
BJ
325 error("No remote directory.\n");
326 exit(1);
bb933cc2 327#endif
1d68cba5 328 }
26c5bd5e 329
0d23e75d
MK
330 if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
331 ruserok(hostname, pwd->pw_uid == 0, remuser, locuser) < 0) {
332 error("Permission denied.\n");
333 exit(1);
334 }
26c5bd5e 335
17ccb086 336 if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) {
c9db37f1
KB
337 error("Logins currently disabled.\n");
338 exit(1);
339 }
beaa2897 340
cf5b24dd 341 (void) write(STDERR_FILENO, "\0", 1);
56940953 342 sent_null = 1;
beaa2897 343 sent_null = 1;
26c5bd5e 344
1d68cba5
BJ
345 if (port) {
346 if (pipe(pv) < 0) {
347 error("Can't make pipe.\n");
348 exit(1);
349 }
350 pid = fork();
351 if (pid == -1) {
56940953 352 error("Can't fork; try again.\n");
1d68cba5
BJ
353 exit(1);
354 }
355 if (pid) {
d6dd6df6 356 {
5492e445
AC
357 (void) close(0);
358 (void) close(1);
d6dd6df6 359 }
5492e445
AC
360 (void) close(2);
361 (void) close(pv[1]);
d6dd6df6 362
046a4121
MK
363 FD_ZERO(&readfrom);
364 FD_SET(s, &readfrom);
365 FD_SET(pv[0], &readfrom);
56940953
MK
366 if (pv[0] > s)
367 nfd = pv[0];
368 else
369 nfd = s;
d6dd6df6
KF
370 ioctl(pv[0], FIONBIO, (char *)&one);
371
1d68cba5 372 /* should set s nbio! */
56940953 373 nfd++;
1d68cba5
BJ
374 do {
375 ready = readfrom;
d6dd6df6 376 if (select(nfd, &ready, (fd_set *)0,
dec47a22 377 (fd_set *)0, (struct timeval *)0) < 0)
d6dd6df6 378 break;
046a4121 379 if (FD_ISSET(s, &ready)) {
d6dd6df6 380 int ret;
d6dd6df6
KF
381 ret = read(s, &sig, 1);
382 if (ret <= 0)
046a4121 383 FD_CLR(s, &readfrom);
1d68cba5
BJ
384 else
385 killpg(pid, sig);
386 }
046a4121 387 if (FD_ISSET(pv[0], &ready)) {
fadc6b8f 388 errno = 0;
d6dd6df6 389 cc = read(pv[0], buf, sizeof(buf));
1d68cba5 390 if (cc <= 0) {
ff24c640 391 shutdown(s, 1+1);
046a4121 392 FD_CLR(pv[0], &readfrom);
d6dd6df6 393 } else {
d6dd6df6
KF
394 (void)
395 write(s, buf, cc);
396 }
397 }
d6dd6df6 398
046a4121
MK
399 } while (FD_ISSET(s, &readfrom) ||
400 FD_ISSET(pv[0], &readfrom));
1d68cba5
BJ
401 exit(0);
402 }
403 setpgrp(0, getpid());
5492e445
AC
404 (void) close(s);
405 (void) close(pv[0]);
1d68cba5 406 dup2(pv[1], 2);
389cc7a4 407 close(pv[1]);
beaa2897 408 close(pv[1]);
1d68cba5
BJ
409 }
410 if (*pwd->pw_shell == '\0')
17ccb086 411 pwd->pw_shell = _PATH_BSHELL;
d6dd6df6 412#if BSD > 43
56940953
MK
413 if (setlogin(pwd->pw_name) < 0)
414 syslog(LOG_ERR, "setlogin() failed: %m");
d6dd6df6 415#endif
56940953
MK
416 (void) setgid((gid_t)pwd->pw_gid);
417 initgroups(pwd->pw_name, pwd->pw_gid);
d4413a13 418 (void) setuid((uid_t)pwd->pw_uid);
1d68cba5
BJ
419 environ = envinit;
420 strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
e515a157 421 strcat(path, _PATH_DEFPATH);
1d68cba5
BJ
422 strncat(shell, pwd->pw_shell, sizeof(shell)-7);
423 strncat(username, pwd->pw_name, sizeof(username)-6);
424 cp = rindex(pwd->pw_shell, '/');
425 if (cp)
426 cp++;
427 else
428 cp = pwd->pw_shell;
cee3e39b 429 endpwent();
beaa2897
MK
430 if (pwd->pw_uid == 0)
431 syslog(LOG_INFO|LOG_AUTH, "ROOT shell from %s@%s, comm: %s\n",
432 remuser, hostname, cmdbuf);
433 endpwent();
5492e445 434 if (log_success || pwd->pw_uid == 0) {
49114004
KF
435#ifdef KERBEROS
436 if (use_kerberos)
5492e445
AC
437 syslog(LOG_INFO|LOG_AUTH,
438 "Kerberos shell from %s.%s@%s on %s as %s, cmd='%.80s'",
439 kdata->pname, kdata->pinst, kdata->prealm,
440 hostname, locuser, cmdbuf);
49114004
KF
441 else
442#endif
5492e445
AC
443 syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
444 remuser, hostname, locuser, cmdbuf);
49114004 445 }
1d68cba5
BJ
446 execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
447 perror(pwd->pw_shell);
448 exit(1);
1d68cba5
BJ
449}
450
beaa2897
MK
451/*
452 * Report error to client.
453 * Note: can't be used until second socket has connected
454 * to client, or older clients will hang waiting
455 * for that connection first.
456 */
56940953 457/*
5492e445
AC
458 * Report error to client. Note: can't be used until second socket has
459 * connected to client, or older clients will hang waiting for that
460 * connection first.
56940953 461 */
5492e445
AC
462#if __STDC__
463#include <stdarg.h>
464#else
465#include <varargs.h>
466#endif
467
468void
469#if __STDC__
470error(const char *fmt, ...)
471#else
472error(fmt, va_alist)
1d68cba5 473 char *fmt;
5492e445
AC
474 va_dcl
475#endif
1d68cba5 476{
5492e445
AC
477 va_list ap;
478 int len;
479 char *bp, buf[BUFSIZ];
480#if __STDC__
481 va_start(ap, fmt);
482#else
483 va_start(ap);
484#endif
485 bp = buf;
486 if (sent_null == 0) {
56940953 487 *bp++ = 1;
5492e445
AC
488 len = 1;
489 } else
490 len = 0;
2dbdf5a9
KB
491 (void)vsnprintf(bp, sizeof(buf) - 1, fmt, ap);
492 (void)write(STDERR_FILENO, buf, len + strlen(bp));
1d68cba5
BJ
493}
494
5492e445 495void
1d68cba5 496getstr(buf, cnt, err)
5492e445 497 char *buf, *err;
1d68cba5 498 int cnt;
1d68cba5
BJ
499{
500 char c;
501
502 do {
cf5b24dd 503 if (read(STDIN_FILENO, &c, 1) != 1)
1d68cba5
BJ
504 exit(1);
505 *buf++ = c;
506 if (--cnt == 0) {
507 error("%s too long\n", err);
508 exit(1);
509 }
510 } while (c != 0);
511}
26c5bd5e 512
046a4121
MK
513/*
514 * Check whether host h is in our local domain,
56940953
MK
515 * defined as sharing the last two components of the domain part,
516 * or the entire domain part if the local domain has only one component.
046a4121
MK
517 * If either name is unqualified (contains no '.'),
518 * assume that the host is local, as it will be
519 * interpreted as such.
520 */
5492e445 521int
046a4121
MK
522local_domain(h)
523 char *h;
26c5bd5e 524{
046a4121 525 char localhost[MAXHOSTNAMELEN];
5492e445 526 char *p1, *p2;
046a4121 527
beaa2897 528 localhost[0] = 0;
56940953 529 localhost[0] = 0;
046a4121 530 (void) gethostname(localhost, sizeof(localhost));
56940953
MK
531 p1 = topdomain(localhost);
532 p2 = topdomain(h);
046a4121 533 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
5492e445
AC
534 return (1);
535 return (0);
26c5bd5e 536}
0d097e0d 537
56940953
MK
538char *
539topdomain(h)
540 char *h;
541{
542 register char *p;
543 char *maybe = NULL;
544 int dots = 0;
545
546 for (p = h + strlen(h); p >= h; p--) {
547 if (*p == '.') {
548 if (++dots == 2)
549 return (p);
550 maybe = p;
551 }
552 }
553 return (maybe);
554}
555
beaa2897
MK
556char *
557topdomain(h)
558 char *h;
559{
560 register char *p;
561 char *maybe = NULL;
562 int dots = 0;
563
564 for (p = h + strlen(h); p >= h; p--) {
565 if (*p == '.') {
566 if (++dots == 2)
567 return (p);
568 maybe = p;
569 }
570 }
571 return (maybe);
572}
573
5492e445 574void
0d097e0d
KF
575usage()
576{
8ae69711 577 syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS);
0d097e0d 578}