| 1 | /* |
| 2 | * Copyright (c) 1983 Regents of the University of California. |
| 3 | * All rights reserved. The Berkeley software License Agreement |
| 4 | * specifies the terms and conditions for redistribution. |
| 5 | */ |
| 6 | |
| 7 | #ifndef lint |
| 8 | char copyright[] = |
| 9 | "@(#) Copyright (c) 1983 Regents of the University of California.\n\ |
| 10 | All rights reserved.\n"; |
| 11 | #endif not lint |
| 12 | |
| 13 | #ifndef lint |
| 14 | static char sccsid[] = "@(#)rlogind.c 5.13 (Berkeley) %G%"; |
| 15 | #endif not lint |
| 16 | |
| 17 | /* |
| 18 | * remote login server: |
| 19 | * remuser\0 |
| 20 | * locuser\0 |
| 21 | * terminal info\0 |
| 22 | * data |
| 23 | */ |
| 24 | |
| 25 | #include <stdio.h> |
| 26 | #include <sys/types.h> |
| 27 | #include <sys/stat.h> |
| 28 | #include <sys/socket.h> |
| 29 | #include <sys/wait.h> |
| 30 | #include <sys/file.h> |
| 31 | |
| 32 | #include <netinet/in.h> |
| 33 | |
| 34 | #include <errno.h> |
| 35 | #include <pwd.h> |
| 36 | #include <signal.h> |
| 37 | #include <sgtty.h> |
| 38 | #include <stdio.h> |
| 39 | #include <netdb.h> |
| 40 | #include <syslog.h> |
| 41 | #include <strings.h> |
| 42 | |
| 43 | # ifndef TIOCPKT_WINDOW |
| 44 | # define TIOCPKT_WINDOW 0x80 |
| 45 | # endif TIOCPKT_WINDOW |
| 46 | |
| 47 | extern errno; |
| 48 | int reapchild(); |
| 49 | struct passwd *getpwnam(); |
| 50 | char *malloc(); |
| 51 | |
| 52 | main(argc, argv) |
| 53 | int argc; |
| 54 | char **argv; |
| 55 | { |
| 56 | int on = 1, options = 0, fromlen; |
| 57 | struct sockaddr_in from; |
| 58 | |
| 59 | openlog("rlogind", LOG_PID | LOG_AUTH, LOG_AUTH); |
| 60 | fromlen = sizeof (from); |
| 61 | if (getpeername(0, &from, &fromlen) < 0) { |
| 62 | fprintf(stderr, "%s: ", argv[0]); |
| 63 | perror("getpeername"); |
| 64 | _exit(1); |
| 65 | } |
| 66 | if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { |
| 67 | syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); |
| 68 | } |
| 69 | doit(0, &from); |
| 70 | } |
| 71 | |
| 72 | int child; |
| 73 | int cleanup(); |
| 74 | int netf; |
| 75 | extern errno; |
| 76 | char *line; |
| 77 | extern char *inet_ntoa(); |
| 78 | |
| 79 | struct winsize win = { 0, 0, 0, 0 }; |
| 80 | |
| 81 | |
| 82 | doit(f, fromp) |
| 83 | int f; |
| 84 | struct sockaddr_in *fromp; |
| 85 | { |
| 86 | int i, p, t, pid, on = 1; |
| 87 | register struct hostent *hp; |
| 88 | struct hostent hostent; |
| 89 | char c; |
| 90 | |
| 91 | alarm(60); |
| 92 | read(f, &c, 1); |
| 93 | if (c != 0) |
| 94 | exit(1); |
| 95 | alarm(0); |
| 96 | fromp->sin_port = ntohs((u_short)fromp->sin_port); |
| 97 | hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr), |
| 98 | fromp->sin_family); |
| 99 | if (hp == 0) { |
| 100 | /* |
| 101 | * Only the name is used below. |
| 102 | */ |
| 103 | hp = &hostent; |
| 104 | hp->h_name = inet_ntoa(fromp->sin_addr); |
| 105 | } |
| 106 | if (fromp->sin_family != AF_INET || |
| 107 | fromp->sin_port >= IPPORT_RESERVED || |
| 108 | fromp->sin_port < IPPORT_RESERVED/2) |
| 109 | fatal(f, "Permission denied"); |
| 110 | write(f, "", 1); |
| 111 | for (c = 'p'; c <= 's'; c++) { |
| 112 | struct stat stb; |
| 113 | line = "/dev/ptyXX"; |
| 114 | line[strlen("/dev/pty")] = c; |
| 115 | line[strlen("/dev/ptyp")] = '0'; |
| 116 | if (stat(line, &stb) < 0) |
| 117 | break; |
| 118 | for (i = 0; i < 16; i++) { |
| 119 | line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; |
| 120 | p = open(line, 2); |
| 121 | if (p > 0) |
| 122 | goto gotpty; |
| 123 | } |
| 124 | } |
| 125 | fatal(f, "Out of ptys"); |
| 126 | /*NOTREACHED*/ |
| 127 | gotpty: |
| 128 | (void) ioctl(p, TIOCSWINSZ, &win); |
| 129 | netf = f; |
| 130 | line[strlen("/dev/")] = 't'; |
| 131 | #ifdef DEBUG |
| 132 | { int tt = open("/dev/tty", 2); |
| 133 | if (tt > 0) { |
| 134 | ioctl(tt, TIOCNOTTY, 0); |
| 135 | close(tt); |
| 136 | } |
| 137 | } |
| 138 | #endif |
| 139 | t = open(line, 2); |
| 140 | if (t < 0) |
| 141 | fatalperror(f, line, errno); |
| 142 | { struct sgttyb b; |
| 143 | gtty(t, &b); b.sg_flags = RAW|ANYP; stty(t, &b); |
| 144 | } |
| 145 | pid = fork(); |
| 146 | if (pid < 0) |
| 147 | fatalperror(f, "", errno); |
| 148 | if (pid == 0) { |
| 149 | close(f), close(p); |
| 150 | dup2(t, 0), dup2(t, 1), dup2(t, 2); |
| 151 | close(t); |
| 152 | execl("/bin/login", "login", "-r", hp->h_name, 0); |
| 153 | fatalperror(2, "/bin/login", errno); |
| 154 | /*NOTREACHED*/ |
| 155 | } |
| 156 | close(t); |
| 157 | ioctl(f, FIONBIO, &on); |
| 158 | ioctl(p, FIONBIO, &on); |
| 159 | ioctl(p, TIOCPKT, &on); |
| 160 | signal(SIGTSTP, SIG_IGN); |
| 161 | signal(SIGCHLD, cleanup); |
| 162 | setpgrp(0, 0); |
| 163 | protocol(f, p); |
| 164 | signal(SIGCHLD, SIG_IGN); |
| 165 | cleanup(); |
| 166 | } |
| 167 | |
| 168 | char magic[2] = { 0377, 0377 }; |
| 169 | char oobdata[] = {TIOCPKT_WINDOW}; |
| 170 | |
| 171 | /* |
| 172 | * Handle a "control" request (signaled by magic being present) |
| 173 | * in the data stream. For now, we are only willing to handle |
| 174 | * window size changes. |
| 175 | */ |
| 176 | control(pty, cp, n) |
| 177 | int pty; |
| 178 | char *cp; |
| 179 | int n; |
| 180 | { |
| 181 | struct winsize w; |
| 182 | |
| 183 | if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') |
| 184 | return (0); |
| 185 | oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ |
| 186 | bcopy(cp+4, (char *)&w, sizeof(w)); |
| 187 | w.ws_row = ntohs(w.ws_row); |
| 188 | w.ws_col = ntohs(w.ws_col); |
| 189 | w.ws_xpixel = ntohs(w.ws_xpixel); |
| 190 | w.ws_ypixel = ntohs(w.ws_ypixel); |
| 191 | (void)ioctl(pty, TIOCSWINSZ, &w); |
| 192 | return (4+sizeof (w)); |
| 193 | } |
| 194 | |
| 195 | /* |
| 196 | * rlogin "protocol" machine. |
| 197 | */ |
| 198 | protocol(f, p) |
| 199 | int f, p; |
| 200 | { |
| 201 | char pibuf[1024], fibuf[1024], *pbp, *fbp; |
| 202 | register pcc = 0, fcc = 0; |
| 203 | int cc; |
| 204 | char cntl; |
| 205 | |
| 206 | /* |
| 207 | * Must ignore SIGTTOU, otherwise we'll stop |
| 208 | * when we try and set slave pty's window shape |
| 209 | * (our controlling tty is the master pty). |
| 210 | */ |
| 211 | (void) signal(SIGTTOU, SIG_IGN); |
| 212 | send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ |
| 213 | for (;;) { |
| 214 | int ibits, obits, ebits; |
| 215 | |
| 216 | ibits = 0; |
| 217 | obits = 0; |
| 218 | if (fcc) |
| 219 | obits |= (1<<p); |
| 220 | else |
| 221 | ibits |= (1<<f); |
| 222 | if (pcc >= 0) |
| 223 | if (pcc) |
| 224 | obits |= (1<<f); |
| 225 | else |
| 226 | ibits |= (1<<p); |
| 227 | ebits = (1<<p); |
| 228 | if (select(16, &ibits, &obits, &ebits, 0) < 0) { |
| 229 | if (errno == EINTR) |
| 230 | continue; |
| 231 | fatalperror(f, "select", errno); |
| 232 | } |
| 233 | if (ibits == 0 && obits == 0 && ebits == 0) { |
| 234 | /* shouldn't happen... */ |
| 235 | sleep(5); |
| 236 | continue; |
| 237 | } |
| 238 | #define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) |
| 239 | if (ebits & (1<<p)) { |
| 240 | cc = read(p, &cntl, 1); |
| 241 | if (cc == 1 && pkcontrol(cntl)) { |
| 242 | cntl |= oobdata[0]; |
| 243 | send(f, &cntl, 1, MSG_OOB); |
| 244 | if (cntl & TIOCPKT_FLUSHWRITE) { |
| 245 | pcc = 0; |
| 246 | ibits &= ~(1<<p); |
| 247 | } |
| 248 | } |
| 249 | } |
| 250 | if (ibits & (1<<f)) { |
| 251 | fcc = read(f, fibuf, sizeof (fibuf)); |
| 252 | if (fcc < 0 && errno == EWOULDBLOCK) |
| 253 | fcc = 0; |
| 254 | else { |
| 255 | register char *cp; |
| 256 | int left, n; |
| 257 | |
| 258 | if (fcc <= 0) |
| 259 | break; |
| 260 | fbp = fibuf; |
| 261 | |
| 262 | top: |
| 263 | for (cp = fibuf; cp < fibuf+fcc-1; cp++) |
| 264 | if (cp[0] == magic[0] && |
| 265 | cp[1] == magic[1]) { |
| 266 | left = fcc - (cp-fibuf); |
| 267 | n = control(p, cp, left); |
| 268 | if (n) { |
| 269 | left -= n; |
| 270 | if (left > 0) |
| 271 | bcopy(cp+n, cp, left); |
| 272 | fcc -= n; |
| 273 | goto top; /* n^2 */ |
| 274 | } |
| 275 | } |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | if ((obits & (1<<p)) && fcc > 0) { |
| 280 | cc = write(p, fbp, fcc); |
| 281 | if (cc > 0) { |
| 282 | fcc -= cc; |
| 283 | fbp += cc; |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | if (ibits & (1<<p)) { |
| 288 | pcc = read(p, pibuf, sizeof (pibuf)); |
| 289 | pbp = pibuf; |
| 290 | if (pcc < 0 && errno == EWOULDBLOCK) |
| 291 | pcc = 0; |
| 292 | else if (pcc <= 0) |
| 293 | break; |
| 294 | else if (pibuf[0] == 0) |
| 295 | pbp++, pcc--; |
| 296 | else { |
| 297 | if (pkcontrol(pibuf[0])) { |
| 298 | pibuf[0] |= oobdata[0]; |
| 299 | send(f, &pibuf[0], 1, MSG_OOB); |
| 300 | } |
| 301 | pcc = 0; |
| 302 | } |
| 303 | } |
| 304 | if ((obits & (1<<f)) && pcc > 0) { |
| 305 | cc = write(f, pbp, pcc); |
| 306 | if (cc < 0 && errno == EWOULDBLOCK) { |
| 307 | /* also shouldn't happen */ |
| 308 | sleep(5); |
| 309 | continue; |
| 310 | } |
| 311 | if (cc > 0) { |
| 312 | pcc -= cc; |
| 313 | pbp += cc; |
| 314 | } |
| 315 | } |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | cleanup() |
| 320 | { |
| 321 | |
| 322 | rmut(); |
| 323 | vhangup(); /* XXX */ |
| 324 | shutdown(netf, 2); |
| 325 | exit(1); |
| 326 | } |
| 327 | |
| 328 | fatal(f, msg) |
| 329 | int f; |
| 330 | char *msg; |
| 331 | { |
| 332 | char buf[BUFSIZ]; |
| 333 | |
| 334 | buf[0] = '\01'; /* error indicator */ |
| 335 | (void) sprintf(buf + 1, "rlogind: %s.\r\n", msg); |
| 336 | (void) write(f, buf, strlen(buf)); |
| 337 | exit(1); |
| 338 | } |
| 339 | |
| 340 | fatalperror(f, msg, errno) |
| 341 | int f; |
| 342 | char *msg; |
| 343 | int errno; |
| 344 | { |
| 345 | char buf[BUFSIZ]; |
| 346 | extern int sys_nerr; |
| 347 | extern char *sys_errlist[]; |
| 348 | |
| 349 | if ((unsigned)errno < sys_nerr) |
| 350 | (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]); |
| 351 | else |
| 352 | (void) sprintf(buf, "%s: Error %d", msg, errno); |
| 353 | fatal(f, buf); |
| 354 | } |
| 355 | |
| 356 | #include <utmp.h> |
| 357 | |
| 358 | struct utmp wtmp; |
| 359 | char wtmpf[] = "/usr/adm/wtmp"; |
| 360 | char utmpf[] = "/etc/utmp"; |
| 361 | #define SCPYN(a, b) strncpy(a, b, sizeof(a)) |
| 362 | #define SCMPN(a, b) strncmp(a, b, sizeof(a)) |
| 363 | |
| 364 | rmut() |
| 365 | { |
| 366 | register f; |
| 367 | int found = 0; |
| 368 | struct utmp *u, *utmp; |
| 369 | int nutmp; |
| 370 | struct stat statbf; |
| 371 | |
| 372 | f = open(utmpf, O_RDWR); |
| 373 | if (f >= 0) { |
| 374 | fstat(f, &statbf); |
| 375 | utmp = (struct utmp *)malloc(statbf.st_size); |
| 376 | if (!utmp) |
| 377 | syslog(LOG_ERR, "utmp malloc failed"); |
| 378 | if (statbf.st_size && utmp) { |
| 379 | nutmp = read(f, utmp, statbf.st_size); |
| 380 | nutmp /= sizeof(struct utmp); |
| 381 | |
| 382 | for (u = utmp ; u < &utmp[nutmp] ; u++) { |
| 383 | if (SCMPN(u->ut_line, line+5) || |
| 384 | u->ut_name[0]==0) |
| 385 | continue; |
| 386 | lseek(f, ((long)u)-((long)utmp), L_SET); |
| 387 | SCPYN(u->ut_name, ""); |
| 388 | SCPYN(u->ut_host, ""); |
| 389 | time(&u->ut_time); |
| 390 | write(f, (char *)u, sizeof(wtmp)); |
| 391 | found++; |
| 392 | } |
| 393 | } |
| 394 | close(f); |
| 395 | } |
| 396 | if (found) { |
| 397 | f = open(wtmpf, O_WRONLY|O_APPEND); |
| 398 | if (f >= 0) { |
| 399 | SCPYN(wtmp.ut_line, line+5); |
| 400 | SCPYN(wtmp.ut_name, ""); |
| 401 | SCPYN(wtmp.ut_host, ""); |
| 402 | time(&wtmp.ut_time); |
| 403 | write(f, (char *)&wtmp, sizeof(wtmp)); |
| 404 | close(f); |
| 405 | } |
| 406 | } |
| 407 | chmod(line, 0666); |
| 408 | chown(line, 0, 0); |
| 409 | line[strlen("/dev/")] = 'p'; |
| 410 | chmod(line, 0666); |
| 411 | chown(line, 0, 0); |
| 412 | } |