[BACK]Return to rshd.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / libexec / rshd

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>