]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/rexecd/rexecd.c
Use sysconf(_SC_ARG_MAX) instead of NCARGS.
[FreeBSD/FreeBSD.git] / libexec / rexecd / rexecd.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1983, 1993\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)rexecd.c    8.1 (Berkeley) 6/4/93";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD$";
46 #endif /* not lint */
47
48 #include <sys/param.h>
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
51
52 #include <netinet/in.h>
53
54 #include <err.h>
55 #include <errno.h>
56 #include <libutil.h>
57 #include <paths.h>
58 #include <pwd.h>
59 #include <signal.h>
60 #include <stdarg.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <syslog.h>
65 #include <unistd.h>
66
67 #include <security/pam_appl.h>
68 #include <security/openpam.h>
69
70 static pam_handle_t *pamh;
71 static struct pam_conv pamc = {
72         openpam_nullconv,
73         NULL
74 };
75 static int pam_flags = PAM_SILENT|PAM_DISALLOW_NULL_AUTHTOK;
76 static int pam_err;
77 #define pam_ok(err) ((pam_err = (err)) == PAM_SUCCESS)
78
79 char    **environ;
80 char    remote[MAXHOSTNAMELEN];
81
82 struct  sockaddr_storage sa;
83
84 char    default_shell[] = _PATH_BSHELL;
85
86 static void doit(struct sockaddr *);
87 static void getstr(char *, int, const char *);
88 static void error(const char *fmt, ...);
89
90 int no_uid_0 = 1;
91
92 /*
93  * remote execute server:
94  *      username\0
95  *      password\0
96  *      command\0
97  *      data
98  */
99 /*ARGSUSED*/
100 int
101 main(int argc, char *argv[])
102 {
103         struct sockaddr_storage from;
104         socklen_t fromlen;
105         int ch;
106
107         openlog("rexecd", LOG_PID, LOG_AUTH);
108
109         while ((ch = getopt(argc, argv, "i")) != -1)
110                 switch (ch) {
111                 case 'i':
112                         no_uid_0 = 0;
113                         break;
114                 default:
115                         syslog(LOG_ERR, "usage: rexecd [-i]");
116                         exit(1);
117                 }
118         argc -= optind;
119         argv += optind;
120
121         fromlen = sizeof (from);
122         if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0)
123                 err(1, "getpeername");
124
125         realhostname_sa(remote, sizeof(remote) - 1,
126                         (struct sockaddr *)&from, fromlen);
127
128         doit((struct sockaddr *)&from);
129         return(0);
130 }
131
132 static void
133 doit(struct sockaddr *fromp)
134 {
135         char *cmdbuf, *cp;
136         int maxcmdlen;
137         char user[16], pass[16];
138         struct passwd *pwd;
139         int fd, r, sd;
140         u_short port;
141         int pv[2], pid, cc, nfds;
142         fd_set rfds, fds;
143         char buf[BUFSIZ], sig;
144         int one = 1;
145
146         maxcmdlen = (int)sysconf(_SC_ARG_MAX);
147         if (maxcmdlen <= 0 || (cmdbuf = malloc(maxcmdlen)) == NULL)
148                 exit(1);
149
150         (void) signal(SIGINT, SIG_DFL);
151         (void) signal(SIGQUIT, SIG_DFL);
152         (void) signal(SIGTERM, SIG_DFL);
153         dup2(STDIN_FILENO, STDOUT_FILENO);
154         dup2(STDIN_FILENO, STDOUT_FILENO);
155         (void) alarm(60);
156         port = 0;
157         sd = -1;
158         for (;;) {
159                 char c;
160                 if (read(STDIN_FILENO, &c, 1) != 1)
161                         exit(1);
162                 if (c == 0)
163                         break;
164                 port = port * 10 + c - '0';
165         }
166         if (port != 0) {
167                 sd = socket(fromp->sa_family, SOCK_STREAM, 0);
168                 if (sd < 0)
169                         exit(1);
170                 bzero(&sa, sizeof(sa));
171                 sa.ss_family = fromp->sa_family;
172                 sa.ss_len = fromp->sa_len;
173                 if (bind(sd, (struct sockaddr *)&sa, sa.ss_len) < 0)
174                         exit(1);
175                 switch (fromp->sa_family) {
176                 case AF_INET:
177                         ((struct sockaddr_in *)(void *)fromp)->sin_port = htons(port);
178                         break;
179                 case AF_INET6:
180                         ((struct sockaddr_in6 *)(void *)fromp)->sin6_port = htons(port);
181                         break;
182                 default:
183                         exit(1);
184                 }
185                 if (connect(sd, fromp, fromp->sa_len) < 0)
186                         exit(1);
187         }
188         getstr(user, sizeof(user), "username");
189         getstr(pass, sizeof(pass), "password");
190         getstr(cmdbuf, maxcmdlen, "command");
191         (void) alarm(0);
192
193         if ((pwd = getpwnam(user))  == NULL || (pwd->pw_uid = 0 && no_uid_0) ||
194             !pam_ok(pam_start("rexecd", user, &pamc, &pamh)) ||
195             !pam_ok(pam_set_item(pamh, PAM_RHOST, remote)) ||
196             !pam_ok(pam_set_item(pamh, PAM_AUTHTOK, pass)) ||
197             !pam_ok(pam_authenticate(pamh, pam_flags)) ||
198             !pam_ok(pam_acct_mgmt(pamh, pam_flags))) {
199                 syslog(LOG_ERR, "%s LOGIN REFUSED from %s", user, remote);
200                 error("Login incorrect.\n");
201                 exit(1);
202         }
203
204         syslog(LOG_INFO, "login from %s as %s", remote, user);
205
206         (void) write(STDERR_FILENO, "\0", 1);
207         if (port != 0) {
208                 (void) pipe(pv);
209
210                 pid = fork();
211                 if (pid == -1)  {
212                         error("Try again.\n");
213                         exit(1);
214                 }
215                 if (pid) {
216                         /* parent */
217                         (void) pam_end(pamh, pam_err);
218                         (void) close(STDIN_FILENO);
219                         (void) close(STDOUT_FILENO);
220                         (void) close(STDERR_FILENO);
221                         (void) close(pv[1]);
222                         ioctl(pv[0], FIONBIO, (char *)&one);
223                         /* should set sd nbio! */
224                         FD_ZERO(&fds);
225                         FD_SET(sd, &fds);
226                         nfds = sd + 1;
227                         FD_SET(pv[0], &fds);
228                         if (pv[0] >= nfds)
229                                 nfds = pv[0] + 1;
230                         do {
231                                 rfds = fds;
232                                 for (;;) {
233                                         r = select(nfds, &rfds, NULL, NULL, NULL);
234                                         if (r > 0)
235                                                 break;
236                                         if (r < 0 && errno != EINTR)
237                                                 exit(0);
238                                 }
239                                 if (FD_ISSET(sd, &rfds)) {
240                                         if (read(sd, &sig, 1) <= 0)
241                                                 FD_CLR(sd, &fds);
242                                         else
243                                                 killpg(pid, sig);
244                                 }
245                                 if (FD_ISSET(pv[0], &fds)) {
246                                         cc = read(pv[0], buf, sizeof (buf));
247                                         if (cc <= 0) {
248                                                 shutdown(sd, SHUT_RDWR);
249                                                 FD_CLR(pv[0], &fds);
250                                         } else {
251                                                 (void) write(sd, buf, cc);
252                                         }
253                                 }
254                         } while (FD_ISSET(sd, &fds) || FD_ISSET(pv[0], &fds));
255                         exit(0);
256                 }
257                 /* child */
258                 (void) close(sd);
259                 (void) close(pv[0]);
260                 dup2(pv[1], 2);
261         }
262         for (fd = getdtablesize(); fd > 2; fd--)
263                 (void) close(fd);
264         if (*pwd->pw_shell == '\0')
265                 pwd->pw_shell = default_shell;
266         if (setsid() == -1)
267                 syslog(LOG_ERR, "setsid() failed: %m");
268         if (setlogin(pwd->pw_name) < 0)
269                 syslog(LOG_ERR, "setlogin() failed: %m");
270         (void) setgid((gid_t)pwd->pw_gid);
271         initgroups(pwd->pw_name, pwd->pw_gid);
272         if (!pam_ok(pam_setcred(pamh, PAM_ESTABLISH_CRED)))
273                 syslog(LOG_ERR, "pam_setcred() failed: %s",
274                     pam_strerror(pamh, pam_err));
275         (void) pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
276         (void) pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
277         (void) pam_setenv(pamh, "USER", pwd->pw_name, 1);
278         (void) pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
279         environ = pam_getenvlist(pamh);
280         (void) pam_end(pamh, pam_err);
281         (void) setuid((uid_t)pwd->pw_uid);
282         cp = strrchr(pwd->pw_shell, '/');
283         if (cp)
284                 cp++;
285         else
286                 cp = pwd->pw_shell;
287         if (chdir(pwd->pw_dir) < 0) {
288                 error("No remote directory.\n");
289                 exit(1);
290         }
291         execl(pwd->pw_shell, cp, "-c", cmdbuf, (char *)0);
292         err(1, "%s", pwd->pw_shell);
293 }
294
295 static void
296 error(const char *fmt, ...)
297 {
298         char buf[BUFSIZ];
299         va_list ap;
300
301         va_start(ap, fmt);
302         buf[0] = 1;
303         (void)vsnprintf(buf + 1, sizeof(buf) - 1, fmt, ap);
304         (void)write(STDERR_FILENO, buf, strlen(buf));
305         va_end(ap);
306 }
307
308 static void
309 getstr(char *buf, int cnt, const char *field)
310 {
311         char c;
312
313         do {
314                 if (read(STDIN_FILENO, &c, 1) != 1)
315                         exit(1);
316                 *buf++ = c;
317                 if (--cnt == 0) {
318                         error("%s too long\n", field);
319                         exit(1);
320                 }
321         } while (c != 0);
322 }