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