netkit-rsh (0.17-15) rshd/rshd.c

Summary

 rshd/rshd.c |  236 +++++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 170 insertions(+), 66 deletions(-)

    
download this patch

Patch contents

--- netkit-rsh-0.17.orig/rshd/rshd.c
+++ netkit-rsh-0.17/rshd/rshd.c
@@ -79,6 +79,7 @@
 #include <stdarg.h>
 #include <ctype.h>
 #include <assert.h>
+#include <limits.h>
 
 #if defined(__GLIBC__) && (__GLIBC__ >= 2)
 #define _check_rhosts_file  __check_rhosts_file
@@ -107,8 +108,11 @@
 extern	char	**environ;
 
 static void error(const char *fmt, ...);
-static void doit(struct sockaddr_in *fromp);
-static void getstr(char *buf, int cnt, const char *err);
+static void doit(struct sockaddr *fromp, socklen_t fromlen);
+static char *getstr(char *, size_t, const char *);
+static int err_conv(
+	int, const struct pam_message **, struct pam_response **, void *
+);
 
 extern int _check_rhosts_file;
 
@@ -145,16 +149,45 @@
 	exit(1);
 }
 
-static void getstr(char *buf, int cnt, const char *err) {
-    char c;
-    do {
-	if (read(0, &c, 1) != 1) exit(1);
-	*buf++ = c;
-	if (--cnt == 0) {
-	    error("%s too long\n", err);
-	    exit(1);
-	}
-    } while (c != 0);
+static char *getstr(char *buf, size_t cnt, const char *err) {
+	char *p;
+	char *end;
+	size_t len;
+
+	end = p = buf;
+	len = cnt;
+	if (p) {
+		end += len;
+		goto read;
+	}
+	if (!len)
+		len = 64;
+	goto alloc;
+
+	do {
+		if (p == end) {
+			size_t n;
+
+			if (cnt || len * 2 < len) {
+				error("%s too long\n", err);
+				exit(1);
+			}
+			len *= 2;
+alloc:
+			n = p - buf;
+			buf = realloc(buf, len);
+			if (!buf) {
+				error("realloc: %s\n", strerror(errno));
+				exit(1);
+			}
+			p = buf + n;
+			end = buf + len;
+		}
+read:
+		if (read(0, p, 1) != 1)
+			exit(1);
+	} while (*p++);
+	return buf;
 }
 
 static int getint(void) {
@@ -223,12 +256,23 @@
 }
 
 
+static int err_conv(
+	int num_msg, const struct pam_message **msg,
+	struct pam_response **resp, void *appdata_ptr
+) {
+	(void) num_msg;
+	(void) msg;
+	(void) resp;
+	(void) appdata_ptr;
+	return PAM_CONV_ERR;
+}
+
 static struct passwd *doauth(const char *remuser, 
 			     const char *hostname, 
 			     const char *locuser)
 {
 #ifdef USE_PAM
-    static struct pam_conv conv = { misc_conv, NULL };
+    static struct pam_conv conv = { err_conv, NULL };
     int retcode;
 #endif
     struct passwd *pwd = getpwnam(locuser);
@@ -275,26 +319,43 @@
     return pwd;
 #else
     if (pwd->pw_uid==0 && !allow_root_rhosts) return NULL;
-    if (ruserok(hostname, pwd->pw_uid==0, remuser, locuser) < 0) {
+    if (ruserok_af(hostname, pwd->pw_uid==0, remuser, locuser, AF_UNSPEC) < 0) {
 	return NULL;
     }
     return pwd;
 #endif
 }
 
-static const char *findhostname(struct sockaddr_in *fromp,
+static const char *findhostname(struct sockaddr *fromp, socklen_t fromlen,
 				const char *remuser, const char *locuser,
 				const char *cmdbuf) 
 {
-	struct hostent *hp;
 	const char *hostname;
+	char hbuf[NI_MAXHOST];
+	char naddr[NI_MAXHOST];
+	char raddr[NI_MAXHOST];
+	struct addrinfo hints, *res, *res0;
+	int gaierr;
+	struct sockaddr_in v4;
+
+	if (fromp->sa_family == AF_INET6) {
+		const struct sockaddr_in6 *v6p = (const void *)fromp;
+
+		if (IN6_IS_ADDR_V4MAPPED(&v6p->sin6_addr)) {
+			v4.sin_family = AF_INET;
+			v4.sin_addr.s_addr = v6p->sin6_addr.s6_addr32[3];
+			fromp = (struct sockaddr *)&v4;
+		}
+	}
 
-	hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr),
-			   fromp->sin_family);
+	if ((gaierr = getnameinfo(fromp, fromlen, hbuf, sizeof(hbuf),
+				  NULL, 0, 0))) {
+		error("getnameinfo: %s\n", gai_strerror(gaierr));
+		exit(1);
+	}
 
 	errno = ENOMEM; /* malloc (thus strdup) may not set it */
-	if (hp) hostname = strdup(hp->h_name);
-	else hostname = strdup(inet_ntoa(fromp->sin_addr));
+	hostname = strdup(hbuf);
 
 	if (hostname==NULL) {
 	    /* out of memory? */
@@ -302,36 +363,46 @@
 	    exit(1);
 	}
 
-	/*
-	 * Attempt to confirm the DNS. 
-	 */
-#ifdef	RES_DNSRCH
-	_res.options &= ~RES_DNSRCH;
-#endif
-	hp = gethostbyname(hostname);
-	if (hp == NULL) {
-	    syslog(LOG_INFO, "Couldn't look up address for %s", hostname);
-	    fail("Couldn't get address for your host (%s)\n", 
-		 remuser, inet_ntoa(fromp->sin_addr), locuser, cmdbuf);
-	} 
-	while (hp->h_addr_list[0] != NULL) {
-	    if (!memcmp(hp->h_addr_list[0], &fromp->sin_addr,
-			sizeof(fromp->sin_addr))) {
-		return hostname;
-	    }
-	    hp->h_addr_list++;
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = fromp->sa_family;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_CANONNAME;
+	if ((gaierr = getaddrinfo(hbuf, NULL, &hints, &res0))) {
+		syslog(LOG_INFO, "Couldn't look up address for %s: %s",
+		       hbuf, gai_strerror(gaierr));
+		fail("Couldn't get address for your host (%s)\n",
+		     remuser, hbuf, locuser, cmdbuf);
+	} else {
+		if (getnameinfo(fromp, fromlen, naddr, sizeof(naddr),
+				NULL, 0, NI_NUMERICHOST))
+			strcpy(naddr, "???");
+		for (res = res0; res; res = res->ai_next) {
+			if (res->ai_family != fromp->sa_family)
+				continue;
+			if (getnameinfo(res->ai_addr, res->ai_addrlen,
+					raddr, sizeof(raddr), NULL, 0,
+					NI_NUMERICHOST) == 0
+			    && strcmp(naddr, raddr) == 0) {
+				break;  /* match */
+			}
+		}
+		if (res) {
+			freeaddrinfo(res0);
+			return hostname;
+		}
 	}
 	syslog(LOG_NOTICE, "Host addr %s not listed for host %s",
-	       inet_ntoa(fromp->sin_addr), hp->h_name);
+	       naddr, res0->ai_canonname ? res0->ai_canonname : "???");
+	freeaddrinfo(res0);
 	fail("Host address mismatch for %s\n", 
-	     remuser, inet_ntoa(fromp->sin_addr), locuser, cmdbuf);
+	     remuser, naddr, locuser, cmdbuf);
 	return NULL; /* not reachable */
 }
 
 static void
-doit(struct sockaddr_in *fromp)
+doit(struct sockaddr *fromp, socklen_t fromlen)
 {
-	char cmdbuf[ARG_MAX+1];
+	char *cmdbuf;
 	const char *theshell, *shellname;
 	char locuser[16], remuser[16];
 	struct passwd *pwd;
@@ -339,6 +410,14 @@
 	const char *hostname;
 	u_short port;
 	int pv[2], pid, ifd;
+	const int family = fromp->sa_family;
+	union {
+		struct sockaddr_in in;
+		struct sockaddr_in6 in6;
+	} *const u = (void *)fromp;
+#ifdef USE_PAM
+	char **pam_envlist;
+#endif
 
 	signal(SIGINT, SIG_DFL);
 	signal(SIGQUIT, SIG_DFL);
@@ -350,7 +429,7 @@
 
 	if (port != 0) {
 		int lport = IPPORT_RESERVED - 1;
-		sock = rresvport(&lport);
+		sock = rresvport_af(&lport, family);
 		if (sock < 0) {
 		    syslog(LOG_ERR, "can't get stderr port: %m");
 		    exit(1);
@@ -359,9 +438,12 @@
 		    syslog(LOG_ERR, "2nd port not reserved\n");
 		    exit(1);
 		}
-		fromp->sin_port = htons(port);
-		if (connect(sock, (struct sockaddr *)fromp,
-			    sizeof(*fromp)) < 0) {
+		port = htons(port);
+		if (family == AF_INET6)
+			u->in.sin_port = port;
+		else
+			u->in6.sin6_port = port;
+		if (connect(sock, fromp, fromlen) < 0) {
 		    syslog(LOG_INFO, "connect second port: %m");
 		    exit(1);
 		}
@@ -376,10 +458,14 @@
 
 	getstr(remuser, sizeof(remuser), "remuser");
 	getstr(locuser, sizeof(locuser), "locuser");
-	getstr(cmdbuf, sizeof(cmdbuf), "command");
+#ifdef ARG_MAX
+	cmdbuf = getstr(0, ARG_MAX + 1, "command");
+#else
+	cmdbuf = getstr(0, 0, "command");
+#endif
 	if (!strcmp(locuser, "root")) paranoid = 1;
 
-	hostname = findhostname(fromp, remuser, locuser, cmdbuf);
+	hostname = findhostname(fromp, fromlen, remuser, locuser, cmdbuf);
 
 	setpwent();
 	pwd = doauth(remuser, hostname, locuser);
@@ -473,6 +559,11 @@
 	else shellname = theshell;
 
 	endpwent();
+#ifdef USE_PAM
+	pam_envlist = pam_getenvlist(pamh);
+	while (*pam_envlist)
+		putenv(*pam_envlist++);
+#endif
 	if (paranoid) {
 	    syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%s'",
 		   remuser, hostname, locuser, cmdbuf);
@@ -489,18 +580,22 @@
 	exit(1);
 }
 
-static void network_init(int fd, struct sockaddr_in *fromp)
+static void network_init(int fd, struct sockaddr *fromp, socklen_t *fromlen)
 {
 	struct linger linger;
-	socklen_t fromlen;
 	int on=1;
 	int port;
+	int family;
+	union {
+		struct sockaddr_in in;
+		struct sockaddr_in6 in6;
+	} *const u = (void *)fromp;
 
-	fromlen = sizeof(*fromp);
-	if (getpeername(fd, (struct sockaddr *) fromp, &fromlen) < 0) {
+	if (getpeername(fd, fromp, fromlen) < 0) {
 		syslog(LOG_ERR, "getpeername: %m");
 		_exit(1);
 	}
+	family = fromp->sa_family;
 	if (keepalive &&
 	    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
 	    sizeof(on)) < 0)
@@ -511,13 +606,12 @@
 	    sizeof (linger)) < 0)
 		syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
 
-	if (fromp->sin_family != AF_INET) {
-	    syslog(LOG_ERR, "malformed \"from\" address (af %d)\n",
-		   fromp->sin_family);
+	if (family != AF_INET && family != AF_INET6) {
+	    syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", family);
 	    exit(1);
 	}
 #ifdef IP_OPTIONS
-      {
+      if (family == AF_INET) {
 	u_char optbuf[BUFSIZ/3], *cp;
 	char lbuf[BUFSIZ+1], *lp;
 	socklen_t optsize = sizeof(optbuf);
@@ -528,7 +622,7 @@
 		ipproto = ip->p_proto;
 	else
 		ipproto = IPPROTO_IP;
-	if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
+	if (!getsockopt(fd, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
 	    optsize != 0) {
 		lp = lbuf;
 
@@ -543,9 +637,9 @@
 		syslog(LOG_NOTICE,
 		       "Connection received from %s using IP options"
 		       " (ignored): %s",
-		       inet_ntoa(fromp->sin_addr), lbuf);
+		       inet_ntoa(u->in.sin_addr), lbuf);
 
-		if (setsockopt(0, ipproto, IP_OPTIONS, NULL, optsize) != 0) {
+		if (setsockopt(fd, ipproto, IP_OPTIONS, NULL, optsize) != 0) {
 			syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
 			exit(1);
 		}
@@ -556,11 +650,16 @@
 	/*
 	 * Check originating port for validity.
 	 */
-	port = ntohs(fromp->sin_port);
+	if (family == AF_INET6)
+		port = u->in6.sin6_port;
+	else
+		port = u->in.sin_port;
+	port = ntohs(port);
 	if (port >= IPPORT_RESERVED || port < IPPORT_RESERVED/2) {
-	    syslog(LOG_NOTICE|LOG_AUTH, "Connection from %s on illegal port",
-		   inet_ntoa(fromp->sin_addr));
-	    exit(1);
+		syslog(LOG_NOTICE|LOG_AUTH,
+		       "Connection from %s on illegal port",
+		       findhostname(fromp, *fromlen, "", "", "") ?: "???");
+		exit(1);
 	}
 }
 
@@ -568,7 +667,11 @@
 main(int argc, char *argv[])
 {
 	int ch;
-	struct sockaddr_in from;
+	union {
+		struct sockaddr_storage storage;
+		struct sockaddr addr;
+	} from;
+	socklen_t fromlen;
 	_check_rhosts_file=1;
 
 	openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
@@ -611,8 +714,9 @@
                                "pam_rhosts_auth in /etc/pam.conf");
 #endif /* USE_PAM */
 
-	network_init(0, &from);
-	doit(&from);
+	fromlen = sizeof(from);
+	network_init(0, &from.addr, &fromlen);
+	doit(&from.addr, fromlen);
 	return 0;
 }