safefile.c revision 66494
1198090Srdivacky/* 2198090Srdivacky * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 3353358Sdim * All rights reserved. 4353358Sdim * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5353358Sdim * Copyright (c) 1988, 1993 6198090Srdivacky * The Regents of the University of California. All rights reserved. 7198090Srdivacky * 8198090Srdivacky * By using this file, you agree to the terms and conditions set 9198090Srdivacky * forth in the LICENSE file which can be found at the top level of 10198090Srdivacky * the sendmail distribution. 11198090Srdivacky * 12198090Srdivacky */ 13198090Srdivacky 14341825Sdim#ifndef lint 15321369Sdimstatic char id[] = "@(#)$Id: safefile.c,v 8.81.4.7 2000/09/01 21:09:23 ca Exp $"; 16249423Sdim#endif /* ! lint */ 17249423Sdim 18249423Sdim#include <sendmail.h> 19249423Sdim 20249423Sdim 21276479Sdim/* 22198090Srdivacky** SAFEFILE -- return 0 if a file exists and is safe for a user. 23198090Srdivacky** 24205218Srdivacky** Parameters: 25202375Srdivacky** fn -- filename to check. 26249423Sdim** uid -- user id to compare against. 27249423Sdim** gid -- group id to compare against. 28198090Srdivacky** user -- user name to compare against (used for group 29198090Srdivacky** sets). 30198090Srdivacky** flags -- modifiers: 31198090Srdivacky** SFF_MUSTOWN -- "uid" must own this file. 32198090Srdivacky** SFF_NOSLINK -- file cannot be a symbolic link. 33198090Srdivacky** mode -- mode bits that must match. 34198090Srdivacky** st -- if set, points to a stat structure that will 35198090Srdivacky** get the stat info for the file. 36226633Sdim** 37226633Sdim** Returns: 38226633Sdim** 0 if fn exists, is owned by uid, and matches mode. 39226633Sdim** An errno otherwise. The actual errno is cleared. 40226633Sdim** 41226633Sdim** Side Effects: 42314564Sdim** none. 43321369Sdim*/ 44314564Sdim 45327952Sdimint 46327952Sdimsafefile(fn, uid, gid, user, flags, mode, st) 47344779Sdim char *fn; 48344779Sdim UID_T uid; 49344779Sdim GID_T gid; 50353358Sdim char *user; 51198090Srdivacky long flags; 52276479Sdim int mode; 53198090Srdivacky struct stat *st; 54314564Sdim{ 55198090Srdivacky register char *p; 56198090Srdivacky register struct group *gr = NULL; 57341825Sdim int file_errno = 0; 58341825Sdim bool checkpath; 59341825Sdim struct stat stbuf; 60341825Sdim struct stat fstbuf; 61341825Sdim char fbuf[MAXPATHLEN + 1]; 62341825Sdim 63341825Sdim if (tTd(44, 4)) 64341825Sdim dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n", 65341825Sdim fn, (int) uid, (int) gid, flags, mode); 66341825Sdim errno = 0; 67341825Sdim if (st == NULL) 68341825Sdim st = &fstbuf; 69341825Sdim if (strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf) 70341825Sdim { 71224145Sdim if (tTd(44, 4)) 72198090Srdivacky dprintf("\tpathname too long\n"); 73198090Srdivacky return ENAMETOOLONG; 74341825Sdim } 75198090Srdivacky fn = fbuf; 76198090Srdivacky 77198090Srdivacky /* ignore SFF_SAFEDIRPATH if we are debugging */ 78198090Srdivacky if (RealUid != 0 && RunAsUid == RealUid) 79198090Srdivacky flags &= ~SFF_SAFEDIRPATH; 80198090Srdivacky 81198090Srdivacky /* first check to see if the file exists at all */ 82276479Sdim# if HASLSTAT 83198090Srdivacky if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) 84198090Srdivacky : stat(fn, st)) < 0) 85198090Srdivacky# else /* HASLSTAT */ 86198090Srdivacky if (stat(fn, st) < 0) 87198090Srdivacky# endif /* HASLSTAT */ 88198090Srdivacky { 89198090Srdivacky file_errno = errno; 90198090Srdivacky } 91234353Sdim else if (bitset(SFF_SETUIDOK, flags) && 92234353Sdim !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) && 93198090Srdivacky S_ISREG(st->st_mode)) 94234353Sdim { 95234353Sdim /* 96234353Sdim ** If final file is setuid, run as the owner of that 97234353Sdim ** file. Gotta be careful not to reveal anything too 98341825Sdim ** soon here! 99234353Sdim */ 100198090Srdivacky 101341825Sdim# ifdef SUID_ROOT_FILES_OK 102198090Srdivacky if (bitset(S_ISUID, st->st_mode)) 103234353Sdim# else /* SUID_ROOT_FILES_OK */ 104234353Sdim if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 && 105198090Srdivacky st->st_uid != TrustedUid) 106198090Srdivacky# endif /* SUID_ROOT_FILES_OK */ 107198090Srdivacky { 108198090Srdivacky uid = st->st_uid; 109198090Srdivacky user = NULL; 110198090Srdivacky } 111234353Sdim# ifdef SUID_ROOT_FILES_OK 112198090Srdivacky if (bitset(S_ISGID, st->st_mode)) 113198090Srdivacky# else /* SUID_ROOT_FILES_OK */ 114198090Srdivacky if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0) 115198090Srdivacky# endif /* SUID_ROOT_FILES_OK */ 116276479Sdim gid = st->st_gid; 117314564Sdim } 118276479Sdim 119276479Sdim checkpath = !bitset(SFF_NOPATHCHECK, flags) || 120261991Sdim (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)); 121296417Sdim if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags)) 122314564Sdim { 123276479Sdim int ret; 124288943Sdim 125261991Sdim /* check the directory */ 126261991Sdim p = strrchr(fn, '/'); 127276479Sdim if (p == NULL) 128314564Sdim { 129276479Sdim ret = safedirpath(".", uid, gid, user, 130314564Sdim flags|SFF_SAFEDIRPATH, 0, 0); 131221345Sdim } 132221345Sdim else 133221345Sdim { 134296417Sdim *p = '\0'; 135221345Sdim ret = safedirpath(fn, uid, gid, user, 136221345Sdim flags|SFF_SAFEDIRPATH, 0, 0); 137221345Sdim *p = '/'; 138221345Sdim } 139198090Srdivacky if (ret == 0) 140341825Sdim { 141341825Sdim /* directory is safe */ 142341825Sdim checkpath = FALSE; 143314564Sdim } 144198090Srdivacky else 145314564Sdim { 146198090Srdivacky# if HASLSTAT 147198090Srdivacky /* Need lstat() information if called stat() before */ 148341825Sdim if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0) 149341825Sdim { 150198090Srdivacky ret = errno; 151198090Srdivacky if (tTd(44, 4)) 152341825Sdim dprintf("\t%s\n", errstring(ret)); 153341825Sdim return ret; 154341825Sdim } 155198090Srdivacky# endif /* HASLSTAT */ 156198090Srdivacky /* directory is writable: disallow links */ 157341825Sdim flags |= SFF_NOLINK; 158198090Srdivacky } 159198090Srdivacky } 160198090Srdivacky 161198090Srdivacky if (checkpath) 162202878Srdivacky { 163202878Srdivacky int ret; 164202878Srdivacky 165202878Srdivacky p = strrchr(fn, '/'); 166341825Sdim if (p == NULL) 167341825Sdim { 168341825Sdim ret = safedirpath(".", uid, gid, user, flags, 0, 0); 169202878Srdivacky } 170202878Srdivacky else 171202878Srdivacky { 172202878Srdivacky *p = '\0'; 173198090Srdivacky ret = safedirpath(fn, uid, gid, user, flags, 0, 0); 174202878Srdivacky *p = '/'; 175198090Srdivacky } 176198090Srdivacky if (ret != 0) 177198090Srdivacky return ret; 178198090Srdivacky } 179198090Srdivacky 180239462Sdim /* 181198090Srdivacky ** If the target file doesn't exist, check the directory to 182341825Sdim ** ensure that it is writable by this user. 183296417Sdim */ 184218893Sdim 185218893Sdim if (file_errno != 0) 186218893Sdim { 187309124Sdim int ret = file_errno; 188218893Sdim char *dir = fn; 189296417Sdim 190198090Srdivacky if (tTd(44, 4)) 191198090Srdivacky dprintf("\t%s\n", errstring(ret)); 192226633Sdim 193226633Sdim errno = 0; 194198090Srdivacky if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT) 195198090Srdivacky return ret; 196198090Srdivacky 197198090Srdivacky /* check to see if legal to create the file */ 198198090Srdivacky p = strrchr(dir, '/'); 199198090Srdivacky if (p == NULL) 200198090Srdivacky dir = "."; 201198090Srdivacky else if (p == dir) 202198090Srdivacky dir = "/"; 203198090Srdivacky else 204198090Srdivacky *p = '\0'; 205198090Srdivacky if (stat(dir, &stbuf) >= 0) 206198090Srdivacky { 207198090Srdivacky int md = S_IWRITE|S_IEXEC; 208198090Srdivacky 209198090Srdivacky if (stbuf.st_uid == uid) 210198090Srdivacky /* EMPTY */ 211198090Srdivacky ; 212314564Sdim else if (uid == 0 && stbuf.st_uid == TrustedUid) 213314564Sdim /* EMPTY */ 214198090Srdivacky ; 215198090Srdivacky else 216198090Srdivacky { 217309124Sdim md >>= 3; 218288943Sdim if (stbuf.st_gid == gid) 219288943Sdim /* EMPTY */ 220198090Srdivacky ; 221198090Srdivacky# ifndef NO_GROUP_SET 222296417Sdim else if (user != NULL && !DontInitGroups && 223314564Sdim ((gr != NULL && 224314564Sdim gr->gr_gid == stbuf.st_gid) || 225314564Sdim (gr = getgrgid(stbuf.st_gid)) != NULL)) 226314564Sdim { 227314564Sdim register char **gp; 228341825Sdim 229314564Sdim for (gp = gr->gr_mem; *gp != NULL; gp++) 230314564Sdim if (strcmp(*gp, user) == 0) 231198090Srdivacky break; 232198090Srdivacky if (*gp == NULL) 233198090Srdivacky md >>= 3; 234198090Srdivacky } 235198090Srdivacky# endif /* ! NO_GROUP_SET */ 236198090Srdivacky else 237198090Srdivacky md >>= 3; 238198090Srdivacky } 239309124Sdim if ((stbuf.st_mode & md) != md) 240296417Sdim errno = EACCES; 241198090Srdivacky } 242198090Srdivacky ret = errno; 243288943Sdim if (tTd(44, 4)) 244288943Sdim dprintf("\t[final dir %s uid %d mode %lo] %s\n", 245288943Sdim dir, (int) stbuf.st_uid, (u_long) stbuf.st_mode, 246314564Sdim errstring(ret)); 247314564Sdim if (p != NULL) 248198090Srdivacky *p = '/'; 249314564Sdim st->st_mode = ST_MODE_NOFILE; 250314564Sdim return ret; 251198090Srdivacky } 252321369Sdim 253321369Sdim# ifdef S_ISLNK 254321369Sdim if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) 255321369Sdim { 256360784Sdim if (tTd(44, 4)) 257321369Sdim dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n", 258321369Sdim (u_long) st->st_mode); 259321369Sdim return E_SM_NOSLINK; 260321369Sdim } 261321369Sdim# endif /* S_ISLNK */ 262321369Sdim if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) 263321369Sdim { 264321369Sdim if (tTd(44, 4)) 265321369Sdim dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n", 266321369Sdim (u_long) st->st_mode); 267198090Srdivacky return E_SM_REGONLY; 268314564Sdim } 269198090Srdivacky if (bitset(SFF_NOGWFILES, flags) && 270198090Srdivacky bitset(S_IWGRP, st->st_mode)) 271288943Sdim { 272314564Sdim if (tTd(44, 4)) 273309124Sdim dprintf("\t[write bits %lo]\tE_SM_GWFILE\n", 274296417Sdim (u_long) st->st_mode); 275309124Sdim return E_SM_GWFILE; 276309124Sdim } 277288943Sdim if (bitset(SFF_NOWWFILES, flags) && 278288943Sdim bitset(S_IWOTH, st->st_mode)) 279288943Sdim { 280288943Sdim if (tTd(44, 4)) 281288943Sdim dprintf("\t[write bits %lo]\tE_SM_WWFILE\n", 282288943Sdim (u_long) st->st_mode); 283288943Sdim return E_SM_WWFILE; 284288943Sdim } 285288943Sdim if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode)) 286288943Sdim { 287288943Sdim if (tTd(44, 4)) 288288943Sdim dprintf("\t[read bits %lo]\tE_SM_GRFILE\n", 289288943Sdim (u_long) st->st_mode); 290288943Sdim return E_SM_GRFILE; 291321369Sdim } 292288943Sdim if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode)) 293288943Sdim { 294288943Sdim if (tTd(44, 4)) 295288943Sdim dprintf("\t[read bits %lo]\tE_SM_WRFILE\n", 296296417Sdim (u_long) st->st_mode); 297309124Sdim return E_SM_WRFILE; 298309124Sdim } 299276479Sdim if (!bitset(SFF_EXECOK, flags) && 300198090Srdivacky bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && 301198090Srdivacky bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode)) 302198090Srdivacky { 303198090Srdivacky if (tTd(44, 4)) 304198090Srdivacky dprintf("\t[exec bits %lo]\tE_SM_ISEXEC]\n", 305249423Sdim (u_long) st->st_mode); 306203954Srdivacky return E_SM_ISEXEC; 307203954Srdivacky } 308276479Sdim if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1) 309314564Sdim { 310314564Sdim if (tTd(44, 4)) 311249423Sdim dprintf("\t[link count %d]\tE_SM_NOHLINK\n", 312314564Sdim (int) st->st_nlink); 313249423Sdim return E_SM_NOHLINK; 314249423Sdim } 315198090Srdivacky 316198090Srdivacky if (uid == 0 && bitset(SFF_OPENASROOT, flags)) 317203954Srdivacky /* EMPTY */ 318249423Sdim ; 319249423Sdim else if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 320221345Sdim mode >>= 6; 321203954Srdivacky else if (st->st_uid == uid) 322207618Srdivacky /* EMPTY */ 323203954Srdivacky ; 324203954Srdivacky else if (uid == 0 && st->st_uid == TrustedUid) 325249423Sdim /* EMPTY */ 326205218Srdivacky ; 327205218Srdivacky else 328205218Srdivacky { 329288943Sdim mode >>= 3; 330205218Srdivacky if (st->st_gid == gid) 331288943Sdim /* EMPTY */ 332288943Sdim ; 333198090Srdivacky# ifndef NO_GROUP_SET 334205218Srdivacky else if (user != NULL && !DontInitGroups && 335198090Srdivacky ((gr != NULL && gr->gr_gid == st->st_gid) || 336261991Sdim (gr = getgrgid(st->st_gid)) != NULL)) 337261991Sdim { 338261991Sdim register char **gp; 339261991Sdim 340288943Sdim for (gp = gr->gr_mem; *gp != NULL; gp++) 341261991Sdim if (strcmp(*gp, user) == 0) 342288943Sdim break; 343288943Sdim if (*gp == NULL) 344314564Sdim mode >>= 3; 345296417Sdim } 346314564Sdim# endif /* ! NO_GROUP_SET */ 347288943Sdim else 348 mode >>= 3; 349 } 350 if (tTd(44, 4)) 351 dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ", 352 (int) st->st_uid, (int) st->st_nlink, 353 (u_long) st->st_mode, (u_long) mode); 354 if ((st->st_uid == uid || st->st_uid == 0 || 355 st->st_uid == TrustedUid || 356 !bitset(SFF_MUSTOWN, flags)) && 357 (st->st_mode & mode) == mode) 358 { 359 if (tTd(44, 4)) 360 dprintf("\tOK\n"); 361 return 0; 362 } 363 if (tTd(44, 4)) 364 dprintf("\tEACCES\n"); 365 return EACCES; 366} 367/* 368** SAFEDIRPATH -- check to make sure a path to a directory is safe 369** 370** Safe means not writable and owned by the right folks. 371** 372** Parameters: 373** fn -- filename to check. 374** uid -- user id to compare against. 375** gid -- group id to compare against. 376** user -- user name to compare against (used for group 377** sets). 378** flags -- modifiers: 379** SFF_ROOTOK -- ok to use root permissions to open. 380** SFF_SAFEDIRPATH -- writable directories are considered 381** to be fatal errors. 382** level -- symlink recursive level. 383** offset -- offset into fn to start checking from. 384** 385** Returns: 386** 0 -- if the directory path is "safe". 387** else -- an error number associated with the path. 388*/ 389 390int 391safedirpath(fn, uid, gid, user, flags, level, offset) 392 char *fn; 393 UID_T uid; 394 GID_T gid; 395 char *user; 396 long flags; 397 int level; 398 int offset; 399{ 400 int ret = 0; 401 int mode = S_IWOTH; 402 char save = '\0'; 403 char *saveptr = NULL; 404 char *p, *enddir; 405 register struct group *gr = NULL; 406 char s[MAXLINKPATHLEN + 1]; 407 struct stat stbuf; 408 409 /* make sure we aren't in a symlink loop */ 410 if (level > MAXSYMLINKS) 411 return ELOOP; 412 413 /* special case root directory */ 414 if (*fn == '\0') 415 fn = "/"; 416 417 if (tTd(44, 4)) 418 dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n", 419 fn, (long) uid, (long) gid, flags, level, offset); 420 421 if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail)) 422 mode |= S_IWGRP; 423 424 /* Make a modifiable copy of the filename */ 425 if (strlcpy(s, fn, sizeof s) >= sizeof s) 426 return EINVAL; 427 428 p = s + offset; 429 while (p != NULL) 430 { 431 /* put back character */ 432 if (saveptr != NULL) 433 { 434 *saveptr = save; 435 saveptr = NULL; 436 p++; 437 } 438 439 if (*p == '\0') 440 break; 441 442 p = strchr(p, '/'); 443 444 /* Special case for root directory */ 445 if (p == s) 446 { 447 save = *(p + 1); 448 saveptr = p + 1; 449 *(p + 1) = '\0'; 450 } 451 else if (p != NULL) 452 { 453 save = *p; 454 saveptr = p; 455 *p = '\0'; 456 } 457 458 /* Heuristic: . and .. have already been checked */ 459 enddir = strrchr(s, '/'); 460 if (enddir != NULL && 461 (strcmp(enddir, "/..") == 0 || 462 strcmp(enddir, "/.") == 0)) 463 continue; 464 465 if (tTd(44, 20)) 466 dprintf("\t[dir %s]\n", s); 467 468# if HASLSTAT 469 ret = lstat(s, &stbuf); 470# else /* HASLSTAT */ 471 ret = stat(s, &stbuf); 472# endif /* HASLSTAT */ 473 if (ret < 0) 474 { 475 ret = errno; 476 break; 477 } 478 479# ifdef S_ISLNK 480 /* Follow symlinks */ 481 if (S_ISLNK(stbuf.st_mode)) 482 { 483 char *target; 484 char buf[MAXPATHLEN + 1]; 485 486 memset(buf, '\0', sizeof buf); 487 if (readlink(s, buf, sizeof buf) < 0) 488 { 489 ret = errno; 490 break; 491 } 492 493 offset = 0; 494 if (*buf == '/') 495 { 496 target = buf; 497 498 /* If path is the same, avoid rechecks */ 499 while (s[offset] == buf[offset] && 500 s[offset] != '\0') 501 offset++; 502 503 if (s[offset] == '\0' && buf[offset] == '\0') 504 { 505 /* strings match, symlink loop */ 506 return ELOOP; 507 } 508 509 /* back off from the mismatch */ 510 if (offset > 0) 511 offset--; 512 513 /* Make sure we are at a directory break */ 514 if (offset > 0 && 515 s[offset] != '/' && 516 s[offset] != '\0') 517 { 518 while (buf[offset] != '/' && 519 offset > 0) 520 offset--; 521 } 522 if (offset > 0 && 523 s[offset] == '/' && 524 buf[offset] == '/') 525 { 526 /* Include the trailing slash */ 527 offset++; 528 } 529 } 530 else 531 { 532 char *sptr; 533 char fullbuf[MAXLINKPATHLEN + 1]; 534 535 sptr = strrchr(s, '/'); 536 if (sptr != NULL) 537 { 538 *sptr = '\0'; 539 offset = sptr + 1 - s; 540 if ((strlen(s) + 1 + 541 strlen(buf) + 1) > sizeof fullbuf) 542 { 543 ret = EINVAL; 544 break; 545 } 546 snprintf(fullbuf, sizeof fullbuf, 547 "%s/%s", s, buf); 548 *sptr = '/'; 549 } 550 else 551 { 552 if (strlen(buf) + 1 > sizeof fullbuf) 553 { 554 ret = EINVAL; 555 break; 556 } 557 (void) strlcpy(fullbuf, buf, 558 sizeof fullbuf); 559 } 560 target = fullbuf; 561 } 562 ret = safedirpath(target, uid, gid, user, flags, 563 level + 1, offset); 564 if (ret != 0) 565 break; 566 567 /* Don't check permissions on the link file itself */ 568 continue; 569 } 570#endif /* S_ISLNK */ 571 572 if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) && 573#ifdef S_ISVTX 574 !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) && 575 bitset(S_ISVTX, stbuf.st_mode)) && 576#endif /* S_ISVTX */ 577 bitset(mode, stbuf.st_mode)) 578 { 579 if (tTd(44, 4)) 580 dprintf("\t[dir %s] mode %lo ", 581 s, (u_long) stbuf.st_mode); 582 if (bitset(SFF_SAFEDIRPATH, flags)) 583 { 584 if (bitset(S_IWOTH, stbuf.st_mode)) 585 ret = E_SM_WWDIR; 586 else 587 ret = E_SM_GWDIR; 588 if (tTd(44, 4)) 589 dprintf("FATAL\n"); 590 break; 591 } 592 if (tTd(44, 4)) 593 dprintf("WARNING\n"); 594 if (Verbose > 1) 595 message("051 WARNING: %s writable directory %s", 596 bitset(S_IWOTH, stbuf.st_mode) 597 ? "World" 598 : "Group", 599 s); 600 } 601 if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)) 602 { 603 if (bitset(S_IXOTH, stbuf.st_mode)) 604 continue; 605 ret = EACCES; 606 break; 607 } 608 609 /* 610 ** Let OS determine access to file if we are not 611 ** running as a privileged user. This allows ACLs 612 ** to work. Also, if opening as root, assume we can 613 ** scan the directory. 614 */ 615 if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags)) 616 continue; 617 618 if (stbuf.st_uid == uid && 619 bitset(S_IXUSR, stbuf.st_mode)) 620 continue; 621 if (stbuf.st_gid == gid && 622 bitset(S_IXGRP, stbuf.st_mode)) 623 continue; 624# ifndef NO_GROUP_SET 625 if (user != NULL && !DontInitGroups && 626 ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 627 (gr = getgrgid(stbuf.st_gid)) != NULL)) 628 { 629 register char **gp; 630 631 for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) 632 if (strcmp(*gp, user) == 0) 633 break; 634 if (gp != NULL && *gp != NULL && 635 bitset(S_IXGRP, stbuf.st_mode)) 636 continue; 637 } 638# endif /* ! NO_GROUP_SET */ 639 if (!bitset(S_IXOTH, stbuf.st_mode)) 640 { 641 ret = EACCES; 642 break; 643 } 644 } 645 if (tTd(44, 4)) 646 dprintf("\t[dir %s] %s\n", fn, 647 ret == 0 ? "OK" : errstring(ret)); 648 return ret; 649} 650/* 651** SAFEOPEN -- do a file open with extra checking 652** 653** Parameters: 654** fn -- the file name to open. 655** omode -- the open-style mode flags. 656** cmode -- the create-style mode flags. 657** sff -- safefile flags. 658** 659** Returns: 660** Same as open. 661*/ 662 663#ifndef O_ACCMODE 664# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) 665#endif /* ! O_ACCMODE */ 666 667int 668safeopen(fn, omode, cmode, sff) 669 char *fn; 670 int omode; 671 int cmode; 672 long sff; 673{ 674 int rval; 675 int fd; 676 int smode; 677 struct stat stb; 678 679 if (tTd(44, 10)) 680 printf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n", 681 fn, omode, cmode, sff); 682 683 if (bitset(O_CREAT, omode)) 684 sff |= SFF_CREAT; 685 omode &= ~O_CREAT; 686 smode = 0; 687 switch (omode & O_ACCMODE) 688 { 689 case O_RDONLY: 690 smode = S_IREAD; 691 break; 692 693 case O_WRONLY: 694 smode = S_IWRITE; 695 break; 696 697 case O_RDWR: 698 smode = S_IREAD|S_IWRITE; 699 break; 700 701 default: 702 smode = 0; 703 break; 704 } 705 if (bitset(SFF_OPENASROOT, sff)) 706 rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName, 707 sff, smode, &stb); 708 else 709 rval = safefile(fn, RealUid, RealGid, RealUserName, 710 sff, smode, &stb); 711 if (rval != 0) 712 { 713 errno = rval; 714 return -1; 715 } 716 if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff)) 717 omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL); 718 else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode)) 719 { 720 /* The file exists so an exclusive create would fail */ 721 errno = EEXIST; 722 return -1; 723 } 724 725 fd = dfopen(fn, omode, cmode, sff); 726 if (fd < 0) 727 return fd; 728 if (filechanged(fn, fd, &stb)) 729 { 730 syserr("554 5.3.0 cannot open: file %s changed after open", fn); 731 (void) close(fd); 732 errno = E_SM_FILECHANGE; 733 return -1; 734 } 735 return fd; 736} 737/* 738** SAFEFOPEN -- do a file open with extra checking 739** 740** Parameters: 741** fn -- the file name to open. 742** omode -- the open-style mode flags. 743** cmode -- the create-style mode flags. 744** sff -- safefile flags. 745** 746** Returns: 747** Same as fopen. 748*/ 749 750FILE * 751safefopen(fn, omode, cmode, sff) 752 char *fn; 753 int omode; 754 int cmode; 755 long sff; 756{ 757 int fd; 758 int save_errno; 759 FILE *fp; 760 char *fmode; 761 762 switch (omode & O_ACCMODE) 763 { 764 case O_RDONLY: 765 fmode = "r"; 766 break; 767 768 case O_WRONLY: 769 if (bitset(O_APPEND, omode)) 770 fmode = "a"; 771 else 772 fmode = "w"; 773 break; 774 775 case O_RDWR: 776 if (bitset(O_TRUNC, omode)) 777 fmode = "w+"; 778 else if (bitset(O_APPEND, omode)) 779 fmode = "a+"; 780 else 781 fmode = "r+"; 782 break; 783 784 default: 785 syserr("554 5.3.5 safefopen: unknown omode %o", omode); 786 fmode = "x"; 787 } 788 fd = safeopen(fn, omode, cmode, sff); 789 if (fd < 0) 790 { 791 save_errno = errno; 792 if (tTd(44, 10)) 793 dprintf("safefopen: safeopen failed: %s\n", 794 errstring(errno)); 795 errno = save_errno; 796 return NULL; 797 } 798 fp = fdopen(fd, fmode); 799 if (fp != NULL) 800 return fp; 801 802 save_errno = errno; 803 if (tTd(44, 10)) 804 { 805 dprintf("safefopen: fdopen(%s, %s) failed: omode=%x, sff=%lx, err=%s\n", 806 fn, fmode, omode, sff, errstring(errno)); 807 } 808 (void) close(fd); 809 errno = save_errno; 810 return NULL; 811} 812/* 813** FILECHANGED -- check to see if file changed after being opened 814** 815** Parameters: 816** fn -- pathname of file to check. 817** fd -- file descriptor to check. 818** stb -- stat structure from before open. 819** 820** Returns: 821** TRUE -- if a problem was detected. 822** FALSE -- if this file is still the same. 823*/ 824 825bool 826filechanged(fn, fd, stb) 827 char *fn; 828 int fd; 829 struct stat *stb; 830{ 831 struct stat sta; 832 833 if (stb->st_mode == ST_MODE_NOFILE) 834 { 835# if HASLSTAT && BOGUS_O_EXCL 836 /* only necessary if exclusive open follows symbolic links */ 837 if (lstat(fn, stb) < 0 || stb->st_nlink != 1) 838 return TRUE; 839# else /* HASLSTAT && BOGUS_O_EXCL */ 840 return FALSE; 841# endif /* HASLSTAT && BOGUS_O_EXCL */ 842 } 843 if (fstat(fd, &sta) < 0) 844 return TRUE; 845 846 if (sta.st_nlink != stb->st_nlink || 847 sta.st_dev != stb->st_dev || 848 sta.st_ino != stb->st_ino || 849# if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ 850 sta.st_gen != stb->st_gen || 851# endif /* HAS_ST_GEN && 0 */ 852 sta.st_uid != stb->st_uid || 853 sta.st_gid != stb->st_gid) 854 { 855 if (tTd(44, 8)) 856 { 857 dprintf("File changed after opening:\n"); 858 dprintf(" nlink = %ld/%ld\n", 859 (long) stb->st_nlink, (long) sta.st_nlink); 860 dprintf(" dev = %ld/%ld\n", 861 (long) stb->st_dev, (long) sta.st_dev); 862 if (sizeof sta.st_ino > sizeof (long)) 863 { 864 dprintf(" ino = %s/", 865 quad_to_string(stb->st_ino)); 866 dprintf("%s\n", 867 quad_to_string(sta.st_ino)); 868 } 869 else 870 dprintf(" ino = %lu/%lu\n", 871 (unsigned long) stb->st_ino, 872 (unsigned long) sta.st_ino); 873# if HAS_ST_GEN 874 dprintf(" gen = %ld/%ld\n", 875 (long) stb->st_gen, (long) sta.st_gen); 876# endif /* HAS_ST_GEN */ 877 dprintf(" uid = %ld/%ld\n", 878 (long) stb->st_uid, (long) sta.st_uid); 879 dprintf(" gid = %ld/%ld\n", 880 (long) stb->st_gid, (long) sta.st_gid); 881 } 882 return TRUE; 883 } 884 885 return FALSE; 886} 887/* 888** DFOPEN -- determined file open 889** 890** This routine has the semantics of open, except that it will 891** keep trying a few times to make this happen. The idea is that 892** on very loaded systems, we may run out of resources (inodes, 893** whatever), so this tries to get around it. 894*/ 895 896int 897dfopen(filename, omode, cmode, sff) 898 char *filename; 899 int omode; 900 int cmode; 901 long sff; 902{ 903 register int tries; 904 int fd = -1; 905 struct stat st; 906 907 for (tries = 0; tries < 10; tries++) 908 { 909 (void) sleep((unsigned) (10 * tries)); 910 errno = 0; 911 fd = open(filename, omode, cmode); 912 if (fd >= 0) 913 break; 914 switch (errno) 915 { 916 case ENFILE: /* system file table full */ 917 case EINTR: /* interrupted syscall */ 918#ifdef ETXTBSY 919 case ETXTBSY: /* Apollo: net file locked */ 920#endif /* ETXTBSY */ 921 continue; 922 } 923 break; 924 } 925 if (!bitset(SFF_NOLOCK, sff) && 926 fd >= 0 && 927 fstat(fd, &st) >= 0 && 928 S_ISREG(st.st_mode)) 929 { 930 int locktype; 931 932 /* lock the file to avoid accidental conflicts */ 933 if ((omode & O_ACCMODE) != O_RDONLY) 934 locktype = LOCK_EX; 935 else 936 locktype = LOCK_SH; 937 if (!lockfile(fd, filename, NULL, locktype)) 938 { 939 int save_errno = errno; 940 941 (void) close(fd); 942 fd = -1; 943 errno = save_errno; 944 } 945 else 946 errno = 0; 947 } 948 return fd; 949} 950