1/* $NetBSD: iruserok.c,v 1.1.1.1 2011/04/13 18:15:42 elric Exp $ */ 2 3/* 4 * Copyright (c) 1983, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <config.h> 33 34#include <stdio.h> 35#include <ctype.h> 36#ifdef HAVE_SYS_TYPES_H 37#include <sys/types.h> 38#endif 39#ifdef HAVE_NETINET_IN_H 40#include <netinet/in.h> 41#endif 42#ifdef HAVE_NETINET_IN6_H 43#include <netinet/in6.h> 44#endif 45#ifdef HAVE_NETINET6_IN6_H 46#include <netinet6/in6.h> 47#endif 48#ifdef HAVE_RPCSVC_YPCLNT_H 49#include <rpcsvc/ypclnt.h> 50#endif 51 52#include <krb5/roken.h> 53 54int __check_rhosts_file = 1; 55char *__rcmd_errstr = 0; 56 57/* 58 * Returns "true" if match, 0 if no match. 59 */ 60static 61int 62__icheckhost(unsigned raddr, const char *lhost) 63{ 64 struct hostent *hp; 65 u_long laddr; 66 char **pp; 67 68 /* Try for raw ip address first. */ 69 if (isdigit((unsigned char)*lhost) 70 && (long)(laddr = inet_addr(lhost)) != -1) 71 return (raddr == laddr); 72 73 /* Better be a hostname. */ 74 if ((hp = gethostbyname(lhost)) == NULL) 75 return (0); 76 77 /* Spin through ip addresses. */ 78 for (pp = hp->h_addr_list; *pp; ++pp) 79 if (memcmp(&raddr, *pp, sizeof(u_long)) == 0) 80 return (1); 81 82 /* No match. */ 83 return (0); 84} 85 86/* 87 * Returns 0 if ok, -1 if not ok. 88 */ 89static 90int 91__ivaliduser(FILE *hostf, unsigned raddr, const char *luser, 92 const char *ruser) 93{ 94 char *user, *p; 95 int ch; 96 char buf[MaxHostNameLen + 128]; /* host + login */ 97 char hname[MaxHostNameLen]; 98 struct hostent *hp; 99 /* Presumed guilty until proven innocent. */ 100 int userok = 0, hostok = 0; 101#ifdef HAVE_YP_GET_DEFAULT_DOMAIN 102 char *ypdomain; 103 104 if (yp_get_default_domain(&ypdomain)) 105 ypdomain = NULL; 106#else 107#define ypdomain NULL 108#endif 109 /* We need to get the damn hostname back for netgroup matching. */ 110 if ((hp = gethostbyaddr((char *)&raddr, 111 sizeof(u_long), 112 AF_INET)) == NULL) 113 return (-1); 114 strlcpy(hname, hp->h_name, sizeof(hname)); 115 116 while (fgets(buf, sizeof(buf), hostf)) { 117 p = buf; 118 /* Skip lines that are too long. */ 119 if (strchr(p, '\n') == NULL) { 120 while ((ch = getc(hostf)) != '\n' && ch != EOF); 121 continue; 122 } 123 if (*p == '\n' || *p == '#') { 124 /* comment... */ 125 continue; 126 } 127 while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') { 128 if (isupper((unsigned char)*p)) 129 *p = tolower((unsigned char)*p); 130 p++; 131 } 132 if (*p == ' ' || *p == '\t') { 133 *p++ = '\0'; 134 while (*p == ' ' || *p == '\t') 135 p++; 136 user = p; 137 while (*p != '\n' && *p != ' ' && 138 *p != '\t' && *p != '\0') 139 p++; 140 } else 141 user = p; 142 *p = '\0'; 143 /* 144 * Do +/- and +@/-@ checking. This looks really nasty, 145 * but it matches SunOS's behavior so far as I can tell. 146 */ 147 switch(buf[0]) { 148 case '+': 149 if (!buf[1]) { /* '+' matches all hosts */ 150 hostok = 1; 151 break; 152 } 153 if (buf[1] == '@') /* match a host by netgroup */ 154 hostok = innetgr((char *)&buf[2], 155 (char *)&hname, NULL, ypdomain); 156 else /* match a host by addr */ 157 hostok = __icheckhost(raddr,(char *)&buf[1]); 158 break; 159 case '-': /* reject '-' hosts and all their users */ 160 if (buf[1] == '@') { 161 if (innetgr((char *)&buf[2], 162 (char *)&hname, NULL, ypdomain)) 163 return(-1); 164 } else { 165 if (__icheckhost(raddr,(char *)&buf[1])) 166 return(-1); 167 } 168 break; 169 default: /* if no '+' or '-', do a simple match */ 170 hostok = __icheckhost(raddr, buf); 171 break; 172 } 173 switch(*user) { 174 case '+': 175 if (!*(user+1)) { /* '+' matches all users */ 176 userok = 1; 177 break; 178 } 179 if (*(user+1) == '@') /* match a user by netgroup */ 180 userok = innetgr(user+2, NULL, (char *)ruser, 181 ypdomain); 182 else /* match a user by direct specification */ 183 userok = !(strcmp(ruser, user+1)); 184 break; 185 case '-': /* if we matched a hostname, */ 186 if (hostok) { /* check for user field rejections */ 187 if (!*(user+1)) 188 return(-1); 189 if (*(user+1) == '@') { 190 if (innetgr(user+2, NULL, 191 (char *)ruser, ypdomain)) 192 return(-1); 193 } else { 194 if (!strcmp(ruser, user+1)) 195 return(-1); 196 } 197 } 198 break; 199 default: /* no rejections: try to match the user */ 200 if (hostok) 201 userok = !(strcmp(ruser,*user ? user : luser)); 202 break; 203 } 204 if (hostok && userok) 205 return(0); 206 } 207 return (-1); 208} 209 210/* 211 * New .rhosts strategy: We are passed an ip address. We spin through 212 * hosts.equiv and .rhosts looking for a match. When the .rhosts only 213 * has ip addresses, we don't have to trust a nameserver. When it 214 * contains hostnames, we spin through the list of addresses the nameserver 215 * gives us and look for a match. 216 * 217 * Returns 0 if ok, -1 if not ok. 218 */ 219ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 220iruserok(unsigned raddr, int superuser, const char *ruser, const char *luser) 221{ 222 char *cp; 223 struct stat sbuf; 224 struct passwd *pwd; 225 FILE *hostf; 226 uid_t uid; 227 int first; 228 char pbuf[MaxPathLen]; 229 230 first = 1; 231 hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); 232again: 233 if (hostf) { 234 if (__ivaliduser(hostf, raddr, luser, ruser) == 0) { 235 fclose(hostf); 236 return (0); 237 } 238 fclose(hostf); 239 } 240 if (first == 1 && (__check_rhosts_file || superuser)) { 241 first = 0; 242 if ((pwd = k_getpwnam((char*)luser)) == NULL) 243 return (-1); 244 snprintf (pbuf, sizeof(pbuf), "%s/.rhosts", pwd->pw_dir); 245 246 /* 247 * Change effective uid while opening .rhosts. If root and 248 * reading an NFS mounted file system, can't read files that 249 * are protected read/write owner only. 250 */ 251 uid = geteuid(); 252 if (seteuid(pwd->pw_uid) < 0) 253 return (-1); 254 hostf = fopen(pbuf, "r"); 255 seteuid(uid); 256 257 if (hostf == NULL) 258 return (-1); 259 /* 260 * If not a regular file, or is owned by someone other than 261 * user or root or if writeable by anyone but the owner, quit. 262 */ 263 cp = NULL; 264 if (lstat(pbuf, &sbuf) < 0) 265 cp = ".rhosts lstat failed"; 266 else if (!S_ISREG(sbuf.st_mode)) 267 cp = ".rhosts not regular file"; 268 else if (fstat(fileno(hostf), &sbuf) < 0) 269 cp = ".rhosts fstat failed"; 270 else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) 271 cp = "bad .rhosts owner"; 272 else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) 273 cp = ".rhosts writeable by other than owner"; 274 /* If there were any problems, quit. */ 275 if (cp) { 276 __rcmd_errstr = cp; 277 fclose(hostf); 278 return (-1); 279 } 280 goto again; 281 } 282 return (-1); 283} 284