106f25ae9SGregory Neil Shapiro /* 2*5dd76dd0SGregory Neil Shapiro * Copyright (c) 1998-2004 Proofpoint, Inc. and its suppliers. 306f25ae9SGregory Neil Shapiro * All rights reserved. 406f25ae9SGregory Neil Shapiro * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 506f25ae9SGregory Neil Shapiro * Copyright (c) 1988, 1993 606f25ae9SGregory Neil Shapiro * The Regents of the University of California. All rights reserved. 706f25ae9SGregory Neil Shapiro * 806f25ae9SGregory Neil Shapiro * By using this file, you agree to the terms and conditions set 906f25ae9SGregory Neil Shapiro * forth in the LICENSE file which can be found at the top level of 1006f25ae9SGregory Neil Shapiro * the sendmail distribution. 1106f25ae9SGregory Neil Shapiro * 1206f25ae9SGregory Neil Shapiro */ 1306f25ae9SGregory Neil Shapiro 1406f25ae9SGregory Neil Shapiro #include <sendmail.h> 1540266059SGregory Neil Shapiro #include <sm/io.h> 1640266059SGregory Neil Shapiro #include <sm/errstring.h> 1740266059SGregory Neil Shapiro 18*5dd76dd0SGregory Neil Shapiro SM_RCSID("@(#)$Id: safefile.c,v 8.130 2013/11/22 20:51:50 ca Exp $") 1906f25ae9SGregory Neil Shapiro 2006f25ae9SGregory Neil Shapiro 2140266059SGregory Neil Shapiro /* 2206f25ae9SGregory Neil Shapiro ** SAFEFILE -- return 0 if a file exists and is safe for a user. 2306f25ae9SGregory Neil Shapiro ** 2406f25ae9SGregory Neil Shapiro ** Parameters: 2506f25ae9SGregory Neil Shapiro ** fn -- filename to check. 2606f25ae9SGregory Neil Shapiro ** uid -- user id to compare against. 2706f25ae9SGregory Neil Shapiro ** gid -- group id to compare against. 2806f25ae9SGregory Neil Shapiro ** user -- user name to compare against (used for group 2906f25ae9SGregory Neil Shapiro ** sets). 3006f25ae9SGregory Neil Shapiro ** flags -- modifiers: 3106f25ae9SGregory Neil Shapiro ** SFF_MUSTOWN -- "uid" must own this file. 3206f25ae9SGregory Neil Shapiro ** SFF_NOSLINK -- file cannot be a symbolic link. 3306f25ae9SGregory Neil Shapiro ** mode -- mode bits that must match. 3406f25ae9SGregory Neil Shapiro ** st -- if set, points to a stat structure that will 3506f25ae9SGregory Neil Shapiro ** get the stat info for the file. 3606f25ae9SGregory Neil Shapiro ** 3706f25ae9SGregory Neil Shapiro ** Returns: 3806f25ae9SGregory Neil Shapiro ** 0 if fn exists, is owned by uid, and matches mode. 3906f25ae9SGregory Neil Shapiro ** An errno otherwise. The actual errno is cleared. 4006f25ae9SGregory Neil Shapiro ** 4106f25ae9SGregory Neil Shapiro ** Side Effects: 4206f25ae9SGregory Neil Shapiro ** none. 4306f25ae9SGregory Neil Shapiro */ 4406f25ae9SGregory Neil Shapiro 4506f25ae9SGregory Neil Shapiro int 4606f25ae9SGregory Neil Shapiro safefile(fn, uid, gid, user, flags, mode, st) 4706f25ae9SGregory Neil Shapiro char *fn; 4806f25ae9SGregory Neil Shapiro UID_T uid; 4906f25ae9SGregory Neil Shapiro GID_T gid; 5006f25ae9SGregory Neil Shapiro char *user; 5106f25ae9SGregory Neil Shapiro long flags; 5206f25ae9SGregory Neil Shapiro int mode; 5306f25ae9SGregory Neil Shapiro struct stat *st; 5406f25ae9SGregory Neil Shapiro { 5506f25ae9SGregory Neil Shapiro register char *p; 5606f25ae9SGregory Neil Shapiro register struct group *gr = NULL; 5706f25ae9SGregory Neil Shapiro int file_errno = 0; 5806f25ae9SGregory Neil Shapiro bool checkpath; 5906f25ae9SGregory Neil Shapiro struct stat stbuf; 6006f25ae9SGregory Neil Shapiro struct stat fstbuf; 6194c01205SGregory Neil Shapiro char fbuf[MAXPATHLEN]; 6206f25ae9SGregory Neil Shapiro 6306f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 6440266059SGregory Neil Shapiro sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n", 6506f25ae9SGregory Neil Shapiro fn, (int) uid, (int) gid, flags, mode); 6606f25ae9SGregory Neil Shapiro errno = 0; 6740266059SGregory Neil Shapiro if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf) 6806f25ae9SGregory Neil Shapiro { 6906f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 7040266059SGregory Neil Shapiro sm_dprintf("\tpathname too long\n"); 7106f25ae9SGregory Neil Shapiro return ENAMETOOLONG; 7206f25ae9SGregory Neil Shapiro } 7306f25ae9SGregory Neil Shapiro fn = fbuf; 7440266059SGregory Neil Shapiro if (st == NULL) 7540266059SGregory Neil Shapiro st = &fstbuf; 7606f25ae9SGregory Neil Shapiro 7706f25ae9SGregory Neil Shapiro /* ignore SFF_SAFEDIRPATH if we are debugging */ 7806f25ae9SGregory Neil Shapiro if (RealUid != 0 && RunAsUid == RealUid) 7906f25ae9SGregory Neil Shapiro flags &= ~SFF_SAFEDIRPATH; 8006f25ae9SGregory Neil Shapiro 8106f25ae9SGregory Neil Shapiro /* first check to see if the file exists at all */ 8206f25ae9SGregory Neil Shapiro # if HASLSTAT 8306f25ae9SGregory Neil Shapiro if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) 8406f25ae9SGregory Neil Shapiro : stat(fn, st)) < 0) 8506f25ae9SGregory Neil Shapiro # else /* HASLSTAT */ 8606f25ae9SGregory Neil Shapiro if (stat(fn, st) < 0) 8706f25ae9SGregory Neil Shapiro # endif /* HASLSTAT */ 8806f25ae9SGregory Neil Shapiro { 8906f25ae9SGregory Neil Shapiro file_errno = errno; 9006f25ae9SGregory Neil Shapiro } 9106f25ae9SGregory Neil Shapiro else if (bitset(SFF_SETUIDOK, flags) && 9206f25ae9SGregory Neil Shapiro !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) && 9306f25ae9SGregory Neil Shapiro S_ISREG(st->st_mode)) 9406f25ae9SGregory Neil Shapiro { 9506f25ae9SGregory Neil Shapiro /* 9640266059SGregory Neil Shapiro ** If final file is set-user-ID, run as the owner of that 9706f25ae9SGregory Neil Shapiro ** file. Gotta be careful not to reveal anything too 9806f25ae9SGregory Neil Shapiro ** soon here! 9906f25ae9SGregory Neil Shapiro */ 10006f25ae9SGregory Neil Shapiro 10106f25ae9SGregory Neil Shapiro # ifdef SUID_ROOT_FILES_OK 10206f25ae9SGregory Neil Shapiro if (bitset(S_ISUID, st->st_mode)) 10306f25ae9SGregory Neil Shapiro # else /* SUID_ROOT_FILES_OK */ 10406f25ae9SGregory Neil Shapiro if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 && 10506f25ae9SGregory Neil Shapiro st->st_uid != TrustedUid) 10606f25ae9SGregory Neil Shapiro # endif /* SUID_ROOT_FILES_OK */ 10706f25ae9SGregory Neil Shapiro { 10806f25ae9SGregory Neil Shapiro uid = st->st_uid; 10906f25ae9SGregory Neil Shapiro user = NULL; 11006f25ae9SGregory Neil Shapiro } 11106f25ae9SGregory Neil Shapiro # ifdef SUID_ROOT_FILES_OK 11206f25ae9SGregory Neil Shapiro if (bitset(S_ISGID, st->st_mode)) 11306f25ae9SGregory Neil Shapiro # else /* SUID_ROOT_FILES_OK */ 11406f25ae9SGregory Neil Shapiro if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0) 11506f25ae9SGregory Neil Shapiro # endif /* SUID_ROOT_FILES_OK */ 11606f25ae9SGregory Neil Shapiro gid = st->st_gid; 11706f25ae9SGregory Neil Shapiro } 11806f25ae9SGregory Neil Shapiro 11906f25ae9SGregory Neil Shapiro checkpath = !bitset(SFF_NOPATHCHECK, flags) || 12006f25ae9SGregory Neil Shapiro (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)); 12106f25ae9SGregory Neil Shapiro if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags)) 12206f25ae9SGregory Neil Shapiro { 12306f25ae9SGregory Neil Shapiro int ret; 12406f25ae9SGregory Neil Shapiro 12506f25ae9SGregory Neil Shapiro /* check the directory */ 12606f25ae9SGregory Neil Shapiro p = strrchr(fn, '/'); 12706f25ae9SGregory Neil Shapiro if (p == NULL) 12806f25ae9SGregory Neil Shapiro { 12906f25ae9SGregory Neil Shapiro ret = safedirpath(".", uid, gid, user, 13006f25ae9SGregory Neil Shapiro flags|SFF_SAFEDIRPATH, 0, 0); 13106f25ae9SGregory Neil Shapiro } 13206f25ae9SGregory Neil Shapiro else 13306f25ae9SGregory Neil Shapiro { 13406f25ae9SGregory Neil Shapiro *p = '\0'; 13506f25ae9SGregory Neil Shapiro ret = safedirpath(fn, uid, gid, user, 13606f25ae9SGregory Neil Shapiro flags|SFF_SAFEDIRPATH, 0, 0); 13706f25ae9SGregory Neil Shapiro *p = '/'; 13806f25ae9SGregory Neil Shapiro } 13906f25ae9SGregory Neil Shapiro if (ret == 0) 14006f25ae9SGregory Neil Shapiro { 14106f25ae9SGregory Neil Shapiro /* directory is safe */ 14240266059SGregory Neil Shapiro checkpath = false; 14306f25ae9SGregory Neil Shapiro } 14406f25ae9SGregory Neil Shapiro else 14506f25ae9SGregory Neil Shapiro { 14606f25ae9SGregory Neil Shapiro # if HASLSTAT 14706f25ae9SGregory Neil Shapiro /* Need lstat() information if called stat() before */ 14806f25ae9SGregory Neil Shapiro if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0) 14906f25ae9SGregory Neil Shapiro { 15006f25ae9SGregory Neil Shapiro ret = errno; 15106f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 15240266059SGregory Neil Shapiro sm_dprintf("\t%s\n", sm_errstring(ret)); 15306f25ae9SGregory Neil Shapiro return ret; 15406f25ae9SGregory Neil Shapiro } 15506f25ae9SGregory Neil Shapiro # endif /* HASLSTAT */ 15606f25ae9SGregory Neil Shapiro /* directory is writable: disallow links */ 15706f25ae9SGregory Neil Shapiro flags |= SFF_NOLINK; 15806f25ae9SGregory Neil Shapiro } 15906f25ae9SGregory Neil Shapiro } 16006f25ae9SGregory Neil Shapiro 16106f25ae9SGregory Neil Shapiro if (checkpath) 16206f25ae9SGregory Neil Shapiro { 16306f25ae9SGregory Neil Shapiro int ret; 16406f25ae9SGregory Neil Shapiro 16506f25ae9SGregory Neil Shapiro p = strrchr(fn, '/'); 16606f25ae9SGregory Neil Shapiro if (p == NULL) 16706f25ae9SGregory Neil Shapiro { 16806f25ae9SGregory Neil Shapiro ret = safedirpath(".", uid, gid, user, flags, 0, 0); 16906f25ae9SGregory Neil Shapiro } 17006f25ae9SGregory Neil Shapiro else 17106f25ae9SGregory Neil Shapiro { 17206f25ae9SGregory Neil Shapiro *p = '\0'; 17306f25ae9SGregory Neil Shapiro ret = safedirpath(fn, uid, gid, user, flags, 0, 0); 17406f25ae9SGregory Neil Shapiro *p = '/'; 17506f25ae9SGregory Neil Shapiro } 17606f25ae9SGregory Neil Shapiro if (ret != 0) 17706f25ae9SGregory Neil Shapiro return ret; 17806f25ae9SGregory Neil Shapiro } 17906f25ae9SGregory Neil Shapiro 18006f25ae9SGregory Neil Shapiro /* 18106f25ae9SGregory Neil Shapiro ** If the target file doesn't exist, check the directory to 18206f25ae9SGregory Neil Shapiro ** ensure that it is writable by this user. 18306f25ae9SGregory Neil Shapiro */ 18406f25ae9SGregory Neil Shapiro 18506f25ae9SGregory Neil Shapiro if (file_errno != 0) 18606f25ae9SGregory Neil Shapiro { 18706f25ae9SGregory Neil Shapiro int ret = file_errno; 18806f25ae9SGregory Neil Shapiro char *dir = fn; 18906f25ae9SGregory Neil Shapiro 19006f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 19140266059SGregory Neil Shapiro sm_dprintf("\t%s\n", sm_errstring(ret)); 19206f25ae9SGregory Neil Shapiro 19306f25ae9SGregory Neil Shapiro errno = 0; 19406f25ae9SGregory Neil Shapiro if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT) 19506f25ae9SGregory Neil Shapiro return ret; 19606f25ae9SGregory Neil Shapiro 19706f25ae9SGregory Neil Shapiro /* check to see if legal to create the file */ 19806f25ae9SGregory Neil Shapiro p = strrchr(dir, '/'); 19906f25ae9SGregory Neil Shapiro if (p == NULL) 20006f25ae9SGregory Neil Shapiro dir = "."; 20106f25ae9SGregory Neil Shapiro else if (p == dir) 20206f25ae9SGregory Neil Shapiro dir = "/"; 20306f25ae9SGregory Neil Shapiro else 20406f25ae9SGregory Neil Shapiro *p = '\0'; 20506f25ae9SGregory Neil Shapiro if (stat(dir, &stbuf) >= 0) 20606f25ae9SGregory Neil Shapiro { 20706f25ae9SGregory Neil Shapiro int md = S_IWRITE|S_IEXEC; 20806f25ae9SGregory Neil Shapiro 20940266059SGregory Neil Shapiro ret = 0; 21006f25ae9SGregory Neil Shapiro if (stbuf.st_uid == uid) 21106f25ae9SGregory Neil Shapiro /* EMPTY */ 21206f25ae9SGregory Neil Shapiro ; 21306f25ae9SGregory Neil Shapiro else if (uid == 0 && stbuf.st_uid == TrustedUid) 21406f25ae9SGregory Neil Shapiro /* EMPTY */ 21506f25ae9SGregory Neil Shapiro ; 21606f25ae9SGregory Neil Shapiro else 21706f25ae9SGregory Neil Shapiro { 21806f25ae9SGregory Neil Shapiro md >>= 3; 21906f25ae9SGregory Neil Shapiro if (stbuf.st_gid == gid) 22006f25ae9SGregory Neil Shapiro /* EMPTY */ 22106f25ae9SGregory Neil Shapiro ; 22206f25ae9SGregory Neil Shapiro # ifndef NO_GROUP_SET 22306f25ae9SGregory Neil Shapiro else if (user != NULL && !DontInitGroups && 22406f25ae9SGregory Neil Shapiro ((gr != NULL && 22506f25ae9SGregory Neil Shapiro gr->gr_gid == stbuf.st_gid) || 22606f25ae9SGregory Neil Shapiro (gr = getgrgid(stbuf.st_gid)) != NULL)) 22706f25ae9SGregory Neil Shapiro { 22806f25ae9SGregory Neil Shapiro register char **gp; 22906f25ae9SGregory Neil Shapiro 23006f25ae9SGregory Neil Shapiro for (gp = gr->gr_mem; *gp != NULL; gp++) 23106f25ae9SGregory Neil Shapiro if (strcmp(*gp, user) == 0) 23206f25ae9SGregory Neil Shapiro break; 23306f25ae9SGregory Neil Shapiro if (*gp == NULL) 23406f25ae9SGregory Neil Shapiro md >>= 3; 23506f25ae9SGregory Neil Shapiro } 23606f25ae9SGregory Neil Shapiro # endif /* ! NO_GROUP_SET */ 23706f25ae9SGregory Neil Shapiro else 23806f25ae9SGregory Neil Shapiro md >>= 3; 23906f25ae9SGregory Neil Shapiro } 24006f25ae9SGregory Neil Shapiro if ((stbuf.st_mode & md) != md) 24140266059SGregory Neil Shapiro ret = errno = EACCES; 24206f25ae9SGregory Neil Shapiro } 24340266059SGregory Neil Shapiro else 24406f25ae9SGregory Neil Shapiro ret = errno; 24506f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 24640266059SGregory Neil Shapiro sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n", 24740266059SGregory Neil Shapiro dir, (int) stbuf.st_uid, 24840266059SGregory Neil Shapiro (unsigned long) stbuf.st_mode, 24940266059SGregory Neil Shapiro sm_errstring(ret)); 25006f25ae9SGregory Neil Shapiro if (p != NULL) 25106f25ae9SGregory Neil Shapiro *p = '/'; 25206f25ae9SGregory Neil Shapiro st->st_mode = ST_MODE_NOFILE; 25306f25ae9SGregory Neil Shapiro return ret; 25406f25ae9SGregory Neil Shapiro } 25506f25ae9SGregory Neil Shapiro 25606f25ae9SGregory Neil Shapiro # ifdef S_ISLNK 25706f25ae9SGregory Neil Shapiro if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) 25806f25ae9SGregory Neil Shapiro { 25906f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 26040266059SGregory Neil Shapiro sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n", 26140266059SGregory Neil Shapiro (unsigned long) st->st_mode); 26206f25ae9SGregory Neil Shapiro return E_SM_NOSLINK; 26306f25ae9SGregory Neil Shapiro } 26406f25ae9SGregory Neil Shapiro # endif /* S_ISLNK */ 26506f25ae9SGregory Neil Shapiro if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) 26606f25ae9SGregory Neil Shapiro { 26706f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 26840266059SGregory Neil Shapiro sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n", 26940266059SGregory Neil Shapiro (unsigned long) st->st_mode); 27006f25ae9SGregory Neil Shapiro return E_SM_REGONLY; 27106f25ae9SGregory Neil Shapiro } 27206f25ae9SGregory Neil Shapiro if (bitset(SFF_NOGWFILES, flags) && 27306f25ae9SGregory Neil Shapiro bitset(S_IWGRP, st->st_mode)) 27406f25ae9SGregory Neil Shapiro { 27506f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 27640266059SGregory Neil Shapiro sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n", 27740266059SGregory Neil Shapiro (unsigned long) st->st_mode); 27806f25ae9SGregory Neil Shapiro return E_SM_GWFILE; 27906f25ae9SGregory Neil Shapiro } 28006f25ae9SGregory Neil Shapiro if (bitset(SFF_NOWWFILES, flags) && 28106f25ae9SGregory Neil Shapiro bitset(S_IWOTH, st->st_mode)) 28206f25ae9SGregory Neil Shapiro { 28306f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 28440266059SGregory Neil Shapiro sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n", 28540266059SGregory Neil Shapiro (unsigned long) st->st_mode); 28606f25ae9SGregory Neil Shapiro return E_SM_WWFILE; 28706f25ae9SGregory Neil Shapiro } 28806f25ae9SGregory Neil Shapiro if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode)) 28906f25ae9SGregory Neil Shapiro { 29006f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 29140266059SGregory Neil Shapiro sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n", 29240266059SGregory Neil Shapiro (unsigned long) st->st_mode); 29306f25ae9SGregory Neil Shapiro return E_SM_GRFILE; 29406f25ae9SGregory Neil Shapiro } 29506f25ae9SGregory Neil Shapiro if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode)) 29606f25ae9SGregory Neil Shapiro { 29706f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 29840266059SGregory Neil Shapiro sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n", 29940266059SGregory Neil Shapiro (unsigned long) st->st_mode); 30006f25ae9SGregory Neil Shapiro return E_SM_WRFILE; 30106f25ae9SGregory Neil Shapiro } 30206f25ae9SGregory Neil Shapiro if (!bitset(SFF_EXECOK, flags) && 30306f25ae9SGregory Neil Shapiro bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && 30406f25ae9SGregory Neil Shapiro bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode)) 30506f25ae9SGregory Neil Shapiro { 30606f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 307e92d3f3fSGregory Neil Shapiro sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n", 30840266059SGregory Neil Shapiro (unsigned long) st->st_mode); 30906f25ae9SGregory Neil Shapiro return E_SM_ISEXEC; 31006f25ae9SGregory Neil Shapiro } 31106f25ae9SGregory Neil Shapiro if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1) 31206f25ae9SGregory Neil Shapiro { 31306f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 31440266059SGregory Neil Shapiro sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n", 31506f25ae9SGregory Neil Shapiro (int) st->st_nlink); 31606f25ae9SGregory Neil Shapiro return E_SM_NOHLINK; 31706f25ae9SGregory Neil Shapiro } 31806f25ae9SGregory Neil Shapiro 31906f25ae9SGregory Neil Shapiro if (uid == 0 && bitset(SFF_OPENASROOT, flags)) 32006f25ae9SGregory Neil Shapiro /* EMPTY */ 32106f25ae9SGregory Neil Shapiro ; 32206f25ae9SGregory Neil Shapiro else if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 32306f25ae9SGregory Neil Shapiro mode >>= 6; 32406f25ae9SGregory Neil Shapiro else if (st->st_uid == uid) 32506f25ae9SGregory Neil Shapiro /* EMPTY */ 32606f25ae9SGregory Neil Shapiro ; 32706f25ae9SGregory Neil Shapiro else if (uid == 0 && st->st_uid == TrustedUid) 32806f25ae9SGregory Neil Shapiro /* EMPTY */ 32906f25ae9SGregory Neil Shapiro ; 33006f25ae9SGregory Neil Shapiro else 33106f25ae9SGregory Neil Shapiro { 33206f25ae9SGregory Neil Shapiro mode >>= 3; 33306f25ae9SGregory Neil Shapiro if (st->st_gid == gid) 33406f25ae9SGregory Neil Shapiro /* EMPTY */ 33506f25ae9SGregory Neil Shapiro ; 33606f25ae9SGregory Neil Shapiro # ifndef NO_GROUP_SET 33706f25ae9SGregory Neil Shapiro else if (user != NULL && !DontInitGroups && 33806f25ae9SGregory Neil Shapiro ((gr != NULL && gr->gr_gid == st->st_gid) || 33906f25ae9SGregory Neil Shapiro (gr = getgrgid(st->st_gid)) != NULL)) 34006f25ae9SGregory Neil Shapiro { 34106f25ae9SGregory Neil Shapiro register char **gp; 34206f25ae9SGregory Neil Shapiro 34306f25ae9SGregory Neil Shapiro for (gp = gr->gr_mem; *gp != NULL; gp++) 34406f25ae9SGregory Neil Shapiro if (strcmp(*gp, user) == 0) 34506f25ae9SGregory Neil Shapiro break; 34606f25ae9SGregory Neil Shapiro if (*gp == NULL) 34706f25ae9SGregory Neil Shapiro mode >>= 3; 34806f25ae9SGregory Neil Shapiro } 34906f25ae9SGregory Neil Shapiro # endif /* ! NO_GROUP_SET */ 35006f25ae9SGregory Neil Shapiro else 35106f25ae9SGregory Neil Shapiro mode >>= 3; 35206f25ae9SGregory Neil Shapiro } 35306f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 35440266059SGregory Neil Shapiro sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ", 35506f25ae9SGregory Neil Shapiro (int) st->st_uid, (int) st->st_nlink, 35640266059SGregory Neil Shapiro (unsigned long) st->st_mode, (unsigned long) mode); 35706f25ae9SGregory Neil Shapiro if ((st->st_uid == uid || st->st_uid == 0 || 35806f25ae9SGregory Neil Shapiro st->st_uid == TrustedUid || 35906f25ae9SGregory Neil Shapiro !bitset(SFF_MUSTOWN, flags)) && 36006f25ae9SGregory Neil Shapiro (st->st_mode & mode) == mode) 36106f25ae9SGregory Neil Shapiro { 36206f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 36340266059SGregory Neil Shapiro sm_dprintf("\tOK\n"); 36406f25ae9SGregory Neil Shapiro return 0; 36506f25ae9SGregory Neil Shapiro } 36606f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 36740266059SGregory Neil Shapiro sm_dprintf("\tEACCES\n"); 36806f25ae9SGregory Neil Shapiro return EACCES; 36906f25ae9SGregory Neil Shapiro } 37040266059SGregory Neil Shapiro /* 37106f25ae9SGregory Neil Shapiro ** SAFEDIRPATH -- check to make sure a path to a directory is safe 37206f25ae9SGregory Neil Shapiro ** 37306f25ae9SGregory Neil Shapiro ** Safe means not writable and owned by the right folks. 37406f25ae9SGregory Neil Shapiro ** 37506f25ae9SGregory Neil Shapiro ** Parameters: 37606f25ae9SGregory Neil Shapiro ** fn -- filename to check. 37706f25ae9SGregory Neil Shapiro ** uid -- user id to compare against. 37806f25ae9SGregory Neil Shapiro ** gid -- group id to compare against. 37906f25ae9SGregory Neil Shapiro ** user -- user name to compare against (used for group 38006f25ae9SGregory Neil Shapiro ** sets). 38106f25ae9SGregory Neil Shapiro ** flags -- modifiers: 38206f25ae9SGregory Neil Shapiro ** SFF_ROOTOK -- ok to use root permissions to open. 38306f25ae9SGregory Neil Shapiro ** SFF_SAFEDIRPATH -- writable directories are considered 38406f25ae9SGregory Neil Shapiro ** to be fatal errors. 38506f25ae9SGregory Neil Shapiro ** level -- symlink recursive level. 38606f25ae9SGregory Neil Shapiro ** offset -- offset into fn to start checking from. 38706f25ae9SGregory Neil Shapiro ** 38806f25ae9SGregory Neil Shapiro ** Returns: 38906f25ae9SGregory Neil Shapiro ** 0 -- if the directory path is "safe". 39006f25ae9SGregory Neil Shapiro ** else -- an error number associated with the path. 39106f25ae9SGregory Neil Shapiro */ 39206f25ae9SGregory Neil Shapiro 39306f25ae9SGregory Neil Shapiro int 39406f25ae9SGregory Neil Shapiro safedirpath(fn, uid, gid, user, flags, level, offset) 39506f25ae9SGregory Neil Shapiro char *fn; 39606f25ae9SGregory Neil Shapiro UID_T uid; 39706f25ae9SGregory Neil Shapiro GID_T gid; 39806f25ae9SGregory Neil Shapiro char *user; 39906f25ae9SGregory Neil Shapiro long flags; 40006f25ae9SGregory Neil Shapiro int level; 40106f25ae9SGregory Neil Shapiro int offset; 40206f25ae9SGregory Neil Shapiro { 40306f25ae9SGregory Neil Shapiro int ret = 0; 40406f25ae9SGregory Neil Shapiro int mode = S_IWOTH; 40506f25ae9SGregory Neil Shapiro char save = '\0'; 40606f25ae9SGregory Neil Shapiro char *saveptr = NULL; 40706f25ae9SGregory Neil Shapiro char *p, *enddir; 40806f25ae9SGregory Neil Shapiro register struct group *gr = NULL; 40994c01205SGregory Neil Shapiro char s[MAXLINKPATHLEN]; 41006f25ae9SGregory Neil Shapiro struct stat stbuf; 41106f25ae9SGregory Neil Shapiro 41206f25ae9SGregory Neil Shapiro /* make sure we aren't in a symlink loop */ 41306f25ae9SGregory Neil Shapiro if (level > MAXSYMLINKS) 41406f25ae9SGregory Neil Shapiro return ELOOP; 41506f25ae9SGregory Neil Shapiro 41640266059SGregory Neil Shapiro if (level < 0 || offset < 0 || offset > strlen(fn)) 41740266059SGregory Neil Shapiro return EINVAL; 41840266059SGregory Neil Shapiro 41906f25ae9SGregory Neil Shapiro /* special case root directory */ 42006f25ae9SGregory Neil Shapiro if (*fn == '\0') 42106f25ae9SGregory Neil Shapiro fn = "/"; 42206f25ae9SGregory Neil Shapiro 42306f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 42440266059SGregory Neil Shapiro sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n", 42506f25ae9SGregory Neil Shapiro fn, (long) uid, (long) gid, flags, level, offset); 42606f25ae9SGregory Neil Shapiro 42706f25ae9SGregory Neil Shapiro if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail)) 42806f25ae9SGregory Neil Shapiro mode |= S_IWGRP; 42906f25ae9SGregory Neil Shapiro 43006f25ae9SGregory Neil Shapiro /* Make a modifiable copy of the filename */ 43140266059SGregory Neil Shapiro if (sm_strlcpy(s, fn, sizeof s) >= sizeof s) 43206f25ae9SGregory Neil Shapiro return EINVAL; 43306f25ae9SGregory Neil Shapiro 43406f25ae9SGregory Neil Shapiro p = s + offset; 43506f25ae9SGregory Neil Shapiro while (p != NULL) 43606f25ae9SGregory Neil Shapiro { 43706f25ae9SGregory Neil Shapiro /* put back character */ 43806f25ae9SGregory Neil Shapiro if (saveptr != NULL) 43906f25ae9SGregory Neil Shapiro { 44006f25ae9SGregory Neil Shapiro *saveptr = save; 44106f25ae9SGregory Neil Shapiro saveptr = NULL; 44206f25ae9SGregory Neil Shapiro p++; 44306f25ae9SGregory Neil Shapiro } 44406f25ae9SGregory Neil Shapiro 44506f25ae9SGregory Neil Shapiro if (*p == '\0') 44606f25ae9SGregory Neil Shapiro break; 44706f25ae9SGregory Neil Shapiro 44806f25ae9SGregory Neil Shapiro p = strchr(p, '/'); 44906f25ae9SGregory Neil Shapiro 45006f25ae9SGregory Neil Shapiro /* Special case for root directory */ 45106f25ae9SGregory Neil Shapiro if (p == s) 45206f25ae9SGregory Neil Shapiro { 45306f25ae9SGregory Neil Shapiro save = *(p + 1); 45406f25ae9SGregory Neil Shapiro saveptr = p + 1; 45506f25ae9SGregory Neil Shapiro *(p + 1) = '\0'; 45606f25ae9SGregory Neil Shapiro } 45706f25ae9SGregory Neil Shapiro else if (p != NULL) 45806f25ae9SGregory Neil Shapiro { 45906f25ae9SGregory Neil Shapiro save = *p; 46006f25ae9SGregory Neil Shapiro saveptr = p; 46106f25ae9SGregory Neil Shapiro *p = '\0'; 46206f25ae9SGregory Neil Shapiro } 46306f25ae9SGregory Neil Shapiro 46406f25ae9SGregory Neil Shapiro /* Heuristic: . and .. have already been checked */ 46506f25ae9SGregory Neil Shapiro enddir = strrchr(s, '/'); 46606f25ae9SGregory Neil Shapiro if (enddir != NULL && 46706f25ae9SGregory Neil Shapiro (strcmp(enddir, "/..") == 0 || 46806f25ae9SGregory Neil Shapiro strcmp(enddir, "/.") == 0)) 46906f25ae9SGregory Neil Shapiro continue; 47006f25ae9SGregory Neil Shapiro 47106f25ae9SGregory Neil Shapiro if (tTd(44, 20)) 47240266059SGregory Neil Shapiro sm_dprintf("\t[dir %s]\n", s); 47306f25ae9SGregory Neil Shapiro 47406f25ae9SGregory Neil Shapiro # if HASLSTAT 47506f25ae9SGregory Neil Shapiro ret = lstat(s, &stbuf); 47606f25ae9SGregory Neil Shapiro # else /* HASLSTAT */ 47706f25ae9SGregory Neil Shapiro ret = stat(s, &stbuf); 47806f25ae9SGregory Neil Shapiro # endif /* HASLSTAT */ 47906f25ae9SGregory Neil Shapiro if (ret < 0) 48006f25ae9SGregory Neil Shapiro { 48106f25ae9SGregory Neil Shapiro ret = errno; 48206f25ae9SGregory Neil Shapiro break; 48306f25ae9SGregory Neil Shapiro } 48406f25ae9SGregory Neil Shapiro 48506f25ae9SGregory Neil Shapiro # ifdef S_ISLNK 48606f25ae9SGregory Neil Shapiro /* Follow symlinks */ 48706f25ae9SGregory Neil Shapiro if (S_ISLNK(stbuf.st_mode)) 48806f25ae9SGregory Neil Shapiro { 48994c01205SGregory Neil Shapiro int linklen; 49006f25ae9SGregory Neil Shapiro char *target; 49194c01205SGregory Neil Shapiro char buf[MAXPATHLEN]; 492b6bacd31SGregory Neil Shapiro char fullbuf[MAXLINKPATHLEN]; 49306f25ae9SGregory Neil Shapiro 49406f25ae9SGregory Neil Shapiro memset(buf, '\0', sizeof buf); 49594c01205SGregory Neil Shapiro linklen = readlink(s, buf, sizeof buf); 49694c01205SGregory Neil Shapiro if (linklen < 0) 49706f25ae9SGregory Neil Shapiro { 49806f25ae9SGregory Neil Shapiro ret = errno; 49906f25ae9SGregory Neil Shapiro break; 50006f25ae9SGregory Neil Shapiro } 50194c01205SGregory Neil Shapiro if (linklen >= sizeof buf) 50294c01205SGregory Neil Shapiro { 50394c01205SGregory Neil Shapiro /* file name too long for buffer */ 50494c01205SGregory Neil Shapiro ret = errno = EINVAL; 50594c01205SGregory Neil Shapiro break; 50694c01205SGregory Neil Shapiro } 50706f25ae9SGregory Neil Shapiro 50806f25ae9SGregory Neil Shapiro offset = 0; 50906f25ae9SGregory Neil Shapiro if (*buf == '/') 51006f25ae9SGregory Neil Shapiro { 51106f25ae9SGregory Neil Shapiro target = buf; 51206f25ae9SGregory Neil Shapiro 51306f25ae9SGregory Neil Shapiro /* If path is the same, avoid rechecks */ 51406f25ae9SGregory Neil Shapiro while (s[offset] == buf[offset] && 51506f25ae9SGregory Neil Shapiro s[offset] != '\0') 51606f25ae9SGregory Neil Shapiro offset++; 51706f25ae9SGregory Neil Shapiro 51806f25ae9SGregory Neil Shapiro if (s[offset] == '\0' && buf[offset] == '\0') 51906f25ae9SGregory Neil Shapiro { 52006f25ae9SGregory Neil Shapiro /* strings match, symlink loop */ 52106f25ae9SGregory Neil Shapiro return ELOOP; 52206f25ae9SGregory Neil Shapiro } 52306f25ae9SGregory Neil Shapiro 52406f25ae9SGregory Neil Shapiro /* back off from the mismatch */ 52506f25ae9SGregory Neil Shapiro if (offset > 0) 52606f25ae9SGregory Neil Shapiro offset--; 52706f25ae9SGregory Neil Shapiro 52806f25ae9SGregory Neil Shapiro /* Make sure we are at a directory break */ 52906f25ae9SGregory Neil Shapiro if (offset > 0 && 53006f25ae9SGregory Neil Shapiro s[offset] != '/' && 53106f25ae9SGregory Neil Shapiro s[offset] != '\0') 53206f25ae9SGregory Neil Shapiro { 53306f25ae9SGregory Neil Shapiro while (buf[offset] != '/' && 53406f25ae9SGregory Neil Shapiro offset > 0) 53506f25ae9SGregory Neil Shapiro offset--; 53606f25ae9SGregory Neil Shapiro } 53706f25ae9SGregory Neil Shapiro if (offset > 0 && 53806f25ae9SGregory Neil Shapiro s[offset] == '/' && 53906f25ae9SGregory Neil Shapiro buf[offset] == '/') 54006f25ae9SGregory Neil Shapiro { 54106f25ae9SGregory Neil Shapiro /* Include the trailing slash */ 54206f25ae9SGregory Neil Shapiro offset++; 54306f25ae9SGregory Neil Shapiro } 54406f25ae9SGregory Neil Shapiro } 54506f25ae9SGregory Neil Shapiro else 54606f25ae9SGregory Neil Shapiro { 54706f25ae9SGregory Neil Shapiro char *sptr; 54806f25ae9SGregory Neil Shapiro 54906f25ae9SGregory Neil Shapiro sptr = strrchr(s, '/'); 55006f25ae9SGregory Neil Shapiro if (sptr != NULL) 55106f25ae9SGregory Neil Shapiro { 55206f25ae9SGregory Neil Shapiro *sptr = '\0'; 55306f25ae9SGregory Neil Shapiro offset = sptr + 1 - s; 55440266059SGregory Neil Shapiro if (sm_strlcpyn(fullbuf, 55540266059SGregory Neil Shapiro sizeof fullbuf, 2, 55640266059SGregory Neil Shapiro s, "/") >= 55740266059SGregory Neil Shapiro sizeof fullbuf || 55840266059SGregory Neil Shapiro sm_strlcat(fullbuf, buf, 55940266059SGregory Neil Shapiro sizeof fullbuf) >= 56040266059SGregory Neil Shapiro sizeof fullbuf) 56106f25ae9SGregory Neil Shapiro { 56206f25ae9SGregory Neil Shapiro ret = EINVAL; 56306f25ae9SGregory Neil Shapiro break; 56406f25ae9SGregory Neil Shapiro } 56506f25ae9SGregory Neil Shapiro *sptr = '/'; 56606f25ae9SGregory Neil Shapiro } 56706f25ae9SGregory Neil Shapiro else 56806f25ae9SGregory Neil Shapiro { 56940266059SGregory Neil Shapiro if (sm_strlcpy(fullbuf, buf, 57040266059SGregory Neil Shapiro sizeof fullbuf) >= 57140266059SGregory Neil Shapiro sizeof fullbuf) 57206f25ae9SGregory Neil Shapiro { 57306f25ae9SGregory Neil Shapiro ret = EINVAL; 57406f25ae9SGregory Neil Shapiro break; 57506f25ae9SGregory Neil Shapiro } 57606f25ae9SGregory Neil Shapiro } 57706f25ae9SGregory Neil Shapiro target = fullbuf; 57806f25ae9SGregory Neil Shapiro } 57906f25ae9SGregory Neil Shapiro ret = safedirpath(target, uid, gid, user, flags, 58006f25ae9SGregory Neil Shapiro level + 1, offset); 58106f25ae9SGregory Neil Shapiro if (ret != 0) 58206f25ae9SGregory Neil Shapiro break; 58306f25ae9SGregory Neil Shapiro 58406f25ae9SGregory Neil Shapiro /* Don't check permissions on the link file itself */ 58506f25ae9SGregory Neil Shapiro continue; 58606f25ae9SGregory Neil Shapiro } 58706f25ae9SGregory Neil Shapiro #endif /* S_ISLNK */ 58806f25ae9SGregory Neil Shapiro 58906f25ae9SGregory Neil Shapiro if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) && 59006f25ae9SGregory Neil Shapiro #ifdef S_ISVTX 59106f25ae9SGregory Neil Shapiro !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) && 59206f25ae9SGregory Neil Shapiro bitset(S_ISVTX, stbuf.st_mode)) && 59306f25ae9SGregory Neil Shapiro #endif /* S_ISVTX */ 59406f25ae9SGregory Neil Shapiro bitset(mode, stbuf.st_mode)) 59506f25ae9SGregory Neil Shapiro { 59606f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 59740266059SGregory Neil Shapiro sm_dprintf("\t[dir %s] mode %lo ", 59840266059SGregory Neil Shapiro s, (unsigned long) stbuf.st_mode); 59906f25ae9SGregory Neil Shapiro if (bitset(SFF_SAFEDIRPATH, flags)) 60006f25ae9SGregory Neil Shapiro { 60106f25ae9SGregory Neil Shapiro if (bitset(S_IWOTH, stbuf.st_mode)) 60206f25ae9SGregory Neil Shapiro ret = E_SM_WWDIR; 60306f25ae9SGregory Neil Shapiro else 60406f25ae9SGregory Neil Shapiro ret = E_SM_GWDIR; 60506f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 60640266059SGregory Neil Shapiro sm_dprintf("FATAL\n"); 60706f25ae9SGregory Neil Shapiro break; 60806f25ae9SGregory Neil Shapiro } 60906f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 61040266059SGregory Neil Shapiro sm_dprintf("WARNING\n"); 61106f25ae9SGregory Neil Shapiro if (Verbose > 1) 61206f25ae9SGregory Neil Shapiro message("051 WARNING: %s writable directory %s", 61306f25ae9SGregory Neil Shapiro bitset(S_IWOTH, stbuf.st_mode) 61406f25ae9SGregory Neil Shapiro ? "World" 61506f25ae9SGregory Neil Shapiro : "Group", 61606f25ae9SGregory Neil Shapiro s); 61706f25ae9SGregory Neil Shapiro } 61806f25ae9SGregory Neil Shapiro if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)) 61906f25ae9SGregory Neil Shapiro { 62006f25ae9SGregory Neil Shapiro if (bitset(S_IXOTH, stbuf.st_mode)) 62106f25ae9SGregory Neil Shapiro continue; 62206f25ae9SGregory Neil Shapiro ret = EACCES; 62306f25ae9SGregory Neil Shapiro break; 62406f25ae9SGregory Neil Shapiro } 62506f25ae9SGregory Neil Shapiro 62606f25ae9SGregory Neil Shapiro /* 62706f25ae9SGregory Neil Shapiro ** Let OS determine access to file if we are not 62806f25ae9SGregory Neil Shapiro ** running as a privileged user. This allows ACLs 62906f25ae9SGregory Neil Shapiro ** to work. Also, if opening as root, assume we can 63006f25ae9SGregory Neil Shapiro ** scan the directory. 63106f25ae9SGregory Neil Shapiro */ 63206f25ae9SGregory Neil Shapiro if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags)) 63306f25ae9SGregory Neil Shapiro continue; 63406f25ae9SGregory Neil Shapiro 63506f25ae9SGregory Neil Shapiro if (stbuf.st_uid == uid && 63606f25ae9SGregory Neil Shapiro bitset(S_IXUSR, stbuf.st_mode)) 63706f25ae9SGregory Neil Shapiro continue; 63806f25ae9SGregory Neil Shapiro if (stbuf.st_gid == gid && 63906f25ae9SGregory Neil Shapiro bitset(S_IXGRP, stbuf.st_mode)) 64006f25ae9SGregory Neil Shapiro continue; 64106f25ae9SGregory Neil Shapiro # ifndef NO_GROUP_SET 64206f25ae9SGregory Neil Shapiro if (user != NULL && !DontInitGroups && 64306f25ae9SGregory Neil Shapiro ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 64406f25ae9SGregory Neil Shapiro (gr = getgrgid(stbuf.st_gid)) != NULL)) 64506f25ae9SGregory Neil Shapiro { 64606f25ae9SGregory Neil Shapiro register char **gp; 64706f25ae9SGregory Neil Shapiro 64806f25ae9SGregory Neil Shapiro for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) 64906f25ae9SGregory Neil Shapiro if (strcmp(*gp, user) == 0) 65006f25ae9SGregory Neil Shapiro break; 65106f25ae9SGregory Neil Shapiro if (gp != NULL && *gp != NULL && 65206f25ae9SGregory Neil Shapiro bitset(S_IXGRP, stbuf.st_mode)) 65306f25ae9SGregory Neil Shapiro continue; 65406f25ae9SGregory Neil Shapiro } 65506f25ae9SGregory Neil Shapiro # endif /* ! NO_GROUP_SET */ 65606f25ae9SGregory Neil Shapiro if (!bitset(S_IXOTH, stbuf.st_mode)) 65706f25ae9SGregory Neil Shapiro { 65806f25ae9SGregory Neil Shapiro ret = EACCES; 65906f25ae9SGregory Neil Shapiro break; 66006f25ae9SGregory Neil Shapiro } 66106f25ae9SGregory Neil Shapiro } 66206f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 66340266059SGregory Neil Shapiro sm_dprintf("\t[dir %s] %s\n", fn, 66440266059SGregory Neil Shapiro ret == 0 ? "OK" : sm_errstring(ret)); 66506f25ae9SGregory Neil Shapiro return ret; 66606f25ae9SGregory Neil Shapiro } 66740266059SGregory Neil Shapiro /* 66806f25ae9SGregory Neil Shapiro ** SAFEOPEN -- do a file open with extra checking 66906f25ae9SGregory Neil Shapiro ** 67006f25ae9SGregory Neil Shapiro ** Parameters: 67106f25ae9SGregory Neil Shapiro ** fn -- the file name to open. 67206f25ae9SGregory Neil Shapiro ** omode -- the open-style mode flags. 67306f25ae9SGregory Neil Shapiro ** cmode -- the create-style mode flags. 67406f25ae9SGregory Neil Shapiro ** sff -- safefile flags. 67506f25ae9SGregory Neil Shapiro ** 67606f25ae9SGregory Neil Shapiro ** Returns: 67706f25ae9SGregory Neil Shapiro ** Same as open. 67806f25ae9SGregory Neil Shapiro */ 67906f25ae9SGregory Neil Shapiro 68006f25ae9SGregory Neil Shapiro int 68106f25ae9SGregory Neil Shapiro safeopen(fn, omode, cmode, sff) 68206f25ae9SGregory Neil Shapiro char *fn; 68306f25ae9SGregory Neil Shapiro int omode; 68406f25ae9SGregory Neil Shapiro int cmode; 68506f25ae9SGregory Neil Shapiro long sff; 68606f25ae9SGregory Neil Shapiro { 687e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE 688e92d3f3fSGregory Neil Shapiro bool truncate; 689e92d3f3fSGregory Neil Shapiro #endif /* !NOFTRUNCATE */ 69006f25ae9SGregory Neil Shapiro int rval; 69106f25ae9SGregory Neil Shapiro int fd; 69206f25ae9SGregory Neil Shapiro int smode; 69306f25ae9SGregory Neil Shapiro struct stat stb; 69406f25ae9SGregory Neil Shapiro 69506f25ae9SGregory Neil Shapiro if (tTd(44, 10)) 69640266059SGregory Neil Shapiro sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n", 69706f25ae9SGregory Neil Shapiro fn, omode, cmode, sff); 69806f25ae9SGregory Neil Shapiro 69906f25ae9SGregory Neil Shapiro if (bitset(O_CREAT, omode)) 70006f25ae9SGregory Neil Shapiro sff |= SFF_CREAT; 70106f25ae9SGregory Neil Shapiro omode &= ~O_CREAT; 70206f25ae9SGregory Neil Shapiro switch (omode & O_ACCMODE) 70306f25ae9SGregory Neil Shapiro { 70406f25ae9SGregory Neil Shapiro case O_RDONLY: 70506f25ae9SGregory Neil Shapiro smode = S_IREAD; 70606f25ae9SGregory Neil Shapiro break; 70706f25ae9SGregory Neil Shapiro 70806f25ae9SGregory Neil Shapiro case O_WRONLY: 70906f25ae9SGregory Neil Shapiro smode = S_IWRITE; 71006f25ae9SGregory Neil Shapiro break; 71106f25ae9SGregory Neil Shapiro 71206f25ae9SGregory Neil Shapiro case O_RDWR: 71306f25ae9SGregory Neil Shapiro smode = S_IREAD|S_IWRITE; 71406f25ae9SGregory Neil Shapiro break; 71506f25ae9SGregory Neil Shapiro 71606f25ae9SGregory Neil Shapiro default: 71706f25ae9SGregory Neil Shapiro smode = 0; 71806f25ae9SGregory Neil Shapiro break; 71906f25ae9SGregory Neil Shapiro } 72006f25ae9SGregory Neil Shapiro if (bitset(SFF_OPENASROOT, sff)) 72106f25ae9SGregory Neil Shapiro rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName, 72206f25ae9SGregory Neil Shapiro sff, smode, &stb); 72306f25ae9SGregory Neil Shapiro else 72406f25ae9SGregory Neil Shapiro rval = safefile(fn, RealUid, RealGid, RealUserName, 72506f25ae9SGregory Neil Shapiro sff, smode, &stb); 72606f25ae9SGregory Neil Shapiro if (rval != 0) 72706f25ae9SGregory Neil Shapiro { 72806f25ae9SGregory Neil Shapiro errno = rval; 72906f25ae9SGregory Neil Shapiro return -1; 73006f25ae9SGregory Neil Shapiro } 73106f25ae9SGregory Neil Shapiro if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff)) 73206f25ae9SGregory Neil Shapiro omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL); 73306f25ae9SGregory Neil Shapiro else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode)) 73406f25ae9SGregory Neil Shapiro { 73506f25ae9SGregory Neil Shapiro /* The file exists so an exclusive create would fail */ 73606f25ae9SGregory Neil Shapiro errno = EEXIST; 73706f25ae9SGregory Neil Shapiro return -1; 73806f25ae9SGregory Neil Shapiro } 73906f25ae9SGregory Neil Shapiro 740e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE 741e92d3f3fSGregory Neil Shapiro truncate = bitset(O_TRUNC, omode); 742e92d3f3fSGregory Neil Shapiro if (truncate) 743e92d3f3fSGregory Neil Shapiro omode &= ~O_TRUNC; 744e92d3f3fSGregory Neil Shapiro #endif /* !NOFTRUNCATE */ 745e92d3f3fSGregory Neil Shapiro 74606f25ae9SGregory Neil Shapiro fd = dfopen(fn, omode, cmode, sff); 74706f25ae9SGregory Neil Shapiro if (fd < 0) 74806f25ae9SGregory Neil Shapiro return fd; 74906f25ae9SGregory Neil Shapiro if (filechanged(fn, fd, &stb)) 75006f25ae9SGregory Neil Shapiro { 75106f25ae9SGregory Neil Shapiro syserr("554 5.3.0 cannot open: file %s changed after open", fn); 75206f25ae9SGregory Neil Shapiro (void) close(fd); 75306f25ae9SGregory Neil Shapiro errno = E_SM_FILECHANGE; 75406f25ae9SGregory Neil Shapiro return -1; 75506f25ae9SGregory Neil Shapiro } 756e92d3f3fSGregory Neil Shapiro 757e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE 758e92d3f3fSGregory Neil Shapiro if (truncate && 759e92d3f3fSGregory Neil Shapiro ftruncate(fd, (off_t) 0) < 0) 760e92d3f3fSGregory Neil Shapiro { 761e92d3f3fSGregory Neil Shapiro int save_errno; 762e92d3f3fSGregory Neil Shapiro 763e92d3f3fSGregory Neil Shapiro save_errno = errno; 764e92d3f3fSGregory Neil Shapiro syserr("554 5.3.0 cannot open: file %s could not be truncated", 765e92d3f3fSGregory Neil Shapiro fn); 766e92d3f3fSGregory Neil Shapiro (void) close(fd); 767e92d3f3fSGregory Neil Shapiro errno = save_errno; 768e92d3f3fSGregory Neil Shapiro return -1; 769e92d3f3fSGregory Neil Shapiro } 770e92d3f3fSGregory Neil Shapiro #endif /* !NOFTRUNCATE */ 771e92d3f3fSGregory Neil Shapiro 77206f25ae9SGregory Neil Shapiro return fd; 77306f25ae9SGregory Neil Shapiro } 77440266059SGregory Neil Shapiro /* 77540266059SGregory Neil Shapiro ** SAFEFOPEN -- do a file open with extra checking 77640266059SGregory Neil Shapiro ** 77740266059SGregory Neil Shapiro ** Parameters: 77840266059SGregory Neil Shapiro ** fn -- the file name to open. 77940266059SGregory Neil Shapiro ** omode -- the open-style mode flags. 78040266059SGregory Neil Shapiro ** cmode -- the create-style mode flags. 78140266059SGregory Neil Shapiro ** sff -- safefile flags. 78240266059SGregory Neil Shapiro ** 78340266059SGregory Neil Shapiro ** Returns: 78440266059SGregory Neil Shapiro ** Same as fopen. 78540266059SGregory Neil Shapiro */ 78640266059SGregory Neil Shapiro 78740266059SGregory Neil Shapiro SM_FILE_T * 78840266059SGregory Neil Shapiro safefopen(fn, omode, cmode, sff) 78940266059SGregory Neil Shapiro char *fn; 79040266059SGregory Neil Shapiro int omode; 79140266059SGregory Neil Shapiro int cmode; 79240266059SGregory Neil Shapiro long sff; 79340266059SGregory Neil Shapiro { 79440266059SGregory Neil Shapiro int fd; 79540266059SGregory Neil Shapiro int save_errno; 79640266059SGregory Neil Shapiro SM_FILE_T *fp; 79740266059SGregory Neil Shapiro int fmode; 79840266059SGregory Neil Shapiro 79940266059SGregory Neil Shapiro switch (omode & O_ACCMODE) 80040266059SGregory Neil Shapiro { 80140266059SGregory Neil Shapiro case O_RDONLY: 80240266059SGregory Neil Shapiro fmode = SM_IO_RDONLY; 80340266059SGregory Neil Shapiro break; 80440266059SGregory Neil Shapiro 80540266059SGregory Neil Shapiro case O_WRONLY: 80640266059SGregory Neil Shapiro if (bitset(O_APPEND, omode)) 80740266059SGregory Neil Shapiro fmode = SM_IO_APPEND; 80840266059SGregory Neil Shapiro else 80940266059SGregory Neil Shapiro fmode = SM_IO_WRONLY; 81040266059SGregory Neil Shapiro break; 81140266059SGregory Neil Shapiro 81240266059SGregory Neil Shapiro case O_RDWR: 81340266059SGregory Neil Shapiro if (bitset(O_TRUNC, omode)) 81440266059SGregory Neil Shapiro fmode = SM_IO_RDWRTR; 81540266059SGregory Neil Shapiro else if (bitset(O_APPEND, omode)) 81640266059SGregory Neil Shapiro fmode = SM_IO_APPENDRW; 81740266059SGregory Neil Shapiro else 81840266059SGregory Neil Shapiro fmode = SM_IO_RDWR; 81940266059SGregory Neil Shapiro break; 82040266059SGregory Neil Shapiro 82140266059SGregory Neil Shapiro default: 82240266059SGregory Neil Shapiro syserr("554 5.3.5 safefopen: unknown omode %o", omode); 82340266059SGregory Neil Shapiro fmode = 0; 82440266059SGregory Neil Shapiro } 82540266059SGregory Neil Shapiro fd = safeopen(fn, omode, cmode, sff); 82640266059SGregory Neil Shapiro if (fd < 0) 82740266059SGregory Neil Shapiro { 82840266059SGregory Neil Shapiro save_errno = errno; 82940266059SGregory Neil Shapiro if (tTd(44, 10)) 83040266059SGregory Neil Shapiro sm_dprintf("safefopen: safeopen failed: %s\n", 83140266059SGregory Neil Shapiro sm_errstring(errno)); 83240266059SGregory Neil Shapiro errno = save_errno; 83340266059SGregory Neil Shapiro return NULL; 83440266059SGregory Neil Shapiro } 83540266059SGregory Neil Shapiro fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 83640266059SGregory Neil Shapiro (void *) &fd, fmode, NULL); 83740266059SGregory Neil Shapiro if (fp != NULL) 83840266059SGregory Neil Shapiro return fp; 83940266059SGregory Neil Shapiro 84040266059SGregory Neil Shapiro save_errno = errno; 84140266059SGregory Neil Shapiro if (tTd(44, 10)) 84240266059SGregory Neil Shapiro { 84340266059SGregory Neil Shapiro sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n", 84440266059SGregory Neil Shapiro fn, fmode, omode, sff, sm_errstring(errno)); 84540266059SGregory Neil Shapiro } 84640266059SGregory Neil Shapiro (void) close(fd); 84740266059SGregory Neil Shapiro errno = save_errno; 84840266059SGregory Neil Shapiro return NULL; 84940266059SGregory Neil Shapiro } 85040266059SGregory Neil Shapiro /* 85106f25ae9SGregory Neil Shapiro ** FILECHANGED -- check to see if file changed after being opened 85206f25ae9SGregory Neil Shapiro ** 85306f25ae9SGregory Neil Shapiro ** Parameters: 85406f25ae9SGregory Neil Shapiro ** fn -- pathname of file to check. 85506f25ae9SGregory Neil Shapiro ** fd -- file descriptor to check. 85606f25ae9SGregory Neil Shapiro ** stb -- stat structure from before open. 85706f25ae9SGregory Neil Shapiro ** 85806f25ae9SGregory Neil Shapiro ** Returns: 85940266059SGregory Neil Shapiro ** true -- if a problem was detected. 86040266059SGregory Neil Shapiro ** false -- if this file is still the same. 86106f25ae9SGregory Neil Shapiro */ 86206f25ae9SGregory Neil Shapiro 86306f25ae9SGregory Neil Shapiro bool 86406f25ae9SGregory Neil Shapiro filechanged(fn, fd, stb) 86506f25ae9SGregory Neil Shapiro char *fn; 86606f25ae9SGregory Neil Shapiro int fd; 86706f25ae9SGregory Neil Shapiro struct stat *stb; 86806f25ae9SGregory Neil Shapiro { 86906f25ae9SGregory Neil Shapiro struct stat sta; 87006f25ae9SGregory Neil Shapiro 87106f25ae9SGregory Neil Shapiro if (stb->st_mode == ST_MODE_NOFILE) 87206f25ae9SGregory Neil Shapiro { 87306f25ae9SGregory Neil Shapiro # if HASLSTAT && BOGUS_O_EXCL 87406f25ae9SGregory Neil Shapiro /* only necessary if exclusive open follows symbolic links */ 87506f25ae9SGregory Neil Shapiro if (lstat(fn, stb) < 0 || stb->st_nlink != 1) 87640266059SGregory Neil Shapiro return true; 87706f25ae9SGregory Neil Shapiro # else /* HASLSTAT && BOGUS_O_EXCL */ 87840266059SGregory Neil Shapiro return false; 87906f25ae9SGregory Neil Shapiro # endif /* HASLSTAT && BOGUS_O_EXCL */ 88006f25ae9SGregory Neil Shapiro } 88106f25ae9SGregory Neil Shapiro if (fstat(fd, &sta) < 0) 88240266059SGregory Neil Shapiro return true; 88306f25ae9SGregory Neil Shapiro 88406f25ae9SGregory Neil Shapiro if (sta.st_nlink != stb->st_nlink || 88506f25ae9SGregory Neil Shapiro sta.st_dev != stb->st_dev || 88606f25ae9SGregory Neil Shapiro sta.st_ino != stb->st_ino || 88706f25ae9SGregory Neil Shapiro # if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ 88806f25ae9SGregory Neil Shapiro sta.st_gen != stb->st_gen || 88906f25ae9SGregory Neil Shapiro # endif /* HAS_ST_GEN && 0 */ 89006f25ae9SGregory Neil Shapiro sta.st_uid != stb->st_uid || 89106f25ae9SGregory Neil Shapiro sta.st_gid != stb->st_gid) 89206f25ae9SGregory Neil Shapiro { 89306f25ae9SGregory Neil Shapiro if (tTd(44, 8)) 89406f25ae9SGregory Neil Shapiro { 89540266059SGregory Neil Shapiro sm_dprintf("File changed after opening:\n"); 89640266059SGregory Neil Shapiro sm_dprintf(" nlink = %ld/%ld\n", 89706f25ae9SGregory Neil Shapiro (long) stb->st_nlink, (long) sta.st_nlink); 89840266059SGregory Neil Shapiro sm_dprintf(" dev = %ld/%ld\n", 89906f25ae9SGregory Neil Shapiro (long) stb->st_dev, (long) sta.st_dev); 90040266059SGregory Neil Shapiro sm_dprintf(" ino = %llu/%llu\n", 90140266059SGregory Neil Shapiro (ULONGLONG_T) stb->st_ino, 90240266059SGregory Neil Shapiro (ULONGLONG_T) sta.st_ino); 90306f25ae9SGregory Neil Shapiro # if HAS_ST_GEN 90440266059SGregory Neil Shapiro sm_dprintf(" gen = %ld/%ld\n", 90506f25ae9SGregory Neil Shapiro (long) stb->st_gen, (long) sta.st_gen); 90606f25ae9SGregory Neil Shapiro # endif /* HAS_ST_GEN */ 90740266059SGregory Neil Shapiro sm_dprintf(" uid = %ld/%ld\n", 90806f25ae9SGregory Neil Shapiro (long) stb->st_uid, (long) sta.st_uid); 90940266059SGregory Neil Shapiro sm_dprintf(" gid = %ld/%ld\n", 91006f25ae9SGregory Neil Shapiro (long) stb->st_gid, (long) sta.st_gid); 91106f25ae9SGregory Neil Shapiro } 91240266059SGregory Neil Shapiro return true; 91306f25ae9SGregory Neil Shapiro } 91406f25ae9SGregory Neil Shapiro 91540266059SGregory Neil Shapiro return false; 91606f25ae9SGregory Neil Shapiro } 91740266059SGregory Neil Shapiro /* 91806f25ae9SGregory Neil Shapiro ** DFOPEN -- determined file open 91906f25ae9SGregory Neil Shapiro ** 92006f25ae9SGregory Neil Shapiro ** This routine has the semantics of open, except that it will 92106f25ae9SGregory Neil Shapiro ** keep trying a few times to make this happen. The idea is that 92206f25ae9SGregory Neil Shapiro ** on very loaded systems, we may run out of resources (inodes, 92306f25ae9SGregory Neil Shapiro ** whatever), so this tries to get around it. 92406f25ae9SGregory Neil Shapiro */ 92506f25ae9SGregory Neil Shapiro 92606f25ae9SGregory Neil Shapiro int 92706f25ae9SGregory Neil Shapiro dfopen(filename, omode, cmode, sff) 92806f25ae9SGregory Neil Shapiro char *filename; 92906f25ae9SGregory Neil Shapiro int omode; 93006f25ae9SGregory Neil Shapiro int cmode; 93106f25ae9SGregory Neil Shapiro long sff; 93206f25ae9SGregory Neil Shapiro { 93306f25ae9SGregory Neil Shapiro register int tries; 93406f25ae9SGregory Neil Shapiro int fd = -1; 93506f25ae9SGregory Neil Shapiro struct stat st; 93606f25ae9SGregory Neil Shapiro 93706f25ae9SGregory Neil Shapiro for (tries = 0; tries < 10; tries++) 93806f25ae9SGregory Neil Shapiro { 93906f25ae9SGregory Neil Shapiro (void) sleep((unsigned) (10 * tries)); 94006f25ae9SGregory Neil Shapiro errno = 0; 94106f25ae9SGregory Neil Shapiro fd = open(filename, omode, cmode); 94206f25ae9SGregory Neil Shapiro if (fd >= 0) 94306f25ae9SGregory Neil Shapiro break; 94406f25ae9SGregory Neil Shapiro switch (errno) 94506f25ae9SGregory Neil Shapiro { 94606f25ae9SGregory Neil Shapiro case ENFILE: /* system file table full */ 94706f25ae9SGregory Neil Shapiro case EINTR: /* interrupted syscall */ 94806f25ae9SGregory Neil Shapiro #ifdef ETXTBSY 94906f25ae9SGregory Neil Shapiro case ETXTBSY: /* Apollo: net file locked */ 95006f25ae9SGregory Neil Shapiro #endif /* ETXTBSY */ 95106f25ae9SGregory Neil Shapiro continue; 95206f25ae9SGregory Neil Shapiro } 95306f25ae9SGregory Neil Shapiro break; 95406f25ae9SGregory Neil Shapiro } 95506f25ae9SGregory Neil Shapiro if (!bitset(SFF_NOLOCK, sff) && 95606f25ae9SGregory Neil Shapiro fd >= 0 && 95706f25ae9SGregory Neil Shapiro fstat(fd, &st) >= 0 && 95806f25ae9SGregory Neil Shapiro S_ISREG(st.st_mode)) 95906f25ae9SGregory Neil Shapiro { 96006f25ae9SGregory Neil Shapiro int locktype; 96106f25ae9SGregory Neil Shapiro 96206f25ae9SGregory Neil Shapiro /* lock the file to avoid accidental conflicts */ 96306f25ae9SGregory Neil Shapiro if ((omode & O_ACCMODE) != O_RDONLY) 96406f25ae9SGregory Neil Shapiro locktype = LOCK_EX; 96506f25ae9SGregory Neil Shapiro else 96606f25ae9SGregory Neil Shapiro locktype = LOCK_SH; 967e92d3f3fSGregory Neil Shapiro if (bitset(SFF_NBLOCK, sff)) 968e92d3f3fSGregory Neil Shapiro locktype |= LOCK_NB; 969e92d3f3fSGregory Neil Shapiro 97006f25ae9SGregory Neil Shapiro if (!lockfile(fd, filename, NULL, locktype)) 97106f25ae9SGregory Neil Shapiro { 97206f25ae9SGregory Neil Shapiro int save_errno = errno; 97306f25ae9SGregory Neil Shapiro 97406f25ae9SGregory Neil Shapiro (void) close(fd); 97506f25ae9SGregory Neil Shapiro fd = -1; 97606f25ae9SGregory Neil Shapiro errno = save_errno; 97706f25ae9SGregory Neil Shapiro } 97806f25ae9SGregory Neil Shapiro else 97906f25ae9SGregory Neil Shapiro errno = 0; 98006f25ae9SGregory Neil Shapiro } 98106f25ae9SGregory Neil Shapiro return fd; 98206f25ae9SGregory Neil Shapiro } 983