1*0c4ddb15Slukem /* $NetBSD: rexecd.c,v 1.26 2008/07/20 01:09:07 lukem Exp $ */
2da5c78b3Smrg
361f28255Scgd /*
4da5c78b3Smrg * Copyright (c) 1983, 1993
5da5c78b3Smrg * The Regents of the University of California. All rights reserved.
661f28255Scgd *
761f28255Scgd * Redistribution and use in source and binary forms, with or without
861f28255Scgd * modification, are permitted provided that the following conditions
961f28255Scgd * are met:
1061f28255Scgd * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd * notice, this list of conditions and the following disclaimer.
1261f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd * notice, this list of conditions and the following disclaimer in the
1461f28255Scgd * documentation and/or other materials provided with the distribution.
158e6ab883Sagc * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd * may be used to endorse or promote products derived from this software
1761f28255Scgd * without specific prior written permission.
1861f28255Scgd *
1961f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd * SUCH DAMAGE.
3061f28255Scgd */
3161f28255Scgd
32da5c78b3Smrg #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
34*0c4ddb15Slukem __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
35*0c4ddb15Slukem The Regents of the University of California. All rights reserved.");
36da5c78b3Smrg #if 0
37da5c78b3Smrg static char sccsid[] = "from: @(#)rexecd.c 8.1 (Berkeley) 6/4/93";
38da5c78b3Smrg #else
39*0c4ddb15Slukem __RCSID("$NetBSD: rexecd.c,v 1.26 2008/07/20 01:09:07 lukem Exp $");
40da5c78b3Smrg #endif
4161f28255Scgd #endif /* not lint */
4261f28255Scgd
4361f28255Scgd #include <sys/ioctl.h>
442e7a44f2Schristos #include <sys/param.h>
4561f28255Scgd #include <sys/socket.h>
46fc0b820cSmrg #include <sys/syslog.h>
4761f28255Scgd #include <sys/time.h>
48da5c78b3Smrg
4961f28255Scgd #include <netinet/in.h>
50da5c78b3Smrg
51fc0b820cSmrg #include <err.h>
5261f28255Scgd #include <errno.h>
53da5c78b3Smrg #include <netdb.h>
54da5c78b3Smrg #include <paths.h>
552e7a44f2Schristos #include <poll.h>
56da5c78b3Smrg #include <pwd.h>
57da5c78b3Smrg #include <signal.h>
58bf840df2Swiz #include <stdarg.h>
5961f28255Scgd #include <stdio.h>
6061f28255Scgd #include <stdlib.h>
6161f28255Scgd #include <string.h>
62da5c78b3Smrg #include <unistd.h>
6361f28255Scgd
6462136d56Schristos #ifdef USE_PAM
6562136d56Schristos #include <security/pam_appl.h>
6662136d56Schristos #include <security/openpam.h>
6762136d56Schristos #endif
6861f28255Scgd
6962136d56Schristos int main(int, char *[]);
7062136d56Schristos static void rexecd_errx(int, const char *, ...)
7162136d56Schristos __attribute__((__noreturn__, __format__(__printf__, 2, 3)));
728b0f9554Sperry static void doit(struct sockaddr *) __dead;
7362136d56Schristos static void getstr(char *, int, const char *);
748b0f9554Sperry static void usage(void) __dead;
7562136d56Schristos
7662136d56Schristos #ifdef USE_PAM
7762136d56Schristos static pam_handle_t *pamh;
7862136d56Schristos static struct pam_conv pamc = {
7962136d56Schristos openpam_nullconv,
8062136d56Schristos NULL
8162136d56Schristos };
8262136d56Schristos static int pam_flags = PAM_SILENT|PAM_DISALLOW_NULL_AUTHTOK;
8362136d56Schristos static int pam_err;
8462136d56Schristos #define pam_ok(err) ((pam_err = (err)) == PAM_SUCCESS)
8562136d56Schristos #endif
8662136d56Schristos
872e7a44f2Schristos extern char **environ;
882e7a44f2Schristos static int dolog;
8962136d56Schristos #ifndef USE_PAM
9062136d56Schristos static char username[32 + 1] = "USER=";
9162136d56Schristos static char logname[32 + 3 + 1] = "LOGNAME=";
9262136d56Schristos static char homedir[PATH_MAX + 1] = "HOME=";
9362136d56Schristos static char shell[PATH_MAX + 1] = "SHELL=";
9462136d56Schristos static char path[sizeof(_PATH_DEFPATH) + sizeof("PATH=")] = "PATH=";
9562136d56Schristos static char *envinit[] = { homedir, shell, path, username, logname, 0 };
9662136d56Schristos #endif
97fc0b820cSmrg
9861f28255Scgd /*
9961f28255Scgd * remote execute server:
10061f28255Scgd * username\0
10161f28255Scgd * password\0
10261f28255Scgd * command\0
10361f28255Scgd * data
10461f28255Scgd */
105da5c78b3Smrg int
main(int argc,char * argv[])1063596f57fSginsbach main(int argc, char *argv[])
10761f28255Scgd {
108abec5636Sitojun struct sockaddr_storage from;
10962136d56Schristos socklen_t fromlen;
11062136d56Schristos int ch;
11161f28255Scgd
112fc0b820cSmrg while ((ch = getopt(argc, argv, "l")) != -1)
113fc0b820cSmrg switch (ch) {
114fc0b820cSmrg case 'l':
1157ec31d73Sthorpej dolog = 1;
116fc0b820cSmrg openlog("rexecd", LOG_PID, LOG_DAEMON);
117fc0b820cSmrg break;
118fc0b820cSmrg default:
11962136d56Schristos usage();
12061f28255Scgd }
121fc0b820cSmrg
122fc0b820cSmrg fromlen = sizeof(from);
12362136d56Schristos if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0)
1242e7a44f2Schristos err(EXIT_FAILURE, "getpeername");
125fc0b820cSmrg
12662136d56Schristos if (((struct sockaddr *)&from)->sa_family == AF_INET6 &&
12762136d56Schristos IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) {
12862136d56Schristos char hbuf[NI_MAXHOST];
12962136d56Schristos if (getnameinfo((struct sockaddr *)&from, fromlen, hbuf,
13062136d56Schristos sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) {
13162136d56Schristos (void)strlcpy(hbuf, "invalid", sizeof(hbuf));
13262136d56Schristos }
13362136d56Schristos if (dolog)
13462136d56Schristos syslog(LOG_ERR,
13562136d56Schristos "malformed \"from\" address (v4 mapped, %s)",
13662136d56Schristos hbuf);
1372e7a44f2Schristos return EXIT_FAILURE;
13862136d56Schristos }
13962136d56Schristos
14062136d56Schristos doit((struct sockaddr *)&from);
14161f28255Scgd }
14261f28255Scgd
143da5c78b3Smrg void
doit(struct sockaddr * fromp)14462136d56Schristos doit(struct sockaddr *fromp)
14561f28255Scgd {
146fc0b820cSmrg struct pollfd fds[2];
14762136d56Schristos char cmdbuf[NCARGS + 1];
1485dd823abSmycroft const char *cp;
14961f28255Scgd char user[16], pass[16];
150fc0b820cSmrg char buf[BUFSIZ], sig;
1512e7a44f2Schristos struct passwd *pwd, pwres;
152da5c78b3Smrg int s = -1; /* XXX gcc */
153fc0b820cSmrg int pv[2], pid, cc;
15461f28255Scgd int one = 1;
155fc0b820cSmrg in_port_t port;
15662136d56Schristos char hostname[2 * MAXHOSTNAMELEN + 1];
15762136d56Schristos char pbuf[NI_MAXSERV];
15862136d56Schristos const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
15962136d56Schristos #ifndef USE_PAM
16062136d56Schristos char *namep;
16162136d56Schristos #endif
1622e7a44f2Schristos char pwbuf[1024];
16361f28255Scgd
16461f28255Scgd (void)signal(SIGINT, SIG_DFL);
16561f28255Scgd (void)signal(SIGQUIT, SIG_DFL);
16661f28255Scgd (void)signal(SIGTERM, SIG_DFL);
16762136d56Schristos (void)dup2(STDIN_FILENO, STDOUT_FILENO);
16862136d56Schristos (void)dup2(STDIN_FILENO, STDERR_FILENO);
16962136d56Schristos
17062136d56Schristos if (getnameinfo(fromp, fromp->sa_len, hostname, sizeof(hostname),
17162136d56Schristos pbuf, sizeof(pbuf), niflags) != 0) {
17262136d56Schristos if (dolog)
17362136d56Schristos syslog(LOG_ERR, "malformed \"from\" address (af %d)",
17462136d56Schristos fromp->sa_family);
1752e7a44f2Schristos exit(EXIT_FAILURE);
17662136d56Schristos }
17762136d56Schristos
17861f28255Scgd (void)alarm(60);
17961f28255Scgd port = 0;
18061f28255Scgd for (;;) {
18161f28255Scgd char c;
18262136d56Schristos if (read(STDIN_FILENO, &c, 1) != 1) {
1837ec31d73Sthorpej if (dolog)
18462136d56Schristos syslog(LOG_ERR, "initial read failed");
1852e7a44f2Schristos exit(EXIT_FAILURE);
186fc0b820cSmrg }
18761f28255Scgd if (c == 0)
18861f28255Scgd break;
18961f28255Scgd port = port * 10 + c - '0';
19061f28255Scgd }
19161f28255Scgd if (port != 0) {
192abec5636Sitojun s = socket(fromp->sa_family, SOCK_STREAM, 0);
193fc0b820cSmrg if (s < 0) {
1947ec31d73Sthorpej if (dolog)
195fc0b820cSmrg syslog(LOG_ERR, "socket: %m");
1962e7a44f2Schristos exit(EXIT_FAILURE);
197fc0b820cSmrg }
198abec5636Sitojun (void)alarm(60);
199abec5636Sitojun switch (fromp->sa_family) {
200abec5636Sitojun case AF_INET:
201abec5636Sitojun ((struct sockaddr_in *)fromp)->sin_port = htons(port);
202abec5636Sitojun break;
203abec5636Sitojun case AF_INET6:
204abec5636Sitojun ((struct sockaddr_in6 *)fromp)->sin6_port = htons(port);
205abec5636Sitojun break;
206abec5636Sitojun default:
207abec5636Sitojun syslog(LOG_ERR, "unsupported address family");
2082e7a44f2Schristos exit(EXIT_FAILURE);
209fc0b820cSmrg }
210abec5636Sitojun if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 0) {
2117ec31d73Sthorpej if (dolog)
212fc0b820cSmrg syslog(LOG_ERR, "connect: %m");
2132e7a44f2Schristos exit(EXIT_FAILURE);
214fc0b820cSmrg }
21561f28255Scgd (void)alarm(0);
21661f28255Scgd }
21762136d56Schristos (void)alarm(60);
21861f28255Scgd getstr(user, sizeof(user), "username");
21961f28255Scgd getstr(pass, sizeof(pass), "password");
22061f28255Scgd getstr(cmdbuf, sizeof(cmdbuf), "command");
22162136d56Schristos (void)alarm(0);
222cce62d09Schristos if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
223cce62d09Schristos pwd == NULL) {
2247ec31d73Sthorpej if (dolog)
225fc0b820cSmrg syslog(LOG_ERR, "no such user %s", user);
2262e7a44f2Schristos rexecd_errx(EXIT_FAILURE, "Login incorrect.");
22761f28255Scgd }
22862136d56Schristos #ifdef USE_PAM
22962136d56Schristos if (!pam_ok(pam_start("rexecd", user, &pamc, &pamh)) ||
23062136d56Schristos !pam_ok(pam_set_item(pamh, PAM_RHOST, hostname)) ||
23162136d56Schristos !pam_ok(pam_set_item(pamh, PAM_AUTHTOK, pass))) {
23262136d56Schristos if (dolog)
23362136d56Schristos syslog(LOG_ERR, "PAM ERROR %s@%s (%s)", user,
23462136d56Schristos hostname, pam_strerror(pamh, pam_err));
2352e7a44f2Schristos rexecd_errx(EXIT_FAILURE, "Try again.");
23662136d56Schristos }
23762136d56Schristos if (!pam_ok(pam_authenticate(pamh, pam_flags)) ||
23862136d56Schristos !pam_ok(pam_acct_mgmt(pamh, pam_flags))) {
23962136d56Schristos if (dolog)
24062136d56Schristos syslog(LOG_ERR, "LOGIN REFUSED for %s@%s (%s)", user,
24162136d56Schristos hostname, pam_strerror(pamh, pam_err));
2422e7a44f2Schristos rexecd_errx(EXIT_FAILURE, "Password incorrect.");
24362136d56Schristos }
24462136d56Schristos #else
24561f28255Scgd if (*pwd->pw_passwd != '\0') {
24661f28255Scgd namep = crypt(pass, pwd->pw_passwd);
24762136d56Schristos if (strcmp(namep, pwd->pw_passwd) != 0) {
2487ec31d73Sthorpej if (dolog)
249fc0b820cSmrg syslog(LOG_ERR, "incorrect password for %s",
250fc0b820cSmrg user);
2512e7a44f2Schristos rexecd_errx(EXIT_FAILURE,
2522e7a44f2Schristos "Password incorrect.");/* XXX: wrong! */
25361f28255Scgd }
254fc0b820cSmrg } else
255fc0b820cSmrg (void)crypt("dummy password", "PA"); /* must always crypt */
25662136d56Schristos #endif
25761f28255Scgd if (chdir(pwd->pw_dir) < 0) {
2587ec31d73Sthorpej if (dolog)
259fc0b820cSmrg syslog(LOG_ERR, "%s does not exist for %s", pwd->pw_dir,
260fc0b820cSmrg user);
2612e7a44f2Schristos rexecd_errx(EXIT_FAILURE, "No remote directory.");
26261f28255Scgd }
26362136d56Schristos
26462136d56Schristos if (dolog)
26562136d56Schristos syslog(LOG_INFO, "login from %s as %s", hostname, user);
2663596f57fSginsbach (void)write(STDERR_FILENO, "\0", 1);
26761f28255Scgd if (port) {
268fc0b820cSmrg if (pipe(pv) < 0 || (pid = fork()) == -1) {
2697ec31d73Sthorpej if (dolog)
270fc0b820cSmrg syslog(LOG_ERR,"pipe or fork failed for %s: %m",
271fc0b820cSmrg user);
2722e7a44f2Schristos rexecd_errx(EXIT_FAILURE, "Try again.");
27361f28255Scgd }
27461f28255Scgd if (pid) {
27562136d56Schristos /* parent */
27662136d56Schristos #ifdef USE_PAM
27762136d56Schristos (void)pam_end(pamh, pam_err);
27862136d56Schristos #endif
2793596f57fSginsbach (void)close(STDIN_FILENO);
2803596f57fSginsbach (void)close(STDOUT_FILENO);
2813596f57fSginsbach (void)close(STDERR_FILENO);
282fc0b820cSmrg (void)close(pv[1]);
283fc0b820cSmrg fds[0].fd = s;
284fc0b820cSmrg fds[1].fd = pv[0];
285fc0b820cSmrg fds[0].events = fds[1].events = POLLIN;
286fc0b820cSmrg if (ioctl(pv[1], FIONBIO, (char *)&one) < 0)
28714dbdf55Swiz _exit(1);
28861f28255Scgd /* should set s nbio! */
28961f28255Scgd do {
290fc0b820cSmrg if (poll(fds, 2, 0) < 0) {
2912e7a44f2Schristos (void)close(s);
2922e7a44f2Schristos (void)close(pv[0]);
29314dbdf55Swiz _exit(1);
294fc0b820cSmrg }
295fc0b820cSmrg if (fds[0].revents & POLLIN) {
29661f28255Scgd if (read(s, &sig, 1) <= 0)
297fc0b820cSmrg fds[0].events = 0;
29861f28255Scgd else
2992e7a44f2Schristos (void)killpg(pid, sig);
30061f28255Scgd }
301fc0b820cSmrg if (fds[1].revents & POLLIN) {
30261f28255Scgd cc = read(pv[0], buf, sizeof (buf));
30361f28255Scgd if (cc <= 0) {
3042e7a44f2Schristos (void)shutdown(s, SHUT_RDWR);
305fc0b820cSmrg fds[1].events = 0;
30661f28255Scgd } else
30761f28255Scgd (void)write(s, buf, cc);
30861f28255Scgd }
309fc0b820cSmrg } while ((fds[0].events | fds[1].events) & POLLIN);
310fc0b820cSmrg _exit(0);
31161f28255Scgd }
31262136d56Schristos /* child */
313fc0b820cSmrg (void)close(s);
314fc0b820cSmrg (void)close(pv[0]);
31562136d56Schristos if (dup2(pv[1], STDERR_FILENO) < 0) {
3167ec31d73Sthorpej if (dolog)
317fc0b820cSmrg syslog(LOG_ERR, "dup2 failed for %s", user);
3182e7a44f2Schristos rexecd_errx(EXIT_FAILURE, "Try again.");
319fc0b820cSmrg }
32061f28255Scgd }
32161f28255Scgd if (*pwd->pw_shell == '\0')
32262136d56Schristos pwd->pw_shell = __UNCONST(_PATH_BSHELL);
32341880acaSdsl if (setsid() < 0 ||
32441880acaSdsl setlogin(pwd->pw_name) < 0 ||
325fc0b820cSmrg initgroups(pwd->pw_name, pwd->pw_gid) < 0 ||
32662136d56Schristos #ifdef USE_PAM
32762136d56Schristos setgid((gid_t)pwd->pw_gid) < 0) {
32862136d56Schristos #else
329fc0b820cSmrg setgid((gid_t)pwd->pw_gid) < 0 ||
330fc0b820cSmrg setuid((uid_t)pwd->pw_uid) < 0) {
33162136d56Schristos #endif
3322e7a44f2Schristos rexecd_errx(EXIT_FAILURE, "Try again.");
3337ec31d73Sthorpej if (dolog)
334fc0b820cSmrg syslog(LOG_ERR, "could not set permissions for %s: %m",
335fc0b820cSmrg user);
3362e7a44f2Schristos exit(EXIT_FAILURE);
337fc0b820cSmrg }
33862136d56Schristos #ifdef USE_PAM
33962136d56Schristos if (!pam_ok(pam_setcred(pamh, PAM_ESTABLISH_CRED)))
34062136d56Schristos syslog(LOG_ERR, "pam_setcred() failed: %s",
34162136d56Schristos pam_strerror(pamh, pam_err));
34262136d56Schristos (void)pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
34362136d56Schristos (void)pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
34462136d56Schristos (void)pam_setenv(pamh, "USER", pwd->pw_name, 1);
34562136d56Schristos (void)pam_setenv(pamh, "LOGNAME", pwd->pw_name, 1);
34662136d56Schristos (void)pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
34762136d56Schristos environ = pam_getenvlist(pamh);
34862136d56Schristos (void)pam_end(pamh, pam_err);
34962136d56Schristos if (setuid((uid_t)pwd->pw_uid) < 0) {
35062136d56Schristos if (dolog)
35162136d56Schristos syslog(LOG_ERR, "could not set uid for %s: %m",
35262136d56Schristos user);
3532e7a44f2Schristos rexecd_errx(EXIT_FAILURE, "Try again.");
35462136d56Schristos }
35562136d56Schristos #else
3563e6b7820Sitojun (void)strlcat(path, _PATH_DEFPATH, sizeof(path));
35761f28255Scgd environ = envinit;
35862136d56Schristos (void)strlcat(homedir, pwd->pw_dir, sizeof(homedir));
35962136d56Schristos (void)strlcat(shell, pwd->pw_shell, sizeof(shell));
36062136d56Schristos (void)strlcat(username, pwd->pw_name, sizeof(username));
36162136d56Schristos (void)strlcat(logname, pwd->pw_name, sizeof(logname));
36262136d56Schristos #endif
36362136d56Schristos
364da5c78b3Smrg cp = strrchr(pwd->pw_shell, '/');
36561f28255Scgd if (cp)
36661f28255Scgd cp++;
36761f28255Scgd else
36861f28255Scgd cp = pwd->pw_shell;
3697ec31d73Sthorpej if (dolog)
370fc0b820cSmrg syslog(LOG_INFO, "running command for %s: %s", user, cmdbuf);
3714bc6feceSmrg (void)execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL);
3727ec31d73Sthorpej if (dolog)
373fc0b820cSmrg syslog(LOG_ERR, "execl failed for %s: %m", user);
3742e7a44f2Schristos err(EXIT_FAILURE, "%s", pwd->pw_shell);
37561f28255Scgd }
37661f28255Scgd
377da5c78b3Smrg void
37862136d56Schristos rexecd_errx(int ex, const char *fmt, ...)
37961f28255Scgd {
38061f28255Scgd char buf[BUFSIZ];
381da5c78b3Smrg va_list ap;
38262136d56Schristos ssize_t len;
383da5c78b3Smrg
384da5c78b3Smrg va_start(ap, fmt);
38561f28255Scgd buf[0] = 1;
38662136d56Schristos len = vsnprintf(buf + 1, sizeof(buf) - 1, fmt, ap) + 1;
38762136d56Schristos buf[len++] = '\n';
38862136d56Schristos (void)write(STDERR_FILENO, buf, len);
3894c999163Swiz va_end(ap);
39062136d56Schristos exit(ex);
39161f28255Scgd }
39261f28255Scgd
393da5c78b3Smrg void
39462136d56Schristos getstr(char *buf, int cnt, const char *emsg)
39561f28255Scgd {
39661f28255Scgd char c;
39761f28255Scgd
39861f28255Scgd do {
3993596f57fSginsbach if (read(STDIN_FILENO, &c, 1) != 1)
4002e7a44f2Schristos exit(EXIT_FAILURE);
40161f28255Scgd *buf++ = c;
40262136d56Schristos if (--cnt == 0)
4032e7a44f2Schristos rexecd_errx(EXIT_FAILURE, "%s too long", emsg);
40461f28255Scgd } while (c != 0);
40561f28255Scgd }
40662136d56Schristos
40762136d56Schristos static void
40862136d56Schristos usage(void)
40962136d56Schristos {
41062136d56Schristos (void)fprintf(stderr, "Usage: %s [-l]\n", getprogname());
4112e7a44f2Schristos exit(EXIT_FAILURE);
41262136d56Schristos }
413