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

Annotation of src/libexec/rexecd/rexecd.c, Revision 1.21

1.21    ! christos    1: /*     $NetBSD: rexecd.c,v 1.20 2005/01/10 19:01:09 ginsbach Exp $     */
1.4       mrg         2:
1.1       cgd         3: /*
1.4       mrg         4:  * Copyright (c) 1983, 1993
                      5:  *     The Regents of the University of California.  All rights reserved.
1.1       cgd         6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
1.17      agc        15:  * 3. Neither the name of the University nor the names of its contributors
1.1       cgd        16:  *    may be used to endorse or promote products derived from this software
                     17:  *    without specific prior written permission.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     20:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     21:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     22:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     23:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     24:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     25:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     26:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     27:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     28:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     29:  * SUCH DAMAGE.
                     30:  */
                     31:
1.4       mrg        32: #include <sys/cdefs.h>
1.1       cgd        33: #ifndef lint
1.4       mrg        34: __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
                     35:        The Regents of the University of California.  All rights reserved.\n");
                     36: #if 0
                     37: static char sccsid[] = "from: @(#)rexecd.c     8.1 (Berkeley) 6/4/93";
                     38: #else
1.21    ! christos   39: __RCSID("$NetBSD: rexecd.c,v 1.20 2005/01/10 19:01:09 ginsbach Exp $");
1.4       mrg        40: #endif
1.1       cgd        41: #endif /* not lint */
                     42:
                     43: #include <sys/param.h>
                     44: #include <sys/ioctl.h>
                     45: #include <sys/socket.h>
1.5       mrg        46: #include <sys/syslog.h>
1.1       cgd        47: #include <sys/time.h>
1.4       mrg        48:
1.1       cgd        49: #include <netinet/in.h>
1.4       mrg        50:
1.5       mrg        51: #include <err.h>
1.4       mrg        52: #include <errno.h>
1.1       cgd        53: #include <netdb.h>
1.4       mrg        54: #include <paths.h>
1.1       cgd        55: #include <pwd.h>
1.4       mrg        56: #include <signal.h>
1.10      wiz        57: #include <stdarg.h>
1.1       cgd        58: #include <stdio.h>
                     59: #include <stdlib.h>
                     60: #include <string.h>
1.4       mrg        61: #include <unistd.h>
1.12      itojun     62: #include <poll.h>
1.1       cgd        63:
1.21    ! christos   64: #ifdef USE_PAM
        !            65: #include <security/pam_appl.h>
        !            66: #include <security/openpam.h>
        !            67: #endif
        !            68:
1.19      ginsbach   69: int main(int, char *[]);
1.21    ! christos   70: static void rexecd_errx(int, const char *, ...)
        !            71:      __attribute__((__noreturn__, __format__(__printf__, 2, 3)));
        !            72: static void doit(struct sockaddr *);
        !            73: static void getstr(char *, int, const char *);
        !            74: static void usage(void) __attribute__((__noreturn__));
        !            75:
        !            76: #ifdef USE_PAM
        !            77: static pam_handle_t *pamh;
        !            78: static struct pam_conv pamc = {
        !            79:        openpam_nullconv,
        !            80:        NULL
        !            81: };
        !            82: static int pam_flags = PAM_SILENT|PAM_DISALLOW_NULL_AUTHTOK;
        !            83: static int pam_err;
        !            84: #define pam_ok(err) ((pam_err = (err)) == PAM_SUCCESS)
        !            85: #endif
1.1       cgd        86:
1.5       mrg        87: char   **environ;
1.14      thorpej    88: int    dolog;
1.21    ! christos   89: #ifndef USE_PAM
        !            90: static char    username[32 + 1] = "USER=";
        !            91: static char    logname[32 + 3 + 1] = "LOGNAME=";
        !            92: static char    homedir[PATH_MAX + 1] = "HOME=";
        !            93: static char    shell[PATH_MAX + 1] = "SHELL=";
        !            94: static char    path[sizeof(_PATH_DEFPATH) + sizeof("PATH=")] = "PATH=";
        !            95: static char    *envinit[] = { homedir, shell, path, username, logname, 0 };
        !            96: #endif
1.5       mrg        97:
1.1       cgd        98: /*
                     99:  * remote execute server:
                    100:  *     username\0
                    101:  *     password\0
                    102:  *     command\0
                    103:  *     data
                    104:  */
1.4       mrg       105: int
1.19      ginsbach  106: main(int argc, char *argv[])
1.1       cgd       107: {
1.11      itojun    108:        struct sockaddr_storage from;
1.21    ! christos  109:        socklen_t fromlen;
        !           110:        int ch;
1.5       mrg       111:
                    112:        while ((ch = getopt(argc, argv, "l")) != -1)
                    113:                switch (ch) {
                    114:                case 'l':
1.14      thorpej   115:                        dolog = 1;
1.5       mrg       116:                        openlog("rexecd", LOG_PID, LOG_DAEMON);
                    117:                        break;
                    118:                default:
1.21    ! christos  119:                        usage();
1.5       mrg       120:                }
1.1       cgd       121:
                    122:        fromlen = sizeof (from);
1.21    ! christos  123:        if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0)
1.5       mrg       124:                err(1, "getpeername");
                    125:
1.21    ! christos  126:        if (((struct sockaddr *)&from)->sa_family == AF_INET6 &&
        !           127:            IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) {
        !           128:                char hbuf[NI_MAXHOST];
        !           129:                if (getnameinfo((struct sockaddr *)&from, fromlen, hbuf,
        !           130:                    sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) {
        !           131:                        (void)strlcpy(hbuf, "invalid", sizeof(hbuf));
        !           132:                }
        !           133:                if (dolog)
        !           134:                        syslog(LOG_ERR,
        !           135:                            "malformed \"from\" address (v4 mapped, %s)",
        !           136:                            hbuf);
        !           137:                return 1;
        !           138:        }
        !           139:
        !           140:        doit((struct sockaddr *)&from);
        !           141:        /* NOTREACHED */
        !           142:        return 1;
1.1       cgd       143: }
                    144:
1.4       mrg       145: void
1.21    ! christos  146: doit(struct sockaddr *fromp)
1.1       cgd       147: {
1.5       mrg       148:        struct pollfd fds[2];
1.21    ! christos  149:        char cmdbuf[NCARGS + 1];
1.6       mycroft   150:        const char *cp;
1.1       cgd       151:        char user[16], pass[16];
1.5       mrg       152:        char buf[BUFSIZ], sig;
1.1       cgd       153:        struct passwd *pwd;
1.4       mrg       154:        int s = -1; /* XXX gcc */
1.5       mrg       155:        int pv[2], pid, cc;
1.1       cgd       156:        int one = 1;
1.5       mrg       157:        in_port_t port;
1.21    ! christos  158:        char hostname[2 * MAXHOSTNAMELEN + 1];
        !           159:        char pbuf[NI_MAXSERV];
        !           160:        const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
        !           161: #ifndef USE_PAM
        !           162:        char *namep;
        !           163: #endif
1.1       cgd       164:
1.5       mrg       165:        (void)signal(SIGINT, SIG_DFL);
                    166:        (void)signal(SIGQUIT, SIG_DFL);
                    167:        (void)signal(SIGTERM, SIG_DFL);
1.21    ! christos  168:        (void)dup2(STDIN_FILENO, STDOUT_FILENO);
        !           169:        (void)dup2(STDIN_FILENO, STDERR_FILENO);
        !           170:
        !           171:        if (getnameinfo(fromp, fromp->sa_len, hostname, sizeof(hostname),
        !           172:            pbuf, sizeof(pbuf), niflags) != 0) {
        !           173:                if (dolog)
        !           174:                        syslog(LOG_ERR, "malformed \"from\" address (af %d)",
        !           175:                               fromp->sa_family);
        !           176:                exit(1);
        !           177:        }
        !           178:
1.5       mrg       179:        (void)alarm(60);
1.1       cgd       180:        port = 0;
                    181:        for (;;) {
                    182:                char c;
1.21    ! christos  183:                if (read(STDIN_FILENO, &c, 1) != 1) {
1.14      thorpej   184:                        if (dolog)
1.21    ! christos  185:                                syslog(LOG_ERR, "initial read failed");
1.1       cgd       186:                        exit(1);
1.5       mrg       187:                }
1.1       cgd       188:                if (c == 0)
                    189:                        break;
                    190:                port = port * 10 + c - '0';
                    191:        }
                    192:        if (port != 0) {
1.11      itojun    193:                s = socket(fromp->sa_family, SOCK_STREAM, 0);
1.5       mrg       194:                if (s < 0) {
1.14      thorpej   195:                        if (dolog)
1.5       mrg       196:                                syslog(LOG_ERR, "socket: %m");
1.1       cgd       197:                        exit(1);
1.5       mrg       198:                }
1.11      itojun    199:                (void)alarm(60);
                    200:                switch (fromp->sa_family) {
                    201:                case AF_INET:
                    202:                        ((struct sockaddr_in *)fromp)->sin_port = htons(port);
                    203:                        break;
                    204:                case AF_INET6:
                    205:                        ((struct sockaddr_in6 *)fromp)->sin6_port = htons(port);
                    206:                        break;
                    207:                default:
                    208:                        syslog(LOG_ERR, "unsupported address family");
1.1       cgd       209:                        exit(1);
1.5       mrg       210:                }
1.11      itojun    211:                if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 0) {
1.14      thorpej   212:                        if (dolog)
1.5       mrg       213:                                syslog(LOG_ERR, "connect: %m");
1.1       cgd       214:                        exit(1);
1.5       mrg       215:                }
                    216:                (void)alarm(0);
1.1       cgd       217:        }
1.21    ! christos  218:        (void)alarm(60);
1.1       cgd       219:        getstr(user, sizeof(user), "username");
                    220:        getstr(pass, sizeof(pass), "password");
                    221:        getstr(cmdbuf, sizeof(cmdbuf), "command");
1.21    ! christos  222:        (void)alarm(0);
1.1       cgd       223:        setpwent();
                    224:        pwd = getpwnam(user);
                    225:        if (pwd == NULL) {
1.14      thorpej   226:                if (dolog)
1.5       mrg       227:                        syslog(LOG_ERR, "no such user %s", user);
1.21    ! christos  228:                rexecd_errx(1, "Login incorrect.");
1.1       cgd       229:        }
                    230:        endpwent();
1.21    ! christos  231: #ifdef USE_PAM
        !           232:        if (!pam_ok(pam_start("rexecd", user, &pamc, &pamh)) ||
        !           233:            !pam_ok(pam_set_item(pamh, PAM_RHOST, hostname)) ||
        !           234:            !pam_ok(pam_set_item(pamh, PAM_AUTHTOK, pass))) {
        !           235:                if (dolog)
        !           236:                        syslog(LOG_ERR, "PAM ERROR %s@%s (%s)", user,
        !           237:                           hostname, pam_strerror(pamh, pam_err));
        !           238:                rexecd_errx(1, "Try again.");
        !           239:        }
        !           240:        if (!pam_ok(pam_authenticate(pamh, pam_flags)) ||
        !           241:            !pam_ok(pam_acct_mgmt(pamh, pam_flags))) {
        !           242:                if (dolog)
        !           243:                        syslog(LOG_ERR, "LOGIN REFUSED for %s@%s (%s)", user,
        !           244:                           hostname, pam_strerror(pamh, pam_err));
        !           245:                rexecd_errx(1, "Password incorrect.");
        !           246:        }
        !           247: #else
1.1       cgd       248:        if (*pwd->pw_passwd != '\0') {
                    249:                namep = crypt(pass, pwd->pw_passwd);
1.21    ! christos  250:                if (strcmp(namep, pwd->pw_passwd) != 0) {
1.14      thorpej   251:                        if (dolog)
1.5       mrg       252:                                syslog(LOG_ERR, "incorrect password for %s",
                    253:                                    user);
1.21    ! christos  254:                        rexecd_errx(1, "Password incorrect.");/* XXX: wrong! */
1.1       cgd       255:                }
1.5       mrg       256:        } else
1.21    ! christos  257:                (void)crypt("dummy password", "PA");    /* must always crypt */
        !           258: #endif
1.1       cgd       259:        if (chdir(pwd->pw_dir) < 0) {
1.14      thorpej   260:                if (dolog)
1.5       mrg       261:                        syslog(LOG_ERR, "%s does not exist for %s", pwd->pw_dir,
1.21    ! christos  262:                               user);
        !           263:                rexecd_errx(1, "No remote directory.");
1.1       cgd       264:        }
1.21    ! christos  265:
        !           266:        if (dolog)
        !           267:                syslog(LOG_INFO, "login from %s as %s", hostname, user);
1.19      ginsbach  268:        (void)write(STDERR_FILENO, "\0", 1);
1.1       cgd       269:        if (port) {
1.5       mrg       270:                if (pipe(pv) < 0 || (pid = fork()) == -1) {
1.14      thorpej   271:                        if (dolog)
1.5       mrg       272:                                syslog(LOG_ERR,"pipe or fork failed for %s: %m",
                    273:                                    user);
1.21    ! christos  274:                        rexecd_errx(1, "Try again.");
1.1       cgd       275:                }
                    276:                if (pid) {
1.21    ! christos  277:                        /* parent */
        !           278: #ifdef USE_PAM
        !           279:                        (void)pam_end(pamh, pam_err);
        !           280: #endif
1.19      ginsbach  281:                        (void)close(STDIN_FILENO);
                    282:                        (void)close(STDOUT_FILENO);
                    283:                        (void)close(STDERR_FILENO);
1.5       mrg       284:                        (void)close(pv[1]);
                    285:                        fds[0].fd = s;
                    286:                        fds[1].fd = pv[0];
                    287:                        fds[0].events = fds[1].events = POLLIN;
                    288:                        if (ioctl(pv[1], FIONBIO, (char *)&one) < 0)
1.8       wiz       289:                                _exit(1);
1.1       cgd       290:                        /* should set s nbio! */
                    291:                        do {
1.5       mrg       292:                                if (poll(fds, 2, 0) < 0) {
                    293:                                        close(s);
                    294:                                        close(pv[0]);
1.8       wiz       295:                                        _exit(1);
1.5       mrg       296:                                }
                    297:                                if (fds[0].revents & POLLIN) {
1.1       cgd       298:                                        if (read(s, &sig, 1) <= 0)
1.5       mrg       299:                                                fds[0].events = 0;
1.1       cgd       300:                                        else
                    301:                                                killpg(pid, sig);
                    302:                                }
1.5       mrg       303:                                if (fds[1].revents & POLLIN) {
1.1       cgd       304:                                        cc = read(pv[0], buf, sizeof (buf));
                    305:                                        if (cc <= 0) {
                    306:                                                shutdown(s, 1+1);
1.5       mrg       307:                                                fds[1].events = 0;
1.1       cgd       308:                                        } else
1.5       mrg       309:                                                (void)write(s, buf, cc);
1.1       cgd       310:                                }
1.5       mrg       311:                        } while ((fds[0].events | fds[1].events) & POLLIN);
                    312:                        _exit(0);
                    313:                }
1.21    ! christos  314:                /* child */
1.5       mrg       315:                (void)close(s);
                    316:                (void)close(pv[0]);
1.21    ! christos  317:                if (dup2(pv[1], STDERR_FILENO) < 0) {
1.14      thorpej   318:                        if (dolog)
1.5       mrg       319:                                syslog(LOG_ERR, "dup2 failed for %s", user);
1.21    ! christos  320:                        rexecd_errx(1, "Try again.");
1.1       cgd       321:                }
                    322:        }
                    323:        if (*pwd->pw_shell == '\0')
1.21    ! christos  324:                pwd->pw_shell = __UNCONST(_PATH_BSHELL);
1.15      dsl       325:        if (setsid() < 0 ||
                    326:            setlogin(pwd->pw_name) < 0 ||
1.5       mrg       327:            initgroups(pwd->pw_name, pwd->pw_gid) < 0 ||
1.21    ! christos  328: #ifdef USE_PAM
        !           329:            setgid((gid_t)pwd->pw_gid) < 0) {
        !           330: #else
1.19      ginsbach  331:            setgid((gid_t)pwd->pw_gid) < 0 ||
1.5       mrg       332:            setuid((uid_t)pwd->pw_uid) < 0) {
1.21    ! christos  333: #endif
        !           334:                rexecd_errx(1, "Try again.");
1.14      thorpej   335:                if (dolog)
1.5       mrg       336:                        syslog(LOG_ERR, "could not set permissions for %s: %m",
                    337:                            user);
                    338:                exit(1);
                    339:        }
1.21    ! christos  340: #ifdef USE_PAM
        !           341:        if (!pam_ok(pam_setcred(pamh, PAM_ESTABLISH_CRED)))
        !           342:                syslog(LOG_ERR, "pam_setcred() failed: %s",
        !           343:                       pam_strerror(pamh, pam_err));
        !           344:        (void)pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
        !           345:        (void)pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
        !           346:        (void)pam_setenv(pamh, "USER", pwd->pw_name, 1);
        !           347:        (void)pam_setenv(pamh, "LOGNAME", pwd->pw_name, 1);
        !           348:        (void)pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
        !           349:        environ = pam_getenvlist(pamh);
        !           350:        (void)pam_end(pamh, pam_err);
        !           351:        if (setuid((uid_t)pwd->pw_uid) < 0) {
        !           352:                 if (dolog)
        !           353:                         syslog(LOG_ERR, "could not set uid for %s: %m",
        !           354:                             user);
        !           355:                 rexecd_errx(1, "Try again.");
        !           356:         }
        !           357: #else
1.16      itojun    358:        (void)strlcat(path, _PATH_DEFPATH, sizeof(path));
1.1       cgd       359:        environ = envinit;
1.21    ! christos  360:        (void)strlcat(homedir, pwd->pw_dir, sizeof(homedir));
        !           361:        (void)strlcat(shell, pwd->pw_shell, sizeof(shell));
        !           362:        (void)strlcat(username, pwd->pw_name, sizeof(username));
        !           363:        (void)strlcat(logname, pwd->pw_name, sizeof(logname));
        !           364: #endif
        !           365:
1.4       mrg       366:        cp = strrchr(pwd->pw_shell, '/');
1.1       cgd       367:        if (cp)
                    368:                cp++;
                    369:        else
                    370:                cp = pwd->pw_shell;
1.14      thorpej   371:        if (dolog)
1.5       mrg       372:                syslog(LOG_INFO, "running command for %s: %s", user, cmdbuf);
1.21    ! christos  373:        (void)execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
1.14      thorpej   374:        if (dolog)
1.5       mrg       375:                syslog(LOG_ERR, "execl failed for %s: %m", user);
1.21    ! christos  376:        err(1, "%s", pwd->pw_shell);
1.1       cgd       377: }
                    378:
1.4       mrg       379: void
1.21    ! christos  380: rexecd_errx(int ex, const char *fmt, ...)
1.1       cgd       381: {
                    382:        char buf[BUFSIZ];
1.4       mrg       383:        va_list ap;
1.21    ! christos  384:        ssize_t len;
1.4       mrg       385:
                    386:        va_start(ap, fmt);
1.1       cgd       387:        buf[0] = 1;
1.21    ! christos  388:        len = vsnprintf(buf + 1, sizeof(buf) - 1, fmt, ap) + 1;
        !           389:        buf[len++] = '\n';
        !           390:        (void)write(STDERR_FILENO, buf, len);
1.9       wiz       391:        va_end(ap);
1.21    ! christos  392:        exit(ex);
1.1       cgd       393: }
                    394:
1.4       mrg       395: void
1.21    ! christos  396: getstr(char *buf, int cnt, const char *emsg)
1.1       cgd       397: {
                    398:        char c;
                    399:
                    400:        do {
1.19      ginsbach  401:                if (read(STDIN_FILENO, &c, 1) != 1)
1.1       cgd       402:                        exit(1);
                    403:                *buf++ = c;
1.21    ! christos  404:                if (--cnt == 0)
        !           405:                        rexecd_errx(1, "%s too long", emsg);
1.1       cgd       406:        } while (c != 0);
                    407: }
1.21    ! christos  408:
        !           409: static void
        !           410: usage(void)
        !           411: {
        !           412:        (void)fprintf(stderr, "Usage: %s [-l]\n", getprogname());
        !           413:        exit(1);
        !           414: }

CVSweb <webmaster@jp.NetBSD.org>