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/param.h> 32 #include <sys/socket.h> 33 #include <sys/stat.h> 34 35 #include <netinet/in.h> 36 #include <arpa/inet.h> 37 38 #include <signal.h> 39 #include <fcntl.h> 40 #include <netdb.h> 41 #include <unistd.h> 42 #include <pwd.h> 43 #include <errno.h> 44 #include <stdio.h> 45 #include <ctype.h> 46 #include <string.h> 47 #include <syslog.h> 48 #include <stdlib.h> 49 #include <netgroup.h> 50 51 int __ivaliduser(FILE *, in_addr_t, const char *, const char *); 52 int __ivaliduser_sa(FILE *, struct sockaddr *, socklen_t, 53 const char *, const char *); 54 static int __icheckhost(struct sockaddr *, socklen_t, const char *); 55 static char *__gethostloop(struct sockaddr *, socklen_t); 56 57 int __check_rhosts_file = 1; 58 char *__rcmd_errstr; 59 60 int 61 ruserok(const char *rhost, int superuser, const char *ruser, const char *luser) 62 { 63 struct addrinfo hints, *res, *r; 64 int error; 65 66 memset(&hints, 0, sizeof(hints)); 67 hints.ai_family = PF_UNSPEC; 68 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 69 error = getaddrinfo(rhost, "0", &hints, &res); 70 if (error) 71 return (-1); 72 73 for (r = res; r; r = r->ai_next) { 74 if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser, 75 luser) == 0) { 76 freeaddrinfo(res); 77 return (0); 78 } 79 } 80 freeaddrinfo(res); 81 return (-1); 82 } 83 84 /* 85 * New .rhosts strategy: We are passed an ip address. We spin through 86 * hosts.equiv and .rhosts looking for a match. When the .rhosts only 87 * has ip addresses, we don't have to trust a nameserver. When it 88 * contains hostnames, we spin through the list of addresses the nameserver 89 * gives us and look for a match. 90 * 91 * Returns 0 if ok, -1 if not ok. 92 */ 93 int 94 iruserok(u_int32_t raddr, int superuser, const char *ruser, const char *luser) 95 { 96 struct sockaddr_in sin; 97 98 memset(&sin, 0, sizeof(sin)); 99 sin.sin_family = AF_INET; 100 sin.sin_len = sizeof(struct sockaddr_in); 101 memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); 102 return iruserok_sa(&sin, sizeof(struct sockaddr_in), superuser, ruser, 103 luser); 104 } 105 106 int 107 iruserok_sa(const void *raddr, int rlen, int superuser, const char *ruser, 108 const char *luser) 109 { 110 struct sockaddr *sa; 111 char *cp; 112 struct stat sbuf; 113 struct passwd *pwd; 114 FILE *hostf; 115 uid_t uid; 116 int first; 117 char pbuf[MAXPATHLEN]; 118 119 sa = (struct sockaddr *)raddr; 120 first = 1; 121 hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); 122 again: 123 if (hostf) { 124 if (__ivaliduser_sa(hostf, sa, rlen, luser, ruser) == 0) { 125 (void)fclose(hostf); 126 return (0); 127 } 128 (void)fclose(hostf); 129 } 130 if (first == 1 && (__check_rhosts_file || superuser)) { 131 int len; 132 133 first = 0; 134 if ((pwd = getpwnam(luser)) == NULL) 135 return (-1); 136 len = snprintf(pbuf, sizeof pbuf, "%s/.rhosts", pwd->pw_dir); 137 if (len < 0 || len >= sizeof pbuf) 138 return (-1); 139 140 /* 141 * Change effective uid while opening .rhosts. If root and 142 * reading an NFS mounted file system, can't read files that 143 * are protected read/write owner only. 144 */ 145 uid = geteuid(); 146 (void)seteuid(pwd->pw_uid); 147 hostf = fopen(pbuf, "r"); 148 (void)seteuid(uid); 149 150 if (hostf == NULL) 151 return (-1); 152 /* 153 * If not a regular file, or is owned by someone other than 154 * user or root or if writeable by anyone but the owner, quit. 155 */ 156 cp = NULL; 157 if (lstat(pbuf, &sbuf) < 0) 158 cp = ".rhosts lstat failed"; 159 else if (!S_ISREG(sbuf.st_mode)) 160 cp = ".rhosts not regular file"; 161 else if (fstat(fileno(hostf), &sbuf) < 0) 162 cp = ".rhosts fstat failed"; 163 else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) 164 cp = "bad .rhosts owner"; 165 else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) 166 cp = ".rhosts writable by other than owner"; 167 /* If there were any problems, quit. */ 168 if (cp) { 169 __rcmd_errstr = cp; 170 (void)fclose(hostf); 171 return (-1); 172 } 173 goto again; 174 } 175 return (-1); 176 } 177 178 /* 179 * XXX 180 * Don't make static, used by lpd(8). 181 * 182 * Returns 0 if ok, -1 if not ok. 183 */ 184 int 185 __ivaliduser(FILE *hostf, in_addr_t raddrl, const char *luser, 186 const char *ruser) 187 { 188 struct sockaddr_in sin; 189 190 memset(&sin, 0, sizeof(sin)); 191 sin.sin_family = AF_INET; 192 sin.sin_len = sizeof(struct sockaddr_in); 193 memcpy(&sin.sin_addr, &raddrl, sizeof(sin.sin_addr)); 194 return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len, 195 luser, ruser); 196 } 197 198 int 199 __ivaliduser_sa(FILE *hostf, struct sockaddr *raddr, socklen_t salen, 200 const char *luser, const char *ruser) 201 { 202 char *user, *p; 203 char *buf; 204 const char *auser, *ahost; 205 int hostok, userok; 206 char *rhost = (char *)-1; 207 char domain[MAXHOSTNAMELEN]; 208 size_t buflen; 209 210 getdomainname(domain, sizeof(domain)); 211 212 while ((buf = fgetln(hostf, &buflen))) { 213 p = buf; 214 if (*p == '#') 215 continue; 216 while (p < buf + buflen && *p != '\n' && *p != ' ' && *p != '\t') { 217 if (!isprint(*p)) 218 goto bail; 219 *p = isupper(*p) ? tolower(*p) : *p; 220 p++; 221 } 222 if (p >= buf + buflen) 223 continue; 224 if (*p == ' ' || *p == '\t') { 225 *p++ = '\0'; 226 while (p < buf + buflen && (*p == ' ' || *p == '\t')) 227 p++; 228 if (p >= buf + buflen) 229 continue; 230 user = p; 231 while (p < buf + buflen && *p != '\n' && *p != ' ' && 232 *p != '\t') { 233 if (!isprint(*p)) 234 goto bail; 235 p++; 236 } 237 } else 238 user = p; 239 *p = '\0'; 240 241 if (p == buf) 242 continue; 243 244 auser = *user ? user : luser; 245 ahost = buf; 246 247 if (strlen(ahost) >= MAXHOSTNAMELEN) 248 continue; 249 250 /* 251 * innetgr() must lookup a hostname (we do not attempt 252 * to change the semantics so that netgroups may have 253 * #.#.#.# addresses in the list.) 254 */ 255 if (ahost[0] == '+') 256 switch (ahost[1]) { 257 case '\0': 258 hostok = 1; 259 break; 260 case '@': 261 if (rhost == (char *)-1) 262 rhost = __gethostloop(raddr, salen); 263 hostok = 0; 264 if (rhost) 265 hostok = innetgr(&ahost[2], rhost, 266 NULL, domain); 267 break; 268 default: 269 hostok = __icheckhost(raddr, salen, &ahost[1]); 270 break; 271 } 272 else if (ahost[0] == '-') 273 switch (ahost[1]) { 274 case '\0': 275 hostok = -1; 276 break; 277 case '@': 278 if (rhost == (char *)-1) 279 rhost = __gethostloop(raddr, salen); 280 hostok = 0; 281 if (rhost) 282 hostok = -innetgr(&ahost[2], rhost, 283 NULL, domain); 284 break; 285 default: 286 hostok = -__icheckhost(raddr, salen, &ahost[1]); 287 break; 288 } 289 else 290 hostok = __icheckhost(raddr, salen, ahost); 291 292 293 if (auser[0] == '+') 294 switch (auser[1]) { 295 case '\0': 296 userok = 1; 297 break; 298 case '@': 299 userok = innetgr(&auser[2], NULL, ruser, 300 domain); 301 break; 302 default: 303 userok = strcmp(ruser, &auser[1]) ? 0 : 1; 304 break; 305 } 306 else if (auser[0] == '-') 307 switch (auser[1]) { 308 case '\0': 309 userok = -1; 310 break; 311 case '@': 312 userok = -innetgr(&auser[2], NULL, ruser, 313 domain); 314 break; 315 default: 316 userok = strcmp(ruser, &auser[1]) ? 0 : -1; 317 break; 318 } 319 else 320 userok = strcmp(ruser, auser) ? 0 : 1; 321 322 /* Check if one component did not match */ 323 if (hostok == 0 || userok == 0) 324 continue; 325 326 /* Check if we got a forbidden pair */ 327 if (userok <= -1 || hostok <= -1) 328 return (-1); 329 330 /* Check if we got a valid pair */ 331 if (hostok >= 1 && userok >= 1) 332 return (0); 333 } 334 bail: 335 return (-1); 336 } 337 338 /* 339 * Returns "true" if match, 0 if no match. If we do not find any 340 * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work. 341 */ 342 static int 343 __icheckhost(struct sockaddr *raddr, socklen_t salen, const char *lhost) 344 { 345 struct addrinfo hints, *res, *r; 346 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 347 int error; 348 const int niflags = NI_NUMERICHOST; 349 350 h1[0] = '\0'; 351 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 352 niflags) != 0) 353 return (0); 354 355 /* Resolve laddr into sockaddr */ 356 memset(&hints, 0, sizeof(hints)); 357 hints.ai_family = raddr->sa_family; 358 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 359 res = NULL; 360 error = getaddrinfo(lhost, "0", &hints, &res); 361 if (error) 362 return (0); 363 364 /* 365 * Try string comparisons between raddr and laddr. 366 */ 367 for (r = res; r; r = r->ai_next) { 368 h2[0] = '\0'; 369 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 370 NULL, 0, niflags) != 0) 371 continue; 372 if (strcmp(h1, h2) == 0) { 373 freeaddrinfo(res); 374 return (1); 375 } 376 } 377 378 /* No match. */ 379 freeaddrinfo(res); 380 return (0); 381 } 382 383 /* 384 * Return the hostname associated with the supplied address. 385 * Do a reverse lookup as well for security. If a loop cannot 386 * be found, pack the result of inet_ntoa() into the string. 387 */ 388 static char * 389 __gethostloop(struct sockaddr *raddr, socklen_t salen) 390 { 391 static char remotehost[NI_MAXHOST]; 392 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 393 struct addrinfo hints, *res, *r; 394 int error; 395 const int niflags = NI_NUMERICHOST; 396 397 h1[0] = remotehost[0] = '\0'; 398 if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost), 399 NULL, 0, NI_NAMEREQD) != 0) 400 return (NULL); 401 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 402 niflags) != 0) 403 return (NULL); 404 405 /* 406 * Look up the name and check that the supplied 407 * address is in the list 408 */ 409 memset(&hints, 0, sizeof(hints)); 410 hints.ai_family = raddr->sa_family; 411 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 412 hints.ai_flags = AI_CANONNAME; 413 res = NULL; 414 error = getaddrinfo(remotehost, "0", &hints, &res); 415 if (error) 416 return (NULL); 417 418 for (r = res; r; r = r->ai_next) { 419 h2[0] = '\0'; 420 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 421 NULL, 0, niflags) != 0) 422 continue; 423 if (strcmp(h1, h2) == 0) { 424 freeaddrinfo(res); 425 return (remotehost); 426 } 427 } 428 429 /* 430 * either the DNS adminstrator has made a configuration 431 * mistake, or someone has attempted to spoof us 432 */ 433 syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s", 434 h1, res->ai_canonname ? res->ai_canonname : remotehost); 435 freeaddrinfo(res); 436 return (NULL); 437 } 438