1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
|
/************************************************************************
* Routines that deal with understanding the folder types *
* *
* Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands *
* Copyright (c) 1999-2001, Philip Guenther, The United States *
* of America *
* #include "../README" *
************************************************************************/
#ifdef RCS
static /*const*/char rcsid[]=
"$Id: foldinfo.c,v 1.11 2001/08/04 07:07:42 guenther Exp $";
#endif
#include "procmail.h"
#include "misc.h"
#include "lastdirsep.h"
#include "robust.h"
#include "exopen.h"
#include "goodies.h"
#include "locking.h"
#include "foldinfo.h"
static const char
maildirtmp[]=MAILDIRtmp,maildircur[]=MAILDIRcur;
const char
maildirnew[]=MAILDIRnew;
int accspooldir; /* can we write to the spool directory? */
/* determine the requested type, chopping off the type specifier and any
extra trailing slashes */
static int folderparse P((void))
{ char*chp;int type;
type=ft_FILE;chp=strchr(buf,'\0');
switch(chp-buf)
{ case 2:
if(chp[-1]==*MCDIRSEP_) /* "//" or "x/" */
chp--,type=ft_MAILDIR;
case 0:case 1: /* "" or "x" */
goto ret;
} /* Okay, put chp right before the type specifier */
if(chp[-1]==chCURDIR&&chp[-2]==*MCDIRSEP_) /* foo/. */
chp-=2,type=ft_MH;
else if(chp[-1]==*MCDIRSEP_) /* foo/ */
chp--,type=ft_MAILDIR;
else /* no specifier */
goto ret;
while(chp-1>buf&&strchr(dirsep,chp[-1])) /* chop extra /s */
chp--;
ret:
*chp='\0';
return type;
}
int rnmbogus(name,stbuf,i,dolog)const char*const name; /* move a file */
const struct stat*const stbuf;const int i,dolog; /* out of the way */
{ static const char renbogus[]="Renamed bogus \"%s\" into \"%s\"",
renfbogus[]="Couldn't rename bogus \"%s\" into \"%s\"",
bogusprefix[]=BOGUSprefix;char*p;
p=strchr(strcpy(strcpy(buf2 i,bogusprefix) STRLEN(bogusprefix),
getenv(lgname)),'\0');
*p ='.';ultoan((unsigned long)stbuf->st_ino,p); /* i-node numbered */
if(dolog)
{ nlog("Renaming bogus mailbox \"");elog(name);elog("\" info");
logqnl(buf2);
}
if(rename(name,buf2)) /* try and move it out of the way */
{ syslog(LOG_ALERT,renfbogus,name,buf2); /* danger! danger! */
return 1;
}
syslog(LOG_CRIT,renbogus,name,buf2);
return 0;
}
/* return the named object's mode, making it a directory if it doesn't exist
* and renaming it out of the way if it's not _just_right_ and we're being
* paranoid */
static mode_t trymkdir(dir,paranoid,i)const char*const dir;
const int paranoid,i;
{ struct stat stbuf;int tries=3-1; /* minus one for post-decrement */
do
{ if(!(paranoid?lstat(dir,&stbuf):stat(dir,&stbuf))) /* does it exist? */
{ if(!paranoid|| /* is it okay? If we're trusting it is */
(S_ISDIR(stbuf.st_mode)&& /* else it must be a directory */
(stbuf.st_uid==uid|| /* and have the correct owner */
!stbuf.st_uid&&!chown(dir,uid,sgid)))) /* or be safely fixable */
return stbuf.st_mode; /* bingo! */
else if(rnmbogus(dir,&stbuf,i,1)) /* try and move it out of the way */
break; /* couldn't! */
}
else if(errno!=ENOENT) /* something more fundamental went wrong */
break;
else if(!mkdir(dir,NORMdirperm)) /* it's not there, can we make it? */
{ if(!paranoid) /* do we need to double check the permissions? */
return S_IFDIR|NORMdirperm&~cumask; /* nope */
tries ; /* the mkdir succeeded, so take another shot */
}
}while(tries-->0);
return 0;
}
static int mkmaildir(buffer,chp,paranoid)char*const buffer,*const chp;
const int paranoid;
{ mode_t mode;int i;
if(paranoid)
memcpy(buf2,buffer,i=chp-buffer 1),buf2[i-1]= *MCDIRSEP_,buf2[i]='\0';
return
(strcpy(chp,maildirnew),mode=trymkdir(buffer,paranoid,i),S_ISDIR(mode))&&
(strcpy(chp,maildircur),mode=trymkdir(buffer,paranoid,i),S_ISDIR(mode))&&
(strcpy(chp,maildirtmp),mode=trymkdir(buffer,paranoid,i),S_ISDIR(mode));
} /* leave tmp in buf on success */
int foldertype(type,forcedir,modep,paranoid)int type,forcedir;
mode_t*const modep;struct stat*const paranoid;
{ struct stat stbuf;mode_t mode;int i;char*chp;
if(!type)
type=folderparse();
switch(type)
{ case ft_MAILDIR:i=MAILDIRLEN;break;
case ft_MH:i=0;break;
case ft_FILE:
i=0; /* resolve the ambiguity */
if(!forcedir)
{ if(paranoid?lstat(buf,&stbuf):stat(buf,&stbuf))
{ if(paranoid)
{ type=ft_NOTYET;
goto ret;
}
goto newfile;
}
else if(mode=stbuf.st_mode,!S_ISDIR(mode))
goto file;
}
type=ft_DIR;
break;
default: /* "this cannot happen" */
nlog("Internal error: improper type (");
ltstr(0,type,buf2);elog(buf2);
elog(") passed to foldertype for folder ");logqnl(buf);
Terminate();
}
chp=strchr(buf,'\0');
if((chp-buf) UNIQnamelen 1 i>linebuf)
{ type=ft_TOOLONG;
goto ret;
}
if(type==ft_DIR&&!forcedir) /* we've already checked this case */
goto done;
if(paranoid)
memcpy(buf2,buf,i=lastdirsep(buf)-buf),buf2[i]='\0';
mode=trymkdir(buf,paranoid!=0,i);
if(!S_ISDIR(mode)||(type==ft_MAILDIR&&
(forcedir=1,!mkmaildir(buf,chp,paranoid!=0))))
{ nlog("Unable to treat as directory");logqnl(buf); /* we can't make it */
if(forcedir) /* fallback or give up? */
{ *chp='\0';skipped(buf);type=ft_CANTCREATE;
goto ret;
}
if(!mode)
newfile:mode=S_IFREG|NORMperm&~cumask;
file:type=ft_FILE;
}
done:
if(paranoid)
*paranoid=stbuf;
else
*modep=mode;
ret:
return type;
}
/* lifted out of main() to reduce main()'s size */
int screenmailbox(chp,egid,Deliverymode)
char*chp;const gid_t egid;const int Deliverymode;
{ char ch;struct stat stbuf;int basetype,type;
/*
* do we need sgidness to access the mail-spool directory/files?
*/
accspooldir=3; /* assume we can write to the spool directory and */
sgid=gid; /* that we don't need to setgid() to create a lockfile */
strcpy(buf,chp);
basetype=folderparse(); /* strip off the type */
if(buf[0]=='\0') /* don't even bother with "" */
return 0;
ch= *(chp=lastdirsep(buf));
if(chp>buf)
*chp='\0'; /* strip off the filename */
if(!stat(buf,&stbuf))
{ unsigned wwsdir;
accspooldir=(wwsdir= /* world writable spool dir? */
((S_IWGRP|S_IXGRP|S_IWOTH|S_IXOTH)&stbuf.st_mode)==
(S_IWGRP|S_IXGRP|S_IWOTH|S_IXOTH)
<<1| /* note it in bit 1 */
uid==stbuf.st_uid); /* we own the spool dir, note it in bit 0 */
if((CAN_toggle_sgid||accspooldir)&&privileged)
privileged=priv_DONTNEED; /* we don't need root to setgid */
if(uid!=stbuf.st_uid&& /* we don't own the spool directory */
(stbuf.st_mode&S_ISGID||!wwsdir)) /* it's not world writable */
{ if(stbuf.st_gid==egid) /* but we have setgid privs */
doumask(GROUPW_UMASK); /* make it group-writable */
goto keepgid;
}
else if(stbuf.st_mode&S_ISGID)
keepgid: /* keep the gid from the parent directory */
if((sgid=stbuf.st_gid)!=egid&& /* we were started nosgid, */
setgid(sgid)) /* but we might need it */
checkroot('g',(unsigned long)sgid);
}
else /* panic, mail-spool directory not available */
{ setids();mkdir(buf,NORMdirperm); /* try creating the last member */
}
*chp=ch;
/*
* check if the default-mailbox-lockfile is owned by the
* recipient, if not, mark it for further investigation, it
* might need to be removed
*/
chp=strchr(buf,'\0')-1;
for(;;) /* what type of folder is this? */
{ type=foldertype(basetype,0,0,&stbuf);
if(type==ft_NOTYET)
{ if(errno!=EACCES||(setids(),lstat(buf,&stbuf)))
goto nobox;
}
else if(!ft_checkcloser(type))
{ setids();
if(type<0)
goto fishy;
goto nl; /* no lock needed */
}
/*
* check if the original/default mailbox of the recipient
* exists, if it does, perform some security checks on it
* (check if it's a regular file, check if it's owned by
* the recipient), if something is wrong try and move the
* bogus mailbox out of the way, create the
* original/default mailbox file, and chown it to
* the recipient
*/
;{ int checkiter=1;
for(;;)
{ if(stbuf.st_uid!=uid|| /* recipient not owner */
!(stbuf.st_mode&S_IWUSR)|| /* recipient can write? */
S_ISLNK(stbuf.st_mode)|| /* no symbolic links */
(S_ISDIR(stbuf.st_mode)? /* directories, yes, hardlinks */
!(stbuf.st_mode&S_IXUSR):stbuf.st_nlink!=1)) /* no */
/*
* If another procmail is about to create the new
* mailbox, and has just made the link, st_nlink==2
*/
if(checkiter--) /* maybe it was a race condition */
suspend(); /* close eyes, and hope it improves */
else /* can't deliver to this contraption */
{ int i=lastdirsep(buf)-buf;
memcpy(buf2,buf,i);buf2[i]='\0';
if(rnmbogus(buf,&stbuf,i,1))
goto fishy;
goto nobox;
}
else
break;
if(lstat(buf,&stbuf))
goto nobox;
} /* SysV type autoforwarding? */
if(Deliverymode&&(stbuf.st_mode&S_ISUID||
!S_ISDIR(stbuf.st_mode)&&stbuf.st_mode&S_ISGID))
{ nlog("Autoforwarding mailbox found\n");
exit(EX_NOUSER);
}
else
{ if(!(stbuf.st_mode&OVERRIDE_MASK)&&
stbuf.st_mode&cumask&
(accspooldir?~(mode_t)0:~(S_IRGRP|S_IWGRP))) /* hold back */
{ static const char enfperm[]=
"Enforcing stricter permissions on";
nlog(enfperm);logqnl(buf);
syslog(LOG_NOTICE,slogstr,enfperm,buf);setids();
chmod(buf,stbuf.st_mode&=~cumask);
}
break; /* everything is just fine */
}
}
nobox:
if(!(accspooldir&1)) /* recipient does not own the spool dir */
{ if(!xcreat(buf,NORMperm,(time_t*)0,doCHOWN|doCHECK)) /* create */
break; /* mailbox... yes we could, fine, proceed */
if(!lstat(buf,&stbuf)) /* anything in the way? */
continue; /* check if it could be valid */
}
setids(); /* try some magic */
if(!xcreat(buf,NORMperm,(time_t*)0,doCHECK)) /* try again */
break;
if(lstat(buf,&stbuf)) /* nothing in the way? */
fishy:
{ nlog("Couldn't create");logqnl(buf);
return 0;
}
}
if(!S_ISDIR(stbuf.st_mode))
{ int isgrpwrite=stbuf.st_mode&S_IWGRP;
strcpy(chp=strchr(buf,'\0'),lockext);
defdeflock=tstrdup(buf);
if(!isgrpwrite&&!lstat(defdeflock,&stbuf)&&stbuf.st_uid!=uid&&
stbuf.st_uid!=ROOT_uid)
{ int i=lastdirsep(buf)-buf;
memcpy(buf2,buf,i);buf2[i]='\0'; /* try & rename bogus lockfile */
rnmbogus(defdeflock,&stbuf,i,0); /* out of the way */
}
*chp='\0';
}
else
nl: defdeflock=empty; /* no lock needed */
return 1;
}
|