checked in for Marc; needed USE_OLD_TTY
[unix-history] / usr / src / libexec / rshd / rshd.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1983, 1988, 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17
18#ifndef lint
19char copyright[] =
20"@(#) Copyright (c) 1983, 1988, 1089 The Regents of the University of California.\n\
21 All rights reserved.\n";
22#endif /* not lint */
23
24#ifndef lint
25static char sccsid[] = "@(#)rshd.c 5.30 (Berkeley) %G%";
26#endif /* not lint */
27
28/* From:
29 * $Source: /mit/kerberos/ucb/mit/rshd/RCS/rshd.c,v $
30 * $Header: /mit/kerberos/ucb/mit/rshd/RCS/rshd.c,v 5.2 89/07/31 19:30:04 kfall Exp $
31 */
32
33
34/*
35 * remote shell server:
36 * [port]\0
37 * remuser\0
38 * locuser\0
39 * command\0
40 * data
41 */
42#include <sys/param.h>
43#include <sys/ioctl.h>
44#include <sys/socket.h>
45#include <sys/file.h>
46#include <sys/signal.h>
47#include <sys/signal.h>
48#include <sys/time.h>
49
50#include <netinet/in.h>
51
52#include <arpa/inet.h>
53
54#include <stdio.h>
55#include <errno.h>
56#include <pwd.h>
57#include <netdb.h>
58#include <syslog.h>
59#include "pathnames.h"
60#include "pathnames.h"
61
62int errno;
63int keepalive = 1;
64int check_all = 0;
65int check_all = 0;
66char *index(), *rindex(), *strncat();
67/*VARARGS1*/
68int error();
69int sent_null;
70int sent_null;
71
72/*ARGSUSED*/
73main(argc, argv)
74 int argc;
75 char **argv;
76{
77
78 extern int opterr, optind;
79 extern int _check_rhosts_file;
80 struct linger linger;
81 int ch, on = 1, fromlen;
82 struct sockaddr_in from;
83
84 openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
85
86 opterr = 0;
87 switch (ch) {
88 case 'a':
89 check_all = 1;
90 break;
91 case 'l':
92 _check_rhosts_file = 0;
93 break;
94 case 'n':
95 keepalive = 0;
96 break;
97 case '?':
98 default:
99 usage();
100 exit(2);
101 }
102
103 argc -= optind;
104 argv += optind;
105
106 fromlen = sizeof (from);
107 if (getpeername(0, &from, &fromlen) < 0) {
108 syslog(LOG_ERR, "getpeername: %m");
109 _exit(1);
110 }
111 if (keepalive &&
112 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
113 sizeof(on)) < 0)
114 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
115 linger.l_onoff = 1;
116 linger.l_linger = 60; /* XXX */
117 if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
118 sizeof (linger)) < 0)
119 syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
120 doit(&from);
121}
122
123char username[20] = "USER=";
124char homedir[64] = "HOME=";
125char shell[64] = "SHELL=";
126char *envinit[] =
127 {homedir, shell, _PATH_DEFPATH, username, 0};
128char **environ;
129
130doit(fromp)
131 struct sockaddr_in *fromp;
132{
133 char cmdbuf[NCARGS+1], *cp;
134 char locuser[16], remuser[16];
135 struct passwd *pwd;
136 int s;
137 struct hostent *hp;
138 char *hostname, *errorstr = NULL, *errorhost;
139 short port;
140 int pv[2], pid, cc;
141 int nfd;
142 fd_set ready, readfrom;
143 char buf[BUFSIZ], sig;
144 int one = 1;
145 char remotehost[2 * MAXHOSTNAMELEN + 1];
146
147 (void) signal(SIGINT, SIG_DFL);
148 (void) signal(SIGQUIT, SIG_DFL);
149 (void) signal(SIGTERM, SIG_DFL);
150#ifdef DEBUG
151 { int t = open(_PATH_TTY, 2);
152 if (t >= 0) {
153 ioctl(t, TIOCNOTTY, (char *)0);
154 (void) close(t);
155 }
156 }
157#endif
158 fromp->sin_port = ntohs((u_short)fromp->sin_port);
159 if (fromp->sin_family != AF_INET) {
160 syslog(LOG_ERR, "malformed \"from\" address (af %d)\n",
161 fromp->sin_family);
162 exit(1);
163 }
164#ifdef IP_OPTIONS
165 {
166 u_char optbuf[BUFSIZ/3], *cp;
167 char lbuf[BUFSIZ], *lp;
168 int optsize = sizeof(optbuf), ipproto;
169 struct protoent *ip;
170#ifdef IP_OPTIONS
171 {
172 u_char optbuf[BUFSIZ/3], *cp;
173 char lbuf[BUFSIZ], *lp;
174 int optsize = sizeof(optbuf), ipproto;
175 struct protoent *ip;
176
177 if ((ip = getprotobyname("ip")) != NULL)
178 ipproto = ip->p_proto;
179 else
180 ipproto = IPPROTO_IP;
181 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) == 0 &&
182 optsize != 0) {
183 lp = lbuf;
184 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
185 sprintf(lp, " %2.2x", *cp);
186 syslog(LOG_NOTICE,
187 "Connection received using IP options (ignored):%s", lbuf);
188 if (setsockopt(0, ipproto, IP_OPTIONS,
189 (char *)NULL, &optsize) != 0) {
190 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
191 exit(1);
192 }
193 }
194 }
195#endif
196
197 if ((ip = getprotobyname("ip")) != NULL)
198 ipproto = ip->p_proto;
199 else
200 ipproto = IPPROTO_IP;
201 if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
202 optsize != 0) {
203 lp = lbuf;
204 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
205 sprintf(lp, " %2.2x", *cp);
206 syslog(LOG_NOTICE,
207 "Connection received from %s using IP options (ignored):%s",
208 inet_ntoa(fromp->sin_addr), lbuf);
209 if (setsockopt(0, ipproto, IP_OPTIONS,
210 (char *)NULL, &optsize) != 0) {
211 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
212 exit(1);
213 }
214 }
215 }
216#endif
217
218#ifdef KERBEROS
219 if (!use_kerberos)
220
221 (void) alarm(60);
222 port = 0;
223 for (;;) {
224 char c;
225 if ((cc = read(0, &c, 1)) != 1) {
226 if (cc < 0)
227 syslog(LOG_NOTICE, "read: %m");
228 shutdown(0, 1+1);
229 exit(1);
230 }
231 port = port * 10 + c - '0';
232 }
233
234 (void) alarm(0);
235 if (port != 0) {
236 int lport = IPPORT_RESERVED - 1;
237 s = rresvport(&lport);
238 if (s < 0) {
239 syslog(LOG_ERR, "can't get stderr port: %m");
240 exit(1);
241 }
242#ifdef KERBEROS
243 if (!use_kerberos)
244 fromp->sin_port = htons((u_short)port);
245 if (connect(s, fromp, sizeof (*fromp)) < 0) {
246 syslog(LOG_INFO, "connect second port: %m");
247 exit(1);
248 }
249 }
250
251#ifdef notdef
252 /* from inetd, socket is already on 0, 1, 2 */
253 dup2(f, 0);
254 dup2(f, 1);
255 dup2(f, 2);
256#endif
257 hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr),
258 fromp->sin_family);
259 if (hp) {
260 /*
261 * If name returned by gethostbyaddr is in our domain,
262 * attempt to verify that we haven't been fooled by someone
263 * in a remote net; look up the name and check that this
264 * address corresponds to the name.
265 */
266#ifdef KERBEROS
267 if (!use_kerberos)
268#endif
269 if (check_all || local_domain(hp->h_name)) {
270 strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
271 remotehost[sizeof(remotehost) - 1] = 0;
272 errorhost = remotehost;
273 hp = gethostbyname(remotehost);
274 if (hp == NULL) {
275 syslog(LOG_INFO,
276 "Couldn't look up address for %s",
277 remotehost);
278 errorstr =
279 "Couldn't look up address for your host (%s)\n";
280 hostname = inet_ntoa(fromp->sin_addr);
281 }
282#ifdef h_addr /* 4.2 hack */
283 for (; ; hp->h_addr_list++) {
284 if (hp->h_addr_list[0] == NULL) {
285 syslog(LOG_NOTICE,
286 "Host addr %s not listed for host %s",
287 inet_ntoa(fromp->sin_addr),
288 hp->h_name);
289 errorstr =
290 "Host address mismatch for %s\n";
291 hostname = inet_ntoa(fromp->sin_addr);
292 break;
293 }
294 if (!bcmp(hp->h_addr_list[0],
295 (caddr_t)&fromp->sin_addr,
296 sizeof(fromp->sin_addr))) {
297 hostname = hp->h_name;
298 break;
299 }
300 }
301#else
302 if (bcmp(hp->h_addr, (caddr_t)&fromp->sin_addr,
303 sizeof(fromp->sin_addr))) {
304 syslog(LOG_NOTICE,
305 "Host addr %s not listed for host %s",
306 inet_ntoa(fromp->sin_addr),
307 hp->h_name);
308 error("Host address mismatch\n");
309 exit(1);
310 }
311#endif
312 }
313 } else
314 errorhost = hostname = inet_ntoa(fromp->sin_addr);
315
316 getstr(remuser, sizeof(remuser), "remuser");
317 getstr(locuser, sizeof(locuser), "locuser");
318 getstr(cmdbuf, sizeof(cmdbuf), "command");
319 setpwent();
320 pwd = getpwnam(locuser);
321 if (pwd == NULL) {
322 if (errorstr == NULL)
323 errorstr = "Login incorrect.\n";
324 goto fail;
325 }
326 if (chdir(pwd->pw_dir) < 0) {
327 (void) chdir("/");
328#ifdef notdef
329 error("No remote directory.\n");
330 exit(1);
331#endif
332 }
333
334 if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
335 ruserok(hostname, pwd->pw_uid == 0, remuser, locuser) < 0) {
336 error("Permission denied.\n");
337 exit(1);
338 }
339
340 if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) {
341 error("Logins currently disabled.\n");
342 exit(1);
343 }
344
345 (void) write(2, "\0", 1);
346 sent_null = 1;
347 sent_null = 1;
348
349 if (port) {
350 if (pipe(pv) < 0) {
351 error("Can't make pipe.\n");
352 exit(1);
353 }
354#ifdef KERBEROS
355 if (encrypt) {
356 if (pipe(pv1) < 0) {
357 error("Can't make 2nd pipe.\n");
358 exit(1);
359 }
360 if (pipe(pv2) < 0) {
361 error("Can't make 3rd pipe.\n");
362 exit(1);
363 }
364 }
365#endif
366 pid = fork();
367 if (pid == -1) {
368 error("Can't fork; try again.\n");
369 exit(1);
370 }
371 if (pid) {
372#ifdef KERBEROS
373 if (encrypt) {
374 static char msg[] = SECURE_MESSAGE;
375 (void) close(pv1[1]);
376 (void) close(pv2[1]);
377 des_write(s, msg, sizeof(msg));
378
379 } else
380#endif
381 {
382 (void) close(0); (void) close(1);
383 }
384 (void) close(2); (void) close(pv[1]);
385
386 FD_ZERO(&readfrom);
387 FD_SET(s, &readfrom);
388 FD_SET(pv[0], &readfrom);
389 if (pv[0] > s)
390 nfd = pv[0];
391 else
392 nfd = s;
393#ifdef KERBEROS
394 if (encrypt) {
395 FD_ZERO(&writeto);
396 FD_SET(pv2[0], &writeto);
397 FD_SET(pv1[0], &readfrom);
398
399 nfd = MAX(nfd, pv2[0]);
400 nfd = MAX(nfd, pv1[0]);
401 } else
402#endif
403 ioctl(pv[0], FIONBIO, (char *)&one);
404
405 /* should set s nbio! */
406 nfd++;
407 do {
408 ready = readfrom;
409#ifdef KERBEROS
410 if (encrypt) {
411 wready = writeto;
412 if (select(nfd, &ready,
413 &wready, (fd_set *) 0,
414 (struct timeval *) 0) < 0)
415 break;
416 } else
417#endif
418 if (select(nfd, &ready, (fd_set *)0,
419 (fd_set *)0, (struct timeval *)0) < 0)
420 break;
421 if (FD_ISSET(s, &ready)) {
422 int ret;
423#ifdef KERBEROS
424 if (encrypt)
425 ret = des_read(s, &sig, 1);
426 else
427#endif
428 ret = read(s, &sig, 1);
429 if (ret <= 0)
430 FD_CLR(s, &readfrom);
431 else
432 killpg(pid, sig);
433 }
434 if (FD_ISSET(pv[0], &ready)) {
435 errno = 0;
436 cc = read(pv[0], buf, sizeof(buf));
437 if (cc <= 0) {
438 shutdown(s, 1+1);
439 FD_CLR(pv[0], &readfrom);
440 } else {
441#ifdef KERBEROS
442 if (encrypt)
443 (void)
444 des_write(s, buf, cc);
445 else
446#endif
447 (void)
448 write(s, buf, cc);
449 }
450 }
451#ifdef KERBEROS
452
453 if (encrypt && FD_ISSET(pv1[0], &ready)) {
454 errno = 0;
455 cc = read(pv1[0], buf, sizeof(buf));
456 if (cc <= 0) {
457 shutdown(pv1[0], 1+1);
458 FD_CLR(pv1[0], &readfrom);
459 } else
460 (void) des_write(1, buf, cc);
461 }
462
463 if (encrypt && FD_ISSET(pv2[0], &wready)) {
464 errno = 0;
465 cc = des_read(0, buf, sizeof(buf));
466 if (cc <= 0) {
467 shutdown(pv2[0], 1+1);
468 FD_CLR(pv2[0], &writeto);
469 } else
470 (void) write(pv2[0], buf, cc);
471 }
472#endif
473
474 } while (FD_ISSET(s, &readfrom) ||
475#ifdef KERBEROS
476 (encrypt && FD_ISSET(pv1[0], &readfrom)) ||
477#endif
478 FD_ISSET(pv[0], &readfrom));
479 exit(0);
480 }
481 setpgrp(0, getpid());
482 (void) close(s); (void) close(pv[0]);
483#ifdef KERBEROS
484 if (encrypt) {
485 close(pv1[0]); close(pv2[0]);
486 dup2(pv1[1], 1);
487 dup2(pv2[1], 0);
488 close(pv1[1]);
489 close(pv2[1]);
490 }
491#endif
492 dup2(pv[1], 2);
493 close(pv[1]);
494 close(pv[1]);
495 }
496 if (*pwd->pw_shell == '\0')
497 pwd->pw_shell = _PATH_BSHELL;
498#if BSD > 43
499 if (setlogin(pwd->pw_name) < 0)
500 syslog(LOG_ERR, "setlogin() failed: %m");
501#endif
502 (void) setgid((gid_t)pwd->pw_gid);
503 initgroups(pwd->pw_name, pwd->pw_gid);
504 (void) setuid((uid_t)pwd->pw_uid);
505 environ = envinit;
506 strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
507 strncat(shell, pwd->pw_shell, sizeof(shell)-7);
508 strncat(username, pwd->pw_name, sizeof(username)-6);
509 cp = rindex(pwd->pw_shell, '/');
510 if (cp)
511 cp++;
512 else
513 cp = pwd->pw_shell;
514 endpwent();
515 if (pwd->pw_uid == 0)
516 syslog(LOG_INFO|LOG_AUTH, "ROOT shell from %s@%s, comm: %s\n",
517 remuser, hostname, cmdbuf);
518 endpwent();
519 if (pwd->pw_uid == 0) {
520#ifdef KERBEROS
521 if (use_kerberos)
522 syslog(LOG_INFO|LOG_AUTH,
523 "ROOT Kerberos shell from %s.%s@%s on %s, comm: %s\n",
524 kdata->pname, kdata->pinst, kdata->prealm,
525 hostname, cmdbuf);
526 else
527#endif
528 syslog(LOG_INFO|LOG_AUTH,
529 "ROOT shell from %s@%s, comm: %s\n",
530 remuser, hostname, cmdbuf);
531 }
532 execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
533 perror(pwd->pw_shell);
534 exit(1);
535}
536
537/*
538 * Report error to client.
539 * Note: can't be used until second socket has connected
540 * to client, or older clients will hang waiting
541 * for that connection first.
542 */
543/*
544 * Report error to client.
545 * Note: can't be used until second socket has connected
546 * to client, or older clients will hang waiting
547 * for that connection first.
548 */
549/*VARARGS1*/
550error(fmt, a1, a2, a3)
551 char *fmt;
552 int a1, a2, a3;
553{
554 char buf[BUFSIZ], *bp = buf;
555
556 if (sent_null == 0)
557 *bp++ = 1;
558 (void) sprintf(bp, fmt, a1, a2, a3);
559 (void) write(2, buf, strlen(buf));
560}
561
562getstr(buf, cnt, err)
563 char *buf;
564 int cnt;
565 char *err;
566{
567 char c;
568
569 do {
570 if (read(0, &c, 1) != 1)
571 exit(1);
572 *buf++ = c;
573 if (--cnt == 0) {
574 error("%s too long\n", err);
575 exit(1);
576 }
577 } while (c != 0);
578}
579
580/*
581 * Check whether host h is in our local domain,
582 * defined as sharing the last two components of the domain part,
583 * or the entire domain part if the local domain has only one component.
584 * If either name is unqualified (contains no '.'),
585 * assume that the host is local, as it will be
586 * interpreted as such.
587 */
588local_domain(h)
589 char *h;
590{
591 char localhost[MAXHOSTNAMELEN];
592 char *p1, *p2, *topdomain();
593
594 localhost[0] = 0;
595 localhost[0] = 0;
596 (void) gethostname(localhost, sizeof(localhost));
597 p1 = topdomain(localhost);
598 p2 = topdomain(h);
599 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
600 return(1);
601 return(0);
602}
603
604char *
605topdomain(h)
606 char *h;
607{
608 register char *p;
609 char *maybe = NULL;
610 int dots = 0;
611
612 for (p = h + strlen(h); p >= h; p--) {
613 if (*p == '.') {
614 if (++dots == 2)
615 return (p);
616 maybe = p;
617 }
618 }
619 return (maybe);
620}
621
622char *
623topdomain(h)
624 char *h;
625{
626 register char *p;
627 char *maybe = NULL;
628 int dots = 0;
629
630 for (p = h + strlen(h); p >= h; p--) {
631 if (*p == '.') {
632 if (++dots == 2)
633 return (p);
634 maybe = p;
635 }
636 }
637 return (maybe);
638}
639
640usage()
641{
642#ifdef KERBEROS
643 syslog(LOG_ERR, "usage: rshd [-aln]");
644#else
645 syslog(LOG_ERR, "usage: rshd [-alknvx]");
646#endif
647}