1 /* $NetBSD: rexecd.c,v 1.25 2007/12/15 19:44:46 perry Exp $ */
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
35 The Regents of the University of California. All rights reserved.");
37 static char sccsid
[] = "from: @(#)rexecd.c 8.1 (Berkeley) 6/4/93";
39 __RCSID("$NetBSD: rexecd.c,v 1.25 2007/12/15 19:44:46 perry Exp $");
43 #include <sys/ioctl.h>
44 #include <sys/param.h>
45 #include <sys/socket.h>
46 #include <sys/syslog.h>
49 #include <netinet/in.h>
65 #include <security/pam_appl.h>
66 #include <security/openpam.h>
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
;
77 static pam_handle_t
*pamh
;
78 static struct pam_conv pamc
= {
82 static int pam_flags
= PAM_SILENT
|PAM_DISALLOW_NULL_AUTHTOK
;
84 #define pam_ok(err) ((pam_err = (err)) == PAM_SUCCESS)
87 extern char **environ
;
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 };
99 * remote execute server:
106 main(int argc
, char *argv
[])
108 struct sockaddr_storage from
;
112 while ((ch
= getopt(argc
, argv
, "l")) != -1)
116 openlog("rexecd", LOG_PID
, LOG_DAEMON
);
122 fromlen
= sizeof(from
);
123 if (getpeername(STDIN_FILENO
, (struct sockaddr
*)&from
, &fromlen
) < 0)
124 err(EXIT_FAILURE
, "getpeername");
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
));
135 "malformed \"from\" address (v4 mapped, %s)",
140 doit((struct sockaddr
*)&from
);
144 doit(struct sockaddr
*fromp
)
146 struct pollfd fds
[2];
147 char cmdbuf
[NCARGS
+ 1];
149 char user
[16], pass
[16];
150 char buf
[BUFSIZ
], sig
;
151 struct passwd
*pwd
, pwres
;
152 int s
= -1; /* XXX gcc */
156 char hostname
[2 * MAXHOSTNAMELEN
+ 1];
157 char pbuf
[NI_MAXSERV
];
158 const int niflags
= NI_NUMERICHOST
| NI_NUMERICSERV
;
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
);
170 if (getnameinfo(fromp
, fromp
->sa_len
, hostname
, sizeof(hostname
),
171 pbuf
, sizeof(pbuf
), niflags
) != 0) {
173 syslog(LOG_ERR
, "malformed \"from\" address (af %d)",
182 if (read(STDIN_FILENO
, &c
, 1) != 1) {
184 syslog(LOG_ERR
, "initial read failed");
189 port
= port
* 10 + c
- '0';
192 s
= socket(fromp
->sa_family
, SOCK_STREAM
, 0);
195 syslog(LOG_ERR
, "socket: %m");
199 switch (fromp
->sa_family
) {
201 ((struct sockaddr_in
*)fromp
)->sin_port
= htons(port
);
204 ((struct sockaddr_in6
*)fromp
)->sin6_port
= htons(port
);
207 syslog(LOG_ERR
, "unsupported address family");
210 if (connect(s
, (struct sockaddr
*)fromp
, fromp
->sa_len
) < 0) {
212 syslog(LOG_ERR
, "connect: %m");
218 getstr(user
, sizeof(user
), "username");
219 getstr(pass
, sizeof(pass
), "password");
220 getstr(cmdbuf
, sizeof(cmdbuf
), "command");
222 if (getpwnam_r(user
, &pwres
, pwbuf
, sizeof(pwbuf
), &pwd
) != 0 ||
225 syslog(LOG_ERR
, "no such user %s", user
);
226 rexecd_errx(EXIT_FAILURE
, "Login incorrect.");
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
))) {
233 syslog(LOG_ERR
, "PAM ERROR %s@%s (%s)", user
,
234 hostname
, pam_strerror(pamh
, pam_err
));
235 rexecd_errx(EXIT_FAILURE
, "Try again.");
237 if (!pam_ok(pam_authenticate(pamh
, pam_flags
)) ||
238 !pam_ok(pam_acct_mgmt(pamh
, pam_flags
))) {
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.");
245 if (*pwd
->pw_passwd
!= '\0') {
246 namep
= crypt(pass
, pwd
->pw_passwd
);
247 if (strcmp(namep
, pwd
->pw_passwd
) != 0) {
249 syslog(LOG_ERR
, "incorrect password for %s",
251 rexecd_errx(EXIT_FAILURE
,
252 "Password incorrect.");/* XXX: wrong! */
255 (void)crypt("dummy password", "PA"); /* must always crypt */
257 if (chdir(pwd
->pw_dir
) < 0) {
259 syslog(LOG_ERR
, "%s does not exist for %s", pwd
->pw_dir
,
261 rexecd_errx(EXIT_FAILURE
, "No remote directory.");
265 syslog(LOG_INFO
, "login from %s as %s", hostname
, user
);
266 (void)write(STDERR_FILENO
, "\0", 1);
268 if (pipe(pv
) < 0 || (pid
= fork()) == -1) {
270 syslog(LOG_ERR
,"pipe or fork failed for %s: %m",
272 rexecd_errx(EXIT_FAILURE
, "Try again.");
277 (void)pam_end(pamh
, pam_err
);
279 (void)close(STDIN_FILENO
);
280 (void)close(STDOUT_FILENO
);
281 (void)close(STDERR_FILENO
);
285 fds
[0].events
= fds
[1].events
= POLLIN
;
286 if (ioctl(pv
[1], FIONBIO
, (char *)&one
) < 0)
288 /* should set s nbio! */
290 if (poll(fds
, 2, 0) < 0) {
295 if (fds
[0].revents
& POLLIN
) {
296 if (read(s
, &sig
, 1) <= 0)
299 (void)killpg(pid
, sig
);
301 if (fds
[1].revents
& POLLIN
) {
302 cc
= read(pv
[0], buf
, sizeof (buf
));
304 (void)shutdown(s
, SHUT_RDWR
);
307 (void)write(s
, buf
, cc
);
309 } while ((fds
[0].events
| fds
[1].events
) & POLLIN
);
315 if (dup2(pv
[1], STDERR_FILENO
) < 0) {
317 syslog(LOG_ERR
, "dup2 failed for %s", user
);
318 rexecd_errx(EXIT_FAILURE
, "Try again.");
321 if (*pwd
->pw_shell
== '\0')
322 pwd
->pw_shell
= __UNCONST(_PATH_BSHELL
);
324 setlogin(pwd
->pw_name
) < 0 ||
325 initgroups(pwd
->pw_name
, pwd
->pw_gid
) < 0 ||
327 setgid((gid_t
)pwd
->pw_gid
) < 0) {
329 setgid((gid_t
)pwd
->pw_gid
) < 0 ||
330 setuid((uid_t
)pwd
->pw_uid
) < 0) {
332 rexecd_errx(EXIT_FAILURE
, "Try again.");
334 syslog(LOG_ERR
, "could not set permissions for %s: %m",
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) {
351 syslog(LOG_ERR
, "could not set uid for %s: %m",
353 rexecd_errx(EXIT_FAILURE
, "Try again.");
356 (void)strlcat(path
, _PATH_DEFPATH
, sizeof(path
));
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
));
364 cp
= strrchr(pwd
->pw_shell
, '/');
370 syslog(LOG_INFO
, "running command for %s: %s", user
, cmdbuf
);
371 (void)execl(pwd
->pw_shell
, cp
, "-c", cmdbuf
, NULL
);
373 syslog(LOG_ERR
, "execl failed for %s: %m", user
);
374 err(EXIT_FAILURE
, "%s", pwd
->pw_shell
);
378 rexecd_errx(int ex
, const char *fmt
, ...)
386 len
= vsnprintf(buf
+ 1, sizeof(buf
) - 1, fmt
, ap
) + 1;
388 (void)write(STDERR_FILENO
, buf
, len
);
394 getstr(char *buf
, int cnt
, const char *emsg
)
399 if (read(STDIN_FILENO
, &c
, 1) != 1)
403 rexecd_errx(EXIT_FAILURE
, "%s too long", emsg
);
410 (void)fprintf(stderr
, "Usage: %s [-l]\n", getprogname());