1 /*- 2 * Copyright (c) 1988, 1989, 1992, 1993, 1994 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. 32 */ 33 34 #ifndef lint 35 static char copyright[] = 36 "@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 /* from: static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94"; */ 42 static char *rcsid = "$Id: rshd.c,v 1.9 1995/01/20 18:48:50 christos Exp $"; 43 #endif /* not lint */ 44 45 /* 46 * remote shell server: 47 * [port]\0 48 * remuser\0 49 * locuser\0 50 * command\0 51 * data 52 */ 53 #include <sys/param.h> 54 #include <sys/ioctl.h> 55 #include <sys/time.h> 56 #include <sys/socket.h> 57 58 #include <netinet/in.h> 59 #include <arpa/inet.h> 60 #include <netdb.h> 61 62 #include <errno.h> 63 #include <fcntl.h> 64 #include <paths.h> 65 #include <pwd.h> 66 #include <signal.h> 67 #include <stdio.h> 68 #include <stdlib.h> 69 #include <string.h> 70 #include <syslog.h> 71 #include <unistd.h> 72 73 int keepalive = 1; 74 int check_all; 75 int log_success; /* If TRUE, log all successful accesses */ 76 int sent_null; 77 78 void doit __P((struct sockaddr_in *)); 79 void error __P((const char *, ...)); 80 void getstr __P((char *, int, char *)); 81 int local_domain __P((char *)); 82 char *topdomain __P((char *)); 83 void usage __P((void)); 84 85 #define OPTIONS "alnL" 86 87 int 88 main(argc, argv) 89 int argc; 90 char *argv[]; 91 { 92 extern int __check_rhosts_file; 93 struct linger linger; 94 int ch, on = 1, fromlen; 95 struct sockaddr_in from; 96 97 openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 98 99 opterr = 0; 100 while ((ch = getopt(argc, argv, OPTIONS)) != EOF) 101 switch (ch) { 102 case 'a': 103 check_all = 1; 104 break; 105 case 'l': 106 __check_rhosts_file = 0; 107 break; 108 case 'n': 109 keepalive = 0; 110 break; 111 case 'L': 112 log_success = 1; 113 break; 114 case '?': 115 default: 116 usage(); 117 break; 118 } 119 120 argc -= optind; 121 argv += optind; 122 123 124 fromlen = sizeof (from); 125 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 126 syslog(LOG_ERR, "getpeername: %m"); 127 _exit(1); 128 } 129 if (keepalive && 130 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, 131 sizeof(on)) < 0) 132 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 133 linger.l_onoff = 1; 134 linger.l_linger = 60; /* XXX */ 135 if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger, 136 sizeof (linger)) < 0) 137 syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); 138 doit(&from); 139 /* NOTREACHED */ 140 } 141 142 char username[20] = "USER="; 143 char homedir[64] = "HOME="; 144 char shell[64] = "SHELL="; 145 char path[100] = "PATH="; 146 char *envinit[] = 147 {homedir, shell, path, username, 0}; 148 char **environ; 149 150 void 151 doit(fromp) 152 struct sockaddr_in *fromp; 153 { 154 extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ 155 struct hostent *hp; 156 struct passwd *pwd; 157 u_short port; 158 fd_set ready, readfrom; 159 int cc, nfd, pv[2], pid, s; 160 int one = 1; 161 char *hostname, *errorstr, *errorhost; 162 char *cp, sig, buf[BUFSIZ]; 163 char cmdbuf[NCARGS+1], locuser[16], remuser[16]; 164 char remotehost[2 * MAXHOSTNAMELEN + 1]; 165 char hostnamebuf[2 * MAXHOSTNAMELEN + 1]; 166 167 168 (void) signal(SIGINT, SIG_DFL); 169 (void) signal(SIGQUIT, SIG_DFL); 170 (void) signal(SIGTERM, SIG_DFL); 171 #ifdef DEBUG 172 { int t = open(_PATH_TTY, 2); 173 if (t >= 0) { 174 ioctl(t, TIOCNOTTY, (char *)0); 175 (void) close(t); 176 } 177 } 178 #endif 179 fromp->sin_port = ntohs((u_short)fromp->sin_port); 180 if (fromp->sin_family != AF_INET) { 181 syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", 182 fromp->sin_family); 183 exit(1); 184 } 185 #ifdef IP_OPTIONS 186 { 187 u_char optbuf[BUFSIZ/3], *cp; 188 char lbuf[BUFSIZ], *lp; 189 int optsize = sizeof(optbuf), ipproto; 190 struct protoent *ip; 191 192 if ((ip = getprotobyname("ip")) != NULL) 193 ipproto = ip->p_proto; 194 else 195 ipproto = IPPROTO_IP; 196 if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && 197 optsize != 0) { 198 lp = lbuf; 199 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) 200 sprintf(lp, " %2.2x", *cp); 201 syslog(LOG_NOTICE, 202 "Connection received from %s using IP options (ignored):%s", 203 inet_ntoa(fromp->sin_addr), lbuf); 204 if (setsockopt(0, ipproto, IP_OPTIONS, 205 (char *)NULL, optsize) != 0) { 206 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); 207 exit(1); 208 } 209 } 210 } 211 #endif 212 213 if (fromp->sin_port >= IPPORT_RESERVED || 214 fromp->sin_port < IPPORT_RESERVED/2) { 215 syslog(LOG_NOTICE|LOG_AUTH, 216 "Connection from %s on illegal port %u", 217 inet_ntoa(fromp->sin_addr), 218 fromp->sin_port); 219 exit(1); 220 } 221 222 (void) alarm(60); 223 port = 0; 224 for (;;) { 225 char c; 226 if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { 227 if (cc < 0) 228 syslog(LOG_NOTICE, "read: %m"); 229 shutdown(0, 1+1); 230 exit(1); 231 } 232 if (c== 0) 233 break; 234 port = port * 10 + c - '0'; 235 } 236 237 (void) alarm(0); 238 if (port != 0) { 239 int lport = IPPORT_RESERVED - 1; 240 s = rresvport(&lport); 241 if (s < 0) { 242 syslog(LOG_ERR, "can't get stderr port: %m"); 243 exit(1); 244 } 245 if (port >= IPPORT_RESERVED) { 246 syslog(LOG_ERR, "2nd port not reserved\n"); 247 exit(1); 248 } 249 fromp->sin_port = htons(port); 250 if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) { 251 syslog(LOG_INFO, "connect second port %d: %m", port); 252 exit(1); 253 } 254 } 255 256 257 #ifdef notdef 258 /* from inetd, socket is already on 0, 1, 2 */ 259 dup2(f, 0); 260 dup2(f, 1); 261 dup2(f, 2); 262 #endif 263 errorstr = NULL; 264 hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr), 265 fromp->sin_family); 266 if (hp) { 267 /* 268 * If name returned by gethostbyaddr is in our domain, 269 * attempt to verify that we haven't been fooled by someone 270 * in a remote net; look up the name and check that this 271 * address corresponds to the name. 272 */ 273 hostname = hp->h_name; 274 if (check_all || local_domain(hp->h_name)) { 275 strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1); 276 remotehost[sizeof(remotehost) - 1] = 0; 277 errorhost = remotehost; 278 hp = gethostbyname(remotehost); 279 if (hp == NULL) { 280 syslog(LOG_INFO, 281 "Couldn't look up address for %s", 282 remotehost); 283 errorstr = 284 "Couldn't look up address for your host (%s)\n"; 285 hostname = inet_ntoa(fromp->sin_addr); 286 } else for (; ; hp->h_addr_list++) { 287 if (hp->h_addr_list[0] == NULL) { 288 syslog(LOG_NOTICE, 289 "Host addr %s not listed for host %s", 290 inet_ntoa(fromp->sin_addr), 291 hp->h_name); 292 errorstr = 293 "Host address mismatch for %s\n"; 294 hostname = inet_ntoa(fromp->sin_addr); 295 break; 296 } 297 if (!bcmp(hp->h_addr_list[0], 298 (caddr_t)&fromp->sin_addr, 299 sizeof(fromp->sin_addr))) { 300 hostname = hp->h_name; 301 break; 302 } 303 } 304 } 305 hostname = strncpy(hostnamebuf, hostname, 306 sizeof(hostnamebuf) - 1); 307 } else 308 errorhost = hostname = strncpy(hostnamebuf, 309 inet_ntoa(fromp->sin_addr), 310 sizeof(hostnamebuf) - 1); 311 312 hostnamebuf[sizeof(hostnamebuf) - 1] = '\0'; 313 314 getstr(remuser, sizeof(remuser), "remuser"); 315 getstr(locuser, sizeof(locuser), "locuser"); 316 getstr(cmdbuf, sizeof(cmdbuf), "command"); 317 setpwent(); 318 pwd = getpwnam(locuser); 319 if (pwd == NULL) { 320 syslog(LOG_INFO|LOG_AUTH, 321 "%s@%s as %s: unknown login. cmd='%.80s'", 322 remuser, hostname, locuser, cmdbuf); 323 if (errorstr == NULL) 324 errorstr = "Login incorrect.\n"; 325 goto fail; 326 } 327 if (chdir(pwd->pw_dir) < 0) { 328 (void) chdir("/"); 329 #ifdef notdef 330 syslog(LOG_INFO|LOG_AUTH, 331 "%s@%s as %s: no home directory. cmd='%.80s'", 332 remuser, hostname, locuser, cmdbuf); 333 error("No remote directory.\n"); 334 exit(1); 335 #endif 336 } 337 338 339 if (errorstr || 340 pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && 341 iruserok(fromp->sin_addr.s_addr, pwd->pw_uid == 0, 342 remuser, locuser) < 0) { 343 if (__rcmd_errstr) 344 syslog(LOG_INFO|LOG_AUTH, 345 "%s@%s as %s: permission denied (%s). cmd='%.80s'", 346 remuser, hostname, locuser, __rcmd_errstr, 347 cmdbuf); 348 else 349 syslog(LOG_INFO|LOG_AUTH, 350 "%s@%s as %s: permission denied. cmd='%.80s'", 351 remuser, hostname, locuser, cmdbuf); 352 fail: 353 if (errorstr == NULL) 354 errorstr = "Permission denied.\n"; 355 error(errorstr, errorhost); 356 exit(1); 357 } 358 359 if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) { 360 error("Logins currently disabled.\n"); 361 exit(1); 362 } 363 364 (void) write(STDERR_FILENO, "\0", 1); 365 sent_null = 1; 366 367 if (port) { 368 if (pipe(pv) < 0) { 369 error("Can't make pipe.\n"); 370 exit(1); 371 } 372 pid = fork(); 373 if (pid == -1) { 374 error("Can't fork; try again.\n"); 375 exit(1); 376 } 377 if (pid) { 378 { 379 (void) close(0); 380 (void) close(1); 381 } 382 (void) close(2); 383 (void) close(pv[1]); 384 385 FD_ZERO(&readfrom); 386 FD_SET(s, &readfrom); 387 FD_SET(pv[0], &readfrom); 388 if (pv[0] > s) 389 nfd = pv[0]; 390 else 391 nfd = s; 392 ioctl(pv[0], FIONBIO, (char *)&one); 393 394 /* should set s nbio! */ 395 nfd++; 396 do { 397 ready = readfrom; 398 if (select(nfd, &ready, (fd_set *)0, 399 (fd_set *)0, (struct timeval *)0) < 0) 400 break; 401 if (FD_ISSET(s, &ready)) { 402 int ret; 403 ret = read(s, &sig, 1); 404 if (ret <= 0) 405 FD_CLR(s, &readfrom); 406 else 407 killpg(pid, sig); 408 } 409 if (FD_ISSET(pv[0], &ready)) { 410 errno = 0; 411 cc = read(pv[0], buf, sizeof(buf)); 412 if (cc <= 0) { 413 shutdown(s, 1+1); 414 FD_CLR(pv[0], &readfrom); 415 } else { 416 (void) 417 write(s, buf, cc); 418 } 419 } 420 421 } while (FD_ISSET(s, &readfrom) || 422 FD_ISSET(pv[0], &readfrom)); 423 exit(0); 424 } 425 setpgrp(0, getpid()); 426 (void) close(s); 427 (void) close(pv[0]); 428 dup2(pv[1], 2); 429 close(pv[1]); 430 } 431 if (*pwd->pw_shell == '\0') 432 pwd->pw_shell = _PATH_BSHELL; 433 #if BSD > 43 434 if (setlogin(pwd->pw_name) < 0) 435 syslog(LOG_ERR, "setlogin() failed: %m"); 436 #endif 437 (void) setgid((gid_t)pwd->pw_gid); 438 initgroups(pwd->pw_name, pwd->pw_gid); 439 (void) setuid((uid_t)pwd->pw_uid); 440 environ = envinit; 441 strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); 442 strcat(path, _PATH_DEFPATH); 443 strncat(shell, pwd->pw_shell, sizeof(shell)-7); 444 strncat(username, pwd->pw_name, sizeof(username)-6); 445 cp = strrchr(pwd->pw_shell, '/'); 446 if (cp) 447 cp++; 448 else 449 cp = pwd->pw_shell; 450 endpwent(); 451 if (log_success || pwd->pw_uid == 0) { 452 syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", 453 remuser, hostname, locuser, cmdbuf); 454 } 455 execl(pwd->pw_shell, cp, "-c", cmdbuf, 0); 456 perror(pwd->pw_shell); 457 exit(1); 458 } 459 460 /* 461 * Report error to client. Note: can't be used until second socket has 462 * connected to client, or older clients will hang waiting for that 463 * connection first. 464 */ 465 #if __STDC__ 466 #include <stdarg.h> 467 #else 468 #include <varargs.h> 469 #endif 470 471 void 472 #if __STDC__ 473 error(const char *fmt, ...) 474 #else 475 error(fmt, va_alist) 476 char *fmt; 477 va_dcl 478 #endif 479 { 480 va_list ap; 481 int len; 482 char *bp, buf[BUFSIZ]; 483 #if __STDC__ 484 va_start(ap, fmt); 485 #else 486 va_start(ap); 487 #endif 488 bp = buf; 489 if (sent_null == 0) { 490 *bp++ = 1; 491 len = 1; 492 } else 493 len = 0; 494 (void)vsnprintf(bp, sizeof(buf) - 1, fmt, ap); 495 (void)write(STDERR_FILENO, buf, len + strlen(bp)); 496 } 497 498 void 499 getstr(buf, cnt, err) 500 char *buf, *err; 501 int cnt; 502 { 503 char c; 504 505 do { 506 if (read(STDIN_FILENO, &c, 1) != 1) 507 exit(1); 508 *buf++ = c; 509 if (--cnt == 0) { 510 error("%s too long\n", err); 511 exit(1); 512 } 513 } while (c != 0); 514 } 515 516 /* 517 * Check whether host h is in our local domain, 518 * defined as sharing the last two components of the domain part, 519 * or the entire domain part if the local domain has only one component. 520 * If either name is unqualified (contains no '.'), 521 * assume that the host is local, as it will be 522 * interpreted as such. 523 */ 524 int 525 local_domain(h) 526 char *h; 527 { 528 char localhost[MAXHOSTNAMELEN]; 529 char *p1, *p2; 530 531 localhost[0] = 0; 532 (void) gethostname(localhost, sizeof(localhost)); 533 p1 = topdomain(localhost); 534 p2 = topdomain(h); 535 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 536 return (1); 537 return (0); 538 } 539 540 char * 541 topdomain(h) 542 char *h; 543 { 544 char *p, *maybe = NULL; 545 int dots = 0; 546 547 for (p = h + strlen(h); p >= h; p--) { 548 if (*p == '.') { 549 if (++dots == 2) 550 return (p); 551 maybe = p; 552 } 553 } 554 return (maybe); 555 } 556 557 void 558 usage() 559 { 560 561 syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS); 562 exit(2); 563 } 564