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