1 /* 2 * Copyright (c) 1983, 1988 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 char copyright[] = 20 "@(#) Copyright (c) 1983, 1988 The Regents of the University of California.\n\ 21 All rights reserved.\n"; 22 #endif /* not lint */ 23 24 #ifndef lint 25 static char sccsid[] = "@(#)rshd.c 5.23 (Berkeley) 05/18/89"; 26 #endif /* not lint */ 27 28 /* 29 * remote shell server: 30 * [port]\0 31 * remuser\0 32 * locuser\0 33 * command\0 34 * data 35 */ 36 #include <sys/param.h> 37 #include <sys/ioctl.h> 38 #include <sys/socket.h> 39 #include <sys/file.h> 40 #include <sys/signal.h> 41 #include <sys/time.h> 42 43 #include <netinet/in.h> 44 45 #include <arpa/inet.h> 46 47 #include <stdio.h> 48 #include <errno.h> 49 #include <pwd.h> 50 #include <netdb.h> 51 #include <syslog.h> 52 #include "pathnames.h" 53 54 int errno; 55 int keepalive = 1; 56 char *index(), *rindex(), *strncat(); 57 /*VARARGS1*/ 58 int error(); 59 60 #ifdef KERBEROS 61 #include <kerberos/krb.h> 62 #define VERSION_SIZE 9 63 #define OPTIONS "lnkve" 64 char *strsave(); 65 char authbuf[sizeof(AUTH_DAT)]; 66 char tickbuf[sizeof(KTEXT_ST)]; 67 int use_kerberos = 0, vacuous = 0; 68 int encrypt = 0; 69 #else 70 #define OPTIONS "ln" 71 #endif 72 73 /*ARGSUSED*/ 74 main(argc, argv) 75 int argc; 76 char **argv; 77 { 78 extern int opterr, optind, _check_rhosts_file; 79 struct linger linger; 80 int ch, on = 1, fromlen; 81 struct sockaddr_in from; 82 83 openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 84 85 opterr = 0; 86 while ((ch = getopt(argc, argv, OPTIONS)) != EOF) 87 switch((char)ch) { 88 case 'l': 89 _check_rhosts_file = 0; 90 break; 91 case 'n': 92 keepalive = 0; 93 break; 94 #ifdef KERBEROS 95 case 'k': 96 use_kerberos = 1; 97 break; 98 99 case 'v': 100 vacuous = 1; 101 break; 102 103 case 'x': 104 encrypt = 1; 105 { 106 syslog(LOG_ERR, 107 "-x option to rshd unimplemented"); 108 exit(1); 109 } 110 break; 111 #endif 112 case '?': 113 default: 114 usage(); 115 break; 116 } 117 118 argc -= optind; 119 argv += optind; 120 121 #ifdef KERBEROS 122 if (use_kerberos && vacuous) { 123 syslog(LOG_ERR, "only one of -k and -v allowed"); 124 exit(1); 125 } 126 #endif 127 128 fromlen = sizeof (from); 129 if (getpeername(0, &from, &fromlen) < 0) { 130 fprintf(stderr, "%s: ", argv[0]); 131 perror("getpeername"); 132 _exit(1); 133 } 134 if (keepalive && 135 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, 136 sizeof(on)) < 0) 137 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 138 linger.l_onoff = 1; 139 linger.l_linger = 60; /* XXX */ 140 if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger, 141 sizeof (linger)) < 0) 142 syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); 143 doit(&from); 144 } 145 146 char username[20] = "USER="; 147 char homedir[64] = "HOME="; 148 char shell[64] = "SHELL="; 149 char *envinit[] = 150 {homedir, shell, _PATH_DEFPATH, username, 0}; 151 char **environ; 152 153 doit(fromp) 154 struct sockaddr_in *fromp; 155 { 156 char cmdbuf[NCARGS+1], *cp; 157 char locuser[16], remuser[16]; 158 struct passwd *pwd; 159 int s; 160 struct hostent *hp; 161 char *hostname; 162 short port; 163 int pv[2], pid, cc; 164 int nfd; 165 fd_set ready, readfrom; 166 char buf[BUFSIZ], sig; 167 int one = 1; 168 char remotehost[2 * MAXHOSTNAMELEN + 1]; 169 170 #ifdef KERBEROS 171 AUTH_DAT *kdata = (AUTH_DAT *) NULL; 172 KTEXT ticket = (KTEXT) NULL; 173 char instance[INST_SZ], version[VERSION_SIZE]; 174 char *h_name; 175 struct sockaddr_in fromaddr; 176 int rc; 177 long authopts; 178 179 fromaddr = *fromp; 180 #endif 181 182 (void) signal(SIGINT, SIG_DFL); 183 (void) signal(SIGQUIT, SIG_DFL); 184 (void) signal(SIGTERM, SIG_DFL); 185 #ifdef DEBUG 186 { int t = open(_PATH_TTY, 2); 187 if (t >= 0) { 188 ioctl(t, TIOCNOTTY, (char *)0); 189 (void) close(t); 190 } 191 } 192 #endif 193 fromp->sin_port = ntohs((u_short)fromp->sin_port); 194 if (fromp->sin_family != AF_INET) { 195 syslog(LOG_ERR, "malformed from address\n"); 196 exit(1); 197 } 198 #ifdef IP_OPTIONS 199 { 200 u_char optbuf[BUFSIZ/3], *cp; 201 char lbuf[BUFSIZ], *lp; 202 int optsize = sizeof(optbuf), ipproto; 203 struct protoent *ip; 204 205 if ((ip = getprotobyname("ip")) != NULL) 206 ipproto = ip->p_proto; 207 else 208 ipproto = IPPROTO_IP; 209 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) == 0 && 210 optsize != 0) { 211 lp = lbuf; 212 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) 213 sprintf(lp, " %2.2x", *cp); 214 syslog(LOG_NOTICE, 215 "Connection received using IP options (ignored):%s", lbuf); 216 if (setsockopt(0, ipproto, IP_OPTIONS, 217 (char *)NULL, &optsize) != 0) { 218 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); 219 exit(1); 220 } 221 } 222 } 223 #endif 224 225 #ifdef KERBEROS 226 if (!use_kerberos) 227 #endif 228 if (fromp->sin_port >= IPPORT_RESERVED || 229 fromp->sin_port < IPPORT_RESERVED/2) { 230 syslog(LOG_NOTICE, 231 "Connection from %s on illegal port", 232 inet_ntoa(fromp->sin_addr)); 233 exit(1); 234 } 235 236 (void) alarm(60); 237 port = 0; 238 for (;;) { 239 char c; 240 if ((cc = read(0, &c, 1)) != 1) { 241 if (cc < 0) 242 syslog(LOG_NOTICE, "read: %m"); 243 shutdown(0, 1+1); 244 exit(1); 245 } 246 if(c== 0) 247 break; 248 port = port * 10 + c - '0'; 249 } 250 251 (void) alarm(0); 252 if (port != 0) { 253 int lport = IPPORT_RESERVED - 1; 254 s = rresvport(&lport); 255 if (s < 0) { 256 syslog(LOG_ERR, "can't get stderr port: %m"); 257 exit(1); 258 } 259 #ifdef KERBEROS 260 if (!use_kerberos) 261 #endif 262 if (port >= IPPORT_RESERVED) { 263 syslog(LOG_ERR, "2nd port not reserved\n"); 264 exit(1); 265 } 266 fromp->sin_port = htons((u_short)port); 267 if (connect(s, fromp, sizeof (*fromp)) < 0) { 268 syslog(LOG_INFO, "connect second port: %m"); 269 exit(1); 270 } 271 } 272 273 #ifdef KERBEROS 274 if (vacuous) { 275 error("rshd: remote host requires Kerberos authentication\n"); 276 exit(1); 277 } 278 #endif 279 280 #ifdef notdef 281 /* from inetd, socket is already on 0, 1, 2 */ 282 dup2(f, 0); 283 dup2(f, 1); 284 dup2(f, 2); 285 #endif 286 hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr), 287 fromp->sin_family); 288 if (hp) { 289 /* 290 * If name returned by gethostbyaddr is in our domain, 291 * attempt to verify that we haven't been fooled by someone 292 * in a remote net; look up the name and check that this 293 * address corresponds to the name. 294 */ 295 if (local_domain(hp->h_name)) { 296 strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1); 297 remotehost[sizeof(remotehost) - 1] = 0; 298 hp = gethostbyname(remotehost); 299 if (hp == NULL) { 300 syslog(LOG_INFO, 301 "Couldn't look up address for %s", 302 remotehost); 303 error("Couldn't look up address for your host"); 304 exit(1); 305 } else for (; ; hp->h_addr_list++) { 306 if (!bcmp(hp->h_addr_list[0], 307 (caddr_t)&fromp->sin_addr, 308 sizeof(fromp->sin_addr))) 309 break; 310 if (hp->h_addr_list[0] == NULL) { 311 syslog(LOG_NOTICE, 312 "Host addr %s not listed for host %s", 313 inet_ntoa(fromp->sin_addr), 314 hp->h_name); 315 error("Host address mismatch"); 316 exit(1); 317 } 318 } 319 } 320 hostname = hp->h_name; 321 } else 322 hostname = inet_ntoa(fromp->sin_addr); 323 324 #ifdef KERBEROS 325 if (use_kerberos) { 326 h_name = strsave(hp->h_name); 327 kdata = (AUTH_DAT *) authbuf; 328 ticket = (KTEXT) tickbuf; 329 authopts = 0L; 330 strcpy(instance, "*"); 331 version[VERSION_SIZE - 1] = '\0'; 332 if (rc = krb_recvauth(authopts, 0, ticket, "rcmd", 333 instance, &fromaddr, 334 (struct sockaddr_in *) 0, 335 kdata, "", (bit_64 *) 0, version)) { 336 fprintf(stderr, 337 "Kerberos authentication failure: %s\r\n", 338 krb_err_txt[rc]); 339 exit(1); 340 } 341 free(h_name); 342 h_name = NULL; 343 } else 344 #endif 345 getstr(remuser, sizeof(remuser), "remuser"); 346 347 getstr(locuser, sizeof(locuser), "locuser"); 348 getstr(cmdbuf, sizeof(cmdbuf), "command"); 349 setpwent(); 350 pwd = getpwnam(locuser); 351 if (pwd == NULL) { 352 error("Login incorrect.\n"); 353 exit(1); 354 } 355 if (chdir(pwd->pw_dir) < 0) { 356 (void) chdir("/"); 357 #ifdef notdef 358 error("No remote directory.\n"); 359 exit(1); 360 #endif 361 } 362 363 #ifdef KERBEROS 364 if (use_kerberos) { 365 if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0') { 366 if (kuserok(kdata, locuser) != 0) { 367 syslog(LOG_NOTICE, "Kerberos rsh denied to %s.%s@%s", 368 kdata->pname, kdata->pinst, kdata->prealm); 369 error("Permission denied.\n"); 370 exit(1); 371 } 372 } 373 } else 374 #endif 375 376 if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && 377 ruserok(hostname, pwd->pw_uid == 0, remuser, locuser) < 0) { 378 error("Permission denied.\n"); 379 exit(1); 380 } 381 382 if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) { 383 error("Logins currently disabled.\n"); 384 exit(1); 385 } 386 387 (void) write(2, "\0", 1); 388 389 if (port) { 390 if (pipe(pv) < 0) { 391 error("Can't make pipe.\n"); 392 exit(1); 393 } 394 pid = fork(); 395 if (pid == -1) { 396 error("Try again.\n"); 397 exit(1); 398 } 399 if (pv[0] > s) 400 nfd = pv[0]; 401 else 402 nfd = s; 403 nfd++; 404 if (pid) { 405 (void) close(0); (void) close(1); (void) close(2); 406 (void) close(pv[1]); 407 FD_ZERO(&readfrom); 408 FD_SET(s, &readfrom); 409 FD_SET(pv[0], &readfrom); 410 ioctl(pv[0], FIONBIO, (char *)&one); 411 /* should set s nbio! */ 412 do { 413 ready = readfrom; 414 if (select(nfd, &ready, (fd_set *)0, 415 (fd_set *)0, (struct timeval *)0) < 0) 416 break; 417 if (FD_ISSET(s, &ready)) { 418 if (read(s, &sig, 1) <= 0) 419 FD_CLR(s, &readfrom); 420 else 421 killpg(pid, sig); 422 } 423 if (FD_ISSET(pv[0], &ready)) { 424 errno = 0; 425 cc = read(pv[0], buf, sizeof (buf)); 426 if (cc <= 0) { 427 shutdown(s, 1+1); 428 FD_CLR(pv[0], &readfrom); 429 } else 430 (void) write(s, buf, cc); 431 } 432 } while (FD_ISSET(s, &readfrom) || 433 FD_ISSET(pv[0], &readfrom)); 434 exit(0); 435 } 436 setpgrp(0, getpid()); 437 (void) close(s); (void) close(pv[0]); 438 dup2(pv[1], 2); 439 close(pv[1]); 440 } 441 if (*pwd->pw_shell == '\0') 442 pwd->pw_shell = _PATH_BSHELL; 443 (void) setgid((gid_t)pwd->pw_gid); 444 initgroups(pwd->pw_name, pwd->pw_gid); 445 if (setlogname(pwd->pw_name, strlen(pwd->pw_name)) < 0) 446 syslog(LOG_NOTICE, "setlogname() failed: %m"); 447 (void) setuid((uid_t)pwd->pw_uid); 448 environ = envinit; 449 strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); 450 strncat(shell, pwd->pw_shell, sizeof(shell)-7); 451 strncat(username, pwd->pw_name, sizeof(username)-6); 452 cp = rindex(pwd->pw_shell, '/'); 453 if (cp) 454 cp++; 455 else 456 cp = pwd->pw_shell; 457 endpwent(); 458 if (!pwd->pw_uid) 459 syslog(LOG_NOTICE, "ROOT shell from %s@%s, comm: %s\n", 460 remuser, hostname, cmdbuf); 461 execl(pwd->pw_shell, cp, "-c", cmdbuf, 0); 462 perror(pwd->pw_shell); 463 exit(1); 464 } 465 466 /*VARARGS1*/ 467 error(fmt, a1, a2, a3) 468 char *fmt; 469 int a1, a2, a3; 470 { 471 char buf[BUFSIZ]; 472 473 buf[0] = 1; 474 (void) sprintf(buf+1, fmt, a1, a2, a3); 475 (void) write(2, buf, strlen(buf)); 476 } 477 478 getstr(buf, cnt, err) 479 char *buf; 480 int cnt; 481 char *err; 482 { 483 char c; 484 485 do { 486 if (read(0, &c, 1) != 1) 487 exit(1); 488 *buf++ = c; 489 if (--cnt == 0) { 490 error("%s too long\n", err); 491 exit(1); 492 } 493 } while (c != 0); 494 } 495 496 /* 497 * Check whether host h is in our local domain, 498 * as determined by the part of the name following 499 * the first '.' in its name and in ours. 500 * If either name is unqualified (contains no '.'), 501 * assume that the host is local, as it will be 502 * interpreted as such. 503 */ 504 local_domain(h) 505 char *h; 506 { 507 char localhost[MAXHOSTNAMELEN]; 508 char *p1, *p2 = index(h, '.'); 509 510 (void) gethostname(localhost, sizeof(localhost)); 511 p1 = index(localhost, '.'); 512 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 513 return(1); 514 return(0); 515 } 516 517 usage() 518 { 519 #ifdef KERBEROS 520 syslog(LOG_ERR, "usage: rshd [-ln]"); 521 #else 522 syslog(LOG_ERR, "usage: rshd [-lknvx]"); 523 #endif 524 } 525