add a man page from Chris Demetrios
[unix-history] / usr / src / libexec / rlogind / rlogind.c
CommitLineData
1dcc3f26 1/*-
a9834437
KB
2 * Copyright (c) 1983, 1988, 1989, 1993
3 * The Regents of the University of California. All rights reserved.
9c901b93 4 *
1dcc3f26 5 * %sccs.include.redist.c%
8c5eec2f
DF
6 */
7
8#ifndef lint
a9834437
KB
9static char copyright[] =
10"@(#) Copyright (c) 1983, 1988, 1989, 1993\n\
11 The Regents of the University of California. All rights reserved.\n";
9c901b93 12#endif /* not lint */
8c5eec2f 13
a10a364d 14#ifndef lint
a9834437 15static char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) %G%";
9c901b93 16#endif /* not lint */
a10a364d 17
bb933cc2
MK
18/*
19 * remote login server:
e3fc62b0 20 * \0
bb933cc2
MK
21 * remuser\0
22 * locuser\0
e3fc62b0 23 * terminal_type/speed\0
9d5f81ab 24 * data
dfd00f77
MK
25 *
26 * Automatic login protocol is done here, using login -f upon success,
27 * unless OLD_LOGIN is defined (then done in login, ala 4.2/4.3BSD).
bb933cc2
MK
28 */
29
b4839a52 30#define FD_SETSIZE 16 /* don't need many bits for select */
2b08f0dd 31#include <sys/param.h>
a10a364d 32#include <sys/stat.h>
95d03f33 33#include <sys/ioctl.h>
618a2b86
KB
34#include <signal.h>
35#include <termios.h>
c6c678f1 36
618a2b86 37#include <sys/socket.h>
c6c678f1 38#include <netinet/in.h>
6e1cccbc
MK
39#include <netinet/in_systm.h>
40#include <netinet/ip.h>
618a2b86
KB
41#include <arpa/inet.h>
42#include <netdb.h>
c6c678f1 43
a10a364d 44#include <pwd.h>
3f99c0f7 45#include <syslog.h>
618a2b86 46#include <errno.h>
95d03f33 47#include <stdio.h>
27ed11f5 48#include <unistd.h>
618a2b86
KB
49#include <stdlib.h>
50#include <string.h>
95d03f33 51#include "pathnames.h"
9d5f81ab
MK
52
53#ifndef TIOCPKT_WINDOW
54#define TIOCPKT_WINDOW 0x80
55#endif
e3fc62b0 56
9d5f81ab
MK
57char *env[2];
58#define NMAX 30
5a93e98d
MK
59char lusername[NMAX+1], rusername[NMAX+1];
60static char term[64] = "TERM=";
61#define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */
62int keepalive = 1;
a6449a97 63int check_all = 0;
f295ed5f 64int check_all = 0;
e3fc62b0 65
618a2b86 66struct passwd *pwd;
bb933cc2 67
e355f53b
CL
68void doit __P((int, struct sockaddr_in *));
69int control __P((int, char *, int));
70void protocol __P((int, int));
9d95c77d 71void cleanup __P((int));
e355f53b
CL
72void fatal __P((int, char *, int));
73int do_rlogin __P((struct sockaddr_in *));
74void getstr __P((char *, int, char *));
75void setup_term __P((int));
76int do_krb_login __P((struct sockaddr_in *));
77void usage __P((void));
78int local_domain __P((char *));
79char *topdomain __P((char *));
80
81int
a10a364d
BJ
82main(argc, argv)
83 int argc;
e355f53b 84 char *argv[];
a10a364d 85{
e355f53b 86 extern int __check_rhosts_file;
a10a364d 87 struct sockaddr_in from;
e355f53b 88 int ch, fromlen, on;
a10a364d 89
20b6f6ce 90 openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);
10869b2d
KB
91
92 opterr = 0;
b3da51a3 93 while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
5a93e98d 94 switch (ch) {
f295ed5f
MK
95 case 'a':
96 check_all = 1;
97 break;
a6449a97
MK
98 case 'a':
99 check_all = 1;
100 break;
10869b2d 101 case 'l':
e355f53b 102 __check_rhosts_file = 0;
10869b2d 103 break;
5a93e98d
MK
104 case 'n':
105 keepalive = 0;
106 break;
b3da51a3
KF
107#ifdef KERBEROS
108 case 'k':
109 use_kerberos = 1;
110 break;
111 case 'v':
112 vacuous = 1;
113 break;
114#endif
10869b2d
KB
115 case '?':
116 default:
b3da51a3 117 usage();
10869b2d
KB
118 break;
119 }
120 argc -= optind;
121 argv += optind;
122
b3da51a3 123#ifdef KERBEROS
c1e16073 124 if (use_kerberos && vacuous) {
b3da51a3 125 usage();
27ed11f5 126 fatal(STDERR_FILENO, "only one of -k and -v allowed", 0);
b3da51a3
KF
127 }
128#endif
bb933cc2 129 fromlen = sizeof (from);
618a2b86 130 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
2b08f0dd 131 syslog(LOG_ERR, "Couldn't get peer name of remote host: %m");
f295ed5f 132 fatalperror("Can't get peer name of remote host");
92e31018 133 }
e355f53b 134 on = 1;
5a93e98d
MK
135 if (keepalive &&
136 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
3f99c0f7 137 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
6e1cccbc
MK
138 on = IPTOS_LOWDELAY;
139 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
140 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
bb933cc2 141 doit(0, &from);
92e31018
SL
142}
143
a10a364d 144int child;
a10a364d 145int netf;
4361e949 146char line[MAXPATHLEN];
b4839a52 147int confirmed;
a10a364d 148
8210fa73
KM
149struct winsize win = { 0, 0, 0, 0 };
150
66feb14c 151
e355f53b 152void
a10a364d
BJ
153doit(f, fromp)
154 int f;
155 struct sockaddr_in *fromp;
156{
e355f53b
CL
157 int master, pid, on = 1;
158 int authenticated = 0;
dfd00f77 159#endif
3f5b52bc 160 register struct hostent *hp;
83300655 161 char hostname[2 * MAXHOSTNAMELEN + 1];
6632767b 162 char c;
a10a364d
BJ
163
164 alarm(60);
165 read(f, &c, 1);
b4839a52 166 if (c != 0)
a10a364d 167 exit(1);
32d4627a 168#ifdef KERBEROS
f295ed5f 169 if (vacuous)
27ed11f5 170 fatal(f, "Remote host requires Kerberos authentication", 0);
e3fc62b0 171
a10a364d 172 alarm(0);
1227078b 173 fromp->sin_port = ntohs((u_short)fromp->sin_port);
618a2b86 174 hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(struct in_addr),
e3fc62b0 175
c1e16073 176 if (use_kerberos) {
e355f53b 177 retval = do_krb_login(fromp);
f5bb5ea0 178 if (retval == 0)
b3da51a3 179 authenticated++;
c1e16073 180 else if (retval > 0)
27ed11f5 181 fatal(f, krb_err_txt[retval], 0);
b4839a52
MK
182 write(f, &c, 1);
183 confirmed = 1; /* we sent the null! */
b3da51a3 184 } else
dfd00f77 185#ifndef OLD_LOGIN
2771f96c 186 {
e355f53b
CL
187 if (fromp->sin_family != AF_INET ||
188 fromp->sin_port >= IPPORT_RESERVED ||
189 fromp->sin_port < IPPORT_RESERVED/2) {
190 syslog(LOG_NOTICE, "Connection from %s on illegal port",
191 inet_ntoa(fromp->sin_addr));
192 fatal(f, "Permission denied", 0);
193 }
194 if (do_rlogin(fromp) == 0)
195 authenticated++;
c1e16073 196 }
b4839a52
MK
197 if (confirmed == 0) {
198 write(f, "", 1);
199 confirmed = 1; /* we sent the null! */
200 }
201#ifdef KERBEROS
b4839a52 202#endif
4361e949 203 netf = f;
84e161e6 204
4361e949
MT
205 pid = forkpty(&master, line, NULL, &win);
206 if (pid < 0) {
207 if (errno == ENOENT)
208 fatal(f, "Out of ptys", 0);
209 else
210 fatal(f, "Forkpty", 1);
211 }
212 if (pid == 0) {
e355f53b 213 if (f > 2) /* f should always be 0, but... */
4361e949
MT
214 (void) close(f);
215 setup_term(0);
dfd00f77
MK
216#ifdef OLD_LOGIN
217 execl("/bin/login", "login", "-r", hp->h_name, 0);
218#else /* OLD_LOGIN */
7aac2862 219 if (authenticated) {
6e1cccbc 220#ifdef KERBEROS
7aac2862
KF
221 if (use_kerberos && (pwd->pw_uid == 0))
222 syslog(LOG_INFO|LOG_AUTH,
223 "ROOT Kerberos login from %s.%s@%s on %s\n",
224 kdata->pname, kdata->pinst, kdata->prealm,
e355f53b 225 hostname);
6e1cccbc 226#endif
7aac2862 227
95d03f33 228 execl(_PATH_LOGIN, "login", "-p",
e355f53b 229 "-h", hostname, "-f", lusername, (char *)NULL);
7aac2862 230 } else
95d03f33 231 execl(_PATH_LOGIN, "login", "-p",
e355f53b 232 "-h", hostname, lusername, (char *)NULL);
dfd00f77 233#endif /* OLD_LOGIN */
27ed11f5 234 fatal(STDERR_FILENO, _PATH_LOGIN, 1);
6632767b
SL
235 /*NOTREACHED*/
236 }
754db9db 237 ioctl(f, FIONBIO, &on);
4361e949
MT
238 ioctl(master, FIONBIO, &on);
239 ioctl(master, TIOCPKT, &on);
6632767b 240 signal(SIGCHLD, cleanup);
4361e949 241 protocol(f, master);
78c1c171 242 signal(SIGCHLD, SIG_IGN);
9d95c77d 243 cleanup(0);
6632767b 244}
82c973f2 245
6632767b 246char magic[2] = { 0377, 0377 };
87e80234 247char oobdata[] = {TIOCPKT_WINDOW};
6632767b
SL
248
249/*
250 * Handle a "control" request (signaled by magic being present)
251 * in the data stream. For now, we are only willing to handle
252 * window size changes.
253 */
e355f53b 254int
6632767b
SL
255control(pty, cp, n)
256 int pty;
257 char *cp;
258 int n;
259{
cfafdcf2 260 struct winsize w;
6632767b 261
cfafdcf2 262 if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
6632767b 263 return (0);
87e80234 264 oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */
cfafdcf2
KM
265 bcopy(cp+4, (char *)&w, sizeof(w));
266 w.ws_row = ntohs(w.ws_row);
267 w.ws_col = ntohs(w.ws_col);
268 w.ws_xpixel = ntohs(w.ws_xpixel);
269 w.ws_ypixel = ntohs(w.ws_ypixel);
270 (void)ioctl(pty, TIOCSWINSZ, &w);
271 return (4+sizeof (w));
6632767b
SL
272}
273
274/*
275 * rlogin "protocol" machine.
276 */
e355f53b 277void
6632767b 278protocol(f, p)
27ed11f5 279 register int f, p;
6632767b 280{
b4839a52 281 char pibuf[1024+1], fibuf[1024], *pbp, *fbp;
6632767b 282 register pcc = 0, fcc = 0;
27ed11f5 283 int cc, nfd, n;
2c360ad3 284 char cntl;
6632767b 285
ffaf1c0b 286 /*
9ee4d121
SL
287 * Must ignore SIGTTOU, otherwise we'll stop
288 * when we try and set slave pty's window shape
87e80234 289 * (our controlling tty is the master pty).
ffaf1c0b 290 */
9ee4d121 291 (void) signal(SIGTTOU, SIG_IGN);
87e80234 292 send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */
5a93e98d
MK
293 if (f > p)
294 nfd = f + 1;
295 else
296 nfd = p + 1;
b4839a52
MK
297 if (nfd > FD_SETSIZE) {
298 syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE");
299 fatal(f, "internal error (select mask too small)", 0);
300 }
6632767b 301 for (;;) {
b4839a52 302 fd_set ibits, obits, ebits, *omask;
6632767b 303
d691a08a 304 FD_ZERO(&ebits);
27ed11f5
KB
305 FD_ZERO(&ibits);
306 FD_ZERO(&obits);
b4839a52
MK
307 omask = (fd_set *)NULL;
308 if (fcc) {
27ed11f5 309 FD_SET(p, &obits);
b4839a52
MK
310 omask = &obits;
311 } else
27ed11f5 312 FD_SET(f, &ibits);
6632767b 313 if (pcc >= 0)
b4839a52 314 if (pcc) {
27ed11f5 315 FD_SET(f, &obits);
b4839a52
MK
316 omask = &obits;
317 } else
27ed11f5
KB
318 FD_SET(p, &ibits);
319 FD_SET(p, &ebits);
b4839a52 320 if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) {
6632767b 321 if (errno == EINTR)
a10a364d 322 continue;
27ed11f5 323 fatal(f, "select", 1);
6632767b 324 }
27ed11f5 325 if (n == 0) {
6632767b
SL
326 /* shouldn't happen... */
327 sleep(5);
328 continue;
329 }
2c360ad3 330#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
27ed11f5 331 if (FD_ISSET(p, &ebits)) {
2c360ad3
MK
332 cc = read(p, &cntl, 1);
333 if (cc == 1 && pkcontrol(cntl)) {
334 cntl |= oobdata[0];
335 send(f, &cntl, 1, MSG_OOB);
336 if (cntl & TIOCPKT_FLUSHWRITE) {
337 pcc = 0;
27ed11f5 338 FD_CLR(p, &ibits);
2c360ad3
MK
339 }
340 }
341 }
754db9db 342 fcc = read(f, fibuf, sizeof(fibuf));
6632767b
SL
343 if (fcc < 0 && errno == EWOULDBLOCK)
344 fcc = 0;
345 else {
346 register char *cp;
347 int left, n;
348
349 if (fcc <= 0)
1227078b 350 break;
6632767b 351 fbp = fibuf;
66feb14c 352
6632767b 353 top:
87e80234 354 for (cp = fibuf; cp < fibuf+fcc-1; cp++)
6632767b
SL
355 if (cp[0] == magic[0] &&
356 cp[1] == magic[1]) {
357 left = fcc - (cp-fibuf);
358 n = control(p, cp, left);
359 if (n) {
360 left -= n;
361 if (left > 0)
87e80234 362 bcopy(cp+n, cp, left);
6632767b
SL
363 fcc -= n;
364 goto top; /* n^2 */
87e80234
MK
365 }
366 }
27ed11f5 367 FD_SET(p, &obits); /* try write */
87e80234
MK
368 }
369 }
66feb14c 370
27ed11f5 371 if (FD_ISSET(p, &obits) && fcc > 0) {
87e80234 372 cc = write(p, fbp, fcc);
66feb14c
KM
373 if (cc > 0) {
374 fcc -= cc;
375 fbp += cc;
a10a364d 376 }
6632767b 377 }
66feb14c 378
27ed11f5 379 if (FD_ISSET(p, &ibits)) {
6632767b
SL
380 pcc = read(p, pibuf, sizeof (pibuf));
381 pbp = pibuf;
382 if (pcc < 0 && errno == EWOULDBLOCK)
383 pcc = 0;
384 else if (pcc <= 0)
385 break;
5a93e98d 386 else if (pibuf[0] == 0) {
6632767b 387 pbp++, pcc--;
754db9db 388 obits |= fmask; /* try a write */
5a93e98d 389 } else {
6632767b 390 if (pkcontrol(pibuf[0])) {
87e80234 391 pibuf[0] |= oobdata[0];
6632767b 392 send(f, &pibuf[0], 1, MSG_OOB);
a10a364d 393 }
6632767b 394 pcc = 0;
a10a364d 395 }
6632767b 396 }
754db9db 397 cc = write(f, pbp, pcc);
87e80234 398 if (cc < 0 && errno == EWOULDBLOCK) {
b4839a52
MK
399 /*
400 * This happens when we try write after read
401 * from p, but some old kernels balk at large
402 * writes even when select returns true.
403 */
404 if (!FD_ISSET(p, &ibits))
405 sleep(5);
87e80234
MK
406 continue;
407 }
6632767b
SL
408 if (cc > 0) {
409 pcc -= cc;
410 pbp += cc;
411 }
412 }
a10a364d 413 }
a10a364d
BJ
414}
415
618a2b86 416void
9d95c77d
KB
417cleanup(signo)
418 int signo;
a10a364d 419{
6487d192
KB
420 char *p;
421
27ed11f5 422 p = line + sizeof(_PATH_DEV) - 1;
6487d192
KB
423 if (logout(p))
424 logwtmp(p, "", "");
69be07b8 425 (void)chmod(line, 0666);
6487d192
KB
426 (void)chown(line, 0, 0);
427 *p = 'p';
69be07b8 428 (void)chmod(line, 0666);
6487d192 429 (void)chown(line, 0, 0);
ff24c640 430 shutdown(netf, 2);
a10a364d
BJ
431 exit(1);
432}
433
e355f53b 434void
27ed11f5 435fatal(f, msg, syserr)
e355f53b 436 int f;
82c973f2 437 char *msg;
e355f53b 438 int syserr;
82c973f2 439{
27ed11f5 440 int len;
b4839a52 441 char buf[BUFSIZ], *bp = buf;
82c973f2 442
b4839a52
MK
443 /*
444 * Prepend binary one to message if we haven't sent
445 * the magic null as confirmation.
446 */
447 if (!confirmed)
448 *bp++ = '\01'; /* error indicator */
27ed11f5 449 if (syserr)
b4839a52 450 len = sprintf(bp, "rlogind: %s: %s.\r\n",
27ed11f5 451 msg, strerror(errno));
1227078b 452 else
b4839a52
MK
453 len = sprintf(bp, "rlogind: %s.\r\n", msg);
454 (void) write(f, buf, bp + len - buf);
27ed11f5 455 exit(1);
82c973f2 456}
e3fc62b0 457
dfd00f77 458#ifndef OLD_LOGIN
e355f53b
CL
459int
460do_rlogin(dest)
461 struct sockaddr_in *dest;
e3fc62b0 462{
9d5f81ab
MK
463 getstr(rusername, sizeof(rusername), "remuser too long");
464 getstr(lusername, sizeof(lusername), "locuser too long");
465 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
466
e3fc62b0 467 pwd = getpwnam(lusername);
9d5f81ab 468 if (pwd == NULL)
e355f53b 469 return (-1);
f5bb5ea0 470 if (pwd->pw_uid == 0)
e355f53b
CL
471 return (-1);
472 /* XXX why don't we syslog() failure? */
473 return (iruserok(dest->sin_addr.s_addr, 0, rusername, lusername));
e3fc62b0
KF
474}
475
e355f53b 476void
9d5f81ab
MK
477getstr(buf, cnt, errmsg)
478 char *buf;
479 int cnt;
480 char *errmsg;
e3fc62b0 481{
9d5f81ab
MK
482 char c;
483
e3fc62b0 484 do {
9d5f81ab 485 if (read(0, &c, 1) != 1)
e3fc62b0 486 exit(1);
9d5f81ab 487 if (--cnt < 0)
27ed11f5 488 fatal(STDOUT_FILENO, errmsg, 0);
e3fc62b0 489 *buf++ = c;
9d5f81ab 490 } while (c != 0);
e3fc62b0
KF
491}
492
9d5f81ab 493extern char **environ;
e3fc62b0 494
e355f53b 495void
dfd00f77
MK
496char *speeds[] = {
497 "0", "50", "75", "110", "134", "150", "200", "300", "600",
498 "1200", "1800", "2400", "4800", "9600", "19200", "38400",
499};
500#define NSPEEDS (sizeof(speeds) / sizeof(speeds[0]))
501
502setup_term(fd)
503 int fd;
504{
505 register char *cp = index(term, '/'), **cpp;
dfd00f77 506 char *speed;
f295ed5f
MK
507#if BSD > 43
508 struct termios tt;
509
510 tcgetattr(fd, &tt);
511 if (cp) {
512 *cp++ = '\0';
513 speed = cp;
514 cp = index(speed, '/');
515 if (cp)
516 *cp++ = '\0';
517 cfsetspeed(&tt, atoi(speed));
518 }
519
520 tt.c_iflag = TTYDEF_IFLAG;
521 tt.c_oflag = TTYDEF_OFLAG;
522 tt.c_lflag = TTYDEF_LFLAG;
523 tcsetattr(fd, TCSADFLUSH, &tt);
524#else
525 struct sgttyb sgttyb;
dfd00f77
MK
526
527 (void)ioctl(fd, TIOCGETP, &sgttyb);
528 if (cp) {
529 *cp++ = '\0';
530 speed = cp;
531 cp = index(speed, '/');
532 if (cp)
533 *cp++ = '\0';
534 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
535 if (strcmp(*cpp, speed) == 0) {
536 sgttyb.sg_ispeed = sgttyb.sg_ospeed = cpp - speeds;
537 break;
538 }
539 }
540 sgttyb.sg_flags = ECHO|CRMOD|ANYP|XTABS;
541 (void)ioctl(fd, TIOCSETP, &sgttyb);
f295ed5f 542#endif
dfd00f77
MK
543
544 env[0] = term;
545 env[1] = 0;
546 environ = env;
547}
2b08f0dd
MK
548
549/*
550 * Check whether host h is in our local domain,
f295ed5f
MK
551 * defined as sharing the last two components of the domain part,
552 * or the entire domain part if the local domain has only one component.
2b08f0dd
MK
553 * If either name is unqualified (contains no '.'),
554 * assume that the host is local, as it will be
555 * interpreted as such.
556 */
557local_domain(h)
558 char *h;
559{
560 char localhost[MAXHOSTNAMELEN];
f295ed5f 561 char *p1, *p2, *topdomain();
2b08f0dd 562
f295ed5f 563 localhost[0] = 0;
2b08f0dd 564 (void) gethostname(localhost, sizeof(localhost));
f295ed5f
MK
565 p1 = topdomain(localhost);
566 p2 = topdomain(h);
2b08f0dd
MK
567 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
568 return(1);
569 return(0);
570}
f295ed5f
MK
571
572char *
573topdomain(h)
574 char *h;
575{
576 register char *p;
577 char *maybe = NULL;
578 int dots = 0;
579
580 for (p = h + strlen(h); p >= h; p--) {
581 if (*p == '.') {
582 if (++dots == 2)
583 return (p);
584 maybe = p;
585 }
586 }
587 return (maybe);
588}
dfd00f77 589#endif /* OLD_LOGIN */