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 59 ruserok(const char *rhost, int superuser, const char *ruser, const char *luser) 60 { 61 struct addrinfo hints, *res, *r; 62 int error; 63 64 memset(&hints, 0, sizeof(hints)); 65 hints.ai_family = PF_UNSPEC; 66 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 67 error = getaddrinfo(rhost, "0", &hints, &res); 68 if (error) 69 return (-1); 70 71 for (r = res; r; r = r->ai_next) { 72 if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser, 73 luser) == 0) { 74 freeaddrinfo(res); 75 return (0); 76 } 77 } 78 freeaddrinfo(res); 79 return (-1); 80 } 81 82 /* 83 * New .rhosts strategy: We are passed an ip address. We spin through 84 * hosts.equiv and .rhosts looking for a match. When the .rhosts only 85 * has ip addresses, we don't have to trust a nameserver. When it 86 * contains hostnames, we spin through the list of addresses the nameserver 87 * gives us and look for a match. 88 * 89 * Returns 0 if ok, -1 if not ok. 90 */ 91 int 92 iruserok(u_int32_t raddr, int superuser, const char *ruser, const char *luser) 93 { 94 struct sockaddr_in sin; 95 96 memset(&sin, 0, sizeof(sin)); 97 sin.sin_family = AF_INET; 98 sin.sin_len = sizeof(struct sockaddr_in); 99 memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); 100 return iruserok_sa(&sin, sizeof(struct sockaddr_in), superuser, ruser, 101 luser); 102 } 103 104 int 105 iruserok_sa(const void *raddr, int rlen, int superuser, const char *ruser, 106 const char *luser) 107 { 108 struct sockaddr *sa; 109 char *cp; 110 struct stat sbuf; 111 struct passwd pwstore, *pwd; 112 FILE *hostf; 113 uid_t uid; 114 int first; 115 char pbuf[PATH_MAX], pwbuf[_PW_BUF_LEN]; 116 117 sa = (struct sockaddr *)raddr; 118 first = 1; 119 hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re"); 120 again: 121 if (hostf) { 122 if (__ivaliduser_sa(hostf, sa, rlen, luser, ruser) == 0) { 123 (void)fclose(hostf); 124 return (0); 125 } 126 (void)fclose(hostf); 127 } 128 if (first == 1) { 129 int len; 130 131 first = 0; 132 pwd = NULL; 133 getpwnam_r(luser, &pwstore, pwbuf, sizeof(pwbuf), &pwd); 134 if (pwd == 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 (void)fclose(hostf); 170 return (-1); 171 } 172 goto again; 173 } 174 return (-1); 175 } 176 DEF_WEAK(iruserok_sa); 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[HOST_NAME_MAX+1]; 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) > HOST_NAME_MAX) 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 DEF_STRONG(__ivaliduser_sa); 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 freeaddrinfo(res); 436 return (NULL); 437 } 438