1 /* 2 * Copyright (c) 1995, 1996, 1998 Theo de Raadt. All rights reserved. 3 * Copyright (c) 1983, 1993, 1994 4 * The Regents of the University of California. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the University nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/socket.h> 32 #include <sys/stat.h> 33 34 #include <netinet/in.h> 35 #include <arpa/inet.h> 36 37 #include <ctype.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <limits.h> 41 #include <netdb.h> 42 #include <netgroup.h> 43 #include <pwd.h> 44 #include <signal.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <syslog.h> 49 #include <unistd.h> 50 51 static int __ivaliduser_sa(FILE *, struct sockaddr *, socklen_t, 52 const char *, const char *); 53 static int __icheckhost(struct sockaddr *, socklen_t, const char *); 54 static char *__gethostloop(struct sockaddr *, socklen_t); 55 static int iruserok_sa(const void *, int, int, const char *, const char *); 56 57 int 58 ruserok(const char *rhost, int superuser, const char *ruser, const char *luser) 59 { 60 struct addrinfo hints, *res, *r; 61 int error; 62 63 memset(&hints, 0, sizeof(hints)); 64 hints.ai_family = PF_UNSPEC; 65 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 66 error = getaddrinfo(rhost, "0", &hints, &res); 67 if (error) 68 return (-1); 69 70 for (r = res; r; r = r->ai_next) { 71 if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser, 72 luser) == 0) { 73 freeaddrinfo(res); 74 return (0); 75 } 76 } 77 freeaddrinfo(res); 78 return (-1); 79 } 80 81 int 82 iruserok_sa(const void *raddr, int rlen, int superuser, const char *ruser, 83 const char *luser) 84 { 85 struct sockaddr *sa; 86 char *cp; 87 struct stat sbuf; 88 struct passwd pwstore, *pwd; 89 FILE *hostf; 90 uid_t uid; 91 int first; 92 char pbuf[PATH_MAX], pwbuf[_PW_BUF_LEN]; 93 94 sa = (struct sockaddr *)raddr; 95 first = 1; 96 hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re"); 97 again: 98 if (hostf) { 99 if (__ivaliduser_sa(hostf, sa, rlen, luser, ruser) == 0) { 100 (void)fclose(hostf); 101 return (0); 102 } 103 (void)fclose(hostf); 104 } 105 if (first == 1) { 106 int len; 107 108 first = 0; 109 pwd = NULL; 110 getpwnam_r(luser, &pwstore, pwbuf, sizeof(pwbuf), &pwd); 111 if (pwd == NULL) 112 return (-1); 113 len = snprintf(pbuf, sizeof pbuf, "%s/.rhosts", pwd->pw_dir); 114 if (len < 0 || len >= sizeof pbuf) 115 return (-1); 116 117 /* 118 * Change effective uid while opening .rhosts. If root and 119 * reading an NFS mounted file system, can't read files that 120 * are protected read/write owner only. 121 */ 122 uid = geteuid(); 123 (void)seteuid(pwd->pw_uid); 124 hostf = fopen(pbuf, "re"); 125 (void)seteuid(uid); 126 127 if (hostf == NULL) 128 return (-1); 129 /* 130 * If not a regular file, or is owned by someone other than 131 * user or root or if writeable by anyone but the owner, quit. 132 */ 133 cp = NULL; 134 if (lstat(pbuf, &sbuf) == -1) 135 cp = ".rhosts lstat failed"; 136 else if (!S_ISREG(sbuf.st_mode)) 137 cp = ".rhosts not regular file"; 138 else if (fstat(fileno(hostf), &sbuf) == -1) 139 cp = ".rhosts fstat failed"; 140 else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) 141 cp = "bad .rhosts owner"; 142 else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) 143 cp = ".rhosts writable by other than owner"; 144 /* If there were any problems, quit. */ 145 if (cp) { 146 (void)fclose(hostf); 147 return (-1); 148 } 149 goto again; 150 } 151 return (-1); 152 } 153 154 int 155 __ivaliduser_sa(FILE *hostf, struct sockaddr *raddr, socklen_t salen, 156 const char *luser, const char *ruser) 157 { 158 char *user, *p; 159 char *buf; 160 const char *auser, *ahost; 161 int hostok, userok; 162 char *rhost = (char *)-1; 163 char domain[HOST_NAME_MAX+1]; 164 size_t buflen; 165 166 getdomainname(domain, sizeof(domain)); 167 168 while ((buf = fgetln(hostf, &buflen))) { 169 p = buf; 170 if (*p == '#') 171 continue; 172 while (p < buf + buflen && *p != '\n' && *p != ' ' && *p != '\t') { 173 if (!isprint((unsigned char)*p)) 174 goto bail; 175 *p = isupper((unsigned char)*p) ? 176 tolower((unsigned char)*p) : *p; 177 p++; 178 } 179 if (p >= buf + buflen) 180 continue; 181 if (*p == ' ' || *p == '\t') { 182 *p++ = '\0'; 183 while (p < buf + buflen && (*p == ' ' || *p == '\t')) 184 p++; 185 if (p >= buf + buflen) 186 continue; 187 user = p; 188 while (p < buf + buflen && *p != '\n' && *p != ' ' && 189 *p != '\t') { 190 if (!isprint((unsigned char)*p)) 191 goto bail; 192 p++; 193 } 194 } else 195 user = p; 196 *p = '\0'; 197 198 if (p == buf) 199 continue; 200 201 auser = *user ? user : luser; 202 ahost = buf; 203 204 if (strlen(ahost) > HOST_NAME_MAX) 205 continue; 206 207 /* 208 * innetgr() must lookup a hostname (we do not attempt 209 * to change the semantics so that netgroups may have 210 * #.#.#.# addresses in the list.) 211 */ 212 if (ahost[0] == '+') 213 switch (ahost[1]) { 214 case '\0': 215 hostok = 1; 216 break; 217 case '@': 218 if (rhost == (char *)-1) 219 rhost = __gethostloop(raddr, salen); 220 hostok = 0; 221 if (rhost) 222 hostok = innetgr(&ahost[2], rhost, 223 NULL, domain); 224 break; 225 default: 226 hostok = __icheckhost(raddr, salen, &ahost[1]); 227 break; 228 } 229 else if (ahost[0] == '-') 230 switch (ahost[1]) { 231 case '\0': 232 hostok = -1; 233 break; 234 case '@': 235 if (rhost == (char *)-1) 236 rhost = __gethostloop(raddr, salen); 237 hostok = 0; 238 if (rhost) 239 hostok = -innetgr(&ahost[2], rhost, 240 NULL, domain); 241 break; 242 default: 243 hostok = -__icheckhost(raddr, salen, &ahost[1]); 244 break; 245 } 246 else 247 hostok = __icheckhost(raddr, salen, ahost); 248 249 250 if (auser[0] == '+') 251 switch (auser[1]) { 252 case '\0': 253 userok = 1; 254 break; 255 case '@': 256 userok = innetgr(&auser[2], NULL, ruser, 257 domain); 258 break; 259 default: 260 userok = strcmp(ruser, &auser[1]) ? 0 : 1; 261 break; 262 } 263 else if (auser[0] == '-') 264 switch (auser[1]) { 265 case '\0': 266 userok = -1; 267 break; 268 case '@': 269 userok = -innetgr(&auser[2], NULL, ruser, 270 domain); 271 break; 272 default: 273 userok = strcmp(ruser, &auser[1]) ? 0 : -1; 274 break; 275 } 276 else 277 userok = strcmp(ruser, auser) ? 0 : 1; 278 279 /* Check if one component did not match */ 280 if (hostok == 0 || userok == 0) 281 continue; 282 283 /* Check if we got a forbidden pair */ 284 if (userok <= -1 || hostok <= -1) 285 return (-1); 286 287 /* Check if we got a valid pair */ 288 if (hostok >= 1 && userok >= 1) 289 return (0); 290 } 291 bail: 292 return (-1); 293 } 294 295 /* 296 * Returns "true" if match, 0 if no match. If we do not find any 297 * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work. 298 */ 299 static int 300 __icheckhost(struct sockaddr *raddr, socklen_t salen, const char *lhost) 301 { 302 struct addrinfo hints, *res, *r; 303 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 304 int error; 305 const int niflags = NI_NUMERICHOST; 306 307 h1[0] = '\0'; 308 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 309 niflags) != 0) 310 return (0); 311 312 /* Resolve laddr into sockaddr */ 313 memset(&hints, 0, sizeof(hints)); 314 hints.ai_family = raddr->sa_family; 315 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 316 res = NULL; 317 error = getaddrinfo(lhost, "0", &hints, &res); 318 if (error) 319 return (0); 320 321 /* 322 * Try string comparisons between raddr and laddr. 323 */ 324 for (r = res; r; r = r->ai_next) { 325 h2[0] = '\0'; 326 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 327 NULL, 0, niflags) != 0) 328 continue; 329 if (strcmp(h1, h2) == 0) { 330 freeaddrinfo(res); 331 return (1); 332 } 333 } 334 335 /* No match. */ 336 freeaddrinfo(res); 337 return (0); 338 } 339 340 /* 341 * Return the hostname associated with the supplied address. 342 * Do a reverse lookup as well for security. If a loop cannot 343 * be found, pack the result of inet_ntoa() into the string. 344 */ 345 static char * 346 __gethostloop(struct sockaddr *raddr, socklen_t salen) 347 { 348 static char remotehost[NI_MAXHOST]; 349 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 350 struct addrinfo hints, *res, *r; 351 int error; 352 const int niflags = NI_NUMERICHOST; 353 354 h1[0] = remotehost[0] = '\0'; 355 if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost), 356 NULL, 0, NI_NAMEREQD) != 0) 357 return (NULL); 358 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 359 niflags) != 0) 360 return (NULL); 361 362 /* 363 * Look up the name and check that the supplied 364 * address is in the list 365 */ 366 memset(&hints, 0, sizeof(hints)); 367 hints.ai_family = raddr->sa_family; 368 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 369 hints.ai_flags = AI_CANONNAME; 370 res = NULL; 371 error = getaddrinfo(remotehost, "0", &hints, &res); 372 if (error) 373 return (NULL); 374 375 for (r = res; r; r = r->ai_next) { 376 h2[0] = '\0'; 377 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 378 NULL, 0, niflags) != 0) 379 continue; 380 if (strcmp(h1, h2) == 0) { 381 freeaddrinfo(res); 382 return (remotehost); 383 } 384 } 385 386 /* 387 * either the DNS adminstrator has made a configuration 388 * mistake, or someone has attempted to spoof us 389 */ 390 freeaddrinfo(res); 391 return (NULL); 392 } 393