121308Sache/* 221308Sache * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers. 3136644Sache * All rights reserved. 421308Sache * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 521308Sache * Copyright (c) 1988, 1993 621308Sache * The Regents of the University of California. All rights reserved. 721308Sache * 821308Sache * By using this file, you agree to the terms and conditions set 921308Sache * forth in the LICENSE file which can be found at the top level of 1058310Sache * the sendmail distribution. 1121308Sache * 1221308Sache */ 1321308Sache 1421308Sache#include <sendmail.h> 1521308Sache#include <sm/io.h> 1621308Sache#include <sm/errstring.h> 1721308Sache 1821308SacheSM_RCSID("@(#)$Id: safefile.c,v 8.129 2008/08/04 18:07:04 gshapiro Exp $") 1921308Sache 2021308Sache 2158310Sache/* 2221308Sache** SAFEFILE -- return 0 if a file exists and is safe for a user. 2321308Sache** 2421308Sache** Parameters: 2521308Sache** fn -- filename to check. 26136644Sache** uid -- user id to compare against. 2721308Sache** gid -- group id to compare against. 2821308Sache** user -- user name to compare against (used for group 29136644Sache** sets). 30136644Sache** flags -- modifiers: 31136644Sache** SFF_MUSTOWN -- "uid" must own this file. 32136644Sache** SFF_NOSLINK -- file cannot be a symbolic link. 3321308Sache** mode -- mode bits that must match. 3421308Sache** st -- if set, points to a stat structure that will 3521308Sache** get the stat info for the file. 3621308Sache** 3721308Sache** Returns: 3821308Sache** 0 if fn exists, is owned by uid, and matches mode. 3921308Sache** An errno otherwise. The actual errno is cleared. 40136644Sache** 4135486Sache** Side Effects: 4235486Sache** none. 4358310Sache*/ 4421308Sache 4521308Sacheint 4621308Sachesafefile(fn, uid, gid, user, flags, mode, st) 4721308Sache char *fn; 4821308Sache UID_T uid; 4921308Sache GID_T gid; 5021308Sache char *user; 5121308Sache long flags; 5221308Sache int mode; 5321308Sache struct stat *st; 5421308Sache{ 5521308Sache register char *p; 56119610Sache register struct group *gr = NULL; 57119610Sache int file_errno = 0; 58119610Sache bool checkpath; 5921308Sache struct stat stbuf; 60136644Sache struct stat fstbuf; 61119610Sache char fbuf[MAXPATHLEN]; 6258310Sache 63119610Sache if (tTd(44, 4)) 64119610Sache sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n", 65119610Sache fn, (int) uid, (int) gid, flags, mode); 66119610Sache errno = 0; 67119610Sache if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf) 68119610Sache { 69119610Sache if (tTd(44, 4)) 70119610Sache sm_dprintf("\tpathname too long\n"); 71119610Sache return ENAMETOOLONG; 72119610Sache } 73119610Sache fn = fbuf; 74119610Sache if (st == NULL) 75136644Sache st = &fstbuf; 76119610Sache 7758310Sache /* ignore SFF_SAFEDIRPATH if we are debugging */ 7858310Sache if (RealUid != 0 && RunAsUid == RealUid) 7958310Sache flags &= ~SFF_SAFEDIRPATH; 8058310Sache 8158310Sache /* first check to see if the file exists at all */ 8226497Sache# if HASLSTAT 8326497Sache if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) 8426497Sache : stat(fn, st)) < 0) 8558310Sache# else /* HASLSTAT */ 8626497Sache if (stat(fn, st) < 0) 8726497Sache# endif /* HASLSTAT */ 8858310Sache { 8926497Sache file_errno = errno; 9021308Sache } 9121308Sache else if (bitset(SFF_SETUIDOK, flags) && 9221308Sache !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) && 9321308Sache S_ISREG(st->st_mode)) 9421308Sache { 9521308Sache /* 9621308Sache ** If final file is set-user-ID, run as the owner of that 9721308Sache ** file. Gotta be careful not to reveal anything too 9858310Sache ** soon here! 9958310Sache */ 10026497Sache 101136644Sache# ifdef SUID_ROOT_FILES_OK 102136644Sache if (bitset(S_ISUID, st->st_mode)) 103136644Sache# else /* SUID_ROOT_FILES_OK */ 104136644Sache if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 && 105136644Sache st->st_uid != TrustedUid) 106136644Sache# endif /* SUID_ROOT_FILES_OK */ 107136644Sache { 10821308Sache uid = st->st_uid; 10921308Sache user = NULL; 11021308Sache } 11121308Sache# ifdef SUID_ROOT_FILES_OK 11221308Sache if (bitset(S_ISGID, st->st_mode)) 11375406Sache# else /* SUID_ROOT_FILES_OK */ 11421308Sache if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0) 11575406Sache# endif /* SUID_ROOT_FILES_OK */ 11675406Sache gid = st->st_gid; 11721308Sache } 11821308Sache 11921308Sache checkpath = !bitset(SFF_NOPATHCHECK, flags) || 12021308Sache (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)); 12121308Sache if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags)) 12221308Sache { 12321308Sache int ret; 12475406Sache 12521308Sache /* check the directory */ 12621308Sache p = strrchr(fn, '/'); 12721308Sache if (p == NULL) 12821308Sache { 12921308Sache ret = safedirpath(".", uid, gid, user, 13021308Sache flags|SFF_SAFEDIRPATH, 0, 0); 13121308Sache } 13221308Sache else 13321308Sache { 134119610Sache *p = '\0'; 13521308Sache ret = safedirpath(fn, uid, gid, user, 13621308Sache flags|SFF_SAFEDIRPATH, 0, 0); 13758310Sache *p = '/'; 13858310Sache } 13958310Sache if (ret == 0) 14021308Sache { 14158310Sache /* directory is safe */ 14221308Sache checkpath = false; 14321308Sache } 14421308Sache else 14521308Sache { 14621308Sache# if HASLSTAT 14721308Sache /* Need lstat() information if called stat() before */ 14821308Sache if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0) 14921308Sache { 15021308Sache ret = errno; 15175406Sache if (tTd(44, 4)) 15221308Sache sm_dprintf("\t%s\n", sm_errstring(ret)); 15321308Sache return ret; 15421308Sache } 15521308Sache# endif /* HASLSTAT */ 15621308Sache /* directory is writable: disallow links */ 15721308Sache flags |= SFF_NOLINK; 15821308Sache } 15921308Sache } 16021308Sache 16121308Sache if (checkpath) 16221308Sache { 16375406Sache int ret; 16421308Sache 16521308Sache p = strrchr(fn, '/'); 166136644Sache if (p == NULL) 167136644Sache { 16858310Sache ret = safedirpath(".", uid, gid, user, flags, 0, 0); 16921308Sache } 17035486Sache else 171136644Sache { 172136644Sache *p = '\0'; 173136644Sache ret = safedirpath(fn, uid, gid, user, flags, 0, 0); 174136644Sache *p = '/'; 175136644Sache } 176136644Sache if (ret != 0) 177136644Sache return ret; 17821308Sache } 179136644Sache 18021308Sache /* 18126497Sache ** If the target file doesn't exist, check the directory to 18221308Sache ** ensure that it is writable by this user. 18321308Sache */ 18421308Sache 18521308Sache if (file_errno != 0) 18635486Sache { 18721308Sache int ret = file_errno; 18835486Sache char *dir = fn; 18935486Sache 19021308Sache if (tTd(44, 4)) 191136644Sache sm_dprintf("\t%s\n", sm_errstring(ret)); 19235486Sache 19335486Sache errno = 0; 19435486Sache if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT) 195136644Sache return ret; 196119610Sache 197119610Sache /* check to see if legal to create the file */ 198119610Sache p = strrchr(dir, '/'); 199119610Sache if (p == NULL) 200136644Sache dir = "."; 201136644Sache else if (p == dir) 202136644Sache dir = "/"; 203136644Sache else 204119610Sache *p = '\0'; 205119610Sache if (stat(dir, &stbuf) >= 0) 206119610Sache { 207119610Sache int md = S_IWRITE|S_IEXEC; 208136644Sache 209136644Sache ret = 0; 210136644Sache if (stbuf.st_uid == uid) 211136644Sache /* EMPTY */ 21258310Sache ; 21358310Sache else if (uid == 0 && stbuf.st_uid == TrustedUid) 214119610Sache /* EMPTY */ 21558310Sache ; 21635486Sache else 21721308Sache { 218136644Sache md >>= 3; 219136644Sache if (stbuf.st_gid == gid) 220136644Sache /* EMPTY */ 221136644Sache ; 22221308Sache# ifndef NO_GROUP_SET 22321308Sache else if (user != NULL && !DontInitGroups && 22421308Sache ((gr != NULL && 22521308Sache gr->gr_gid == stbuf.st_gid) || 226136644Sache (gr = getgrgid(stbuf.st_gid)) != NULL)) 22721308Sache { 228119610Sache register char **gp; 22921308Sache 230119610Sache for (gp = gr->gr_mem; *gp != NULL; gp++) 23121308Sache if (strcmp(*gp, user) == 0) 23221308Sache break; 23321308Sache if (*gp == NULL) 23421308Sache md >>= 3; 23521308Sache } 23621308Sache# endif /* ! NO_GROUP_SET */ 23758310Sache else 23821308Sache md >>= 3; 23921308Sache } 240119610Sache if ((stbuf.st_mode & md) != md) 241119610Sache ret = errno = EACCES; 24221308Sache } 24321308Sache else 244119610Sache ret = errno; 245119610Sache if (tTd(44, 4)) 246119610Sache sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n", 247136644Sache dir, (int) stbuf.st_uid, 248136644Sache (unsigned long) stbuf.st_mode, 249136644Sache sm_errstring(ret)); 250136644Sache if (p != NULL) 251136644Sache *p = '/'; 252136644Sache st->st_mode = ST_MODE_NOFILE; 253119610Sache return ret; 25421308Sache } 25521308Sache 256119610Sache# ifdef S_ISLNK 257119610Sache if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) 25821308Sache { 259165670Sache if (tTd(44, 4)) 260165670Sache sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n", 261165670Sache (unsigned long) st->st_mode); 262165670Sache return E_SM_NOSLINK; 263165670Sache } 26421308Sache# endif /* S_ISLNK */ 265119610Sache if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) 266136644Sache { 267136644Sache if (tTd(44, 4)) 268136644Sache sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n", 269136644Sache (unsigned long) st->st_mode); 270136644Sache return E_SM_REGONLY; 271136644Sache } 272136644Sache if (bitset(SFF_NOGWFILES, flags) && 273136644Sache bitset(S_IWGRP, st->st_mode)) 274136644Sache { 275136644Sache if (tTd(44, 4)) 276136644Sache sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n", 277136644Sache (unsigned long) st->st_mode); 278136644Sache return E_SM_GWFILE; 279136644Sache } 280136644Sache if (bitset(SFF_NOWWFILES, flags) && 281136644Sache bitset(S_IWOTH, st->st_mode)) 28221308Sache { 28321308Sache if (tTd(44, 4)) 28421308Sache sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n", 28521308Sache (unsigned long) st->st_mode); 28621308Sache return E_SM_WWFILE; 28721308Sache } 28821308Sache if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode)) 28921308Sache { 29021308Sache if (tTd(44, 4)) 29121308Sache sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n", 292136644Sache (unsigned long) st->st_mode); 29321308Sache return E_SM_GRFILE; 294119610Sache } 295119610Sache if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode)) 296119610Sache { 29721308Sache if (tTd(44, 4)) 29821308Sache sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n", 29921308Sache (unsigned long) st->st_mode); 30021308Sache return E_SM_WRFILE; 30121308Sache } 30275406Sache if (!bitset(SFF_EXECOK, flags) && 30375406Sache bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && 30421308Sache bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode)) 30521308Sache { 30675406Sache if (tTd(44, 4)) 30747558Sache sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n", 30821308Sache (unsigned long) st->st_mode); 309136644Sache return E_SM_ISEXEC; 31075406Sache } 31121308Sache if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1) 31235486Sache { 31321308Sache if (tTd(44, 4)) 31426497Sache sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n", 31521308Sache (int) st->st_nlink); 31626497Sache return E_SM_NOHLINK; 31775406Sache } 31821308Sache 31975406Sache if (uid == 0 && bitset(SFF_OPENASROOT, flags)) 32021308Sache /* EMPTY */ 32175406Sache ; 32275406Sache else if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 32375406Sache mode >>= 6; 32475406Sache else if (st->st_uid == uid) 32575406Sache /* EMPTY */ 32675406Sache ; 32721308Sache else if (uid == 0 && st->st_uid == TrustedUid) 32875406Sache /* EMPTY */ 32975406Sache ; 33075406Sache else 33175406Sache { 33275406Sache mode >>= 3; 33375406Sache if (st->st_gid == gid) 33475406Sache /* EMPTY */ 33575406Sache ; 33675406Sache# ifndef NO_GROUP_SET 33775406Sache else if (user != NULL && !DontInitGroups && 33858310Sache ((gr != NULL && gr->gr_gid == st->st_gid) || 33935486Sache (gr = getgrgid(st->st_gid)) != NULL)) 34035486Sache { 34135486Sache register char **gp; 34235486Sache 34335486Sache for (gp = gr->gr_mem; *gp != NULL; gp++) 34435486Sache if (strcmp(*gp, user) == 0) 34535486Sache break; 34675406Sache if (*gp == NULL) 34775406Sache mode >>= 3; 34875406Sache } 34975406Sache# endif /* ! NO_GROUP_SET */ 35075406Sache else 35135486Sache mode >>= 3; 35235486Sache } 35335486Sache if (tTd(44, 4)) 35435486Sache sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ", 355119610Sache (int) st->st_uid, (int) st->st_nlink, 356119610Sache (unsigned long) st->st_mode, (unsigned long) mode); 357119610Sache if ((st->st_uid == uid || st->st_uid == 0 || 358119610Sache st->st_uid == TrustedUid || 359119610Sache !bitset(SFF_MUSTOWN, flags)) && 360119610Sache (st->st_mode & mode) == mode) 361119610Sache { 36235486Sache if (tTd(44, 4)) 36321308Sache sm_dprintf("\tOK\n"); 36421308Sache return 0; 36521308Sache } 36675406Sache if (tTd(44, 4)) 36775406Sache sm_dprintf("\tEACCES\n"); 36875406Sache return EACCES; 36975406Sache} 37021308Sache/* 37121308Sache** SAFEDIRPATH -- check to make sure a path to a directory is safe 372136644Sache** 373136644Sache** Safe means not writable and owned by the right folks. 374136644Sache** 375136644Sache** Parameters: 37621308Sache** fn -- filename to check. 377136644Sache** uid -- user id to compare against. 37821308Sache** gid -- group id to compare against. 379136644Sache** user -- user name to compare against (used for group 38021308Sache** sets). 38121308Sache** flags -- modifiers: 38221308Sache** SFF_ROOTOK -- ok to use root permissions to open. 38321308Sache** SFF_SAFEDIRPATH -- writable directories are considered 38421308Sache** to be fatal errors. 38521308Sache** level -- symlink recursive level. 38621308Sache** offset -- offset into fn to start checking from. 387119610Sache** 388136644Sache** Returns: 389136644Sache** 0 -- if the directory path is "safe". 390136644Sache** else -- an error number associated with the path. 391136644Sache*/ 392136644Sache 393136644Sacheint 394136644Sachesafedirpath(fn, uid, gid, user, flags, level, offset) 395136644Sache char *fn; 39621308Sache UID_T uid; 39721308Sache GID_T gid; 39821308Sache char *user; 399119610Sache long flags; 40021308Sache int level; 401119610Sache int offset; 40247558Sache{ 40347558Sache int ret = 0; 40447558Sache int mode = S_IWOTH; 405119610Sache char save = '\0'; 40647558Sache char *saveptr = NULL; 40747558Sache char *p, *enddir; 40821308Sache register struct group *gr = NULL; 40921308Sache char s[MAXLINKPATHLEN]; 41021308Sache struct stat stbuf; 41121308Sache 41221308Sache /* make sure we aren't in a symlink loop */ 41321308Sache if (level > MAXSYMLINKS) 41421308Sache return ELOOP; 41521308Sache 41675406Sache if (level < 0 || offset < 0 || offset > strlen(fn)) 41721308Sache return EINVAL; 41821308Sache 41921308Sache /* special case root directory */ 42021308Sache if (*fn == '\0') 42121308Sache fn = "/"; 42221308Sache 42321308Sache if (tTd(44, 4)) 42475406Sache sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n", 42521308Sache fn, (long) uid, (long) gid, flags, level, offset); 42621308Sache 42721308Sache if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail)) 42826497Sache mode |= S_IWGRP; 42975406Sache 430136644Sache /* Make a modifiable copy of the filename */ 431119610Sache if (sm_strlcpy(s, fn, sizeof s) >= sizeof s) 43221308Sache return EINVAL; 433119610Sache 434119610Sache p = s + offset; 43526497Sache while (p != NULL) 436119610Sache { 43726497Sache /* put back character */ 43875406Sache if (saveptr != NULL) 43921308Sache { 44030971Sache *saveptr = save; 44121308Sache saveptr = NULL; 44221308Sache p++; 44321308Sache } 44421308Sache 44521308Sache if (*p == '\0') 446136644Sache break; 447119610Sache 448119610Sache p = strchr(p, '/'); 449119610Sache 45021308Sache /* Special case for root directory */ 45121308Sache if (p == s) 45221308Sache { 45321308Sache save = *(p + 1); 45421308Sache saveptr = p + 1; 45521308Sache *(p + 1) = '\0'; 45621308Sache } 45721308Sache else if (p != NULL) 45821308Sache { 45921308Sache save = *p; 46021308Sache saveptr = p; 46121308Sache *p = '\0'; 46221308Sache } 46321308Sache 464136644Sache /* Heuristic: . and .. have already been checked */ 465136644Sache enddir = strrchr(s, '/'); 466136644Sache if (enddir != NULL && 467136644Sache (strcmp(enddir, "/..") == 0 || 468136644Sache strcmp(enddir, "/.") == 0)) 469136644Sache continue; 470136644Sache 471136644Sache if (tTd(44, 20)) 472136644Sache sm_dprintf("\t[dir %s]\n", s); 47321308Sache 47421308Sache# if HASLSTAT 475136644Sache ret = lstat(s, &stbuf); 476119610Sache# else /* HASLSTAT */ 477119610Sache ret = stat(s, &stbuf); 478119610Sache# endif /* HASLSTAT */ 479119610Sache if (ret < 0) 480119610Sache { 481119610Sache ret = errno; 482119610Sache break; 483119610Sache } 484119610Sache 485119610Sache# ifdef S_ISLNK 486119610Sache /* Follow symlinks */ 487119610Sache if (S_ISLNK(stbuf.st_mode)) 488119610Sache { 489119610Sache int linklen; 490119610Sache char *target; 491119610Sache char buf[MAXPATHLEN]; 492119610Sache char fullbuf[MAXLINKPATHLEN]; 493119610Sache 494119610Sache memset(buf, '\0', sizeof buf); 495119610Sache linklen = readlink(s, buf, sizeof buf); 496119610Sache if (linklen < 0) 49721308Sache { 49821308Sache ret = errno; 49921308Sache break; 500136644Sache } 501136644Sache if (linklen >= sizeof buf) 502136644Sache { 503136644Sache /* file name too long for buffer */ 504136644Sache ret = errno = EINVAL; 505136644Sache break; 50621308Sache } 50721308Sache 50821308Sache offset = 0; 50921308Sache if (*buf == '/') 51021308Sache { 511136644Sache target = buf; 512119610Sache 513119610Sache /* If path is the same, avoid rechecks */ 514119610Sache while (s[offset] == buf[offset] && 51575406Sache s[offset] != '\0') 51675406Sache offset++; 51721308Sache 518119610Sache if (s[offset] == '\0' && buf[offset] == '\0') 51921308Sache { 52021308Sache /* strings match, symlink loop */ 52121308Sache return ELOOP; 52221308Sache } 52321308Sache 52421308Sache /* back off from the mismatch */ 52575406Sache if (offset > 0) 52621308Sache offset--; 52721308Sache 52821308Sache /* Make sure we are at a directory break */ 52921308Sache if (offset > 0 && 53021308Sache s[offset] != '/' && 53121308Sache s[offset] != '\0') 53221308Sache { 53375406Sache while (buf[offset] != '/' && 53421308Sache offset > 0) 53521308Sache offset--; 53621308Sache } 53721308Sache if (offset > 0 && 53821308Sache s[offset] == '/' && 53921308Sache buf[offset] == '/') 54021308Sache { 54121308Sache /* Include the trailing slash */ 54221308Sache offset++; 54375406Sache } 54421308Sache } 54521308Sache else 54621308Sache { 547 char *sptr; 548 549 sptr = strrchr(s, '/'); 550 if (sptr != NULL) 551 { 552 *sptr = '\0'; 553 offset = sptr + 1 - s; 554 if (sm_strlcpyn(fullbuf, 555 sizeof fullbuf, 2, 556 s, "/") >= 557 sizeof fullbuf || 558 sm_strlcat(fullbuf, buf, 559 sizeof fullbuf) >= 560 sizeof fullbuf) 561 { 562 ret = EINVAL; 563 break; 564 } 565 *sptr = '/'; 566 } 567 else 568 { 569 if (sm_strlcpy(fullbuf, buf, 570 sizeof fullbuf) >= 571 sizeof fullbuf) 572 { 573 ret = EINVAL; 574 break; 575 } 576 } 577 target = fullbuf; 578 } 579 ret = safedirpath(target, uid, gid, user, flags, 580 level + 1, offset); 581 if (ret != 0) 582 break; 583 584 /* Don't check permissions on the link file itself */ 585 continue; 586 } 587#endif /* S_ISLNK */ 588 589 if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) && 590#ifdef S_ISVTX 591 !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) && 592 bitset(S_ISVTX, stbuf.st_mode)) && 593#endif /* S_ISVTX */ 594 bitset(mode, stbuf.st_mode)) 595 { 596 if (tTd(44, 4)) 597 sm_dprintf("\t[dir %s] mode %lo ", 598 s, (unsigned long) stbuf.st_mode); 599 if (bitset(SFF_SAFEDIRPATH, flags)) 600 { 601 if (bitset(S_IWOTH, stbuf.st_mode)) 602 ret = E_SM_WWDIR; 603 else 604 ret = E_SM_GWDIR; 605 if (tTd(44, 4)) 606 sm_dprintf("FATAL\n"); 607 break; 608 } 609 if (tTd(44, 4)) 610 sm_dprintf("WARNING\n"); 611 if (Verbose > 1) 612 message("051 WARNING: %s writable directory %s", 613 bitset(S_IWOTH, stbuf.st_mode) 614 ? "World" 615 : "Group", 616 s); 617 } 618 if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)) 619 { 620 if (bitset(S_IXOTH, stbuf.st_mode)) 621 continue; 622 ret = EACCES; 623 break; 624 } 625 626 /* 627 ** Let OS determine access to file if we are not 628 ** running as a privileged user. This allows ACLs 629 ** to work. Also, if opening as root, assume we can 630 ** scan the directory. 631 */ 632 if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags)) 633 continue; 634 635 if (stbuf.st_uid == uid && 636 bitset(S_IXUSR, stbuf.st_mode)) 637 continue; 638 if (stbuf.st_gid == gid && 639 bitset(S_IXGRP, stbuf.st_mode)) 640 continue; 641# ifndef NO_GROUP_SET 642 if (user != NULL && !DontInitGroups && 643 ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 644 (gr = getgrgid(stbuf.st_gid)) != NULL)) 645 { 646 register char **gp; 647 648 for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) 649 if (strcmp(*gp, user) == 0) 650 break; 651 if (gp != NULL && *gp != NULL && 652 bitset(S_IXGRP, stbuf.st_mode)) 653 continue; 654 } 655# endif /* ! NO_GROUP_SET */ 656 if (!bitset(S_IXOTH, stbuf.st_mode)) 657 { 658 ret = EACCES; 659 break; 660 } 661 } 662 if (tTd(44, 4)) 663 sm_dprintf("\t[dir %s] %s\n", fn, 664 ret == 0 ? "OK" : sm_errstring(ret)); 665 return ret; 666} 667/* 668** SAFEOPEN -- do a file open with extra checking 669** 670** Parameters: 671** fn -- the file name to open. 672** omode -- the open-style mode flags. 673** cmode -- the create-style mode flags. 674** sff -- safefile flags. 675** 676** Returns: 677** Same as open. 678*/ 679 680int 681safeopen(fn, omode, cmode, sff) 682 char *fn; 683 int omode; 684 int cmode; 685 long sff; 686{ 687#if !NOFTRUNCATE 688 bool truncate; 689#endif /* !NOFTRUNCATE */ 690 int rval; 691 int fd; 692 int smode; 693 struct stat stb; 694 695 if (tTd(44, 10)) 696 sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n", 697 fn, omode, cmode, sff); 698 699 if (bitset(O_CREAT, omode)) 700 sff |= SFF_CREAT; 701 omode &= ~O_CREAT; 702 switch (omode & O_ACCMODE) 703 { 704 case O_RDONLY: 705 smode = S_IREAD; 706 break; 707 708 case O_WRONLY: 709 smode = S_IWRITE; 710 break; 711 712 case O_RDWR: 713 smode = S_IREAD|S_IWRITE; 714 break; 715 716 default: 717 smode = 0; 718 break; 719 } 720 if (bitset(SFF_OPENASROOT, sff)) 721 rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName, 722 sff, smode, &stb); 723 else 724 rval = safefile(fn, RealUid, RealGid, RealUserName, 725 sff, smode, &stb); 726 if (rval != 0) 727 { 728 errno = rval; 729 return -1; 730 } 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