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 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[PATH_MAX]; 118 119 sa = (struct sockaddr *)raddr; 120 first = 1; 121 hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re"); 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, "re"); 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((unsigned char)*p)) 218 goto bail; 219 *p = isupper((unsigned char)*p) ? 220 tolower((unsigned char)*p) : *p; 221 p++; 222 } 223 if (p >= buf + buflen) 224 continue; 225 if (*p == ' ' || *p == '\t') { 226 *p++ = '\0'; 227 while (p < buf + buflen && (*p == ' ' || *p == '\t')) 228 p++; 229 if (p >= buf + buflen) 230 continue; 231 user = p; 232 while (p < buf + buflen && *p != '\n' && *p != ' ' && 233 *p != '\t') { 234 if (!isprint((unsigned char)*p)) 235 goto bail; 236 p++; 237 } 238 } else 239 user = p; 240 *p = '\0'; 241 242 if (p == buf) 243 continue; 244 245 auser = *user ? user : luser; 246 ahost = buf; 247 248 if (strlen(ahost) >= MAXHOSTNAMELEN) 249 continue; 250 251 /* 252 * innetgr() must lookup a hostname (we do not attempt 253 * to change the semantics so that netgroups may have 254 * #.#.#.# addresses in the list.) 255 */ 256 if (ahost[0] == '+') 257 switch (ahost[1]) { 258 case '\0': 259 hostok = 1; 260 break; 261 case '@': 262 if (rhost == (char *)-1) 263 rhost = __gethostloop(raddr, salen); 264 hostok = 0; 265 if (rhost) 266 hostok = innetgr(&ahost[2], rhost, 267 NULL, domain); 268 break; 269 default: 270 hostok = __icheckhost(raddr, salen, &ahost[1]); 271 break; 272 } 273 else if (ahost[0] == '-') 274 switch (ahost[1]) { 275 case '\0': 276 hostok = -1; 277 break; 278 case '@': 279 if (rhost == (char *)-1) 280 rhost = __gethostloop(raddr, salen); 281 hostok = 0; 282 if (rhost) 283 hostok = -innetgr(&ahost[2], rhost, 284 NULL, domain); 285 break; 286 default: 287 hostok = -__icheckhost(raddr, salen, &ahost[1]); 288 break; 289 } 290 else 291 hostok = __icheckhost(raddr, salen, ahost); 292 293 294 if (auser[0] == '+') 295 switch (auser[1]) { 296 case '\0': 297 userok = 1; 298 break; 299 case '@': 300 userok = innetgr(&auser[2], NULL, ruser, 301 domain); 302 break; 303 default: 304 userok = strcmp(ruser, &auser[1]) ? 0 : 1; 305 break; 306 } 307 else if (auser[0] == '-') 308 switch (auser[1]) { 309 case '\0': 310 userok = -1; 311 break; 312 case '@': 313 userok = -innetgr(&auser[2], NULL, ruser, 314 domain); 315 break; 316 default: 317 userok = strcmp(ruser, &auser[1]) ? 0 : -1; 318 break; 319 } 320 else 321 userok = strcmp(ruser, auser) ? 0 : 1; 322 323 /* Check if one component did not match */ 324 if (hostok == 0 || userok == 0) 325 continue; 326 327 /* Check if we got a forbidden pair */ 328 if (userok <= -1 || hostok <= -1) 329 return (-1); 330 331 /* Check if we got a valid pair */ 332 if (hostok >= 1 && userok >= 1) 333 return (0); 334 } 335 bail: 336 return (-1); 337 } 338 339 /* 340 * Returns "true" if match, 0 if no match. If we do not find any 341 * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work. 342 */ 343 static int 344 __icheckhost(struct sockaddr *raddr, socklen_t salen, const char *lhost) 345 { 346 struct addrinfo hints, *res, *r; 347 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 348 int error; 349 const int niflags = NI_NUMERICHOST; 350 351 h1[0] = '\0'; 352 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 353 niflags) != 0) 354 return (0); 355 356 /* Resolve laddr into sockaddr */ 357 memset(&hints, 0, sizeof(hints)); 358 hints.ai_family = raddr->sa_family; 359 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 360 res = NULL; 361 error = getaddrinfo(lhost, "0", &hints, &res); 362 if (error) 363 return (0); 364 365 /* 366 * Try string comparisons between raddr and laddr. 367 */ 368 for (r = res; r; r = r->ai_next) { 369 h2[0] = '\0'; 370 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 371 NULL, 0, niflags) != 0) 372 continue; 373 if (strcmp(h1, h2) == 0) { 374 freeaddrinfo(res); 375 return (1); 376 } 377 } 378 379 /* No match. */ 380 freeaddrinfo(res); 381 return (0); 382 } 383 384 /* 385 * Return the hostname associated with the supplied address. 386 * Do a reverse lookup as well for security. If a loop cannot 387 * be found, pack the result of inet_ntoa() into the string. 388 */ 389 static char * 390 __gethostloop(struct sockaddr *raddr, socklen_t salen) 391 { 392 static char remotehost[NI_MAXHOST]; 393 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 394 struct addrinfo hints, *res, *r; 395 int error; 396 const int niflags = NI_NUMERICHOST; 397 398 h1[0] = remotehost[0] = '\0'; 399 if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost), 400 NULL, 0, NI_NAMEREQD) != 0) 401 return (NULL); 402 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 403 niflags) != 0) 404 return (NULL); 405 406 /* 407 * Look up the name and check that the supplied 408 * address is in the list 409 */ 410 memset(&hints, 0, sizeof(hints)); 411 hints.ai_family = raddr->sa_family; 412 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 413 hints.ai_flags = AI_CANONNAME; 414 res = NULL; 415 error = getaddrinfo(remotehost, "0", &hints, &res); 416 if (error) 417 return (NULL); 418 419 for (r = res; r; r = r->ai_next) { 420 h2[0] = '\0'; 421 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 422 NULL, 0, niflags) != 0) 423 continue; 424 if (strcmp(h1, h2) == 0) { 425 freeaddrinfo(res); 426 return (remotehost); 427 } 428 } 429 430 /* 431 * either the DNS adminstrator has made a configuration 432 * mistake, or someone has attempted to spoof us 433 */ 434 syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s", 435 h1, res->ai_canonname ? res->ai_canonname : remotehost); 436 freeaddrinfo(res); 437 return (NULL); 438 } 439