Postfix3.3.1
dotforward.c
[詳解]
1 /*++
2 /* NAME
3 /* dotforward 3
4 /* SUMMARY
5 /* $HOME/.forward file expansion
6 /* SYNOPSIS
7 /* #include "local.h"
8 /*
9 /* int deliver_dotforward(state, usr_attr, statusp)
10 /* LOCAL_STATE state;
11 /* USER_ATTR usr_attr;
12 /* int *statusp;
13 /* DESCRIPTION
14 /* deliver_dotforward() delivers a message to the destinations
15 /* listed in a recipient's .forward file(s) as specified through
16 /* the forward_path configuration parameter. The result is
17 /* zero when no acceptable .forward file was found, or when
18 /* a recipient is listed in her own .forward file. Expansions
19 /* are scrutinized with the forward_expansion_filter parameter.
20 /*
21 /* Arguments:
22 /* .IP state
23 /* Message delivery attributes (sender, recipient etc.).
24 /* Attributes describing alias, include or forward expansion.
25 /* A table with the results from expanding aliases or lists.
26 /* A table with delivered-to: addresses taken from the message.
27 /* .IP usr_attr
28 /* Attributes describing user rights and environment.
29 /* .IP statusp
30 /* Message delivery status. See below.
31 /* DIAGNOSTICS
32 /* Fatal errors: out of memory. Warnings: bad $HOME/.forward
33 /* file type, permissions or ownership. The message delivery
34 /* status is non-zero when delivery should be tried again.
35 /* SEE ALSO
36 /* include(3) include file processor.
37 /* LICENSE
38 /* .ad
39 /* .fi
40 /* The Secure Mailer license must be distributed with this software.
41 /* AUTHOR(S)
42 /* Wietse Venema
43 /* IBM T.J. Watson Research
44 /* P.O. Box 704
45 /* Yorktown Heights, NY 10598, USA
46 /*--*/
47 
48 /* System library. */
49 
50 #include <sys_defs.h>
51 #include <sys/stat.h>
52 #include <unistd.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #ifdef USE_PATHS_H
56 #include <paths.h>
57 #endif
58 #include <string.h>
59 
60 /* Utility library. */
61 
62 #include <msg.h>
63 #include <vstring.h>
64 #include <vstream.h>
65 #include <htable.h>
66 #include <open_as.h>
67 #include <lstat_as.h>
68 #include <iostuff.h>
69 #include <stringops.h>
70 #include <mymalloc.h>
71 #include <mac_expand.h>
72 
73 /* Global library. */
74 
75 #include <mypwd.h>
76 #include <bounce.h>
77 #include <defer.h>
78 #include <been_here.h>
79 #include <mail_params.h>
80 #include <mail_conf.h>
81 #include <ext_prop.h>
82 #include <sent.h>
83 #include <dsn_mask.h>
84 #include <trace.h>
85 
86 /* Application-specific. */
87 
88 #include "local.h"
89 
90 #define NO 0
91 #define YES 1
92 
93 /* deliver_dotforward - expand contents of .forward file */
94 
95 int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
96 {
97  const char *myname = "deliver_dotforward";
98  struct stat st;
99  VSTRING *path;
100  struct mypasswd *mypwd;
101  int fd;
102  VSTREAM *fp;
103  int status;
104  int forward_found = NO;
105  int lookup_status;
106  int addr_count;
107  char *saved_forward_path;
108  char *lhs;
109  char *next;
110  int expand_status;
111  int saved_notify;
112 
113  /*
114  * Make verbose logging easier to understand.
115  */
116  state.level++;
117  if (msg_verbose)
118  MSG_LOG_STATE(myname, state);
119 
120  /*
121  * Skip this module if per-user forwarding is disabled.
122  */
123  if (*var_forward_path == 0)
124  return (NO);
125 
126  /*
127  * Skip non-existing users. The mailbox delivery routine will catch the
128  * error.
129  */
130  if ((errno = mypwnam_err(state.msg_attr.user, &mypwd)) != 0) {
131  msg_warn("error looking up passwd info for %s: %m",
132  state.msg_attr.user);
133  dsb_simple(state.msg_attr.why, "4.0.0", "user lookup error");
134  *statusp = defer_append(BOUNCE_FLAGS(state.request),
135  BOUNCE_ATTR(state.msg_attr));
136  return (YES);
137  }
138  if (mypwd == 0)
139  return (NO);
140 
141  /*
142  * From here on no early returns or we have a memory leak.
143  */
144 
145  /*
146  * EXTERNAL LOOP CONTROL
147  *
148  * Set the delivered message attribute to the recipient, so that this
149  * message will list the correct forwarding address.
150  */
151  if (var_frozen_delivered == 0)
152  state.msg_attr.delivered = state.msg_attr.rcpt.address;
153 
154  /*
155  * DELIVERY RIGHTS
156  *
157  * Do not inherit rights from the .forward file owner. Instead, use the
158  * recipient's rights, and insist that the .forward file is owned by the
159  * recipient. This is a small but significant difference. Use the
160  * recipient's rights for all /file and |command deliveries, and pass on
161  * these rights to command/file destinations in included files. When
162  * these are the rights of root, the /file and |command delivery routines
163  * will use unprivileged default rights instead. Better safe than sorry.
164  */
165  SET_USER_ATTR(usr_attr, mypwd, state.level);
166 
167  /*
168  * DELIVERY POLICY
169  *
170  * Update the expansion type attribute so that we can decide if deliveries
171  * to |command and /file/name are allowed at all.
172  */
174 
175  /*
176  * WHERE TO REPORT DELIVERY PROBLEMS
177  *
178  * Set the owner attribute so that 1) include files won't set the sender to
179  * be this user and 2) mail forwarded to other local users will be
180  * resubmitted as a new queue file.
181  */
182  state.msg_attr.owner = state.msg_attr.user;
183 
184  /*
185  * Search the forward_path for an existing forward file.
186  *
187  * If unmatched extensions should never be propagated, or if a forward file
188  * name includes the address extension, don't propagate the extension to
189  * the recipient addresses.
190  */
191  status = 0;
192  path = vstring_alloc(100);
193  saved_forward_path = mystrdup(var_forward_path);
194  next = saved_forward_path;
195  lookup_status = -1;
196 
197  while ((lhs = mystrtok(&next, CHARS_COMMA_SP)) != 0) {
198  expand_status = local_expand(path, lhs, &state, &usr_attr,
200  if ((expand_status & (MAC_PARSE_ERROR | MAC_PARSE_UNDEF)) == 0) {
201  lookup_status =
202  lstat_as(STR(path), &st, usr_attr.uid, usr_attr.gid);
203  if (msg_verbose)
204  msg_info("%s: path %s expand_status %d look_status %d", myname,
205  STR(path), expand_status, lookup_status);
206  if (lookup_status >= 0) {
207  if ((expand_status & LOCAL_EXP_EXTENSION_MATCHED) != 0
209  state.msg_attr.unmatched = 0;
210  break;
211  }
212  }
213  }
214 
215  /*
216  * Process the forward file.
217  *
218  * Assume that usernames do not have file system meta characters. Open the
219  * .forward file as the user. Ignore files that aren't regular files,
220  * files that are owned by the wrong user, or files that have world write
221  * permission enabled.
222  *
223  * DUPLICATE/LOOP ELIMINATION
224  *
225  * If this user includes (an alias of) herself in her own .forward file,
226  * deliver to the user instead.
227  */
228  if (lookup_status >= 0) {
229 
230  /*
231  * Don't expand a verify-only request.
232  */
233  if (state.request->flags & DEL_REQ_FLAG_MTA_VRFY) {
234  dsb_simple(state.msg_attr.why, "2.0.0",
235  "forward via file: %s", STR(path));
236  *statusp = sent(BOUNCE_FLAGS(state.request),
237  SENT_ATTR(state.msg_attr));
238  forward_found = YES;
239  } else if (been_here(state.dup_filter, "forward %s", STR(path)) == 0) {
240  state.msg_attr.exp_from = state.msg_attr.local;
241  if (S_ISREG(st.st_mode) == 0) {
242  msg_warn("file %s is not a regular file", STR(path));
243  } else if (st.st_uid != 0 && st.st_uid != usr_attr.uid) {
244  msg_warn("file %s has bad owner uid %ld",
245  STR(path), (long) st.st_uid);
246  } else if (st.st_mode & 002) {
247  msg_warn("file %s is world writable", STR(path));
248  } else if ((fd = open_as(STR(path), O_RDONLY, 0, usr_attr.uid, usr_attr.gid)) < 0) {
249  msg_warn("cannot open file %s: %m", STR(path));
250  } else {
251 
252  /*
253  * XXX DSN. When delivering to an alias (i.e. the envelope
254  * sender address is not replaced) any ENVID, RET, or ORCPT
255  * parameters are propagated to all forwarding addresses
256  * associated with that alias. The NOTIFY parameter is
257  * propagated to the forwarding addresses, except that any
258  * SUCCESS keyword is removed.
259  */
261  addr_count = 0;
262  fp = vstream_fdopen(fd, O_RDONLY);
263  saved_notify = state.msg_attr.rcpt.dsn_notify;
264  state.msg_attr.rcpt.dsn_notify =
265  (saved_notify == DSN_NOTIFY_SUCCESS ?
266  DSN_NOTIFY_NEVER : saved_notify & ~DSN_NOTIFY_SUCCESS);
267  status = deliver_token_stream(state, usr_attr, fp, &addr_count);
268  if (vstream_fclose(fp))
269  msg_warn("close file %s: %m", STR(path));
270  if (addr_count > 0) {
271  forward_found = YES;
272  been_here(state.dup_filter, "forward-done %s", STR(path));
273 
274  /*
275  * XXX DSN. When delivering to an alias (i.e. the
276  * envelope sender address is not replaced) and the
277  * original NOTIFY parameter for the alias contained the
278  * SUCCESS keyword, an "expanded" DSN is issued for the
279  * alias.
280  */
281  if (status == 0 && (saved_notify & DSN_NOTIFY_SUCCESS)) {
282  state.msg_attr.rcpt.dsn_notify = saved_notify;
283  dsb_update(state.msg_attr.why, "2.0.0", "expanded",
285  "alias expanded");
287  SENT_ATTR(state.msg_attr));
288  }
289  }
290  }
291  } else if (been_here_check(state.dup_filter, "forward-done %s", STR(path)) != 0)
292  forward_found = YES; /* else we're recursive */
293  }
294 
295  /*
296  * Clean up.
297  */
298  vstring_free(path);
299  myfree(saved_forward_path);
300  mypwfree(mypwd);
301 
302  *statusp = status;
303  return (forward_found);
304 }
int msg_verbose
Definition: msg.c:177
#define SENT_ATTR(attr)
Definition: local.h:142
char * exp_from
Definition: local.h:90
int mypwnam_err(const char *name, struct mypasswd **result)
Definition: mypwd.c:242
#define BOUNCE_ATTR(attr)
Definition: local.h:134
void myfree(void *ptr)
Definition: mymalloc.c:207
#define EXT_PROP_FORWARD
Definition: ext_prop.h:20
char * mystrdup(const char *str)
Definition: mymalloc.c:225
const char * address
DSN_BUF * dsb_update(DSN_BUF *dsb, const char *status, const char *action, const char *mtype, const char *mname, const char *dtype, const char *dtext, const char *format,...)
Definition: dsn_buf.c:239
#define stat(p, s)
Definition: warn_stat.h:18
int trace_append(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, DSN *dsn)
Definition: trace.c:106
Definition: mypwd.h:17
int open_as(const char *path, int flags, int mode, uid_t euid, gid_t egid)
Definition: open_as.c:48
char * local
Definition: local.h:81
char * var_forward_path
Definition: local.c:668
#define BOUNCE_FLAG_NONE
Definition: bounce.h:60
#define DSB_SKIP_RMTA
Definition: dsn_buf.h:42
char * mystrtok(char **src, const char *sep)
Definition: mystrtok.c:54
#define DSN_NOTIFY_NEVER
Definition: dsn_mask.h:43
DELIVER_REQUEST * request
Definition: local.h:113
#define EXPAND_TYPE_FWD
Definition: local.h:100
#define BOUNCE_FLAGS(request)
#define DSB_SKIP_REPLY
Definition: dsn_buf.h:46
int exp_type
Definition: local.h:89
BH_TABLE * dup_filter
Definition: local.h:111
char * unmatched
Definition: local.h:84
int been_here(BH_TABLE *dup_filter, const char *fmt,...)
Definition: been_here.c:124
int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
Definition: dotforward.c:95
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
char * user
Definition: local.h:82
DELIVER_ATTR msg_attr
Definition: local.h:110
#define STR(x)
Definition: anvil.c:518
void msg_warn(const char *fmt,...)
Definition: msg.c:215
#define NO
Definition: dotforward.c:90
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
const char * owner
Definition: local.h:85
#define LOCAL_EXP_EXTENSION_MATCHED
Definition: local.h:225
bool var_frozen_delivered
Definition: local.c:678
DSN_BUF * dsb_simple(DSN_BUF *dsb, const char *status, const char *format,...)
Definition: dsn_buf.c:275
int been_here_check(BH_TABLE *dup_filter, const char *fmt,...)
Definition: been_here.c:193
#define CHARS_COMMA_SP
Definition: sys_defs.h:1761
gid_t gid
Definition: local.h:37
DSN_BUF * why
Definition: local.h:92
void mypwfree(struct mypasswd *mypwd)
Definition: mypwd.c:292
int lstat_as(const char *path, struct stat *st, uid_t euid, gid_t egid)
Definition: lstat_as.c:51
#define SET_USER_ATTR(usr_attr, pwd, level)
Definition: local.h:53
const char * delivered
Definition: local.h:86
int level
Definition: local.h:109
#define DSN_NOTIFY_SUCCESS
Definition: dsn_mask.h:44
int local_expand(VSTRING *, const char *, LOCAL_STATE *, USER_ATTR *, const char *)
Definition: local_expand.c:166
uid_t uid
Definition: local.h:36
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define MAC_PARSE_UNDEF
Definition: mac_parse.h:28
#define YES
Definition: dotforward.c:91
int defer_append(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, DSN *dsn)
Definition: defer.c:187
#define CLOSE_ON_EXEC
Definition: iostuff.h:51
RECIPIENT rcpt
Definition: local.h:79
char * var_fwd_exp_filter
Definition: local.c:670
int local_ext_prop_mask
Definition: local.c:684
int sent(int flags, const char *id, MSG_STATS *stats, RECIPIENT *recipient, const char *relay, DSN *dsn)
Definition: sent.c:95
#define MSG_LOG_STATE(m, p)
Definition: local.h:150
int close_on_exec(int fd, int on)
Definition: close_on_exec.c:49
#define DEL_REQ_FLAG_MTA_VRFY
VSTREAM * vstream_fdopen(int fd, int flags)
Definition: vstream.c:1204
int deliver_token_stream(LOCAL_STATE, USER_ATTR, VSTREAM *, int *)
Definition: token.c:198
#define MAC_PARSE_ERROR
Definition: mac_parse.h:27
void msg_info(const char *fmt,...)
Definition: msg.c:199