Postfix3.3.1
mbox_open.c
[詳解]
1 /*++
2 /* NAME
3 /* mbox_open 3
4 /* SUMMARY
5 /* mailbox access
6 /* SYNOPSIS
7 /* #include <mbox_open.h>
8 /*
9 /* typedef struct {
10 /* .in +4
11 /* /* public members... */
12 /* VSTREAM *fp;
13 /* .in -4
14 /* } MBOX;
15 /*
16 /* MBOX *mbox_open(path, flags, mode, st, user, group, lock_style,
17 /* def_dsn, why)
18 /* const char *path;
19 /* int flags;
20 /* mode_t mode;
21 /* struct stat *st;
22 /* uid_t user;
23 /* gid_t group;
24 /* int lock_style;
25 /* const char *def_dsn;
26 /* DSN_BUF *why;
27 /*
28 /* void mbox_release(mbox)
29 /* MBOX *mbox;
30 /*
31 /* const char *mbox_dsn(err, def_dsn)
32 /* int err;
33 /* const char *def_dsn;
34 /* DESCRIPTION
35 /* This module manages access to UNIX mailbox-style files.
36 /*
37 /* mbox_open() acquires exclusive access to the named file.
38 /* The \fBpath, flags, mode, st, user, group, why\fR arguments
39 /* are passed to the \fBsafe_open\fR() routine. Attempts to change
40 /* file ownership will succeed only if the process runs with
41 /* adequate effective privileges.
42 /* The \fBlock_style\fR argument specifies a lock style from
43 /* mbox_lock_mask(). Locks are applied to regular files only.
44 /* The result is a handle that must be destroyed by mbox_release().
45 /* The \fBdef_dsn\fR argument is given to mbox_dsn().
46 /*
47 /* mbox_release() releases the named mailbox. It is up to the
48 /* application to close the stream.
49 /*
50 /* mbox_dsn() translates an errno value to a mailbox related
51 /* enhanced status code.
52 /* .IP "EAGAIN, ESTALE"
53 /* These result in a 4.2.0 soft error (mailbox problem).
54 /* .IP ENOSPC
55 /* This results in a 4.3.0 soft error (mail system full).
56 /* .IP "EDQUOT, EFBIG"
57 /* These result in a 5.2.2 hard error (mailbox full).
58 /* .PP
59 /* All other errors are assigned the specified default error
60 /* code. Typically, one would specify 4.2.0 or 5.2.0.
61 /* DIAGNOSTICS
62 /* mbox_open() returns a null pointer in case of problems, and
63 /* sets errno to EAGAIN if someone else has exclusive access.
64 /* Other errors are likely to have a more permanent nature.
65 /* LICENSE
66 /* .ad
67 /* .fi
68 /* The Secure Mailer license must be distributed with this software.
69 /* AUTHOR(S)
70 /* Wietse Venema
71 /* IBM T.J. Watson Research
72 /* P.O. Box 704
73 /* Yorktown Heights, NY 10598, USA
74 /*--*/
75 
76 /* System library. */
77 
78 #include <sys_defs.h>
79 #include <sys/stat.h>
80 #include <errno.h>
81 
82 #ifndef EDQUOT
83 #define EDQUOT EFBIG
84 #endif
85 
86 /* Utility library. */
87 
88 #include <msg.h>
89 #include <vstream.h>
90 #include <vstring.h>
91 #include <safe_open.h>
92 #include <iostuff.h>
93 #include <mymalloc.h>
94 #include <warn_stat.h>
95 
96 /* Global library. */
97 
98 #include <dot_lockfile.h>
99 #include <deliver_flock.h>
100 #include <mbox_conf.h>
101 #include <mbox_open.h>
102 
103 /* mbox_open - open mailbox-style file for exclusive access */
104 
105 MBOX *mbox_open(const char *path, int flags, mode_t mode, struct stat * st,
106  uid_t chown_uid, gid_t chown_gid,
107  int lock_style, const char *def_dsn,
108  DSN_BUF *why)
109 {
110  struct stat local_statbuf;
111  MBOX *mp;
112  int locked = 0;
113  VSTREAM *fp;
114 
115  if (st == 0)
116  st = &local_statbuf;
117 
118  /*
119  * If this is a regular file, create a dotlock file. This locking method
120  * does not work well over NFS, but it is better than some alternatives.
121  * With NFS, creating files atomically is a problem, and a successful
122  * operation can fail with EEXIST.
123  *
124  * If filename.lock can't be created for reasons other than "file exists",
125  * issue only a warning if the application says it is non-fatal. This is
126  * for bass-awkward compatibility with existing installations that
127  * deliver to files in non-writable directories.
128  *
129  * We dot-lock the file before opening, so we must avoid doing silly things
130  * like dot-locking /dev/null. Fortunately, deliveries to non-mailbox
131  * files execute with recipient privileges, so we don't have to worry
132  * about creating dotlock files in places where the recipient would not
133  * be able to write.
134  *
135  * Note: we use stat() to follow symlinks, because safe_open() allows the
136  * target to be a root-owned symlink, and we don't want to create dotlock
137  * files for /dev/null or other non-file objects.
138  */
139  if ((lock_style & MBOX_DOT_LOCK)
140  && (stat(path, st) < 0 || S_ISREG(st->st_mode))) {
141  if (dot_lockfile(path, why->reason) == 0) {
142  locked |= MBOX_DOT_LOCK;
143  } else if (errno == EEXIST) {
144  dsb_status(why, mbox_dsn(EAGAIN, def_dsn));
145  return (0);
146  } else if (lock_style & MBOX_DOT_LOCK_MAY_FAIL) {
147  msg_warn("%s", vstring_str(why->reason));
148  } else {
149  dsb_status(why, mbox_dsn(errno, def_dsn));
150  return (0);
151  }
152  }
153 
154  /*
155  * Open or create the target file. In case of a privileged open, the
156  * privileged user may be attacked with hard/soft link tricks in an
157  * unsafe parent directory. In case of an unprivileged open, the mail
158  * system may be attacked by a malicious user-specified path, or the
159  * unprivileged user may be attacked with hard/soft link tricks in an
160  * unsafe parent directory. Open non-blocking to fend off attacks
161  * involving non-file targets.
162  */
163  if ((fp = safe_open(path, flags | O_NONBLOCK, mode, st,
164  chown_uid, chown_gid, why->reason)) == 0) {
165  dsb_status(why, mbox_dsn(errno, def_dsn));
166  if (locked & MBOX_DOT_LOCK)
167  dot_unlockfile(path);
168  return (0);
169  }
171 
172  /*
173  * If this is a regular file, acquire kernel locks. flock() locks are not
174  * intended to work across a network; fcntl() locks are supposed to work
175  * over NFS, but in the real world, NFS lock daemons often have serious
176  * problems.
177  */
178 #define HUNKY_DORY(lock_mask, myflock_style) ((lock_style & (lock_mask)) == 0 \
179  || deliver_flock(vstream_fileno(fp), (myflock_style), why->reason) == 0)
180 
181  if (S_ISREG(st->st_mode)) {
184  locked |= lock_style;
185  } else {
186  dsb_status(why, mbox_dsn(errno, def_dsn));
187  if (locked & MBOX_DOT_LOCK)
188  dot_unlockfile(path);
189  vstream_fclose(fp);
190  return (0);
191  }
192  }
193 
194  /*
195  * Sanity check: reportedly, GNU POP3D creates a new mailbox file and
196  * deletes the old one. This does not play well with software that opens
197  * the mailbox first and then locks it, such as software that uses FCNTL
198  * or FLOCK locks on open file descriptors (some UNIX systems don't use
199  * dotlock files).
200  *
201  * To detect that GNU POP3D deletes the mailbox file we look at the target
202  * file hard-link count. Note that safe_open() guarantees a hard-link
203  * count of 1, so any change in this count is a sign of trouble.
204  */
205  if (S_ISREG(st->st_mode)
206  && (fstat(vstream_fileno(fp), st) < 0 || st->st_nlink != 1)) {
207  vstring_sprintf(why->reason, "target file status changed unexpectedly");
208  dsb_status(why, mbox_dsn(EAGAIN, def_dsn));
209  msg_warn("%s: file status changed unexpectedly", path);
210  if (locked & MBOX_DOT_LOCK)
211  dot_unlockfile(path);
212  vstream_fclose(fp);
213  return (0);
214  }
215  mp = (MBOX *) mymalloc(sizeof(*mp));
216  mp->path = mystrdup(path);
217  mp->fp = fp;
218  mp->locked = locked;
219  return (mp);
220 }
221 
222 /* mbox_release - release mailbox exclusive access */
223 
225 {
226 
227  /*
228  * Unfortunately we can't close the stream, because on some file systems
229  * (AFS), the only way to find out if a file was written successfully is
230  * to close it, and therefore the close() operation is in the mail_copy()
231  * routine. If we really insist on owning the vstream member, then we
232  * should export appropriate methods that mail_copy() can use in order to
233  * manipulate a message stream.
234  */
235  if (mp->locked & MBOX_DOT_LOCK)
236  dot_unlockfile(mp->path);
237  myfree(mp->path);
238  myfree((void *) mp);
239 }
240 
241 /* mbox_dsn - map errno value to mailbox-related DSN detail */
242 
243 const char *mbox_dsn(int err, const char *def_dsn)
244 {
245 #define TRY_AGAIN_ERROR(e) \
246  (e == EAGAIN || e == ESTALE)
247 #define SYSTEM_FULL_ERROR(e) \
248  (e == ENOSPC)
249 #define MBOX_FULL_ERROR(e) \
250  (e == EDQUOT || e == EFBIG)
251 
252  return (TRY_AGAIN_ERROR(err) ? "4.2.0" :
253  SYSTEM_FULL_ERROR(err) ? "4.3.0" :
254  MBOX_FULL_ERROR(err) ? "5.2.2" :
255  def_dsn);
256 }
const char * mbox_dsn(int err, const char *def_dsn)
Definition: mbox_open.c:243
void myfree(void *ptr)
Definition: mymalloc.c:207
char * mystrdup(const char *str)
Definition: mymalloc.c:225
#define vstring_str(vp)
Definition: vstring.h:71
#define stat(p, s)
Definition: warn_stat.h:18
#define MBOX_DOT_LOCK_MAY_FAIL
Definition: mbox_conf.h:25
#define TRY_AGAIN_ERROR(e)
void dot_unlockfile(const char *path)
Definition: dot_lockfile.c:133
#define HUNKY_DORY(lock_mask, myflock_style)
#define MBOX_FULL_ERROR(e)
#define MBOX_DOT_LOCK
Definition: mbox_conf.h:24
#define MBOX_FLOCK_LOCK
Definition: mbox_conf.h:22
int dot_lockfile(const char *path, VSTRING *why)
Definition: dot_lockfile.c:80
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
Definition: mbox_open.h:29
void msg_warn(const char *fmt,...)
Definition: msg.c:215
#define MBOX_FCNTL_LOCK
Definition: mbox_conf.h:23
void mbox_release(MBOX *mp)
Definition: mbox_open.c:224
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
VSTRING * reason
Definition: dsn_buf.h:37
DSN_BUF * dsb_status(DSN_BUF *dsb, const char *status)
Definition: dsn_buf.c:320
VSTREAM * safe_open(const char *path, int flags, mode_t mode, struct stat *st, uid_t user, gid_t group, VSTRING *why)
Definition: safe_open.c:243
#define MYFLOCK_STYLE_FLOCK
Definition: myflock.h:22
#define vstream_fileno(vp)
Definition: vstream.h:115
#define CLOSE_ON_EXEC
Definition: iostuff.h:51
int locked
Definition: mbox_open.h:32
MBOX * mbox_open(const char *path, int flags, mode_t mode, struct stat *st, uid_t chown_uid, gid_t chown_gid, int lock_style, const char *def_dsn, DSN_BUF *why)
Definition: mbox_open.c:105
#define MYFLOCK_STYLE_FCNTL
Definition: myflock.h:23
int close_on_exec(int fd, int on)
Definition: close_on_exec.c:49
#define SYSTEM_FULL_ERROR(e)
char * path
Definition: mbox_open.h:30
#define fstat(f, s)
Definition: warn_stat.h:20
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150