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