Annotation of src/libexec/rshd/rshd.c, Revision 1.39
1.39 ! christos 1: /* $NetBSD: rshd.c,v 1.38 2005/03/11 16:04:09 ginsbach Exp $ */
1.18 itojun 2:
3: /*
4: * Copyright (C) 1998 WIDE Project.
5: * All rights reserved.
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 WIDE Project and
18: * its contributors.
19: * 4. Neither the name of the project 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 PROJECT 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 PROJECT 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: */
1.10 mrg 35:
1.1 cgd 36: /*-
1.7 cgd 37: * Copyright (c) 1988, 1989, 1992, 1993, 1994
38: * The Regents of the University of California. All rights reserved.
1.1 cgd 39: *
40: * Redistribution and use in source and binary forms, with or without
41: * modification, are permitted provided that the following conditions
42: * are met:
43: * 1. Redistributions of source code must retain the above copyright
44: * notice, this list of conditions and the following disclaimer.
45: * 2. Redistributions in binary form must reproduce the above copyright
46: * notice, this list of conditions and the following disclaimer in the
47: * documentation and/or other materials provided with the distribution.
1.32 agc 48: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 49: * may be used to endorse or promote products derived from this software
50: * without specific prior written permission.
51: *
52: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62: * SUCH DAMAGE.
63: */
64:
1.10 mrg 65: #include <sys/cdefs.h>
1.1 cgd 66: #ifndef lint
1.10 mrg 67: __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\
68: The Regents of the University of California. All rights reserved.\n");
69: #if 0
70: static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94";
71: #else
1.39 ! christos 72: __RCSID("$NetBSD: rshd.c,v 1.38 2005/03/11 16:04:09 ginsbach Exp $");
1.10 mrg 73: #endif
1.1 cgd 74: #endif /* not lint */
75:
76: /*
77: * remote shell server:
78: * [port]\0
79: * remuser\0
80: * locuser\0
81: * command\0
82: * data
83: */
84: #include <sys/param.h>
85: #include <sys/ioctl.h>
86: #include <sys/time.h>
1.7 cgd 87: #include <sys/socket.h>
1.1 cgd 88:
1.36 christos 89: #include <netinet/in_systm.h>
1.1 cgd 90: #include <netinet/in.h>
1.36 christos 91: #include <netinet/ip.h>
1.31 joff 92: #include <netinet/tcp.h>
1.1 cgd 93: #include <arpa/inet.h>
94: #include <netdb.h>
95:
1.7 cgd 96: #include <errno.h>
97: #include <fcntl.h>
98: #include <paths.h>
1.1 cgd 99: #include <pwd.h>
1.7 cgd 100: #include <signal.h>
1.1 cgd 101: #include <stdio.h>
102: #include <stdlib.h>
103: #include <string.h>
1.7 cgd 104: #include <syslog.h>
105: #include <unistd.h>
1.27 itojun 106: #include <poll.h>
1.17 mjl 107: #ifdef LOGIN_CAP
108: #include <login_cap.h>
109: #endif
1.1 cgd 110:
1.34 christos 111: #ifdef USE_PAM
112: #include <security/pam_appl.h>
113: #include <security/openpam.h>
114: #include <sys/wait.h>
115:
116: static struct pam_conv pamc = { openpam_nullconv, NULL };
117: static pam_handle_t *pamh;
118: static int pam_err;
119:
1.39 ! christos 120: #define PAM_END do { \
1.34 christos 121: if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
122: syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", \
123: pam_strerror(pamh, pam_err)); \
124: if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \
125: syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", \
126: pam_strerror(pamh, pam_err)); \
127: if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \
128: syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", \
129: pam_strerror(pamh, pam_err)); \
1.39 ! christos 130: } while (/*CONSTCOND*/0)
1.34 christos 131: #else
132: #define PAM_END
133: #endif
134:
1.1 cgd 135: int keepalive = 1;
1.7 cgd 136: int check_all;
137: int log_success; /* If TRUE, log all successful accesses */
1.1 cgd 138: int sent_null;
139:
1.34 christos 140: void doit(struct sockaddr *);
141: void rshd_errx(int, const char *, ...)
142: __attribute__((__noreturn__, __format__(__printf__, 2, 3)));
143: void getstr(char *, int, char *);
144: int local_domain(char *);
145: char *topdomain(char *);
146: void usage(void);
147: int main(int, char *[]);
1.7 cgd 148:
1.37 wiz 149: #define OPTIONS "aLln"
1.23 christos 150: extern int __check_rhosts_file;
151: extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */
1.34 christos 152: static const char incorrect[] = "Login incorrect.";
1.1 cgd 153:
1.7 cgd 154: int
1.25 mjl 155: main(int argc, char *argv[])
1.1 cgd 156: {
157: struct linger linger;
158: int ch, on = 1, fromlen;
1.18 itojun 159: struct sockaddr_storage from;
1.31 joff 160: struct protoent *proto;
1.1 cgd 161:
1.22 lukem 162: openlog("rshd", LOG_PID, LOG_DAEMON);
1.1 cgd 163:
164: opterr = 0;
1.11 enami 165: while ((ch = getopt(argc, argv, OPTIONS)) != -1)
1.1 cgd 166: switch (ch) {
167: case 'a':
168: check_all = 1;
169: break;
170: case 'l':
1.6 pk 171: __check_rhosts_file = 0;
1.1 cgd 172: break;
173: case 'n':
174: keepalive = 0;
175: break;
1.3 cgd 176: case 'L':
1.7 cgd 177: log_success = 1;
1.3 cgd 178: break;
1.1 cgd 179: case '?':
180: default:
181: usage();
1.7 cgd 182: break;
1.1 cgd 183: }
184:
185: argc -= optind;
186: argv += optind;
187:
1.18 itojun 188: fromlen = sizeof (from); /* xxx */
1.1 cgd 189: if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
190: syslog(LOG_ERR, "getpeername: %m");
1.25 mjl 191: exit(1);
1.1 cgd 192: }
1.19 itojun 193: #if 0
194: if (((struct sockaddr *)&from)->sa_family == AF_INET6 &&
195: IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr) &&
196: sizeof(struct sockaddr_in) <= sizeof(from)) {
197: struct sockaddr_in sin;
198: struct sockaddr_in6 *sin6;
199: const int off = sizeof(struct sockaddr_in6) -
200: sizeof(struct sockaddr_in);
201:
202: sin6 = (struct sockaddr_in6 *)&from;
203: memset(&sin, 0, sizeof(sin));
204: sin.sin_family = AF_INET;
205: sin.sin_len = sizeof(struct sockaddr_in);
206: memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[off],
207: sizeof(sin.sin_addr));
208: memcpy(&from, &sin, sizeof(sin));
209: fromlen = sin.sin_len;
210: }
211: #else
212: if (((struct sockaddr *)&from)->sa_family == AF_INET6 &&
213: IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) {
214: char hbuf[NI_MAXHOST];
215: if (getnameinfo((struct sockaddr *)&from, fromlen, hbuf,
216: sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) {
1.30 itojun 217: strlcpy(hbuf, "invalid", sizeof(hbuf));
1.19 itojun 218: }
1.25 mjl 219: syslog(LOG_ERR, "malformed \"from\" address (v4 mapped, %s)",
1.19 itojun 220: hbuf);
221: exit(1);
222: }
223: #endif
1.1 cgd 224: if (keepalive &&
225: setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
226: sizeof(on)) < 0)
227: syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
228: linger.l_onoff = 1;
229: linger.l_linger = 60; /* XXX */
230: if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
231: sizeof (linger)) < 0)
232: syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
1.31 joff 233: proto = getprotobyname("tcp");
234: setsockopt(0, proto->p_proto, TCP_NODELAY, &on, sizeof(on));
1.18 itojun 235: doit((struct sockaddr *)&from);
1.7 cgd 236: /* NOTREACHED */
1.10 mrg 237: #ifdef __GNUC__
238: exit(0);
239: #endif
1.1 cgd 240: }
241:
242: char username[20] = "USER=";
243: char homedir[64] = "HOME=";
244: char shell[64] = "SHELL=";
245: char path[100] = "PATH=";
246: char *envinit[] =
247: {homedir, shell, path, username, 0};
248: char **environ;
249:
1.7 cgd 250: void
1.25 mjl 251: doit(struct sockaddr *fromp)
1.1 cgd 252: {
253: struct passwd *pwd;
1.14 mrg 254: in_port_t port;
1.26 mycroft 255: struct pollfd set[2];
256: int cc, pv[2], pid, s = -1; /* XXX gcc */
1.1 cgd 257: int one = 1;
1.34 christos 258: char *hostname, *errorhost = NULL; /* XXX gcc */
1.15 mycroft 259: const char *cp;
260: char sig, buf[BUFSIZ];
1.7 cgd 261: char cmdbuf[NCARGS+1], locuser[16], remuser[16];
1.1 cgd 262: char remotehost[2 * MAXHOSTNAMELEN + 1];
1.9 christos 263: char hostnamebuf[2 * MAXHOSTNAMELEN + 1];
1.17 mjl 264: #ifdef LOGIN_CAP
265: login_cap_t *lc;
266: #endif
1.18 itojun 267: char naddr[NI_MAXHOST];
268: char saddr[NI_MAXHOST];
269: char raddr[NI_MAXHOST];
270: char pbuf[NI_MAXSERV];
271: int af = fromp->sa_family;
272: u_int16_t *portp;
273: struct addrinfo hints, *res, *res0;
274: int gaierror;
275: const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1.34 christos 276: const char *errormsg = NULL, *errorstr = NULL;
1.1 cgd 277:
278: (void) signal(SIGINT, SIG_DFL);
279: (void) signal(SIGQUIT, SIG_DFL);
280: (void) signal(SIGTERM, SIG_DFL);
281: #ifdef DEBUG
1.36 christos 282: {
283: int t = open(_PATH_TTY, 2);
284: if (t >= 0) {
285: ioctl(t, TIOCNOTTY, (char *)0);
286: (void)close(t);
287: }
1.1 cgd 288: }
289: #endif
1.18 itojun 290: switch (af) {
291: case AF_INET:
292: portp = &((struct sockaddr_in *)fromp)->sin_port;
293: break;
294: #ifdef INET6
295: case AF_INET6:
296: portp = &((struct sockaddr_in6 *)fromp)->sin6_port;
297: break;
298: #endif
299: default:
1.25 mjl 300: syslog(LOG_ERR, "malformed \"from\" address (af %d)", af);
1.18 itojun 301: exit(1);
302: }
303: if (getnameinfo(fromp, fromp->sa_len, naddr, sizeof(naddr),
304: pbuf, sizeof(pbuf), niflags) != 0) {
1.25 mjl 305: syslog(LOG_ERR, "malformed \"from\" address (af %d)", af);
1.1 cgd 306: exit(1);
307: }
308: #ifdef IP_OPTIONS
1.36 christos 309: if (af == AF_INET) {
310:
311: u_char optbuf[BUFSIZ/3];
312: int optsize = sizeof(optbuf), ipproto, i;
1.1 cgd 313: struct protoent *ip;
314:
315: if ((ip = getprotobyname("ip")) != NULL)
316: ipproto = ip->p_proto;
317: else
318: ipproto = IPPROTO_IP;
319: if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
320: optsize != 0) {
1.36 christos 321: for (i = 0; i < optsize;) {
322: u_char c = optbuf[i];
323: if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
324: syslog(LOG_NOTICE,
325: "Connection refused from %s "
326: "with IP option %s",
327: inet_ntoa((
328: (struct sockaddr_in *)fromp)->sin_addr),
329: c == IPOPT_LSRR ? "LSRR" : "SSRR");
330: exit(1);
331: }
332: if (c == IPOPT_EOL)
333: break;
334: i += (c == IPOPT_NOP) ? 1 : optbuf[i + 1];
1.1 cgd 335: }
336: }
1.36 christos 337: }
1.1 cgd 338: #endif
1.18 itojun 339: if (ntohs(*portp) >= IPPORT_RESERVED
1.36 christos 340: || ntohs(*portp) < IPPORT_RESERVED/2) {
1.13 enami 341: syslog(LOG_NOTICE|LOG_AUTH,
342: "Connection from %s on illegal port %u",
1.18 itojun 343: naddr, ntohs(*portp));
1.13 enami 344: exit(1);
345: }
1.1 cgd 346:
347: (void) alarm(60);
348: port = 0;
349: for (;;) {
350: char c;
1.13 enami 351:
1.7 cgd 352: if ((cc = read(STDIN_FILENO, &c, 1)) != 1) {
1.1 cgd 353: if (cc < 0)
1.22 lukem 354: syslog(LOG_ERR, "read: %m");
355: shutdown(0, SHUT_RDWR);
1.1 cgd 356: exit(1);
357: }
1.13 enami 358: if (c == 0)
1.1 cgd 359: break;
360: port = port * 10 + c - '0';
361: }
362:
363: (void) alarm(0);
364: if (port != 0) {
365: int lport = IPPORT_RESERVED - 1;
1.18 itojun 366: s = rresvport_af(&lport, af);
1.1 cgd 367: if (s < 0) {
368: syslog(LOG_ERR, "can't get stderr port: %m");
369: exit(1);
370: }
1.12 lukem 371: if (port >= IPPORT_RESERVED) {
1.25 mjl 372: syslog(LOG_ERR, "2nd port not reserved");
1.12 lukem 373: exit(1);
374: }
1.18 itojun 375: *portp = htons(port);
1.38 ginsbach 376: if (connect(s, fromp, fromp->sa_len) < 0) {
1.22 lukem 377: syslog(LOG_ERR, "connect second port %d: %m", port);
1.1 cgd 378: exit(1);
379: }
380: }
381:
382:
383: #ifdef notdef
384: /* from inetd, socket is already on 0, 1, 2 */
385: dup2(f, 0);
386: dup2(f, 1);
387: dup2(f, 2);
388: #endif
1.18 itojun 389: if (getnameinfo(fromp, fromp->sa_len, saddr, sizeof(saddr),
390: NULL, 0, NI_NAMEREQD) == 0) {
1.1 cgd 391: /*
1.18 itojun 392: * If name returned by getnameinfo is in our domain,
1.1 cgd 393: * attempt to verify that we haven't been fooled by someone
394: * in a remote net; look up the name and check that this
395: * address corresponds to the name.
396: */
1.18 itojun 397: hostname = saddr;
1.21 itojun 398: res0 = NULL;
1.18 itojun 399: if (check_all || local_domain(saddr)) {
1.25 mjl 400: strlcpy(remotehost, saddr, sizeof(remotehost));
1.1 cgd 401: errorhost = remotehost;
1.18 itojun 402: memset(&hints, 0, sizeof(hints));
403: hints.ai_family = fromp->sa_family;
404: hints.ai_socktype = SOCK_STREAM;
405: hints.ai_flags = AI_CANONNAME;
406: gaierror = getaddrinfo(remotehost, pbuf, &hints, &res0);
407: if (gaierror) {
1.22 lukem 408: syslog(LOG_NOTICE,
1.18 itojun 409: "Couldn't look up address for %s: %s",
410: remotehost, gai_strerror(gaierror));
1.1 cgd 411: errorstr =
412: "Couldn't look up address for your host (%s)\n";
1.18 itojun 413: hostname = naddr;
414: } else {
415: for (res = res0; res; res = res->ai_next) {
416: if (res->ai_family != fromp->sa_family)
417: continue;
418: if (res->ai_addrlen != fromp->sa_len)
419: continue;
420: if (getnameinfo(res->ai_addr,
421: res->ai_addrlen,
422: raddr, sizeof(raddr), NULL, 0,
423: niflags) == 0
424: && strcmp(naddr, raddr) == 0) {
425: hostname = res->ai_canonname
426: ? res->ai_canonname
427: : saddr;
428: break;
429: }
430: }
431: if (res == NULL) {
1.1 cgd 432: syslog(LOG_NOTICE,
433: "Host addr %s not listed for host %s",
1.18 itojun 434: naddr, res0->ai_canonname
435: ? res0->ai_canonname
436: : saddr);
1.1 cgd 437: errorstr =
438: "Host address mismatch for %s\n";
1.18 itojun 439: hostname = naddr;
1.1 cgd 440: }
441: }
442: }
1.25 mjl 443: strlcpy(hostnamebuf, hostname, sizeof(hostnamebuf));
444: hostname = hostnamebuf;
1.21 itojun 445: if (res0)
446: freeaddrinfo(res0);
1.18 itojun 447: } else {
1.25 mjl 448: strlcpy(hostnamebuf, naddr, sizeof(hostnamebuf));
449: errorhost = hostname = hostnamebuf;
1.18 itojun 450: }
1.1 cgd 451:
1.34 christos 452: (void)alarm(60);
1.9 christos 453: getstr(remuser, sizeof(remuser), "remuser");
1.1 cgd 454: getstr(locuser, sizeof(locuser), "locuser");
455: getstr(cmdbuf, sizeof(cmdbuf), "command");
1.34 christos 456: (void)alarm(0);
457:
458: #ifdef USE_PAM
459: pam_err = pam_start("rsh", locuser, &pamc, &pamh);
460: if (pam_err != PAM_SUCCESS) {
461: syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s",
462: pam_strerror(pamh, pam_err));
463: rshd_errx(1, incorrect);
464: }
465:
466: if ((pam_err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS ||
467: (pam_err = pam_set_item(pamh, PAM_RHOST, hostname) != PAM_SUCCESS)){
468: syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s",
469: pam_strerror(pamh, pam_err));
470: rshd_errx(1, incorrect);
471: }
472:
473: pam_err = pam_authenticate(pamh, 0);
474: if (pam_err == PAM_SUCCESS) {
475: if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) {
476: strlcpy(locuser, cp, sizeof(locuser));
477: /* XXX truncation! */
478: }
479: pam_err = pam_acct_mgmt(pamh, 0);
480: }
481: if (pam_err != PAM_SUCCESS) {
482: errorstr = incorrect;
483: errormsg = pam_strerror(pamh, pam_err);
484: goto badlogin;
485: }
486: #endif /* USE_PAM */
1.1 cgd 487: setpwent();
488: pwd = getpwnam(locuser);
489: if (pwd == NULL) {
1.7 cgd 490: syslog(LOG_INFO|LOG_AUTH,
491: "%s@%s as %s: unknown login. cmd='%.80s'",
492: remuser, hostname, locuser, cmdbuf);
1.1 cgd 493: if (errorstr == NULL)
1.34 christos 494: errorstr = "Permission denied.";
495: rshd_errx(1, errorstr, errorhost);
1.1 cgd 496: }
1.17 mjl 497: #ifdef LOGIN_CAP
498: lc = login_getclass(pwd ? pwd->pw_class : NULL);
499: #endif
500:
1.1 cgd 501: if (chdir(pwd->pw_dir) < 0) {
1.34 christos 502: if (chdir("/") < 0
1.17 mjl 503: #ifdef LOGIN_CAP
1.34 christos 504: || login_getcapbool(lc, "requirehome", pwd->pw_uid ? 1 : 0)
505: #endif
506: ) {
1.17 mjl 507: syslog(LOG_INFO|LOG_AUTH,
508: "%s@%s as %s: no home directory. cmd='%.80s'",
509: remuser, hostname, locuser, cmdbuf);
1.34 christos 510: rshd_errx(0, "No remote home directory.");
1.17 mjl 511: }
1.1 cgd 512: }
513:
1.34 christos 514: #ifndef USE_PAM
1.13 enami 515: if (errorstr ||
516: (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
1.18 itojun 517: iruserok_sa(fromp, fromp->sa_len, pwd->pw_uid == 0, remuser,
518: locuser) < 0)) {
1.34 christos 519: errormsg = __rcmd_errstr ? __rcmd_errstr : "unknown error";
1.13 enami 520: if (errorstr == NULL)
1.34 christos 521: errorstr = "Permission denied.";
1.35 christos 522: goto badlogin;
1.13 enami 523: }
1.1 cgd 524:
1.34 christos 525: if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK))
526: rshd_errx(1, "Logins currently disabled.");
1.39 ! christos 527: #endif
! 528:
! 529: #ifdef LOGIN_CAP
! 530: /*
! 531: * PAM modules might add supplementary groups in
! 532: * pam_setcred(), so initialize them first.
! 533: * But we need to open the session as root.
! 534: */
! 535: if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
! 536: syslog(LOG_ERR, "setusercontext: %m");
! 537: exit(1);
! 538: }
! 539: #else
! 540: initgroups(pwd->pw_name, pwd->pw_gid);
! 541: #endif
! 542:
! 543: #ifdef USE_PAM
! 544: if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
! 545: syslog(LOG_ERR, "pam_open_session: %s",
! 546: pam_strerror(pamh, pam_err));
! 547: } else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED))
! 548: != PAM_SUCCESS) {
! 549: syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err));
! 550: }
! 551: #endif
1.1 cgd 552:
1.7 cgd 553: (void) write(STDERR_FILENO, "\0", 1);
1.1 cgd 554: sent_null = 1;
555:
556: if (port) {
1.34 christos 557: if (pipe(pv) < 0)
558: rshd_errx(1, "Can't make pipe. (%s)", strerror(errno));
1.1 cgd 559: pid = fork();
1.34 christos 560: if (pid == -1)
561: rshd_errx(1, "Can't fork. (%s)", strerror(errno));
1.1 cgd 562: if (pid) {
563: {
1.7 cgd 564: (void) close(0);
565: (void) close(1);
1.1 cgd 566: }
1.7 cgd 567: (void) close(2);
568: (void) close(pv[1]);
1.1 cgd 569:
1.26 mycroft 570: set[0].fd = s;
571: set[0].events = POLLIN;
572: set[1].fd = pv[0];
573: set[1].events = POLLIN;
1.13 enami 574: ioctl(pv[0], FIONBIO, (char *)&one);
1.1 cgd 575:
576: /* should set s nbio! */
577: do {
1.26 mycroft 578: if (poll(set, 2, INFTIM) < 0)
1.13 enami 579: break;
1.26 mycroft 580: if (set[0].revents & POLLIN) {
1.1 cgd 581: int ret;
1.13 enami 582:
583: ret = read(s, &sig, 1);
1.1 cgd 584: if (ret <= 0)
1.26 mycroft 585: set[0].events = 0;
1.1 cgd 586: else
587: killpg(pid, sig);
588: }
1.26 mycroft 589: if (set[1].revents & POLLIN) {
1.1 cgd 590: errno = 0;
591: cc = read(pv[0], buf, sizeof(buf));
592: if (cc <= 0) {
1.22 lukem 593: shutdown(s, SHUT_RDWR);
1.26 mycroft 594: set[1].events = 0;
1.1 cgd 595: } else {
1.13 enami 596: (void) write(s, buf, cc);
1.1 cgd 597: }
598: }
599:
1.26 mycroft 600: } while ((set[0].revents | set[1].revents) & POLLIN);
1.39 ! christos 601: PAM_END;
1.1 cgd 602: exit(0);
603: }
1.7 cgd 604: (void) close(s);
605: (void) close(pv[0]);
1.1 cgd 606: dup2(pv[1], 2);
607: close(pv[1]);
608: }
1.34 christos 609: #ifdef USE_PAM
610: else {
611: pid = fork();
612: if (pid == -1)
613: rshd_errx(1, "Can't fork. (%s)", strerror(errno));
614: if (pid) {
1.39 ! christos 615: pid_t xpid;
! 616: int status;
! 617: if ((xpid = waitpid(pid, &status, 0)) != pid) {
! 618: pam_err = pam_close_session(pamh, 0);
! 619: if (pam_err != PAM_SUCCESS) {
! 620: syslog(LOG_ERR,
! 621: "pam_close_session: %s",
! 622: pam_strerror(pamh, pam_err));
! 623: }
! 624: PAM_END;
! 625: if (xpid != -1)
! 626: syslog(LOG_WARNING,
! 627: "wrong PID: %d != %d", pid, xpid);
! 628: else
! 629: syslog(LOG_WARNING,
! 630: "wait pid=%d failed %m", pid);
! 631: exit(1);
! 632: }
! 633: }
1.34 christos 634: }
635: #endif
1.17 mjl 636:
1.34 christos 637: #ifdef F_CLOSEM
638: (void)fcntl(3, F_CLOSEM, 0);
639: #else
640: for (fd = getdtablesize(); fd > 2; fd--)
641: (void)close(fd);
642: #endif
643: if (setsid() == -1)
644: syslog(LOG_ERR, "setsid() failed: %m");
645: #ifdef USE_PAM
646: if (setlogin(pwd->pw_name) < 0)
647: syslog(LOG_ERR, "setlogin() failed: %m");
648:
1.39 ! christos 649: if (*pwd->pw_shell == '\0')
! 650: pwd->pw_shell = _PATH_BSHELL;
! 651:
1.34 christos 652: (void)pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
653: (void)pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
654: (void)pam_setenv(pamh, "USER", pwd->pw_name, 1);
655: (void)pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
656: environ = pam_getenvlist(pamh);
657: (void)pam_end(pamh, pam_err);
1.39 ! christos 658: #else
1.17 mjl 659: #ifdef LOGIN_CAP
1.39 ! christos 660: {
! 661: char *sh;
! 662: if ((sh = login_getcapstr(lc, "shell", NULL, NULL))) {
! 663: if(!(sh = strdup(sh))) {
! 664: syslog(LOG_ERR, "Cannot alloc mem");
! 665: exit(1);
! 666: }
! 667: pwd->pw_shell = sh;
1.17 mjl 668: }
669: }
670: #endif
1.1 cgd 671: environ = envinit;
1.30 itojun 672: strlcat(homedir, pwd->pw_dir, sizeof(homedir));
673: strlcat(path, _PATH_DEFPATH, sizeof(path));
674: strlcat(shell, pwd->pw_shell, sizeof(shell));
675: strlcat(username, pwd->pw_name, sizeof(username));
1.34 christos 676: #endif
677:
1.7 cgd 678: cp = strrchr(pwd->pw_shell, '/');
1.1 cgd 679: if (cp)
680: cp++;
681: else
682: cp = pwd->pw_shell;
1.34 christos 683:
684: #ifdef LOGIN_CAP
685: if (setusercontext(lc, pwd, pwd->pw_uid,
686: LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) {
687: syslog(LOG_ERR, "setusercontext(): %m");
688: exit(1);
689: }
690: login_close(lc);
691: #else
692: (void)setgid((gid_t)pwd->pw_gid);
693: (void)setuid((uid_t)pwd->pw_uid);
694: #endif
695: endpwent();
696: if (log_success || pwd->pw_uid == 0) {
697: syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
698: remuser, hostname, locuser, cmdbuf);
699: }
1.25 mjl 700: execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL);
1.34 christos 701: rshd_errx(1, "%s: %s", pwd->pw_shell, strerror(errno));
702: badlogin:
703: syslog(LOG_INFO|LOG_AUTH,
704: "%s@%s as %s: permission denied (%s). cmd='%.80s'",
705: remuser, hostname, locuser, errormsg, cmdbuf);
706: rshd_errx(1, errorstr, errorhost);
1.1 cgd 707: }
708:
709: /*
1.7 cgd 710: * Report error to client. Note: can't be used until second socket has
711: * connected to client, or older clients will hang waiting for that
712: * connection first.
1.1 cgd 713: */
1.25 mjl 714:
1.7 cgd 715: #include <stdarg.h>
716:
1.34 christos 717: void
718: rshd_errx(int error, const char *fmt, ...)
1.1 cgd 719: {
1.7 cgd 720: va_list ap;
1.34 christos 721: int len, rv;
1.7 cgd 722: char *bp, buf[BUFSIZ];
723: va_start(ap, fmt);
724: bp = buf;
725: if (sent_null == 0) {
1.1 cgd 726: *bp++ = 1;
1.7 cgd 727: len = 1;
728: } else
729: len = 0;
1.34 christos 730: rv = vsnprintf(bp, sizeof(buf) - 2, fmt, ap);
731: bp[rv++] = '\n';
732: (void)write(STDERR_FILENO, buf, len + rv);
1.24 wiz 733: va_end(ap);
1.34 christos 734: exit(error);
1.1 cgd 735: }
736:
1.7 cgd 737: void
1.25 mjl 738: getstr(char *buf, int cnt, char *err)
1.1 cgd 739: {
740: char c;
741:
742: do {
1.7 cgd 743: if (read(STDIN_FILENO, &c, 1) != 1)
1.1 cgd 744: exit(1);
745: *buf++ = c;
1.34 christos 746: if (--cnt == 0)
747: rshd_errx(1, "%s too long", err);
1.1 cgd 748: } while (c != 0);
749: }
750:
751: /*
752: * Check whether host h is in our local domain,
753: * defined as sharing the last two components of the domain part,
754: * or the entire domain part if the local domain has only one component.
755: * If either name is unqualified (contains no '.'),
756: * assume that the host is local, as it will be
757: * interpreted as such.
758: */
1.7 cgd 759: int
1.25 mjl 760: local_domain(char *h)
1.1 cgd 761: {
1.14 mrg 762: char localhost[MAXHOSTNAMELEN + 1];
1.7 cgd 763: char *p1, *p2;
1.1 cgd 764:
765: localhost[0] = 0;
1.14 mrg 766: (void)gethostname(localhost, sizeof(localhost));
767: localhost[sizeof(localhost) - 1] = '\0';
1.1 cgd 768: p1 = topdomain(localhost);
769: p2 = topdomain(h);
770: if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
1.7 cgd 771: return (1);
772: return (0);
1.1 cgd 773: }
774:
775: char *
1.25 mjl 776: topdomain(char *h)
1.1 cgd 777: {
1.7 cgd 778: char *p, *maybe = NULL;
1.1 cgd 779: int dots = 0;
780:
781: for (p = h + strlen(h); p >= h; p--) {
782: if (*p == '.') {
783: if (++dots == 2)
784: return (p);
785: maybe = p;
786: }
787: }
788: return (maybe);
789: }
790:
1.7 cgd 791: void
1.25 mjl 792: usage(void)
1.1 cgd 793: {
1.7 cgd 794:
1.1 cgd 795: syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS);
1.7 cgd 796: exit(2);
1.1 cgd 797: }
CVSweb <webmaster@jp.NetBSD.org>