1 /*        $NetBSD: rexecd.c,v 1.26 2008/07/20 01:09:07 lukem Exp $    */
2 
3 /*
4  * Copyright (c) 1983, 1993
5  *        The Regents of the University of California.  All rights reserved.
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.
15  * 3. Neither the name of the University nor the names of its contributors
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 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #if 0
37 static char sccsid[] = "from: @(#)rexecd.c        8.1 (Berkeley) 6/4/93";
38 #else
39 __RCSID("$NetBSD: rexecd.c,v 1.26 2008/07/20 01:09:07 lukem Exp $");
40 #endif
41 #endif /* not lint */
42 
43 #include <sys/ioctl.h>
44 #include <sys/param.h>
45 #include <sys/socket.h>
46 #include <sys/syslog.h>
47 #include <sys/time.h>
48 
49 #include <netinet/in.h>
50 
51 #include <err.h>
52 #include <errno.h>
53 #include <netdb.h>
54 #include <paths.h>
55 #include <poll.h>
56 #include <pwd.h>
57 #include <signal.h>
58 #include <stdarg.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 
64 #ifdef USE_PAM
65 #include <security/pam_appl.h>
66 #include <security/openpam.h>
67 #endif
68 
69 int main(int, char *[]);
70 static void rexecd_errx(int, const char *, ...)
71      __attribute__((__noreturn__, __format__(__printf__, 2, 3)));
72 static void doit(struct sockaddr *) __dead;
73 static void getstr(char *, int, const char *);
74 static void usage(void) __dead;
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
86 
87 extern char         **environ;
88 static int          dolog;
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
97 
98 /*
99  * remote execute server:
100  *        username\0
101  *        password\0
102  *        command\0
103  *        data
104  */
105 int
main(int argc,char * argv[])106 main(int argc, char *argv[])
107 {
108           struct sockaddr_storage from;
109           socklen_t fromlen;
110           int ch;
111 
112           while ((ch = getopt(argc, argv, "l")) != -1)
113                     switch (ch) {
114                     case 'l':
115                               dolog = 1;
116                               openlog("rexecd", LOG_PID, LOG_DAEMON);
117                               break;
118                     default:
119                               usage();
120                     }
121 
122           fromlen = sizeof(from);
123           if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0)
124                     err(EXIT_FAILURE, "getpeername");
125 
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 EXIT_FAILURE;
138           }
139 
140           doit((struct sockaddr *)&from);
141 }
142 
143 void
doit(struct sockaddr * fromp)144 doit(struct sockaddr *fromp)
145 {
146           struct pollfd fds[2];
147           char cmdbuf[NCARGS + 1];
148           const char *cp;
149           char user[16], pass[16];
150           char buf[BUFSIZ], sig;
151           struct passwd *pwd, pwres;
152           int s = -1; /* XXX gcc */
153           int pv[2], pid, cc;
154           int one = 1;
155           in_port_t port;
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
162           char pwbuf[1024];
163 
164           (void)signal(SIGINT, SIG_DFL);
165           (void)signal(SIGQUIT, SIG_DFL);
166           (void)signal(SIGTERM, SIG_DFL);
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);
175                     exit(EXIT_FAILURE);
176           }
177 
178           (void)alarm(60);
179           port = 0;
180           for (;;) {
181                     char c;
182                     if (read(STDIN_FILENO, &c, 1) != 1) {
183                               if (dolog)
184                                         syslog(LOG_ERR, "initial read failed");
185                               exit(EXIT_FAILURE);
186                     }
187                     if (c == 0)
188                               break;
189                     port = port * 10 + c - '0';
190           }
191           if (port != 0) {
192                     s = socket(fromp->sa_family, SOCK_STREAM, 0);
193                     if (s < 0) {
194                               if (dolog)
195                                         syslog(LOG_ERR, "socket: %m");
196                               exit(EXIT_FAILURE);
197                     }
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");
208                               exit(EXIT_FAILURE);
209                     }
210                     if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 0) {
211                               if (dolog)
212                                         syslog(LOG_ERR, "connect: %m");
213                               exit(EXIT_FAILURE);
214                     }
215                     (void)alarm(0);
216           }
217           (void)alarm(60);
218           getstr(user, sizeof(user), "username");
219           getstr(pass, sizeof(pass), "password");
220           getstr(cmdbuf, sizeof(cmdbuf), "command");
221           (void)alarm(0);
222           if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
223               pwd == NULL) {
224                     if (dolog)
225                               syslog(LOG_ERR, "no such user %s", user);
226                     rexecd_errx(EXIT_FAILURE, "Login incorrect.");
227           }
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));
235                     rexecd_errx(EXIT_FAILURE, "Try again.");
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));
242                     rexecd_errx(EXIT_FAILURE, "Password incorrect.");
243           }
244 #else
245           if (*pwd->pw_passwd != '\0') {
246                     namep = crypt(pass, pwd->pw_passwd);
247                     if (strcmp(namep, pwd->pw_passwd) != 0) {
248                               if (dolog)
249                                         syslog(LOG_ERR, "incorrect password for %s",
250                                             user);
251                               rexecd_errx(EXIT_FAILURE,
252                                             "Password incorrect.");/* XXX: wrong! */
253                     }
254           } else
255                     (void)crypt("dummy password", "PA");    /* must always crypt */
256 #endif
257           if (chdir(pwd->pw_dir) < 0) {
258                     if (dolog)
259                               syslog(LOG_ERR, "%s does not exist for %s", pwd->pw_dir,
260                                      user);
261                     rexecd_errx(EXIT_FAILURE, "No remote directory.");
262           }
263 
264           if (dolog)
265                     syslog(LOG_INFO, "login from %s as %s", hostname, user);
266           (void)write(STDERR_FILENO, "\0", 1);
267           if (port) {
268                     if (pipe(pv) < 0 || (pid = fork()) == -1) {
269                               if (dolog)
270                                         syslog(LOG_ERR,"pipe or fork failed for %s: %m",
271                                             user);
272                               rexecd_errx(EXIT_FAILURE, "Try again.");
273                     }
274                     if (pid) {
275                               /* parent */
276 #ifdef USE_PAM
277                               (void)pam_end(pamh, pam_err);
278 #endif
279                               (void)close(STDIN_FILENO);
280                               (void)close(STDOUT_FILENO);
281                               (void)close(STDERR_FILENO);
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)
287                                         _exit(1);
288                               /* should set s nbio! */
289                               do {
290                                         if (poll(fds, 2, 0) < 0) {
291                                                   (void)close(s);
292                                                   (void)close(pv[0]);
293                                                   _exit(1);
294                                         }
295                                         if (fds[0].revents & POLLIN) {
296                                                   if (read(s, &sig, 1) <= 0)
297                                                             fds[0].events = 0;
298                                                   else
299                                                             (void)killpg(pid, sig);
300                                         }
301                                         if (fds[1].revents & POLLIN) {
302                                                   cc = read(pv[0], buf, sizeof (buf));
303                                                   if (cc <= 0) {
304                                                             (void)shutdown(s, SHUT_RDWR);
305                                                             fds[1].events = 0;
306                                                   } else
307                                                             (void)write(s, buf, cc);
308                                         }
309                               } while ((fds[0].events | fds[1].events) & POLLIN);
310                               _exit(0);
311                     }
312                     /* child */
313                     (void)close(s);
314                     (void)close(pv[0]);
315                     if (dup2(pv[1], STDERR_FILENO) < 0) {
316                               if (dolog)
317                                         syslog(LOG_ERR, "dup2 failed for %s", user);
318                               rexecd_errx(EXIT_FAILURE, "Try again.");
319                     }
320           }
321           if (*pwd->pw_shell == '\0')
322                     pwd->pw_shell = __UNCONST(_PATH_BSHELL);
323           if (setsid() < 0 ||
324               setlogin(pwd->pw_name) < 0 ||
325               initgroups(pwd->pw_name, pwd->pw_gid) < 0 ||
326 #ifdef USE_PAM
327               setgid((gid_t)pwd->pw_gid) < 0) {
328 #else
329               setgid((gid_t)pwd->pw_gid) < 0 ||
330               setuid((uid_t)pwd->pw_uid) < 0) {
331 #endif
332                     rexecd_errx(EXIT_FAILURE, "Try again.");
333                     if (dolog)
334                               syslog(LOG_ERR, "could not set permissions for %s: %m",
335                                   user);
336                     exit(EXIT_FAILURE);
337           }
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);
353                 rexecd_errx(EXIT_FAILURE, "Try again.");
354         }
355 #else
356           (void)strlcat(path, _PATH_DEFPATH, sizeof(path));
357           environ = envinit;
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 
364           cp = strrchr(pwd->pw_shell, '/');
365           if (cp)
366                     cp++;
367           else
368                     cp = pwd->pw_shell;
369           if (dolog)
370                     syslog(LOG_INFO, "running command for %s: %s", user, cmdbuf);
371           (void)execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL);
372           if (dolog)
373                     syslog(LOG_ERR, "execl failed for %s: %m", user);
374           err(EXIT_FAILURE, "%s", pwd->pw_shell);
375 }
376 
377 void
378 rexecd_errx(int ex, const char *fmt, ...)
379 {
380           char buf[BUFSIZ];
381           va_list ap;
382           ssize_t len;
383 
384           va_start(ap, fmt);
385           buf[0] = 1;
386           len = vsnprintf(buf + 1, sizeof(buf) - 1, fmt, ap) + 1;
387           buf[len++] = '\n';
388           (void)write(STDERR_FILENO, buf, len);
389           va_end(ap);
390           exit(ex);
391 }
392 
393 void
394 getstr(char *buf, int cnt, const char *emsg)
395 {
396           char c;
397 
398           do {
399                     if (read(STDIN_FILENO, &c, 1) != 1)
400                               exit(EXIT_FAILURE);
401                     *buf++ = c;
402                     if (--cnt == 0)
403                               rexecd_errx(EXIT_FAILURE, "%s too long", emsg);
404           } while (c != 0);
405 }
406 
407 static void
408 usage(void)
409 {
410           (void)fprintf(stderr, "Usage: %s [-l]\n", getprogname());
411           exit(EXIT_FAILURE);
412 }
413