diff --git a/include/rpc/rpc.h b/include/rpc/rpc.h index 5a2948da7f4d..ccfa30f253b5 100644 --- a/include/rpc/rpc.h +++ b/include/rpc/rpc.h @@ -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 diff --git a/include/unistd.h b/include/unistd.h index 02bc2d37ae2a..ff7fc01f5e59 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -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 **)); diff --git a/lib/libc/net/rcmd.3 b/lib/libc/net/rcmd.3 index 0154e828e5d5..b00f94a3cd72 100644 --- a/lib/libc/net/rcmd.3 +++ b/lib/libc/net/rcmd.3 @@ -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 @@ -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. diff --git a/lib/libc/net/rcmd.c b/lib/libc/net/rcmd.c index 7f687544e734..a1416ed79da2 100644 --- a/lib/libc/net/rcmd.c +++ b/lib/libc/net/rcmd.c @@ -59,12 +59,19 @@ static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; #include #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); } diff --git a/lib/libc/rpc/Makefile.inc b/lib/libc/rpc/Makefile.inc index 79103b3afc72..472cea7f6854 100644 --- a/lib/libc/rpc/Makefile.inc +++ b/lib/libc/rpc/Makefile.inc @@ -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 diff --git a/lib/libc/rpc/bindresvport.3 b/lib/libc/rpc/bindresvport.3 index 9abdcb991619..8d1f975bc673 100644 --- a/lib/libc/rpc/bindresvport.3 +++ b/lib/libc/rpc/bindresvport.3 @@ -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 \ No newline at end of file diff --git a/lib/libc/rpc/bindresvport.c b/lib/libc/rpc/bindresvport.c index 1a62efaca877..e8e8c15cff64 100644 --- a/lib/libc/rpc/bindresvport.c +++ b/lib/libc/rpc/bindresvport.c @@ -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); }