601f1fee03533686c0eda1790ccb9da227ee0a8a
[dragonfly.git] / libexec / rshd / rshd.c
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  * @(#) Copyright (c) 1988, 1989, 1992, 1993, 1994 The Regents of the University of California.  All rights reserved.
34  * @(#)rshd.c   8.2 (Berkeley) 4/6/94
35  * $FreeBSD: src/libexec/rshd/rshd.c,v 1.30.2.5 2002/05/14 22:27:21 des Exp $
36  */
37
38 /*
39  * remote shell server:
40  *      [port]\0
41  *      remuser\0
42  *      locuser\0
43  *      command\0
44  *      data
45  */
46 #include <sys/param.h>
47 #include <sys/ioctl.h>
48 #include <sys/time.h>
49 #include <sys/socket.h>
50
51 #include <netinet/in_systm.h>
52 #include <netinet/in.h>
53 #include <netinet/ip.h>
54 #include <netinet/tcp.h>
55 #include <arpa/inet.h>
56 #include <netdb.h>
57
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <libutil.h>
61 #include <paths.h>
62 #include <pwd.h>
63 #include <signal.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <syslog.h>
68 #include <unistd.h>
69 #include <stdarg.h>
70 #ifdef  LOGIN_CAP
71 #include <login_cap.h>
72 #endif
73
74 /* wrapper for KAME-special getnameinfo() */
75 #ifndef NI_WITHSCOPEID
76 #define NI_WITHSCOPEID  0
77 #endif
78
79 extern int __check_rhosts_file;
80 extern char *__rcmd_errstr;     /* syslog hook from libc/net/rcmd.c. */
81
82 int     keepalive = 1;
83 int     log_success;            /* If TRUE, log all successful accesses */
84 int     sent_null;
85 int     no_delay;
86 #ifdef CRYPT
87 int     doencrypt = 0;
88 #endif
89
90 union sockunion {
91         struct sockinet {
92                 u_char si_len;
93                 u_char si_family;
94                 u_short si_port;
95         } su_si;
96         struct sockaddr_in  su_sin;
97         struct sockaddr_in6 su_sin6;
98 };
99 #define su_len          su_si.si_len
100 #define su_family       su_si.si_family
101 #define su_port         su_si.si_port
102
103 void     doit(union sockunion *);
104 void     error(const char *, ...) __printflike(1, 2);
105 void     getstr(char *, int, const char *);
106 int      local_domain(char *);
107 char    *topdomain(char *);
108 void     usage(void);
109
110 char    slash[] = "/";
111 char    bshell[] = _PATH_BSHELL;
112
113 #define OPTIONS "alnDL"
114
115 int
116 main(int argc, char *argv[])
117 {
118         struct linger linger;
119         socklen_t fromlen;
120         int ch, on = 1;
121         struct sockaddr_storage from;
122
123         openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
124
125         opterr = 0;
126         while ((ch = getopt(argc, argv, OPTIONS)) != -1)
127                 switch (ch) {
128                 case 'a':
129                         /* ignored for compatibility */
130                         break;
131                 case 'l':
132                         __check_rhosts_file = 0;
133                         break;
134                 case 'n':
135                         keepalive = 0;
136                         break;
137 #ifdef CRYPT
138                 case 'x':
139                         doencrypt = 1;
140                         break;
141 #endif
142                 case 'D':
143                         no_delay = 1;
144                         break;
145                 case 'L':
146                         log_success = 1;
147                         break;
148                 case '?':
149                 default:
150                         usage();
151                         break;
152                 }
153
154         argc -= optind;
155         argv += optind;
156
157 #ifdef CRYPT
158         if (doencrypt) {
159                 syslog(LOG_ERR, "-k is required for -x");
160                 exit(2);
161         }
162 #endif
163
164         fromlen = sizeof (from);
165         if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
166                 syslog(LOG_ERR, "getpeername: %m");
167                 exit(1);
168         }
169         if (keepalive &&
170             setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
171             sizeof(on)) < 0)
172                 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
173         linger.l_onoff = 1;
174         linger.l_linger = 60;                   /* XXX */
175         if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
176             sizeof (linger)) < 0)
177                 syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
178         if (no_delay &&
179             setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
180                 syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
181         doit((union sockunion *)&from);
182         /* NOTREACHED */
183         return(0);
184 }
185
186 char    username[20] = "USER=";
187 char    homedir[64] = "HOME=";
188 char    shell[64] = "SHELL=";
189 char    path[100] = "PATH=";
190 char    *envinit[] =
191             {homedir, shell, path, username, 0};
192 char    **environ;
193
194 void
195 doit(union sockunion *fromp)
196 {
197         struct passwd *pwd;
198         u_short port;
199         fd_set ready, readfrom;
200         int cc, nfd, pv[2], pid, s;
201         int one = 1;
202         const char *errorstr;
203         char *cp, sig, buf[BUFSIZ];
204         char cmdbuf[NCARGS+1], locuser[16], remuser[16];
205         char fromhost[2 * MAXHOSTNAMELEN + 1];
206         char numericname[INET6_ADDRSTRLEN];
207         int af = fromp->su_family, gaierror;
208 #ifdef  CRYPT
209         int rc;
210         int pv1[2], pv2[2];
211 #endif
212 #ifdef  LOGIN_CAP
213         login_cap_t *lc;
214 #endif
215
216         signal(SIGINT, SIG_DFL);
217         signal(SIGQUIT, SIG_DFL);
218         signal(SIGTERM, SIG_DFL);
219         fromp->su_port = ntohs((u_short)fromp->su_port);
220         if (af != AF_INET
221 #ifdef INET6
222             && af != AF_INET6
223 #endif
224             ) {
225                 syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", af);
226                 exit(1);
227         }
228         gaierror = getnameinfo((struct sockaddr *)fromp, fromp->su_len,
229             numericname, sizeof(numericname), NULL, 0,
230             NI_NUMERICHOST|NI_WITHSCOPEID);
231         if (gaierror != 0) {
232                 syslog(LOG_ERR, "malformed \"from\" address (af %d): %s", af,
233                     gai_strerror(gaierror));
234                 exit(1);
235         }
236 #ifdef IP_OPTIONS
237       if (af == AF_INET) {
238         u_char optbuf[BUFSIZ/3];
239         socklen_t optsize = sizeof(optbuf), i;
240         int ipproto;
241         struct protoent *ip;
242
243         if ((ip = getprotobyname("ip")) != NULL)
244                 ipproto = ip->p_proto;
245         else
246                 ipproto = IPPROTO_IP;
247         if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
248             optsize != 0) {
249                 for (i = 0; i < optsize; ) {
250                         u_char c = optbuf[i];
251                         if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
252                                 syslog(LOG_NOTICE,
253                                         "connection refused from %s with IP option %s",
254                                         numericname,
255                                         c == IPOPT_LSRR ? "LSRR" : "SSRR");
256                                 exit(1);
257                         }
258                         if (c == IPOPT_EOL)
259                                 break;
260                         i += (c == IPOPT_NOP) ? 1 : optbuf[i+1];
261                 }
262         }
263       }
264 #endif
265
266         if (fromp->su_port >= IPPORT_RESERVED ||
267             fromp->su_port < IPPORT_RESERVED/2) {
268                 syslog(LOG_NOTICE|LOG_AUTH,
269                     "connection from %s on illegal port %u",
270                     numericname,
271                     fromp->su_port);
272                 exit(1);
273         }
274
275         alarm(60);
276         port = 0;
277         s = 0;          /* not set or used if port == 0 */
278         for (;;) {
279                 char c;
280                 if ((cc = read(STDIN_FILENO, &c, 1)) != 1) {
281                         if (cc < 0)
282                                 syslog(LOG_NOTICE, "read: %m");
283                         shutdown(0, SHUT_RDWR);
284                         exit(1);
285                 }
286                 if (c == 0)
287                         break;
288                 port = port * 10 + c - '0';
289         }
290
291         alarm(0);
292         if (port != 0) {
293                 int lport = IPPORT_RESERVED - 1;
294                 s = rresvport_af(&lport, af);
295                 if (s < 0) {
296                         syslog(LOG_ERR, "can't get stderr port: %m");
297                         exit(1);
298                 }
299                 if (port >= IPPORT_RESERVED ||
300                     port < IPPORT_RESERVED/2) {
301                         syslog(LOG_NOTICE|LOG_AUTH,
302                             "2nd socket from %s on unreserved port %u",
303                             numericname,
304                             port);
305                         exit(1);
306                 }
307                 fromp->su_port = htons(port);
308                 if (connect(s, (struct sockaddr *)fromp, fromp->su_len) < 0) {
309                         syslog(LOG_INFO, "connect second port %d: %m", port);
310                         exit(1);
311                 }
312         }
313
314         errorstr = NULL;
315         realhostname_sa(fromhost, sizeof(fromhost) - 1,
316                         (struct sockaddr *)fromp,
317                         fromp->su_len);
318         fromhost[sizeof(fromhost) - 1] = '\0';
319
320 #ifdef CRYPT
321         if (doencrypt && af == AF_INET) {
322                 struct sockaddr_in local_addr;
323                 rc = sizeof(local_addr);
324                 if (getsockname(0, (struct sockaddr *)&local_addr,
325                     &rc) < 0) {
326                         syslog(LOG_ERR, "getsockname: %m");
327                         error("rlogind: getsockname: %m");
328                         exit(1);
329                 }
330                 authopts = KOPT_DO_MUTUAL;
331                 rc = krb_recvauth(authopts, 0, ticket,
332                         "rcmd", instance, &fromaddr,
333                         &local_addr, kdata, "", schedule,
334                         version);
335                 des_set_key(&kdata->session, schedule);
336         }
337 #endif
338         alarm(60);
339         getstr(remuser, sizeof(remuser), "remuser");
340         getstr(locuser, sizeof(locuser), "locuser");
341         getstr(cmdbuf, sizeof(cmdbuf), "command");
342         alarm(0);
343         setpwent();
344         pwd = getpwnam(locuser);
345         if (pwd == NULL) {
346                 syslog(LOG_INFO|LOG_AUTH,
347                     "%s@%s as %s: unknown login. cmd='%.80s'",
348                     remuser, fromhost, locuser, cmdbuf);
349                 if (errorstr == NULL)
350                         errorstr = "Login incorrect.\n";
351                 goto fail;
352         }
353 #ifdef  LOGIN_CAP
354         lc = login_getpwclass(pwd);
355 #endif
356         if (chdir(pwd->pw_dir) < 0) {
357 #ifdef  LOGIN_CAP
358                 if (chdir("/") < 0 ||
359                     login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) {
360                         syslog(LOG_INFO|LOG_AUTH,
361                         "%s@%s as %s: no home directory. cmd='%.80s'",
362                         remuser, fromhost, locuser, cmdbuf);
363                         error("No remote home directory.\n");
364                         exit(0);
365                 }
366 #else
367                 chdir("/");
368 #ifdef notdef
369                 syslog(LOG_INFO|LOG_AUTH,
370                     "%s@%s as %s: no home directory. cmd='%.80s'",
371                     remuser, fromhost, locuser, cmdbuf);
372                 error("No remote directory.\n");
373                 exit(1);
374 #endif
375 #endif
376                 pwd->pw_dir = slash;
377         }
378
379                 if (errorstr ||
380                     (pwd->pw_expire && time(NULL) >= pwd->pw_expire) ||
381                     iruserok_sa(fromp, fromp->su_len, pwd->pw_uid == 0,
382                                  remuser, locuser) < 0) {
383                         if (__rcmd_errstr) {
384                                 syslog(LOG_INFO|LOG_AUTH,
385                             "%s@%s as %s: permission denied (%s). cmd='%.80s'",
386                                     remuser, fromhost, locuser, __rcmd_errstr,
387                                     cmdbuf);
388                         } else {
389                                 syslog(LOG_INFO|LOG_AUTH,
390                             "%s@%s as %s: permission denied. cmd='%.80s'",
391                                     remuser, fromhost, locuser, cmdbuf);
392                         }
393 fail:
394                         if (errorstr == NULL)
395                                 errorstr = "Login incorrect.\n";
396                         error(errorstr, fromhost);
397                         exit(1);
398                 }
399
400         if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) {
401                 error("Logins currently disabled.\n");
402                 exit(1);
403         }
404 #ifdef  LOGIN_CAP
405         if (lc != NULL && fromp->su_family == AF_INET) {        /*XXX*/
406                 char    remote_ip[MAXHOSTNAMELEN];
407
408                 strncpy(remote_ip, numericname,
409                         sizeof(remote_ip) - 1);
410                 remote_ip[sizeof(remote_ip) - 1] = 0;
411                 if (!auth_hostok(lc, fromhost, remote_ip)) {
412                         syslog(LOG_INFO|LOG_AUTH,
413                             "%s@%s as %s: permission denied (%s). cmd='%.80s'",
414                             remuser, fromhost, locuser, __rcmd_errstr,
415                             cmdbuf);
416                         error("Login incorrect.\n");
417                         exit(1);
418                 }
419                 if (!auth_timeok(lc, time(NULL))) {
420                         error("Logins not available right now\n");
421                         exit(1);
422                 }
423         }
424 #endif  /* !LOGIN_CAP */
425 #if     BSD > 43
426         /* before fork, while we're session leader */
427         if (setlogin(pwd->pw_name) < 0)
428                 syslog(LOG_ERR, "setlogin() failed: %m");
429 #endif
430
431         write(STDERR_FILENO, "\0", 1);
432         sent_null = 1;
433
434         if (port) {
435                 if (pipe(pv) < 0) {
436                         error("Can't make pipe.\n");
437                         exit(1);
438                 }
439 #ifdef CRYPT
440                 if (doencrypt) {
441                         if (pipe(pv1) < 0) {
442                                 error("Can't make 2nd pipe.\n");
443                                 exit(1);
444                         }
445                         if (pipe(pv2) < 0) {
446                                 error("Can't make 3rd pipe.\n");
447                                 exit(1);
448                         }
449                 }
450 #endif
451                 pid = fork();
452                 if (pid == -1)  {
453                         error("Can't fork; try again.\n");
454                         exit(1);
455                 }
456                 if (pid) {
457 #ifdef CRYPT
458                         if (doencrypt) {
459                                 static char msg[] = SECURE_MESSAGE;
460                                 close(pv1[1]);
461                                 close(pv2[1]);
462                                 des_enc_write(s, msg, sizeof(msg) - 1, 
463                                         schedule, &kdata->session);
464
465                         } else
466 #endif
467                         {
468                                 close(0);
469                                 close(1);
470                         }
471                         close(2);
472                         close(pv[1]);
473
474                         FD_ZERO(&readfrom);
475                         FD_SET(s, &readfrom);
476                         FD_SET(pv[0], &readfrom);
477                         if (pv[0] > s)
478                                 nfd = pv[0];
479                         else
480                                 nfd = s;
481 #ifdef CRYPT
482                         if (doencrypt) {
483                                 FD_ZERO(&writeto);
484                                 FD_SET(pv2[0], &writeto);
485                                 FD_SET(pv1[0], &readfrom);
486
487                                 nfd = MAX(nfd, pv2[0]);
488                                 nfd = MAX(nfd, pv1[0]);
489                         } else
490 #endif
491                                 ioctl(pv[0], FIONBIO, (char *)&one);
492
493                         /* should set s nbio! */
494                         nfd++;
495                         do {
496                                 ready = readfrom;
497 #ifdef CRYPT
498                                 if (doencrypt) {
499                                         wready = writeto;
500                                         if (select(nfd, &ready,
501                                             &wready, NULL,
502                                             NULL) < 0)
503                                                 break;
504                                 } else
505 #endif
506                                         if (select(nfd, &ready, NULL,
507                                           NULL, NULL) < 0)
508                                                 break;
509                                 if (FD_ISSET(s, &ready)) {
510                                         int     ret;
511 #ifdef CRYPT
512                                         if (doencrypt)
513                                                 ret = des_enc_read(s, &sig, 1,
514                                                 schedule, &kdata->session);
515                                         else
516 #endif
517                                                 ret = read(s, &sig, 1);
518                                         if (ret <= 0)
519                                                 FD_CLR(s, &readfrom);
520                                         else
521                                                 killpg(pid, sig);
522                                 }
523                                 if (FD_ISSET(pv[0], &ready)) {
524                                         errno = 0;
525                                         cc = read(pv[0], buf, sizeof(buf));
526                                         if (cc <= 0) {
527                                                 shutdown(s, SHUT_RDWR);
528                                                 FD_CLR(pv[0], &readfrom);
529                                         } else {
530 #ifdef CRYPT
531                                                 if (doencrypt) {
532                                                         des_enc_write(s, buf,
533                                                             cc, schedule,
534                                                             &kdata->session);
535                                                 } else
536 #endif
537                                                         write(s, buf, cc);
538                                         }
539                                 }
540 #ifdef CRYPT
541                                 if (doencrypt && FD_ISSET(pv1[0], &ready)) {
542                                         errno = 0;
543                                         cc = read(pv1[0], buf, sizeof(buf));
544                                         if (cc <= 0) {
545                                                 shutdown(pv1[0], SHUT_RDWR);
546                                                 FD_CLR(pv1[0], &readfrom);
547                                         } else {
548                                                 des_enc_write(STDOUT_FILENO,
549                                                     buf, cc, schedule,
550                                                     &kdata->session);
551                                         }
552                                 }
553
554                                 if (doencrypt && FD_ISSET(pv2[0], &wready)) {
555                                         errno = 0;
556                                         cc = des_enc_read(STDIN_FILENO,
557                                             buf, sizeof(buf),
558                                                 schedule, &kdata->session);
559                                         if (cc <= 0) {
560                                                 shutdown(pv2[0], SHUT_RDWR);
561                                                 FD_CLR(pv2[0], &writeto);
562                                         } else {
563                                                 write(pv2[0], buf, cc);
564                                         }
565                                 }
566 #endif
567
568                         } while (FD_ISSET(s, &readfrom) ||
569 #ifdef CRYPT
570                             (doencrypt && FD_ISSET(pv1[0], &readfrom)) ||
571 #endif
572                             FD_ISSET(pv[0], &readfrom));
573                         exit(0);
574                 }
575                 setpgrp(0, getpid());
576                 close(s);
577                 close(pv[0]);
578 #ifdef CRYPT
579                 if (doencrypt) {
580                         close(pv1[0]); close(pv2[0]);
581                         dup2(pv1[1], 1);
582                         dup2(pv2[1], 0);
583                         close(pv1[1]);
584                         close(pv2[1]);
585                 }
586 #endif
587                 dup2(pv[1], 2);
588                 close(pv[1]);
589         }
590         if (*pwd->pw_shell == '\0')
591                 pwd->pw_shell = bshell;
592         environ = envinit;
593         strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
594         strcat(path, _PATH_DEFPATH);
595         strncat(shell, pwd->pw_shell, sizeof(shell)-7);
596         strncat(username, pwd->pw_name, sizeof(username)-6);
597         cp = strrchr(pwd->pw_shell, '/');
598         if (cp)
599                 cp++;
600         else
601                 cp = pwd->pw_shell;
602 #ifdef  LOGIN_CAP
603         if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) {
604                 syslog(LOG_ERR, "setusercontext: %m");
605                 exit(1);
606         }
607         login_close(lc);
608 #else
609         setgid((gid_t)pwd->pw_gid);
610         initgroups(pwd->pw_name, pwd->pw_gid);
611         setuid((uid_t)pwd->pw_uid);
612 #endif
613         endpwent();
614         if (log_success || pwd->pw_uid == 0) {
615                     syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
616                         remuser, fromhost, locuser, cmdbuf);
617         }
618         execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL);
619         perror(pwd->pw_shell);
620         exit(1);
621 }
622
623 /*
624  * Report error to client.  Note: can't be used until second socket has
625  * connected to client, or older clients will hang waiting for that
626  * connection first.
627  */
628
629 void
630 error(const char *fmt, ...)
631 {
632         va_list ap;
633         int len;
634         char *bp, buf[BUFSIZ];
635         va_start(ap, fmt);
636         bp = buf;
637         if (sent_null == 0) {
638                 *bp++ = 1;
639                 len = 1;
640         } else
641                 len = 0;
642         vsnprintf(bp, sizeof(buf) - 1, fmt, ap);
643         write(STDERR_FILENO, buf, len + strlen(bp));
644 }
645
646 void
647 getstr(char *buf, int cnt, const char *err)
648 {
649         char c;
650
651         do {
652                 if (read(STDIN_FILENO, &c, 1) != 1)
653                         exit(1);
654                 *buf++ = c;
655                 if (--cnt == 0) {
656                         error("%s too long\n", err);
657                         exit(1);
658                 }
659         } while (c != 0);
660 }
661
662 /*
663  * Check whether host h is in our local domain,
664  * defined as sharing the last two components of the domain part,
665  * or the entire domain part if the local domain has only one component.
666  * If either name is unqualified (contains no '.'),
667  * assume that the host is local, as it will be
668  * interpreted as such.
669  */
670 int
671 local_domain(char *h)
672 {
673         char localhost[MAXHOSTNAMELEN];
674         char *p1, *p2;
675
676         localhost[0] = 0;
677         gethostname(localhost, sizeof(localhost) - 1);
678         localhost[sizeof(localhost) - 1] = '\0';
679         p1 = topdomain(localhost);
680         p2 = topdomain(h);
681         if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
682                 return (1);
683         return (0);
684 }
685
686 char *
687 topdomain(char *h)
688 {
689         char *p, *maybe = NULL;
690         int dots = 0;
691
692         for (p = h + strlen(h); p >= h; p--) {
693                 if (*p == '.') {
694                         if (++dots == 2)
695                                 return (p);
696                         maybe = p;
697                 }
698         }
699         return (maybe);
700 }
701
702 void
703 usage(void)
704 {
705
706         syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS);
707         exit(2);
708 }