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>