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