safefile.c revision 261194
190075Sobrien/* 290075Sobrien * Copyright (c) 1998-2004 Proofpoint, Inc. and its suppliers. 3169689Skan * All rights reserved. 4169689Skan * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 590075Sobrien * Copyright (c) 1988, 1993 690075Sobrien * The Regents of the University of California. All rights reserved. 790075Sobrien * 890075Sobrien * By using this file, you agree to the terms and conditions set 990075Sobrien * forth in the LICENSE file which can be found at the top level of 1090075Sobrien * the sendmail distribution. 1190075Sobrien * 1290075Sobrien */ 1390075Sobrien 1490075Sobrien#include <sendmail.h> 1590075Sobrien#include <sm/io.h> 1690075Sobrien#include <sm/errstring.h> 1790075Sobrien 1890075SobrienSM_RCSID("@(#)$Id: safefile.c,v 8.130 2013/11/22 20:51:50 ca Exp $") 1990075Sobrien 20169689Skan 21169689Skan/* 2290075Sobrien** SAFEFILE -- return 0 if a file exists and is safe for a user. 2390075Sobrien** 2490075Sobrien** Parameters: 2590075Sobrien** fn -- filename to check. 26169689Skan** uid -- user id to compare against. 2790075Sobrien** gid -- group id to compare against. 2890075Sobrien** user -- user name to compare against (used for group 2990075Sobrien** sets). 3090075Sobrien** flags -- modifiers: 3190075Sobrien** SFF_MUSTOWN -- "uid" must own this file. 3290075Sobrien** SFF_NOSLINK -- file cannot be a symbolic link. 3390075Sobrien** mode -- mode bits that must match. 3490075Sobrien** st -- if set, points to a stat structure that will 3590075Sobrien** get the stat info for the file. 3690075Sobrien** 3790075Sobrien** Returns: 3890075Sobrien** 0 if fn exists, is owned by uid, and matches mode. 3990075Sobrien** An errno otherwise. The actual errno is cleared. 4090075Sobrien** 4190075Sobrien** Side Effects: 4290075Sobrien** none. 4390075Sobrien*/ 4490075Sobrien 4590075Sobrienint 4690075Sobriensafefile(fn, uid, gid, user, flags, mode, st) 4790075Sobrien char *fn; 4890075Sobrien UID_T uid; 4990075Sobrien GID_T gid; 5090075Sobrien char *user; 5190075Sobrien long flags; 5290075Sobrien int mode; 5390075Sobrien struct stat *st; 54117395Skan{ 55117395Skan register char *p; 56117395Skan register struct group *gr = NULL; 57169689Skan int file_errno = 0; 58132718Skan bool checkpath; 59132718Skan struct stat stbuf; 60132718Skan struct stat fstbuf; 61132718Skan char fbuf[MAXPATHLEN]; 62132718Skan 63132718Skan if (tTd(44, 4)) 64169689Skan sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n", 65169689Skan fn, (int) uid, (int) gid, flags, mode); 66169689Skan errno = 0; 67169689Skan if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf) 6890075Sobrien { 6990075Sobrien if (tTd(44, 4)) 7090075Sobrien sm_dprintf("\tpathname too long\n"); 7190075Sobrien return ENAMETOOLONG; 72117395Skan } 73132718Skan fn = fbuf; 74169689Skan if (st == NULL) 7590075Sobrien st = &fstbuf; 76169689Skan 77169689Skan /* ignore SFF_SAFEDIRPATH if we are debugging */ 78169689Skan if (RealUid != 0 && RunAsUid == RealUid) 79169689Skan flags &= ~SFF_SAFEDIRPATH; 80169689Skan 81169689Skan /* first check to see if the file exists at all */ 8290075Sobrien# if HASLSTAT 8390075Sobrien if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) 8490075Sobrien : stat(fn, st)) < 0) 8590075Sobrien# else /* HASLSTAT */ 8690075Sobrien if (stat(fn, st) < 0) 87117395Skan# endif /* HASLSTAT */ 88169689Skan { 89117395Skan file_errno = errno; 90132718Skan } 91169689Skan else if (bitset(SFF_SETUIDOK, flags) && 9290075Sobrien !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) && 9390075Sobrien S_ISREG(st->st_mode)) 9490075Sobrien { 9590075Sobrien /* 9690075Sobrien ** If final file is set-user-ID, run as the owner of that 97169689Skan ** file. Gotta be careful not to reveal anything too 98117395Skan ** soon here! 99132718Skan */ 100169689Skan 10190075Sobrien# ifdef SUID_ROOT_FILES_OK 10290075Sobrien if (bitset(S_ISUID, st->st_mode)) 103169689Skan# else /* SUID_ROOT_FILES_OK */ 104169689Skan if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 && 105169689Skan st->st_uid != TrustedUid) 106169689Skan# endif /* SUID_ROOT_FILES_OK */ 107169689Skan { 108169689Skan uid = st->st_uid; 109169689Skan user = NULL; 11090075Sobrien } 111169689Skan# ifdef SUID_ROOT_FILES_OK 112117395Skan if (bitset(S_ISGID, st->st_mode)) 113132718Skan# else /* SUID_ROOT_FILES_OK */ 114169689Skan if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0) 11590075Sobrien# endif /* SUID_ROOT_FILES_OK */ 116132718Skan gid = st->st_gid; 117132718Skan } 118132718Skan 119169689Skan checkpath = !bitset(SFF_NOPATHCHECK, flags) || 120132718Skan (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)); 121132718Skan if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags)) 122169689Skan { 12390075Sobrien int ret; 124169689Skan 125169689Skan /* check the directory */ 126169689Skan p = strrchr(fn, '/'); 127169689Skan if (p == NULL) 128169689Skan { 129169689Skan ret = safedirpath(".", uid, gid, user, 130169689Skan flags|SFF_SAFEDIRPATH, 0, 0); 131169689Skan } 132169689Skan else 133169689Skan { 134169689Skan *p = '\0'; 135169689Skan ret = safedirpath(fn, uid, gid, user, 136169689Skan flags|SFF_SAFEDIRPATH, 0, 0); 137169689Skan *p = '/'; 138169689Skan } 139169689Skan if (ret == 0) 140169689Skan { 141169689Skan /* directory is safe */ 142169689Skan checkpath = false; 143169689Skan } 144169689Skan else 145132718Skan { 146132718Skan# if HASLSTAT 147132718Skan /* Need lstat() information if called stat() before */ 148132718Skan if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0) 149132718Skan { 150132718Skan ret = errno; 15190075Sobrien if (tTd(44, 4)) 152132718Skan sm_dprintf("\t%s\n", sm_errstring(ret)); 153132718Skan return ret; 154132718Skan } 155169689Skan# endif /* HASLSTAT */ 156132718Skan /* directory is writable: disallow links */ 157132718Skan flags |= SFF_NOLINK; 158132718Skan } 159169689Skan } 16090075Sobrien 161132718Skan if (checkpath) 162132718Skan { 163132718Skan int ret; 164132718Skan 165132718Skan p = strrchr(fn, '/'); 166132718Skan if (p == NULL) 167132718Skan { 16890075Sobrien ret = safedirpath(".", uid, gid, user, flags, 0, 0); 169132718Skan } 170132718Skan else 171132718Skan { 172132718Skan *p = '\0'; 173132718Skan ret = safedirpath(fn, uid, gid, user, flags, 0, 0); 174132718Skan *p = '/'; 17590075Sobrien } 176132718Skan if (ret != 0) 177132718Skan return ret; 178132718Skan } 179132718Skan 180132718Skan /* 181132718Skan ** If the target file doesn't exist, check the directory to 182132718Skan ** ensure that it is writable by this user. 183132718Skan */ 184132718Skan 185132718Skan if (file_errno != 0) 186132718Skan { 187132718Skan int ret = file_errno; 188132718Skan char *dir = fn; 189132718Skan 190132718Skan if (tTd(44, 4)) 191132718Skan sm_dprintf("\t%s\n", sm_errstring(ret)); 192132718Skan 193132718Skan errno = 0; 194132718Skan if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT) 195132718Skan return ret; 196132718Skan 197132718Skan /* check to see if legal to create the file */ 198132718Skan p = strrchr(dir, '/'); 199132718Skan if (p == NULL) 200132718Skan dir = "."; 201132718Skan else if (p == dir) 202132718Skan dir = "/"; 203132718Skan else 204132718Skan *p = '\0'; 205132718Skan if (stat(dir, &stbuf) >= 0) 206132718Skan { 207132718Skan int md = S_IWRITE|S_IEXEC; 208132718Skan 209132718Skan ret = 0; 210132718Skan if (stbuf.st_uid == uid) 211132718Skan /* EMPTY */ 212132718Skan ; 213132718Skan else if (uid == 0 && stbuf.st_uid == TrustedUid) 214132718Skan /* EMPTY */ 215132718Skan ; 216132718Skan else 217132718Skan { 218132718Skan md >>= 3; 219132718Skan if (stbuf.st_gid == gid) 220132718Skan /* EMPTY */ 221132718Skan ; 222132718Skan# ifndef NO_GROUP_SET 223132718Skan else if (user != NULL && !DontInitGroups && 224132718Skan ((gr != NULL && 225132718Skan gr->gr_gid == stbuf.st_gid) || 226132718Skan (gr = getgrgid(stbuf.st_gid)) != NULL)) 227132718Skan { 228132718Skan register char **gp; 229132718Skan 230132718Skan for (gp = gr->gr_mem; *gp != NULL; gp++) 231132718Skan if (strcmp(*gp, user) == 0) 232132718Skan break; 233132718Skan if (*gp == NULL) 234132718Skan md >>= 3; 235132718Skan } 236132718Skan# endif /* ! NO_GROUP_SET */ 237132718Skan else 238132718Skan md >>= 3; 239132718Skan } 240132718Skan if ((stbuf.st_mode & md) != md) 241132718Skan ret = errno = EACCES; 242132718Skan } 243132718Skan else 244132718Skan ret = errno; 245132718Skan if (tTd(44, 4)) 246132718Skan sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n", 247132718Skan dir, (int) stbuf.st_uid, 248132718Skan (unsigned long) stbuf.st_mode, 249132718Skan sm_errstring(ret)); 250132718Skan if (p != NULL) 251132718Skan *p = '/'; 252132718Skan st->st_mode = ST_MODE_NOFILE; 253132718Skan return ret; 254132718Skan } 255132718Skan 256132718Skan# ifdef S_ISLNK 257132718Skan if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) 258169689Skan { 259169689Skan if (tTd(44, 4)) 260169689Skan sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n", 261132718Skan (unsigned long) st->st_mode); 262132718Skan return E_SM_NOSLINK; 263132718Skan } 264132718Skan# endif /* S_ISLNK */ 265132718Skan if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) 266132718Skan { 267132718Skan if (tTd(44, 4)) 268132718Skan sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n", 269132718Skan (unsigned long) st->st_mode); 270132718Skan return E_SM_REGONLY; 271132718Skan } 272132718Skan if (bitset(SFF_NOGWFILES, flags) && 273169689Skan bitset(S_IWGRP, st->st_mode)) 274169689Skan { 275169689Skan if (tTd(44, 4)) 276132718Skan sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n", 277132718Skan (unsigned long) st->st_mode); 278132718Skan return E_SM_GWFILE; 279132718Skan } 280132718Skan if (bitset(SFF_NOWWFILES, flags) && 281132718Skan bitset(S_IWOTH, st->st_mode)) 282132718Skan { 283132718Skan if (tTd(44, 4)) 284132718Skan sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n", 285169689Skan (unsigned long) st->st_mode); 286169689Skan return E_SM_WWFILE; 287169689Skan } 288132718Skan if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode)) 289132718Skan { 290132718Skan if (tTd(44, 4)) 291169689Skan sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n", 292169689Skan (unsigned long) st->st_mode); 293169689Skan return E_SM_GRFILE; 294169689Skan } 295169689Skan if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode)) 296169689Skan { 297169689Skan if (tTd(44, 4)) 298169689Skan sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n", 299169689Skan (unsigned long) st->st_mode); 300169689Skan return E_SM_WRFILE; 301169689Skan } 302169689Skan if (!bitset(SFF_EXECOK, flags) && 303132718Skan bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && 304132718Skan bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode)) 305132718Skan { 306132718Skan if (tTd(44, 4)) 307132718Skan sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n", 308132718Skan (unsigned long) st->st_mode); 309132718Skan return E_SM_ISEXEC; 310132718Skan } 311132718Skan if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1) 312132718Skan { 313132718Skan if (tTd(44, 4)) 314132718Skan sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n", 315132718Skan (int) st->st_nlink); 316132718Skan return E_SM_NOHLINK; 317132718Skan } 318132718Skan 319132718Skan if (uid == 0 && bitset(SFF_OPENASROOT, flags)) 320132718Skan /* EMPTY */ 321132718Skan ; 322132718Skan else if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 323132718Skan mode >>= 6; 324132718Skan else if (st->st_uid == uid) 325132718Skan /* EMPTY */ 326132718Skan ; 327132718Skan else if (uid == 0 && st->st_uid == TrustedUid) 328132718Skan /* EMPTY */ 329132718Skan ; 330132718Skan else 331132718Skan { 332132718Skan mode >>= 3; 333169689Skan if (st->st_gid == gid) 334169689Skan /* EMPTY */ 335169689Skan ; 336169689Skan# ifndef NO_GROUP_SET 337169689Skan else if (user != NULL && !DontInitGroups && 338169689Skan ((gr != NULL && gr->gr_gid == st->st_gid) || 339169689Skan (gr = getgrgid(st->st_gid)) != NULL)) 340169689Skan { 341169689Skan register char **gp; 342132718Skan 343132718Skan for (gp = gr->gr_mem; *gp != NULL; gp++) 344132718Skan if (strcmp(*gp, user) == 0) 345132718Skan break; 346132718Skan if (*gp == NULL) 347132718Skan mode >>= 3; 348132718Skan } 349132718Skan# endif /* ! NO_GROUP_SET */ 350132718Skan else 351132718Skan mode >>= 3; 352132718Skan } 353132718Skan if (tTd(44, 4)) 354132718Skan sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ", 355132718Skan (int) st->st_uid, (int) st->st_nlink, 356169689Skan (unsigned long) st->st_mode, (unsigned long) mode); 357169689Skan if ((st->st_uid == uid || st->st_uid == 0 || 358169689Skan st->st_uid == TrustedUid || 359132718Skan !bitset(SFF_MUSTOWN, flags)) && 360132718Skan (st->st_mode & mode) == mode) 361132718Skan { 362132718Skan if (tTd(44, 4)) 363132718Skan sm_dprintf("\tOK\n"); 364132718Skan return 0; 365132718Skan } 366132718Skan if (tTd(44, 4)) 367132718Skan sm_dprintf("\tEACCES\n"); 368132718Skan return EACCES; 369132718Skan} 370132718Skan/* 371132718Skan** SAFEDIRPATH -- check to make sure a path to a directory is safe 372132718Skan** 373132718Skan** Safe means not writable and owned by the right folks. 374132718Skan** 375132718Skan** Parameters: 376132718Skan** fn -- filename to check. 377132718Skan** uid -- user id to compare against. 378132718Skan** gid -- group id to compare against. 379132718Skan** user -- user name to compare against (used for group 380132718Skan** sets). 381169689Skan** flags -- modifiers: 382169689Skan** SFF_ROOTOK -- ok to use root permissions to open. 383169689Skan** SFF_SAFEDIRPATH -- writable directories are considered 384132718Skan** to be fatal errors. 385132718Skan** level -- symlink recursive level. 386132718Skan** offset -- offset into fn to start checking from. 387132718Skan** 388132718Skan** Returns: 389132718Skan** 0 -- if the directory path is "safe". 390132718Skan** else -- an error number associated with the path. 391132718Skan*/ 392132718Skan 393132718Skanint 394132718Skansafedirpath(fn, uid, gid, user, flags, level, offset) 395132718Skan char *fn; 396132718Skan UID_T uid; 397132718Skan GID_T gid; 398132718Skan char *user; 399132718Skan long flags; 400132718Skan int level; 401132718Skan int offset; 402132718Skan{ 403132718Skan int ret = 0; 404132718Skan int mode = S_IWOTH; 405132718Skan char save = '\0'; 406132718Skan char *saveptr = NULL; 407132718Skan char *p, *enddir; 408132718Skan register struct group *gr = NULL; 409132718Skan char s[MAXLINKPATHLEN]; 410132718Skan struct stat stbuf; 411132718Skan 412132718Skan /* make sure we aren't in a symlink loop */ 413132718Skan if (level > MAXSYMLINKS) 414132718Skan return ELOOP; 415132718Skan 416132718Skan if (level < 0 || offset < 0 || offset > strlen(fn)) 417132718Skan return EINVAL; 418132718Skan 419132718Skan /* special case root directory */ 42090075Sobrien if (*fn == '\0') 421132718Skan fn = "/"; 422132718Skan 423132718Skan if (tTd(44, 4)) 424132718Skan sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n", 425132718Skan fn, (long) uid, (long) gid, flags, level, offset); 426132718Skan 427132718Skan if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail)) 428132718Skan mode |= S_IWGRP; 429132718Skan 430132718Skan /* Make a modifiable copy of the filename */ 431132718Skan if (sm_strlcpy(s, fn, sizeof s) >= sizeof s) 432132718Skan return EINVAL; 433132718Skan 434132718Skan p = s + offset; 435132718Skan while (p != NULL) 436132718Skan { 437132718Skan /* put back character */ 438132718Skan if (saveptr != NULL) 439132718Skan { 440132718Skan *saveptr = save; 441132718Skan saveptr = NULL; 442132718Skan p++; 443132718Skan } 444132718Skan 445132718Skan if (*p == '\0') 446132718Skan break; 447132718Skan 448132718Skan p = strchr(p, '/'); 449132718Skan 450132718Skan /* Special case for root directory */ 451132718Skan if (p == s) 452132718Skan { 453132718Skan save = *(p + 1); 454132718Skan saveptr = p + 1; 455132718Skan *(p + 1) = '\0'; 456132718Skan } 457132718Skan else if (p != NULL) 458169689Skan { 459169689Skan save = *p; 460169689Skan saveptr = p; 461169689Skan *p = '\0'; 462169689Skan } 463169689Skan 464132718Skan /* Heuristic: . and .. have already been checked */ 465132718Skan enddir = strrchr(s, '/'); 466132718Skan if (enddir != NULL && 467132718Skan (strcmp(enddir, "/..") == 0 || 468132718Skan strcmp(enddir, "/.") == 0)) 469132718Skan continue; 470132718Skan 471132718Skan if (tTd(44, 20)) 472132718Skan sm_dprintf("\t[dir %s]\n", s); 473132718Skan 474132718Skan# if HASLSTAT 475132718Skan ret = lstat(s, &stbuf); 476132718Skan# else /* HASLSTAT */ 477132718Skan ret = stat(s, &stbuf); 478132718Skan# endif /* HASLSTAT */ 479132718Skan if (ret < 0) 480132718Skan { 481132718Skan ret = errno; 482132718Skan break; 483132718Skan } 484132718Skan 485132718Skan# ifdef S_ISLNK 486132718Skan /* Follow symlinks */ 487132718Skan if (S_ISLNK(stbuf.st_mode)) 488132718Skan { 489132718Skan int linklen; 490132718Skan char *target; 49190075Sobrien char buf[MAXPATHLEN]; 492132718Skan char fullbuf[MAXLINKPATHLEN]; 493132718Skan 494132718Skan memset(buf, '\0', sizeof buf); 495132718Skan linklen = readlink(s, buf, sizeof buf); 496132718Skan if (linklen < 0) 497132718Skan { 498169689Skan ret = errno; 499169689Skan break; 500169689Skan } 501169689Skan if (linklen >= sizeof buf) 502169689Skan { 503169689Skan /* file name too long for buffer */ 504169689Skan ret = errno = EINVAL; 505169689Skan break; 506169689Skan } 507169689Skan 508169689Skan offset = 0; 509169689Skan if (*buf == '/') 510169689Skan { 511169689Skan target = buf; 512169689Skan 513169689Skan /* If path is the same, avoid rechecks */ 514169689Skan while (s[offset] == buf[offset] && 515169689Skan s[offset] != '\0') 516169689Skan offset++; 517169689Skan 518169689Skan if (s[offset] == '\0' && buf[offset] == '\0') 519169689Skan { 520169689Skan /* strings match, symlink loop */ 521169689Skan return ELOOP; 522169689Skan } 523169689Skan 52490075Sobrien /* back off from the mismatch */ 525132718Skan if (offset > 0) 526169689Skan offset--; 527169689Skan 528169689Skan /* Make sure we are at a directory break */ 529169689Skan if (offset > 0 && 530169689Skan s[offset] != '/' && 531169689Skan s[offset] != '\0') 532169689Skan { 533169689Skan while (buf[offset] != '/' && 534169689Skan offset > 0) 535169689Skan offset--; 536169689Skan } 537132718Skan if (offset > 0 && 538132718Skan s[offset] == '/' && 539169689Skan buf[offset] == '/') 540169689Skan { 541169689Skan /* Include the trailing slash */ 542169689Skan offset++; 543132718Skan } 544132718Skan } 545132718Skan else 546132718Skan { 547169689Skan char *sptr; 548169689Skan 549132718Skan sptr = strrchr(s, '/'); 550132718Skan if (sptr != NULL) 551132718Skan { 552132718Skan *sptr = '\0'; 553132718Skan offset = sptr + 1 - s; 55490075Sobrien if (sm_strlcpyn(fullbuf, 555169689Skan sizeof fullbuf, 2, 556169689Skan s, "/") >= 557169689Skan sizeof fullbuf || 558169689Skan sm_strlcat(fullbuf, buf, 559169689Skan sizeof fullbuf) >= 560169689Skan sizeof fullbuf) 561169689Skan { 562169689Skan ret = EINVAL; 563169689Skan break; 564169689Skan } 565169689Skan *sptr = '/'; 566169689Skan } 567169689Skan else 568169689Skan { 569169689Skan if (sm_strlcpy(fullbuf, buf, 570169689Skan sizeof fullbuf) >= 571169689Skan sizeof fullbuf) 572169689Skan { 573169689Skan ret = EINVAL; 574169689Skan break; 575169689Skan } 576169689Skan } 577169689Skan target = fullbuf; 578169689Skan } 579169689Skan ret = safedirpath(target, uid, gid, user, flags, 580169689Skan level + 1, offset); 581169689Skan if (ret != 0) 582169689Skan break; 583169689Skan 584169689Skan /* Don't check permissions on the link file itself */ 585169689Skan continue; 586169689Skan } 587169689Skan#endif /* S_ISLNK */ 588169689Skan 589132718Skan if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) && 590132718Skan#ifdef S_ISVTX 591132718Skan !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) && 592132718Skan bitset(S_ISVTX, stbuf.st_mode)) && 593132718Skan#endif /* S_ISVTX */ 594132718Skan bitset(mode, stbuf.st_mode)) 595132718Skan { 596132718Skan if (tTd(44, 4)) 597132718Skan sm_dprintf("\t[dir %s] mode %lo ", 598132718Skan s, (unsigned long) stbuf.st_mode); 599169689Skan if (bitset(SFF_SAFEDIRPATH, flags)) 600169689Skan { 601169689Skan if (bitset(S_IWOTH, stbuf.st_mode)) 602169689Skan ret = E_SM_WWDIR; 603132718Skan else 604169689Skan ret = E_SM_GWDIR; 605169689Skan if (tTd(44, 4)) 606169689Skan sm_dprintf("FATAL\n"); 607169689Skan break; 608132718Skan } 609132718Skan if (tTd(44, 4)) 610132718Skan sm_dprintf("WARNING\n"); 611169689Skan if (Verbose > 1) 612132718Skan message("051 WARNING: %s writable directory %s", 613132718Skan bitset(S_IWOTH, stbuf.st_mode) 614169689Skan ? "World" 615169689Skan : "Group", 616169689Skan s); 617169689Skan } 618169689Skan if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)) 619169689Skan { 620132718Skan if (bitset(S_IXOTH, stbuf.st_mode)) 621169689Skan continue; 622132718Skan ret = EACCES; 623132718Skan break; 624169689Skan } 625169689Skan 626169689Skan /* 627169689Skan ** Let OS determine access to file if we are not 628169689Skan ** running as a privileged user. This allows ACLs 629169689Skan ** to work. Also, if opening as root, assume we can 630132718Skan ** scan the directory. 631132718Skan */ 632132718Skan if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags)) 633132718Skan continue; 634169689Skan 635169689Skan if (stbuf.st_uid == uid && 636169689Skan bitset(S_IXUSR, stbuf.st_mode)) 637169689Skan continue; 638169689Skan if (stbuf.st_gid == gid && 639169689Skan bitset(S_IXGRP, stbuf.st_mode)) 640169689Skan continue; 641169689Skan# ifndef NO_GROUP_SET 642169689Skan if (user != NULL && !DontInitGroups && 643169689Skan ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 644169689Skan (gr = getgrgid(stbuf.st_gid)) != NULL)) 645169689Skan { 646169689Skan register char **gp; 647169689Skan 648169689Skan for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) 649169689Skan if (strcmp(*gp, user) == 0) 650169689Skan break; 651169689Skan if (gp != NULL && *gp != NULL && 652132718Skan bitset(S_IXGRP, stbuf.st_mode)) 653132718Skan continue; 654132718Skan } 655132718Skan# endif /* ! NO_GROUP_SET */ 656132718Skan if (!bitset(S_IXOTH, stbuf.st_mode)) 657132718Skan { 658132718Skan ret = EACCES; 659132718Skan break; 660132718Skan } 661132718Skan } 662132718Skan if (tTd(44, 4)) 663169689Skan sm_dprintf("\t[dir %s] %s\n", fn, 664169689Skan ret == 0 ? "OK" : sm_errstring(ret)); 665169689Skan return ret; 666169689Skan} 667169689Skan/* 668169689Skan** SAFEOPEN -- do a file open with extra checking 669169689Skan** 670169689Skan** Parameters: 671169689Skan** fn -- the file name to open. 672132718Skan** omode -- the open-style mode flags. 673169689Skan** cmode -- the create-style mode flags. 674132718Skan** sff -- safefile flags. 675132718Skan** 676132718Skan** Returns: 677132718Skan** Same as open. 678132718Skan*/ 679132718Skan 680132718Skanint 681169689Skansafeopen(fn, omode, cmode, sff) 682132718Skan char *fn; 683132718Skan int omode; 684132718Skan int cmode; 685132718Skan long sff; 686132718Skan{ 687169689Skan#if !NOFTRUNCATE 688169689Skan bool truncate; 689169689Skan#endif /* !NOFTRUNCATE */ 690169689Skan int rval; 691169689Skan int fd; 692169689Skan int smode; 693169689Skan struct stat stb; 694169689Skan 695169689Skan if (tTd(44, 10)) 696169689Skan sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n", 697169689Skan fn, omode, cmode, sff); 698169689Skan 699169689Skan if (bitset(O_CREAT, omode)) 700169689Skan sff |= SFF_CREAT; 701169689Skan omode &= ~O_CREAT; 702169689Skan switch (omode & O_ACCMODE) 703169689Skan { 704169689Skan case O_RDONLY: 705169689Skan smode = S_IREAD; 706169689Skan break; 707169689Skan 708169689Skan case O_WRONLY: 709169689Skan smode = S_IWRITE; 710169689Skan break; 711169689Skan 712169689Skan case O_RDWR: 713169689Skan smode = S_IREAD|S_IWRITE; 714169689Skan break; 715169689Skan 716169689Skan default: 717169689Skan smode = 0; 718169689Skan break; 719169689Skan } 720169689Skan if (bitset(SFF_OPENASROOT, sff)) 721169689Skan rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName, 722169689Skan sff, smode, &stb); 723169689Skan else 724169689Skan rval = safefile(fn, RealUid, RealGid, RealUserName, 725169689Skan sff, smode, &stb); 726169689Skan if (rval != 0) 727169689Skan { 728169689Skan errno = rval; 729169689Skan return -1; 730169689Skan } 731 if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff)) 732 omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL); 733 else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode)) 734 { 735 /* The file exists so an exclusive create would fail */ 736 errno = EEXIST; 737 return -1; 738 } 739 740#if !NOFTRUNCATE 741 truncate = bitset(O_TRUNC, omode); 742 if (truncate) 743 omode &= ~O_TRUNC; 744#endif /* !NOFTRUNCATE */ 745 746 fd = dfopen(fn, omode, cmode, sff); 747 if (fd < 0) 748 return fd; 749 if (filechanged(fn, fd, &stb)) 750 { 751 syserr("554 5.3.0 cannot open: file %s changed after open", fn); 752 (void) close(fd); 753 errno = E_SM_FILECHANGE; 754 return -1; 755 } 756 757#if !NOFTRUNCATE 758 if (truncate && 759 ftruncate(fd, (off_t) 0) < 0) 760 { 761 int save_errno; 762 763 save_errno = errno; 764 syserr("554 5.3.0 cannot open: file %s could not be truncated", 765 fn); 766 (void) close(fd); 767 errno = save_errno; 768 return -1; 769 } 770#endif /* !NOFTRUNCATE */ 771 772 return fd; 773} 774/* 775** SAFEFOPEN -- do a file open with extra checking 776** 777** Parameters: 778** fn -- the file name to open. 779** omode -- the open-style mode flags. 780** cmode -- the create-style mode flags. 781** sff -- safefile flags. 782** 783** Returns: 784** Same as fopen. 785*/ 786 787SM_FILE_T * 788safefopen(fn, omode, cmode, sff) 789 char *fn; 790 int omode; 791 int cmode; 792 long sff; 793{ 794 int fd; 795 int save_errno; 796 SM_FILE_T *fp; 797 int fmode; 798 799 switch (omode & O_ACCMODE) 800 { 801 case O_RDONLY: 802 fmode = SM_IO_RDONLY; 803 break; 804 805 case O_WRONLY: 806 if (bitset(O_APPEND, omode)) 807 fmode = SM_IO_APPEND; 808 else 809 fmode = SM_IO_WRONLY; 810 break; 811 812 case O_RDWR: 813 if (bitset(O_TRUNC, omode)) 814 fmode = SM_IO_RDWRTR; 815 else if (bitset(O_APPEND, omode)) 816 fmode = SM_IO_APPENDRW; 817 else 818 fmode = SM_IO_RDWR; 819 break; 820 821 default: 822 syserr("554 5.3.5 safefopen: unknown omode %o", omode); 823 fmode = 0; 824 } 825 fd = safeopen(fn, omode, cmode, sff); 826 if (fd < 0) 827 { 828 save_errno = errno; 829 if (tTd(44, 10)) 830 sm_dprintf("safefopen: safeopen failed: %s\n", 831 sm_errstring(errno)); 832 errno = save_errno; 833 return NULL; 834 } 835 fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 836 (void *) &fd, fmode, NULL); 837 if (fp != NULL) 838 return fp; 839 840 save_errno = errno; 841 if (tTd(44, 10)) 842 { 843 sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n", 844 fn, fmode, omode, sff, sm_errstring(errno)); 845 } 846 (void) close(fd); 847 errno = save_errno; 848 return NULL; 849} 850/* 851** FILECHANGED -- check to see if file changed after being opened 852** 853** Parameters: 854** fn -- pathname of file to check. 855** fd -- file descriptor to check. 856** stb -- stat structure from before open. 857** 858** Returns: 859** true -- if a problem was detected. 860** false -- if this file is still the same. 861*/ 862 863bool 864filechanged(fn, fd, stb) 865 char *fn; 866 int fd; 867 struct stat *stb; 868{ 869 struct stat sta; 870 871 if (stb->st_mode == ST_MODE_NOFILE) 872 { 873# if HASLSTAT && BOGUS_O_EXCL 874 /* only necessary if exclusive open follows symbolic links */ 875 if (lstat(fn, stb) < 0 || stb->st_nlink != 1) 876 return true; 877# else /* HASLSTAT && BOGUS_O_EXCL */ 878 return false; 879# endif /* HASLSTAT && BOGUS_O_EXCL */ 880 } 881 if (fstat(fd, &sta) < 0) 882 return true; 883 884 if (sta.st_nlink != stb->st_nlink || 885 sta.st_dev != stb->st_dev || 886 sta.st_ino != stb->st_ino || 887# if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ 888 sta.st_gen != stb->st_gen || 889# endif /* HAS_ST_GEN && 0 */ 890 sta.st_uid != stb->st_uid || 891 sta.st_gid != stb->st_gid) 892 { 893 if (tTd(44, 8)) 894 { 895 sm_dprintf("File changed after opening:\n"); 896 sm_dprintf(" nlink = %ld/%ld\n", 897 (long) stb->st_nlink, (long) sta.st_nlink); 898 sm_dprintf(" dev = %ld/%ld\n", 899 (long) stb->st_dev, (long) sta.st_dev); 900 sm_dprintf(" ino = %llu/%llu\n", 901 (ULONGLONG_T) stb->st_ino, 902 (ULONGLONG_T) sta.st_ino); 903# if HAS_ST_GEN 904 sm_dprintf(" gen = %ld/%ld\n", 905 (long) stb->st_gen, (long) sta.st_gen); 906# endif /* HAS_ST_GEN */ 907 sm_dprintf(" uid = %ld/%ld\n", 908 (long) stb->st_uid, (long) sta.st_uid); 909 sm_dprintf(" gid = %ld/%ld\n", 910 (long) stb->st_gid, (long) sta.st_gid); 911 } 912 return true; 913 } 914 915 return false; 916} 917/* 918** DFOPEN -- determined file open 919** 920** This routine has the semantics of open, except that it will 921** keep trying a few times to make this happen. The idea is that 922** on very loaded systems, we may run out of resources (inodes, 923** whatever), so this tries to get around it. 924*/ 925 926int 927dfopen(filename, omode, cmode, sff) 928 char *filename; 929 int omode; 930 int cmode; 931 long sff; 932{ 933 register int tries; 934 int fd = -1; 935 struct stat st; 936 937 for (tries = 0; tries < 10; tries++) 938 { 939 (void) sleep((unsigned) (10 * tries)); 940 errno = 0; 941 fd = open(filename, omode, cmode); 942 if (fd >= 0) 943 break; 944 switch (errno) 945 { 946 case ENFILE: /* system file table full */ 947 case EINTR: /* interrupted syscall */ 948#ifdef ETXTBSY 949 case ETXTBSY: /* Apollo: net file locked */ 950#endif /* ETXTBSY */ 951 continue; 952 } 953 break; 954 } 955 if (!bitset(SFF_NOLOCK, sff) && 956 fd >= 0 && 957 fstat(fd, &st) >= 0 && 958 S_ISREG(st.st_mode)) 959 { 960 int locktype; 961 962 /* lock the file to avoid accidental conflicts */ 963 if ((omode & O_ACCMODE) != O_RDONLY) 964 locktype = LOCK_EX; 965 else 966 locktype = LOCK_SH; 967 if (bitset(SFF_NBLOCK, sff)) 968 locktype |= LOCK_NB; 969 970 if (!lockfile(fd, filename, NULL, locktype)) 971 { 972 int save_errno = errno; 973 974 (void) close(fd); 975 fd = -1; 976 errno = save_errno; 977 } 978 else 979 errno = 0; 980 } 981 return fd; 982} 983