Merge from vendor branch GROFF:
[dragonfly.git] / libexec / rlogind / rlogind.c
CommitLineData
984263bc
MD
1/*-
2 * Copyright (c) 1983, 1988, 1989, 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.
1de703da
MD
32 *
33 * @(#) Copyright (c) 1983, 1988, 1989, 1993 The Regents of the University of California. All rights reserved.
34 * @(#)rlogind.c 8.1 (Berkeley) 6/4/93
35 * $FreeBSD: src/libexec/rlogind/rlogind.c,v 1.29.2.5 2000/12/07 15:02:31 ru Exp $
3eefb696 36 * $DragonFly: src/libexec/rlogind/rlogind.c,v 1.3 2003/11/14 03:54:31 dillon Exp $
984263bc
MD
37 */
38
984263bc
MD
39/*
40 * remote login server:
41 * \0
42 * remuser\0
43 * locuser\0
44 * terminal_type/speed\0
45 * data
46 */
47
48#define FD_SETSIZE 16 /* don't need many bits for select */
49#include <sys/types.h>
50#include <sys/param.h>
51#include <sys/stat.h>
52#include <sys/ioctl.h>
53#include <signal.h>
54#include <termios.h>
55
56#include <sys/socket.h>
57#include <netinet/in.h>
58#include <netinet/in_systm.h>
59#include <netinet/ip.h>
60#include <netinet/tcp.h>
61#include <arpa/inet.h>
62#include <netdb.h>
63
64#include <errno.h>
65#include <libutil.h>
66#include <pwd.h>
67#include <syslog.h>
68#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <unistd.h>
72#include "pathnames.h"
73
74
75#ifndef TIOCPKT_WINDOW
76#define TIOCPKT_WINDOW 0x80
77#endif
78
79#define ARGSTR "Dalnx"
80
81/* wrapper for KAME-special getnameinfo() */
82#ifndef NI_WITHSCOPEID
83#define NI_WITHSCOPEID 0
84#endif
85
86char *env[2];
87#define NMAX 30
88char lusername[NMAX+1], rusername[NMAX+1];
89static char term[64] = "TERM=";
90#define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */
91int keepalive = 1;
92int check_all = 0;
93int no_delay;
94
95struct passwd *pwd;
96
97union sockunion {
98 struct sockinet {
99 u_char si_len;
100 u_char si_family;
101 u_short si_port;
102 } su_si;
103 struct sockaddr_in su_sin;
104 struct sockaddr_in6 su_sin6;
105};
106#define su_len su_si.si_len
107#define su_family su_si.si_family
108#define su_port su_si.si_port
109
3eefb696
MD
110void doit (int, union sockunion *);
111int control (int, char *, int);
112void protocol (int, int);
113void cleanup (int);
114void fatal (int, char *, int);
115int do_rlogin (union sockunion *);
116void getstr (char *, int, char *);
117void setup_term (int);
118int do_krb_login (struct sockaddr_in *);
119void usage (void);
984263bc
MD
120
121
122int
123main(argc, argv)
124 int argc;
125 char *argv[];
126{
127 extern int __check_rhosts_file;
128 union sockunion from;
129 int ch, fromlen, on;
130
131 openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);
132
133 opterr = 0;
134 while ((ch = getopt(argc, argv, ARGSTR)) != -1)
135 switch (ch) {
136 case 'D':
137 no_delay = 1;
138 break;
139 case 'a':
140 check_all = 1;
141 break;
142 case 'l':
143 __check_rhosts_file = 0;
144 break;
145 case 'n':
146 keepalive = 0;
147 break;
148#ifdef CRYPT
149 case 'x':
150 doencrypt = 1;
151 break;
152#endif
153 case '?':
154 default:
155 usage();
156 break;
157 }
158 argc -= optind;
159 argv += optind;
160
161 fromlen = sizeof (from);
162 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
163 syslog(LOG_ERR,"Can't get peer name of remote host: %m");
164 fatal(STDERR_FILENO, "Can't get peer name of remote host", 1);
165 }
166 on = 1;
167 if (keepalive &&
168 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
169 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
170 if (no_delay &&
171 setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
172 syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
173 if (from.su_family == AF_INET)
174 {
175 on = IPTOS_LOWDELAY;
176 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
177 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
178 }
179
180 doit(0, &from);
181 return 0;
182}
183
184int child;
185int netf;
186char line[MAXPATHLEN];
187int confirmed;
188
189struct winsize win = { 0, 0, 0, 0 };
190
191
192void
193doit(f, fromp)
194 int f;
195 union sockunion *fromp;
196{
197 int master, pid, on = 1;
198 int authenticated = 0;
199 char hostname[2 * MAXHOSTNAMELEN + 1];
200 char nameinfo[2 * INET6_ADDRSTRLEN + 1];
201 char c;
202
203 alarm(60);
204 read(f, &c, 1);
205
206 if (c != 0)
207 exit(1);
208
209 alarm(0);
210
211 realhostname_sa(hostname, sizeof(hostname) - 1,
212 (struct sockaddr *)fromp, fromp->su_len);
213 /* error check ? */
214 fromp->su_port = ntohs((u_short)fromp->su_port);
215 hostname[sizeof(hostname) - 1] = '\0';
216
217 {
218 if ((fromp->su_family != AF_INET
219#ifdef INET6
220 && fromp->su_family != AF_INET6
221#endif
222 ) ||
223 fromp->su_port >= IPPORT_RESERVED ||
224 fromp->su_port < IPPORT_RESERVED/2) {
225 getnameinfo((struct sockaddr *)fromp,
226 fromp->su_len,
227 nameinfo, sizeof(nameinfo), NULL, 0,
228 NI_NUMERICHOST|NI_WITHSCOPEID);
229 /* error check ? */
230 syslog(LOG_NOTICE, "Connection from %s on illegal port",
231 nameinfo);
232 fatal(f, "Permission denied", 0);
233 }
234#ifdef IP_OPTIONS
235 if (fromp->su_family == AF_INET)
236 {
237 u_char optbuf[BUFSIZ/3];
238 int optsize = sizeof(optbuf), ipproto, i;
239 struct protoent *ip;
240
241 if ((ip = getprotobyname("ip")) != NULL)
242 ipproto = ip->p_proto;
243 else
244 ipproto = IPPROTO_IP;
245 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf,
246 &optsize) == 0 && optsize != 0) {
247 for (i = 0; i < optsize; ) {
248 u_char c = optbuf[i];
249 if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
250 syslog(LOG_NOTICE,
251 "Connection refused from %s with IP option %s",
252 inet_ntoa(fromp->su_sin.sin_addr),
253 c == IPOPT_LSRR ? "LSRR" : "SSRR");
254 exit(1);
255 }
256 if (c == IPOPT_EOL)
257 break;
258 i += (c == IPOPT_NOP) ? 1 : optbuf[i+1];
259 }
260 }
261 }
262#endif
263 if (do_rlogin(fromp) == 0)
264 authenticated++;
265 }
266 if (confirmed == 0) {
267 write(f, "", 1);
268 confirmed = 1; /* we sent the null! */
269 }
270#ifdef CRYPT
271 if (doencrypt)
272 (void) des_enc_write(f,
273 SECURE_MESSAGE,
274 strlen(SECURE_MESSAGE),
275 schedule, &kdata->session);
276#endif
277 netf = f;
278
279 pid = forkpty(&master, line, NULL, &win);
280 if (pid < 0) {
281 if (errno == ENOENT)
282 fatal(f, "Out of ptys", 0);
283 else
284 fatal(f, "Forkpty", 1);
285 }
286 if (pid == 0) {
287 if (f > 2) /* f should always be 0, but... */
288 (void) close(f);
289 setup_term(0);
290 if (*lusername=='-') {
291 syslog(LOG_ERR, "tried to pass user \"%s\" to login",
292 lusername);
293 fatal(STDERR_FILENO, "invalid user", 0);
294 }
295 if (authenticated) {
296 execl(_PATH_LOGIN, "login", "-p",
297 "-h", hostname, "-f", lusername, (char *)NULL);
298 } else
299 execl(_PATH_LOGIN, "login", "-p",
300 "-h", hostname, lusername, (char *)NULL);
301 fatal(STDERR_FILENO, _PATH_LOGIN, 1);
302 /*NOTREACHED*/
303 }
304#ifdef CRYPT
305 /*
306 * If encrypted, don't turn on NBIO or the des read/write
307 * routines will croak.
308 */
309
310 if (!doencrypt)
311#endif
312 ioctl(f, FIONBIO, &on);
313 ioctl(master, FIONBIO, &on);
314 ioctl(master, TIOCPKT, &on);
315 signal(SIGCHLD, cleanup);
316 protocol(f, master);
317 signal(SIGCHLD, SIG_IGN);
318 cleanup(0);
319}
320
321char magic[2] = { 0377, 0377 };
322char oobdata[] = {TIOCPKT_WINDOW};
323
324/*
325 * Handle a "control" request (signaled by magic being present)
326 * in the data stream. For now, we are only willing to handle
327 * window size changes.
328 */
329int
330control(pty, cp, n)
331 int pty;
332 char *cp;
333 int n;
334{
335 struct winsize w;
336
337 if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
338 return (0);
339 oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */
340 bcopy(cp+4, (char *)&w, sizeof(w));
341 w.ws_row = ntohs(w.ws_row);
342 w.ws_col = ntohs(w.ws_col);
343 w.ws_xpixel = ntohs(w.ws_xpixel);
344 w.ws_ypixel = ntohs(w.ws_ypixel);
345 (void)ioctl(pty, TIOCSWINSZ, &w);
346 return (4+sizeof (w));
347}
348
349/*
350 * rlogin "protocol" machine.
351 */
352void
353protocol(f, p)
354 register int f, p;
355{
356 char pibuf[1024+1], fibuf[1024], *pbp = NULL, *fbp = NULL;
357 int pcc = 0, fcc = 0;
358 int cc, nfd, n;
359 char cntl;
360
361 /*
362 * Must ignore SIGTTOU, otherwise we'll stop
363 * when we try and set slave pty's window shape
364 * (our controlling tty is the master pty).
365 */
366 (void) signal(SIGTTOU, SIG_IGN);
367 send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */
368 if (f > p)
369 nfd = f + 1;
370 else
371 nfd = p + 1;
372 if (nfd > FD_SETSIZE) {
373 syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE");
374 fatal(f, "internal error (select mask too small)", 0);
375 }
376 for (;;) {
377 fd_set ibits, obits, ebits, *omask;
378
379 FD_ZERO(&ebits);
380 FD_ZERO(&ibits);
381 FD_ZERO(&obits);
382 omask = (fd_set *)NULL;
383 if (fcc) {
384 FD_SET(p, &obits);
385 omask = &obits;
386 } else
387 FD_SET(f, &ibits);
388 if (pcc >= 0) {
389 if (pcc) {
390 FD_SET(f, &obits);
391 omask = &obits;
392 } else
393 FD_SET(p, &ibits);
394 }
395 FD_SET(p, &ebits);
396 if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) {
397 if (errno == EINTR)
398 continue;
399 fatal(f, "select", 1);
400 }
401 if (n == 0) {
402 /* shouldn't happen... */
403 sleep(5);
404 continue;
405 }
406#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
407 if (FD_ISSET(p, &ebits)) {
408 cc = read(p, &cntl, 1);
409 if (cc == 1 && pkcontrol(cntl)) {
410 cntl |= oobdata[0];
411 send(f, &cntl, 1, MSG_OOB);
412 if (cntl & TIOCPKT_FLUSHWRITE) {
413 pcc = 0;
414 FD_CLR(p, &ibits);
415 }
416 }
417 }
418 if (FD_ISSET(f, &ibits)) {
419#ifdef CRYPT
420 if (doencrypt)
421 fcc = des_enc_read(f, fibuf, sizeof(fibuf),
422 schedule, &kdata->session);
423 else
424#endif
425 fcc = read(f, fibuf, sizeof(fibuf));
426 if (fcc < 0 && errno == EWOULDBLOCK)
427 fcc = 0;
428 else {
429 register char *cp;
430 int left, n;
431
432 if (fcc <= 0)
433 break;
434 fbp = fibuf;
435
436 top:
437 for (cp = fibuf; cp < fibuf+fcc-1; cp++)
438 if (cp[0] == magic[0] &&
439 cp[1] == magic[1]) {
440 left = fcc - (cp-fibuf);
441 n = control(p, cp, left);
442 if (n) {
443 left -= n;
444 if (left > 0)
445 bcopy(cp+n, cp, left);
446 fcc -= n;
447 goto top; /* n^2 */
448 }
449 }
450 FD_SET(p, &obits); /* try write */
451 }
452 }
453
454 if (FD_ISSET(p, &obits) && fcc > 0) {
455 cc = write(p, fbp, fcc);
456 if (cc > 0) {
457 fcc -= cc;
458 fbp += cc;
459 }
460 }
461
462 if (FD_ISSET(p, &ibits)) {
463 pcc = read(p, pibuf, sizeof (pibuf));
464 pbp = pibuf;
465 if (pcc < 0 && errno == EWOULDBLOCK)
466 pcc = 0;
467 else if (pcc <= 0)
468 break;
469 else if (pibuf[0] == 0) {
470 pbp++, pcc--;
471#ifdef CRYPT
472 if (!doencrypt)
473#endif
474 FD_SET(f, &obits); /* try write */
475 } else {
476 if (pkcontrol(pibuf[0])) {
477 pibuf[0] |= oobdata[0];
478 send(f, &pibuf[0], 1, MSG_OOB);
479 }
480 pcc = 0;
481 }
482 }
483 if ((FD_ISSET(f, &obits)) && pcc > 0) {
484#ifdef CRYPT
485 if (doencrypt)
486 cc = des_enc_write(f, pbp, pcc,
487 schedule, &kdata->session);
488 else
489#endif
490 cc = write(f, pbp, pcc);
491 if (cc < 0 && errno == EWOULDBLOCK) {
492 /*
493 * This happens when we try write after read
494 * from p, but some old kernels balk at large
495 * writes even when select returns true.
496 */
497 if (!FD_ISSET(p, &ibits))
498 sleep(5);
499 continue;
500 }
501 if (cc > 0) {
502 pcc -= cc;
503 pbp += cc;
504 }
505 }
506 }
507}
508
509void
510cleanup(signo)
511 int signo;
512{
513 char *p;
514
515 p = line + sizeof(_PATH_DEV) - 1;
516 if (logout(p))
517 logwtmp(p, "", "");
518 (void)chflags(line, 0);
519 (void)chmod(line, 0666);
520 (void)chown(line, 0, 0);
521 *p = 'p';
522 (void)chflags(line, 0);
523 (void)chmod(line, 0666);
524 (void)chown(line, 0, 0);
525 shutdown(netf, 2);
526 exit(1);
527}
528
529void
530fatal(f, msg, syserr)
531 int f;
532 char *msg;
533 int syserr;
534{
535 int len;
536 char buf[BUFSIZ], *bp = buf;
537
538 /*
539 * Prepend binary one to message if we haven't sent
540 * the magic null as confirmation.
541 */
542 if (!confirmed)
543 *bp++ = '\01'; /* error indicator */
544 if (syserr)
545 len = snprintf(bp, sizeof(buf), "rlogind: %s: %s.\r\n",
546 msg, strerror(errno));
547 else
548 len = snprintf(bp, sizeof(buf), "rlogind: %s.\r\n", msg);
549 (void) write(f, buf, bp + len - buf);
550 exit(1);
551}
552
553int
554do_rlogin(dest)
555 union sockunion *dest;
556{
557
558 getstr(rusername, sizeof(rusername), "remuser too long");
559 getstr(lusername, sizeof(lusername), "locuser too long");
560 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
561
562 pwd = getpwnam(lusername);
563 if (pwd == NULL)
564 return (-1);
565 /* XXX why don't we syslog() failure? */
566
567 return (iruserok_sa(dest, dest->su_len, pwd->pw_uid == 0, rusername,
568 lusername));
569}
570
571void
572getstr(buf, cnt, errmsg)
573 char *buf;
574 int cnt;
575 char *errmsg;
576{
577 char c;
578
579 do {
580 if (read(0, &c, 1) != 1)
581 exit(1);
582 if (--cnt < 0)
583 fatal(STDOUT_FILENO, errmsg, 0);
584 *buf++ = c;
585 } while (c != 0);
586}
587
588extern char **environ;
589
590void
591setup_term(fd)
592 int fd;
593{
594 register char *cp = index(term+ENVSIZE, '/');
595 char *speed;
596 struct termios tt;
597
598#ifndef notyet
599 tcgetattr(fd, &tt);
600 if (cp) {
601 *cp++ = '\0';
602 speed = cp;
603 cp = index(speed, '/');
604 if (cp)
605 *cp++ = '\0';
606 cfsetspeed(&tt, atoi(speed));
607 }
608
609 tt.c_iflag = TTYDEF_IFLAG;
610 tt.c_oflag = TTYDEF_OFLAG;
611 tt.c_lflag = TTYDEF_LFLAG;
612 tcsetattr(fd, TCSAFLUSH, &tt);
613#else
614 if (cp) {
615 *cp++ = '\0';
616 speed = cp;
617 cp = index(speed, '/');
618 if (cp)
619 *cp++ = '\0';
620 tcgetattr(fd, &tt);
621 cfsetspeed(&tt, atoi(speed));
622 tcsetattr(fd, TCSAFLUSH, &tt);
623 }
624#endif
625
626 env[0] = term;
627 env[1] = 0;
628 environ = env;
629}
630
631void
632usage()
633{
634 syslog(LOG_ERR, "usage: rlogind [-" ARGSTR "]");
635}