164562Sgshapiro/* 2141858Sgshapiro * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 464562Sgshapiro * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 564562Sgshapiro * Copyright (c) 1988, 1993 664562Sgshapiro * The Regents of the University of California. All rights reserved. 764562Sgshapiro * 864562Sgshapiro * By using this file, you agree to the terms and conditions set 964562Sgshapiro * forth in the LICENSE file which can be found at the top level of 1064562Sgshapiro * the sendmail distribution. 1164562Sgshapiro * 1264562Sgshapiro */ 1364562Sgshapiro 1464562Sgshapiro#include <sendmail.h> 1590792Sgshapiro#include <sm/io.h> 1690792Sgshapiro#include <sm/errstring.h> 1764562Sgshapiro 18203004SgshapiroSM_RCSID("@(#)$Id: safefile.c,v 8.129 2008/08/04 18:07:04 gshapiro Exp $") 1964562Sgshapiro 2090792Sgshapiro 2190792Sgshapiro/* 2264562Sgshapiro** SAFEFILE -- return 0 if a file exists and is safe for a user. 2364562Sgshapiro** 2464562Sgshapiro** Parameters: 2564562Sgshapiro** fn -- filename to check. 2664562Sgshapiro** uid -- user id to compare against. 2764562Sgshapiro** gid -- group id to compare against. 2864562Sgshapiro** user -- user name to compare against (used for group 2964562Sgshapiro** sets). 3064562Sgshapiro** flags -- modifiers: 3164562Sgshapiro** SFF_MUSTOWN -- "uid" must own this file. 3264562Sgshapiro** SFF_NOSLINK -- file cannot be a symbolic link. 3364562Sgshapiro** mode -- mode bits that must match. 3464562Sgshapiro** st -- if set, points to a stat structure that will 3564562Sgshapiro** get the stat info for the file. 3664562Sgshapiro** 3764562Sgshapiro** Returns: 3864562Sgshapiro** 0 if fn exists, is owned by uid, and matches mode. 3964562Sgshapiro** An errno otherwise. The actual errno is cleared. 4064562Sgshapiro** 4164562Sgshapiro** Side Effects: 4264562Sgshapiro** none. 4364562Sgshapiro*/ 4464562Sgshapiro 4564562Sgshapiroint 4664562Sgshapirosafefile(fn, uid, gid, user, flags, mode, st) 4764562Sgshapiro char *fn; 4864562Sgshapiro UID_T uid; 4964562Sgshapiro GID_T gid; 5064562Sgshapiro char *user; 5164562Sgshapiro long flags; 5264562Sgshapiro int mode; 5364562Sgshapiro struct stat *st; 5464562Sgshapiro{ 5564562Sgshapiro register char *p; 5664562Sgshapiro register struct group *gr = NULL; 5764562Sgshapiro int file_errno = 0; 5864562Sgshapiro bool checkpath; 5964562Sgshapiro struct stat stbuf; 6064562Sgshapiro struct stat fstbuf; 6198121Sgshapiro char fbuf[MAXPATHLEN]; 6264562Sgshapiro 6364562Sgshapiro if (tTd(44, 4)) 6490792Sgshapiro sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n", 6564562Sgshapiro fn, (int) uid, (int) gid, flags, mode); 6664562Sgshapiro errno = 0; 6790792Sgshapiro if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf) 6864562Sgshapiro { 6964562Sgshapiro if (tTd(44, 4)) 7090792Sgshapiro sm_dprintf("\tpathname too long\n"); 7164562Sgshapiro return ENAMETOOLONG; 7264562Sgshapiro } 7364562Sgshapiro fn = fbuf; 7490792Sgshapiro if (st == NULL) 7590792Sgshapiro st = &fstbuf; 7664562Sgshapiro 7764562Sgshapiro /* ignore SFF_SAFEDIRPATH if we are debugging */ 7864562Sgshapiro if (RealUid != 0 && RunAsUid == RealUid) 7964562Sgshapiro flags &= ~SFF_SAFEDIRPATH; 8064562Sgshapiro 8164562Sgshapiro /* first check to see if the file exists at all */ 8264562Sgshapiro# if HASLSTAT 8364562Sgshapiro if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) 8464562Sgshapiro : stat(fn, st)) < 0) 8564562Sgshapiro# else /* HASLSTAT */ 8664562Sgshapiro if (stat(fn, st) < 0) 8764562Sgshapiro# endif /* HASLSTAT */ 8864562Sgshapiro { 8964562Sgshapiro file_errno = errno; 9064562Sgshapiro } 9164562Sgshapiro else if (bitset(SFF_SETUIDOK, flags) && 9264562Sgshapiro !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) && 9364562Sgshapiro S_ISREG(st->st_mode)) 9464562Sgshapiro { 9564562Sgshapiro /* 9690792Sgshapiro ** If final file is set-user-ID, run as the owner of that 9764562Sgshapiro ** file. Gotta be careful not to reveal anything too 9864562Sgshapiro ** soon here! 9964562Sgshapiro */ 10064562Sgshapiro 10164562Sgshapiro# ifdef SUID_ROOT_FILES_OK 10264562Sgshapiro if (bitset(S_ISUID, st->st_mode)) 10364562Sgshapiro# else /* SUID_ROOT_FILES_OK */ 10464562Sgshapiro if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 && 10564562Sgshapiro st->st_uid != TrustedUid) 10664562Sgshapiro# endif /* SUID_ROOT_FILES_OK */ 10764562Sgshapiro { 10864562Sgshapiro uid = st->st_uid; 10964562Sgshapiro user = NULL; 11064562Sgshapiro } 11164562Sgshapiro# ifdef SUID_ROOT_FILES_OK 11264562Sgshapiro if (bitset(S_ISGID, st->st_mode)) 11364562Sgshapiro# else /* SUID_ROOT_FILES_OK */ 11464562Sgshapiro if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0) 11564562Sgshapiro# endif /* SUID_ROOT_FILES_OK */ 11664562Sgshapiro gid = st->st_gid; 11764562Sgshapiro } 11864562Sgshapiro 11964562Sgshapiro checkpath = !bitset(SFF_NOPATHCHECK, flags) || 12064562Sgshapiro (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)); 12164562Sgshapiro if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags)) 12264562Sgshapiro { 12364562Sgshapiro int ret; 12464562Sgshapiro 12564562Sgshapiro /* check the directory */ 12664562Sgshapiro p = strrchr(fn, '/'); 12764562Sgshapiro if (p == NULL) 12864562Sgshapiro { 12964562Sgshapiro ret = safedirpath(".", uid, gid, user, 13064562Sgshapiro flags|SFF_SAFEDIRPATH, 0, 0); 13164562Sgshapiro } 13264562Sgshapiro else 13364562Sgshapiro { 13464562Sgshapiro *p = '\0'; 13564562Sgshapiro ret = safedirpath(fn, uid, gid, user, 13664562Sgshapiro flags|SFF_SAFEDIRPATH, 0, 0); 13764562Sgshapiro *p = '/'; 13864562Sgshapiro } 13964562Sgshapiro if (ret == 0) 14064562Sgshapiro { 14164562Sgshapiro /* directory is safe */ 14290792Sgshapiro checkpath = false; 14364562Sgshapiro } 14464562Sgshapiro else 14564562Sgshapiro { 14664562Sgshapiro# if HASLSTAT 14764562Sgshapiro /* Need lstat() information if called stat() before */ 14864562Sgshapiro if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0) 14964562Sgshapiro { 15064562Sgshapiro ret = errno; 15164562Sgshapiro if (tTd(44, 4)) 15290792Sgshapiro sm_dprintf("\t%s\n", sm_errstring(ret)); 15364562Sgshapiro return ret; 15464562Sgshapiro } 15564562Sgshapiro# endif /* HASLSTAT */ 15664562Sgshapiro /* directory is writable: disallow links */ 15764562Sgshapiro flags |= SFF_NOLINK; 15864562Sgshapiro } 15964562Sgshapiro } 16064562Sgshapiro 16164562Sgshapiro if (checkpath) 16264562Sgshapiro { 16364562Sgshapiro int ret; 16464562Sgshapiro 16564562Sgshapiro p = strrchr(fn, '/'); 16664562Sgshapiro if (p == NULL) 16764562Sgshapiro { 16864562Sgshapiro ret = safedirpath(".", uid, gid, user, flags, 0, 0); 16964562Sgshapiro } 17064562Sgshapiro else 17164562Sgshapiro { 17264562Sgshapiro *p = '\0'; 17364562Sgshapiro ret = safedirpath(fn, uid, gid, user, flags, 0, 0); 17464562Sgshapiro *p = '/'; 17564562Sgshapiro } 17664562Sgshapiro if (ret != 0) 17764562Sgshapiro return ret; 17864562Sgshapiro } 17964562Sgshapiro 18064562Sgshapiro /* 18164562Sgshapiro ** If the target file doesn't exist, check the directory to 18264562Sgshapiro ** ensure that it is writable by this user. 18364562Sgshapiro */ 18464562Sgshapiro 18564562Sgshapiro if (file_errno != 0) 18664562Sgshapiro { 18764562Sgshapiro int ret = file_errno; 18864562Sgshapiro char *dir = fn; 18964562Sgshapiro 19064562Sgshapiro if (tTd(44, 4)) 19190792Sgshapiro sm_dprintf("\t%s\n", sm_errstring(ret)); 19264562Sgshapiro 19364562Sgshapiro errno = 0; 19464562Sgshapiro if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT) 19564562Sgshapiro return ret; 19664562Sgshapiro 19764562Sgshapiro /* check to see if legal to create the file */ 19864562Sgshapiro p = strrchr(dir, '/'); 19964562Sgshapiro if (p == NULL) 20064562Sgshapiro dir = "."; 20164562Sgshapiro else if (p == dir) 20264562Sgshapiro dir = "/"; 20364562Sgshapiro else 20464562Sgshapiro *p = '\0'; 20564562Sgshapiro if (stat(dir, &stbuf) >= 0) 20664562Sgshapiro { 20764562Sgshapiro int md = S_IWRITE|S_IEXEC; 20864562Sgshapiro 20990792Sgshapiro ret = 0; 21064562Sgshapiro if (stbuf.st_uid == uid) 21164562Sgshapiro /* EMPTY */ 21264562Sgshapiro ; 21364562Sgshapiro else if (uid == 0 && stbuf.st_uid == TrustedUid) 21464562Sgshapiro /* EMPTY */ 21564562Sgshapiro ; 21664562Sgshapiro else 21764562Sgshapiro { 21864562Sgshapiro md >>= 3; 21964562Sgshapiro if (stbuf.st_gid == gid) 22064562Sgshapiro /* EMPTY */ 22164562Sgshapiro ; 22264562Sgshapiro# ifndef NO_GROUP_SET 22364562Sgshapiro else if (user != NULL && !DontInitGroups && 22464562Sgshapiro ((gr != NULL && 22564562Sgshapiro gr->gr_gid == stbuf.st_gid) || 22664562Sgshapiro (gr = getgrgid(stbuf.st_gid)) != NULL)) 22764562Sgshapiro { 22864562Sgshapiro register char **gp; 22964562Sgshapiro 23064562Sgshapiro for (gp = gr->gr_mem; *gp != NULL; gp++) 23164562Sgshapiro if (strcmp(*gp, user) == 0) 23264562Sgshapiro break; 23364562Sgshapiro if (*gp == NULL) 23464562Sgshapiro md >>= 3; 23564562Sgshapiro } 23664562Sgshapiro# endif /* ! NO_GROUP_SET */ 23764562Sgshapiro else 23864562Sgshapiro md >>= 3; 23964562Sgshapiro } 24064562Sgshapiro if ((stbuf.st_mode & md) != md) 24190792Sgshapiro ret = errno = EACCES; 24264562Sgshapiro } 24390792Sgshapiro else 24490792Sgshapiro ret = errno; 24564562Sgshapiro if (tTd(44, 4)) 24690792Sgshapiro sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n", 24790792Sgshapiro dir, (int) stbuf.st_uid, 24890792Sgshapiro (unsigned long) stbuf.st_mode, 24990792Sgshapiro sm_errstring(ret)); 25064562Sgshapiro if (p != NULL) 25164562Sgshapiro *p = '/'; 25264562Sgshapiro st->st_mode = ST_MODE_NOFILE; 25364562Sgshapiro return ret; 25464562Sgshapiro } 25564562Sgshapiro 25664562Sgshapiro# ifdef S_ISLNK 25764562Sgshapiro if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) 25864562Sgshapiro { 25964562Sgshapiro if (tTd(44, 4)) 26090792Sgshapiro sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n", 26190792Sgshapiro (unsigned long) st->st_mode); 26264562Sgshapiro return E_SM_NOSLINK; 26364562Sgshapiro } 26464562Sgshapiro# endif /* S_ISLNK */ 26564562Sgshapiro if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) 26664562Sgshapiro { 26764562Sgshapiro if (tTd(44, 4)) 26890792Sgshapiro sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n", 26990792Sgshapiro (unsigned long) st->st_mode); 27064562Sgshapiro return E_SM_REGONLY; 27164562Sgshapiro } 27264562Sgshapiro if (bitset(SFF_NOGWFILES, flags) && 27364562Sgshapiro bitset(S_IWGRP, st->st_mode)) 27464562Sgshapiro { 27564562Sgshapiro if (tTd(44, 4)) 27690792Sgshapiro sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n", 27790792Sgshapiro (unsigned long) st->st_mode); 27864562Sgshapiro return E_SM_GWFILE; 27964562Sgshapiro } 28064562Sgshapiro if (bitset(SFF_NOWWFILES, flags) && 28164562Sgshapiro bitset(S_IWOTH, st->st_mode)) 28264562Sgshapiro { 28364562Sgshapiro if (tTd(44, 4)) 28490792Sgshapiro sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n", 28590792Sgshapiro (unsigned long) st->st_mode); 28664562Sgshapiro return E_SM_WWFILE; 28764562Sgshapiro } 28864562Sgshapiro if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode)) 28964562Sgshapiro { 29064562Sgshapiro if (tTd(44, 4)) 29190792Sgshapiro sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n", 29290792Sgshapiro (unsigned long) st->st_mode); 29364562Sgshapiro return E_SM_GRFILE; 29464562Sgshapiro } 29564562Sgshapiro if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode)) 29664562Sgshapiro { 29764562Sgshapiro if (tTd(44, 4)) 29890792Sgshapiro sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n", 29990792Sgshapiro (unsigned long) st->st_mode); 30064562Sgshapiro return E_SM_WRFILE; 30164562Sgshapiro } 30264562Sgshapiro if (!bitset(SFF_EXECOK, flags) && 30364562Sgshapiro bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && 30464562Sgshapiro bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode)) 30564562Sgshapiro { 30664562Sgshapiro if (tTd(44, 4)) 307132943Sgshapiro sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n", 30890792Sgshapiro (unsigned long) st->st_mode); 30964562Sgshapiro return E_SM_ISEXEC; 31064562Sgshapiro } 31164562Sgshapiro if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1) 31264562Sgshapiro { 31364562Sgshapiro if (tTd(44, 4)) 31490792Sgshapiro sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n", 31564562Sgshapiro (int) st->st_nlink); 31664562Sgshapiro return E_SM_NOHLINK; 31764562Sgshapiro } 31864562Sgshapiro 31964562Sgshapiro if (uid == 0 && bitset(SFF_OPENASROOT, flags)) 32064562Sgshapiro /* EMPTY */ 32164562Sgshapiro ; 32264562Sgshapiro else if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 32364562Sgshapiro mode >>= 6; 32464562Sgshapiro else if (st->st_uid == uid) 32564562Sgshapiro /* EMPTY */ 32664562Sgshapiro ; 32764562Sgshapiro else if (uid == 0 && st->st_uid == TrustedUid) 32864562Sgshapiro /* EMPTY */ 32964562Sgshapiro ; 33064562Sgshapiro else 33164562Sgshapiro { 33264562Sgshapiro mode >>= 3; 33364562Sgshapiro if (st->st_gid == gid) 33464562Sgshapiro /* EMPTY */ 33564562Sgshapiro ; 33664562Sgshapiro# ifndef NO_GROUP_SET 33764562Sgshapiro else if (user != NULL && !DontInitGroups && 33864562Sgshapiro ((gr != NULL && gr->gr_gid == st->st_gid) || 33964562Sgshapiro (gr = getgrgid(st->st_gid)) != NULL)) 34064562Sgshapiro { 34164562Sgshapiro register char **gp; 34264562Sgshapiro 34364562Sgshapiro for (gp = gr->gr_mem; *gp != NULL; gp++) 34464562Sgshapiro if (strcmp(*gp, user) == 0) 34564562Sgshapiro break; 34664562Sgshapiro if (*gp == NULL) 34764562Sgshapiro mode >>= 3; 34864562Sgshapiro } 34964562Sgshapiro# endif /* ! NO_GROUP_SET */ 35064562Sgshapiro else 35164562Sgshapiro mode >>= 3; 35264562Sgshapiro } 35364562Sgshapiro if (tTd(44, 4)) 35490792Sgshapiro sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ", 35564562Sgshapiro (int) st->st_uid, (int) st->st_nlink, 35690792Sgshapiro (unsigned long) st->st_mode, (unsigned long) mode); 35764562Sgshapiro if ((st->st_uid == uid || st->st_uid == 0 || 35864562Sgshapiro st->st_uid == TrustedUid || 35964562Sgshapiro !bitset(SFF_MUSTOWN, flags)) && 36064562Sgshapiro (st->st_mode & mode) == mode) 36164562Sgshapiro { 36264562Sgshapiro if (tTd(44, 4)) 36390792Sgshapiro sm_dprintf("\tOK\n"); 36464562Sgshapiro return 0; 36564562Sgshapiro } 36664562Sgshapiro if (tTd(44, 4)) 36790792Sgshapiro sm_dprintf("\tEACCES\n"); 36864562Sgshapiro return EACCES; 36964562Sgshapiro} 37090792Sgshapiro/* 37164562Sgshapiro** SAFEDIRPATH -- check to make sure a path to a directory is safe 37264562Sgshapiro** 37364562Sgshapiro** Safe means not writable and owned by the right folks. 37464562Sgshapiro** 37564562Sgshapiro** Parameters: 37664562Sgshapiro** fn -- filename to check. 37764562Sgshapiro** uid -- user id to compare against. 37864562Sgshapiro** gid -- group id to compare against. 37964562Sgshapiro** user -- user name to compare against (used for group 38064562Sgshapiro** sets). 38164562Sgshapiro** flags -- modifiers: 38264562Sgshapiro** SFF_ROOTOK -- ok to use root permissions to open. 38364562Sgshapiro** SFF_SAFEDIRPATH -- writable directories are considered 38464562Sgshapiro** to be fatal errors. 38564562Sgshapiro** level -- symlink recursive level. 38664562Sgshapiro** offset -- offset into fn to start checking from. 38764562Sgshapiro** 38864562Sgshapiro** Returns: 38964562Sgshapiro** 0 -- if the directory path is "safe". 39064562Sgshapiro** else -- an error number associated with the path. 39164562Sgshapiro*/ 39264562Sgshapiro 39364562Sgshapiroint 39464562Sgshapirosafedirpath(fn, uid, gid, user, flags, level, offset) 39564562Sgshapiro char *fn; 39664562Sgshapiro UID_T uid; 39764562Sgshapiro GID_T gid; 39864562Sgshapiro char *user; 39964562Sgshapiro long flags; 40064562Sgshapiro int level; 40164562Sgshapiro int offset; 40264562Sgshapiro{ 40364562Sgshapiro int ret = 0; 40464562Sgshapiro int mode = S_IWOTH; 40564562Sgshapiro char save = '\0'; 40664562Sgshapiro char *saveptr = NULL; 40764562Sgshapiro char *p, *enddir; 40864562Sgshapiro register struct group *gr = NULL; 40998121Sgshapiro char s[MAXLINKPATHLEN]; 41064562Sgshapiro struct stat stbuf; 41164562Sgshapiro 41264562Sgshapiro /* make sure we aren't in a symlink loop */ 41364562Sgshapiro if (level > MAXSYMLINKS) 41464562Sgshapiro return ELOOP; 41564562Sgshapiro 41690792Sgshapiro if (level < 0 || offset < 0 || offset > strlen(fn)) 41790792Sgshapiro return EINVAL; 41890792Sgshapiro 41964562Sgshapiro /* special case root directory */ 42064562Sgshapiro if (*fn == '\0') 42164562Sgshapiro fn = "/"; 42264562Sgshapiro 42364562Sgshapiro if (tTd(44, 4)) 42490792Sgshapiro sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n", 42564562Sgshapiro fn, (long) uid, (long) gid, flags, level, offset); 42664562Sgshapiro 42764562Sgshapiro if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail)) 42864562Sgshapiro mode |= S_IWGRP; 42964562Sgshapiro 43064562Sgshapiro /* Make a modifiable copy of the filename */ 43190792Sgshapiro if (sm_strlcpy(s, fn, sizeof s) >= sizeof s) 43264562Sgshapiro return EINVAL; 43364562Sgshapiro 43464562Sgshapiro p = s + offset; 43564562Sgshapiro while (p != NULL) 43664562Sgshapiro { 43764562Sgshapiro /* put back character */ 43864562Sgshapiro if (saveptr != NULL) 43964562Sgshapiro { 44064562Sgshapiro *saveptr = save; 44164562Sgshapiro saveptr = NULL; 44264562Sgshapiro p++; 44364562Sgshapiro } 44464562Sgshapiro 44564562Sgshapiro if (*p == '\0') 44664562Sgshapiro break; 44764562Sgshapiro 44864562Sgshapiro p = strchr(p, '/'); 44964562Sgshapiro 45064562Sgshapiro /* Special case for root directory */ 45164562Sgshapiro if (p == s) 45264562Sgshapiro { 45364562Sgshapiro save = *(p + 1); 45464562Sgshapiro saveptr = p + 1; 45564562Sgshapiro *(p + 1) = '\0'; 45664562Sgshapiro } 45764562Sgshapiro else if (p != NULL) 45864562Sgshapiro { 45964562Sgshapiro save = *p; 46064562Sgshapiro saveptr = p; 46164562Sgshapiro *p = '\0'; 46264562Sgshapiro } 46364562Sgshapiro 46464562Sgshapiro /* Heuristic: . and .. have already been checked */ 46564562Sgshapiro enddir = strrchr(s, '/'); 46664562Sgshapiro if (enddir != NULL && 46764562Sgshapiro (strcmp(enddir, "/..") == 0 || 46864562Sgshapiro strcmp(enddir, "/.") == 0)) 46964562Sgshapiro continue; 47064562Sgshapiro 47164562Sgshapiro if (tTd(44, 20)) 47290792Sgshapiro sm_dprintf("\t[dir %s]\n", s); 47364562Sgshapiro 47464562Sgshapiro# if HASLSTAT 47564562Sgshapiro ret = lstat(s, &stbuf); 47664562Sgshapiro# else /* HASLSTAT */ 47764562Sgshapiro ret = stat(s, &stbuf); 47864562Sgshapiro# endif /* HASLSTAT */ 47964562Sgshapiro if (ret < 0) 48064562Sgshapiro { 48164562Sgshapiro ret = errno; 48264562Sgshapiro break; 48364562Sgshapiro } 48464562Sgshapiro 48564562Sgshapiro# ifdef S_ISLNK 48664562Sgshapiro /* Follow symlinks */ 48764562Sgshapiro if (S_ISLNK(stbuf.st_mode)) 48864562Sgshapiro { 48998121Sgshapiro int linklen; 49064562Sgshapiro char *target; 49198121Sgshapiro char buf[MAXPATHLEN]; 492141858Sgshapiro char fullbuf[MAXLINKPATHLEN]; 49364562Sgshapiro 49464562Sgshapiro memset(buf, '\0', sizeof buf); 49598121Sgshapiro linklen = readlink(s, buf, sizeof buf); 49698121Sgshapiro if (linklen < 0) 49764562Sgshapiro { 49864562Sgshapiro ret = errno; 49964562Sgshapiro break; 50064562Sgshapiro } 50198121Sgshapiro if (linklen >= sizeof buf) 50298121Sgshapiro { 50398121Sgshapiro /* file name too long for buffer */ 50498121Sgshapiro ret = errno = EINVAL; 50598121Sgshapiro break; 50698121Sgshapiro } 50764562Sgshapiro 50864562Sgshapiro offset = 0; 50964562Sgshapiro if (*buf == '/') 51064562Sgshapiro { 51164562Sgshapiro target = buf; 51264562Sgshapiro 51364562Sgshapiro /* If path is the same, avoid rechecks */ 51464562Sgshapiro while (s[offset] == buf[offset] && 51564562Sgshapiro s[offset] != '\0') 51664562Sgshapiro offset++; 51764562Sgshapiro 51864562Sgshapiro if (s[offset] == '\0' && buf[offset] == '\0') 51964562Sgshapiro { 52064562Sgshapiro /* strings match, symlink loop */ 52164562Sgshapiro return ELOOP; 52264562Sgshapiro } 52364562Sgshapiro 52464562Sgshapiro /* back off from the mismatch */ 52564562Sgshapiro if (offset > 0) 52664562Sgshapiro offset--; 52764562Sgshapiro 52864562Sgshapiro /* Make sure we are at a directory break */ 52964562Sgshapiro if (offset > 0 && 53064562Sgshapiro s[offset] != '/' && 53164562Sgshapiro s[offset] != '\0') 53264562Sgshapiro { 53364562Sgshapiro while (buf[offset] != '/' && 53464562Sgshapiro offset > 0) 53564562Sgshapiro offset--; 53664562Sgshapiro } 53764562Sgshapiro if (offset > 0 && 53864562Sgshapiro s[offset] == '/' && 53964562Sgshapiro buf[offset] == '/') 54064562Sgshapiro { 54164562Sgshapiro /* Include the trailing slash */ 54264562Sgshapiro offset++; 54364562Sgshapiro } 54464562Sgshapiro } 54564562Sgshapiro else 54664562Sgshapiro { 54764562Sgshapiro char *sptr; 54864562Sgshapiro 54964562Sgshapiro sptr = strrchr(s, '/'); 55064562Sgshapiro if (sptr != NULL) 55164562Sgshapiro { 55264562Sgshapiro *sptr = '\0'; 55364562Sgshapiro offset = sptr + 1 - s; 55490792Sgshapiro if (sm_strlcpyn(fullbuf, 55590792Sgshapiro sizeof fullbuf, 2, 55690792Sgshapiro s, "/") >= 55790792Sgshapiro sizeof fullbuf || 55890792Sgshapiro sm_strlcat(fullbuf, buf, 55990792Sgshapiro sizeof fullbuf) >= 56090792Sgshapiro sizeof fullbuf) 56164562Sgshapiro { 56264562Sgshapiro ret = EINVAL; 56364562Sgshapiro break; 56464562Sgshapiro } 56564562Sgshapiro *sptr = '/'; 56664562Sgshapiro } 56764562Sgshapiro else 56864562Sgshapiro { 56990792Sgshapiro if (sm_strlcpy(fullbuf, buf, 57090792Sgshapiro sizeof fullbuf) >= 57190792Sgshapiro sizeof fullbuf) 57264562Sgshapiro { 57364562Sgshapiro ret = EINVAL; 57464562Sgshapiro break; 57564562Sgshapiro } 57664562Sgshapiro } 57764562Sgshapiro target = fullbuf; 57864562Sgshapiro } 57964562Sgshapiro ret = safedirpath(target, uid, gid, user, flags, 58064562Sgshapiro level + 1, offset); 58164562Sgshapiro if (ret != 0) 58264562Sgshapiro break; 58364562Sgshapiro 58464562Sgshapiro /* Don't check permissions on the link file itself */ 58564562Sgshapiro continue; 58664562Sgshapiro } 58764562Sgshapiro#endif /* S_ISLNK */ 58864562Sgshapiro 58964562Sgshapiro if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) && 59064562Sgshapiro#ifdef S_ISVTX 59164562Sgshapiro !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) && 59264562Sgshapiro bitset(S_ISVTX, stbuf.st_mode)) && 59364562Sgshapiro#endif /* S_ISVTX */ 59464562Sgshapiro bitset(mode, stbuf.st_mode)) 59564562Sgshapiro { 59664562Sgshapiro if (tTd(44, 4)) 59790792Sgshapiro sm_dprintf("\t[dir %s] mode %lo ", 59890792Sgshapiro s, (unsigned long) stbuf.st_mode); 59964562Sgshapiro if (bitset(SFF_SAFEDIRPATH, flags)) 60064562Sgshapiro { 60164562Sgshapiro if (bitset(S_IWOTH, stbuf.st_mode)) 60264562Sgshapiro ret = E_SM_WWDIR; 60364562Sgshapiro else 60464562Sgshapiro ret = E_SM_GWDIR; 60564562Sgshapiro if (tTd(44, 4)) 60690792Sgshapiro sm_dprintf("FATAL\n"); 60764562Sgshapiro break; 60864562Sgshapiro } 60964562Sgshapiro if (tTd(44, 4)) 61090792Sgshapiro sm_dprintf("WARNING\n"); 61164562Sgshapiro if (Verbose > 1) 61264562Sgshapiro message("051 WARNING: %s writable directory %s", 61364562Sgshapiro bitset(S_IWOTH, stbuf.st_mode) 61464562Sgshapiro ? "World" 61564562Sgshapiro : "Group", 61664562Sgshapiro s); 61764562Sgshapiro } 61864562Sgshapiro if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)) 61964562Sgshapiro { 62064562Sgshapiro if (bitset(S_IXOTH, stbuf.st_mode)) 62164562Sgshapiro continue; 62264562Sgshapiro ret = EACCES; 62364562Sgshapiro break; 62464562Sgshapiro } 62564562Sgshapiro 62664562Sgshapiro /* 62764562Sgshapiro ** Let OS determine access to file if we are not 62864562Sgshapiro ** running as a privileged user. This allows ACLs 62964562Sgshapiro ** to work. Also, if opening as root, assume we can 63064562Sgshapiro ** scan the directory. 63164562Sgshapiro */ 63264562Sgshapiro if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags)) 63364562Sgshapiro continue; 63464562Sgshapiro 63564562Sgshapiro if (stbuf.st_uid == uid && 63664562Sgshapiro bitset(S_IXUSR, stbuf.st_mode)) 63764562Sgshapiro continue; 63864562Sgshapiro if (stbuf.st_gid == gid && 63964562Sgshapiro bitset(S_IXGRP, stbuf.st_mode)) 64064562Sgshapiro continue; 64164562Sgshapiro# ifndef NO_GROUP_SET 64264562Sgshapiro if (user != NULL && !DontInitGroups && 64364562Sgshapiro ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 64464562Sgshapiro (gr = getgrgid(stbuf.st_gid)) != NULL)) 64564562Sgshapiro { 64664562Sgshapiro register char **gp; 64764562Sgshapiro 64864562Sgshapiro for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) 64964562Sgshapiro if (strcmp(*gp, user) == 0) 65064562Sgshapiro break; 65164562Sgshapiro if (gp != NULL && *gp != NULL && 65264562Sgshapiro bitset(S_IXGRP, stbuf.st_mode)) 65364562Sgshapiro continue; 65464562Sgshapiro } 65564562Sgshapiro# endif /* ! NO_GROUP_SET */ 65664562Sgshapiro if (!bitset(S_IXOTH, stbuf.st_mode)) 65764562Sgshapiro { 65864562Sgshapiro ret = EACCES; 65964562Sgshapiro break; 66064562Sgshapiro } 66164562Sgshapiro } 66264562Sgshapiro if (tTd(44, 4)) 66390792Sgshapiro sm_dprintf("\t[dir %s] %s\n", fn, 66490792Sgshapiro ret == 0 ? "OK" : sm_errstring(ret)); 66564562Sgshapiro return ret; 66664562Sgshapiro} 66790792Sgshapiro/* 66864562Sgshapiro** SAFEOPEN -- do a file open with extra checking 66964562Sgshapiro** 67064562Sgshapiro** Parameters: 67164562Sgshapiro** fn -- the file name to open. 67264562Sgshapiro** omode -- the open-style mode flags. 67364562Sgshapiro** cmode -- the create-style mode flags. 67464562Sgshapiro** sff -- safefile flags. 67564562Sgshapiro** 67664562Sgshapiro** Returns: 67764562Sgshapiro** Same as open. 67864562Sgshapiro*/ 67964562Sgshapiro 68064562Sgshapiroint 68164562Sgshapirosafeopen(fn, omode, cmode, sff) 68264562Sgshapiro char *fn; 68364562Sgshapiro int omode; 68464562Sgshapiro int cmode; 68564562Sgshapiro long sff; 68664562Sgshapiro{ 687132943Sgshapiro#if !NOFTRUNCATE 688132943Sgshapiro bool truncate; 689132943Sgshapiro#endif /* !NOFTRUNCATE */ 69064562Sgshapiro int rval; 69164562Sgshapiro int fd; 69264562Sgshapiro int smode; 69364562Sgshapiro struct stat stb; 69464562Sgshapiro 69564562Sgshapiro if (tTd(44, 10)) 69690792Sgshapiro sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n", 69790792Sgshapiro fn, omode, cmode, sff); 69864562Sgshapiro 69964562Sgshapiro if (bitset(O_CREAT, omode)) 70064562Sgshapiro sff |= SFF_CREAT; 70164562Sgshapiro omode &= ~O_CREAT; 70264562Sgshapiro switch (omode & O_ACCMODE) 70364562Sgshapiro { 70464562Sgshapiro case O_RDONLY: 70564562Sgshapiro smode = S_IREAD; 70664562Sgshapiro break; 70764562Sgshapiro 70864562Sgshapiro case O_WRONLY: 70964562Sgshapiro smode = S_IWRITE; 71064562Sgshapiro break; 71164562Sgshapiro 71264562Sgshapiro case O_RDWR: 71364562Sgshapiro smode = S_IREAD|S_IWRITE; 71464562Sgshapiro break; 71564562Sgshapiro 71664562Sgshapiro default: 71764562Sgshapiro smode = 0; 71864562Sgshapiro break; 71964562Sgshapiro } 72064562Sgshapiro if (bitset(SFF_OPENASROOT, sff)) 72164562Sgshapiro rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName, 72264562Sgshapiro sff, smode, &stb); 72364562Sgshapiro else 72464562Sgshapiro rval = safefile(fn, RealUid, RealGid, RealUserName, 72564562Sgshapiro sff, smode, &stb); 72664562Sgshapiro if (rval != 0) 72764562Sgshapiro { 72864562Sgshapiro errno = rval; 72964562Sgshapiro return -1; 73064562Sgshapiro } 73164562Sgshapiro if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff)) 73264562Sgshapiro omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL); 73364562Sgshapiro else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode)) 73464562Sgshapiro { 73564562Sgshapiro /* The file exists so an exclusive create would fail */ 73664562Sgshapiro errno = EEXIST; 73764562Sgshapiro return -1; 73864562Sgshapiro } 73964562Sgshapiro 740132943Sgshapiro#if !NOFTRUNCATE 741132943Sgshapiro truncate = bitset(O_TRUNC, omode); 742132943Sgshapiro if (truncate) 743132943Sgshapiro omode &= ~O_TRUNC; 744132943Sgshapiro#endif /* !NOFTRUNCATE */ 745132943Sgshapiro 74664562Sgshapiro fd = dfopen(fn, omode, cmode, sff); 74764562Sgshapiro if (fd < 0) 74864562Sgshapiro return fd; 74964562Sgshapiro if (filechanged(fn, fd, &stb)) 75064562Sgshapiro { 75164562Sgshapiro syserr("554 5.3.0 cannot open: file %s changed after open", fn); 75264562Sgshapiro (void) close(fd); 75364562Sgshapiro errno = E_SM_FILECHANGE; 75464562Sgshapiro return -1; 75564562Sgshapiro } 756132943Sgshapiro 757132943Sgshapiro#if !NOFTRUNCATE 758132943Sgshapiro if (truncate && 759132943Sgshapiro ftruncate(fd, (off_t) 0) < 0) 760132943Sgshapiro { 761132943Sgshapiro int save_errno; 762132943Sgshapiro 763132943Sgshapiro save_errno = errno; 764132943Sgshapiro syserr("554 5.3.0 cannot open: file %s could not be truncated", 765132943Sgshapiro fn); 766132943Sgshapiro (void) close(fd); 767132943Sgshapiro errno = save_errno; 768132943Sgshapiro return -1; 769132943Sgshapiro } 770132943Sgshapiro#endif /* !NOFTRUNCATE */ 771132943Sgshapiro 77264562Sgshapiro return fd; 77364562Sgshapiro} 77490792Sgshapiro/* 77590792Sgshapiro** SAFEFOPEN -- do a file open with extra checking 77690792Sgshapiro** 77790792Sgshapiro** Parameters: 77890792Sgshapiro** fn -- the file name to open. 77990792Sgshapiro** omode -- the open-style mode flags. 78090792Sgshapiro** cmode -- the create-style mode flags. 78190792Sgshapiro** sff -- safefile flags. 78290792Sgshapiro** 78390792Sgshapiro** Returns: 78490792Sgshapiro** Same as fopen. 78590792Sgshapiro*/ 78690792Sgshapiro 78790792SgshapiroSM_FILE_T * 78890792Sgshapirosafefopen(fn, omode, cmode, sff) 78990792Sgshapiro char *fn; 79090792Sgshapiro int omode; 79190792Sgshapiro int cmode; 79290792Sgshapiro long sff; 79390792Sgshapiro{ 79490792Sgshapiro int fd; 79590792Sgshapiro int save_errno; 79690792Sgshapiro SM_FILE_T *fp; 79790792Sgshapiro int fmode; 79890792Sgshapiro 79990792Sgshapiro switch (omode & O_ACCMODE) 80090792Sgshapiro { 80190792Sgshapiro case O_RDONLY: 80290792Sgshapiro fmode = SM_IO_RDONLY; 80390792Sgshapiro break; 80490792Sgshapiro 80590792Sgshapiro case O_WRONLY: 80690792Sgshapiro if (bitset(O_APPEND, omode)) 80790792Sgshapiro fmode = SM_IO_APPEND; 80890792Sgshapiro else 80990792Sgshapiro fmode = SM_IO_WRONLY; 81090792Sgshapiro break; 81190792Sgshapiro 81290792Sgshapiro case O_RDWR: 81390792Sgshapiro if (bitset(O_TRUNC, omode)) 81490792Sgshapiro fmode = SM_IO_RDWRTR; 81590792Sgshapiro else if (bitset(O_APPEND, omode)) 81690792Sgshapiro fmode = SM_IO_APPENDRW; 81790792Sgshapiro else 81890792Sgshapiro fmode = SM_IO_RDWR; 81990792Sgshapiro break; 82090792Sgshapiro 82190792Sgshapiro default: 82290792Sgshapiro syserr("554 5.3.5 safefopen: unknown omode %o", omode); 82390792Sgshapiro fmode = 0; 82490792Sgshapiro } 82590792Sgshapiro fd = safeopen(fn, omode, cmode, sff); 82690792Sgshapiro if (fd < 0) 82790792Sgshapiro { 82890792Sgshapiro save_errno = errno; 82990792Sgshapiro if (tTd(44, 10)) 83090792Sgshapiro sm_dprintf("safefopen: safeopen failed: %s\n", 83190792Sgshapiro sm_errstring(errno)); 83290792Sgshapiro errno = save_errno; 83390792Sgshapiro return NULL; 83490792Sgshapiro } 83590792Sgshapiro fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 83690792Sgshapiro (void *) &fd, fmode, NULL); 83790792Sgshapiro if (fp != NULL) 83890792Sgshapiro return fp; 83990792Sgshapiro 84090792Sgshapiro save_errno = errno; 84190792Sgshapiro if (tTd(44, 10)) 84290792Sgshapiro { 84390792Sgshapiro sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n", 84490792Sgshapiro fn, fmode, omode, sff, sm_errstring(errno)); 84590792Sgshapiro } 84690792Sgshapiro (void) close(fd); 84790792Sgshapiro errno = save_errno; 84890792Sgshapiro return NULL; 84990792Sgshapiro} 85090792Sgshapiro/* 85164562Sgshapiro** FILECHANGED -- check to see if file changed after being opened 85264562Sgshapiro** 85364562Sgshapiro** Parameters: 85464562Sgshapiro** fn -- pathname of file to check. 85564562Sgshapiro** fd -- file descriptor to check. 85664562Sgshapiro** stb -- stat structure from before open. 85764562Sgshapiro** 85864562Sgshapiro** Returns: 85990792Sgshapiro** true -- if a problem was detected. 86090792Sgshapiro** false -- if this file is still the same. 86164562Sgshapiro*/ 86264562Sgshapiro 86364562Sgshapirobool 86464562Sgshapirofilechanged(fn, fd, stb) 86564562Sgshapiro char *fn; 86664562Sgshapiro int fd; 86764562Sgshapiro struct stat *stb; 86864562Sgshapiro{ 86964562Sgshapiro struct stat sta; 87064562Sgshapiro 87164562Sgshapiro if (stb->st_mode == ST_MODE_NOFILE) 87264562Sgshapiro { 87364562Sgshapiro# if HASLSTAT && BOGUS_O_EXCL 87464562Sgshapiro /* only necessary if exclusive open follows symbolic links */ 87564562Sgshapiro if (lstat(fn, stb) < 0 || stb->st_nlink != 1) 87690792Sgshapiro return true; 87764562Sgshapiro# else /* HASLSTAT && BOGUS_O_EXCL */ 87890792Sgshapiro return false; 87964562Sgshapiro# endif /* HASLSTAT && BOGUS_O_EXCL */ 88064562Sgshapiro } 88164562Sgshapiro if (fstat(fd, &sta) < 0) 88290792Sgshapiro return true; 88364562Sgshapiro 88464562Sgshapiro if (sta.st_nlink != stb->st_nlink || 88564562Sgshapiro sta.st_dev != stb->st_dev || 88664562Sgshapiro sta.st_ino != stb->st_ino || 88764562Sgshapiro# if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ 88864562Sgshapiro sta.st_gen != stb->st_gen || 88964562Sgshapiro# endif /* HAS_ST_GEN && 0 */ 89064562Sgshapiro sta.st_uid != stb->st_uid || 89164562Sgshapiro sta.st_gid != stb->st_gid) 89264562Sgshapiro { 89364562Sgshapiro if (tTd(44, 8)) 89464562Sgshapiro { 89590792Sgshapiro sm_dprintf("File changed after opening:\n"); 89690792Sgshapiro sm_dprintf(" nlink = %ld/%ld\n", 89764562Sgshapiro (long) stb->st_nlink, (long) sta.st_nlink); 89890792Sgshapiro sm_dprintf(" dev = %ld/%ld\n", 89964562Sgshapiro (long) stb->st_dev, (long) sta.st_dev); 90090792Sgshapiro sm_dprintf(" ino = %llu/%llu\n", 90190792Sgshapiro (ULONGLONG_T) stb->st_ino, 90290792Sgshapiro (ULONGLONG_T) sta.st_ino); 90364562Sgshapiro# if HAS_ST_GEN 90490792Sgshapiro sm_dprintf(" gen = %ld/%ld\n", 90564562Sgshapiro (long) stb->st_gen, (long) sta.st_gen); 90664562Sgshapiro# endif /* HAS_ST_GEN */ 90790792Sgshapiro sm_dprintf(" uid = %ld/%ld\n", 90864562Sgshapiro (long) stb->st_uid, (long) sta.st_uid); 90990792Sgshapiro sm_dprintf(" gid = %ld/%ld\n", 91064562Sgshapiro (long) stb->st_gid, (long) sta.st_gid); 91164562Sgshapiro } 91290792Sgshapiro return true; 91364562Sgshapiro } 91464562Sgshapiro 91590792Sgshapiro return false; 91664562Sgshapiro} 91790792Sgshapiro/* 91864562Sgshapiro** DFOPEN -- determined file open 91964562Sgshapiro** 92064562Sgshapiro** This routine has the semantics of open, except that it will 92164562Sgshapiro** keep trying a few times to make this happen. The idea is that 92264562Sgshapiro** on very loaded systems, we may run out of resources (inodes, 92364562Sgshapiro** whatever), so this tries to get around it. 92464562Sgshapiro*/ 92564562Sgshapiro 92664562Sgshapiroint 92764562Sgshapirodfopen(filename, omode, cmode, sff) 92864562Sgshapiro char *filename; 92964562Sgshapiro int omode; 93064562Sgshapiro int cmode; 93164562Sgshapiro long sff; 93264562Sgshapiro{ 93364562Sgshapiro register int tries; 93464562Sgshapiro int fd = -1; 93564562Sgshapiro struct stat st; 93664562Sgshapiro 93764562Sgshapiro for (tries = 0; tries < 10; tries++) 93864562Sgshapiro { 93964562Sgshapiro (void) sleep((unsigned) (10 * tries)); 94064562Sgshapiro errno = 0; 94164562Sgshapiro fd = open(filename, omode, cmode); 94264562Sgshapiro if (fd >= 0) 94364562Sgshapiro break; 94464562Sgshapiro switch (errno) 94564562Sgshapiro { 94664562Sgshapiro case ENFILE: /* system file table full */ 94764562Sgshapiro case EINTR: /* interrupted syscall */ 94864562Sgshapiro#ifdef ETXTBSY 94964562Sgshapiro case ETXTBSY: /* Apollo: net file locked */ 95064562Sgshapiro#endif /* ETXTBSY */ 95164562Sgshapiro continue; 95264562Sgshapiro } 95364562Sgshapiro break; 95464562Sgshapiro } 95564562Sgshapiro if (!bitset(SFF_NOLOCK, sff) && 95664562Sgshapiro fd >= 0 && 95764562Sgshapiro fstat(fd, &st) >= 0 && 95864562Sgshapiro S_ISREG(st.st_mode)) 95964562Sgshapiro { 96064562Sgshapiro int locktype; 96164562Sgshapiro 96264562Sgshapiro /* lock the file to avoid accidental conflicts */ 96364562Sgshapiro if ((omode & O_ACCMODE) != O_RDONLY) 96464562Sgshapiro locktype = LOCK_EX; 96564562Sgshapiro else 96664562Sgshapiro locktype = LOCK_SH; 967132943Sgshapiro if (bitset(SFF_NBLOCK, sff)) 968132943Sgshapiro locktype |= LOCK_NB; 969132943Sgshapiro 97064562Sgshapiro if (!lockfile(fd, filename, NULL, locktype)) 97164562Sgshapiro { 97264562Sgshapiro int save_errno = errno; 97364562Sgshapiro 97464562Sgshapiro (void) close(fd); 97564562Sgshapiro fd = -1; 97664562Sgshapiro errno = save_errno; 97764562Sgshapiro } 97864562Sgshapiro else 97964562Sgshapiro errno = 0; 98064562Sgshapiro } 98164562Sgshapiro return fd; 98264562Sgshapiro} 983