libc rcmd update for IPv6.

A new function bindresvport2(), AF independent version of bindresvport()
is also added.

Reviewed by: sumikawa
Obtained from: KAME project
This commit is contained in:
Yoshinobu Inoue 2000-01-13 15:09:48 +00:00
parent 5d60ed0e69
commit 42b4f28ebd
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=55918
7 changed files with 301 additions and 89 deletions

View file

@ -87,6 +87,7 @@ extern void setrpcent __P((int));
extern void endrpcent __P((void));
extern int bindresvport __P((int, struct sockaddr_in *));
extern int bindresvport2 __P((int, struct sockaddr *, int addrlen));
extern int get_myaddress __P((struct sockaddr_in *));
__END_DECLS

View file

@ -147,6 +147,7 @@ char *getusershell __P((void));
char *getwd __P((char *)); /* obsoleted by getcwd() */
int initgroups __P((const char *, int));
int iruserok __P((unsigned long, int, const char *, const char *));
int iruserok_af __P((void *, int, const char *, const char *, int));
int issetugid __P((void));
int lchown __P((const char *, uid_t, gid_t));
int lockf __P((int, int, off_t));
@ -203,6 +204,9 @@ int unwhiteout __P((const char *));
int usleep __P((unsigned int));
void *valloc __P((size_t)); /* obsoleted by malloc() */
pid_t vfork __P((void));
int rresvport_af __P((int *, int));
int ruserok_af __P((const char *, int, const char *, const char *, int));
int iruserok_af __P((void *, int, const char *, const char *, int));
extern char *suboptarg; /* getsubopt(3) external variable */
int getsubopt __P((char **, char * const *, char **));

View file

@ -39,7 +39,10 @@
.Nm rcmd ,
.Nm rresvport ,
.Nm iruserok ,
.Nm ruserok
.Nm ruserok ,
.Nm rresvport_af ,
.Nm iruserok_af ,
.Nm ruserok_af
.Nd routines for returning a stream to a remote command
.Sh SYNOPSIS
.Fd #include <unistd.h>
@ -51,6 +54,12 @@
.Fn iruserok "u_long raddr" "int superuser" "const char *ruser" "const char *luser"
.Ft int
.Fn ruserok "const char *rhost" "int superuser" "const char *ruser" "const char *luser"
.Ft int
.Fn rresvport_af "int *port" "int family"
.Ft int
.Fn iruserok_af "void *raddr" "int superuser" "const char *ruser" "const char *luser" "int af"
.Ft int
.Fn ruserok_af "const char *rhost" "int superuser" "const char *ruser" "const char *luser" "int af"
.Sh DESCRIPTION
The
.Fn rcmd
@ -173,6 +182,19 @@ function is strongly preferred for security reasons.
It requires trusting the local DNS at most, while the
.Fn ruserok
function requires trusting the entire DNS, which can be spoofed.
.Pp
Functions with ``_af'' suffix, i.e.
.Fn rresvport_af ,
.Fn iruserok_af and
.Fn ruserok_af ,
works just as same as functions without ``_af'', and is capable of
handling both IPv6 port and IPv4 port.
To switch address family,
.Fa af
argument must be filled with
.Dv AF_INET
or
.Dv AF_INET6 .
.Sh DIAGNOSTICS
The
.Fn rcmd
@ -198,7 +220,18 @@ is overloaded to mean ``All network ports in use.''
.Xr rexecd 8 ,
.Xr rlogind 8 ,
.Xr rshd 8
.Pp
W. Stevens and M. Thomas, ``Advanced Socket API for IPv6,''
RFC2292.
.Sh HISTORY
These
functions appeared in
.Bx 4.2 .
.Fn rresvport_af
appeared in RFC2292, and implemented by WIDE project
for Hydrangea IPv6 protocol stack kit.
.Fn iruserok_af
and
.Fn rusreok_af
are proposed and implemented by WIDE project
for Hydrangea IPv6 protocol stack kit.

View file

@ -59,12 +59,19 @@ static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94";
#include <rpcsvc/ypclnt.h>
#endif
/* wrapper for KAME-special getnameinfo() */
#ifndef NI_WITHSCOPEID
#define NI_WITHSCOPEID 0
#endif
extern int innetgr __P(( const char *, const char *, const char *, const char * ));
#define max(a, b) ((a > b) ? a : b)
int __ivaliduser __P((FILE *, u_int32_t, const char *, const char *));
static int __icheckhost __P((u_int32_t, char *));
static int __icheckhost __P((void *, char *, int, int));
char paddr[INET6_ADDRSTRLEN];
int
rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
@ -73,24 +80,40 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
const char *locuser, *remuser, *cmd;
int *fd2p;
{
struct hostent *hp;
struct sockaddr_in sin, from;
struct addrinfo hints, *res, *ai;
struct sockaddr_storage from;
fd_set reads;
long oldmask;
pid_t pid;
int s, lport, timo;
int s, aport, lport, timo, error;
char c;
int refused;
char num[8];
pid = getpid();
hp = gethostbyname(*ahost);
if (hp == NULL) {
herror(*ahost);
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
(void)snprintf(num, sizeof(num), "%d", ntohs(rport));
error = getaddrinfo(*ahost, num, &hints, &res);
if (error) {
fprintf(stderr, "rcmd: getaddrinfo: %s\n",
gai_strerror(error));
if (error == EAI_SYSTEM)
fprintf(stderr, "rcmd: getaddrinfo: %s\n",
strerror(errno));
return (-1);
}
*ahost = hp->h_name;
if (res->ai_canonname)
*ahost = res->ai_canonname;
ai = res;
refused = 0;
oldmask = sigblock(sigmask(SIGURG));
for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
s = rresvport(&lport);
s = rresvport_af(&lport, ai->ai_family);
if (s < 0) {
if (errno == EAGAIN)
(void)fprintf(stderr,
@ -99,40 +122,47 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
(void)fprintf(stderr, "rcmd: socket: %s\n",
strerror(errno));
sigsetmask(oldmask);
freeaddrinfo(res);
return (-1);
}
_libc_fcntl(s, F_SETOWN, pid);
bzero(&sin, sizeof sin);
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_family = hp->h_addrtype;
sin.sin_port = rport;
bcopy(hp->h_addr_list[0], &sin.sin_addr, MIN(hp->h_length, sizeof sin.sin_addr));
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
if (connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
break;
(void)_libc_close(s);
if (errno == EADDRINUSE) {
lport--;
continue;
}
if (errno == ECONNREFUSED && timo <= 16) {
(void)_libc_sleep(timo);
timo *= 2;
continue;
}
if (hp->h_addr_list[1] != NULL) {
if (errno == ECONNREFUSED)
refused = 1;
if (ai->ai_next != NULL) {
int oerrno = errno;
getnameinfo(ai->ai_addr, ai->ai_addrlen,
paddr, sizeof(paddr),
NULL, 0,
NI_NUMERICHOST|NI_WITHSCOPEID);
(void)fprintf(stderr, "connect to address %s: ",
inet_ntoa(sin.sin_addr));
paddr);
errno = oerrno;
perror(0);
hp->h_addr_list++;
bcopy(hp->h_addr_list[0], &sin.sin_addr, MIN(hp->h_length, sizeof sin.sin_addr));
(void)fprintf(stderr, "Trying %s...\n",
inet_ntoa(sin.sin_addr));
ai = ai->ai_next;
getnameinfo(ai->ai_addr, ai->ai_addrlen,
paddr, sizeof(paddr),
NULL, 0,
NI_NUMERICHOST|NI_WITHSCOPEID);
fprintf(stderr, "Trying %s...\n", paddr);
continue;
}
(void)fprintf(stderr, "%s: %s\n", hp->h_name, strerror(errno));
if (refused && timo <= 16) {
(void)_libc_sleep(timo);
timo *= 2;
ai = res;
refused = 0;
continue;
}
freeaddrinfo(res);
(void)fprintf(stderr, "%s: %s\n", *ahost, strerror(errno));
sigsetmask(oldmask);
return (-1);
}
@ -142,8 +172,8 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
lport = 0;
} else {
char num[8];
int s2 = rresvport(&lport), s3;
int len = sizeof(from);
int s2 = rresvport_af(&lport, ai->ai_family), s3;
int len = ai->ai_addrlen;
int nfds;
if (s2 < 0)
@ -180,11 +210,24 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
goto bad;
}
s3 = accept(s2, (struct sockaddr *)&from, &len);
switch (from.ss_family) {
case AF_INET:
aport = ntohs(((struct sockaddr_in *)&from)->sin_port);
break;
#ifdef INET6
case AF_INET6:
aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
break;
#endif
default:
aport = 0; /* error */
break;
}
/*
* XXX careful for ftp bounce attacks. If discovered, shut them
* down and check for the real auxiliary channel to connect.
*/
if (from.sin_family == AF_INET && from.sin_port == htons(20)) {
if (aport == 20) {
_libc_close(s3);
goto again;
}
@ -196,10 +239,7 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
goto bad;
}
*fd2p = s3;
from.sin_port = ntohs((u_short)from.sin_port);
if (from.sin_family != AF_INET ||
from.sin_port >= IPPORT_RESERVED ||
from.sin_port < IPPORT_RESERVED / 2) {
if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) {
(void)fprintf(stderr,
"socket: protocol failure in circuit setup.\n");
goto bad2;
@ -222,6 +262,7 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
goto bad2;
}
sigsetmask(oldmask);
freeaddrinfo(res);
return (s);
bad2:
if (lport)
@ -229,21 +270,46 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
bad:
(void)_libc_close(s);
sigsetmask(oldmask);
freeaddrinfo(res);
return (-1);
}
int
rresvport(alport)
int *alport;
rresvport(port)
int *port;
{
struct sockaddr_in sin;
int s;
return rresvport_af(port, AF_INET);
}
bzero(&sin, sizeof sin);
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
s = socket(AF_INET, SOCK_STREAM, 0);
int
rresvport_af(alport, family)
int *alport, family;
{
int i, s, len, err;
struct sockaddr_storage ss;
u_short *sport;
memset(&ss, 0, sizeof(ss));
ss.ss_family = family;
switch (family) {
case AF_INET:
((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in);
sport = &((struct sockaddr_in *)&ss)->sin_port;
((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY;
break;
#ifdef INET6
case AF_INET6:
((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6);
sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any;
break;
#endif
default:
errno = EAFNOSUPPORT;
return -1;
}
s = socket(ss.ss_family, SOCK_STREAM, 0);
if (s < 0)
return (-1);
#if 0 /* compat_exact_traditional_rresvport_semantics */
@ -255,12 +321,13 @@ rresvport(alport)
return (-1);
}
#endif
sin.sin_port = 0;
if (bindresvport(s, &sin) == -1) {
*sport = 0;
if (bindresvport2(s, (struct sockaddr *)&ss,
((struct sockaddr *)&ss)->sa_len) == -1) {
(void)_libc_close(s);
return (-1);
}
*alport = (int)ntohs(sin.sin_port);
*alport = (int)ntohs(*sport);
return (s);
}
@ -272,18 +339,34 @@ ruserok(rhost, superuser, ruser, luser)
const char *rhost, *ruser, *luser;
int superuser;
{
struct hostent *hp;
u_int32_t addr;
char **ap;
return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
}
if ((hp = gethostbyname(rhost)) == NULL)
int
ruserok_af(rhost, superuser, ruser, luser, af)
const char *rhost, *ruser, *luser;
int superuser, af;
{
struct hostent *hp;
union {
struct in_addr addr_in;
struct in6_addr addr_in6;
} addr;
char **ap;
int ret, h_error;
if ((hp = getipnodebyname(rhost, af, AI_DEFAULT, &h_error)) == NULL)
return (-1);
ret = -1;
for (ap = hp->h_addr_list; *ap; ++ap) {
bcopy(*ap, &addr, sizeof(addr));
if (iruserok(addr, superuser, ruser, luser) == 0)
return (0);
bcopy(*ap, &addr, hp->h_length);
if (iruserok_af(&addr, superuser, ruser, luser, af) == 0) {
ret = 0;
break;
}
}
return (-1);
freehostent(hp);
return (ret);
}
/*
@ -300,6 +383,16 @@ iruserok(raddr, superuser, ruser, luser)
unsigned long raddr;
int superuser;
const char *ruser, *luser;
{
return iruserok_af(&raddr, superuser, ruser, luser, AF_INET);
}
int
iruserok_af(raddr, superuser, ruser, luser, af)
void *raddr;
int superuser;
const char *ruser, *luser;
int af;
{
register char *cp;
struct stat sbuf;
@ -308,12 +401,25 @@ iruserok(raddr, superuser, ruser, luser)
uid_t uid;
int first;
char pbuf[MAXPATHLEN];
int len = 0;
switch (af) {
case AF_INET:
len = sizeof(struct in_addr);
break;
#ifdef INET6
case AF_INET6:
len = sizeof(struct in6_addr);
break;
#endif
}
first = 1;
hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
again:
if (hostf) {
if (__ivaliduser(hostf, (u_int32_t)raddr, luser, ruser) == 0) {
if (__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
== 0) {
(void)fclose(hostf);
return (0);
}
@ -375,6 +481,17 @@ __ivaliduser(hostf, raddr, luser, ruser)
FILE *hostf;
u_int32_t raddr;
const char *luser, *ruser;
{
return __ivaliduser_af(hostf, &raddr, luser, ruser, AF_INET,
sizeof(raddr));
}
int
__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
FILE *hostf;
void *raddr;
const char *luser, *ruser;
int af, len;
{
register char *user, *p;
int ch;
@ -383,6 +500,7 @@ __ivaliduser(hostf, raddr, luser, ruser)
struct hostent *hp;
/* Presumed guilty until proven innocent. */
int userok = 0, hostok = 0;
int h_error;
#ifdef YP
char *ypdomain;
@ -392,11 +510,11 @@ __ivaliduser(hostf, raddr, luser, ruser)
#define ypdomain NULL
#endif
/* We need to get the damn hostname back for netgroup matching. */
if ((hp = gethostbyaddr((char *)&raddr, sizeof(u_int32_t),
AF_INET)) == NULL)
if ((hp = getipnodebyaddr((char *)raddr, len, af, &h_error)) == NULL)
return (-1);
strncpy(hname, hp->h_name, sizeof(hname));
hname[sizeof(hname) - 1] = '\0';
freehostent(hp);
while (fgets(buf, sizeof(buf), hostf)) {
p = buf;
@ -438,7 +556,8 @@ __ivaliduser(hostf, raddr, luser, ruser)
hostok = innetgr((char *)&buf[2],
(char *)&hname, NULL, ypdomain);
else /* match a host by addr */
hostok = __icheckhost(raddr,(char *)&buf[1]);
hostok = __icheckhost(raddr,(char *)&buf[1],
af, len);
break;
case '-': /* reject '-' hosts and all their users */
if (buf[1] == '@') {
@ -446,12 +565,12 @@ __ivaliduser(hostf, raddr, luser, ruser)
(char *)&hname, NULL, ypdomain))
return(-1);
} else {
if (__icheckhost(raddr,(char *)&buf[1]))
if (__icheckhost(raddr,(char *)&buf[1],af,len))
return(-1);
}
break;
default: /* if no '+' or '-', do a simple match */
hostok = __icheckhost(raddr, buf);
hostok = __icheckhost(raddr, buf, af, len);
break;
}
switch(*user) {
@ -494,27 +613,37 @@ __ivaliduser(hostf, raddr, luser, ruser)
* Returns "true" if match, 0 if no match.
*/
static int
__icheckhost(raddr, lhost)
u_int32_t raddr;
__icheckhost(raddr, lhost, af, len)
void *raddr;
register char *lhost;
int af, len;
{
register struct hostent *hp;
register u_int32_t laddr;
char laddr[BUFSIZ]; /* xxx */
register char **pp;
int h_error;
int match;
/* Try for raw ip address first. */
if (isdigit((unsigned char)*lhost) && (u_int32_t)(laddr = inet_addr(lhost)) != -1)
return (raddr == laddr);
if (inet_pton(af, lhost, laddr) == 1) {
if (memcmp(raddr, laddr, len) == 0)
return (1);
else
return (0);
}
/* Better be a hostname. */
if ((hp = gethostbyname(lhost)) == NULL)
if ((hp = getipnodebyname(lhost, af, AI_DEFAULT, &h_error)) == NULL)
return (0);
/* Spin through ip addresses. */
match = 0;
for (pp = hp->h_addr_list; *pp; ++pp)
if (!bcmp(&raddr, *pp, sizeof(u_int32_t)))
return (1);
if (!bcmp(raddr, *pp, len)) {
match = 1;
break;
}
/* No match. */
return (0);
freehostent(hp);
return (match);
}

View file

@ -44,7 +44,8 @@ crypt.h: ${RPCDIR}/crypt.x
#
# MAN1+= rstat.1
MAN3+= bindresvport.3 des_crypt.3 getrpcent.3 getrpcport.3 publickey.3 rpc.3 \
MAN3+= bindresvport.3 bindresvport2.3 des_crypt.3 getrpcent.3 getrpcport.3 \
publickey.3 rpc.3 \
rpc_secure.3 rtime.3
MAN5+= publickey.5 rpc.5
# MAN8+= rstat_svc.8

View file

@ -30,3 +30,5 @@ If the value of sin->sin_port is non-zero
.Fn bindresvport
will attempt to use that specific port. If it fails, it chooses another
privileged port automatically.
.Sh "SEE ALSO"
.Xr bindresvport2 3

View file

@ -55,7 +55,6 @@ bindresvport(sd, sin)
int sd;
struct sockaddr_in *sin;
{
int on, old, error;
struct sockaddr_in myaddr;
int sinlen = sizeof(struct sockaddr_in);
@ -69,39 +68,82 @@ bindresvport(sd, sin)
return (-1);
}
if (sin->sin_port == 0) {
return (bindresvport2(sd, sin, sinlen));
}
int
bindresvport2(sd, sa, addrlen)
int sd;
struct sockaddr *sa;
socklen_t addrlen;
{
int on, old, error, level, optname;
u_short port;
if (sa == NULL) {
errno = EINVAL;
return (-1);
}
switch (sa->sa_family) {
case AF_INET:
port = ntohs(((struct sockaddr_in *)sa)->sin_port);
level = IPPROTO_IP;
optname = IP_PORTRANGE;
on = IP_PORTRANGE_LOW;
break;
#ifdef INET6
case AF_INET6:
port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
level = IPPROTO_IPV6;
optname = IPV6_PORTRANGE;
on = IPV6_PORTRANGE_LOW;
break;
#endif
default:
errno = EAFNOSUPPORT;
return (-1);
}
if (port == 0) {
int oldlen = sizeof(old);
error = getsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
&old, &oldlen);
error = getsockopt(sd, level, optname, &old, &oldlen);
if (error < 0)
return(error);
on = IP_PORTRANGE_LOW;
error = setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
&on, sizeof(on));
error = setsockopt(sd, level, optname, &on, sizeof(on));
if (error < 0)
return(error);
}
error = bind(sd, (struct sockaddr *)sin, sinlen);
error = bind(sd, sa, addrlen);
if (sin->sin_port == 0) {
switch (sa->sa_family) {
case AF_INET:
port = ntohs(((struct sockaddr_in *)sa)->sin_port);
break;
#ifdef INET6
case AF_INET6:
port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
break;
#endif
default: /* shoud not match here */
errno = EAFNOSUPPORT;
return (-1);
}
if (port == 0) {
int saved_errno = errno;
if (error) {
if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
if (setsockopt(sd, level, optname,
&old, sizeof(old)) < 0)
errno = saved_errno;
return (error);
}
if (sin != &myaddr) {
/* Hmm, what did the kernel assign... */
if (getsockname(sd, (struct sockaddr *)sin,
&sinlen) < 0)
errno = saved_errno;
return (error);
}
/* Hmm, what did the kernel assign... */
if (getsockname(sd, (struct sockaddr *)sa, &addrlen) < 0)
errno = saved_errno;
return (error);
}
return (error);
}