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>