[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.23

1.23    ! christos    1: /*     $NetBSD: rexecd.c,v 1.22 2005/03/30 01:07:47 christos 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.23    ! christos   39: __RCSID("$NetBSD: rexecd.c,v 1.22 2005/03/30 01:07:47 christos Exp $");
1.4       mrg        40: #endif
1.1       cgd        41: #endif /* not lint */
                     42:
1.22      christos   43: #include <sys/ioctl.h>
1.1       cgd        44: #include <sys/param.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.22      christos   55: #include <poll.h>
1.1       cgd        56: #include <pwd.h>
1.4       mrg        57: #include <signal.h>
1.10      wiz        58: #include <stdarg.h>
1.1       cgd        59: #include <stdio.h>
                     60: #include <stdlib.h>
                     61: #include <string.h>
1.4       mrg        62: #include <unistd.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)));
1.22      christos   72: static void doit(struct sockaddr *) __attribute__((__noreturn__));
1.21      christos   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.22      christos   87: extern char    **environ;
                     88: static 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:
1.22      christos  122:        fromlen = sizeof(from);
1.21      christos  123:        if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0)
1.22      christos  124:                err(EXIT_FAILURE, "getpeername");
1.5       mrg       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);
1.22      christos  137:                return EXIT_FAILURE;
1.21      christos  138:        }
                    139:
                    140:        doit((struct sockaddr *)&from);
1.1       cgd       141: }
                    142:
1.4       mrg       143: void
1.21      christos  144: doit(struct sockaddr *fromp)
1.1       cgd       145: {
1.5       mrg       146:        struct pollfd fds[2];
1.21      christos  147:        char cmdbuf[NCARGS + 1];
1.6       mycroft   148:        const char *cp;
1.1       cgd       149:        char user[16], pass[16];
1.5       mrg       150:        char buf[BUFSIZ], sig;
1.22      christos  151:        struct passwd *pwd, pwres;
1.4       mrg       152:        int s = -1; /* XXX gcc */
1.5       mrg       153:        int pv[2], pid, cc;
1.1       cgd       154:        int one = 1;
1.5       mrg       155:        in_port_t port;
1.21      christos  156:        char hostname[2 * MAXHOSTNAMELEN + 1];
                    157:        char pbuf[NI_MAXSERV];
                    158:        const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
                    159: #ifndef USE_PAM
                    160:        char *namep;
                    161: #endif
1.22      christos  162:        char pwbuf[1024];
1.1       cgd       163:
1.5       mrg       164:        (void)signal(SIGINT, SIG_DFL);
                    165:        (void)signal(SIGQUIT, SIG_DFL);
                    166:        (void)signal(SIGTERM, SIG_DFL);
1.21      christos  167:        (void)dup2(STDIN_FILENO, STDOUT_FILENO);
                    168:        (void)dup2(STDIN_FILENO, STDERR_FILENO);
                    169:
                    170:        if (getnameinfo(fromp, fromp->sa_len, hostname, sizeof(hostname),
                    171:            pbuf, sizeof(pbuf), niflags) != 0) {
                    172:                if (dolog)
                    173:                        syslog(LOG_ERR, "malformed \"from\" address (af %d)",
                    174:                               fromp->sa_family);
1.22      christos  175:                exit(EXIT_FAILURE);
1.21      christos  176:        }
                    177:
1.5       mrg       178:        (void)alarm(60);
1.1       cgd       179:        port = 0;
                    180:        for (;;) {
                    181:                char c;
1.21      christos  182:                if (read(STDIN_FILENO, &c, 1) != 1) {
1.14      thorpej   183:                        if (dolog)
1.21      christos  184:                                syslog(LOG_ERR, "initial read failed");
1.22      christos  185:                        exit(EXIT_FAILURE);
1.5       mrg       186:                }
1.1       cgd       187:                if (c == 0)
                    188:                        break;
                    189:                port = port * 10 + c - '0';
                    190:        }
                    191:        if (port != 0) {
1.11      itojun    192:                s = socket(fromp->sa_family, SOCK_STREAM, 0);
1.5       mrg       193:                if (s < 0) {
1.14      thorpej   194:                        if (dolog)
1.5       mrg       195:                                syslog(LOG_ERR, "socket: %m");
1.22      christos  196:                        exit(EXIT_FAILURE);
1.5       mrg       197:                }
1.11      itojun    198:                (void)alarm(60);
                    199:                switch (fromp->sa_family) {
                    200:                case AF_INET:
                    201:                        ((struct sockaddr_in *)fromp)->sin_port = htons(port);
                    202:                        break;
                    203:                case AF_INET6:
                    204:                        ((struct sockaddr_in6 *)fromp)->sin6_port = htons(port);
                    205:                        break;
                    206:                default:
                    207:                        syslog(LOG_ERR, "unsupported address family");
1.22      christos  208:                        exit(EXIT_FAILURE);
1.5       mrg       209:                }
1.11      itojun    210:                if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 0) {
1.14      thorpej   211:                        if (dolog)
1.5       mrg       212:                                syslog(LOG_ERR, "connect: %m");
1.22      christos  213:                        exit(EXIT_FAILURE);
1.5       mrg       214:                }
                    215:                (void)alarm(0);
1.1       cgd       216:        }
1.21      christos  217:        (void)alarm(60);
1.1       cgd       218:        getstr(user, sizeof(user), "username");
                    219:        getstr(pass, sizeof(pass), "password");
                    220:        getstr(cmdbuf, sizeof(cmdbuf), "command");
1.21      christos  221:        (void)alarm(0);
1.23    ! christos  222:        if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
        !           223:            pwd == NULL) {
1.14      thorpej   224:                if (dolog)
1.5       mrg       225:                        syslog(LOG_ERR, "no such user %s", user);
1.22      christos  226:                rexecd_errx(EXIT_FAILURE, "Login incorrect.");
1.1       cgd       227:        }
1.21      christos  228: #ifdef USE_PAM
                    229:        if (!pam_ok(pam_start("rexecd", user, &pamc, &pamh)) ||
                    230:            !pam_ok(pam_set_item(pamh, PAM_RHOST, hostname)) ||
                    231:            !pam_ok(pam_set_item(pamh, PAM_AUTHTOK, pass))) {
                    232:                if (dolog)
                    233:                        syslog(LOG_ERR, "PAM ERROR %s@%s (%s)", user,
                    234:                           hostname, pam_strerror(pamh, pam_err));
1.22      christos  235:                rexecd_errx(EXIT_FAILURE, "Try again.");
1.21      christos  236:        }
                    237:        if (!pam_ok(pam_authenticate(pamh, pam_flags)) ||
                    238:            !pam_ok(pam_acct_mgmt(pamh, pam_flags))) {
                    239:                if (dolog)
                    240:                        syslog(LOG_ERR, "LOGIN REFUSED for %s@%s (%s)", user,
                    241:                           hostname, pam_strerror(pamh, pam_err));
1.22      christos  242:                rexecd_errx(EXIT_FAILURE, "Password incorrect.");
1.21      christos  243:        }
                    244: #else
1.1       cgd       245:        if (*pwd->pw_passwd != '\0') {
                    246:                namep = crypt(pass, pwd->pw_passwd);
1.21      christos  247:                if (strcmp(namep, pwd->pw_passwd) != 0) {
1.14      thorpej   248:                        if (dolog)
1.5       mrg       249:                                syslog(LOG_ERR, "incorrect password for %s",
                    250:                                    user);
1.22      christos  251:                        rexecd_errx(EXIT_FAILURE,
                    252:                                    "Password incorrect.");/* XXX: wrong! */
1.1       cgd       253:                }
1.5       mrg       254:        } else
1.21      christos  255:                (void)crypt("dummy password", "PA");    /* must always crypt */
                    256: #endif
1.1       cgd       257:        if (chdir(pwd->pw_dir) < 0) {
1.14      thorpej   258:                if (dolog)
1.5       mrg       259:                        syslog(LOG_ERR, "%s does not exist for %s", pwd->pw_dir,
1.21      christos  260:                               user);
1.22      christos  261:                rexecd_errx(EXIT_FAILURE, "No remote directory.");
1.1       cgd       262:        }
1.21      christos  263:
                    264:        if (dolog)
                    265:                syslog(LOG_INFO, "login from %s as %s", hostname, user);
1.19      ginsbach  266:        (void)write(STDERR_FILENO, "\0", 1);
1.1       cgd       267:        if (port) {
1.5       mrg       268:                if (pipe(pv) < 0 || (pid = fork()) == -1) {
1.14      thorpej   269:                        if (dolog)
1.5       mrg       270:                                syslog(LOG_ERR,"pipe or fork failed for %s: %m",
                    271:                                    user);
1.22      christos  272:                        rexecd_errx(EXIT_FAILURE, "Try again.");
1.1       cgd       273:                }
                    274:                if (pid) {
1.21      christos  275:                        /* parent */
                    276: #ifdef USE_PAM
                    277:                        (void)pam_end(pamh, pam_err);
                    278: #endif
1.19      ginsbach  279:                        (void)close(STDIN_FILENO);
                    280:                        (void)close(STDOUT_FILENO);
                    281:                        (void)close(STDERR_FILENO);
1.5       mrg       282:                        (void)close(pv[1]);
                    283:                        fds[0].fd = s;
                    284:                        fds[1].fd = pv[0];
                    285:                        fds[0].events = fds[1].events = POLLIN;
                    286:                        if (ioctl(pv[1], FIONBIO, (char *)&one) < 0)
1.8       wiz       287:                                _exit(1);
1.1       cgd       288:                        /* should set s nbio! */
                    289:                        do {
1.5       mrg       290:                                if (poll(fds, 2, 0) < 0) {
1.22      christos  291:                                        (void)close(s);
                    292:                                        (void)close(pv[0]);
1.8       wiz       293:                                        _exit(1);
1.5       mrg       294:                                }
                    295:                                if (fds[0].revents & POLLIN) {
1.1       cgd       296:                                        if (read(s, &sig, 1) <= 0)
1.5       mrg       297:                                                fds[0].events = 0;
1.1       cgd       298:                                        else
1.22      christos  299:                                                (void)killpg(pid, sig);
1.1       cgd       300:                                }
1.5       mrg       301:                                if (fds[1].revents & POLLIN) {
1.1       cgd       302:                                        cc = read(pv[0], buf, sizeof (buf));
                    303:                                        if (cc <= 0) {
1.22      christos  304:                                                (void)shutdown(s, SHUT_RDWR);
1.5       mrg       305:                                                fds[1].events = 0;
1.1       cgd       306:                                        } else
1.5       mrg       307:                                                (void)write(s, buf, cc);
1.1       cgd       308:                                }
1.5       mrg       309:                        } while ((fds[0].events | fds[1].events) & POLLIN);
                    310:                        _exit(0);
                    311:                }
1.21      christos  312:                /* child */
1.5       mrg       313:                (void)close(s);
                    314:                (void)close(pv[0]);
1.21      christos  315:                if (dup2(pv[1], STDERR_FILENO) < 0) {
1.14      thorpej   316:                        if (dolog)
1.5       mrg       317:                                syslog(LOG_ERR, "dup2 failed for %s", user);
1.22      christos  318:                        rexecd_errx(EXIT_FAILURE, "Try again.");
1.1       cgd       319:                }
                    320:        }
                    321:        if (*pwd->pw_shell == '\0')
1.21      christos  322:                pwd->pw_shell = __UNCONST(_PATH_BSHELL);
1.15      dsl       323:        if (setsid() < 0 ||
                    324:            setlogin(pwd->pw_name) < 0 ||
1.5       mrg       325:            initgroups(pwd->pw_name, pwd->pw_gid) < 0 ||
1.21      christos  326: #ifdef USE_PAM
                    327:            setgid((gid_t)pwd->pw_gid) < 0) {
                    328: #else
1.19      ginsbach  329:            setgid((gid_t)pwd->pw_gid) < 0 ||
1.5       mrg       330:            setuid((uid_t)pwd->pw_uid) < 0) {
1.21      christos  331: #endif
1.22      christos  332:                rexecd_errx(EXIT_FAILURE, "Try again.");
1.14      thorpej   333:                if (dolog)
1.5       mrg       334:                        syslog(LOG_ERR, "could not set permissions for %s: %m",
                    335:                            user);
1.22      christos  336:                exit(EXIT_FAILURE);
1.5       mrg       337:        }
1.21      christos  338: #ifdef USE_PAM
                    339:        if (!pam_ok(pam_setcred(pamh, PAM_ESTABLISH_CRED)))
                    340:                syslog(LOG_ERR, "pam_setcred() failed: %s",
                    341:                       pam_strerror(pamh, pam_err));
                    342:        (void)pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
                    343:        (void)pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
                    344:        (void)pam_setenv(pamh, "USER", pwd->pw_name, 1);
                    345:        (void)pam_setenv(pamh, "LOGNAME", pwd->pw_name, 1);
                    346:        (void)pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
                    347:        environ = pam_getenvlist(pamh);
                    348:        (void)pam_end(pamh, pam_err);
                    349:        if (setuid((uid_t)pwd->pw_uid) < 0) {
                    350:                 if (dolog)
                    351:                         syslog(LOG_ERR, "could not set uid for %s: %m",
                    352:                             user);
1.22      christos  353:                 rexecd_errx(EXIT_FAILURE, "Try again.");
1.21      christos  354:         }
                    355: #else
1.16      itojun    356:        (void)strlcat(path, _PATH_DEFPATH, sizeof(path));
1.1       cgd       357:        environ = envinit;
1.21      christos  358:        (void)strlcat(homedir, pwd->pw_dir, sizeof(homedir));
                    359:        (void)strlcat(shell, pwd->pw_shell, sizeof(shell));
                    360:        (void)strlcat(username, pwd->pw_name, sizeof(username));
                    361:        (void)strlcat(logname, pwd->pw_name, sizeof(logname));
                    362: #endif
                    363:
1.4       mrg       364:        cp = strrchr(pwd->pw_shell, '/');
1.1       cgd       365:        if (cp)
                    366:                cp++;
                    367:        else
                    368:                cp = pwd->pw_shell;
1.14      thorpej   369:        if (dolog)
1.5       mrg       370:                syslog(LOG_INFO, "running command for %s: %s", user, cmdbuf);
1.21      christos  371:        (void)execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
1.14      thorpej   372:        if (dolog)
1.5       mrg       373:                syslog(LOG_ERR, "execl failed for %s: %m", user);
1.22      christos  374:        err(EXIT_FAILURE, "%s", pwd->pw_shell);
1.1       cgd       375: }
                    376:
1.4       mrg       377: void
1.21      christos  378: rexecd_errx(int ex, const char *fmt, ...)
1.1       cgd       379: {
                    380:        char buf[BUFSIZ];
1.4       mrg       381:        va_list ap;
1.21      christos  382:        ssize_t len;
1.4       mrg       383:
                    384:        va_start(ap, fmt);
1.1       cgd       385:        buf[0] = 1;
1.21      christos  386:        len = vsnprintf(buf + 1, sizeof(buf) - 1, fmt, ap) + 1;
                    387:        buf[len++] = '\n';
                    388:        (void)write(STDERR_FILENO, buf, len);
1.9       wiz       389:        va_end(ap);
1.21      christos  390:        exit(ex);
1.1       cgd       391: }
                    392:
1.4       mrg       393: void
1.21      christos  394: getstr(char *buf, int cnt, const char *emsg)
1.1       cgd       395: {
                    396:        char c;
                    397:
                    398:        do {
1.19      ginsbach  399:                if (read(STDIN_FILENO, &c, 1) != 1)
1.22      christos  400:                        exit(EXIT_FAILURE);
1.1       cgd       401:                *buf++ = c;
1.21      christos  402:                if (--cnt == 0)
1.22      christos  403:                        rexecd_errx(EXIT_FAILURE, "%s too long", emsg);
1.1       cgd       404:        } while (c != 0);
                    405: }
1.21      christos  406:
                    407: static void
                    408: usage(void)
                    409: {
                    410:        (void)fprintf(stderr, "Usage: %s [-l]\n", getprogname());
1.22      christos  411:        exit(EXIT_FAILURE);
1.21      christos  412: }

CVSweb <webmaster@jp.NetBSD.org>