Annotation of src/libexec/rshd/rshd.c, Revision 1.15
1.15 ! mycroft 1: /* $NetBSD: rshd.c,v 1.14 1998/07/06 06:48:56 mrg Exp $ */
1.10 mrg 2:
1.1 cgd 3: /*-
1.7 cgd 4: * Copyright (c) 1988, 1989, 1992, 1993, 1994
5: * The Regents of the University of California. All rights reserved.
1.1 cgd 6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: * 3. All advertising materials mentioning features or use of this software
16: * must display the following acknowledgement:
17: * This product includes software developed by the University of
18: * California, Berkeley and its contributors.
19: * 4. Neither the name of the University nor the names of its contributors
20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
1.10 mrg 36: #include <sys/cdefs.h>
1.1 cgd 37: #ifndef lint
1.10 mrg 38: __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\
39: The Regents of the University of California. All rights reserved.\n");
40: #if 0
41: static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94";
42: #else
1.15 ! mycroft 43: __RCSID("$NetBSD: rshd.c,v 1.14 1998/07/06 06:48:56 mrg Exp $");
1.10 mrg 44: #endif
1.1 cgd 45: #endif /* not lint */
46:
47: /*
48: * remote shell server:
49: * [port]\0
50: * remuser\0
51: * locuser\0
52: * command\0
53: * data
54: */
55: #include <sys/param.h>
56: #include <sys/ioctl.h>
57: #include <sys/time.h>
1.7 cgd 58: #include <sys/socket.h>
1.1 cgd 59:
60: #include <netinet/in.h>
61: #include <arpa/inet.h>
62: #include <netdb.h>
63:
1.7 cgd 64: #include <errno.h>
65: #include <fcntl.h>
66: #include <paths.h>
1.1 cgd 67: #include <pwd.h>
1.7 cgd 68: #include <signal.h>
1.1 cgd 69: #include <stdio.h>
70: #include <stdlib.h>
71: #include <string.h>
1.7 cgd 72: #include <syslog.h>
73: #include <unistd.h>
1.1 cgd 74:
75: int keepalive = 1;
1.7 cgd 76: int check_all;
77: int log_success; /* If TRUE, log all successful accesses */
1.1 cgd 78: int sent_null;
79:
1.7 cgd 80: void doit __P((struct sockaddr_in *));
81: void error __P((const char *, ...));
82: void getstr __P((char *, int, char *));
83: int local_domain __P((char *));
84: char *topdomain __P((char *));
85: void usage __P((void));
1.10 mrg 86: int main __P((int, char *[]));
1.7 cgd 87:
1.3 cgd 88: #define OPTIONS "alnL"
1.1 cgd 89:
1.7 cgd 90: int
1.1 cgd 91: main(argc, argv)
92: int argc;
1.7 cgd 93: char *argv[];
1.1 cgd 94: {
1.6 pk 95: extern int __check_rhosts_file;
1.1 cgd 96: struct linger linger;
97: int ch, on = 1, fromlen;
98: struct sockaddr_in from;
99:
100: openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
101:
102: opterr = 0;
1.11 enami 103: while ((ch = getopt(argc, argv, OPTIONS)) != -1)
1.1 cgd 104: switch (ch) {
105: case 'a':
106: check_all = 1;
107: break;
108: case 'l':
1.6 pk 109: __check_rhosts_file = 0;
1.1 cgd 110: break;
111: case 'n':
112: keepalive = 0;
113: break;
1.3 cgd 114: case 'L':
1.7 cgd 115: log_success = 1;
1.3 cgd 116: break;
1.1 cgd 117: case '?':
118: default:
119: usage();
1.7 cgd 120: break;
1.1 cgd 121: }
122:
123: argc -= optind;
124: argv += optind;
125:
126:
127: fromlen = sizeof (from);
128: if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
129: syslog(LOG_ERR, "getpeername: %m");
130: _exit(1);
131: }
132: if (keepalive &&
133: setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
134: sizeof(on)) < 0)
135: syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
136: linger.l_onoff = 1;
137: linger.l_linger = 60; /* XXX */
138: if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
139: sizeof (linger)) < 0)
140: syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
141: doit(&from);
1.7 cgd 142: /* NOTREACHED */
1.10 mrg 143: #ifdef __GNUC__
144: exit(0);
145: #endif
1.1 cgd 146: }
147:
148: char username[20] = "USER=";
149: char homedir[64] = "HOME=";
150: char shell[64] = "SHELL=";
151: char path[100] = "PATH=";
152: char *envinit[] =
153: {homedir, shell, path, username, 0};
154: char **environ;
155:
1.7 cgd 156: void
1.1 cgd 157: doit(fromp)
158: struct sockaddr_in *fromp;
159: {
1.7 cgd 160: extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */
161: struct hostent *hp;
1.1 cgd 162: struct passwd *pwd;
1.14 mrg 163: in_port_t port;
1.1 cgd 164: fd_set ready, readfrom;
1.10 mrg 165: int cc, nfd, pv[2], pid, s = -1; /* XXX gcc */
1.1 cgd 166: int one = 1;
1.10 mrg 167: char *hostname, *errorstr, *errorhost = NULL; /* XXX gcc */
1.15 ! mycroft 168: const char *cp;
! 169: char sig, buf[BUFSIZ];
1.7 cgd 170: char cmdbuf[NCARGS+1], locuser[16], remuser[16];
1.1 cgd 171: char remotehost[2 * MAXHOSTNAMELEN + 1];
1.9 christos 172: char hostnamebuf[2 * MAXHOSTNAMELEN + 1];
1.1 cgd 173:
174:
175: (void) signal(SIGINT, SIG_DFL);
176: (void) signal(SIGQUIT, SIG_DFL);
177: (void) signal(SIGTERM, SIG_DFL);
178: #ifdef DEBUG
179: { int t = open(_PATH_TTY, 2);
180: if (t >= 0) {
181: ioctl(t, TIOCNOTTY, (char *)0);
182: (void) close(t);
183: }
184: }
185: #endif
1.14 mrg 186: fromp->sin_port = ntohs((in_port_t)fromp->sin_port);
1.1 cgd 187: if (fromp->sin_family != AF_INET) {
188: syslog(LOG_ERR, "malformed \"from\" address (af %d)\n",
189: fromp->sin_family);
190: exit(1);
191: }
192: #ifdef IP_OPTIONS
193: {
194: u_char optbuf[BUFSIZ/3], *cp;
195: char lbuf[BUFSIZ], *lp;
196: int optsize = sizeof(optbuf), ipproto;
197: struct protoent *ip;
198:
199: if ((ip = getprotobyname("ip")) != NULL)
200: ipproto = ip->p_proto;
201: else
202: ipproto = IPPROTO_IP;
203: if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
204: optsize != 0) {
205: lp = lbuf;
206: for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
207: sprintf(lp, " %2.2x", *cp);
208: syslog(LOG_NOTICE,
209: "Connection received from %s using IP options (ignored):%s",
210: inet_ntoa(fromp->sin_addr), lbuf);
211: if (setsockopt(0, ipproto, IP_OPTIONS,
212: (char *)NULL, optsize) != 0) {
213: syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
214: exit(1);
215: }
216: }
217: }
218: #endif
219:
1.13 enami 220: if (fromp->sin_port >= IPPORT_RESERVED ||
221: fromp->sin_port < IPPORT_RESERVED/2) {
222: syslog(LOG_NOTICE|LOG_AUTH,
223: "Connection from %s on illegal port %u",
224: inet_ntoa(fromp->sin_addr), fromp->sin_port);
225: exit(1);
226: }
1.1 cgd 227:
228: (void) alarm(60);
229: port = 0;
230: for (;;) {
231: char c;
1.13 enami 232:
1.7 cgd 233: if ((cc = read(STDIN_FILENO, &c, 1)) != 1) {
1.1 cgd 234: if (cc < 0)
235: syslog(LOG_NOTICE, "read: %m");
236: shutdown(0, 1+1);
237: exit(1);
238: }
1.13 enami 239: if (c == 0)
1.1 cgd 240: break;
241: port = port * 10 + c - '0';
242: }
243:
244: (void) alarm(0);
245: if (port != 0) {
246: int lport = IPPORT_RESERVED - 1;
247: s = rresvport(&lport);
248: if (s < 0) {
249: syslog(LOG_ERR, "can't get stderr port: %m");
250: exit(1);
251: }
1.12 lukem 252: if (port >= IPPORT_RESERVED) {
253: syslog(LOG_ERR, "2nd port not reserved\n");
254: exit(1);
255: }
1.1 cgd 256: fromp->sin_port = htons(port);
257: if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) {
1.7 cgd 258: syslog(LOG_INFO, "connect second port %d: %m", port);
1.1 cgd 259: exit(1);
260: }
261: }
262:
263:
264: #ifdef notdef
265: /* from inetd, socket is already on 0, 1, 2 */
266: dup2(f, 0);
267: dup2(f, 1);
268: dup2(f, 2);
269: #endif
1.7 cgd 270: errorstr = NULL;
1.1 cgd 271: hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr),
1.13 enami 272: fromp->sin_family);
1.1 cgd 273: if (hp) {
274: /*
275: * If name returned by gethostbyaddr is in our domain,
276: * attempt to verify that we haven't been fooled by someone
277: * in a remote net; look up the name and check that this
278: * address corresponds to the name.
279: */
280: hostname = hp->h_name;
281: if (check_all || local_domain(hp->h_name)) {
282: strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
283: remotehost[sizeof(remotehost) - 1] = 0;
284: errorhost = remotehost;
285: hp = gethostbyname(remotehost);
286: if (hp == NULL) {
287: syslog(LOG_INFO,
288: "Couldn't look up address for %s",
289: remotehost);
290: errorstr =
291: "Couldn't look up address for your host (%s)\n";
292: hostname = inet_ntoa(fromp->sin_addr);
293: } else for (; ; hp->h_addr_list++) {
294: if (hp->h_addr_list[0] == NULL) {
295: syslog(LOG_NOTICE,
296: "Host addr %s not listed for host %s",
297: inet_ntoa(fromp->sin_addr),
298: hp->h_name);
299: errorstr =
300: "Host address mismatch for %s\n";
301: hostname = inet_ntoa(fromp->sin_addr);
302: break;
303: }
304: if (!bcmp(hp->h_addr_list[0],
305: (caddr_t)&fromp->sin_addr,
306: sizeof(fromp->sin_addr))) {
307: hostname = hp->h_name;
308: break;
309: }
310: }
311: }
1.9 christos 312: hostname = strncpy(hostnamebuf, hostname,
1.13 enami 313: sizeof(hostnamebuf) - 1);
1.1 cgd 314: } else
1.9 christos 315: errorhost = hostname = strncpy(hostnamebuf,
1.13 enami 316: inet_ntoa(fromp->sin_addr), sizeof(hostnamebuf) - 1);
1.1 cgd 317:
1.9 christos 318: hostnamebuf[sizeof(hostnamebuf) - 1] = '\0';
1.1 cgd 319:
1.9 christos 320: getstr(remuser, sizeof(remuser), "remuser");
1.1 cgd 321: getstr(locuser, sizeof(locuser), "locuser");
322: getstr(cmdbuf, sizeof(cmdbuf), "command");
323: setpwent();
324: pwd = getpwnam(locuser);
325: if (pwd == NULL) {
1.7 cgd 326: syslog(LOG_INFO|LOG_AUTH,
327: "%s@%s as %s: unknown login. cmd='%.80s'",
328: remuser, hostname, locuser, cmdbuf);
1.1 cgd 329: if (errorstr == NULL)
330: errorstr = "Login incorrect.\n";
331: goto fail;
332: }
333: if (chdir(pwd->pw_dir) < 0) {
334: (void) chdir("/");
335: #ifdef notdef
1.7 cgd 336: syslog(LOG_INFO|LOG_AUTH,
337: "%s@%s as %s: no home directory. cmd='%.80s'",
338: remuser, hostname, locuser, cmdbuf);
1.1 cgd 339: error("No remote directory.\n");
340: exit(1);
341: #endif
342: }
343:
344:
1.13 enami 345: if (errorstr ||
346: (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
347: iruserok(fromp->sin_addr.s_addr, pwd->pw_uid == 0,
1.10 mrg 348: remuser, locuser) < 0)) {
1.13 enami 349: if (__rcmd_errstr)
350: syslog(LOG_INFO|LOG_AUTH,
1.7 cgd 351: "%s@%s as %s: permission denied (%s). cmd='%.80s'",
1.13 enami 352: remuser, hostname, locuser, __rcmd_errstr,
353: cmdbuf);
354: else
355: syslog(LOG_INFO|LOG_AUTH,
1.7 cgd 356: "%s@%s as %s: permission denied. cmd='%.80s'",
1.13 enami 357: remuser, hostname, locuser, cmdbuf);
1.1 cgd 358: fail:
1.13 enami 359: if (errorstr == NULL)
360: errorstr = "Permission denied.\n";
361: error(errorstr, errorhost);
362: exit(1);
363: }
1.1 cgd 364:
365: if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) {
366: error("Logins currently disabled.\n");
367: exit(1);
368: }
369:
1.7 cgd 370: (void) write(STDERR_FILENO, "\0", 1);
1.1 cgd 371: sent_null = 1;
372:
373: if (port) {
374: if (pipe(pv) < 0) {
375: error("Can't make pipe.\n");
376: exit(1);
377: }
378: pid = fork();
379: if (pid == -1) {
380: error("Can't fork; try again.\n");
381: exit(1);
382: }
383: if (pid) {
384: {
1.7 cgd 385: (void) close(0);
386: (void) close(1);
1.1 cgd 387: }
1.7 cgd 388: (void) close(2);
389: (void) close(pv[1]);
1.1 cgd 390:
391: FD_ZERO(&readfrom);
392: FD_SET(s, &readfrom);
393: FD_SET(pv[0], &readfrom);
394: if (pv[0] > s)
395: nfd = pv[0];
396: else
397: nfd = s;
1.13 enami 398: ioctl(pv[0], FIONBIO, (char *)&one);
1.1 cgd 399:
400: /* should set s nbio! */
401: nfd++;
402: do {
403: ready = readfrom;
1.13 enami 404: if (select(nfd, &ready, (fd_set *)0,
405: (fd_set *)0, (struct timeval *)0) < 0)
406: break;
1.1 cgd 407: if (FD_ISSET(s, &ready)) {
408: int ret;
1.13 enami 409:
410: ret = read(s, &sig, 1);
1.1 cgd 411: if (ret <= 0)
412: FD_CLR(s, &readfrom);
413: else
414: killpg(pid, sig);
415: }
416: if (FD_ISSET(pv[0], &ready)) {
417: errno = 0;
418: cc = read(pv[0], buf, sizeof(buf));
419: if (cc <= 0) {
420: shutdown(s, 1+1);
421: FD_CLR(pv[0], &readfrom);
422: } else {
1.13 enami 423: (void) write(s, buf, cc);
1.1 cgd 424: }
425: }
426:
427: } while (FD_ISSET(s, &readfrom) ||
428: FD_ISSET(pv[0], &readfrom));
429: exit(0);
430: }
431: setpgrp(0, getpid());
1.7 cgd 432: (void) close(s);
433: (void) close(pv[0]);
1.1 cgd 434: dup2(pv[1], 2);
435: close(pv[1]);
436: }
437: if (*pwd->pw_shell == '\0')
438: pwd->pw_shell = _PATH_BSHELL;
439: #if BSD > 43
440: if (setlogin(pwd->pw_name) < 0)
441: syslog(LOG_ERR, "setlogin() failed: %m");
442: #endif
443: (void) setgid((gid_t)pwd->pw_gid);
444: initgroups(pwd->pw_name, pwd->pw_gid);
445: (void) setuid((uid_t)pwd->pw_uid);
446: environ = envinit;
447: strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
448: strcat(path, _PATH_DEFPATH);
449: strncat(shell, pwd->pw_shell, sizeof(shell)-7);
450: strncat(username, pwd->pw_name, sizeof(username)-6);
1.7 cgd 451: cp = strrchr(pwd->pw_shell, '/');
1.1 cgd 452: if (cp)
453: cp++;
454: else
455: cp = pwd->pw_shell;
456: endpwent();
1.7 cgd 457: if (log_success || pwd->pw_uid == 0) {
1.13 enami 458: syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
459: remuser, hostname, locuser, cmdbuf);
1.1 cgd 460: }
461: execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
462: perror(pwd->pw_shell);
463: exit(1);
464: }
465:
466: /*
1.7 cgd 467: * Report error to client. Note: can't be used until second socket has
468: * connected to client, or older clients will hang waiting for that
469: * connection first.
1.1 cgd 470: */
1.7 cgd 471: #if __STDC__
472: #include <stdarg.h>
473: #else
474: #include <varargs.h>
475: #endif
476:
477: void
478: #if __STDC__
479: error(const char *fmt, ...)
480: #else
481: error(fmt, va_alist)
1.1 cgd 482: char *fmt;
1.7 cgd 483: va_dcl
484: #endif
1.1 cgd 485: {
1.7 cgd 486: va_list ap;
487: int len;
488: char *bp, buf[BUFSIZ];
489: #if __STDC__
490: va_start(ap, fmt);
491: #else
492: va_start(ap);
493: #endif
494: bp = buf;
495: if (sent_null == 0) {
1.1 cgd 496: *bp++ = 1;
1.7 cgd 497: len = 1;
498: } else
499: len = 0;
500: (void)vsnprintf(bp, sizeof(buf) - 1, fmt, ap);
501: (void)write(STDERR_FILENO, buf, len + strlen(bp));
1.1 cgd 502: }
503:
1.7 cgd 504: void
1.1 cgd 505: getstr(buf, cnt, err)
1.7 cgd 506: char *buf, *err;
1.1 cgd 507: int cnt;
508: {
509: char c;
510:
511: do {
1.7 cgd 512: if (read(STDIN_FILENO, &c, 1) != 1)
1.1 cgd 513: exit(1);
514: *buf++ = c;
515: if (--cnt == 0) {
516: error("%s too long\n", err);
517: exit(1);
518: }
519: } while (c != 0);
520: }
521:
522: /*
523: * Check whether host h is in our local domain,
524: * defined as sharing the last two components of the domain part,
525: * or the entire domain part if the local domain has only one component.
526: * If either name is unqualified (contains no '.'),
527: * assume that the host is local, as it will be
528: * interpreted as such.
529: */
1.7 cgd 530: int
1.1 cgd 531: local_domain(h)
532: char *h;
533: {
1.14 mrg 534: char localhost[MAXHOSTNAMELEN + 1];
1.7 cgd 535: char *p1, *p2;
1.1 cgd 536:
537: localhost[0] = 0;
1.14 mrg 538: (void)gethostname(localhost, sizeof(localhost));
539: localhost[sizeof(localhost) - 1] = '\0';
1.1 cgd 540: p1 = topdomain(localhost);
541: p2 = topdomain(h);
542: if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
1.7 cgd 543: return (1);
544: return (0);
1.1 cgd 545: }
546:
547: char *
548: topdomain(h)
549: char *h;
550: {
1.7 cgd 551: char *p, *maybe = NULL;
1.1 cgd 552: int dots = 0;
553:
554: for (p = h + strlen(h); p >= h; p--) {
555: if (*p == '.') {
556: if (++dots == 2)
557: return (p);
558: maybe = p;
559: }
560: }
561: return (maybe);
562: }
563:
1.7 cgd 564: void
1.1 cgd 565: usage()
566: {
1.7 cgd 567:
1.1 cgd 568: syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS);
1.7 cgd 569: exit(2);
1.1 cgd 570: }
CVSweb <webmaster@jp.NetBSD.org>