rcmd.c revision 141920
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 141920 2005-02-14 17:51:45Z stefanf $"); 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 6655918Sshin/* wrapper for KAME-special getnameinfo() */ 6755918Sshin#ifndef NI_WITHSCOPEID 6855918Sshin#define NI_WITHSCOPEID 0 6955918Sshin#endif 7055918Sshin 7192905Sobrienextern int innetgr( const char *, const char *, const char *, const char * ); 7217141Sjkh 732592Scsgr#define max(a, b) ((a > b) ? a : b) 742592Scsgr 7592941Sobrienint __ivaliduser(FILE *, u_int32_t, const char *, const char *); 7692941Sobrienint __ivaliduser_af(FILE *,const void *, const char *, const char *, int, int); 7792941Sobrienint __ivaliduser_sa(FILE *, const struct sockaddr *, socklen_t, const char *, 7892941Sobrien const char *); 7992941Sobrienstatic int __icheckhost(const struct sockaddr *, socklen_t, const char *); 801573Srgrimes 8169575Sumechar paddr[NI_MAXHOST]; 8255918Sshin 831573Srgrimesint 841573Srgrimesrcmd(ahost, rport, locuser, remuser, cmd, fd2p) 851573Srgrimes char **ahost; 861573Srgrimes u_short rport; 871573Srgrimes const char *locuser, *remuser, *cmd; 881573Srgrimes int *fd2p; 891573Srgrimes{ 9056590Sshin return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET); 9156590Sshin} 9256590Sshin 9356590Sshinint 9456590Sshinrcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af) 9556590Sshin char **ahost; 9656590Sshin u_short rport; 9756590Sshin const char *locuser, *remuser, *cmd; 9856590Sshin int *fd2p; 9956590Sshin int af; 10056590Sshin{ 10155918Sshin struct addrinfo hints, *res, *ai; 10255918Sshin struct sockaddr_storage from; 1031573Srgrimes fd_set reads; 10471579Sdeischen sigset_t oldmask, newmask; 1051573Srgrimes pid_t pid; 10655918Sshin int s, aport, lport, timo, error; 10785342Simp char c, *p; 10869575Sume int refused, nres; 10963682Sume char num[8]; 11063682Sume static char canonnamebuf[MAXDNAME]; /* is it proper here? */ 1111573Srgrimes 11285342Simp /* call rcmdsh() with specified remote shell if appropriate. */ 11385342Simp if (!issetugid() && (p = getenv("RSH"))) { 11485342Simp struct servent *sp = getservbyname("shell", "tcp"); 11585342Simp 11685342Simp if (sp && sp->s_port == rport) 11785342Simp return (rcmdsh(ahost, rport, locuser, remuser, 11885342Simp cmd, p)); 11985342Simp } 12085342Simp 12185342Simp /* use rsh(1) if non-root and remote port is shell. */ 12285342Simp if (geteuid()) { 12385342Simp struct servent *sp = getservbyname("shell", "tcp"); 12485342Simp 12585342Simp if (sp && sp->s_port == rport) 12685342Simp return (rcmdsh(ahost, rport, locuser, remuser, 12785342Simp cmd, NULL)); 12885342Simp } 12985342Simp 1301573Srgrimes pid = getpid(); 13155918Sshin 13255918Sshin memset(&hints, 0, sizeof(hints)); 13355918Sshin hints.ai_flags = AI_CANONNAME; 13456590Sshin hints.ai_family = af; 13555918Sshin hints.ai_socktype = SOCK_STREAM; 13655918Sshin hints.ai_protocol = 0; 13755918Sshin (void)snprintf(num, sizeof(num), "%d", ntohs(rport)); 13855918Sshin error = getaddrinfo(*ahost, num, &hints, &res); 13955918Sshin if (error) { 14055918Sshin fprintf(stderr, "rcmd: getaddrinfo: %s\n", 14155918Sshin gai_strerror(error)); 14255918Sshin if (error == EAI_SYSTEM) 14355918Sshin fprintf(stderr, "rcmd: getaddrinfo: %s\n", 14455918Sshin strerror(errno)); 1451573Srgrimes return (-1); 1461573Srgrimes } 14763682Sume 14863682Sume if (res->ai_canonname 14963682Sume && strlen(res->ai_canonname) + 1 < sizeof(canonnamebuf)) { 15063682Sume strncpy(canonnamebuf, res->ai_canonname, sizeof(canonnamebuf)); 15163682Sume *ahost = canonnamebuf; 15263682Sume } 15369575Sume nres = 0; 15469575Sume for (ai = res; ai; ai = ai->ai_next) 15569575Sume nres++; 15655918Sshin ai = res; 15769575Sume refused = 0; 15871579Sdeischen sigemptyset(&newmask); 15971579Sdeischen sigaddset(&newmask, SIGURG); 16071579Sdeischen _sigprocmask(SIG_BLOCK, (const sigset_t *)&newmask, &oldmask); 1611573Srgrimes for (timo = 1, lport = IPPORT_RESERVED - 1;;) { 16255918Sshin s = rresvport_af(&lport, ai->ai_family); 1631573Srgrimes if (s < 0) { 16457123Sshin if (errno != EAGAIN && ai->ai_next) { 16557123Sshin ai = ai->ai_next; 16657123Sshin continue; 16757123Sshin } 1681573Srgrimes if (errno == EAGAIN) 1691573Srgrimes (void)fprintf(stderr, 1701573Srgrimes "rcmd: socket: All ports in use\n"); 1711573Srgrimes else 1721573Srgrimes (void)fprintf(stderr, "rcmd: socket: %s\n", 1731573Srgrimes strerror(errno)); 17457123Sshin freeaddrinfo(res); 17571579Sdeischen _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, 17671579Sdeischen NULL); 1771573Srgrimes return (-1); 1781573Srgrimes } 17956698Sjasone _fcntl(s, F_SETOWN, pid); 18071579Sdeischen if (_connect(s, ai->ai_addr, ai->ai_addrlen) >= 0) 1811573Srgrimes break; 18256698Sjasone (void)_close(s); 1831573Srgrimes if (errno == EADDRINUSE) { 1841573Srgrimes lport--; 1851573Srgrimes continue; 1861573Srgrimes } 18769575Sume if (errno == ECONNREFUSED) 18869575Sume refused = 1; 18969575Sume if (ai->ai_next == NULL && (!refused || timo > 16)) { 19069575Sume (void)fprintf(stderr, "%s: %s\n", 19169575Sume *ahost, strerror(errno)); 19269575Sume freeaddrinfo(res); 19371579Sdeischen _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, 19471579Sdeischen NULL); 19569575Sume return (-1); 19664493Sume } 19769575Sume if (nres > 1) { 1981573Srgrimes int oerrno = errno; 1991573Srgrimes 20055918Sshin getnameinfo(ai->ai_addr, ai->ai_addrlen, 20155918Sshin paddr, sizeof(paddr), 20255918Sshin NULL, 0, 20355918Sshin NI_NUMERICHOST|NI_WITHSCOPEID); 2041573Srgrimes (void)fprintf(stderr, "connect to address %s: ", 20555918Sshin paddr); 2061573Srgrimes errno = oerrno; 2071573Srgrimes perror(0); 20869575Sume } 20969575Sume if ((ai = ai->ai_next) == NULL) { 21069575Sume /* refused && timo <= 16 */ 21169575Sume struct timespec time_to_sleep, time_remaining; 21269575Sume 21369575Sume time_to_sleep.tv_sec = timo; 21469575Sume time_to_sleep.tv_nsec = 0; 21569575Sume (void)_nanosleep(&time_to_sleep, &time_remaining); 21669575Sume timo *= 2; 21769575Sume ai = res; 21869575Sume refused = 0; 21969575Sume } 22069575Sume if (nres > 1) { 22155918Sshin getnameinfo(ai->ai_addr, ai->ai_addrlen, 22255918Sshin paddr, sizeof(paddr), 22355918Sshin NULL, 0, 22455918Sshin NI_NUMERICHOST|NI_WITHSCOPEID); 22555918Sshin fprintf(stderr, "Trying %s...\n", paddr); 2261573Srgrimes } 2271573Srgrimes } 2281573Srgrimes lport--; 2291573Srgrimes if (fd2p == 0) { 23056698Sjasone _write(s, "", 1); 2311573Srgrimes lport = 0; 2321573Srgrimes } else { 23355918Sshin int s2 = rresvport_af(&lport, ai->ai_family), s3; 234141920Sstefanf socklen_t len = ai->ai_addrlen; 2352592Scsgr int nfds; 2361573Srgrimes 2371573Srgrimes if (s2 < 0) 2381573Srgrimes goto bad; 23971579Sdeischen _listen(s2, 1); 2401573Srgrimes (void)snprintf(num, sizeof(num), "%d", lport); 24156698Sjasone if (_write(s, num, strlen(num)+1) != strlen(num)+1) { 2421573Srgrimes (void)fprintf(stderr, 2431573Srgrimes "rcmd: write (setting up stderr): %s\n", 2441573Srgrimes strerror(errno)); 24556698Sjasone (void)_close(s2); 2461573Srgrimes goto bad; 2471573Srgrimes } 2482592Scsgr nfds = max(s, s2)+1; 2492592Scsgr if(nfds > FD_SETSIZE) { 2502592Scsgr fprintf(stderr, "rcmd: too many files\n"); 25156698Sjasone (void)_close(s2); 2522592Scsgr goto bad; 2532592Scsgr } 25417543Speteragain: 2551573Srgrimes FD_ZERO(&reads); 2561573Srgrimes FD_SET(s, &reads); 2571573Srgrimes FD_SET(s2, &reads); 2581573Srgrimes errno = 0; 25971579Sdeischen if (_select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){ 2601573Srgrimes if (errno != 0) 2611573Srgrimes (void)fprintf(stderr, 2621573Srgrimes "rcmd: select (setting up stderr): %s\n", 2631573Srgrimes strerror(errno)); 2641573Srgrimes else 2651573Srgrimes (void)fprintf(stderr, 2661573Srgrimes "select: protocol failure in circuit setup\n"); 26756698Sjasone (void)_close(s2); 2681573Srgrimes goto bad; 2691573Srgrimes } 27071579Sdeischen s3 = _accept(s2, (struct sockaddr *)&from, &len); 27155918Sshin switch (from.ss_family) { 27255918Sshin case AF_INET: 27355918Sshin aport = ntohs(((struct sockaddr_in *)&from)->sin_port); 27455918Sshin break; 27555918Sshin#ifdef INET6 27655918Sshin case AF_INET6: 27755918Sshin aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port); 27855918Sshin break; 27955918Sshin#endif 28055918Sshin default: 28155918Sshin aport = 0; /* error */ 28255918Sshin break; 28355918Sshin } 28417543Speter /* 28517543Speter * XXX careful for ftp bounce attacks. If discovered, shut them 28617543Speter * down and check for the real auxiliary channel to connect. 28717543Speter */ 28855918Sshin if (aport == 20) { 28956698Sjasone _close(s3); 29017543Speter goto again; 29117543Speter } 29256698Sjasone (void)_close(s2); 2931573Srgrimes if (s3 < 0) { 2941573Srgrimes (void)fprintf(stderr, 2951573Srgrimes "rcmd: accept: %s\n", strerror(errno)); 2961573Srgrimes lport = 0; 2971573Srgrimes goto bad; 2981573Srgrimes } 2991573Srgrimes *fd2p = s3; 30055918Sshin if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) { 3011573Srgrimes (void)fprintf(stderr, 3021573Srgrimes "socket: protocol failure in circuit setup.\n"); 3031573Srgrimes goto bad2; 3041573Srgrimes } 3051573Srgrimes } 30656698Sjasone (void)_write(s, locuser, strlen(locuser)+1); 30756698Sjasone (void)_write(s, remuser, strlen(remuser)+1); 30856698Sjasone (void)_write(s, cmd, strlen(cmd)+1); 30956698Sjasone if (_read(s, &c, 1) != 1) { 3101573Srgrimes (void)fprintf(stderr, 3111573Srgrimes "rcmd: %s: %s\n", *ahost, strerror(errno)); 3121573Srgrimes goto bad2; 3131573Srgrimes } 3141573Srgrimes if (c != 0) { 31556698Sjasone while (_read(s, &c, 1) == 1) { 31656698Sjasone (void)_write(STDERR_FILENO, &c, 1); 3171573Srgrimes if (c == '\n') 3181573Srgrimes break; 3191573Srgrimes } 3201573Srgrimes goto bad2; 3211573Srgrimes } 32271579Sdeischen _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); 32355918Sshin freeaddrinfo(res); 3241573Srgrimes return (s); 3251573Srgrimesbad2: 3261573Srgrimes if (lport) 32756698Sjasone (void)_close(*fd2p); 3281573Srgrimesbad: 32956698Sjasone (void)_close(s); 33071579Sdeischen _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); 33155918Sshin freeaddrinfo(res); 3321573Srgrimes return (-1); 3331573Srgrimes} 3341573Srgrimes 3351573Srgrimesint 33655918Sshinrresvport(port) 33755918Sshin int *port; 3381573Srgrimes{ 33955918Sshin return rresvport_af(port, AF_INET); 34055918Sshin} 3411573Srgrimes 34255918Sshinint 34355918Sshinrresvport_af(alport, family) 34455918Sshin int *alport, family; 34555918Sshin{ 346111082Snectar int s; 34755918Sshin struct sockaddr_storage ss; 34855918Sshin u_short *sport; 34955918Sshin 35055918Sshin memset(&ss, 0, sizeof(ss)); 35155918Sshin ss.ss_family = family; 35255918Sshin switch (family) { 35355918Sshin case AF_INET: 35455918Sshin ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in); 35555918Sshin sport = &((struct sockaddr_in *)&ss)->sin_port; 35655918Sshin ((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY; 35755918Sshin break; 35855918Sshin#ifdef INET6 35955918Sshin case AF_INET6: 36055918Sshin ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6); 36155918Sshin sport = &((struct sockaddr_in6 *)&ss)->sin6_port; 36255918Sshin ((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any; 36355918Sshin break; 36455918Sshin#endif 36555918Sshin default: 36655918Sshin errno = EAFNOSUPPORT; 36755918Sshin return -1; 36855918Sshin } 36955918Sshin 37071579Sdeischen s = _socket(ss.ss_family, SOCK_STREAM, 0); 3711573Srgrimes if (s < 0) 3721573Srgrimes return (-1); 37317543Speter#if 0 /* compat_exact_traditional_rresvport_semantics */ 37417543Speter sin.sin_port = htons((u_short)*alport); 37571579Sdeischen if (_bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 37617543Speter return (s); 37717543Speter if (errno != EADDRINUSE) { 37856698Sjasone (void)_close(s); 37917543Speter return (-1); 38016034Speter } 38117543Speter#endif 38255918Sshin *sport = 0; 38356636Sshin if (bindresvport_sa(s, (struct sockaddr *)&ss) == -1) { 38456698Sjasone (void)_close(s); 38517543Speter return (-1); 3861573Srgrimes } 38755918Sshin *alport = (int)ntohs(*sport); 38817543Speter return (s); 3891573Srgrimes} 3901573Srgrimes 3911573Srgrimesint __check_rhosts_file = 1; 3921573Srgrimeschar *__rcmd_errstr; 3931573Srgrimes 3941573Srgrimesint 3951573Srgrimesruserok(rhost, superuser, ruser, luser) 3961573Srgrimes const char *rhost, *ruser, *luser; 3971573Srgrimes int superuser; 3981573Srgrimes{ 39956939Sshin struct addrinfo hints, *res, *r; 40056939Sshin int error; 40155918Sshin 40256939Sshin memset(&hints, 0, sizeof(hints)); 40356939Sshin hints.ai_family = PF_UNSPEC; 40456939Sshin hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 40556939Sshin error = getaddrinfo(rhost, "0", &hints, &res); 40656939Sshin if (error) 40756939Sshin return (-1); 4081573Srgrimes 40956939Sshin for (r = res; r; r = r->ai_next) { 41056939Sshin if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser, 41156939Sshin luser) == 0) { 41256939Sshin freeaddrinfo(res); 41356939Sshin return (0); 41455918Sshin } 4151573Srgrimes } 41656939Sshin freeaddrinfo(res); 41756939Sshin return (-1); 4181573Srgrimes} 4191573Srgrimes 4201573Srgrimes/* 4211573Srgrimes * New .rhosts strategy: We are passed an ip address. We spin through 4221573Srgrimes * hosts.equiv and .rhosts looking for a match. When the .rhosts only 4231573Srgrimes * has ip addresses, we don't have to trust a nameserver. When it 4241573Srgrimes * contains hostnames, we spin through the list of addresses the nameserver 4251573Srgrimes * gives us and look for a match. 4261573Srgrimes * 4271573Srgrimes * Returns 0 if ok, -1 if not ok. 4281573Srgrimes */ 4291573Srgrimesint 4301573Srgrimesiruserok(raddr, superuser, ruser, luser) 43139979Sdfr unsigned long raddr; 4321573Srgrimes int superuser; 4331573Srgrimes const char *ruser, *luser; 4341573Srgrimes{ 43569575Sume struct sockaddr_in sin; 43669575Sume 43769575Sume memset(&sin, 0, sizeof(sin)); 43869575Sume sin.sin_family = AF_INET; 43969575Sume sin.sin_len = sizeof(struct sockaddr_in); 44069575Sume memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); 44169575Sume return iruserok_sa((struct sockaddr *)&sin, sin.sin_len, superuser, 44269575Sume ruser, luser); 44355918Sshin} 44455918Sshin 44569575Sume/* 44669575Sume * AF independent extension of iruserok. 44769575Sume * 44869575Sume * Returns 0 if ok, -1 if not ok. 44969575Sume */ 45069575Sumeint 45169575Sumeiruserok_sa(ra, rlen, superuser, ruser, luser) 45269575Sume const void *ra; 45369575Sume int rlen; 45455918Sshin int superuser; 45555918Sshin const char *ruser, *luser; 45655918Sshin{ 45792889Sobrien char *cp; 4581573Srgrimes struct stat sbuf; 4591573Srgrimes struct passwd *pwd; 4601573Srgrimes FILE *hostf; 4611573Srgrimes uid_t uid; 4621573Srgrimes int first; 4631573Srgrimes char pbuf[MAXPATHLEN]; 46469575Sume const struct sockaddr *raddr; 46569575Sume struct sockaddr_storage ss; 4661573Srgrimes 46769575Sume /* avoid alignment issue */ 46869575Sume if (rlen > sizeof(ss)) 46969575Sume return(-1); 47069575Sume memcpy(&ss, ra, rlen); 47169575Sume raddr = (struct sockaddr *)&ss; 47255918Sshin 4731573Srgrimes first = 1; 4741573Srgrimes hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); 4751573Srgrimesagain: 4761573Srgrimes if (hostf) { 47769575Sume if (__ivaliduser_sa(hostf, raddr, rlen, luser, ruser) == 0) { 4781573Srgrimes (void)fclose(hostf); 4791573Srgrimes return (0); 4801573Srgrimes } 4811573Srgrimes (void)fclose(hostf); 4821573Srgrimes } 4831573Srgrimes if (first == 1 && (__check_rhosts_file || superuser)) { 4841573Srgrimes first = 0; 4851573Srgrimes if ((pwd = getpwnam(luser)) == NULL) 4861573Srgrimes return (-1); 4871573Srgrimes (void)strcpy(pbuf, pwd->pw_dir); 4881573Srgrimes (void)strcat(pbuf, "/.rhosts"); 4891573Srgrimes 4901573Srgrimes /* 4911573Srgrimes * Change effective uid while opening .rhosts. If root and 4921573Srgrimes * reading an NFS mounted file system, can't read files that 4931573Srgrimes * are protected read/write owner only. 4941573Srgrimes */ 4951573Srgrimes uid = geteuid(); 4961573Srgrimes (void)seteuid(pwd->pw_uid); 4971573Srgrimes hostf = fopen(pbuf, "r"); 4981573Srgrimes (void)seteuid(uid); 4991573Srgrimes 5001573Srgrimes if (hostf == NULL) 5011573Srgrimes return (-1); 5021573Srgrimes /* 5031573Srgrimes * If not a regular file, or is owned by someone other than 5041573Srgrimes * user or root or if writeable by anyone but the owner, quit. 5051573Srgrimes */ 5061573Srgrimes cp = NULL; 5071573Srgrimes if (lstat(pbuf, &sbuf) < 0) 5081573Srgrimes cp = ".rhosts lstat failed"; 5091573Srgrimes else if (!S_ISREG(sbuf.st_mode)) 5101573Srgrimes cp = ".rhosts not regular file"; 51171579Sdeischen else if (_fstat(fileno(hostf), &sbuf) < 0) 5121573Srgrimes cp = ".rhosts fstat failed"; 5131573Srgrimes else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) 5141573Srgrimes cp = "bad .rhosts owner"; 5151573Srgrimes else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) 5161573Srgrimes cp = ".rhosts writeable by other than owner"; 5171573Srgrimes /* If there were any problems, quit. */ 5181573Srgrimes if (cp) { 5191573Srgrimes __rcmd_errstr = cp; 5201573Srgrimes (void)fclose(hostf); 5211573Srgrimes return (-1); 5221573Srgrimes } 5231573Srgrimes goto again; 5241573Srgrimes } 5251573Srgrimes return (-1); 5261573Srgrimes} 5271573Srgrimes 5281573Srgrimes/* 52969575Sume * XXX 53069575Sume * Don't make static, used by lpd(8). 53156939Sshin * 53256939Sshin * Returns 0 if ok, -1 if not ok. 53356939Sshin */ 53456939Sshinint 53569575Sume__ivaliduser(hostf, raddr, luser, ruser) 53669575Sume FILE *hostf; 53769575Sume u_int32_t raddr; 53869575Sume const char *luser, *ruser; 53956939Sshin{ 54069575Sume struct sockaddr_in sin; 54156939Sshin 54269575Sume memset(&sin, 0, sizeof(sin)); 54369575Sume sin.sin_family = AF_INET; 54469575Sume sin.sin_len = sizeof(struct sockaddr_in); 54569575Sume memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); 54669575Sume return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len, 54769575Sume luser, ruser); 54869575Sume} 54969575Sume 55069575Sume/* 55169575Sume * Returns 0 if ok, -1 if not ok. 55269575Sume * 55369575Sume * XXX obsolete API. 55469575Sume */ 55569575Sumeint 55669575Sume__ivaliduser_af(hostf, raddr, luser, ruser, af, len) 55769575Sume FILE *hostf; 55869575Sume const void *raddr; 55969575Sume const char *luser, *ruser; 56069575Sume int af, len; 56169575Sume{ 56269575Sume struct sockaddr *sa = NULL; 56369575Sume struct sockaddr_in *sin = NULL; 56469575Sume#ifdef INET6 56569575Sume struct sockaddr_in6 *sin6 = NULL; 56669575Sume#endif 56769575Sume struct sockaddr_storage ss; 56869575Sume 56969575Sume memset(&ss, 0, sizeof(ss)); 57069575Sume switch (af) { 57156939Sshin case AF_INET: 57269575Sume if (len != sizeof(sin->sin_addr)) 57369575Sume return -1; 57469575Sume sin = (struct sockaddr_in *)&ss; 57569575Sume sin->sin_family = AF_INET; 57669575Sume sin->sin_len = sizeof(struct sockaddr_in); 57769575Sume memcpy(&sin->sin_addr, raddr, sizeof(sin->sin_addr)); 57856939Sshin break; 57956939Sshin#ifdef INET6 58056939Sshin case AF_INET6: 58169575Sume if (len != sizeof(sin6->sin6_addr)) 58269575Sume return -1; 58369575Sume /* you will lose scope info */ 58469575Sume sin6 = (struct sockaddr_in6 *)&ss; 58569575Sume sin6->sin6_family = AF_INET6; 58669575Sume sin6->sin6_len = sizeof(struct sockaddr_in6); 58769575Sume memcpy(&sin6->sin6_addr, raddr, sizeof(sin6->sin6_addr)); 58856939Sshin break; 58956939Sshin#endif 59069575Sume default: 59169575Sume return -1; 59256939Sshin } 59356939Sshin 59469575Sume sa = (struct sockaddr *)&ss; 59569575Sume return __ivaliduser_sa(hostf, sa, sa->sa_len, luser, ruser); 59656939Sshin} 59756939Sshin 5981573Srgrimesint 59969575Sume__ivaliduser_sa(hostf, raddr, salen, luser, ruser) 6001573Srgrimes FILE *hostf; 60169575Sume const struct sockaddr *raddr; 60269575Sume socklen_t salen; 6031573Srgrimes const char *luser, *ruser; 6041573Srgrimes{ 60592889Sobrien char *user, *p; 6061573Srgrimes int ch; 6071573Srgrimes char buf[MAXHOSTNAMELEN + 128]; /* host + login */ 60810059Swpaul char hname[MAXHOSTNAMELEN]; 6097183Swpaul /* Presumed guilty until proven innocent. */ 6107183Swpaul int userok = 0, hostok = 0; 6119978Swpaul#ifdef YP 6129978Swpaul char *ypdomain; 6131573Srgrimes 6149978Swpaul if (yp_get_default_domain(&ypdomain)) 6159978Swpaul ypdomain = NULL; 6169978Swpaul#else 6179978Swpaul#define ypdomain NULL 6189978Swpaul#endif 6197183Swpaul /* We need to get the damn hostname back for netgroup matching. */ 62069575Sume if (getnameinfo(raddr, salen, hname, sizeof(hname), NULL, 0, 62169575Sume NI_NAMEREQD) != 0) 622102369Sjdp hname[0] = '\0'; 6237183Swpaul 6241573Srgrimes while (fgets(buf, sizeof(buf), hostf)) { 6251573Srgrimes p = buf; 6261573Srgrimes /* Skip lines that are too long. */ 62758154Sbsd if (strchr(p, '\n') == NULL) { 6281573Srgrimes while ((ch = getc(hostf)) != '\n' && ch != EOF); 6291573Srgrimes continue; 6301573Srgrimes } 6319552Speter if (*p == '\n' || *p == '#') { 6329552Speter /* comment... */ 6339552Speter continue; 6349552Speter } 6351573Srgrimes while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') { 63652859Sache *p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p; 6371573Srgrimes p++; 6381573Srgrimes } 6391573Srgrimes if (*p == ' ' || *p == '\t') { 6401573Srgrimes *p++ = '\0'; 6411573Srgrimes while (*p == ' ' || *p == '\t') 6421573Srgrimes p++; 6431573Srgrimes user = p; 6441573Srgrimes while (*p != '\n' && *p != ' ' && 6451573Srgrimes *p != '\t' && *p != '\0') 6461573Srgrimes p++; 6471573Srgrimes } else 6481573Srgrimes user = p; 6491573Srgrimes *p = '\0'; 6507183Swpaul /* 6517183Swpaul * Do +/- and +@/-@ checking. This looks really nasty, 6527183Swpaul * but it matches SunOS's behavior so far as I can tell. 6537183Swpaul */ 6547183Swpaul switch(buf[0]) { 6557183Swpaul case '+': 6567183Swpaul if (!buf[1]) { /* '+' matches all hosts */ 6577183Swpaul hostok = 1; 6587183Swpaul break; 6597183Swpaul } 6607183Swpaul if (buf[1] == '@') /* match a host by netgroup */ 661102369Sjdp hostok = hname[0] != '\0' && 662102369Sjdp innetgr(&buf[2], hname, NULL, ypdomain); 6637183Swpaul else /* match a host by addr */ 66469575Sume hostok = __icheckhost(raddr, salen, 66569575Sume (char *)&buf[1]); 6667183Swpaul break; 6677183Swpaul case '-': /* reject '-' hosts and all their users */ 6687183Swpaul if (buf[1] == '@') { 669102369Sjdp if (hname[0] == '\0' || 670102369Sjdp innetgr(&buf[2], hname, NULL, ypdomain)) 6717183Swpaul return(-1); 6727183Swpaul } else { 67369575Sume if (__icheckhost(raddr, salen, 67469575Sume (char *)&buf[1])) 6757183Swpaul return(-1); 6767183Swpaul } 6777183Swpaul break; 6787183Swpaul default: /* if no '+' or '-', do a simple match */ 67969575Sume hostok = __icheckhost(raddr, salen, buf); 6807183Swpaul break; 6811573Srgrimes } 6827183Swpaul switch(*user) { 6837183Swpaul case '+': 6847183Swpaul if (!*(user+1)) { /* '+' matches all users */ 6857183Swpaul userok = 1; 6867183Swpaul break; 6877183Swpaul } 6887183Swpaul if (*(user+1) == '@') /* match a user by netgroup */ 6899978Swpaul userok = innetgr(user+2, NULL, ruser, ypdomain); 6907183Swpaul else /* match a user by direct specification */ 6917183Swpaul userok = !(strcmp(ruser, user+1)); 6927183Swpaul break; 6937183Swpaul case '-': /* if we matched a hostname, */ 6947183Swpaul if (hostok) { /* check for user field rejections */ 6957183Swpaul if (!*(user+1)) 6967183Swpaul return(-1); 6977183Swpaul if (*(user+1) == '@') { 6987183Swpaul if (innetgr(user+2, NULL, 6999978Swpaul ruser, ypdomain)) 7007183Swpaul return(-1); 7017183Swpaul } else { 7027183Swpaul if (!strcmp(ruser, user+1)) 7037183Swpaul return(-1); 7047183Swpaul } 7057183Swpaul } 7067183Swpaul break; 7077183Swpaul default: /* no rejections: try to match the user */ 7087183Swpaul if (hostok) 7097183Swpaul userok = !(strcmp(ruser,*user ? user : luser)); 7107183Swpaul break; 7117183Swpaul } 7127183Swpaul if (hostok && userok) 7137183Swpaul return(0); 7141573Srgrimes } 7151573Srgrimes return (-1); 7161573Srgrimes} 7171573Srgrimes 7181573Srgrimes/* 7191573Srgrimes * Returns "true" if match, 0 if no match. 72069575Sume * 72169575Sume * NI_WITHSCOPEID is useful for comparing sin6_scope_id portion 72269575Sume * if af == AF_INET6. 7231573Srgrimes */ 7241573Srgrimesstatic int 72569575Sume__icheckhost(raddr, salen, lhost) 72669575Sume const struct sockaddr *raddr; 72769575Sume socklen_t salen; 72869575Sume const char *lhost; 7291573Srgrimes{ 73069575Sume struct sockaddr_in sin; 73169575Sume struct sockaddr_in6 *sin6; 73269575Sume struct addrinfo hints, *res, *r; 73369575Sume int error; 73469575Sume char h1[NI_MAXHOST], h2[NI_MAXHOST]; 7351573Srgrimes 73669575Sume if (raddr->sa_family == AF_INET6) { 73769575Sume sin6 = (struct sockaddr_in6 *)raddr; 73869575Sume if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 73969575Sume memset(&sin, 0, sizeof(sin)); 74069575Sume sin.sin_family = AF_INET; 74169575Sume sin.sin_len = sizeof(struct sockaddr_in); 74269575Sume memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], 74369575Sume sizeof(sin.sin_addr)); 74469575Sume raddr = (struct sockaddr *)&sin; 74569575Sume salen = sin.sin_len; 74669575Sume } 74755918Sshin } 7481573Srgrimes 74969575Sume h1[0] = '\0'; 75069575Sume if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 75169575Sume NI_NUMERICHOST | NI_WITHSCOPEID) != 0) 7521573Srgrimes return (0); 7531573Srgrimes 75469575Sume /* Resolve laddr into sockaddr */ 75569575Sume memset(&hints, 0, sizeof(hints)); 75669575Sume hints.ai_family = raddr->sa_family; 75769575Sume hints.ai_socktype = SOCK_DGRAM; /*XXX dummy*/ 75869575Sume res = NULL; 75969575Sume error = getaddrinfo(lhost, "0", &hints, &res); 76069575Sume if (error) 76169575Sume return (0); 76269575Sume 76369575Sume for (r = res; r ; r = r->ai_next) { 76469575Sume h2[0] = '\0'; 76569575Sume if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 76669575Sume NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0) 76769575Sume continue; 76869575Sume if (strcmp(h1, h2) == 0) { 76969575Sume freeaddrinfo(res); 77069575Sume return (1); 77155918Sshin } 77269575Sume } 7731573Srgrimes 77469575Sume /* No match. */ 77569575Sume freeaddrinfo(res); 77669575Sume return (0); 7771573Srgrimes} 778