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