1 /* $NetBSD: rlogind.c,v 1.38 2008/07/20 01:09:07 lukem Exp $ */
4 * Copyright (C) 1998 WIDE Project.
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. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by WIDE Project and
19 * 4. Neither the name of the project nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * Copyright (c) 1983, 1988, 1989, 1993
38 * The Regents of the University of California. All rights reserved.
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 3. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 #include <sys/cdefs.h>
67 __COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1989, 1993\
68 The Regents of the University of California. All rights reserved.");
70 static char sccsid
[] = "@(#)rlogind.c 8.2 (Berkeley) 4/28/95";
72 __RCSID("$NetBSD: rlogind.c,v 1.38 2008/07/20 01:09:07 lukem Exp $");
77 * remote login server:
81 * terminal_type/speed\0
85 #include <sys/param.h>
87 #include <sys/ioctl.h>
93 #include <sys/socket.h>
94 #include <netinet/in.h>
95 #include <netinet/in_systm.h>
96 #include <netinet/ip.h>
97 #include <arpa/inet.h>
108 #include "pathnames.h"
110 #ifndef TIOCPKT_WINDOW
111 #define TIOCPKT_WINDOW 0x80
114 #define OPTIONS "alnL"
118 char lusername
[NMAX
+1], rusername
[NMAX
+1];
119 static char term
[64] = "TERM=";
120 #define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */
127 void doit
__P((int, struct sockaddr_storage
*));
128 int control
__P((int, char *, int));
129 void protocol
__P((int, int));
130 void cleanup
__P((int));
131 void fatal
__P((int, const char *, int));
132 int do_rlogin
__P((struct sockaddr
*, char *));
133 void getstr
__P((char *, int, const char *));
134 void setup_term
__P((int));
136 int do_krb_login
__P((union sockunion
*));
138 void usage
__P((void));
139 int local_domain
__P((char *));
140 char *topdomain
__P((char *));
141 int main
__P((int, char *[]));
143 extern int __check_rhosts_file
;
144 extern char *__rcmd_errstr
; /* syslog hook from libc/net/rcmd.c */
145 extern char **environ
;
152 struct sockaddr_storage from
;
154 socklen_t fromlen
= sizeof(from
);
156 openlog("rlogind", LOG_PID
, LOG_AUTH
);
159 while ((ch
= getopt(argc
, argv
, OPTIONS
)) != -1)
165 __check_rhosts_file
= 0;
181 if (getpeername(0, (struct sockaddr
*)&from
, &fromlen
) < 0) {
182 syslog(LOG_ERR
,"Can't get peer name of remote host: %m");
183 fatal(STDERR_FILENO
, "Can't get peer name of remote host", 1);
186 if (from
.ss_family
== AF_INET6
&&
187 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6
*)&from
)->sin6_addr
) &&
188 sizeof(struct sockaddr_in
) <= sizeof(from
)) {
189 struct sockaddr_in sin4
;
190 struct sockaddr_in6
*sin6
;
191 const int off
= sizeof(struct sockaddr_in6
) -
192 sizeof(struct sockaddr_in
);
194 sin6
= (struct sockaddr_in6
*)&from
;
195 memset(&sin4
, 0, sizeof(sin4
));
196 sin4
.sin_family
= AF_INET
;
197 sin4
.sin_len
= sizeof(struct sockaddr_in
);
198 memcpy(&sin4
.sin_addr
, &sin6
->sin6_addr
.s6_addr
[off
],
199 sizeof(sin4
.sin_addr
));
200 memcpy(&from
, &sin4
, sizeof(sin4
));
203 if (from
.ss_family
== AF_INET6
&&
204 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6
*)&from
)->sin6_addr
)) {
205 char hbuf
[NI_MAXHOST
];
206 if (getnameinfo((struct sockaddr
*)&from
, fromlen
, hbuf
,
207 sizeof(hbuf
), NULL
, 0, NI_NUMERICHOST
) != 0) {
208 strlcpy(hbuf
, "invalid", sizeof(hbuf
));
210 syslog(LOG_ERR
, "malformed \"from\" address (v4 mapped, %s)\n",
217 setsockopt(0, SOL_SOCKET
, SO_KEEPALIVE
, &on
, sizeof (on
)) < 0)
218 syslog(LOG_WARNING
, "setsockopt (SO_KEEPALIVE): %m");
220 if (from
.ss_family
== AF_INET
) {
222 if (setsockopt(0, IPPROTO_IP
, IP_TOS
, (char *)&on
, sizeof(int)) < 0)
223 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
235 char line
[MAXPATHLEN
];
238 struct winsize win
= { 0, 0, 0, 0 };
244 struct sockaddr_storage
*fromp
;
246 int master
, pid
, on
= 1;
247 int authenticated
= 0;
249 char hostnamebuf
[2 * MAXHOSTNAMELEN
+ 1];
250 char hostaddrbuf
[sizeof(*fromp
) * 4 + 1];
252 char naddr
[NI_MAXHOST
];
253 char saddr
[NI_MAXHOST
];
254 char raddr
[NI_MAXHOST
];
255 int af
= fromp
->ss_family
;
257 struct addrinfo hints
, *res
, *res0
;
259 socklen_t fromlen
= fromp
->ss_len
> sizeof(*fromp
)
260 ? sizeof(*fromp
) : fromp
->ss_len
;
261 const int niflags
= NI_NUMERICHOST
| NI_NUMERICSERV
;
272 portp
= &((struct sockaddr_in
*)fromp
)->sin_port
;
276 portp
= &((struct sockaddr_in6
*)fromp
)->sin6_port
;
280 syslog(LOG_ERR
, "malformed \"from\" address (af %d)\n", af
);
283 if (getnameinfo((struct sockaddr
*)fromp
, fromlen
,
284 naddr
, sizeof(naddr
), NULL
, 0, niflags
) != 0) {
285 syslog(LOG_ERR
, "malformed \"from\" address (af %d)\n", af
);
289 if (getnameinfo((struct sockaddr
*)fromp
, fromlen
,
290 saddr
, sizeof(saddr
), NULL
, 0, NI_NAMEREQD
) == 0) {
292 * If name returned by getnameinfo is in our domain,
293 * attempt to verify that we haven't been fooled by someone
294 * in a remote net; look up the name and check that this
295 * address corresponds to the name.
299 if (check_all
|| local_domain(saddr
)) {
300 strlcpy(hostnamebuf
, saddr
, sizeof(hostnamebuf
));
301 memset(&hints
, 0, sizeof(hints
));
302 hints
.ai_family
= fromp
->ss_family
;
303 hints
.ai_socktype
= SOCK_STREAM
;
304 hints
.ai_flags
= AI_CANONNAME
;
305 gaierror
= getaddrinfo(hostnamebuf
, "0", &hints
, &res0
);
308 "Couldn't look up address for %s: %s",
309 hostnamebuf
, gai_strerror(gaierror
));
312 for (res
= res0
; res
; res
= res
->ai_next
) {
313 if (res
->ai_family
!= fromp
->ss_family
)
315 if (res
->ai_addrlen
!= fromp
->ss_len
)
317 if (getnameinfo(res
->ai_addr
,
319 raddr
, sizeof(raddr
), NULL
, 0,
321 && strcmp(naddr
, raddr
) == 0) {
322 hostname
= res
->ai_canonname
330 "Host addr %s not listed for host %s",
331 naddr
, res0
->ai_canonname
338 strlcpy(hostnamebuf
, hostname
, sizeof(hostnamebuf
));
339 hostname
= hostnamebuf
;
343 strlcpy(hostnamebuf
, naddr
, sizeof(hostnamebuf
));
344 hostname
= hostnamebuf
;
347 if (ntohs(*portp
) >= IPPORT_RESERVED
||
348 ntohs(*portp
) < IPPORT_RESERVED
/2) {
349 syslog(LOG_NOTICE
, "Connection from %s on illegal port",
351 fatal(f
, "Permission denied", 0);
354 if (fromp
->ss_family
== AF_INET
) {
355 u_char optbuf
[BUFSIZ
/3], *cp
;
356 char lbuf
[BUFSIZ
], *lp
, *ep
;
357 socklen_t optsize
= sizeof(optbuf
);
361 if ((ip
= getprotobyname("ip")) != NULL
)
362 ipproto
= ip
->p_proto
;
364 ipproto
= IPPROTO_IP
;
365 if (getsockopt(0, ipproto
, IP_OPTIONS
, (char *)optbuf
,
366 &optsize
) == 0 && optsize
!= 0) {
368 ep
= lbuf
+ sizeof(lbuf
);
369 for (cp
= optbuf
; optsize
> 0; cp
++, optsize
--, lp
+= 3)
370 snprintf(lp
, ep
- lp
, " %2.2x", *cp
);
372 "Connection received using IP options (ignored):%s",
374 if (setsockopt(0, ipproto
, IP_OPTIONS
,
375 (char *)NULL
, optsize
) != 0) {
377 "setsockopt IP_OPTIONS NULL: %m");
383 if (do_rlogin((struct sockaddr
*)fromp
, hostname
) == 0)
385 if (confirmed
== 0) {
387 confirmed
= 1; /* we sent the null! */
391 pid
= forkpty(&master
, line
, NULL
, &win
);
394 fatal(f
, "Out of ptys", 0);
396 fatal(f
, "Forkpty", 1);
399 if (f
> 2) /* f should always be 0, but... */
402 (void)strvisx(hostaddrbuf
, (const char *)(const void *)fromp
,
403 sizeof(*fromp
), VIS_WHITE
);
405 execl(_PATH_LOGIN
, "login", "-p",
406 "-h", hostname
, "-a", hostaddrbuf
,
407 "-f", "--", lusername
, (char *)0);
409 execl(_PATH_LOGIN
, "login", "-p",
410 "-h", hostname
, "-a", hostaddrbuf
,
411 "--", lusername
, (char *)0);
412 fatal(STDERR_FILENO
, _PATH_LOGIN
, 1);
415 ioctl(f
, FIONBIO
, &on
);
416 ioctl(master
, FIONBIO
, &on
);
417 ioctl(master
, TIOCPKT
, &on
);
418 signal(SIGCHLD
, cleanup
);
420 signal(SIGCHLD
, SIG_IGN
);
424 char magic
[2] = { 0377, 0377 };
425 char oobdata
[] = {TIOCPKT_WINDOW
};
428 * Handle a "control" request (signaled by magic being present)
429 * in the data stream. For now, we are only willing to handle
430 * window size changes.
440 if (n
< (int)(4+sizeof (w
)) || cp
[2] != 's' || cp
[3] != 's')
442 oobdata
[0] &= ~TIOCPKT_WINDOW
; /* we know he heard */
443 memmove(&w
, cp
+4, sizeof(w
));
444 w
.ws_row
= ntohs(w
.ws_row
);
445 w
.ws_col
= ntohs(w
.ws_col
);
446 w
.ws_xpixel
= ntohs(w
.ws_xpixel
);
447 w
.ws_ypixel
= ntohs(w
.ws_ypixel
);
448 (void)ioctl(pty
, TIOCSWINSZ
, &w
);
449 return (4+sizeof (w
));
453 * rlogin "protocol" machine.
459 char pibuf
[1024+1], fibuf
[1024], *pbp
= NULL
, *fbp
= NULL
;
461 int pcc
= 0, fcc
= 0;
464 struct pollfd set
[2];
467 * Must ignore SIGTTOU, otherwise we'll stop
468 * when we try and set slave pty's window shape
469 * (our controlling tty is the master pty).
471 (void) signal(SIGTTOU
, SIG_IGN
);
472 send(f
, oobdata
, 1, MSG_OOB
); /* indicate new rlogin */
476 set
[0].events
= POLLPRI
;
479 set
[0].events
|= POLLOUT
;
481 set
[1].events
|= POLLIN
;
484 set
[1].events
|= POLLOUT
;
486 set
[0].events
|= POLLIN
;
488 if ((nfd
= poll(set
, 2, INFTIM
)) < 0) {
494 /* shouldn't happen... */
498 #define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
499 if (set
[0].revents
& POLLPRI
) {
500 cc
= read(p
, &cntl
, 1);
501 if (cc
== 1 && pkcontrol(cntl
)) {
503 send(f
, &cntl
, 1, MSG_OOB
);
504 if (cntl
& TIOCPKT_FLUSHWRITE
)
508 if (set
[1].revents
& POLLIN
) {
509 fcc
= read(f
, fibuf
, sizeof(fibuf
));
510 if (fcc
< 0 && errno
== EWOULDBLOCK
)
521 for (cp
= fibuf
; cp
< fibuf
+fcc
-1; cp
++)
522 if (cp
[0] == magic
[0] &&
524 left
= fcc
- (cp
-fibuf
);
525 n
= control(p
, cp
, left
);
539 if (set
[0].revents
& POLLOUT
&& fcc
> 0) {
540 cc
= write(p
, fbp
, fcc
);
547 if (set
[0].revents
& POLLIN
) {
548 pcc
= read(p
, pibuf
, sizeof (pibuf
));
550 if (pcc
< 0 && errno
== EWOULDBLOCK
)
554 else if (pibuf
[0] == 0) {
557 if (pkcontrol(pibuf
[0])) {
558 pibuf
[0] |= oobdata
[0];
559 send(f
, &pibuf
[0], 1, MSG_OOB
);
564 if (set
[1].revents
& POLLOUT
&& pcc
> 0) {
565 cc
= write(f
, pbp
, pcc
);
580 p
= line
+ sizeof(_PATH_DEV
) - 1;
586 if (logoutx(p
, 0, DEAD_PROCESS
))
587 logwtmpx(p
, "", "", 0, DEAD_PROCESS
);
589 (void)chmod(line
, 0666);
590 (void)chown(line
, 0, 0);
592 (void)chmod(line
, 0666);
593 (void)chown(line
, 0, 0);
595 if (ttyaction(line
, "rlogind", "root"))
596 syslog(LOG_ERR
, "%s: ttyaction failed", line
);
602 fatal(f
, msg
, syserr
)
608 char buf
[BUFSIZ
], *bp
, *ep
;
611 ep
= buf
+ sizeof(buf
);
614 * Prepend binary one to message if we haven't sent
615 * the magic null as confirmation.
618 *bp
++ = '\001'; /* error indicator */
620 len
= snprintf(bp
, ep
- bp
, "rlogind: %s: %s.\r\n",
621 msg
, strerror(errno
));
623 len
= snprintf(bp
, ep
- bp
, "rlogind: %s.\r\n", msg
);
624 (void) write(f
, buf
, bp
+ len
- buf
);
629 do_rlogin(dest
, host
)
630 struct sockaddr
*dest
;
635 getstr(rusername
, sizeof(rusername
), "remuser too long");
636 getstr(lusername
, sizeof(lusername
), "locuser too long");
637 getstr(term
+ENVSIZE
, sizeof(term
)-ENVSIZE
, "Terminal type too long");
639 pwd
= getpwnam(lusername
);
642 "%s@%s as %s: unknown login.", rusername
, host
, lusername
);
646 retval
= iruserok_sa(dest
, dest
->sa_len
, pwd
->pw_uid
== 0, rusername
,
648 /* XXX put inet_ntoa(dest->sin_addr.s_addr) into all messages below */
651 syslog(LOG_INFO
, "%s@%s as %s: iruserok succeeded",
652 rusername
, host
, lusername
);
655 syslog(LOG_INFO
, "%s@%s as %s: iruserok failed (%s)",
656 rusername
, host
, lusername
, __rcmd_errstr
);
658 syslog(LOG_INFO
, "%s@%s as %s: iruserok failed",
659 rusername
, host
, lusername
);
665 getstr(buf
, cnt
, errmsg
)
673 if (read(0, &c
, 1) != 1)
676 fatal(STDOUT_FILENO
, errmsg
, 0);
686 char *cp
= index(term
+ENVSIZE
, '/');
695 cp
= index(speed
, '/');
698 cfsetspeed(&tt
, atoi(speed
));
701 tt
.c_iflag
= TTYDEF_IFLAG
;
702 tt
.c_oflag
= TTYDEF_OFLAG
;
703 tt
.c_lflag
= TTYDEF_LFLAG
;
704 tcsetattr(fd
, TCSAFLUSH
, &tt
);
709 cp
= index(speed
, '/');
713 cfsetspeed(&tt
, atoi(speed
));
714 tcsetattr(fd
, TCSAFLUSH
, &tt
);
727 syslog(LOG_ERR
, "usage: rlogind [-alnL]");
731 * Check whether host h is in our local domain,
732 * defined as sharing the last two components of the domain part,
733 * or the entire domain part if the local domain has only one component.
734 * If either name is unqualified (contains no '.'),
735 * assume that the host is local, as it will be
736 * interpreted as such.
742 char localhost
[MAXHOSTNAMELEN
+ 1];
746 (void) gethostname(localhost
, sizeof(localhost
));
747 localhost
[sizeof(localhost
) - 1] = '\0';
748 p1
= topdomain(localhost
);
750 if (p1
== NULL
|| p2
== NULL
|| !strcasecmp(p1
, p2
))
763 for (p
= h
+ strlen(h
); p
>= h
; p
--) {