File: foldinfo.c

package info (click to toggle)
procmail 3.22-27
  • links: PTS
  • area: main
  • in suites: bookworm
  • size: 2,128 kB
  • sloc: ansic: 9,888; sh: 1,957; makefile: 136
file content (312 lines) | stat: -rw-r--r-- 11,022 bytes parent folder | download | duplicates (13)
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;
}