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