1 /* $NetBSD: rshd.c,v 1.32 2003/08/07 09:46:49 agc 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.32 2003/08/07 09:46:49 agc 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 #ifdef NI_WITHSCOPEID 249 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; 250 #else 251 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; 252 #endif 253 254 (void) signal(SIGINT, SIG_DFL); 255 (void) signal(SIGQUIT, SIG_DFL); 256 (void) signal(SIGTERM, SIG_DFL); 257 #ifdef DEBUG 258 { int t = open(_PATH_TTY, 2); 259 if (t >= 0) { 260 ioctl(t, TIOCNOTTY, (char *)0); 261 (void) close(t); 262 } 263 } 264 #endif 265 switch (af) { 266 case AF_INET: 267 portp = &((struct sockaddr_in *)fromp)->sin_port; 268 break; 269 #ifdef INET6 270 case AF_INET6: 271 portp = &((struct sockaddr_in6 *)fromp)->sin6_port; 272 break; 273 #endif 274 default: 275 syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); 276 exit(1); 277 } 278 if (getnameinfo(fromp, fromp->sa_len, naddr, sizeof(naddr), 279 pbuf, sizeof(pbuf), niflags) != 0) { 280 syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); 281 exit(1); 282 } 283 #ifdef IP_OPTIONS 284 if (af == AF_INET) 285 { 286 u_char optbuf[BUFSIZ/3], *cp; 287 char lbuf[BUFSIZ], *lp, *ep; 288 int optsize = sizeof(optbuf), ipproto; 289 struct protoent *ip; 290 291 if ((ip = getprotobyname("ip")) != NULL) 292 ipproto = ip->p_proto; 293 else 294 ipproto = IPPROTO_IP; 295 if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && 296 optsize != 0) { 297 lp = lbuf; 298 ep = lbuf + sizeof(lbuf); 299 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) 300 snprintf(lp, ep - lp, " %2.2x", *cp); 301 syslog(LOG_NOTICE, 302 "Connection received from %s using IP options (ignored):%s", 303 naddr, lbuf); 304 if (setsockopt(0, ipproto, IP_OPTIONS, 305 (char *)NULL, optsize) != 0) { 306 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); 307 exit(1); 308 } 309 } 310 } 311 #endif 312 313 if (ntohs(*portp) >= IPPORT_RESERVED 314 || ntohs(*portp) < IPPORT_RESERVED/2) { 315 syslog(LOG_NOTICE|LOG_AUTH, 316 "Connection from %s on illegal port %u", 317 naddr, ntohs(*portp)); 318 exit(1); 319 } 320 321 (void) alarm(60); 322 port = 0; 323 for (;;) { 324 char c; 325 326 if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { 327 if (cc < 0) 328 syslog(LOG_ERR, "read: %m"); 329 shutdown(0, SHUT_RDWR); 330 exit(1); 331 } 332 if (c == 0) 333 break; 334 port = port * 10 + c - '0'; 335 } 336 337 (void) alarm(0); 338 if (port != 0) { 339 int lport = IPPORT_RESERVED - 1; 340 s = rresvport_af(&lport, af); 341 if (s < 0) { 342 syslog(LOG_ERR, "can't get stderr port: %m"); 343 exit(1); 344 } 345 if (port >= IPPORT_RESERVED) { 346 syslog(LOG_ERR, "2nd port not reserved"); 347 exit(1); 348 } 349 *portp = htons(port); 350 if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 0) { 351 syslog(LOG_ERR, "connect second port %d: %m", port); 352 exit(1); 353 } 354 } 355 356 357 #ifdef notdef 358 /* from inetd, socket is already on 0, 1, 2 */ 359 dup2(f, 0); 360 dup2(f, 1); 361 dup2(f, 2); 362 #endif 363 errorstr = NULL; 364 if (getnameinfo(fromp, fromp->sa_len, saddr, sizeof(saddr), 365 NULL, 0, NI_NAMEREQD) == 0) { 366 /* 367 * If name returned by getnameinfo is in our domain, 368 * attempt to verify that we haven't been fooled by someone 369 * in a remote net; look up the name and check that this 370 * address corresponds to the name. 371 */ 372 hostname = saddr; 373 res0 = NULL; 374 if (check_all || local_domain(saddr)) { 375 strlcpy(remotehost, saddr, sizeof(remotehost)); 376 errorhost = remotehost; 377 memset(&hints, 0, sizeof(hints)); 378 hints.ai_family = fromp->sa_family; 379 hints.ai_socktype = SOCK_STREAM; 380 hints.ai_flags = AI_CANONNAME; 381 gaierror = getaddrinfo(remotehost, pbuf, &hints, &res0); 382 if (gaierror) { 383 syslog(LOG_NOTICE, 384 "Couldn't look up address for %s: %s", 385 remotehost, gai_strerror(gaierror)); 386 errorstr = 387 "Couldn't look up address for your host (%s)\n"; 388 hostname = naddr; 389 } else { 390 for (res = res0; res; res = res->ai_next) { 391 if (res->ai_family != fromp->sa_family) 392 continue; 393 if (res->ai_addrlen != fromp->sa_len) 394 continue; 395 if (getnameinfo(res->ai_addr, 396 res->ai_addrlen, 397 raddr, sizeof(raddr), NULL, 0, 398 niflags) == 0 399 && strcmp(naddr, raddr) == 0) { 400 hostname = res->ai_canonname 401 ? res->ai_canonname 402 : saddr; 403 break; 404 } 405 } 406 if (res == NULL) { 407 syslog(LOG_NOTICE, 408 "Host addr %s not listed for host %s", 409 naddr, res0->ai_canonname 410 ? res0->ai_canonname 411 : saddr); 412 errorstr = 413 "Host address mismatch for %s\n"; 414 hostname = naddr; 415 } 416 } 417 } 418 strlcpy(hostnamebuf, hostname, sizeof(hostnamebuf)); 419 hostname = hostnamebuf; 420 if (res0) 421 freeaddrinfo(res0); 422 } else { 423 strlcpy(hostnamebuf, naddr, sizeof(hostnamebuf)); 424 errorhost = hostname = hostnamebuf; 425 } 426 427 getstr(remuser, sizeof(remuser), "remuser"); 428 getstr(locuser, sizeof(locuser), "locuser"); 429 getstr(cmdbuf, sizeof(cmdbuf), "command"); 430 setpwent(); 431 pwd = getpwnam(locuser); 432 if (pwd == NULL) { 433 syslog(LOG_INFO|LOG_AUTH, 434 "%s@%s as %s: unknown login. cmd='%.80s'", 435 remuser, hostname, locuser, cmdbuf); 436 if (errorstr == NULL) 437 errorstr = "Permission denied.\n"; 438 goto fail; 439 } 440 #ifdef LOGIN_CAP 441 lc = login_getclass(pwd ? pwd->pw_class : NULL); 442 #endif 443 444 if (chdir(pwd->pw_dir) < 0) { 445 #ifdef LOGIN_CAP 446 if (chdir("/") < 0 || 447 login_getcapbool(lc, "requirehome", pwd->pw_uid ? 1 : 0)) { 448 syslog(LOG_INFO|LOG_AUTH, 449 "%s@%s as %s: no home directory. cmd='%.80s'", 450 remuser, hostname, locuser, cmdbuf); 451 error("No remote home directory.\n"); 452 exit(0); 453 } 454 #else 455 (void) chdir("/"); 456 #ifdef notdef 457 syslog(LOG_INFO|LOG_AUTH, 458 "%s@%s as %s: no home directory. cmd='%.80s'", 459 remuser, hostname, locuser, cmdbuf); 460 error("No remote directory.\n"); 461 exit(1); 462 #endif /* notdef */ 463 #endif /* LOGIN_CAP */ 464 } 465 466 467 if (errorstr || 468 (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && 469 iruserok_sa(fromp, fromp->sa_len, pwd->pw_uid == 0, remuser, 470 locuser) < 0)) { 471 if (__rcmd_errstr) 472 syslog(LOG_INFO|LOG_AUTH, 473 "%s@%s as %s: permission denied (%s). cmd='%.80s'", 474 remuser, hostname, locuser, __rcmd_errstr, 475 cmdbuf); 476 else 477 syslog(LOG_INFO|LOG_AUTH, 478 "%s@%s as %s: permission denied. cmd='%.80s'", 479 remuser, hostname, locuser, cmdbuf); 480 fail: 481 if (errorstr == NULL) 482 errorstr = "Permission denied.\n"; 483 error(errorstr, errorhost); 484 exit(1); 485 } 486 487 if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) { 488 error("Logins currently disabled.\n"); 489 exit(1); 490 } 491 492 (void) write(STDERR_FILENO, "\0", 1); 493 sent_null = 1; 494 495 if (port) { 496 if (pipe(pv) < 0) { 497 error("Can't make pipe.\n"); 498 exit(1); 499 } 500 pid = fork(); 501 if (pid == -1) { 502 error("Can't fork; try again.\n"); 503 exit(1); 504 } 505 if (pid) { 506 { 507 (void) close(0); 508 (void) close(1); 509 } 510 (void) close(2); 511 (void) close(pv[1]); 512 513 set[0].fd = s; 514 set[0].events = POLLIN; 515 set[1].fd = pv[0]; 516 set[1].events = POLLIN; 517 ioctl(pv[0], FIONBIO, (char *)&one); 518 519 /* should set s nbio! */ 520 do { 521 if (poll(set, 2, INFTIM) < 0) 522 break; 523 if (set[0].revents & POLLIN) { 524 int ret; 525 526 ret = read(s, &sig, 1); 527 if (ret <= 0) 528 set[0].events = 0; 529 else 530 killpg(pid, sig); 531 } 532 if (set[1].revents & POLLIN) { 533 errno = 0; 534 cc = read(pv[0], buf, sizeof(buf)); 535 if (cc <= 0) { 536 shutdown(s, SHUT_RDWR); 537 set[1].events = 0; 538 } else { 539 (void) write(s, buf, cc); 540 } 541 } 542 543 } while ((set[0].revents | set[1].revents) & POLLIN); 544 exit(0); 545 } 546 (void) close(s); 547 (void) close(pv[0]); 548 dup2(pv[1], 2); 549 close(pv[1]); 550 } 551 setsid(); 552 553 if (*pwd->pw_shell == '\0') 554 pwd->pw_shell = _PATH_BSHELL; 555 #ifdef LOGIN_CAP 556 { 557 char *sh; 558 559 if((sh = login_getcapstr(lc, "shell", NULL, NULL))) { 560 if(!(sh = strdup(sh))) { 561 syslog(LOG_ERR, "Cannot alloc mem"); 562 exit(1); 563 } 564 pwd->pw_shell = sh; 565 } 566 } 567 #endif 568 environ = envinit; 569 strlcat(homedir, pwd->pw_dir, sizeof(homedir)); 570 strlcat(path, _PATH_DEFPATH, sizeof(path)); 571 strlcat(shell, pwd->pw_shell, sizeof(shell)); 572 strlcat(username, pwd->pw_name, sizeof(username)); 573 #ifdef LOGIN_CAP 574 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) { 575 syslog(LOG_ERR, "setusercontext: %m"); 576 exit(1); 577 } 578 login_close(lc); 579 #else 580 (void) setgid((gid_t)pwd->pw_gid); 581 initgroups(pwd->pw_name, pwd->pw_gid); 582 (void) setuid((uid_t)pwd->pw_uid); 583 #endif 584 585 endpwent(); 586 if (log_success || pwd->pw_uid == 0) { 587 syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", 588 remuser, hostname, locuser, cmdbuf); 589 } 590 cp = strrchr(pwd->pw_shell, '/'); 591 if (cp) 592 cp++; 593 else 594 cp = pwd->pw_shell; 595 execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL); 596 perror(pwd->pw_shell); 597 exit(1); 598 } 599 600 /* 601 * Report error to client. Note: can't be used until second socket has 602 * connected to client, or older clients will hang waiting for that 603 * connection first. 604 */ 605 606 #include <stdarg.h> 607 608 void error(const char *fmt, ...) 609 { 610 va_list ap; 611 int len; 612 char *bp, buf[BUFSIZ]; 613 va_start(ap, fmt); 614 bp = buf; 615 if (sent_null == 0) { 616 *bp++ = 1; 617 len = 1; 618 } else 619 len = 0; 620 (void)vsnprintf(bp, sizeof(buf) - 1, fmt, ap); 621 (void)write(STDERR_FILENO, buf, len + strlen(bp)); 622 va_end(ap); 623 } 624 625 void 626 getstr(char *buf, int cnt, char *err) 627 { 628 char c; 629 630 do { 631 if (read(STDIN_FILENO, &c, 1) != 1) 632 exit(1); 633 *buf++ = c; 634 if (--cnt == 0) { 635 error("%s too long\n", err); 636 exit(1); 637 } 638 } while (c != 0); 639 } 640 641 /* 642 * Check whether host h is in our local domain, 643 * defined as sharing the last two components of the domain part, 644 * or the entire domain part if the local domain has only one component. 645 * If either name is unqualified (contains no '.'), 646 * assume that the host is local, as it will be 647 * interpreted as such. 648 */ 649 int 650 local_domain(char *h) 651 { 652 char localhost[MAXHOSTNAMELEN + 1]; 653 char *p1, *p2; 654 655 localhost[0] = 0; 656 (void)gethostname(localhost, sizeof(localhost)); 657 localhost[sizeof(localhost) - 1] = '\0'; 658 p1 = topdomain(localhost); 659 p2 = topdomain(h); 660 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 661 return (1); 662 return (0); 663 } 664 665 char * 666 topdomain(char *h) 667 { 668 char *p, *maybe = NULL; 669 int dots = 0; 670 671 for (p = h + strlen(h); p >= h; p--) { 672 if (*p == '.') { 673 if (++dots == 2) 674 return (p); 675 maybe = p; 676 } 677 } 678 return (maybe); 679 } 680 681 void 682 usage(void) 683 { 684 685 syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS); 686 exit(2); 687 } 688