Postfix3.3.1
recipient.c
[詳解]
1 /*++
2 /* NAME
3 /* recipient 3
4 /* SUMMARY
5 /* deliver to one local recipient
6 /* SYNOPSIS
7 /* #include "local.h"
8 /*
9 /* int deliver_recipient(state, usr_attr)
10 /* LOCAL_STATE state;
11 /* USER_ATTR *usr_attr;
12 /* DESCRIPTION
13 /* deliver_recipient() delivers a message to a local recipient.
14 /* It is called initially when the queue manager requests
15 /* delivery to a local recipient, and is called recursively
16 /* when an alias or forward file expands to a local recipient.
17 /*
18 /* When called recursively with, for example, a result from alias
19 /* or forward file expansion, aliases are expanded immediately,
20 /* but mail for non-alias destinations is submitted as a new
21 /* message, so that each recipient has a dedicated queue file
22 /* message delivery status record (in a shared queue file).
23 /*
24 /* When the \fIrecipient_delimiter\fR configuration parameter
25 /* is set, it is used to separate cookies off recipient names.
26 /* A common setting is to have "recipient_delimiter = +"
27 /* so that mail for \fIuser+foo\fR is delivered to \fIuser\fR,
28 /* with a "Delivered-To: user+foo@domain" header line.
29 /*
30 /* Arguments:
31 /* .IP state
32 /* The attributes that specify the message, sender, and more.
33 /* Attributes describing alias, include or forward expansion.
34 /* A table with the results from expanding aliases or lists.
35 /* A table with delivered-to: addresses taken from the message.
36 /* .IP usr_attr
37 /* Attributes describing user rights and environment.
38 /* DIAGNOSTICS
39 /* deliver_recipient() returns non-zero when delivery should be
40 /* tried again.
41 /* BUGS
42 /* Mutually-recursive aliases or $HOME/.forward files aren't
43 /* detected when they could be. The resulting mail forwarding loop
44 /* is broken by the use of the Delivered-To: message header.
45 /* SEE ALSO
46 /* alias(3) delivery to aliases
47 /* mailbox(3) delivery to mailbox
48 /* dotforward(3) delivery to destinations in .forward file
49 /* LICENSE
50 /* .ad
51 /* .fi
52 /* The Secure Mailer license must be distributed with this software.
53 /* AUTHOR(S)
54 /* Wietse Venema
55 /* IBM T.J. Watson Research
56 /* P.O. Box 704
57 /* Yorktown Heights, NY 10598, USA
58 /*--*/
59 
60 /* System library. */
61 
62 #include <sys_defs.h>
63 #include <sys/stat.h>
64 #include <unistd.h>
65 #include <string.h>
66 #include <errno.h>
67 
68 /* Utility library. */
69 
70 #include <msg.h>
71 #include <mymalloc.h>
72 #include <htable.h>
73 #include <split_at.h>
74 #include <stringops.h>
75 #include <dict.h>
76 #include <stat_as.h>
77 
78 /* Global library. */
79 
80 #include <bounce.h>
81 #include <defer.h>
82 #include <mail_params.h>
83 #include <split_addr.h>
84 #include <strip_addr.h>
85 #include <ext_prop.h>
86 #include <mypwd.h>
87 #include <canon_addr.h>
88 
89 /* Application-specific. */
90 
91 #include "local.h"
92 
93 /* deliver_switch - branch on recipient type */
94 
95 static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr)
96 {
97  const char *myname = "deliver_switch";
98  int status = 0;
99  struct stat st;
100  struct mypasswd *mypwd;
101 
102  /*
103  * Make verbose logging easier to understand.
104  */
105  state.level++;
106  if (msg_verbose)
107  MSG_LOG_STATE(myname, state);
108 
109 
110  /*
111  * \user is special: it means don't do any alias or forward expansion.
112  *
113  * XXX This code currently does not work due to revision of the RFC822
114  * address parser. \user should be permitted only in locally specified
115  * aliases, includes or forward files.
116  *
117  * XXX Should test for presence of user home directory.
118  */
119  if (state.msg_attr.rcpt.address[0] == '\\') {
120  state.msg_attr.rcpt.address++, state.msg_attr.local++, state.msg_attr.user++;
121  if (deliver_mailbox(state, usr_attr, &status) == 0)
122  status = deliver_unknown(state, usr_attr);
123  return (status);
124  }
125 
126  /*
127  * Otherwise, alias expansion has highest precedence. First look up the
128  * full localpart, then the bare user. Obey the address extension
129  * propagation policy.
130  */
131  state.msg_attr.unmatched = 0;
132  if (deliver_alias(state, usr_attr, state.msg_attr.local, &status))
133  return (status);
134  if (state.msg_attr.extension != 0) {
136  state.msg_attr.unmatched = state.msg_attr.extension;
137  if (deliver_alias(state, usr_attr, state.msg_attr.user, &status))
138  return (status);
139  state.msg_attr.unmatched = state.msg_attr.extension;
140  }
141 
142  /*
143  * Special case for mail locally forwarded or aliased to a different
144  * local address. Resubmit the message via the cleanup service, so that
145  * each recipient gets a separate delivery queue file status record in
146  * the new queue file. The downside of this approach is that mutually
147  * recursive .forward files cause a mail forwarding loop. Fortunately,
148  * the loop can be broken by the use of the Delivered-To: message header.
149  *
150  * The code below must not trigger on mail sent to an alias that has no
151  * owner- companion, so that mail for an alias first.last->username is
152  * delivered directly, instead of going through username->first.last
153  * canonical mappings in the cleanup service. The downside of this
154  * approach is that recipients in the expansion of an alias without
155  * owner- won't have separate delivery queue file status records, because
156  * for them, the message won't be resubmitted as a new queue file.
157  *
158  * Do something sensible on systems that receive mail for multiple domains,
159  * such as primary.name and secondary.name. Don't resubmit the message
160  * when mail for `user@secondary.name' is delivered to a .forward file
161  * that lists `user' or `user@primary.name'. We already know that the
162  * recipient domain is local, so we only have to compare local parts.
163  */
164  if (state.msg_attr.owner != 0
165  && strcasecmp_utf8(state.msg_attr.owner, state.msg_attr.user) != 0)
166  return (deliver_indirect(state));
167 
168  /*
169  * Always forward recipients in :include: files.
170  */
171  if (state.msg_attr.exp_type == EXPAND_TYPE_INCL)
172  return (deliver_indirect(state));
173 
174  /*
175  * Delivery to local user. First try expansion of the recipient's
176  * $HOME/.forward file, then mailbox delivery. Back off when the user's
177  * home directory does not exist.
178  */
179  mypwd = 0;
181  && (errno = mypwnam_err(state.msg_attr.user, &mypwd)) != 0) {
182  msg_warn("error looking up passwd info for %s: %m",
183  state.msg_attr.user);
184  dsb_simple(state.msg_attr.why, "4.0.0", "user lookup error");
185  return (defer_append(BOUNCE_FLAGS(state.request),
186  BOUNCE_ATTR(state.msg_attr)));
187  }
188  if (mypwd != 0) {
189  if (stat_as(mypwd->pw_dir, &st, mypwd->pw_uid, mypwd->pw_gid) < 0) {
190  dsb_simple(state.msg_attr.why, "4.3.0",
191  "cannot access home directory %s: %m", mypwd->pw_dir);
192  mypwfree(mypwd);
193  return (defer_append(BOUNCE_FLAGS(state.request),
194  BOUNCE_ATTR(state.msg_attr)));
195  }
196  mypwfree(mypwd);
197  }
198  if (deliver_dotforward(state, usr_attr, &status) == 0
199  && deliver_mailbox(state, usr_attr, &status) == 0)
200  status = deliver_unknown(state, usr_attr);
201  return (status);
202 }
203 
204 /* deliver_recipient - deliver one local recipient */
205 
207 {
208  const char *myname = "deliver_recipient";
209  VSTRING *folded;
210  int rcpt_stat;
211 
212  /*
213  * Make verbose logging easier to understand.
214  */
215  state.level++;
216  if (msg_verbose)
217  MSG_LOG_STATE(myname, state);
218 
219  /*
220  * Duplicate filter.
221  */
222  if (been_here(state.dup_filter, "recipient %d %s",
223  state.level, state.msg_attr.rcpt.address))
224  return (0);
225 
226  /*
227  * With each level of recursion, detect and break external message
228  * forwarding loops.
229  *
230  * If the looping recipient address has an owner- alias, send the error
231  * report there instead.
232  *
233  * XXX A delivery agent cannot change the envelope sender address for
234  * bouncing. As a workaround we use a one-recipient bounce procedure.
235  *
236  * The proper fix would be to record in the bounce logfile an error return
237  * address for each individual recipient. This would also eliminate the
238  * need for VERP specific bouncing code, at the cost of complicating the
239  * normal bounce sending procedure, but would simplify the code below.
240  */
241  if (delivered_hdr_find(state.loop_info, state.msg_attr.rcpt.address)) {
242  dsb_simple(state.msg_attr.why, "5.4.6", "mail forwarding loop for %s",
243  state.msg_attr.rcpt.address);
244  /* Account for possible owner- sender address override. */
245  return (bounce_workaround(state));
246  }
247 
248  /*
249  * Set up the recipient-specific attributes. If this is forwarded mail,
250  * leave the delivered attribute alone, so that the forwarded message
251  * will show the correct forwarding recipient.
252  */
253  if (state.msg_attr.delivered == 0)
254  state.msg_attr.delivered = state.msg_attr.rcpt.address;
255  folded = vstring_alloc(100);
256  state.msg_attr.local = casefold(folded, state.msg_attr.rcpt.address);
257  if ((state.msg_attr.domain = split_at_right(state.msg_attr.local, '@')) == 0)
258  msg_warn("no @ in recipient address: %s", state.msg_attr.local);
259 
260  /*
261  * Address extension management.
262  *
263  * XXX Fix 20100422, finalized 20100529: it is too error-prone to
264  * distinguish between "no extension" and "no valid extension", so we
265  * drop an invalid extension from the recipient address local-part.
266  */
267  state.msg_attr.user = mystrdup(state.msg_attr.local);
268  if (*var_rcpt_delim) {
269  state.msg_attr.extension =
271  if (state.msg_attr.extension && strchr(state.msg_attr.extension, '/')) {
272  msg_warn("%s: address with illegal extension: %s",
273  state.msg_attr.queue_id, state.msg_attr.local);
274  state.msg_attr.extension = 0;
275  /* XXX Can't myfree + mystrdup, must truncate instead. */
276  state.msg_attr.local[strlen(state.msg_attr.user)] = 0;
277  /* Truncating is safe. The code below rejects null usernames. */
278  }
279  } else
280  state.msg_attr.extension = 0;
281  state.msg_attr.unmatched = state.msg_attr.extension;
282 
283  /*
284  * Do not allow null usernames.
285  */
286  if (state.msg_attr.user[0] == 0) {
287  dsb_simple(state.msg_attr.why, "5.1.3",
288  "null username in \"%s\"", state.msg_attr.rcpt.address);
289  return (bounce_append(BOUNCE_FLAGS(state.request),
290  BOUNCE_ATTR(state.msg_attr)));
291  }
292 
293  /*
294  * Run the recipient through the delivery switch.
295  */
296  if (msg_verbose)
297  deliver_attr_dump(&state.msg_attr);
298  rcpt_stat = deliver_switch(state, usr_attr);
299 
300  /*
301  * Clean up.
302  */
303  vstring_free(folded);
304  myfree(state.msg_attr.user);
305 
306  return (rcpt_stat);
307 }
int msg_verbose
Definition: msg.c:177
int mypwnam_err(const char *name, struct mypasswd **result)
Definition: mypwd.c:242
char * extension
Definition: local.h:83
#define BOUNCE_ATTR(attr)
Definition: local.h:134
void myfree(void *ptr)
Definition: mymalloc.c:207
char * pw_dir
Definition: mypwd.h:24
bool var_stat_home_dir
Definition: local.c:674
char * mystrdup(const char *str)
Definition: mymalloc.c:225
int deliver_indirect(LOCAL_STATE state)
Definition: indirect.c:60
int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, char *name, int *statusp)
Definition: alias.c:102
const char * address
#define stat(p, s)
Definition: warn_stat.h:18
Definition: mypwd.h:17
int delivered_hdr_find(DELIVERED_HDR_INFO *info, const char *address)
char * local
Definition: local.h:81
#define split_addr
Definition: split_addr.h:20
#define strcasecmp_utf8(s1, s2)
Definition: stringops.h:75
char * domain
Definition: local.h:80
DELIVER_REQUEST * request
Definition: local.h:113
void deliver_attr_dump(DELIVER_ATTR *attrp)
Definition: deliver_attr.c:77
#define BOUNCE_FLAGS(request)
#define casefold(dst, src)
Definition: stringops.h:67
int bounce_append(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, DSN *dsn)
Definition: bounce.c:222
char * var_rcpt_delim
Definition: mail_params.c:274
int exp_type
Definition: local.h:89
int stat_as(const char *path, struct stat *st, uid_t euid, gid_t egid)
Definition: stat_as.c:51
int deliver_mailbox(LOCAL_STATE, USER_ATTR, int *)
Definition: mailbox.c:250
BH_TABLE * dup_filter
Definition: local.h:111
gid_t pw_gid
Definition: mypwd.h:22
char * unmatched
Definition: local.h:84
#define EXT_PROP_ALIAS
Definition: ext_prop.h:19
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
char * user
Definition: local.h:82
uid_t pw_uid
Definition: mypwd.h:21
DELIVERED_HDR_INFO * loop_info
Definition: local.h:112
DELIVER_ATTR msg_attr
Definition: local.h:110
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
Definition: recipient.c:206
const char * owner
Definition: local.h:85
DSN_BUF * dsb_simple(DSN_BUF *dsb, const char *status, const char *format,...)
Definition: dsn_buf.c:275
int bounce_workaround(LOCAL_STATE state)
DSN_BUF * why
Definition: local.h:92
void mypwfree(struct mypasswd *mypwd)
Definition: mypwd.c:292
const char * delivered
Definition: local.h:86
int level
Definition: local.h:109
char * queue_id
Definition: local.h:72
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
int defer_append(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, DSN *dsn)
Definition: defer.c:187
RECIPIENT rcpt
Definition: local.h:79
int local_ext_prop_mask
Definition: local.c:684
#define MSG_LOG_STATE(m, p)
Definition: local.h:150
int deliver_unknown(LOCAL_STATE, USER_ATTR)
Definition: unknown.c:86
char * split_at_right(char *string, int delimiter)
Definition: split_at.c:64
#define EXPAND_TYPE_INCL
Definition: local.h:101