rcmd.c revision 146187
11573Srgrimes/* 21573Srgrimes * Copyright (c) 1983, 1993, 1994 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 51573Srgrimes * Redistribution and use in source and binary forms, with or without 61573Srgrimes * modification, are permitted provided that the following conditions 71573Srgrimes * are met: 81573Srgrimes * 1. Redistributions of source code must retain the above copyright 91573Srgrimes * notice, this list of conditions and the following disclaimer. 101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111573Srgrimes * notice, this list of conditions and the following disclaimer in the 121573Srgrimes * documentation and/or other materials provided with the distribution. 131573Srgrimes * 3. All advertising materials mentioning features or use of this software 141573Srgrimes * must display the following acknowledgement: 151573Srgrimes * This product includes software developed by the University of 161573Srgrimes * California, Berkeley and its contributors. 171573Srgrimes * 4. Neither the name of the University nor the names of its contributors 181573Srgrimes * may be used to endorse or promote products derived from this software 191573Srgrimes * without specific prior written permission. 201573Srgrimes * 211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311573Srgrimes * SUCH DAMAGE. 321573Srgrimes */ 331573Srgrimes 341573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 351573Srgrimesstatic char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; 361573Srgrimes#endif /* LIBC_SCCS and not lint */ 3792986Sobrien#include <sys/cdefs.h> 3892986Sobrien__FBSDID("$FreeBSD: head/lib/libc/net/rcmd.c 146187 2005-05-13 16:31:11Z ume $"); 391573Srgrimes 4071579Sdeischen#include "namespace.h" 411573Srgrimes#include <sys/param.h> 421573Srgrimes#include <sys/socket.h> 431573Srgrimes#include <sys/stat.h> 441573Srgrimes 451573Srgrimes#include <netinet/in.h> 461573Srgrimes#include <arpa/inet.h> 471573Srgrimes 481573Srgrimes#include <signal.h> 491573Srgrimes#include <fcntl.h> 501573Srgrimes#include <netdb.h> 5185342Simp#include <stdlib.h> 521573Srgrimes#include <unistd.h> 531573Srgrimes#include <pwd.h> 541573Srgrimes#include <errno.h> 551573Srgrimes#include <stdio.h> 561573Srgrimes#include <ctype.h> 571573Srgrimes#include <string.h> 58137675Sbz#include <rpc/rpc.h> 599978Swpaul#ifdef YP 609978Swpaul#include <rpcsvc/yp_prot.h> 619978Swpaul#include <rpcsvc/ypclnt.h> 629978Swpaul#endif 6363682Sume#include <arpa/nameser.h> 6471579Sdeischen#include "un-namespace.h" 651573Srgrimes 6692905Sobrienextern int innetgr( const char *, const char *, const char *, const char * ); 6717141Sjkh 682592Scsgr#define max(a, b) ((a > b) ? a : b) 692592Scsgr 7092941Sobrienint __ivaliduser(FILE *, u_int32_t, const char *, const char *); 7192941Sobrienint __ivaliduser_af(FILE *,const void *, const char *, const char *, int, int); 7292941Sobrienint __ivaliduser_sa(FILE *, const struct sockaddr *, socklen_t, const char *, 7392941Sobrien const char *); 7492941Sobrienstatic int __icheckhost(const struct sockaddr *, socklen_t, const char *); 751573Srgrimes 7669575Sumechar paddr[NI_MAXHOST]; 7755918Sshin 781573Srgrimesint 791573Srgrimesrcmd(ahost, rport, locuser, remuser, cmd, fd2p) 801573Srgrimes char **ahost; 811573Srgrimes u_short rport; 821573Srgrimes const char *locuser, *remuser, *cmd; 831573Srgrimes int *fd2p; 841573Srgrimes{ 8556590Sshin return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET); 8656590Sshin} 8756590Sshin 8856590Sshinint 8956590Sshinrcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af) 9056590Sshin char **ahost; 9156590Sshin u_short rport; 9256590Sshin const char *locuser, *remuser, *cmd; 9356590Sshin int *fd2p; 9456590Sshin int af; 9556590Sshin{ 9655918Sshin struct addrinfo hints, *res, *ai; 9755918Sshin struct sockaddr_storage from; 981573Srgrimes fd_set reads; 9971579Sdeischen sigset_t oldmask, newmask; 1001573Srgrimes pid_t pid; 10155918Sshin int s, aport, lport, timo, error; 10285342Simp char c, *p; 10369575Sume int refused, nres; 10463682Sume char num[8]; 10563682Sume static char canonnamebuf[MAXDNAME]; /* is it proper here? */ 1061573Srgrimes 10785342Simp /* call rcmdsh() with specified remote shell if appropriate. */ 10885342Simp if (!issetugid() && (p = getenv("RSH"))) { 10985342Simp struct servent *sp = getservbyname("shell", "tcp"); 11085342Simp 11185342Simp if (sp && sp->s_port == rport) 11285342Simp return (rcmdsh(ahost, rport, locuser, remuser, 11385342Simp cmd, p)); 11485342Simp } 11585342Simp 11685342Simp /* use rsh(1) if non-root and remote port is shell. */ 11785342Simp if (geteuid()) { 11885342Simp struct servent *sp = getservbyname("shell", "tcp"); 11985342Simp 12085342Simp if (sp && sp->s_port == rport) 12185342Simp return (rcmdsh(ahost, rport, locuser, remuser, 12285342Simp cmd, NULL)); 12385342Simp } 12485342Simp 1251573Srgrimes pid = getpid(); 12655918Sshin 12755918Sshin memset(&hints, 0, sizeof(hints)); 12855918Sshin hints.ai_flags = AI_CANONNAME; 12956590Sshin hints.ai_family = af; 13055918Sshin hints.ai_socktype = SOCK_STREAM; 13155918Sshin hints.ai_protocol = 0; 13255918Sshin (void)snprintf(num, sizeof(num), "%d", ntohs(rport)); 13355918Sshin error = getaddrinfo(*ahost, num, &hints, &res); 13455918Sshin if (error) { 13555918Sshin fprintf(stderr, "rcmd: getaddrinfo: %s\n", 13655918Sshin gai_strerror(error)); 13755918Sshin if (error == EAI_SYSTEM) 13855918Sshin fprintf(stderr, "rcmd: getaddrinfo: %s\n", 13955918Sshin strerror(errno)); 1401573Srgrimes return (-1); 1411573Srgrimes } 14263682Sume 14363682Sume if (res->ai_canonname 14463682Sume && strlen(res->ai_canonname) + 1 < sizeof(canonnamebuf)) { 14563682Sume strncpy(canonnamebuf, res->ai_canonname, sizeof(canonnamebuf)); 14663682Sume *ahost = canonnamebuf; 14763682Sume } 14869575Sume nres = 0; 14969575Sume for (ai = res; ai; ai = ai->ai_next) 15069575Sume nres++; 15155918Sshin ai = res; 15269575Sume refused = 0; 15371579Sdeischen sigemptyset(&newmask); 15471579Sdeischen sigaddset(&newmask, SIGURG); 15571579Sdeischen _sigprocmask(SIG_BLOCK, (const sigset_t *)&newmask, &oldmask); 1561573Srgrimes for (timo = 1, lport = IPPORT_RESERVED - 1;;) { 15755918Sshin s = rresvport_af(&lport, ai->ai_family); 1581573Srgrimes if (s < 0) { 15957123Sshin if (errno != EAGAIN && ai->ai_next) { 16057123Sshin ai = ai->ai_next; 16157123Sshin continue; 16257123Sshin } 1631573Srgrimes if (errno == EAGAIN) 1641573Srgrimes (void)fprintf(stderr, 1651573Srgrimes "rcmd: socket: All ports in use\n"); 1661573Srgrimes else 1671573Srgrimes (void)fprintf(stderr, "rcmd: socket: %s\n", 1681573Srgrimes strerror(errno)); 16957123Sshin freeaddrinfo(res); 17071579Sdeischen _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, 17171579Sdeischen NULL); 1721573Srgrimes return (-1); 1731573Srgrimes } 17456698Sjasone _fcntl(s, F_SETOWN, pid); 17571579Sdeischen if (_connect(s, ai->ai_addr, ai->ai_addrlen) >= 0) 1761573Srgrimes break; 17756698Sjasone (void)_close(s); 1781573Srgrimes if (errno == EADDRINUSE) { 1791573Srgrimes lport--; 1801573Srgrimes continue; 1811573Srgrimes } 18269575Sume if (errno == ECONNREFUSED) 18369575Sume refused = 1; 18469575Sume if (ai->ai_next == NULL && (!refused || timo > 16)) { 18569575Sume (void)fprintf(stderr, "%s: %s\n", 18669575Sume *ahost, strerror(errno)); 18769575Sume freeaddrinfo(res); 18871579Sdeischen _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, 18971579Sdeischen NULL); 19069575Sume return (-1); 19164493Sume } 19269575Sume if (nres > 1) { 1931573Srgrimes int oerrno = errno; 1941573Srgrimes 195146187Sume getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr, 196146187Sume sizeof(paddr), NULL, 0, NI_NUMERICHOST); 1971573Srgrimes (void)fprintf(stderr, "connect to address %s: ", 19855918Sshin paddr); 1991573Srgrimes errno = oerrno; 2001573Srgrimes perror(0); 20169575Sume } 20269575Sume if ((ai = ai->ai_next) == NULL) { 20369575Sume /* refused && timo <= 16 */ 20469575Sume struct timespec time_to_sleep, time_remaining; 20569575Sume 20669575Sume time_to_sleep.tv_sec = timo; 20769575Sume time_to_sleep.tv_nsec = 0; 20869575Sume (void)_nanosleep(&time_to_sleep, &time_remaining); 20969575Sume timo *= 2; 21069575Sume ai = res; 21169575Sume refused = 0; 21269575Sume } 21369575Sume if (nres > 1) { 214146187Sume getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr, 215146187Sume sizeof(paddr), NULL, 0, NI_NUMERICHOST); 21655918Sshin fprintf(stderr, "Trying %s...\n", paddr); 2171573Srgrimes } 2181573Srgrimes } 2191573Srgrimes lport--; 2201573Srgrimes if (fd2p == 0) { 22156698Sjasone _write(s, "", 1); 2221573Srgrimes lport = 0; 2231573Srgrimes } else { 22455918Sshin int s2 = rresvport_af(&lport, ai->ai_family), s3; 225141920Sstefanf socklen_t len = ai->ai_addrlen; 2262592Scsgr int nfds; 2271573Srgrimes 2281573Srgrimes if (s2 < 0) 2291573Srgrimes goto bad; 23071579Sdeischen _listen(s2, 1); 2311573Srgrimes (void)snprintf(num, sizeof(num), "%d", lport); 23256698Sjasone if (_write(s, num, strlen(num)+1) != strlen(num)+1) { 2331573Srgrimes (void)fprintf(stderr, 2341573Srgrimes "rcmd: write (setting up stderr): %s\n", 2351573Srgrimes strerror(errno)); 23656698Sjasone (void)_close(s2); 2371573Srgrimes goto bad; 2381573Srgrimes } 2392592Scsgr nfds = max(s, s2)+1; 2402592Scsgr if(nfds > FD_SETSIZE) { 2412592Scsgr fprintf(stderr, "rcmd: too many files\n"); 24256698Sjasone (void)_close(s2); 2432592Scsgr goto bad; 2442592Scsgr } 24517543Speteragain: 2461573Srgrimes FD_ZERO(&reads); 2471573Srgrimes FD_SET(s, &reads); 2481573Srgrimes FD_SET(s2, &reads); 2491573Srgrimes errno = 0; 25071579Sdeischen if (_select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){ 2511573Srgrimes if (errno != 0) 2521573Srgrimes (void)fprintf(stderr, 2531573Srgrimes "rcmd: select (setting up stderr): %s\n", 2541573Srgrimes strerror(errno)); 2551573Srgrimes else 2561573Srgrimes (void)fprintf(stderr, 2571573Srgrimes "select: protocol failure in circuit setup\n"); 25856698Sjasone (void)_close(s2); 2591573Srgrimes goto bad; 2601573Srgrimes } 26171579Sdeischen s3 = _accept(s2, (struct sockaddr *)&from, &len); 26255918Sshin switch (from.ss_family) { 26355918Sshin case AF_INET: 26455918Sshin aport = ntohs(((struct sockaddr_in *)&from)->sin_port); 26555918Sshin break; 26655918Sshin#ifdef INET6 26755918Sshin case AF_INET6: 26855918Sshin aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port); 26955918Sshin break; 27055918Sshin#endif 27155918Sshin default: 27255918Sshin aport = 0; /* error */ 27355918Sshin break; 27455918Sshin } 27517543Speter /* 27617543Speter * XXX careful for ftp bounce attacks. If discovered, shut them 27717543Speter * down and check for the real auxiliary channel to connect. 27817543Speter */ 27955918Sshin if (aport == 20) { 28056698Sjasone _close(s3); 28117543Speter goto again; 28217543Speter } 28356698Sjasone (void)_close(s2); 2841573Srgrimes if (s3 < 0) { 2851573Srgrimes (void)fprintf(stderr, 2861573Srgrimes "rcmd: accept: %s\n", strerror(errno)); 2871573Srgrimes lport = 0; 2881573Srgrimes goto bad; 2891573Srgrimes } 2901573Srgrimes *fd2p = s3; 29155918Sshin if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) { 2921573Srgrimes (void)fprintf(stderr, 2931573Srgrimes "socket: protocol failure in circuit setup.\n"); 2941573Srgrimes goto bad2; 2951573Srgrimes } 2961573Srgrimes } 29756698Sjasone (void)_write(s, locuser, strlen(locuser)+1); 29856698Sjasone (void)_write(s, remuser, strlen(remuser)+1); 29956698Sjasone (void)_write(s, cmd, strlen(cmd)+1); 30056698Sjasone if (_read(s, &c, 1) != 1) { 3011573Srgrimes (void)fprintf(stderr, 3021573Srgrimes "rcmd: %s: %s\n", *ahost, strerror(errno)); 3031573Srgrimes goto bad2; 3041573Srgrimes } 3051573Srgrimes if (c != 0) { 30656698Sjasone while (_read(s, &c, 1) == 1) { 30756698Sjasone (void)_write(STDERR_FILENO, &c, 1); 3081573Srgrimes if (c == '\n') 3091573Srgrimes break; 3101573Srgrimes } 3111573Srgrimes goto bad2; 3121573Srgrimes } 31371579Sdeischen _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); 31455918Sshin freeaddrinfo(res); 3151573Srgrimes return (s); 3161573Srgrimesbad2: 3171573Srgrimes if (lport) 31856698Sjasone (void)_close(*fd2p); 3191573Srgrimesbad: 32056698Sjasone (void)_close(s); 32171579Sdeischen _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); 32255918Sshin freeaddrinfo(res); 3231573Srgrimes return (-1); 3241573Srgrimes} 3251573Srgrimes 3261573Srgrimesint 32755918Sshinrresvport(port) 32855918Sshin int *port; 3291573Srgrimes{ 33055918Sshin return rresvport_af(port, AF_INET); 33155918Sshin} 3321573Srgrimes 33355918Sshinint 33455918Sshinrresvport_af(alport, family) 33555918Sshin int *alport, family; 33655918Sshin{ 337111082Snectar int s; 33855918Sshin struct sockaddr_storage ss; 33955918Sshin u_short *sport; 34055918Sshin 34155918Sshin memset(&ss, 0, sizeof(ss)); 34255918Sshin ss.ss_family = family; 34355918Sshin switch (family) { 34455918Sshin case AF_INET: 34555918Sshin ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in); 34655918Sshin sport = &((struct sockaddr_in *)&ss)->sin_port; 34755918Sshin ((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY; 34855918Sshin break; 34955918Sshin#ifdef INET6 35055918Sshin case AF_INET6: 35155918Sshin ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6); 35255918Sshin sport = &((struct sockaddr_in6 *)&ss)->sin6_port; 35355918Sshin ((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any; 35455918Sshin break; 35555918Sshin#endif 35655918Sshin default: 35755918Sshin errno = EAFNOSUPPORT; 35855918Sshin return -1; 35955918Sshin } 36055918Sshin 36171579Sdeischen s = _socket(ss.ss_family, SOCK_STREAM, 0); 3621573Srgrimes if (s < 0) 3631573Srgrimes return (-1); 36417543Speter#if 0 /* compat_exact_traditional_rresvport_semantics */ 36517543Speter sin.sin_port = htons((u_short)*alport); 36671579Sdeischen if (_bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 36717543Speter return (s); 36817543Speter if (errno != EADDRINUSE) { 36956698Sjasone (void)_close(s); 37017543Speter return (-1); 37116034Speter } 37217543Speter#endif 37355918Sshin *sport = 0; 37456636Sshin if (bindresvport_sa(s, (struct sockaddr *)&ss) == -1) { 37556698Sjasone (void)_close(s); 37617543Speter return (-1); 3771573Srgrimes } 37855918Sshin *alport = (int)ntohs(*sport); 37917543Speter return (s); 3801573Srgrimes} 3811573Srgrimes 3821573Srgrimesint __check_rhosts_file = 1; 3831573Srgrimeschar *__rcmd_errstr; 3841573Srgrimes 3851573Srgrimesint 3861573Srgrimesruserok(rhost, superuser, ruser, luser) 3871573Srgrimes const char *rhost, *ruser, *luser; 3881573Srgrimes int superuser; 3891573Srgrimes{ 39056939Sshin struct addrinfo hints, *res, *r; 39156939Sshin int error; 39255918Sshin 39356939Sshin memset(&hints, 0, sizeof(hints)); 39456939Sshin hints.ai_family = PF_UNSPEC; 39556939Sshin hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 39656939Sshin error = getaddrinfo(rhost, "0", &hints, &res); 39756939Sshin if (error) 39856939Sshin return (-1); 3991573Srgrimes 40056939Sshin for (r = res; r; r = r->ai_next) { 40156939Sshin if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser, 40256939Sshin luser) == 0) { 40356939Sshin freeaddrinfo(res); 40456939Sshin return (0); 40555918Sshin } 4061573Srgrimes } 40756939Sshin freeaddrinfo(res); 40856939Sshin return (-1); 4091573Srgrimes} 4101573Srgrimes 4111573Srgrimes/* 4121573Srgrimes * New .rhosts strategy: We are passed an ip address. We spin through 4131573Srgrimes * hosts.equiv and .rhosts looking for a match. When the .rhosts only 4141573Srgrimes * has ip addresses, we don't have to trust a nameserver. When it 4151573Srgrimes * contains hostnames, we spin through the list of addresses the nameserver 4161573Srgrimes * gives us and look for a match. 4171573Srgrimes * 4181573Srgrimes * Returns 0 if ok, -1 if not ok. 4191573Srgrimes */ 4201573Srgrimesint 4211573Srgrimesiruserok(raddr, superuser, ruser, luser) 42239979Sdfr unsigned long raddr; 4231573Srgrimes int superuser; 4241573Srgrimes const char *ruser, *luser; 4251573Srgrimes{ 42669575Sume struct sockaddr_in sin; 42769575Sume 42869575Sume memset(&sin, 0, sizeof(sin)); 42969575Sume sin.sin_family = AF_INET; 43069575Sume sin.sin_len = sizeof(struct sockaddr_in); 43169575Sume memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); 43269575Sume return iruserok_sa((struct sockaddr *)&sin, sin.sin_len, superuser, 43369575Sume ruser, luser); 43455918Sshin} 43555918Sshin 43669575Sume/* 43769575Sume * AF independent extension of iruserok. 43869575Sume * 43969575Sume * Returns 0 if ok, -1 if not ok. 44069575Sume */ 44169575Sumeint 44269575Sumeiruserok_sa(ra, rlen, superuser, ruser, luser) 44369575Sume const void *ra; 44469575Sume int rlen; 44555918Sshin int superuser; 44655918Sshin const char *ruser, *luser; 44755918Sshin{ 44892889Sobrien char *cp; 4491573Srgrimes struct stat sbuf; 4501573Srgrimes struct passwd *pwd; 4511573Srgrimes FILE *hostf; 4521573Srgrimes uid_t uid; 4531573Srgrimes int first; 4541573Srgrimes char pbuf[MAXPATHLEN]; 45569575Sume const struct sockaddr *raddr; 45669575Sume struct sockaddr_storage ss; 4571573Srgrimes 45869575Sume /* avoid alignment issue */ 45969575Sume if (rlen > sizeof(ss)) 46069575Sume return(-1); 46169575Sume memcpy(&ss, ra, rlen); 46269575Sume raddr = (struct sockaddr *)&ss; 46355918Sshin 4641573Srgrimes first = 1; 4651573Srgrimes hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); 4661573Srgrimesagain: 4671573Srgrimes if (hostf) { 46869575Sume if (__ivaliduser_sa(hostf, raddr, rlen, luser, ruser) == 0) { 4691573Srgrimes (void)fclose(hostf); 4701573Srgrimes return (0); 4711573Srgrimes } 4721573Srgrimes (void)fclose(hostf); 4731573Srgrimes } 4741573Srgrimes if (first == 1 && (__check_rhosts_file || superuser)) { 4751573Srgrimes first = 0; 4761573Srgrimes if ((pwd = getpwnam(luser)) == NULL) 4771573Srgrimes return (-1); 4781573Srgrimes (void)strcpy(pbuf, pwd->pw_dir); 4791573Srgrimes (void)strcat(pbuf, "/.rhosts"); 4801573Srgrimes 4811573Srgrimes /* 4821573Srgrimes * Change effective uid while opening .rhosts. If root and 4831573Srgrimes * reading an NFS mounted file system, can't read files that 4841573Srgrimes * are protected read/write owner only. 4851573Srgrimes */ 4861573Srgrimes uid = geteuid(); 4871573Srgrimes (void)seteuid(pwd->pw_uid); 4881573Srgrimes hostf = fopen(pbuf, "r"); 4891573Srgrimes (void)seteuid(uid); 4901573Srgrimes 4911573Srgrimes if (hostf == NULL) 4921573Srgrimes return (-1); 4931573Srgrimes /* 4941573Srgrimes * If not a regular file, or is owned by someone other than 4951573Srgrimes * user or root or if writeable by anyone but the owner, quit. 4961573Srgrimes */ 4971573Srgrimes cp = NULL; 4981573Srgrimes if (lstat(pbuf, &sbuf) < 0) 4991573Srgrimes cp = ".rhosts lstat failed"; 5001573Srgrimes else if (!S_ISREG(sbuf.st_mode)) 5011573Srgrimes cp = ".rhosts not regular file"; 50271579Sdeischen else if (_fstat(fileno(hostf), &sbuf) < 0) 5031573Srgrimes cp = ".rhosts fstat failed"; 5041573Srgrimes else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) 5051573Srgrimes cp = "bad .rhosts owner"; 5061573Srgrimes else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) 5071573Srgrimes cp = ".rhosts writeable by other than owner"; 5081573Srgrimes /* If there were any problems, quit. */ 5091573Srgrimes if (cp) { 5101573Srgrimes __rcmd_errstr = cp; 5111573Srgrimes (void)fclose(hostf); 5121573Srgrimes return (-1); 5131573Srgrimes } 5141573Srgrimes goto again; 5151573Srgrimes } 5161573Srgrimes return (-1); 5171573Srgrimes} 5181573Srgrimes 5191573Srgrimes/* 52069575Sume * XXX 52169575Sume * Don't make static, used by lpd(8). 52256939Sshin * 52356939Sshin * Returns 0 if ok, -1 if not ok. 52456939Sshin */ 52556939Sshinint 52669575Sume__ivaliduser(hostf, raddr, luser, ruser) 52769575Sume FILE *hostf; 52869575Sume u_int32_t raddr; 52969575Sume const char *luser, *ruser; 53056939Sshin{ 53169575Sume struct sockaddr_in sin; 53256939Sshin 53369575Sume memset(&sin, 0, sizeof(sin)); 53469575Sume sin.sin_family = AF_INET; 53569575Sume sin.sin_len = sizeof(struct sockaddr_in); 53669575Sume memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); 53769575Sume return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len, 53869575Sume luser, ruser); 53969575Sume} 54069575Sume 54169575Sume/* 54269575Sume * Returns 0 if ok, -1 if not ok. 54369575Sume * 54469575Sume * XXX obsolete API. 54569575Sume */ 54669575Sumeint 54769575Sume__ivaliduser_af(hostf, raddr, luser, ruser, af, len) 54869575Sume FILE *hostf; 54969575Sume const void *raddr; 55069575Sume const char *luser, *ruser; 55169575Sume int af, len; 55269575Sume{ 55369575Sume struct sockaddr *sa = NULL; 55469575Sume struct sockaddr_in *sin = NULL; 55569575Sume#ifdef INET6 55669575Sume struct sockaddr_in6 *sin6 = NULL; 55769575Sume#endif 55869575Sume struct sockaddr_storage ss; 55969575Sume 56069575Sume memset(&ss, 0, sizeof(ss)); 56169575Sume switch (af) { 56256939Sshin case AF_INET: 56369575Sume if (len != sizeof(sin->sin_addr)) 56469575Sume return -1; 56569575Sume sin = (struct sockaddr_in *)&ss; 56669575Sume sin->sin_family = AF_INET; 56769575Sume sin->sin_len = sizeof(struct sockaddr_in); 56869575Sume memcpy(&sin->sin_addr, raddr, sizeof(sin->sin_addr)); 56956939Sshin break; 57056939Sshin#ifdef INET6 57156939Sshin case AF_INET6: 57269575Sume if (len != sizeof(sin6->sin6_addr)) 57369575Sume return -1; 57469575Sume /* you will lose scope info */ 57569575Sume sin6 = (struct sockaddr_in6 *)&ss; 57669575Sume sin6->sin6_family = AF_INET6; 57769575Sume sin6->sin6_len = sizeof(struct sockaddr_in6); 57869575Sume memcpy(&sin6->sin6_addr, raddr, sizeof(sin6->sin6_addr)); 57956939Sshin break; 58056939Sshin#endif 58169575Sume default: 58269575Sume return -1; 58356939Sshin } 58456939Sshin 58569575Sume sa = (struct sockaddr *)&ss; 58669575Sume return __ivaliduser_sa(hostf, sa, sa->sa_len, luser, ruser); 58756939Sshin} 58856939Sshin 5891573Srgrimesint 59069575Sume__ivaliduser_sa(hostf, raddr, salen, luser, ruser) 5911573Srgrimes FILE *hostf; 59269575Sume const struct sockaddr *raddr; 59369575Sume socklen_t salen; 5941573Srgrimes const char *luser, *ruser; 5951573Srgrimes{ 59692889Sobrien char *user, *p; 5971573Srgrimes int ch; 5981573Srgrimes char buf[MAXHOSTNAMELEN + 128]; /* host + login */ 59910059Swpaul char hname[MAXHOSTNAMELEN]; 6007183Swpaul /* Presumed guilty until proven innocent. */ 6017183Swpaul int userok = 0, hostok = 0; 6029978Swpaul#ifdef YP 6039978Swpaul char *ypdomain; 6041573Srgrimes 6059978Swpaul if (yp_get_default_domain(&ypdomain)) 6069978Swpaul ypdomain = NULL; 6079978Swpaul#else 6089978Swpaul#define ypdomain NULL 6099978Swpaul#endif 6107183Swpaul /* We need to get the damn hostname back for netgroup matching. */ 61169575Sume if (getnameinfo(raddr, salen, hname, sizeof(hname), NULL, 0, 61269575Sume NI_NAMEREQD) != 0) 613102369Sjdp hname[0] = '\0'; 6147183Swpaul 6151573Srgrimes while (fgets(buf, sizeof(buf), hostf)) { 6161573Srgrimes p = buf; 6171573Srgrimes /* Skip lines that are too long. */ 61858154Sbsd if (strchr(p, '\n') == NULL) { 6191573Srgrimes while ((ch = getc(hostf)) != '\n' && ch != EOF); 6201573Srgrimes continue; 6211573Srgrimes } 6229552Speter if (*p == '\n' || *p == '#') { 6239552Speter /* comment... */ 6249552Speter continue; 6259552Speter } 6261573Srgrimes while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') { 62752859Sache *p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p; 6281573Srgrimes p++; 6291573Srgrimes } 6301573Srgrimes if (*p == ' ' || *p == '\t') { 6311573Srgrimes *p++ = '\0'; 6321573Srgrimes while (*p == ' ' || *p == '\t') 6331573Srgrimes p++; 6341573Srgrimes user = p; 6351573Srgrimes while (*p != '\n' && *p != ' ' && 6361573Srgrimes *p != '\t' && *p != '\0') 6371573Srgrimes p++; 6381573Srgrimes } else 6391573Srgrimes user = p; 6401573Srgrimes *p = '\0'; 6417183Swpaul /* 6427183Swpaul * Do +/- and +@/-@ checking. This looks really nasty, 6437183Swpaul * but it matches SunOS's behavior so far as I can tell. 6447183Swpaul */ 6457183Swpaul switch(buf[0]) { 6467183Swpaul case '+': 6477183Swpaul if (!buf[1]) { /* '+' matches all hosts */ 6487183Swpaul hostok = 1; 6497183Swpaul break; 6507183Swpaul } 6517183Swpaul if (buf[1] == '@') /* match a host by netgroup */ 652102369Sjdp hostok = hname[0] != '\0' && 653102369Sjdp innetgr(&buf[2], hname, NULL, ypdomain); 6547183Swpaul else /* match a host by addr */ 65569575Sume hostok = __icheckhost(raddr, salen, 65669575Sume (char *)&buf[1]); 6577183Swpaul break; 6587183Swpaul case '-': /* reject '-' hosts and all their users */ 6597183Swpaul if (buf[1] == '@') { 660102369Sjdp if (hname[0] == '\0' || 661102369Sjdp innetgr(&buf[2], hname, NULL, ypdomain)) 6627183Swpaul return(-1); 6637183Swpaul } else { 66469575Sume if (__icheckhost(raddr, salen, 66569575Sume (char *)&buf[1])) 6667183Swpaul return(-1); 6677183Swpaul } 6687183Swpaul break; 6697183Swpaul default: /* if no '+' or '-', do a simple match */ 67069575Sume hostok = __icheckhost(raddr, salen, buf); 6717183Swpaul break; 6721573Srgrimes } 6737183Swpaul switch(*user) { 6747183Swpaul case '+': 6757183Swpaul if (!*(user+1)) { /* '+' matches all users */ 6767183Swpaul userok = 1; 6777183Swpaul break; 6787183Swpaul } 6797183Swpaul if (*(user+1) == '@') /* match a user by netgroup */ 6809978Swpaul userok = innetgr(user+2, NULL, ruser, ypdomain); 6817183Swpaul else /* match a user by direct specification */ 6827183Swpaul userok = !(strcmp(ruser, user+1)); 6837183Swpaul break; 6847183Swpaul case '-': /* if we matched a hostname, */ 6857183Swpaul if (hostok) { /* check for user field rejections */ 6867183Swpaul if (!*(user+1)) 6877183Swpaul return(-1); 6887183Swpaul if (*(user+1) == '@') { 6897183Swpaul if (innetgr(user+2, NULL, 6909978Swpaul ruser, ypdomain)) 6917183Swpaul return(-1); 6927183Swpaul } else { 6937183Swpaul if (!strcmp(ruser, user+1)) 6947183Swpaul return(-1); 6957183Swpaul } 6967183Swpaul } 6977183Swpaul break; 6987183Swpaul default: /* no rejections: try to match the user */ 6997183Swpaul if (hostok) 7007183Swpaul userok = !(strcmp(ruser,*user ? user : luser)); 7017183Swpaul break; 7027183Swpaul } 7037183Swpaul if (hostok && userok) 7047183Swpaul return(0); 7051573Srgrimes } 7061573Srgrimes return (-1); 7071573Srgrimes} 7081573Srgrimes 7091573Srgrimes/* 7101573Srgrimes * Returns "true" if match, 0 if no match. 7111573Srgrimes */ 7121573Srgrimesstatic int 71369575Sume__icheckhost(raddr, salen, lhost) 71469575Sume const struct sockaddr *raddr; 71569575Sume socklen_t salen; 71669575Sume const char *lhost; 7171573Srgrimes{ 71869575Sume struct sockaddr_in sin; 71969575Sume struct sockaddr_in6 *sin6; 72069575Sume struct addrinfo hints, *res, *r; 72169575Sume int error; 72269575Sume char h1[NI_MAXHOST], h2[NI_MAXHOST]; 7231573Srgrimes 72469575Sume if (raddr->sa_family == AF_INET6) { 72569575Sume sin6 = (struct sockaddr_in6 *)raddr; 72669575Sume if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 72769575Sume memset(&sin, 0, sizeof(sin)); 72869575Sume sin.sin_family = AF_INET; 72969575Sume sin.sin_len = sizeof(struct sockaddr_in); 73069575Sume memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], 73169575Sume sizeof(sin.sin_addr)); 73269575Sume raddr = (struct sockaddr *)&sin; 73369575Sume salen = sin.sin_len; 73469575Sume } 73555918Sshin } 7361573Srgrimes 73769575Sume h1[0] = '\0'; 73869575Sume if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 739146187Sume NI_NUMERICHOST) != 0) 7401573Srgrimes return (0); 7411573Srgrimes 74269575Sume /* Resolve laddr into sockaddr */ 74369575Sume memset(&hints, 0, sizeof(hints)); 74469575Sume hints.ai_family = raddr->sa_family; 74569575Sume hints.ai_socktype = SOCK_DGRAM; /*XXX dummy*/ 74669575Sume res = NULL; 74769575Sume error = getaddrinfo(lhost, "0", &hints, &res); 74869575Sume if (error) 74969575Sume return (0); 75069575Sume 75169575Sume for (r = res; r ; r = r->ai_next) { 75269575Sume h2[0] = '\0'; 75369575Sume if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 754146187Sume NULL, 0, NI_NUMERICHOST) != 0) 75569575Sume continue; 75669575Sume if (strcmp(h1, h2) == 0) { 75769575Sume freeaddrinfo(res); 75869575Sume return (1); 75955918Sshin } 76069575Sume } 7611573Srgrimes 76269575Sume /* No match. */ 76369575Sume freeaddrinfo(res); 76469575Sume return (0); 7651573Srgrimes} 766