106f25ae9SGregory Neil Shapiro /* 206f25ae9SGregory Neil Shapiro * Copyright (c) 1998-2000 Sendmail, 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 #ifndef lint 1506f25ae9SGregory Neil Shapiro static char id[] = "@(#)$Id: safefile.c,v 8.81.4.5 2000/07/17 22:33:37 ca Exp $"; 1606f25ae9SGregory Neil Shapiro #endif /* ! lint */ 1706f25ae9SGregory Neil Shapiro 1806f25ae9SGregory Neil Shapiro #include <sendmail.h> 1906f25ae9SGregory Neil Shapiro 2006f25ae9SGregory Neil Shapiro 2106f25ae9SGregory 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; 6106f25ae9SGregory Neil Shapiro char fbuf[MAXPATHLEN + 1]; 6206f25ae9SGregory Neil Shapiro 6306f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 6406f25ae9SGregory Neil Shapiro 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; 6706f25ae9SGregory Neil Shapiro if (st == NULL) 6806f25ae9SGregory Neil Shapiro st = &fstbuf; 6906f25ae9SGregory Neil Shapiro if (strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf) 7006f25ae9SGregory Neil Shapiro { 7106f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 7206f25ae9SGregory Neil Shapiro dprintf("\tpathname too long\n"); 7306f25ae9SGregory Neil Shapiro return ENAMETOOLONG; 7406f25ae9SGregory Neil Shapiro } 7506f25ae9SGregory Neil Shapiro fn = fbuf; 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 /* 9606f25ae9SGregory Neil Shapiro ** If final file is setuid, 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 */ 14206f25ae9SGregory 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)) 15206f25ae9SGregory Neil Shapiro dprintf("\t%s\n", 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)) 19106f25ae9SGregory Neil Shapiro dprintf("\t%s\n", 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 20906f25ae9SGregory Neil Shapiro if (stbuf.st_uid == uid) 21006f25ae9SGregory Neil Shapiro /* EMPTY */ 21106f25ae9SGregory Neil Shapiro ; 21206f25ae9SGregory Neil Shapiro else if (uid == 0 && stbuf.st_uid == TrustedUid) 21306f25ae9SGregory Neil Shapiro /* EMPTY */ 21406f25ae9SGregory Neil Shapiro ; 21506f25ae9SGregory Neil Shapiro else 21606f25ae9SGregory Neil Shapiro { 21706f25ae9SGregory Neil Shapiro md >>= 3; 21806f25ae9SGregory Neil Shapiro if (stbuf.st_gid == gid) 21906f25ae9SGregory Neil Shapiro /* EMPTY */ 22006f25ae9SGregory Neil Shapiro ; 22106f25ae9SGregory Neil Shapiro # ifndef NO_GROUP_SET 22206f25ae9SGregory Neil Shapiro else if (user != NULL && !DontInitGroups && 22306f25ae9SGregory Neil Shapiro ((gr != NULL && 22406f25ae9SGregory Neil Shapiro gr->gr_gid == stbuf.st_gid) || 22506f25ae9SGregory Neil Shapiro (gr = getgrgid(stbuf.st_gid)) != NULL)) 22606f25ae9SGregory Neil Shapiro { 22706f25ae9SGregory Neil Shapiro register char **gp; 22806f25ae9SGregory Neil Shapiro 22906f25ae9SGregory Neil Shapiro for (gp = gr->gr_mem; *gp != NULL; gp++) 23006f25ae9SGregory Neil Shapiro if (strcmp(*gp, user) == 0) 23106f25ae9SGregory Neil Shapiro break; 23206f25ae9SGregory Neil Shapiro if (*gp == NULL) 23306f25ae9SGregory Neil Shapiro md >>= 3; 23406f25ae9SGregory Neil Shapiro } 23506f25ae9SGregory Neil Shapiro # endif /* ! NO_GROUP_SET */ 23606f25ae9SGregory Neil Shapiro else 23706f25ae9SGregory Neil Shapiro md >>= 3; 23806f25ae9SGregory Neil Shapiro } 23906f25ae9SGregory Neil Shapiro if ((stbuf.st_mode & md) != md) 24006f25ae9SGregory Neil Shapiro errno = EACCES; 24106f25ae9SGregory Neil Shapiro } 24206f25ae9SGregory Neil Shapiro ret = errno; 24306f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 24406f25ae9SGregory Neil Shapiro dprintf("\t[final dir %s uid %d mode %lo] %s\n", 24506f25ae9SGregory Neil Shapiro dir, (int) stbuf.st_uid, (u_long) stbuf.st_mode, 24606f25ae9SGregory Neil Shapiro errstring(ret)); 24706f25ae9SGregory Neil Shapiro if (p != NULL) 24806f25ae9SGregory Neil Shapiro *p = '/'; 24906f25ae9SGregory Neil Shapiro st->st_mode = ST_MODE_NOFILE; 25006f25ae9SGregory Neil Shapiro return ret; 25106f25ae9SGregory Neil Shapiro } 25206f25ae9SGregory Neil Shapiro 25306f25ae9SGregory Neil Shapiro # ifdef S_ISLNK 25406f25ae9SGregory Neil Shapiro if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) 25506f25ae9SGregory Neil Shapiro { 25606f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 25706f25ae9SGregory Neil Shapiro dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n", 25806f25ae9SGregory Neil Shapiro (u_long) st->st_mode); 25906f25ae9SGregory Neil Shapiro return E_SM_NOSLINK; 26006f25ae9SGregory Neil Shapiro } 26106f25ae9SGregory Neil Shapiro # endif /* S_ISLNK */ 26206f25ae9SGregory Neil Shapiro if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) 26306f25ae9SGregory Neil Shapiro { 26406f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 26506f25ae9SGregory Neil Shapiro dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n", 26606f25ae9SGregory Neil Shapiro (u_long) st->st_mode); 26706f25ae9SGregory Neil Shapiro return E_SM_REGONLY; 26806f25ae9SGregory Neil Shapiro } 26906f25ae9SGregory Neil Shapiro if (bitset(SFF_NOGWFILES, flags) && 27006f25ae9SGregory Neil Shapiro bitset(S_IWGRP, st->st_mode)) 27106f25ae9SGregory Neil Shapiro { 27206f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 27306f25ae9SGregory Neil Shapiro dprintf("\t[write bits %lo]\tE_SM_GWFILE\n", 27406f25ae9SGregory Neil Shapiro (u_long) st->st_mode); 27506f25ae9SGregory Neil Shapiro return E_SM_GWFILE; 27606f25ae9SGregory Neil Shapiro } 27706f25ae9SGregory Neil Shapiro if (bitset(SFF_NOWWFILES, flags) && 27806f25ae9SGregory Neil Shapiro bitset(S_IWOTH, st->st_mode)) 27906f25ae9SGregory Neil Shapiro { 28006f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 28106f25ae9SGregory Neil Shapiro dprintf("\t[write bits %lo]\tE_SM_WWFILE\n", 28206f25ae9SGregory Neil Shapiro (u_long) st->st_mode); 28306f25ae9SGregory Neil Shapiro return E_SM_WWFILE; 28406f25ae9SGregory Neil Shapiro } 28506f25ae9SGregory Neil Shapiro if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode)) 28606f25ae9SGregory Neil Shapiro { 28706f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 28806f25ae9SGregory Neil Shapiro dprintf("\t[read bits %lo]\tE_SM_GRFILE\n", 28906f25ae9SGregory Neil Shapiro (u_long) st->st_mode); 29006f25ae9SGregory Neil Shapiro return E_SM_GRFILE; 29106f25ae9SGregory Neil Shapiro } 29206f25ae9SGregory Neil Shapiro if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode)) 29306f25ae9SGregory Neil Shapiro { 29406f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 29506f25ae9SGregory Neil Shapiro dprintf("\t[read bits %lo]\tE_SM_WRFILE\n", 29606f25ae9SGregory Neil Shapiro (u_long) st->st_mode); 29706f25ae9SGregory Neil Shapiro return E_SM_WRFILE; 29806f25ae9SGregory Neil Shapiro } 29906f25ae9SGregory Neil Shapiro if (!bitset(SFF_EXECOK, flags) && 30006f25ae9SGregory Neil Shapiro bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && 30106f25ae9SGregory Neil Shapiro bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode)) 30206f25ae9SGregory Neil Shapiro { 30306f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 30406f25ae9SGregory Neil Shapiro dprintf("\t[exec bits %lo]\tE_SM_ISEXEC]\n", 30506f25ae9SGregory Neil Shapiro (u_long) st->st_mode); 30606f25ae9SGregory Neil Shapiro return E_SM_ISEXEC; 30706f25ae9SGregory Neil Shapiro } 30806f25ae9SGregory Neil Shapiro if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1) 30906f25ae9SGregory Neil Shapiro { 31006f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 31106f25ae9SGregory Neil Shapiro dprintf("\t[link count %d]\tE_SM_NOHLINK\n", 31206f25ae9SGregory Neil Shapiro (int) st->st_nlink); 31306f25ae9SGregory Neil Shapiro return E_SM_NOHLINK; 31406f25ae9SGregory Neil Shapiro } 31506f25ae9SGregory Neil Shapiro 31606f25ae9SGregory Neil Shapiro if (uid == 0 && bitset(SFF_OPENASROOT, flags)) 31706f25ae9SGregory Neil Shapiro /* EMPTY */ 31806f25ae9SGregory Neil Shapiro ; 31906f25ae9SGregory Neil Shapiro else if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 32006f25ae9SGregory Neil Shapiro mode >>= 6; 32106f25ae9SGregory Neil Shapiro else if (st->st_uid == uid) 32206f25ae9SGregory Neil Shapiro /* EMPTY */ 32306f25ae9SGregory Neil Shapiro ; 32406f25ae9SGregory Neil Shapiro else if (uid == 0 && st->st_uid == TrustedUid) 32506f25ae9SGregory Neil Shapiro /* EMPTY */ 32606f25ae9SGregory Neil Shapiro ; 32706f25ae9SGregory Neil Shapiro else 32806f25ae9SGregory Neil Shapiro { 32906f25ae9SGregory Neil Shapiro mode >>= 3; 33006f25ae9SGregory Neil Shapiro if (st->st_gid == gid) 33106f25ae9SGregory Neil Shapiro /* EMPTY */ 33206f25ae9SGregory Neil Shapiro ; 33306f25ae9SGregory Neil Shapiro # ifndef NO_GROUP_SET 33406f25ae9SGregory Neil Shapiro else if (user != NULL && !DontInitGroups && 33506f25ae9SGregory Neil Shapiro ((gr != NULL && gr->gr_gid == st->st_gid) || 33606f25ae9SGregory Neil Shapiro (gr = getgrgid(st->st_gid)) != NULL)) 33706f25ae9SGregory Neil Shapiro { 33806f25ae9SGregory Neil Shapiro register char **gp; 33906f25ae9SGregory Neil Shapiro 34006f25ae9SGregory Neil Shapiro for (gp = gr->gr_mem; *gp != NULL; gp++) 34106f25ae9SGregory Neil Shapiro if (strcmp(*gp, user) == 0) 34206f25ae9SGregory Neil Shapiro break; 34306f25ae9SGregory Neil Shapiro if (*gp == NULL) 34406f25ae9SGregory Neil Shapiro mode >>= 3; 34506f25ae9SGregory Neil Shapiro } 34606f25ae9SGregory Neil Shapiro # endif /* ! NO_GROUP_SET */ 34706f25ae9SGregory Neil Shapiro else 34806f25ae9SGregory Neil Shapiro mode >>= 3; 34906f25ae9SGregory Neil Shapiro } 35006f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 35106f25ae9SGregory Neil Shapiro dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ", 35206f25ae9SGregory Neil Shapiro (int) st->st_uid, (int) st->st_nlink, 35306f25ae9SGregory Neil Shapiro (u_long) st->st_mode, (u_long) mode); 35406f25ae9SGregory Neil Shapiro if ((st->st_uid == uid || st->st_uid == 0 || 35506f25ae9SGregory Neil Shapiro st->st_uid == TrustedUid || 35606f25ae9SGregory Neil Shapiro !bitset(SFF_MUSTOWN, flags)) && 35706f25ae9SGregory Neil Shapiro (st->st_mode & mode) == mode) 35806f25ae9SGregory Neil Shapiro { 35906f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 36006f25ae9SGregory Neil Shapiro dprintf("\tOK\n"); 36106f25ae9SGregory Neil Shapiro return 0; 36206f25ae9SGregory Neil Shapiro } 36306f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 36406f25ae9SGregory Neil Shapiro dprintf("\tEACCES\n"); 36506f25ae9SGregory Neil Shapiro return EACCES; 36606f25ae9SGregory Neil Shapiro } 36706f25ae9SGregory Neil Shapiro /* 36806f25ae9SGregory Neil Shapiro ** SAFEDIRPATH -- check to make sure a path to a directory is safe 36906f25ae9SGregory Neil Shapiro ** 37006f25ae9SGregory Neil Shapiro ** Safe means not writable and owned by the right folks. 37106f25ae9SGregory Neil Shapiro ** 37206f25ae9SGregory Neil Shapiro ** Parameters: 37306f25ae9SGregory Neil Shapiro ** fn -- filename to check. 37406f25ae9SGregory Neil Shapiro ** uid -- user id to compare against. 37506f25ae9SGregory Neil Shapiro ** gid -- group id to compare against. 37606f25ae9SGregory Neil Shapiro ** user -- user name to compare against (used for group 37706f25ae9SGregory Neil Shapiro ** sets). 37806f25ae9SGregory Neil Shapiro ** flags -- modifiers: 37906f25ae9SGregory Neil Shapiro ** SFF_ROOTOK -- ok to use root permissions to open. 38006f25ae9SGregory Neil Shapiro ** SFF_SAFEDIRPATH -- writable directories are considered 38106f25ae9SGregory Neil Shapiro ** to be fatal errors. 38206f25ae9SGregory Neil Shapiro ** level -- symlink recursive level. 38306f25ae9SGregory Neil Shapiro ** offset -- offset into fn to start checking from. 38406f25ae9SGregory Neil Shapiro ** 38506f25ae9SGregory Neil Shapiro ** Returns: 38606f25ae9SGregory Neil Shapiro ** 0 -- if the directory path is "safe". 38706f25ae9SGregory Neil Shapiro ** else -- an error number associated with the path. 38806f25ae9SGregory Neil Shapiro */ 38906f25ae9SGregory Neil Shapiro 39006f25ae9SGregory Neil Shapiro int 39106f25ae9SGregory Neil Shapiro safedirpath(fn, uid, gid, user, flags, level, offset) 39206f25ae9SGregory Neil Shapiro char *fn; 39306f25ae9SGregory Neil Shapiro UID_T uid; 39406f25ae9SGregory Neil Shapiro GID_T gid; 39506f25ae9SGregory Neil Shapiro char *user; 39606f25ae9SGregory Neil Shapiro long flags; 39706f25ae9SGregory Neil Shapiro int level; 39806f25ae9SGregory Neil Shapiro int offset; 39906f25ae9SGregory Neil Shapiro { 40006f25ae9SGregory Neil Shapiro int ret = 0; 40106f25ae9SGregory Neil Shapiro int mode = S_IWOTH; 40206f25ae9SGregory Neil Shapiro char save = '\0'; 40306f25ae9SGregory Neil Shapiro char *saveptr = NULL; 40406f25ae9SGregory Neil Shapiro char *p, *enddir; 40506f25ae9SGregory Neil Shapiro register struct group *gr = NULL; 40606f25ae9SGregory Neil Shapiro char s[MAXLINKPATHLEN + 1]; 40706f25ae9SGregory Neil Shapiro struct stat stbuf; 40806f25ae9SGregory Neil Shapiro 40906f25ae9SGregory Neil Shapiro /* make sure we aren't in a symlink loop */ 41006f25ae9SGregory Neil Shapiro if (level > MAXSYMLINKS) 41106f25ae9SGregory Neil Shapiro return ELOOP; 41206f25ae9SGregory Neil Shapiro 41306f25ae9SGregory Neil Shapiro /* special case root directory */ 41406f25ae9SGregory Neil Shapiro if (*fn == '\0') 41506f25ae9SGregory Neil Shapiro fn = "/"; 41606f25ae9SGregory Neil Shapiro 41706f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 41806f25ae9SGregory Neil Shapiro dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n", 41906f25ae9SGregory Neil Shapiro fn, (long) uid, (long) gid, flags, level, offset); 42006f25ae9SGregory Neil Shapiro 42106f25ae9SGregory Neil Shapiro if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail)) 42206f25ae9SGregory Neil Shapiro mode |= S_IWGRP; 42306f25ae9SGregory Neil Shapiro 42406f25ae9SGregory Neil Shapiro /* Make a modifiable copy of the filename */ 42506f25ae9SGregory Neil Shapiro if (strlcpy(s, fn, sizeof s) >= sizeof s) 42606f25ae9SGregory Neil Shapiro return EINVAL; 42706f25ae9SGregory Neil Shapiro 42806f25ae9SGregory Neil Shapiro p = s + offset; 42906f25ae9SGregory Neil Shapiro while (p != NULL) 43006f25ae9SGregory Neil Shapiro { 43106f25ae9SGregory Neil Shapiro /* put back character */ 43206f25ae9SGregory Neil Shapiro if (saveptr != NULL) 43306f25ae9SGregory Neil Shapiro { 43406f25ae9SGregory Neil Shapiro *saveptr = save; 43506f25ae9SGregory Neil Shapiro saveptr = NULL; 43606f25ae9SGregory Neil Shapiro p++; 43706f25ae9SGregory Neil Shapiro } 43806f25ae9SGregory Neil Shapiro 43906f25ae9SGregory Neil Shapiro if (*p == '\0') 44006f25ae9SGregory Neil Shapiro break; 44106f25ae9SGregory Neil Shapiro 44206f25ae9SGregory Neil Shapiro p = strchr(p, '/'); 44306f25ae9SGregory Neil Shapiro 44406f25ae9SGregory Neil Shapiro /* Special case for root directory */ 44506f25ae9SGregory Neil Shapiro if (p == s) 44606f25ae9SGregory Neil Shapiro { 44706f25ae9SGregory Neil Shapiro save = *(p + 1); 44806f25ae9SGregory Neil Shapiro saveptr = p + 1; 44906f25ae9SGregory Neil Shapiro *(p + 1) = '\0'; 45006f25ae9SGregory Neil Shapiro } 45106f25ae9SGregory Neil Shapiro else if (p != NULL) 45206f25ae9SGregory Neil Shapiro { 45306f25ae9SGregory Neil Shapiro save = *p; 45406f25ae9SGregory Neil Shapiro saveptr = p; 45506f25ae9SGregory Neil Shapiro *p = '\0'; 45606f25ae9SGregory Neil Shapiro } 45706f25ae9SGregory Neil Shapiro 45806f25ae9SGregory Neil Shapiro /* Heuristic: . and .. have already been checked */ 45906f25ae9SGregory Neil Shapiro enddir = strrchr(s, '/'); 46006f25ae9SGregory Neil Shapiro if (enddir != NULL && 46106f25ae9SGregory Neil Shapiro (strcmp(enddir, "/..") == 0 || 46206f25ae9SGregory Neil Shapiro strcmp(enddir, "/.") == 0)) 46306f25ae9SGregory Neil Shapiro continue; 46406f25ae9SGregory Neil Shapiro 46506f25ae9SGregory Neil Shapiro if (tTd(44, 20)) 46606f25ae9SGregory Neil Shapiro dprintf("\t[dir %s]\n", s); 46706f25ae9SGregory Neil Shapiro 46806f25ae9SGregory Neil Shapiro # if HASLSTAT 46906f25ae9SGregory Neil Shapiro ret = lstat(s, &stbuf); 47006f25ae9SGregory Neil Shapiro # else /* HASLSTAT */ 47106f25ae9SGregory Neil Shapiro ret = stat(s, &stbuf); 47206f25ae9SGregory Neil Shapiro # endif /* HASLSTAT */ 47306f25ae9SGregory Neil Shapiro if (ret < 0) 47406f25ae9SGregory Neil Shapiro { 47506f25ae9SGregory Neil Shapiro ret = errno; 47606f25ae9SGregory Neil Shapiro break; 47706f25ae9SGregory Neil Shapiro } 47806f25ae9SGregory Neil Shapiro 47906f25ae9SGregory Neil Shapiro # ifdef S_ISLNK 48006f25ae9SGregory Neil Shapiro /* Follow symlinks */ 48106f25ae9SGregory Neil Shapiro if (S_ISLNK(stbuf.st_mode)) 48206f25ae9SGregory Neil Shapiro { 48306f25ae9SGregory Neil Shapiro char *target; 48406f25ae9SGregory Neil Shapiro char buf[MAXPATHLEN + 1]; 48506f25ae9SGregory Neil Shapiro 48606f25ae9SGregory Neil Shapiro memset(buf, '\0', sizeof buf); 48706f25ae9SGregory Neil Shapiro if (readlink(s, buf, sizeof buf) < 0) 48806f25ae9SGregory Neil Shapiro { 48906f25ae9SGregory Neil Shapiro ret = errno; 49006f25ae9SGregory Neil Shapiro break; 49106f25ae9SGregory Neil Shapiro } 49206f25ae9SGregory Neil Shapiro 49306f25ae9SGregory Neil Shapiro offset = 0; 49406f25ae9SGregory Neil Shapiro if (*buf == '/') 49506f25ae9SGregory Neil Shapiro { 49606f25ae9SGregory Neil Shapiro target = buf; 49706f25ae9SGregory Neil Shapiro 49806f25ae9SGregory Neil Shapiro /* If path is the same, avoid rechecks */ 49906f25ae9SGregory Neil Shapiro while (s[offset] == buf[offset] && 50006f25ae9SGregory Neil Shapiro s[offset] != '\0') 50106f25ae9SGregory Neil Shapiro offset++; 50206f25ae9SGregory Neil Shapiro 50306f25ae9SGregory Neil Shapiro if (s[offset] == '\0' && buf[offset] == '\0') 50406f25ae9SGregory Neil Shapiro { 50506f25ae9SGregory Neil Shapiro /* strings match, symlink loop */ 50606f25ae9SGregory Neil Shapiro return ELOOP; 50706f25ae9SGregory Neil Shapiro } 50806f25ae9SGregory Neil Shapiro 50906f25ae9SGregory Neil Shapiro /* back off from the mismatch */ 51006f25ae9SGregory Neil Shapiro if (offset > 0) 51106f25ae9SGregory Neil Shapiro offset--; 51206f25ae9SGregory Neil Shapiro 51306f25ae9SGregory Neil Shapiro /* Make sure we are at a directory break */ 51406f25ae9SGregory Neil Shapiro if (offset > 0 && 51506f25ae9SGregory Neil Shapiro s[offset] != '/' && 51606f25ae9SGregory Neil Shapiro s[offset] != '\0') 51706f25ae9SGregory Neil Shapiro { 51806f25ae9SGregory Neil Shapiro while (buf[offset] != '/' && 51906f25ae9SGregory Neil Shapiro offset > 0) 52006f25ae9SGregory Neil Shapiro offset--; 52106f25ae9SGregory Neil Shapiro } 52206f25ae9SGregory Neil Shapiro if (offset > 0 && 52306f25ae9SGregory Neil Shapiro s[offset] == '/' && 52406f25ae9SGregory Neil Shapiro buf[offset] == '/') 52506f25ae9SGregory Neil Shapiro { 52606f25ae9SGregory Neil Shapiro /* Include the trailing slash */ 52706f25ae9SGregory Neil Shapiro offset++; 52806f25ae9SGregory Neil Shapiro } 52906f25ae9SGregory Neil Shapiro } 53006f25ae9SGregory Neil Shapiro else 53106f25ae9SGregory Neil Shapiro { 53206f25ae9SGregory Neil Shapiro char *sptr; 53306f25ae9SGregory Neil Shapiro char fullbuf[MAXLINKPATHLEN + 1]; 53406f25ae9SGregory Neil Shapiro 53506f25ae9SGregory Neil Shapiro sptr = strrchr(s, '/'); 53606f25ae9SGregory Neil Shapiro if (sptr != NULL) 53706f25ae9SGregory Neil Shapiro { 53806f25ae9SGregory Neil Shapiro *sptr = '\0'; 53906f25ae9SGregory Neil Shapiro offset = sptr + 1 - s; 54006f25ae9SGregory Neil Shapiro if ((strlen(s) + 1 + 54106f25ae9SGregory Neil Shapiro strlen(buf) + 1) > sizeof fullbuf) 54206f25ae9SGregory Neil Shapiro { 54306f25ae9SGregory Neil Shapiro ret = EINVAL; 54406f25ae9SGregory Neil Shapiro break; 54506f25ae9SGregory Neil Shapiro } 54606f25ae9SGregory Neil Shapiro snprintf(fullbuf, sizeof fullbuf, 54706f25ae9SGregory Neil Shapiro "%s/%s", s, buf); 54806f25ae9SGregory Neil Shapiro *sptr = '/'; 54906f25ae9SGregory Neil Shapiro } 55006f25ae9SGregory Neil Shapiro else 55106f25ae9SGregory Neil Shapiro { 55206f25ae9SGregory Neil Shapiro if (strlen(buf) + 1 > sizeof fullbuf) 55306f25ae9SGregory Neil Shapiro { 55406f25ae9SGregory Neil Shapiro ret = EINVAL; 55506f25ae9SGregory Neil Shapiro break; 55606f25ae9SGregory Neil Shapiro } 55706f25ae9SGregory Neil Shapiro (void) strlcpy(fullbuf, buf, 55806f25ae9SGregory Neil Shapiro sizeof fullbuf); 55906f25ae9SGregory Neil Shapiro } 56006f25ae9SGregory Neil Shapiro target = fullbuf; 56106f25ae9SGregory Neil Shapiro } 56206f25ae9SGregory Neil Shapiro ret = safedirpath(target, uid, gid, user, flags, 56306f25ae9SGregory Neil Shapiro level + 1, offset); 56406f25ae9SGregory Neil Shapiro if (ret != 0) 56506f25ae9SGregory Neil Shapiro break; 56606f25ae9SGregory Neil Shapiro 56706f25ae9SGregory Neil Shapiro /* Don't check permissions on the link file itself */ 56806f25ae9SGregory Neil Shapiro continue; 56906f25ae9SGregory Neil Shapiro } 57006f25ae9SGregory Neil Shapiro #endif /* S_ISLNK */ 57106f25ae9SGregory Neil Shapiro 57206f25ae9SGregory Neil Shapiro if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) && 57306f25ae9SGregory Neil Shapiro #ifdef S_ISVTX 57406f25ae9SGregory Neil Shapiro !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) && 57506f25ae9SGregory Neil Shapiro bitset(S_ISVTX, stbuf.st_mode)) && 57606f25ae9SGregory Neil Shapiro #endif /* S_ISVTX */ 57706f25ae9SGregory Neil Shapiro bitset(mode, stbuf.st_mode)) 57806f25ae9SGregory Neil Shapiro { 57906f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 58006f25ae9SGregory Neil Shapiro dprintf("\t[dir %s] mode %lo ", 58106f25ae9SGregory Neil Shapiro s, (u_long) stbuf.st_mode); 58206f25ae9SGregory Neil Shapiro if (bitset(SFF_SAFEDIRPATH, flags)) 58306f25ae9SGregory Neil Shapiro { 58406f25ae9SGregory Neil Shapiro if (bitset(S_IWOTH, stbuf.st_mode)) 58506f25ae9SGregory Neil Shapiro ret = E_SM_WWDIR; 58606f25ae9SGregory Neil Shapiro else 58706f25ae9SGregory Neil Shapiro ret = E_SM_GWDIR; 58806f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 58906f25ae9SGregory Neil Shapiro dprintf("FATAL\n"); 59006f25ae9SGregory Neil Shapiro break; 59106f25ae9SGregory Neil Shapiro } 59206f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 59306f25ae9SGregory Neil Shapiro dprintf("WARNING\n"); 59406f25ae9SGregory Neil Shapiro if (Verbose > 1) 59506f25ae9SGregory Neil Shapiro message("051 WARNING: %s writable directory %s", 59606f25ae9SGregory Neil Shapiro bitset(S_IWOTH, stbuf.st_mode) 59706f25ae9SGregory Neil Shapiro ? "World" 59806f25ae9SGregory Neil Shapiro : "Group", 59906f25ae9SGregory Neil Shapiro s); 60006f25ae9SGregory Neil Shapiro } 60106f25ae9SGregory Neil Shapiro if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)) 60206f25ae9SGregory Neil Shapiro { 60306f25ae9SGregory Neil Shapiro if (bitset(S_IXOTH, stbuf.st_mode)) 60406f25ae9SGregory Neil Shapiro continue; 60506f25ae9SGregory Neil Shapiro ret = EACCES; 60606f25ae9SGregory Neil Shapiro break; 60706f25ae9SGregory Neil Shapiro } 60806f25ae9SGregory Neil Shapiro 60906f25ae9SGregory Neil Shapiro /* 61006f25ae9SGregory Neil Shapiro ** Let OS determine access to file if we are not 61106f25ae9SGregory Neil Shapiro ** running as a privileged user. This allows ACLs 61206f25ae9SGregory Neil Shapiro ** to work. Also, if opening as root, assume we can 61306f25ae9SGregory Neil Shapiro ** scan the directory. 61406f25ae9SGregory Neil Shapiro */ 61506f25ae9SGregory Neil Shapiro if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags)) 61606f25ae9SGregory Neil Shapiro continue; 61706f25ae9SGregory Neil Shapiro 61806f25ae9SGregory Neil Shapiro if (stbuf.st_uid == uid && 61906f25ae9SGregory Neil Shapiro bitset(S_IXUSR, stbuf.st_mode)) 62006f25ae9SGregory Neil Shapiro continue; 62106f25ae9SGregory Neil Shapiro if (stbuf.st_gid == gid && 62206f25ae9SGregory Neil Shapiro bitset(S_IXGRP, stbuf.st_mode)) 62306f25ae9SGregory Neil Shapiro continue; 62406f25ae9SGregory Neil Shapiro # ifndef NO_GROUP_SET 62506f25ae9SGregory Neil Shapiro if (user != NULL && !DontInitGroups && 62606f25ae9SGregory Neil Shapiro ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 62706f25ae9SGregory Neil Shapiro (gr = getgrgid(stbuf.st_gid)) != NULL)) 62806f25ae9SGregory Neil Shapiro { 62906f25ae9SGregory Neil Shapiro register char **gp; 63006f25ae9SGregory Neil Shapiro 63106f25ae9SGregory Neil Shapiro for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) 63206f25ae9SGregory Neil Shapiro if (strcmp(*gp, user) == 0) 63306f25ae9SGregory Neil Shapiro break; 63406f25ae9SGregory Neil Shapiro if (gp != NULL && *gp != NULL && 63506f25ae9SGregory Neil Shapiro bitset(S_IXGRP, stbuf.st_mode)) 63606f25ae9SGregory Neil Shapiro continue; 63706f25ae9SGregory Neil Shapiro } 63806f25ae9SGregory Neil Shapiro # endif /* ! NO_GROUP_SET */ 63906f25ae9SGregory Neil Shapiro if (!bitset(S_IXOTH, stbuf.st_mode)) 64006f25ae9SGregory Neil Shapiro { 64106f25ae9SGregory Neil Shapiro ret = EACCES; 64206f25ae9SGregory Neil Shapiro break; 64306f25ae9SGregory Neil Shapiro } 64406f25ae9SGregory Neil Shapiro } 64506f25ae9SGregory Neil Shapiro if (tTd(44, 4)) 64606f25ae9SGregory Neil Shapiro dprintf("\t[dir %s] %s\n", fn, 64706f25ae9SGregory Neil Shapiro ret == 0 ? "OK" : errstring(ret)); 64806f25ae9SGregory Neil Shapiro return ret; 64906f25ae9SGregory Neil Shapiro } 65006f25ae9SGregory Neil Shapiro /* 65106f25ae9SGregory Neil Shapiro ** SAFEOPEN -- do a file open with extra checking 65206f25ae9SGregory Neil Shapiro ** 65306f25ae9SGregory Neil Shapiro ** Parameters: 65406f25ae9SGregory Neil Shapiro ** fn -- the file name to open. 65506f25ae9SGregory Neil Shapiro ** omode -- the open-style mode flags. 65606f25ae9SGregory Neil Shapiro ** cmode -- the create-style mode flags. 65706f25ae9SGregory Neil Shapiro ** sff -- safefile flags. 65806f25ae9SGregory Neil Shapiro ** 65906f25ae9SGregory Neil Shapiro ** Returns: 66006f25ae9SGregory Neil Shapiro ** Same as open. 66106f25ae9SGregory Neil Shapiro */ 66206f25ae9SGregory Neil Shapiro 66306f25ae9SGregory Neil Shapiro #ifndef O_ACCMODE 66406f25ae9SGregory Neil Shapiro # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) 66506f25ae9SGregory Neil Shapiro #endif /* ! O_ACCMODE */ 66606f25ae9SGregory Neil Shapiro 66706f25ae9SGregory Neil Shapiro int 66806f25ae9SGregory Neil Shapiro safeopen(fn, omode, cmode, sff) 66906f25ae9SGregory Neil Shapiro char *fn; 67006f25ae9SGregory Neil Shapiro int omode; 67106f25ae9SGregory Neil Shapiro int cmode; 67206f25ae9SGregory Neil Shapiro long sff; 67306f25ae9SGregory Neil Shapiro { 67406f25ae9SGregory Neil Shapiro int rval; 67506f25ae9SGregory Neil Shapiro int fd; 67606f25ae9SGregory Neil Shapiro int smode; 67706f25ae9SGregory Neil Shapiro struct stat stb; 67806f25ae9SGregory Neil Shapiro 67906f25ae9SGregory Neil Shapiro if (tTd(44, 10)) 68006f25ae9SGregory Neil Shapiro printf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n", 68106f25ae9SGregory Neil Shapiro fn, omode, cmode, sff); 68206f25ae9SGregory Neil Shapiro 68306f25ae9SGregory Neil Shapiro if (bitset(O_CREAT, omode)) 68406f25ae9SGregory Neil Shapiro sff |= SFF_CREAT; 68506f25ae9SGregory Neil Shapiro omode &= ~O_CREAT; 68606f25ae9SGregory Neil Shapiro smode = 0; 68706f25ae9SGregory Neil Shapiro switch (omode & O_ACCMODE) 68806f25ae9SGregory Neil Shapiro { 68906f25ae9SGregory Neil Shapiro case O_RDONLY: 69006f25ae9SGregory Neil Shapiro smode = S_IREAD; 69106f25ae9SGregory Neil Shapiro break; 69206f25ae9SGregory Neil Shapiro 69306f25ae9SGregory Neil Shapiro case O_WRONLY: 69406f25ae9SGregory Neil Shapiro smode = S_IWRITE; 69506f25ae9SGregory Neil Shapiro break; 69606f25ae9SGregory Neil Shapiro 69706f25ae9SGregory Neil Shapiro case O_RDWR: 69806f25ae9SGregory Neil Shapiro smode = S_IREAD|S_IWRITE; 69906f25ae9SGregory Neil Shapiro break; 70006f25ae9SGregory Neil Shapiro 70106f25ae9SGregory Neil Shapiro default: 70206f25ae9SGregory Neil Shapiro smode = 0; 70306f25ae9SGregory Neil Shapiro break; 70406f25ae9SGregory Neil Shapiro } 70506f25ae9SGregory Neil Shapiro if (bitset(SFF_OPENASROOT, sff)) 70606f25ae9SGregory Neil Shapiro rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName, 70706f25ae9SGregory Neil Shapiro sff, smode, &stb); 70806f25ae9SGregory Neil Shapiro else 70906f25ae9SGregory Neil Shapiro rval = safefile(fn, RealUid, RealGid, RealUserName, 71006f25ae9SGregory Neil Shapiro sff, smode, &stb); 71106f25ae9SGregory Neil Shapiro if (rval != 0) 71206f25ae9SGregory Neil Shapiro { 71306f25ae9SGregory Neil Shapiro errno = rval; 71406f25ae9SGregory Neil Shapiro return -1; 71506f25ae9SGregory Neil Shapiro } 71606f25ae9SGregory Neil Shapiro if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff)) 71706f25ae9SGregory Neil Shapiro omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL); 71806f25ae9SGregory Neil Shapiro else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode)) 71906f25ae9SGregory Neil Shapiro { 72006f25ae9SGregory Neil Shapiro /* The file exists so an exclusive create would fail */ 72106f25ae9SGregory Neil Shapiro errno = EEXIST; 72206f25ae9SGregory Neil Shapiro return -1; 72306f25ae9SGregory Neil Shapiro } 72406f25ae9SGregory Neil Shapiro 72506f25ae9SGregory Neil Shapiro fd = dfopen(fn, omode, cmode, sff); 72606f25ae9SGregory Neil Shapiro if (fd < 0) 72706f25ae9SGregory Neil Shapiro return fd; 72806f25ae9SGregory Neil Shapiro if (filechanged(fn, fd, &stb)) 72906f25ae9SGregory Neil Shapiro { 73006f25ae9SGregory Neil Shapiro syserr("554 5.3.0 cannot open: file %s changed after open", fn); 73106f25ae9SGregory Neil Shapiro (void) close(fd); 73206f25ae9SGregory Neil Shapiro errno = E_SM_FILECHANGE; 73306f25ae9SGregory Neil Shapiro return -1; 73406f25ae9SGregory Neil Shapiro } 73506f25ae9SGregory Neil Shapiro return fd; 73606f25ae9SGregory Neil Shapiro } 73706f25ae9SGregory Neil Shapiro /* 73806f25ae9SGregory Neil Shapiro ** SAFEFOPEN -- do a file open with extra checking 73906f25ae9SGregory Neil Shapiro ** 74006f25ae9SGregory Neil Shapiro ** Parameters: 74106f25ae9SGregory Neil Shapiro ** fn -- the file name to open. 74206f25ae9SGregory Neil Shapiro ** omode -- the open-style mode flags. 74306f25ae9SGregory Neil Shapiro ** cmode -- the create-style mode flags. 74406f25ae9SGregory Neil Shapiro ** sff -- safefile flags. 74506f25ae9SGregory Neil Shapiro ** 74606f25ae9SGregory Neil Shapiro ** Returns: 74706f25ae9SGregory Neil Shapiro ** Same as fopen. 74806f25ae9SGregory Neil Shapiro */ 74906f25ae9SGregory Neil Shapiro 75006f25ae9SGregory Neil Shapiro FILE * 75106f25ae9SGregory Neil Shapiro safefopen(fn, omode, cmode, sff) 75206f25ae9SGregory Neil Shapiro char *fn; 75306f25ae9SGregory Neil Shapiro int omode; 75406f25ae9SGregory Neil Shapiro int cmode; 75506f25ae9SGregory Neil Shapiro long sff; 75606f25ae9SGregory Neil Shapiro { 75706f25ae9SGregory Neil Shapiro int fd; 75806f25ae9SGregory Neil Shapiro int save_errno; 75906f25ae9SGregory Neil Shapiro FILE *fp; 76006f25ae9SGregory Neil Shapiro char *fmode; 76106f25ae9SGregory Neil Shapiro 76206f25ae9SGregory Neil Shapiro switch (omode & O_ACCMODE) 76306f25ae9SGregory Neil Shapiro { 76406f25ae9SGregory Neil Shapiro case O_RDONLY: 76506f25ae9SGregory Neil Shapiro fmode = "r"; 76606f25ae9SGregory Neil Shapiro break; 76706f25ae9SGregory Neil Shapiro 76806f25ae9SGregory Neil Shapiro case O_WRONLY: 76906f25ae9SGregory Neil Shapiro if (bitset(O_APPEND, omode)) 77006f25ae9SGregory Neil Shapiro fmode = "a"; 77106f25ae9SGregory Neil Shapiro else 77206f25ae9SGregory Neil Shapiro fmode = "w"; 77306f25ae9SGregory Neil Shapiro break; 77406f25ae9SGregory Neil Shapiro 77506f25ae9SGregory Neil Shapiro case O_RDWR: 77606f25ae9SGregory Neil Shapiro if (bitset(O_TRUNC, omode)) 77706f25ae9SGregory Neil Shapiro fmode = "w+"; 77806f25ae9SGregory Neil Shapiro else if (bitset(O_APPEND, omode)) 77906f25ae9SGregory Neil Shapiro fmode = "a+"; 78006f25ae9SGregory Neil Shapiro else 78106f25ae9SGregory Neil Shapiro fmode = "r+"; 78206f25ae9SGregory Neil Shapiro break; 78306f25ae9SGregory Neil Shapiro 78406f25ae9SGregory Neil Shapiro default: 78506f25ae9SGregory Neil Shapiro syserr("554 5.3.5 safefopen: unknown omode %o", omode); 78606f25ae9SGregory Neil Shapiro fmode = "x"; 78706f25ae9SGregory Neil Shapiro } 78806f25ae9SGregory Neil Shapiro fd = safeopen(fn, omode, cmode, sff); 78906f25ae9SGregory Neil Shapiro if (fd < 0) 79006f25ae9SGregory Neil Shapiro { 79106f25ae9SGregory Neil Shapiro save_errno = errno; 79206f25ae9SGregory Neil Shapiro if (tTd(44, 10)) 79306f25ae9SGregory Neil Shapiro dprintf("safefopen: safeopen failed: %s\n", 79406f25ae9SGregory Neil Shapiro errstring(errno)); 79506f25ae9SGregory Neil Shapiro errno = save_errno; 79606f25ae9SGregory Neil Shapiro return NULL; 79706f25ae9SGregory Neil Shapiro } 79806f25ae9SGregory Neil Shapiro fp = fdopen(fd, fmode); 79906f25ae9SGregory Neil Shapiro if (fp != NULL) 80006f25ae9SGregory Neil Shapiro return fp; 80106f25ae9SGregory Neil Shapiro 80206f25ae9SGregory Neil Shapiro save_errno = errno; 80306f25ae9SGregory Neil Shapiro if (tTd(44, 10)) 80406f25ae9SGregory Neil Shapiro { 80506f25ae9SGregory Neil Shapiro dprintf("safefopen: fdopen(%s, %s) failed: omode=%x, sff=%lx, err=%s\n", 80606f25ae9SGregory Neil Shapiro fn, fmode, omode, sff, errstring(errno)); 80706f25ae9SGregory Neil Shapiro } 80806f25ae9SGregory Neil Shapiro (void) close(fd); 80906f25ae9SGregory Neil Shapiro errno = save_errno; 81006f25ae9SGregory Neil Shapiro return NULL; 81106f25ae9SGregory Neil Shapiro } 81206f25ae9SGregory Neil Shapiro /* 81306f25ae9SGregory Neil Shapiro ** FILECHANGED -- check to see if file changed after being opened 81406f25ae9SGregory Neil Shapiro ** 81506f25ae9SGregory Neil Shapiro ** Parameters: 81606f25ae9SGregory Neil Shapiro ** fn -- pathname of file to check. 81706f25ae9SGregory Neil Shapiro ** fd -- file descriptor to check. 81806f25ae9SGregory Neil Shapiro ** stb -- stat structure from before open. 81906f25ae9SGregory Neil Shapiro ** 82006f25ae9SGregory Neil Shapiro ** Returns: 82106f25ae9SGregory Neil Shapiro ** TRUE -- if a problem was detected. 82206f25ae9SGregory Neil Shapiro ** FALSE -- if this file is still the same. 82306f25ae9SGregory Neil Shapiro */ 82406f25ae9SGregory Neil Shapiro 82506f25ae9SGregory Neil Shapiro bool 82606f25ae9SGregory Neil Shapiro filechanged(fn, fd, stb) 82706f25ae9SGregory Neil Shapiro char *fn; 82806f25ae9SGregory Neil Shapiro int fd; 82906f25ae9SGregory Neil Shapiro struct stat *stb; 83006f25ae9SGregory Neil Shapiro { 83106f25ae9SGregory Neil Shapiro struct stat sta; 83206f25ae9SGregory Neil Shapiro 83306f25ae9SGregory Neil Shapiro if (stb->st_mode == ST_MODE_NOFILE) 83406f25ae9SGregory Neil Shapiro { 83506f25ae9SGregory Neil Shapiro # if HASLSTAT && BOGUS_O_EXCL 83606f25ae9SGregory Neil Shapiro /* only necessary if exclusive open follows symbolic links */ 83706f25ae9SGregory Neil Shapiro if (lstat(fn, stb) < 0 || stb->st_nlink != 1) 83806f25ae9SGregory Neil Shapiro return TRUE; 83906f25ae9SGregory Neil Shapiro # else /* HASLSTAT && BOGUS_O_EXCL */ 84006f25ae9SGregory Neil Shapiro return FALSE; 84106f25ae9SGregory Neil Shapiro # endif /* HASLSTAT && BOGUS_O_EXCL */ 84206f25ae9SGregory Neil Shapiro } 84306f25ae9SGregory Neil Shapiro if (fstat(fd, &sta) < 0) 84406f25ae9SGregory Neil Shapiro return TRUE; 84506f25ae9SGregory Neil Shapiro 84606f25ae9SGregory Neil Shapiro if (sta.st_nlink != stb->st_nlink || 84706f25ae9SGregory Neil Shapiro sta.st_dev != stb->st_dev || 84806f25ae9SGregory Neil Shapiro sta.st_ino != stb->st_ino || 84906f25ae9SGregory Neil Shapiro # if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ 85006f25ae9SGregory Neil Shapiro sta.st_gen != stb->st_gen || 85106f25ae9SGregory Neil Shapiro # endif /* HAS_ST_GEN && 0 */ 85206f25ae9SGregory Neil Shapiro sta.st_uid != stb->st_uid || 85306f25ae9SGregory Neil Shapiro sta.st_gid != stb->st_gid) 85406f25ae9SGregory Neil Shapiro { 85506f25ae9SGregory Neil Shapiro if (tTd(44, 8)) 85606f25ae9SGregory Neil Shapiro { 85706f25ae9SGregory Neil Shapiro dprintf("File changed after opening:\n"); 85806f25ae9SGregory Neil Shapiro dprintf(" nlink = %ld/%ld\n", 85906f25ae9SGregory Neil Shapiro (long) stb->st_nlink, (long) sta.st_nlink); 86006f25ae9SGregory Neil Shapiro dprintf(" dev = %ld/%ld\n", 86106f25ae9SGregory Neil Shapiro (long) stb->st_dev, (long) sta.st_dev); 86206f25ae9SGregory Neil Shapiro if (sizeof sta.st_ino > sizeof (long)) 86306f25ae9SGregory Neil Shapiro { 86406f25ae9SGregory Neil Shapiro dprintf(" ino = %s/", 86506f25ae9SGregory Neil Shapiro quad_to_string(stb->st_ino)); 86606f25ae9SGregory Neil Shapiro dprintf("%s\n", 86706f25ae9SGregory Neil Shapiro quad_to_string(sta.st_ino)); 86806f25ae9SGregory Neil Shapiro } 86906f25ae9SGregory Neil Shapiro else 87006f25ae9SGregory Neil Shapiro dprintf(" ino = %lu/%lu\n", 87106f25ae9SGregory Neil Shapiro (unsigned long) stb->st_ino, 87206f25ae9SGregory Neil Shapiro (unsigned long) sta.st_ino); 87306f25ae9SGregory Neil Shapiro # if HAS_ST_GEN 87406f25ae9SGregory Neil Shapiro dprintf(" gen = %ld/%ld\n", 87506f25ae9SGregory Neil Shapiro (long) stb->st_gen, (long) sta.st_gen); 87606f25ae9SGregory Neil Shapiro # endif /* HAS_ST_GEN */ 87706f25ae9SGregory Neil Shapiro dprintf(" uid = %ld/%ld\n", 87806f25ae9SGregory Neil Shapiro (long) stb->st_uid, (long) sta.st_uid); 87906f25ae9SGregory Neil Shapiro dprintf(" gid = %ld/%ld\n", 88006f25ae9SGregory Neil Shapiro (long) stb->st_gid, (long) sta.st_gid); 88106f25ae9SGregory Neil Shapiro } 88206f25ae9SGregory Neil Shapiro return TRUE; 88306f25ae9SGregory Neil Shapiro } 88406f25ae9SGregory Neil Shapiro 88506f25ae9SGregory Neil Shapiro return FALSE; 88606f25ae9SGregory Neil Shapiro } 88706f25ae9SGregory Neil Shapiro /* 88806f25ae9SGregory Neil Shapiro ** DFOPEN -- determined file open 88906f25ae9SGregory Neil Shapiro ** 89006f25ae9SGregory Neil Shapiro ** This routine has the semantics of open, except that it will 89106f25ae9SGregory Neil Shapiro ** keep trying a few times to make this happen. The idea is that 89206f25ae9SGregory Neil Shapiro ** on very loaded systems, we may run out of resources (inodes, 89306f25ae9SGregory Neil Shapiro ** whatever), so this tries to get around it. 89406f25ae9SGregory Neil Shapiro */ 89506f25ae9SGregory Neil Shapiro 89606f25ae9SGregory Neil Shapiro int 89706f25ae9SGregory Neil Shapiro dfopen(filename, omode, cmode, sff) 89806f25ae9SGregory Neil Shapiro char *filename; 89906f25ae9SGregory Neil Shapiro int omode; 90006f25ae9SGregory Neil Shapiro int cmode; 90106f25ae9SGregory Neil Shapiro long sff; 90206f25ae9SGregory Neil Shapiro { 90306f25ae9SGregory Neil Shapiro register int tries; 90406f25ae9SGregory Neil Shapiro int fd = -1; 90506f25ae9SGregory Neil Shapiro struct stat st; 90606f25ae9SGregory Neil Shapiro 90706f25ae9SGregory Neil Shapiro for (tries = 0; tries < 10; tries++) 90806f25ae9SGregory Neil Shapiro { 90906f25ae9SGregory Neil Shapiro (void) sleep((unsigned) (10 * tries)); 91006f25ae9SGregory Neil Shapiro errno = 0; 91106f25ae9SGregory Neil Shapiro fd = open(filename, omode, cmode); 91206f25ae9SGregory Neil Shapiro if (fd >= 0) 91306f25ae9SGregory Neil Shapiro break; 91406f25ae9SGregory Neil Shapiro switch (errno) 91506f25ae9SGregory Neil Shapiro { 91606f25ae9SGregory Neil Shapiro case ENFILE: /* system file table full */ 91706f25ae9SGregory Neil Shapiro case EINTR: /* interrupted syscall */ 91806f25ae9SGregory Neil Shapiro #ifdef ETXTBSY 91906f25ae9SGregory Neil Shapiro case ETXTBSY: /* Apollo: net file locked */ 92006f25ae9SGregory Neil Shapiro #endif /* ETXTBSY */ 92106f25ae9SGregory Neil Shapiro continue; 92206f25ae9SGregory Neil Shapiro } 92306f25ae9SGregory Neil Shapiro break; 92406f25ae9SGregory Neil Shapiro } 92506f25ae9SGregory Neil Shapiro if (!bitset(SFF_NOLOCK, sff) && 92606f25ae9SGregory Neil Shapiro fd >= 0 && 92706f25ae9SGregory Neil Shapiro fstat(fd, &st) >= 0 && 92806f25ae9SGregory Neil Shapiro S_ISREG(st.st_mode)) 92906f25ae9SGregory Neil Shapiro { 93006f25ae9SGregory Neil Shapiro int locktype; 93106f25ae9SGregory Neil Shapiro 93206f25ae9SGregory Neil Shapiro /* lock the file to avoid accidental conflicts */ 93306f25ae9SGregory Neil Shapiro if ((omode & O_ACCMODE) != O_RDONLY) 93406f25ae9SGregory Neil Shapiro locktype = LOCK_EX; 93506f25ae9SGregory Neil Shapiro else 93606f25ae9SGregory Neil Shapiro locktype = LOCK_SH; 93706f25ae9SGregory Neil Shapiro if (!lockfile(fd, filename, NULL, locktype)) 93806f25ae9SGregory Neil Shapiro { 93906f25ae9SGregory Neil Shapiro int save_errno = errno; 94006f25ae9SGregory Neil Shapiro 94106f25ae9SGregory Neil Shapiro (void) close(fd); 94206f25ae9SGregory Neil Shapiro fd = -1; 94306f25ae9SGregory Neil Shapiro errno = save_errno; 94406f25ae9SGregory Neil Shapiro } 94506f25ae9SGregory Neil Shapiro else 94606f25ae9SGregory Neil Shapiro errno = 0; 94706f25ae9SGregory Neil Shapiro } 94806f25ae9SGregory Neil Shapiro return fd; 94906f25ae9SGregory Neil Shapiro } 950