1 /* $NetBSD: rshd.c,v 1.33 2004/11/16 06:04:13 itojun Exp $ */ 2 3 /* 4 * Copyright (C) 1998 WIDE Project. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by WIDE Project and 18 * its contributors. 19 * 4. Neither the name of the project nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 /*- 37 * Copyright (c) 1988, 1989, 1992, 1993, 1994 38 * The Regents of the University of California. All rights reserved. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 3. Neither the name of the University nor the names of its contributors 49 * may be used to endorse or promote products derived from this software 50 * without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 * SUCH DAMAGE. 63 */ 64 65 #include <sys/cdefs.h> 66 #ifndef lint 67 __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\ 68 The Regents of the University of California. All rights reserved.\n"); 69 #if 0 70 static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94"; 71 #else 72 __RCSID("$NetBSD: rshd.c,v 1.33 2004/11/16 06:04:13 itojun Exp $"); 73 #endif 74 #endif /* not lint */ 75 76 /* 77 * remote shell server: 78 * [port]\0 79 * remuser\0 80 * locuser\0 81 * command\0 82 * data 83 */ 84 #include <sys/param.h> 85 #include <sys/ioctl.h> 86 #include <sys/time.h> 87 #include <sys/socket.h> 88 89 #include <netinet/in.h> 90 #include <netinet/tcp.h> 91 #include <arpa/inet.h> 92 #include <netdb.h> 93 94 #include <errno.h> 95 #include <fcntl.h> 96 #include <paths.h> 97 #include <pwd.h> 98 #include <signal.h> 99 #include <stdio.h> 100 #include <stdlib.h> 101 #include <string.h> 102 #include <syslog.h> 103 #include <unistd.h> 104 #include <poll.h> 105 #ifdef LOGIN_CAP 106 #include <login_cap.h> 107 #endif 108 109 int keepalive = 1; 110 int check_all; 111 int log_success; /* If TRUE, log all successful accesses */ 112 int sent_null; 113 114 void doit __P((struct sockaddr *)); 115 void error __P((const char *, ...)) 116 __attribute__((__format__(__printf__, 1, 2))); 117 void getstr __P((char *, int, char *)); 118 int local_domain __P((char *)); 119 char *topdomain __P((char *)); 120 void usage __P((void)); 121 int main __P((int, char *[])); 122 123 #define OPTIONS "alnL" 124 extern int __check_rhosts_file; 125 extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ 126 127 int 128 main(int argc, char *argv[]) 129 { 130 struct linger linger; 131 int ch, on = 1, fromlen; 132 struct sockaddr_storage from; 133 struct protoent *proto; 134 135 openlog("rshd", LOG_PID, LOG_DAEMON); 136 137 opterr = 0; 138 while ((ch = getopt(argc, argv, OPTIONS)) != -1) 139 switch (ch) { 140 case 'a': 141 check_all = 1; 142 break; 143 case 'l': 144 __check_rhosts_file = 0; 145 break; 146 case 'n': 147 keepalive = 0; 148 break; 149 case 'L': 150 log_success = 1; 151 break; 152 case '?': 153 default: 154 usage(); 155 break; 156 } 157 158 argc -= optind; 159 argv += optind; 160 161 fromlen = sizeof (from); /* xxx */ 162 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 163 syslog(LOG_ERR, "getpeername: %m"); 164 exit(1); 165 } 166 #if 0 167 if (((struct sockaddr *)&from)->sa_family == AF_INET6 && 168 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr) && 169 sizeof(struct sockaddr_in) <= sizeof(from)) { 170 struct sockaddr_in sin; 171 struct sockaddr_in6 *sin6; 172 const int off = sizeof(struct sockaddr_in6) - 173 sizeof(struct sockaddr_in); 174 175 sin6 = (struct sockaddr_in6 *)&from; 176 memset(&sin, 0, sizeof(sin)); 177 sin.sin_family = AF_INET; 178 sin.sin_len = sizeof(struct sockaddr_in); 179 memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[off], 180 sizeof(sin.sin_addr)); 181 memcpy(&from, &sin, sizeof(sin)); 182 fromlen = sin.sin_len; 183 } 184 #else 185 if (((struct sockaddr *)&from)->sa_family == AF_INET6 && 186 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) { 187 char hbuf[NI_MAXHOST]; 188 if (getnameinfo((struct sockaddr *)&from, fromlen, hbuf, 189 sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) { 190 strlcpy(hbuf, "invalid", sizeof(hbuf)); 191 } 192 syslog(LOG_ERR, "malformed \"from\" address (v4 mapped, %s)", 193 hbuf); 194 exit(1); 195 } 196 #endif 197 if (keepalive && 198 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, 199 sizeof(on)) < 0) 200 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 201 linger.l_onoff = 1; 202 linger.l_linger = 60; /* XXX */ 203 if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger, 204 sizeof (linger)) < 0) 205 syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); 206 proto = getprotobyname("tcp"); 207 setsockopt(0, proto->p_proto, TCP_NODELAY, &on, sizeof(on)); 208 doit((struct sockaddr *)&from); 209 /* NOTREACHED */ 210 #ifdef __GNUC__ 211 exit(0); 212 #endif 213 } 214 215 char username[20] = "USER="; 216 char homedir[64] = "HOME="; 217 char shell[64] = "SHELL="; 218 char path[100] = "PATH="; 219 char *envinit[] = 220 {homedir, shell, path, username, 0}; 221 char **environ; 222 223 void 224 doit(struct sockaddr *fromp) 225 { 226 struct passwd *pwd; 227 in_port_t port; 228 struct pollfd set[2]; 229 int cc, pv[2], pid, s = -1; /* XXX gcc */ 230 int one = 1; 231 char *hostname, *errorstr, *errorhost = NULL; /* XXX gcc */ 232 const char *cp; 233 char sig, buf[BUFSIZ]; 234 char cmdbuf[NCARGS+1], locuser[16], remuser[16]; 235 char remotehost[2 * MAXHOSTNAMELEN + 1]; 236 char hostnamebuf[2 * MAXHOSTNAMELEN + 1]; 237 #ifdef LOGIN_CAP 238 login_cap_t *lc; 239 #endif 240 char naddr[NI_MAXHOST]; 241 char saddr[NI_MAXHOST]; 242 char raddr[NI_MAXHOST]; 243 char pbuf[NI_MAXSERV]; 244 int af = fromp->sa_family; 245 u_int16_t *portp; 246 struct addrinfo hints, *res, *res0; 247 int gaierror; 248 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; 249 250 (void) signal(SIGINT, SIG_DFL); 251 (void) signal(SIGQUIT, SIG_DFL); 252 (void) signal(SIGTERM, SIG_DFL); 253 #ifdef DEBUG 254 { int t = open(_PATH_TTY, 2); 255 if (t >= 0) { 256 ioctl(t, TIOCNOTTY, (char *)0); 257 (void) close(t); 258 } 259 } 260 #endif 261 switch (af) { 262 case AF_INET: 263 portp = &((struct sockaddr_in *)fromp)->sin_port; 264 break; 265 #ifdef INET6 266 case AF_INET6: 267 portp = &((struct sockaddr_in6 *)fromp)->sin6_port; 268 break; 269 #endif 270 default: 271 syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); 272 exit(1); 273 } 274 if (getnameinfo(fromp, fromp->sa_len, naddr, sizeof(naddr), 275 pbuf, sizeof(pbuf), niflags) != 0) { 276 syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); 277 exit(1); 278 } 279 #ifdef IP_OPTIONS 280 if (af == AF_INET) 281 { 282 u_char optbuf[BUFSIZ/3], *cp; 283 char lbuf[BUFSIZ], *lp, *ep; 284 int optsize = sizeof(optbuf), ipproto; 285 struct protoent *ip; 286 287 if ((ip = getprotobyname("ip")) != NULL) 288 ipproto = ip->p_proto; 289 else 290 ipproto = IPPROTO_IP; 291 if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && 292 optsize != 0) { 293 lp = lbuf; 294 ep = lbuf + sizeof(lbuf); 295 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) 296 snprintf(lp, ep - lp, " %2.2x", *cp); 297 syslog(LOG_NOTICE, 298 "Connection received from %s using IP options (ignored):%s", 299 naddr, lbuf); 300 if (setsockopt(0, ipproto, IP_OPTIONS, 301 (char *)NULL, optsize) != 0) { 302 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); 303 exit(1); 304 } 305 } 306 } 307 #endif 308 309 if (ntohs(*portp) >= IPPORT_RESERVED 310 || ntohs(*portp) < IPPORT_RESERVED/2) { 311 syslog(LOG_NOTICE|LOG_AUTH, 312 "Connection from %s on illegal port %u", 313 naddr, ntohs(*portp)); 314 exit(1); 315 } 316 317 (void) alarm(60); 318 port = 0; 319 for (;;) { 320 char c; 321 322 if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { 323 if (cc < 0) 324 syslog(LOG_ERR, "read: %m"); 325 shutdown(0, SHUT_RDWR); 326 exit(1); 327 } 328 if (c == 0) 329 break; 330 port = port * 10 + c - '0'; 331 } 332 333 (void) alarm(0); 334 if (port != 0) { 335 int lport = IPPORT_RESERVED - 1; 336 s = rresvport_af(&lport, af); 337 if (s < 0) { 338 syslog(LOG_ERR, "can't get stderr port: %m"); 339 exit(1); 340 } 341 if (port >= IPPORT_RESERVED) { 342 syslog(LOG_ERR, "2nd port not reserved"); 343 exit(1); 344 } 345 *portp = htons(port); 346 if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 0) { 347 syslog(LOG_ERR, "connect second port %d: %m", port); 348 exit(1); 349 } 350 } 351 352 353 #ifdef notdef 354 /* from inetd, socket is already on 0, 1, 2 */ 355 dup2(f, 0); 356 dup2(f, 1); 357 dup2(f, 2); 358 #endif 359 errorstr = NULL; 360 if (getnameinfo(fromp, fromp->sa_len, saddr, sizeof(saddr), 361 NULL, 0, NI_NAMEREQD) == 0) { 362 /* 363 * If name returned by getnameinfo is in our domain, 364 * attempt to verify that we haven't been fooled by someone 365 * in a remote net; look up the name and check that this 366 * address corresponds to the name. 367 */ 368 hostname = saddr; 369 res0 = NULL; 370 if (check_all || local_domain(saddr)) { 371 strlcpy(remotehost, saddr, sizeof(remotehost)); 372 errorhost = remotehost; 373 memset(&hints, 0, sizeof(hints)); 374 hints.ai_family = fromp->sa_family; 375 hints.ai_socktype = SOCK_STREAM; 376 hints.ai_flags = AI_CANONNAME; 377 gaierror = getaddrinfo(remotehost, pbuf, &hints, &res0); 378 if (gaierror) { 379 syslog(LOG_NOTICE, 380 "Couldn't look up address for %s: %s", 381 remotehost, gai_strerror(gaierror)); 382 errorstr = 383 "Couldn't look up address for your host (%s)\n"; 384 hostname = naddr; 385 } else { 386 for (res = res0; res; res = res->ai_next) { 387 if (res->ai_family != fromp->sa_family) 388 continue; 389 if (res->ai_addrlen != fromp->sa_len) 390 continue; 391 if (getnameinfo(res->ai_addr, 392 res->ai_addrlen, 393 raddr, sizeof(raddr), NULL, 0, 394 niflags) == 0 395 && strcmp(naddr, raddr) == 0) { 396 hostname = res->ai_canonname 397 ? res->ai_canonname 398 : saddr; 399 break; 400 } 401 } 402 if (res == NULL) { 403 syslog(LOG_NOTICE, 404 "Host addr %s not listed for host %s", 405 naddr, res0->ai_canonname 406 ? res0->ai_canonname 407 : saddr); 408 errorstr = 409 "Host address mismatch for %s\n"; 410 hostname = naddr; 411 } 412 } 413 } 414 strlcpy(hostnamebuf, hostname, sizeof(hostnamebuf)); 415 hostname = hostnamebuf; 416 if (res0) 417 freeaddrinfo(res0); 418 } else { 419 strlcpy(hostnamebuf, naddr, sizeof(hostnamebuf)); 420 errorhost = hostname = hostnamebuf; 421 } 422 423 getstr(remuser, sizeof(remuser), "remuser"); 424 getstr(locuser, sizeof(locuser), "locuser"); 425 getstr(cmdbuf, sizeof(cmdbuf), "command"); 426 setpwent(); 427 pwd = getpwnam(locuser); 428 if (pwd == NULL) { 429 syslog(LOG_INFO|LOG_AUTH, 430 "%s@%s as %s: unknown login. cmd='%.80s'", 431 remuser, hostname, locuser, cmdbuf); 432 if (errorstr == NULL) 433 errorstr = "Permission denied.\n"; 434 goto fail; 435 } 436 #ifdef LOGIN_CAP 437 lc = login_getclass(pwd ? pwd->pw_class : NULL); 438 #endif 439 440 if (chdir(pwd->pw_dir) < 0) { 441 #ifdef LOGIN_CAP 442 if (chdir("/") < 0 || 443 login_getcapbool(lc, "requirehome", pwd->pw_uid ? 1 : 0)) { 444 syslog(LOG_INFO|LOG_AUTH, 445 "%s@%s as %s: no home directory. cmd='%.80s'", 446 remuser, hostname, locuser, cmdbuf); 447 error("No remote home directory.\n"); 448 exit(0); 449 } 450 #else 451 (void) chdir("/"); 452 #ifdef notdef 453 syslog(LOG_INFO|LOG_AUTH, 454 "%s@%s as %s: no home directory. cmd='%.80s'", 455 remuser, hostname, locuser, cmdbuf); 456 error("No remote directory.\n"); 457 exit(1); 458 #endif /* notdef */ 459 #endif /* LOGIN_CAP */ 460 } 461 462 463 if (errorstr || 464 (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && 465 iruserok_sa(fromp, fromp->sa_len, pwd->pw_uid == 0, remuser, 466 locuser) < 0)) { 467 if (__rcmd_errstr) 468 syslog(LOG_INFO|LOG_AUTH, 469 "%s@%s as %s: permission denied (%s). cmd='%.80s'", 470 remuser, hostname, locuser, __rcmd_errstr, 471 cmdbuf); 472 else 473 syslog(LOG_INFO|LOG_AUTH, 474 "%s@%s as %s: permission denied. cmd='%.80s'", 475 remuser, hostname, locuser, cmdbuf); 476 fail: 477 if (errorstr == NULL) 478 errorstr = "Permission denied.\n"; 479 error(errorstr, errorhost); 480 exit(1); 481 } 482 483 if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) { 484 error("Logins currently disabled.\n"); 485 exit(1); 486 } 487 488 (void) write(STDERR_FILENO, "\0", 1); 489 sent_null = 1; 490 491 if (port) { 492 if (pipe(pv) < 0) { 493 error("Can't make pipe.\n"); 494 exit(1); 495 } 496 pid = fork(); 497 if (pid == -1) { 498 error("Can't fork; try again.\n"); 499 exit(1); 500 } 501 if (pid) { 502 { 503 (void) close(0); 504 (void) close(1); 505 } 506 (void) close(2); 507 (void) close(pv[1]); 508 509 set[0].fd = s; 510 set[0].events = POLLIN; 511 set[1].fd = pv[0]; 512 set[1].events = POLLIN; 513 ioctl(pv[0], FIONBIO, (char *)&one); 514 515 /* should set s nbio! */ 516 do { 517 if (poll(set, 2, INFTIM) < 0) 518 break; 519 if (set[0].revents & POLLIN) { 520 int ret; 521 522 ret = read(s, &sig, 1); 523 if (ret <= 0) 524 set[0].events = 0; 525 else 526 killpg(pid, sig); 527 } 528 if (set[1].revents & POLLIN) { 529 errno = 0; 530 cc = read(pv[0], buf, sizeof(buf)); 531 if (cc <= 0) { 532 shutdown(s, SHUT_RDWR); 533 set[1].events = 0; 534 } else { 535 (void) write(s, buf, cc); 536 } 537 } 538 539 } while ((set[0].revents | set[1].revents) & POLLIN); 540 exit(0); 541 } 542 (void) close(s); 543 (void) close(pv[0]); 544 dup2(pv[1], 2); 545 close(pv[1]); 546 } 547 setsid(); 548 549 if (*pwd->pw_shell == '\0') 550 pwd->pw_shell = _PATH_BSHELL; 551 #ifdef LOGIN_CAP 552 { 553 char *sh; 554 555 if((sh = login_getcapstr(lc, "shell", NULL, NULL))) { 556 if(!(sh = strdup(sh))) { 557 syslog(LOG_ERR, "Cannot alloc mem"); 558 exit(1); 559 } 560 pwd->pw_shell = sh; 561 } 562 } 563 #endif 564 environ = envinit; 565 strlcat(homedir, pwd->pw_dir, sizeof(homedir)); 566 strlcat(path, _PATH_DEFPATH, sizeof(path)); 567 strlcat(shell, pwd->pw_shell, sizeof(shell)); 568 strlcat(username, pwd->pw_name, sizeof(username)); 569 #ifdef LOGIN_CAP 570 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) { 571 syslog(LOG_ERR, "setusercontext: %m"); 572 exit(1); 573 } 574 login_close(lc); 575 #else 576 (void) setgid((gid_t)pwd->pw_gid); 577 initgroups(pwd->pw_name, pwd->pw_gid); 578 (void) setuid((uid_t)pwd->pw_uid); 579 #endif 580 581 endpwent(); 582 if (log_success || pwd->pw_uid == 0) { 583 syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", 584 remuser, hostname, locuser, cmdbuf); 585 } 586 cp = strrchr(pwd->pw_shell, '/'); 587 if (cp) 588 cp++; 589 else 590 cp = pwd->pw_shell; 591 execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL); 592 perror(pwd->pw_shell); 593 exit(1); 594 } 595 596 /* 597 * Report error to client. Note: can't be used until second socket has 598 * connected to client, or older clients will hang waiting for that 599 * connection first. 600 */ 601 602 #include <stdarg.h> 603 604 void error(const char *fmt, ...) 605 { 606 va_list ap; 607 int len; 608 char *bp, buf[BUFSIZ]; 609 va_start(ap, fmt); 610 bp = buf; 611 if (sent_null == 0) { 612 *bp++ = 1; 613 len = 1; 614 } else 615 len = 0; 616 (void)vsnprintf(bp, sizeof(buf) - 1, fmt, ap); 617 (void)write(STDERR_FILENO, buf, len + strlen(bp)); 618 va_end(ap); 619 } 620 621 void 622 getstr(char *buf, int cnt, char *err) 623 { 624 char c; 625 626 do { 627 if (read(STDIN_FILENO, &c, 1) != 1) 628 exit(1); 629 *buf++ = c; 630 if (--cnt == 0) { 631 error("%s too long\n", err); 632 exit(1); 633 } 634 } while (c != 0); 635 } 636 637 /* 638 * Check whether host h is in our local domain, 639 * defined as sharing the last two components of the domain part, 640 * or the entire domain part if the local domain has only one component. 641 * If either name is unqualified (contains no '.'), 642 * assume that the host is local, as it will be 643 * interpreted as such. 644 */ 645 int 646 local_domain(char *h) 647 { 648 char localhost[MAXHOSTNAMELEN + 1]; 649 char *p1, *p2; 650 651 localhost[0] = 0; 652 (void)gethostname(localhost, sizeof(localhost)); 653 localhost[sizeof(localhost) - 1] = '\0'; 654 p1 = topdomain(localhost); 655 p2 = topdomain(h); 656 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 657 return (1); 658 return (0); 659 } 660 661 char * 662 topdomain(char *h) 663 { 664 char *p, *maybe = NULL; 665 int dots = 0; 666 667 for (p = h + strlen(h); p >= h; p--) { 668 if (*p == '.') { 669 if (++dots == 2) 670 return (p); 671 maybe = p; 672 } 673 } 674 return (maybe); 675 } 676 677 void 678 usage(void) 679 { 680 681 syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS); 682 exit(2); 683 } 684