rshd.c revision 96195
1/*-
2 * Copyright (c) 1988, 1989, 1992, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 2002 Networks Associates Technology, Inc.
5 * All rights reserved.
6 *
7 * Portions of this software were developed for the FreeBSD Project by
8 * ThinkSec AS and NAI Labs, the Security Research Division of Network
9 * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10 * ("CBOSS"), as part of the DARPA CHATS research program.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 *    must display the following acknowledgement:
22 *	This product includes software developed by the University of
23 *	California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 *    may be used to endorse or promote products derived from this software
26 *    without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 */
40
41#ifndef lint
42static const char copyright[] =
43"@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\
44	The Regents of the University of California.  All rights reserved.\n";
45#endif /* not lint */
46
47#ifndef lint
48#if 0
49static const char sccsid[] = "@(#)rshd.c	8.2 (Berkeley) 4/6/94";
50#endif
51static const char rcsid[] =
52  "$FreeBSD: head/libexec/rshd/rshd.c 96195 2002-05-08 00:43:46Z des $";
53#endif /* not lint */
54
55/*
56 * remote shell server:
57 *	[port]\0
58 *	ruser\0
59 *	luser\0
60 *	command\0
61 *	data
62 */
63#include <sys/param.h>
64#include <sys/ioctl.h>
65#include <sys/time.h>
66#include <sys/socket.h>
67
68#include <netinet/in_systm.h>
69#include <netinet/in.h>
70#include <netinet/ip.h>
71#include <netinet/tcp.h>
72#include <arpa/inet.h>
73#include <netdb.h>
74
75#include <err.h>
76#include <errno.h>
77#include <fcntl.h>
78#include <libutil.h>
79#include <paths.h>
80#include <pwd.h>
81#include <signal.h>
82#include <stdarg.h>
83#include <stdio.h>
84#include <stdlib.h>
85#include <string.h>
86#include <syslog.h>
87#include <unistd.h>
88#include <login_cap.h>
89
90#include <security/pam_appl.h>
91#include <security/openpam.h>
92#include <sys/wait.h>
93
94static struct pam_conv pamc = { openpam_nullconv, NULL };
95static pam_handle_t *pamh;
96static int pam_err;
97
98#define PAM_END { \
99	if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
100		syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", pam_strerror(pamh, pam_err)); \
101	if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \
102		syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", pam_strerror(pamh, pam_err)); \
103	if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \
104		syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", pam_strerror(pamh, pam_err)); \
105}
106
107int	keepalive = 1;
108int	log_success;		/* If TRUE, log all successful accesses */
109int	sent_null;
110int	no_delay;
111#ifdef CRYPT
112int	doencrypt = 0;
113#endif
114
115void	 doit(struct sockaddr *);
116static void	 rshd_errx(int, const char *, ...) __printf0like(2, 3);
117void	 getstr(char *, int, const char *);
118int	 local_domain(char *);
119char	*topdomain(char *);
120void	 usage(void);
121
122#define	OPTIONS	"alnDL"
123
124int
125main(int argc, char *argv[])
126{
127	extern int __check_rhosts_file;
128	struct linger linger;
129	int ch, on = 1, fromlen;
130	struct sockaddr_storage from;
131
132	openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
133
134	opterr = 0;
135	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
136		switch (ch) {
137		case 'a':
138			/* ignored for compatibility */
139			break;
140		case 'l':
141			__check_rhosts_file = 0;
142			break;
143		case 'n':
144			keepalive = 0;
145			break;
146#ifdef CRYPT
147		case 'x':
148			doencrypt = 1;
149			break;
150#endif
151		case 'D':
152			no_delay = 1;
153			break;
154		case 'L':
155			log_success = 1;
156			break;
157		case '?':
158		default:
159			usage();
160			break;
161		}
162
163	argc -= optind;
164	argv += optind;
165
166#ifdef CRYPT
167	if (doencrypt) {
168		syslog(LOG_ERR, "-k is required for -x");
169		exit(2);
170	}
171#endif
172
173	fromlen = sizeof (from);
174	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
175		syslog(LOG_ERR, "getpeername: %m");
176		exit(1);
177	}
178	if (keepalive &&
179	    setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
180	    sizeof(on)) < 0)
181		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
182	linger.l_onoff = 1;
183	linger.l_linger = 60;			/* XXX */
184	if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
185	    sizeof (linger)) < 0)
186		syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
187	if (no_delay &&
188	    setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
189		syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
190	doit((struct sockaddr *)&from);
191	/* NOTREACHED */
192	return(0);
193}
194
195extern char **environ;
196
197void
198doit(struct sockaddr *fromp)
199{
200	extern char *__rcmd_errstr;	/* syslog hook from libc/net/rcmd.c. */
201	struct passwd *pwd;
202	u_short port;
203	fd_set ready, readfrom;
204	int cc, fd, nfd, pv[2], pid, s;
205	int one = 1;
206	const char *cp, *errorstr;
207	char sig, buf[BUFSIZ];
208	char cmdbuf[NCARGS+1], luser[16], ruser[16];
209	char rhost[2 * MAXHOSTNAMELEN + 1];
210	char numericname[INET6_ADDRSTRLEN];
211	int af, error, srcport;
212#ifdef	CRYPT
213	int rc;
214	int pv1[2], pv2[2];
215#endif
216	login_cap_t *lc;
217
218	(void) signal(SIGINT, SIG_DFL);
219	(void) signal(SIGQUIT, SIG_DFL);
220	(void) signal(SIGTERM, SIG_DFL);
221	af = fromp->sa_family;
222	srcport = ntohs(*((in_port_t *)&fromp->sa_data));
223	if (af == AF_INET) {
224		inet_ntop(af, &((struct sockaddr_in *)fromp)->sin_addr,
225		    numericname, sizeof numericname);
226#ifdef INET6
227	} else if (af == AF_INET6) {
228		inet_ntop(af, &((struct sockaddr_in6 *)fromp)->sin6_addr,
229		    numericname, sizeof numericname);
230#endif
231	} else {
232		syslog(LOG_ERR, "malformed \"from\" address (af %d)", af);
233		exit(1);
234	}
235#ifdef IP_OPTIONS
236	if (af == AF_INET) {
237		u_char optbuf[BUFSIZ/3];
238		int optsize = sizeof(optbuf), ipproto, i;
239		struct protoent *ip;
240
241		if ((ip = getprotobyname("ip")) != NULL)
242			ipproto = ip->p_proto;
243		else
244			ipproto = IPPROTO_IP;
245		if (!getsockopt(0, ipproto, IP_OPTIONS, optbuf, &optsize) &&
246		    optsize != 0) {
247			for (i = 0; i < optsize; ) {
248				u_char c = optbuf[i];
249				if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
250					syslog(LOG_NOTICE,
251					    "connection refused from %s with IP option %s",
252					    numericname,
253					    c == IPOPT_LSRR ? "LSRR" : "SSRR");
254					exit(1);
255				}
256				if (c == IPOPT_EOL)
257					break;
258				i += (c == IPOPT_NOP) ? 1 : optbuf[i+1];
259			}
260		}
261	}
262#endif
263
264	if (srcport >= IPPORT_RESERVED ||
265	    srcport < IPPORT_RESERVED/2) {
266		syslog(LOG_NOTICE|LOG_AUTH,
267		    "connection from %s on illegal port %u",
268		    numericname,
269		    srcport);
270		exit(1);
271	}
272
273	(void) alarm(60);
274	port = 0;
275	s = 0;		/* not set or used if port == 0 */
276	for (;;) {
277		char c;
278		if ((cc = read(STDIN_FILENO, &c, 1)) != 1) {
279			if (cc < 0)
280				syslog(LOG_NOTICE, "read: %m");
281			shutdown(0, 1+1);
282			exit(1);
283		}
284		if (c == 0)
285			break;
286		port = port * 10 + c - '0';
287	}
288
289	(void) alarm(0);
290	if (port != 0) {
291		int lport = IPPORT_RESERVED - 1;
292		s = rresvport_af(&lport, af);
293		if (s < 0) {
294			syslog(LOG_ERR, "can't get stderr port: %m");
295			exit(1);
296		}
297		if (port >= IPPORT_RESERVED ||
298		    port < IPPORT_RESERVED/2) {
299			syslog(LOG_NOTICE|LOG_AUTH,
300			    "2nd socket from %s on unreserved port %u",
301			    numericname,
302			    port);
303			exit(1);
304		}
305		*((in_port_t *)&fromp->sa_data) = htons(port);
306		if (connect(s, fromp, fromp->sa_len) < 0) {
307			syslog(LOG_INFO, "connect second port %d: %m", port);
308			exit(1);
309		}
310	}
311
312	errorstr = NULL;
313	realhostname_sa(rhost, sizeof(rhost) - 1, fromp, fromp->sa_len);
314	rhost[sizeof(rhost) - 1] = '\0';
315	/* XXX truncation! */
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			rshd_errx(1, "rlogind: getsockname: %m"); /* XXX */
325		}
326		authopts = KOPT_DO_MUTUAL;
327		rc = krb_recvauth(authopts, 0, ticket,
328			"rcmd", instance, &fromaddr,
329			&local_addr, kdata, "", schedule,
330			version);
331		des_set_key(&kdata->session, schedule);
332	}
333#endif
334	(void) alarm(60);
335	getstr(ruser, sizeof(ruser), "ruser");
336	getstr(luser, sizeof(luser), "luser");
337	getstr(cmdbuf, sizeof(cmdbuf), "command");
338	(void) alarm(0);
339
340	pam_err = pam_start("rsh", luser, &pamc, &pamh);
341	if (pam_err != PAM_SUCCESS) {
342		syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s",
343		    pam_strerror(pamh, pam_err));
344		rshd_errx(1, "Login incorrect.");
345	}
346
347	if ((pam_err = pam_set_item(pamh, PAM_RUSER, ruser)) != PAM_SUCCESS ||
348	    (pam_err = pam_set_item(pamh, PAM_RHOST, rhost) != PAM_SUCCESS)) {
349		syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s",
350		    pam_strerror(pamh, pam_err));
351		rshd_errx(1, "Login incorrect.");
352	}
353
354	pam_err = pam_authenticate(pamh, 0);
355	if (pam_err == PAM_SUCCESS) {
356		if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) {
357			strncpy(luser, cp, sizeof(luser));
358			luser[sizeof(luser) - 1] = '\0';
359			/* XXX truncation! */
360		}
361		pam_err = pam_acct_mgmt(pamh, 0);
362	}
363	if (pam_err != PAM_SUCCESS) {
364		syslog(LOG_INFO|LOG_AUTH,
365		    "%s@%s as %s: permission denied (%s). cmd='%.80s'",
366		    ruser, rhost, luser, pam_strerror(pamh, pam_err), cmdbuf);
367		rshd_errx(1, "Login incorrect.");
368	}
369
370	setpwent();
371	pwd = getpwnam(luser);
372	if (pwd == NULL) {
373		syslog(LOG_INFO|LOG_AUTH,
374		    "%s@%s as %s: unknown login. cmd='%.80s'",
375		    ruser, rhost, luser, cmdbuf);
376		if (errorstr == NULL)
377			errorstr = "Login incorrect.";
378		rshd_errx(1, errorstr, rhost);
379	}
380
381	lc = login_getpwclass(pwd);
382	if (pwd->pw_uid)
383		auth_checknologin(lc);
384
385	if (chdir(pwd->pw_dir) < 0) {
386		if (chdir("/") < 0 ||
387		    login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) {
388			syslog(LOG_INFO|LOG_AUTH,
389			"%s@%s as %s: no home directory. cmd='%.80s'",
390			ruser, rhost, luser, cmdbuf);
391			rshd_errx(0, "No remote home directory.");
392		}
393		pwd->pw_dir = "/";
394	}
395
396	if (lc != NULL && fromp->sa_family == AF_INET) {	/*XXX*/
397		char	remote_ip[MAXHOSTNAMELEN];
398
399		strncpy(remote_ip, numericname,
400			sizeof(remote_ip) - 1);
401		remote_ip[sizeof(remote_ip) - 1] = 0;
402		/* XXX truncation! */
403		if (!auth_hostok(lc, rhost, remote_ip)) {
404			syslog(LOG_INFO|LOG_AUTH,
405			    "%s@%s as %s: permission denied (%s). cmd='%.80s'",
406			    ruser, rhost, luser, __rcmd_errstr,
407			    cmdbuf);
408			rshd_errx(1, "Login incorrect.");
409		}
410		if (!auth_timeok(lc, time(NULL)))
411			rshd_errx(1, "Logins not available right now");
412	}
413
414	/*
415	 * PAM modules might add supplementary groups in
416	 * pam_setcred(), so initialize them first.
417	 * But we need to open the session as root.
418	 */
419	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
420		syslog(LOG_ERR, "setusercontext: %m");
421		exit(1);
422	}
423
424	if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
425		syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, pam_err));
426	} else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
427		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err));
428	}
429
430	(void) write(STDERR_FILENO, "\0", 1);
431	sent_null = 1;
432
433	if (port) {
434		if (pipe(pv) < 0)
435			rshd_errx(1, "Can't make pipe.");
436#ifdef CRYPT
437		if (doencrypt) {
438			if (pipe(pv1) < 0)
439				rshd_errx(1, "Can't make 2nd pipe.");
440			if (pipe(pv2) < 0)
441				rshd_errx(1, "Can't make 3rd pipe.");
442		}
443#endif
444		pid = fork();
445		if (pid == -1)
446			rshd_errx(1, "Can't fork; try again.");
447		if (pid) {
448#ifdef CRYPT
449			if (doencrypt) {
450				static char msg[] = SECURE_MESSAGE;
451				(void) close(pv1[1]);
452				(void) close(pv2[1]);
453				des_enc_write(s, msg, sizeof(msg) - 1,
454					schedule, &kdata->session);
455
456			} else
457#endif
458			{
459				(void) close(0);
460				(void) close(1);
461			}
462			(void) close(2);
463			(void) close(pv[1]);
464
465			FD_ZERO(&readfrom);
466			FD_SET(s, &readfrom);
467			FD_SET(pv[0], &readfrom);
468			if (pv[0] > s)
469				nfd = pv[0];
470			else
471				nfd = s;
472#ifdef CRYPT
473			if (doencrypt) {
474				FD_ZERO(&writeto);
475				FD_SET(pv2[0], &writeto);
476				FD_SET(pv1[0], &readfrom);
477
478				nfd = MAX(nfd, pv2[0]);
479				nfd = MAX(nfd, pv1[0]);
480			} else
481#endif
482				ioctl(pv[0], FIONBIO, (char *)&one);
483
484			/* should set s nbio! */
485			nfd++;
486			do {
487				ready = readfrom;
488#ifdef CRYPT
489				if (doencrypt) {
490					wready = writeto;
491					if (select(nfd, &ready,
492					    &wready, (fd_set *) 0,
493					    (struct timeval *) 0) < 0)
494						break;
495				} else
496#endif
497					if (select(nfd, &ready, (fd_set *)0,
498					  (fd_set *)0, (struct timeval *)0) < 0)
499						break;
500				if (FD_ISSET(s, &ready)) {
501					int	ret;
502#ifdef CRYPT
503					if (doencrypt)
504						ret = des_enc_read(s, &sig, 1,
505						schedule, &kdata->session);
506					else
507#endif
508						ret = read(s, &sig, 1);
509					if (ret <= 0)
510						FD_CLR(s, &readfrom);
511					else
512						killpg(pid, sig);
513				}
514				if (FD_ISSET(pv[0], &ready)) {
515					errno = 0;
516					cc = read(pv[0], buf, sizeof(buf));
517					if (cc <= 0) {
518						shutdown(s, 1+1);
519						FD_CLR(pv[0], &readfrom);
520					} else {
521#ifdef CRYPT
522						if (doencrypt)
523							(void)
524							  des_enc_write(s, buf, cc,
525								schedule, &kdata->session);
526						else
527#endif
528							(void)
529							  write(s, buf, cc);
530					}
531				}
532#ifdef CRYPT
533				if (doencrypt && FD_ISSET(pv1[0], &ready)) {
534					errno = 0;
535					cc = read(pv1[0], buf, sizeof(buf));
536					if (cc <= 0) {
537						shutdown(pv1[0], 1+1);
538						FD_CLR(pv1[0], &readfrom);
539					} else
540						(void) des_enc_write(STDOUT_FILENO,
541						    buf, cc,
542							schedule, &kdata->session);
543				}
544
545				if (doencrypt && FD_ISSET(pv2[0], &wready)) {
546					errno = 0;
547					cc = des_enc_read(STDIN_FILENO,
548					    buf, sizeof(buf),
549						schedule, &kdata->session);
550					if (cc <= 0) {
551						shutdown(pv2[0], 1+1);
552						FD_CLR(pv2[0], &writeto);
553					} else
554						(void) write(pv2[0], buf, cc);
555				}
556#endif
557
558			} while (FD_ISSET(s, &readfrom) ||
559#ifdef CRYPT
560			    (doencrypt && FD_ISSET(pv1[0], &readfrom)) ||
561#endif
562			    FD_ISSET(pv[0], &readfrom));
563			PAM_END;
564			exit(0);
565		}
566		(void) close(s);
567		(void) close(pv[0]);
568#ifdef CRYPT
569		if (doencrypt) {
570			close(pv1[0]); close(pv2[0]);
571			dup2(pv1[1], 1);
572			dup2(pv2[1], 0);
573			close(pv1[1]);
574			close(pv2[1]);
575		}
576#endif
577		dup2(pv[1], 2);
578		close(pv[1]);
579	}
580	else {
581		pid = fork();
582		if (pid == -1)
583			rshd_errx(1, "Can't fork; try again.");
584		if (pid) {
585			/* Parent. */
586			while (wait(NULL) > 0 || errno == EINTR)
587				/* nothing */ ;
588			PAM_END;
589			exit(0);
590		}
591	}
592
593	for (fd = getdtablesize(); fd > 2; fd--)
594		(void) close(fd);
595	if (setsid() == -1)
596		syslog(LOG_ERR, "setsid() failed: %m");
597	if (setlogin(pwd->pw_name) < 0)
598		syslog(LOG_ERR, "setlogin() failed: %m");
599
600	if (*pwd->pw_shell == '\0')
601		pwd->pw_shell = _PATH_BSHELL;
602	(void) pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
603	(void) pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
604	(void) pam_setenv(pamh, "USER", pwd->pw_name, 1);
605	(void) pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
606	environ = pam_getenvlist(pamh);
607	(void) pam_end(pamh, pam_err);
608	cp = strrchr(pwd->pw_shell, '/');
609	if (cp)
610		cp++;
611	else
612		cp = pwd->pw_shell;
613
614	if (setusercontext(lc, pwd, pwd->pw_uid,
615		LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) {
616		syslog(LOG_ERR, "setusercontext(): %m");
617		exit(1);
618	}
619	login_close(lc);
620	endpwent();
621	if (log_success || pwd->pw_uid == 0) {
622		    syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
623			ruser, rhost, luser, cmdbuf);
624	}
625	execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL);
626	err(1, "%s", pwd->pw_shell);
627	exit(1);
628}
629
630/*
631 * Report error to client.  Note: can't be used until second socket has
632 * connected to client, or older clients will hang waiting for that
633 * connection first.
634 */
635
636static void
637rshd_errx(int errcode, const char *fmt, ...)
638{
639	va_list ap;
640
641	va_start(ap, fmt);
642
643	if (sent_null == 0)
644		write(STDERR_FILENO, "\1", 1);
645
646	verrx(errcode, fmt, ap);
647	/* NOTREACHED */
648}
649
650void
651getstr(char *buf, int cnt, const char *error)
652{
653	char c;
654
655	do {
656		if (read(STDIN_FILENO, &c, 1) != 1)
657			exit(1);
658		*buf++ = c;
659		if (--cnt == 0)
660			rshd_errx(1, "%s too long", error);
661	} while (c != 0);
662}
663
664/*
665 * Check whether host h is in our local domain,
666 * defined as sharing the last two components of the domain part,
667 * or the entire domain part if the local domain has only one component.
668 * If either name is unqualified (contains no '.'),
669 * assume that the host is local, as it will be
670 * interpreted as such.
671 */
672int
673local_domain(char *h)
674{
675	char localhost[MAXHOSTNAMELEN];
676	char *p1, *p2;
677
678	localhost[0] = 0;
679	(void) gethostname(localhost, sizeof(localhost) - 1);
680	localhost[sizeof(localhost) - 1] = '\0';
681	/* XXX truncation! */
682	p1 = topdomain(localhost);
683	p2 = topdomain(h);
684	if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
685		return (1);
686	return (0);
687}
688
689char *
690topdomain(char *h)
691{
692	char *p, *maybe = NULL;
693	int dots = 0;
694
695	for (p = h + strlen(h); p >= h; p--) {
696		if (*p == '.') {
697			if (++dots == 2)
698				return (p);
699			maybe = p;
700		}
701	}
702	return (maybe);
703}
704
705void
706usage(void)
707{
708
709	syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS);
710	exit(2);
711}
712