2 * Copyright (c) 1988, 1989, 1992, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 2002 Networks Associates Technology, Inc.
7 * Portions of this software were developed for the FreeBSD Project by
8 * ThinkSec AS and NAI Labs, the Security Research Division of Network
9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
10 * ("CBOSS"), as part of the DARPA CHATS research program.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 static const char copyright
[] =
43 "@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\
44 The Regents of the University of California. All rights reserved.\n";
49 static const char sccsid
[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94";
53 #include <sys/cdefs.h>
54 __FBSDID("$FreeBSD$");
57 * remote shell server:
64 #include <sys/param.h>
65 #include <sys/ioctl.h>
67 #include <sys/socket.h>
69 #include <netinet/in_systm.h>
70 #include <netinet/in.h>
71 #include <netinet/ip.h>
72 #include <netinet/tcp.h>
73 #include <arpa/inet.h>
89 #include <login_cap.h>
91 #include <security/pam_appl.h>
92 #include <security/openpam.h>
95 static struct pam_conv pamc
= { openpam_nullconv
, NULL
};
96 static pam_handle_t
*pamh
;
100 if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
101 syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", pam_strerror(pamh, pam_err)); \
102 if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \
103 syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", pam_strerror(pamh, pam_err)); \
104 if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \
105 syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", pam_strerror(pamh, pam_err)); \
109 int log_success
; /* If TRUE, log all successful accesses */
113 void doit(struct sockaddr
*);
114 static void rshd_errx(int, const char *, ...) __printf0like(2, 3);
115 void getstr(char *, int, const char *);
116 int local_domain(char *);
117 char *topdomain(char *);
121 char bshell
[] = _PATH_BSHELL
;
123 #define OPTIONS "aDLln"
126 main(int argc
, char *argv
[])
128 extern int __check_rhosts_file
;
129 struct linger linger
;
132 struct sockaddr_storage from
;
134 openlog("rshd", LOG_PID
| LOG_ODELAY
, LOG_DAEMON
);
137 while ((ch
= getopt(argc
, argv
, OPTIONS
)) != -1)
140 /* ignored for compatibility */
143 __check_rhosts_file
= 0;
163 fromlen
= sizeof (from
);
164 if (getpeername(0, (struct sockaddr
*)&from
, &fromlen
) < 0) {
165 syslog(LOG_ERR
, "getpeername: %m");
169 setsockopt(0, SOL_SOCKET
, SO_KEEPALIVE
, (char *)&on
,
171 syslog(LOG_WARNING
, "setsockopt (SO_KEEPALIVE): %m");
173 linger
.l_linger
= 60; /* XXX */
174 if (setsockopt(0, SOL_SOCKET
, SO_LINGER
, (char *)&linger
,
175 sizeof (linger
)) < 0)
176 syslog(LOG_WARNING
, "setsockopt (SO_LINGER): %m");
178 setsockopt(0, IPPROTO_TCP
, TCP_NODELAY
, &on
, sizeof(on
)) < 0)
179 syslog(LOG_WARNING
, "setsockopt (TCP_NODELAY): %m");
180 doit((struct sockaddr
*)&from
);
185 extern char **environ
;
188 doit(struct sockaddr
*fromp
)
190 extern char *__rcmd_errstr
; /* syslog hook from libc/net/rcmd.c. */
193 fd_set ready
, readfrom
;
194 int cc
, fd
, nfd
, pv
[2], pid
, s
;
196 const char *cp
, *errorstr
;
197 char sig
, buf
[BUFSIZ
];
198 char *cmdbuf
, luser
[16], ruser
[16];
199 char rhost
[2 * MAXHOSTNAMELEN
+ 1];
200 char numericname
[INET6_ADDRSTRLEN
];
205 maxcmdlen
= (int)sysconf(_SC_ARG_MAX
);
206 if (maxcmdlen
<= 0 || (cmdbuf
= malloc(maxcmdlen
)) == NULL
)
209 (void) signal(SIGINT
, SIG_DFL
);
210 (void) signal(SIGQUIT
, SIG_DFL
);
211 (void) signal(SIGTERM
, SIG_DFL
);
212 af
= fromp
->sa_family
;
213 srcport
= ntohs(*((in_port_t
*)&fromp
->sa_data
));
215 inet_ntop(af
, &((struct sockaddr_in
*)fromp
)->sin_addr
,
216 numericname
, sizeof numericname
);
217 } else if (af
== AF_INET6
) {
218 inet_ntop(af
, &((struct sockaddr_in6
*)fromp
)->sin6_addr
,
219 numericname
, sizeof numericname
);
221 syslog(LOG_ERR
, "malformed \"from\" address (af %d)", af
);
226 u_char optbuf
[BUFSIZ
/3];
227 socklen_t optsize
= sizeof(optbuf
), ipproto
, i
;
230 if ((ip
= getprotobyname("ip")) != NULL
)
231 ipproto
= ip
->p_proto
;
233 ipproto
= IPPROTO_IP
;
234 if (!getsockopt(0, ipproto
, IP_OPTIONS
, optbuf
, &optsize
) &&
236 for (i
= 0; i
< optsize
; ) {
237 u_char c
= optbuf
[i
];
238 if (c
== IPOPT_LSRR
|| c
== IPOPT_SSRR
) {
240 "connection refused from %s with IP option %s",
242 c
== IPOPT_LSRR
? "LSRR" : "SSRR");
247 i
+= (c
== IPOPT_NOP
) ? 1 : optbuf
[i
+1];
253 if (srcport
>= IPPORT_RESERVED
||
254 srcport
< IPPORT_RESERVED
/2) {
255 syslog(LOG_NOTICE
|LOG_AUTH
,
256 "connection from %s on illegal port %u",
264 s
= 0; /* not set or used if port == 0 */
267 if ((cc
= read(STDIN_FILENO
, &c
, 1)) != 1) {
269 syslog(LOG_NOTICE
, "read: %m");
270 shutdown(0, SHUT_RDWR
);
275 port
= port
* 10 + c
- '0';
280 int lport
= IPPORT_RESERVED
- 1;
281 s
= rresvport_af(&lport
, af
);
283 syslog(LOG_ERR
, "can't get stderr port: %m");
286 if (port
>= IPPORT_RESERVED
||
287 port
< IPPORT_RESERVED
/2) {
288 syslog(LOG_NOTICE
|LOG_AUTH
,
289 "2nd socket from %s on unreserved port %u",
294 *((in_port_t
*)&fromp
->sa_data
) = htons(port
);
295 if (connect(s
, fromp
, fromp
->sa_len
) < 0) {
296 syslog(LOG_INFO
, "connect second port %d: %m", port
);
302 realhostname_sa(rhost
, sizeof(rhost
) - 1, fromp
, fromp
->sa_len
);
303 rhost
[sizeof(rhost
) - 1] = '\0';
304 /* XXX truncation! */
307 getstr(ruser
, sizeof(ruser
), "ruser");
308 getstr(luser
, sizeof(luser
), "luser");
309 getstr(cmdbuf
, maxcmdlen
, "command");
312 pam_err
= pam_start("rsh", luser
, &pamc
, &pamh
);
313 if (pam_err
!= PAM_SUCCESS
) {
314 syslog(LOG_ERR
|LOG_AUTH
, "pam_start(): %s",
315 pam_strerror(pamh
, pam_err
));
316 rshd_errx(1, "Login incorrect.");
319 if ((pam_err
= pam_set_item(pamh
, PAM_RUSER
, ruser
)) != PAM_SUCCESS
||
320 (pam_err
= pam_set_item(pamh
, PAM_RHOST
, rhost
) != PAM_SUCCESS
)) {
321 syslog(LOG_ERR
|LOG_AUTH
, "pam_set_item(): %s",
322 pam_strerror(pamh
, pam_err
));
323 rshd_errx(1, "Login incorrect.");
326 pam_err
= pam_authenticate(pamh
, 0);
327 if (pam_err
== PAM_SUCCESS
) {
328 if ((pam_err
= pam_get_user(pamh
, &cp
, NULL
)) == PAM_SUCCESS
) {
329 strncpy(luser
, cp
, sizeof(luser
));
330 luser
[sizeof(luser
) - 1] = '\0';
331 /* XXX truncation! */
333 pam_err
= pam_acct_mgmt(pamh
, 0);
335 if (pam_err
!= PAM_SUCCESS
) {
336 syslog(LOG_INFO
|LOG_AUTH
,
337 "%s@%s as %s: permission denied (%s). cmd='%.80s'",
338 ruser
, rhost
, luser
, pam_strerror(pamh
, pam_err
), cmdbuf
);
339 rshd_errx(1, "Login incorrect.");
343 pwd
= getpwnam(luser
);
345 syslog(LOG_INFO
|LOG_AUTH
,
346 "%s@%s as %s: unknown login. cmd='%.80s'",
347 ruser
, rhost
, luser
, cmdbuf
);
348 if (errorstr
== NULL
)
349 errorstr
= "Login incorrect.";
350 rshd_errx(1, errorstr
, rhost
);
353 lc
= login_getpwclass(pwd
);
355 auth_checknologin(lc
);
357 if (chdir(pwd
->pw_dir
) < 0) {
358 if (chdir("/") < 0 ||
359 login_getcapbool(lc
, "requirehome", !!pwd
->pw_uid
)) {
360 syslog(LOG_INFO
|LOG_AUTH
,
361 "%s@%s as %s: no home directory. cmd='%.80s'",
362 ruser
, rhost
, luser
, cmdbuf
);
363 rshd_errx(0, "No remote home directory.");
368 if (lc
!= NULL
&& fromp
->sa_family
== AF_INET
) { /*XXX*/
369 char remote_ip
[MAXHOSTNAMELEN
];
371 strncpy(remote_ip
, numericname
,
372 sizeof(remote_ip
) - 1);
373 remote_ip
[sizeof(remote_ip
) - 1] = 0;
374 /* XXX truncation! */
375 if (!auth_hostok(lc
, rhost
, remote_ip
)) {
376 syslog(LOG_INFO
|LOG_AUTH
,
377 "%s@%s as %s: permission denied (%s). cmd='%.80s'",
378 ruser
, rhost
, luser
, __rcmd_errstr
,
380 rshd_errx(1, "Login incorrect.");
382 if (!auth_timeok(lc
, time(NULL
)))
383 rshd_errx(1, "Logins not available right now");
387 * PAM modules might add supplementary groups in
388 * pam_setcred(), so initialize them first.
389 * But we need to open the session as root.
391 if (setusercontext(lc
, pwd
, pwd
->pw_uid
, LOGIN_SETGROUP
) != 0) {
392 syslog(LOG_ERR
, "setusercontext: %m");
396 if ((pam_err
= pam_open_session(pamh
, 0)) != PAM_SUCCESS
) {
397 syslog(LOG_ERR
, "pam_open_session: %s", pam_strerror(pamh
, pam_err
));
398 } else if ((pam_err
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
)) != PAM_SUCCESS
) {
399 syslog(LOG_ERR
, "pam_setcred: %s", pam_strerror(pamh
, pam_err
));
402 (void) write(STDERR_FILENO
, "\0", 1);
407 rshd_errx(1, "Can't make pipe.");
410 rshd_errx(1, "Can't fork; try again.");
418 FD_SET(s
, &readfrom
);
419 FD_SET(pv
[0], &readfrom
);
424 ioctl(pv
[0], FIONBIO
, (char *)&one
);
426 /* should set s nbio! */
430 if (select(nfd
, &ready
, (fd_set
*)0,
431 (fd_set
*)0, (struct timeval
*)0) < 0)
433 if (FD_ISSET(s
, &ready
)) {
435 ret
= read(s
, &sig
, 1);
437 FD_CLR(s
, &readfrom
);
441 if (FD_ISSET(pv
[0], &ready
)) {
443 cc
= read(pv
[0], buf
, sizeof(buf
));
445 shutdown(s
, SHUT_RDWR
);
446 FD_CLR(pv
[0], &readfrom
);
448 (void)write(s
, buf
, cc
);
452 } while (FD_ISSET(s
, &readfrom
) ||
453 FD_ISSET(pv
[0], &readfrom
));
465 rshd_errx(1, "Can't fork; try again.");
468 while (wait(NULL
) > 0 || errno
== EINTR
)
475 for (fd
= getdtablesize(); fd
> 2; fd
--)
478 syslog(LOG_ERR
, "setsid() failed: %m");
479 if (setlogin(pwd
->pw_name
) < 0)
480 syslog(LOG_ERR
, "setlogin() failed: %m");
482 if (*pwd
->pw_shell
== '\0')
483 pwd
->pw_shell
= bshell
;
484 (void) pam_setenv(pamh
, "HOME", pwd
->pw_dir
, 1);
485 (void) pam_setenv(pamh
, "SHELL", pwd
->pw_shell
, 1);
486 (void) pam_setenv(pamh
, "USER", pwd
->pw_name
, 1);
487 (void) pam_setenv(pamh
, "PATH", _PATH_DEFPATH
, 1);
488 environ
= pam_getenvlist(pamh
);
489 (void) pam_end(pamh
, pam_err
);
490 cp
= strrchr(pwd
->pw_shell
, '/');
496 if (setusercontext(lc
, pwd
, pwd
->pw_uid
,
497 LOGIN_SETALL
& ~LOGIN_SETGROUP
) < 0) {
498 syslog(LOG_ERR
, "setusercontext(): %m");
503 if (log_success
|| pwd
->pw_uid
== 0) {
504 syslog(LOG_INFO
|LOG_AUTH
, "%s@%s as %s: cmd='%.80s'",
505 ruser
, rhost
, luser
, cmdbuf
);
507 execl(pwd
->pw_shell
, cp
, "-c", cmdbuf
, (char *)NULL
);
508 err(1, "%s", pwd
->pw_shell
);
513 * Report error to client. Note: can't be used until second socket has
514 * connected to client, or older clients will hang waiting for that
519 rshd_errx(int errcode
, const char *fmt
, ...)
526 write(STDERR_FILENO
, "\1", 1);
528 verrx(errcode
, fmt
, ap
);
533 getstr(char *buf
, int cnt
, const char *error
)
538 if (read(STDIN_FILENO
, &c
, 1) != 1)
542 rshd_errx(1, "%s too long", error
);
547 * Check whether host h is in our local domain,
548 * defined as sharing the last two components of the domain part,
549 * or the entire domain part if the local domain has only one component.
550 * If either name is unqualified (contains no '.'),
551 * assume that the host is local, as it will be
552 * interpreted as such.
555 local_domain(char *h
)
557 char localhost
[MAXHOSTNAMELEN
];
561 (void) gethostname(localhost
, sizeof(localhost
) - 1);
562 localhost
[sizeof(localhost
) - 1] = '\0';
563 /* XXX truncation! */
564 p1
= topdomain(localhost
);
566 if (p1
== NULL
|| p2
== NULL
|| !strcasecmp(p1
, p2
))
574 char *p
, *maybe
= NULL
;
577 for (p
= h
+ strlen(h
); p
>= h
; p
--) {
591 syslog(LOG_ERR
, "usage: rshd [-%s]", OPTIONS
);